diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b491c600..a1ae875a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -113,7 +113,7 @@ jobs: run: | echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; chmod 600 ./deploy_key; - rsync -avzP --mkpath \ + rsync -avzP --delete --mkpath \ -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ artifacts/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:"${{ secrets.RSYNC_DEPLOY_BASE_PATH }}${{steps.names.outputs.artifacts-path}}/"; rm ./deploy_key; diff --git a/.vscode/.gitignore b/.vscode/.gitignore new file mode 100644 index 00000000..670df7d4 --- /dev/null +++ b/.vscode/.gitignore @@ -0,0 +1,4 @@ +./c_cpp_properties.json +./launch.json +./settings.json +./tasks.json diff --git a/.vscode/ReadMe.md b/.vscode/ReadMe.md new file mode 100644 index 00000000..5aed0435 --- /dev/null +++ b/.vscode/ReadMe.md @@ -0,0 +1,17 @@ +# Visual Studio Code workspace for Flipper Zero + +## Setup + + * To start developing with VSCode, run `./fbt vscode_dist` in project root. _That should only be done once_ + * After that, open firmware folder in VSCode: "File" > "Open folder" + + For more details on fbt, see [fbt docs](../documentation/fbt.md). + + +## Workflow + +Commands for building firmware are invoked through Build menu: Ctrl+Shift+B. + +To attach a debugging session, first build and flash firmware, then choose your debug probe in Debug menu (Ctrl+Shift+D). + +Note that you have to detach debugging session before rebuilding and re-flashing firmware. diff --git a/.vscode/example/c_cpp_properties.json b/.vscode/example/c_cpp_properties.json new file mode 100644 index 00000000..db08320c --- /dev/null +++ b/.vscode/example/c_cpp_properties.json @@ -0,0 +1,32 @@ +{ + "configurations": [ + { + "name": "Win32", + "compilerPath": "${workspaceFolder}/toolchain/i686-windows/bin/arm-none-eabi-gcc.exe", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + }, + { + "name": "Linux", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-linux/bin/arm-none-eabi-gcc", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + }, + { + "name": "Mac", + "compilerPath": "${workspaceFolder}/toolchain/x86_64-darwin/bin/arm-none-eabi-gcc", + "intelliSenseMode": "gcc-arm", + "compileCommands": "${workspaceFolder}/build/latest/compile_commands.json", + "configurationProvider": "ms-vscode.cpptools", + "cStandard": "gnu17", + "cppStandard": "c++17" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/example/launch.json b/.vscode/example/launch.json new file mode 100644 index 00000000..bdad0428 --- /dev/null +++ b/.vscode/example/launch.json @@ -0,0 +1,87 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "inputs": [ + { + "id": "BLACKMAGIC", + "type": "command", + "command": "shellCommand.execute", + "args": { + "command": "./fbt get_blackmagic", + "description": "Get Blackmagic device", + } + } + ], + "configurations": [ + { + "name": "Attach FW (ST-Link)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "stlink", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/stlink.cfg", + "./debug/stm32wbx.cfg", + ], + "postAttachCommands": [ + // "attach 1", + "compare-sections", + ] + // "showDevDebugOutput": "raw", + }, + { + "name": "Attach FW (blackmagic)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "external", + "gdbTarget": "${input:BLACKMAGIC}", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "postAttachCommands": [ + "monitor swdp_scan", + "attach 1", + "set confirm off", + "set mem inaccessible-by-default off", + "compare-sections", + ] + // "showDevDebugOutput": "raw", + }, + { + "name": "Attach FW (JLink)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "jlink", + "interface": "swd", + "device": "STM32WB55RG", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + // "showDevDebugOutput": "raw", + }, + { + "name": "fbt debug", + "type": "python", + "request": "launch", + "program": "./lib/scons/scripts/scons.py", + "args": [ + "sdk" + ] + }, + { + "name": "python debug", + "type": "python", + "request": "launch", + "program": "${file}", + "args": [] + } + ] +} \ No newline at end of file diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json new file mode 100644 index 00000000..925c2e07 --- /dev/null +++ b/.vscode/example/settings.json @@ -0,0 +1,22 @@ +{ + "C_Cpp.default.cStandard": "gnu17", + "C_Cpp.default.cppStandard": "c++17", + "python.formatting.provider": "black", + "workbench.tree.indent": 12, + "cortex-debug.enableTelemetry": false, + "cortex-debug.variableUseNaturalFormat": true, + "cortex-debug.showRTOS": true, + "cortex-debug.armToolchainPath.windows": "${workspaceFolder}/toolchain/i686-windows/bin", + "cortex-debug.armToolchainPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/bin", + "cortex-debug.armToolchainPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/bin", + "cortex-debug.openocdPath.windows": "${workspaceFolder}/toolchain/i686-windows/openocd/bin/openocd.exe", + "cortex-debug.openocdPath.linux": "${workspaceFolder}/toolchain/x86_64-linux/openocd/bin/openocd", + "cortex-debug.openocdPath.osx": "${workspaceFolder}/toolchain/x86_64-darwin/openocd/bin/openocd", + "editor.formatOnSave": true, + "files.associations": { + "*.scons": "python", + "SConscript": "python", + "SConstruct": "python", + "*.fam": "python", + } +} \ No newline at end of file diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json new file mode 100644 index 00000000..32efa006 --- /dev/null +++ b/.vscode/example/tasks.json @@ -0,0 +1,103 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "[Release] Build", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0" + }, + { + "label": "[Debug] Build", + "group": "build", + "type": "shell", + "command": "./fbt" + }, + { + "label": "[Release] Flash (ST-Link)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash" + }, + { + "label": "[Debug] Flash (ST-Link)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 flash" + }, + { + "label": "[Release] Flash (blackmagic)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_blackmagic" + }, + { + "label": "[Debug] Flash (blackmagic)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 flash_blackmagic" + }, + { + "label": "[Release] Flash (JLink)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 jflash" + }, + { + "label": "[Debug] Flash (JLink)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 jflash" + }, + { + "label": "[Release] Build update bundle", + "group": "build", + "type": "shell", + "command": "./fbt updater_package COMPACT=1 DEBUG=0" + }, + { + "label": "[Debug] Build update bundle", + "group": "build", + "type": "shell", + "command": "./fbt updater_package" + }, + { + "label": "[Release] Build updater", + "group": "build", + "type": "shell", + "command": "./fbt updater_all COMPACT=1 DEBUG=0" + }, + { + "label": "[Debug] Build updater", + "group": "build", + "type": "shell", + "command": "./fbt updater_all" + }, + { + "label": "[Debug] Flash (USB, w/o resources)", + "group": "build", + "type": "shell", + "command": "./fbt FORCE=1 flash_usb" + }, + { + "label": "[Release] Flash (USB, w/o resources)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb" + }, + { + "label": "[Debug:unit_tests] Flash (USB)", + "group": "build", + "type": "shell", + "command": "./fbt FIRMWARE_APP_SET=unit_tests FORCE=1 flash_usb" + }, + { + "label": "[Release] Flash (USB, with resources)", + "group": "build", + "type": "shell", + "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_usb_full" + }, + ] +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..b53ffc24 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,15 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-python.black-formatter", + "ms-vscode.cpptools", + "amiralizadeh9480.cpp-helper", + "marus25.cortex-debug", + "zxh404.vscode-proto3", + "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 diff --git a/SConstruct b/SConstruct index fe731e35..52fe75a6 100644 --- a/SConstruct +++ b/SConstruct @@ -274,8 +274,13 @@ distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py") # Find blackmagic probe - distenv.PhonyTarget( "get_blackmagic", "@echo $( ${BLACKMAGIC_ADDR} $)", ) + +# Prepare vscode environment +vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*")) +distenv.Precious(vscode_dist) +distenv.NoClean(vscode_dist) +distenv.Alias("vscode_dist", vscode_dist) diff --git a/applications/about/about.c b/applications/about/about.c index 26738fe0..f785c2e4 100644 --- a/applications/about/about.c +++ b/applications/about/about.c @@ -45,7 +45,7 @@ static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* DialogMessageButton result; const char* screen_text = "For all compliance\n" - "certificates please visit\n" + "certificates please visit:\n" "www.flipp.dev/compliance"; dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop); @@ -91,13 +91,13 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* furi_hal_version_get_hw_region_name(), my_name ? my_name : "Unknown"); - string_cat_printf(buffer, "Serial number:\n"); + string_cat_printf(buffer, "Serial Number:\n"); const uint8_t* uid = furi_hal_version_uid(); for(size_t i = 0; i < furi_hal_version_uid_size(); i++) { string_cat_printf(buffer, "%02X", uid[i]); } - dialog_message_set_header(message, "HW Version info:", 0, 0, AlignLeft, AlignTop); + dialog_message_set_header(message, "HW Version Info:", 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); @@ -133,7 +133,7 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* version_get_gitbranch(ver)); } - dialog_message_set_header(message, "FW Version info:", 0, 0, AlignLeft, AlignTop); + dialog_message_set_header(message, "FW Version Info:", 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); diff --git a/applications/accessor/accessor_app.cpp b/applications/accessor/accessor_app.cpp index f021a816..2e3e27ec 100644 --- a/applications/accessor/accessor_app.cpp +++ b/applications/accessor/accessor_app.cpp @@ -32,14 +32,14 @@ void AccessorApp::run(void) { } AccessorApp::AccessorApp() { - notification = static_cast(furi_record_open("notification")); + notification = static_cast(furi_record_open(RECORD_NOTIFICATION)); onewire_host = onewire_host_alloc(); furi_hal_power_enable_otg(); } AccessorApp::~AccessorApp() { furi_hal_power_disable_otg(); - furi_record_close("notification"); + furi_record_close(RECORD_NOTIFICATION); onewire_host_free(onewire_host); } diff --git a/applications/accessor/accessor_view_manager.cpp b/applications/accessor/accessor_view_manager.cpp index 7c681cbb..db723d68 100644 --- a/applications/accessor/accessor_view_manager.cpp +++ b/applications/accessor/accessor_view_manager.cpp @@ -15,7 +15,7 @@ AccessorAppViewManager::AccessorAppViewManager() { popup = popup_alloc(); add_view(ViewType::Popup, popup_get_view(popup)); - gui = static_cast(furi_record_open("gui")); + gui = static_cast(furi_record_open(RECORD_GUI)); view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); // set previous view callback for all views @@ -31,6 +31,7 @@ AccessorAppViewManager::~AccessorAppViewManager() { view_dispatcher, static_cast(AccessorAppViewManager::ViewType::Popup)); // free view modules + furi_record_close(RECORD_GUI); submenu_free(submenu); popup_free(popup); diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c index fc0cad57..35199242 100644 --- a/applications/archive/helpers/archive_favorites.c +++ b/applications/archive/helpers/archive_favorites.c @@ -64,7 +64,7 @@ uint16_t archive_favorites_count(void* context) { break; } if(!string_size(buffer)) { - break; + continue; // Skip empty lines } ++lines; } @@ -93,7 +93,7 @@ static bool archive_favourites_rescan() { break; } if(!string_size(buffer)) { - break; + continue; } if(string_search(buffer, "/app:") == 0) { @@ -152,7 +152,7 @@ bool archive_favorites_read(void* context) { break; } if(!string_size(buffer)) { - break; + continue; } if(string_search(buffer, "/app:") == 0) { @@ -215,7 +215,7 @@ bool archive_favorites_delete(const char* format, ...) { break; } if(!string_size(buffer)) { - break; + continue; } if(string_search(buffer, filename)) { @@ -259,7 +259,7 @@ bool archive_is_favorite(const char* format, ...) { break; } if(!string_size(buffer)) { - break; + continue; } if(!string_search(buffer, filename)) { found = true; @@ -299,7 +299,7 @@ bool archive_favorites_rename(const char* src, const char* dst) { break; } if(!string_size(buffer)) { - break; + continue; } archive_file_append( diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/archive/scenes/archive_scene_browser.c index 74861bea..e22ac792 100644 --- a/applications/archive/scenes/archive_scene_browser.c +++ b/applications/archive/scenes/archive_scene_browser.c @@ -92,8 +92,6 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { ArchiveBrowserView* browser = archive->browser; ArchiveFile_t* selected = archive_get_current_file(browser); - const char* name = archive_get_name(browser); - bool known_app = archive_is_known_app(selected->type); bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; bool consumed = false; @@ -108,18 +106,19 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventFileMenuRun: - if(known_app) { + if(archive_is_known_app(selected->type)) { archive_run_in_app(browser, selected); archive_show_file_menu(browser, false); } consumed = true; break; - case ArchiveBrowserEventFileMenuPin: + case ArchiveBrowserEventFileMenuPin: { + const char* name = archive_get_name(browser); if(favorites) { archive_favorites_delete(name); archive_file_array_rm_selected(browser); archive_show_file_menu(browser, false); - } else if(known_app) { + } else if(archive_is_known_app(selected->type)) { if(archive_is_favorite("%s", name)) { archive_favorites_delete("%s", name); } else { @@ -128,12 +127,12 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_show_file_menu(browser, false); } consumed = true; - break; + } break; case ArchiveBrowserEventFileMenuRename: if(favorites) { browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); - } else if((known_app) && (selected->is_app == false)) { + } else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { archive_show_file_menu(browser, false); scene_manager_set_scene_state( archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); diff --git a/applications/archive/scenes/archive_scene_rename.c b/applications/archive/scenes/archive_scene_rename.c index 2a85f3ce..293fa89a 100644 --- a/applications/archive/scenes/archive_scene_rename.c +++ b/applications/archive/scenes/archive_scene_rename.c @@ -37,7 +37,7 @@ void archive_scene_rename_on_enter(void* context) { false); ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(archive->browser->path), archive->file_extension, NULL); + string_get_cstr(archive->browser->path), archive->file_extension, ""); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); string_clear(filename); diff --git a/applications/bad_usb/bad_usb_app.c b/applications/bad_usb/bad_usb_app.c index 7eb86181..09d7d346 100644 --- a/applications/bad_usb/bad_usb_app.c +++ b/applications/bad_usb/bad_usb_app.c @@ -28,7 +28,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { string_init(app->file_path); - if(arg != NULL) { + if(arg && strlen(arg)) { string_set_str(app->file_path, arg); } @@ -79,7 +79,6 @@ void bad_usb_app_free(BadUsbApp* app) { furi_assert(app); // Views - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewFileSelect); view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); bad_usb_free(app->bad_usb_view); diff --git a/applications/bad_usb/bad_usb_app_i.h b/applications/bad_usb/bad_usb_app_i.h index 6b323d35..6378bddf 100644 --- a/applications/bad_usb/bad_usb_app_i.h +++ b/applications/bad_usb/bad_usb_app_i.h @@ -38,6 +38,5 @@ struct BadUsbApp { typedef enum { BadUsbAppViewError, - BadUsbAppViewFileSelect, BadUsbAppViewWork, } BadUsbAppView; diff --git a/applications/bt/bt_hid_app/bt_hid.c b/applications/bt/bt_hid_app/bt_hid.c index 29a25c86..b6f00e93 100755 --- a/applications/bt/bt_hid_app/bt_hid.c +++ b/applications/bt/bt_hid_app/bt_hid.c @@ -90,7 +90,7 @@ BtHid* bt_hid_app_alloc() { submenu_add_item( app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app); submenu_add_item( - app->submenu, "Media player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); + app->submenu, "Media Player", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app); submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app); view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit); view_dispatcher_add_view( @@ -103,7 +103,7 @@ BtHid* bt_hid_app_alloc() { dialog_ex_set_left_button_text(app->dialog, "Exit"); dialog_ex_set_right_button_text(app->dialog, "Stay"); dialog_ex_set_center_button_text(app->dialog, "Menu"); - dialog_ex_set_header(app->dialog, "Close current app?", 16, 12, AlignLeft, AlignTop); + dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); view_dispatcher_add_view( app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog)); diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index 6f0810dd..bc80acc1 100644 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -347,7 +347,8 @@ static void bt_close_connection(Bt* bt) { furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); } -int32_t bt_srv() { +int32_t bt_srv(void* p) { + UNUSED(p); Bt* bt = bt_alloc(); if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { diff --git a/applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index be9a7196..964736b6 100755 --- a/applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -10,9 +10,9 @@ void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void bt_settings_scene_forget_dev_confirm_on_enter(void* context) { BtSettingsApp* app = context; DialogEx* dialog = app->dialog; - dialog_ex_set_header(dialog, "Unpair all devices?", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog, "Unpair All Devices?", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( - dialog, "All previous pairings\nwill be lost.", 64, 22, AlignCenter, AlignTop); + dialog, "All previous pairings\nwill be lost!", 64, 22, AlignCenter, AlignTop); dialog_ex_set_left_button_text(dialog, "Back"); dialog_ex_set_right_button_text(dialog, "Unpair"); dialog_ex_set_context(dialog, app); diff --git a/applications/cli/cli.c b/applications/cli/cli.c index 4d9b8a5f..e554ac89 100644 --- a/applications/cli/cli.c +++ b/applications/cli/cli.c @@ -439,9 +439,9 @@ void cli_session_open(Cli* cli, void* session) { cli->session = session; if(cli->session != NULL) { cli->session->init(); - furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout); + furi_thread_set_stdout_callback(cli->session->tx_stdout); } else { - furi_stdglue_set_thread_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL); } furi_semaphore_release(cli->idle_sem); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); @@ -455,7 +455,7 @@ void cli_session_close(Cli* cli) { cli->session->deinit(); } cli->session = NULL; - furi_stdglue_set_thread_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); } @@ -469,9 +469,9 @@ int32_t cli_srv(void* p) { furi_record_create(RECORD_CLI, cli); if(cli->session != NULL) { - furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout); + furi_thread_set_stdout_callback(cli->session->tx_stdout); } else { - furi_stdglue_set_thread_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL); } if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c index 19887796..177a274a 100644 --- a/applications/cli/cli_commands.c +++ b/applications/cli/cli_commands.c @@ -15,7 +15,7 @@ void cli_command_device_info_callback(const char* key, const char* value, bool last, void* context) { UNUSED(context); UNUSED(last); - printf("%-24s: %s\r\n", key, value); + printf("%-30s: %s\r\n", key, value); } /* diff --git a/applications/cli/cli_i.h b/applications/cli/cli_i.h index 076dd75e..8f0bd85d 100755 --- a/applications/cli/cli_i.h +++ b/applications/cli/cli_i.h @@ -25,7 +25,7 @@ struct CliSession { void (*deinit)(void); size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout); void (*tx)(const uint8_t* buffer, size_t size); - void (*tx_stdout)(void* _cookie, const char* data, size_t size); + void (*tx_stdout)(const char* data, size_t size); bool (*is_connected)(void); }; diff --git a/applications/cli/cli_vcp.c b/applications/cli/cli_vcp.c index 5d66b8f8..5a8b44dc 100644 --- a/applications/cli/cli_vcp.c +++ b/applications/cli/cli_vcp.c @@ -277,8 +277,7 @@ static void cli_vcp_tx(const uint8_t* buffer, size_t size) { #endif } -static void cli_vcp_tx_stdout(void* _cookie, const char* data, size_t size) { - UNUSED(_cookie); +static void cli_vcp_tx_stdout(const char* data, size_t size) { cli_vcp_tx((const uint8_t*)data, size); } diff --git a/applications/debug_tools/keypad_test.c b/applications/debug_tools/keypad_test.c index 6708c82b..2470baf8 100644 --- a/applications/debug_tools/keypad_test.c +++ b/applications/debug_tools/keypad_test.c @@ -26,11 +26,11 @@ static void keypad_test_render_callback(Canvas* canvas, void* ctx) { canvas_clear(canvas); char strings[5][20]; - sprintf(strings[0], "Ok: %d", state->ok); - sprintf(strings[1], "L: %d", state->left); - sprintf(strings[2], "R: %d", state->right); - sprintf(strings[3], "U: %d", state->up); - sprintf(strings[4], "D: %d", state->down); + snprintf(strings[0], 20, "Ok: %d", state->ok); + snprintf(strings[1], 20, "L: %d", state->left); + snprintf(strings[2], 20, "R: %d", state->right); + snprintf(strings[3], 20, "U: %d", state->up); + snprintf(strings[4], 20, "D: %d", state->down); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 0, 10, "Keypad test"); diff --git a/applications/desktop/animations/views/bubble_animation_view.c b/applications/desktop/animations/views/bubble_animation_view.c index 54a686fb..607862d1 100644 --- a/applications/desktop/animations/views/bubble_animation_view.c +++ b/applications/desktop/animations/views/bubble_animation_view.c @@ -143,7 +143,7 @@ static void bubble_animation_activate(BubbleAnimationView* view, bool force) { furi_assert(view); bool activate = true; BubbleAnimationViewModel* model = view_get_model(view->view); - if(!model->current) { + if(model->current == NULL) { activate = false; } else if(model->freeze_frame) { activate = false; @@ -151,14 +151,16 @@ static void bubble_animation_activate(BubbleAnimationView* view, bool force) { activate = false; } - if(!force) { - if((model->active_ended_at + model->current->active_cooldown * 1000) > - xTaskGetTickCount()) { - activate = false; - } else if(model->active_shift) { - activate = false; - } else if(model->current_frame >= model->current->passive_frames) { - activate = false; + if(model->current != NULL) { + if(!force) { + if((model->active_ended_at + model->current->active_cooldown * 1000) > + xTaskGetTickCount()) { + activate = false; + } else if(model->active_shift) { + activate = false; + } else if(model->current_frame >= model->current->passive_frames) { + activate = false; + } } } view_commit_model(view->view, false); @@ -288,7 +290,10 @@ static void bubble_animation_enter(void* context) { bubble_animation_activate(view, false); BubbleAnimationViewModel* model = view_get_model(view->view); - uint8_t frame_rate = model->current->icon_animation.frame_rate; + uint8_t frame_rate = 0; + if(model->current != NULL) { + frame_rate = model->current->icon_animation.frame_rate; + } view_commit_model(view->view, false); if(frame_rate) { diff --git a/applications/desktop/desktop_settings/desktop_settings_app.c b/applications/desktop/desktop_settings/desktop_settings_app.c index bc41be6e..89513a8b 100644 --- a/applications/desktop/desktop_settings/desktop_settings_app.c +++ b/applications/desktop/desktop_settings/desktop_settings_app.c @@ -90,7 +90,7 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { extern int32_t desktop_settings_app(void* p) { DesktopSettingsApp* app = desktop_settings_app_alloc(); LOAD_DESKTOP_SETTINGS(&app->settings); - if(!strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG)) { + if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); } else { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart); diff --git a/applications/desktop/views/desktop_view_debug.c b/applications/desktop/views/desktop_view_debug.c index 68c054c2..e2641193 100644 --- a/applications/desktop/views/desktop_view_debug.c +++ b/applications/desktop/views/desktop_view_debug.c @@ -23,7 +23,7 @@ void desktop_debug_render(Canvas* canvas, void* model) { const Version* ver; char buffer[64]; - static const char* headers[] = {"FW Version info:", "Dolphin info:"}; + static const char* headers[] = {"FW Version Info:", "Dolphin Info:"}; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); @@ -78,7 +78,6 @@ void desktop_debug_render(Canvas* canvas, void* model) { canvas_draw_str(canvas, 5, 50 + STATUS_BAR_Y_SHIFT, buffer); } else { - char buffer[64]; Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); furi_record_close(RECORD_DOLPHIN); @@ -87,18 +86,20 @@ void desktop_debug_render(Canvas* canvas, void* model) { uint32_t remaining = dolphin_state_xp_to_levelup(m->icounter); canvas_set_font(canvas, FontSecondary); - snprintf(buffer, 64, "Icounter: %ld Butthurt %ld", m->icounter, m->butthurt); + snprintf(buffer, sizeof(buffer), "Icounter: %ld Butthurt %ld", m->icounter, m->butthurt); canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); snprintf( buffer, - 64, + sizeof(buffer), "Level: %ld To level up: %ld", current_lvl, (remaining == (uint32_t)(-1) ? remaining : 0)); canvas_draw_str(canvas, 5, 29 + STATUS_BAR_Y_SHIFT, buffer); - snprintf(buffer, 64, "%s", asctime(localtime((const time_t*)&m->timestamp))); + // even if timestamp is uint64_t, it's safe to cast it to uint32_t, because furi_hal_rtc_datetime_to_timestamp only returns uint32_t + snprintf(buffer, sizeof(buffer), "%ld", (uint32_t)m->timestamp); + canvas_draw_str(canvas, 5, 39 + STATUS_BAR_Y_SHIFT, buffer); canvas_draw_str(canvas, 0, 49 + STATUS_BAR_Y_SHIFT, "[< >] icounter value [ok] save"); } diff --git a/applications/desktop/views/desktop_view_lock_menu.c b/applications/desktop/views/desktop_view_lock_menu.c index 1b2bd76b..97d3c489 100644 --- a/applications/desktop/views/desktop_view_lock_menu.c +++ b/applications/desktop/views/desktop_view_lock_menu.c @@ -67,7 +67,7 @@ void desktop_lock_menu_render(Canvas* canvas, void* model) { const char* str = Lockmenu_Items[i]; if(i == 1 && !m->pin_set) str = "Set PIN"; - if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not implemented"; + if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not Implemented"; if(str != NULL) canvas_draw_str_aligned( diff --git a/applications/gpio/views/gpio_test.c b/applications/gpio/views/gpio_test.c index 37c2f408..89c09f86 100755 --- a/applications/gpio/views/gpio_test.c +++ b/applications/gpio/views/gpio_test.c @@ -20,7 +20,7 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event); static void gpio_test_draw_callback(Canvas* canvas, void* _model) { GpioTestModel* model = _model; canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Gpio Output mode test"); + elements_multiline_text_aligned(canvas, 64, 2, AlignCenter, AlignTop, "GPIO Output Mode Test"); canvas_set_font(canvas, FontSecondary); elements_multiline_text_aligned( canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin"); diff --git a/applications/gui/modules/button_menu.c b/applications/gui/modules/button_menu.c index 36fd6f3a..84fea788 100644 --- a/applications/gui/modules/button_menu.c +++ b/applications/gui/modules/button_menu.c @@ -185,17 +185,19 @@ static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { return false; }); - if(item->type == ButtonMenuItemTypeControl) { - if(type == InputTypeShort) { - if(item && item->callback) { - item->callback(item->callback_context, item->index, type); + if(item) { + if(item->type == ButtonMenuItemTypeControl) { + if(type == InputTypeShort) { + if(item && item->callback) { + item->callback(item->callback_context, item->index, type); + } } } - } - if(item->type == ButtonMenuItemTypeCommon) { - if((type == InputTypePress) || (type == InputTypeRelease)) { - if(item && item->callback) { - item->callback(item->callback_context, item->index, type); + if(item->type == ButtonMenuItemTypeCommon) { + if((type == InputTypePress) || (type == InputTypeRelease)) { + if(item && item->callback) { + item->callback(item->callback_context, item->index, type); + } } } } diff --git a/applications/gui/modules/text_input.c b/applications/gui/modules/text_input.c index 5aa101bb..26f74e7a 100644 --- a/applications/gui/modules/text_input.c +++ b/applications/gui/modules/text_input.c @@ -131,7 +131,9 @@ static bool char_is_lowercase(char letter) { } static char char_to_uppercase(const char letter) { - if(isalpha(letter)) { + if(letter == '_') { + return 0x20; + } else if(isalpha(letter)) { return (letter - 0x20); } else { return letter; @@ -147,7 +149,7 @@ static void text_input_backspace_cb(TextInputModel* model) { static void text_input_view_draw_callback(Canvas* canvas, void* _model) { TextInputModel* model = _model; - uint8_t text_length = strlen(model->text_buffer); + uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; uint8_t needed_string_width = canvas_width(canvas) - 8; uint8_t start_pos = 4; diff --git a/applications/gui/modules/validators.h b/applications/gui/modules/validators.h index 15dbe901..c4c4ef54 100644 --- a/applications/gui/modules/validators.h +++ b/applications/gui/modules/validators.h @@ -1,7 +1,7 @@ #pragma once -// #include #include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/ibutton/ibutton.c b/applications/ibutton/ibutton.c index 5ccb1f6c..7ee1110e 100644 --- a/applications/ibutton/ibutton.c +++ b/applications/ibutton/ibutton.c @@ -9,20 +9,6 @@ #define TAG "iButtonApp" -static const NotificationSequence sequence_blink_start_cyan = { - &message_blink_start_10, - &message_blink_set_color_cyan, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_blink_start_magenta = { - &message_blink_start_10, - &message_blink_set_color_magenta, - &message_do_not_reset, - NULL, -}; - static const NotificationSequence sequence_blink_set_yellow = { &message_blink_set_color_yellow, NULL, @@ -33,11 +19,6 @@ static const NotificationSequence sequence_blink_set_magenta = { NULL, }; -static const NotificationSequence sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - static const NotificationSequence* ibutton_notification_sequences[] = { &sequence_error, &sequence_success, @@ -106,6 +87,8 @@ static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) if(event == RpcAppEventSessionClose) { view_dispatcher_send_custom_event( ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); + rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); + ibutton->rpc_ctx = NULL; } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); } else if(event == RpcAppEventLoadFile) { @@ -324,22 +307,6 @@ void ibutton_text_store_clear(iButton* ibutton) { memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE); } -void ibutton_switch_to_previous_scene_one_of( - iButton* ibutton, - const uint32_t* scene_ids, - size_t scene_ids_size) { - furi_assert(scene_ids_size); - SceneManager* scene_manager = ibutton->scene_manager; - - for(size_t i = 0; i < scene_ids_size; ++i) { - const uint32_t scene_id = scene_ids[i]; - if(scene_manager_has_previous_scene(scene_manager, scene_id)) { - scene_manager_search_and_switch_to_previous_scene(scene_manager, scene_id); - return; - } - } -} - void ibutton_notification_message(iButton* ibutton, uint32_t message) { furi_assert(message < sizeof(ibutton_notification_sequences) / sizeof(NotificationSequence*)); notification_message(ibutton->notifications, ibutton_notification_sequences[message]); @@ -353,7 +320,7 @@ int32_t ibutton_app(void* p) { bool key_loaded = false; bool rpc_mode = false; - if(p) { + if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { FURI_LOG_D(TAG, "Running in RPC mode"); diff --git a/applications/ibutton/ibutton_i.h b/applications/ibutton/ibutton_i.h index 9d4354d0..fd11b8c1 100644 --- a/applications/ibutton/ibutton_i.h +++ b/applications/ibutton/ibutton_i.h @@ -83,8 +83,4 @@ bool ibutton_save_key(iButton* ibutton, const char* key_name); bool ibutton_delete_key(iButton* ibutton); void ibutton_text_store_set(iButton* ibutton, const char* text, ...); void ibutton_text_store_clear(iButton* ibutton); -void ibutton_switch_to_previous_scene_one_of( - iButton* ibutton, - const uint32_t* scene_ids, - size_t scene_ids_size); void ibutton_notification_message(iButton* ibutton, uint32_t message); diff --git a/applications/ibutton/scenes/ibutton_scene_exit_confirm.c b/applications/ibutton/scenes/ibutton_scene_exit_confirm.c index c4e90892..2367e121 100644 --- a/applications/ibutton/scenes/ibutton_scene_exit_confirm.c +++ b/applications/ibutton/scenes/ibutton_scene_exit_confirm.c @@ -21,7 +21,7 @@ void ibutton_scene_exit_confirm_on_enter(void* context) { widget_add_string_element( widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton menu?"); widget_add_string_element( - widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost."); + widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); } diff --git a/applications/ibutton/scenes/ibutton_scene_read.c b/applications/ibutton/scenes/ibutton_scene_read.c index c4eb15d3..7af351f0 100644 --- a/applications/ibutton/scenes/ibutton_scene_read.c +++ b/applications/ibutton/scenes/ibutton_scene_read.c @@ -14,7 +14,7 @@ void ibutton_scene_read_on_enter(void* context) { DOLPHIN_DEED(DolphinDeedIbuttonRead); popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom); - popup_set_text(popup, "waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); + popup_set_text(popup, "Waiting\nfor key ...", 95, 30, AlignCenter, AlignTop); popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup); diff --git a/applications/ibutton/scenes/ibutton_scene_retry_confirm.c b/applications/ibutton/scenes/ibutton_scene_retry_confirm.c index fa2e1dec..7f8c95b1 100644 --- a/applications/ibutton/scenes/ibutton_scene_retry_confirm.c +++ b/applications/ibutton/scenes/ibutton_scene_retry_confirm.c @@ -21,7 +21,7 @@ void ibutton_scene_retry_confirm_on_enter(void* context) { widget_add_string_element( widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?"); widget_add_string_element( - widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost."); + widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); } diff --git a/applications/ibutton/scenes/ibutton_scene_rpc.c b/applications/ibutton/scenes/ibutton_scene_rpc.c index a3f5eeee..0755c8ff 100644 --- a/applications/ibutton/scenes/ibutton_scene_rpc.c +++ b/applications/ibutton/scenes/ibutton_scene_rpc.c @@ -29,7 +29,7 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(event.event == iButtonCustomEventRpcLoad) { const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx); bool result = false; - if(arg) { + if(arg && (string_empty_p(ibutton->file_path))) { string_set_str(ibutton->file_path, arg); if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); @@ -51,17 +51,17 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { string_clear(key_name); result = true; + } else { + string_reset(ibutton->file_path); } } rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result); } else if(event.event == iButtonCustomEventRpcExit) { rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true); - ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); + scene_manager_stop(ibutton->scene_manager); view_dispatcher_stop(ibutton->view_dispatcher); } else if(event.event == iButtonCustomEventRpcSessionClose) { - rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); - ibutton->rpc_ctx = NULL; - ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); + scene_manager_stop(ibutton->scene_manager); view_dispatcher_stop(ibutton->view_dispatcher); } } diff --git a/applications/ibutton/scenes/ibutton_scene_save_name.c b/applications/ibutton/scenes/ibutton_scene_save_name.c index 6caf5d2d..be640387 100644 --- a/applications/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/ibutton/scenes/ibutton_scene_save_name.c @@ -61,8 +61,8 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { } else { const uint32_t possible_scenes[] = { iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; - ibutton_switch_to_previous_scene_one_of( - ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager_search_and_switch_to_previous_scene_one_of( + ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } } } diff --git a/applications/ibutton/scenes/ibutton_scene_save_success.c b/applications/ibutton/scenes/ibutton_scene_save_success.c index dffda6a0..6c24a897 100644 --- a/applications/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/ibutton/scenes/ibutton_scene_save_success.c @@ -31,8 +31,8 @@ bool ibutton_scene_save_success_on_event(void* context, SceneManagerEvent event) if(event.event == iButtonCustomEventBack) { const uint32_t possible_scenes[] = { iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType}; - ibutton_switch_to_previous_scene_one_of( - ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager_search_and_switch_to_previous_scene_one_of( + ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } } diff --git a/applications/ibutton/scenes/ibutton_scene_write_success.c b/applications/ibutton/scenes/ibutton_scene_write_success.c index 6abafbb3..3acb1dea 100644 --- a/applications/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/ibutton/scenes/ibutton_scene_write_success.c @@ -31,8 +31,8 @@ bool ibutton_scene_write_success_on_event(void* context, SceneManagerEvent event consumed = true; if(event.event == iButtonCustomEventBack) { const uint32_t possible_scenes[] = {iButtonSceneReadKeyMenu, iButtonSceneStart}; - ibutton_switch_to_previous_scene_one_of( - ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager_search_and_switch_to_previous_scene_one_of( + ibutton->scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } } diff --git a/applications/infrared/infrared.c b/applications/infrared/infrared.c index cbbd375d..3b4da647 100644 --- a/applications/infrared/infrared.c +++ b/applications/infrared/infrared.c @@ -7,10 +7,12 @@ static const NotificationSequence* infrared_notification_sequences[] = { &sequence_success, &sequence_set_only_green_255, &sequence_reset_green, - &sequence_blink_cyan_10, - &sequence_blink_magenta_10, &sequence_solid_yellow, - &sequence_reset_rgb}; + &sequence_reset_rgb, + &sequence_blink_start_cyan, + &sequence_blink_start_magenta, + &sequence_blink_stop, +}; static void infrared_make_app_folder(Infrared* infrared) { if(!storage_simply_mkdir(infrared->storage, INFRARED_APP_FOLDER)) { @@ -44,6 +46,8 @@ static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context if(event == RpcAppEventSessionClose) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose); + rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); + infrared->rpc_ctx = NULL; } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeRpcExit); @@ -301,6 +305,7 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { DOLPHIN_DEED(DolphinDeedIrSend); infrared_worker_tx_start(infrared->worker); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { @@ -310,14 +315,17 @@ void infrared_tx_start_button_index(Infrared* infrared, size_t button_index) { InfraredSignal* signal = infrared_remote_button_get_signal(button); infrared_tx_start_signal(infrared, signal); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } void infrared_tx_start_received(Infrared* infrared) { infrared_tx_start_signal(infrared, infrared->received_signal); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } void infrared_tx_stop(Infrared* infrared) { infrared_worker_tx_stop(infrared->worker); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); } void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) { @@ -354,12 +362,6 @@ void infrared_show_loading_popup(Infrared* infrared, bool show) { } } -void infrared_signal_sent_callback(void* context) { - furi_assert(context); - Infrared* infrared = context; - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkSend); -} - void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { furi_assert(context); Infrared* infrared = context; @@ -405,7 +407,7 @@ int32_t infrared_app(void* p) { bool is_remote_loaded = false; bool is_rpc_mode = false; - if(p) { + if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { infrared->rpc_ctx = (void*)rpc_ctx; diff --git a/applications/infrared/infrared_cli.c b/applications/infrared/infrared_cli.c index c190aad3..aae02e8f 100644 --- a/applications/infrared/infrared_cli.c +++ b/applications/infrared/infrared_cli.c @@ -27,7 +27,7 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv if(infrared_worker_signal_is_decoded(received_signal)) { const InfraredMessage* message = infrared_worker_get_decoded_signal(received_signal); - buf_cnt = sniprintf( + buf_cnt = snprintf( buf, sizeof(buf), "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", @@ -43,13 +43,13 @@ static void signal_received_callback(void* context, InfraredWorkerSignal* receiv size_t timings_cnt; infrared_worker_get_raw_signal(received_signal, &timings, &timings_cnt); - buf_cnt = sniprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); + buf_cnt = snprintf(buf, sizeof(buf), "RAW, %d samples:\r\n", timings_cnt); cli_write(cli, (uint8_t*)buf, buf_cnt); for(size_t i = 0; i < timings_cnt; ++i) { - buf_cnt = sniprintf(buf, sizeof(buf), "%lu ", timings[i]); + buf_cnt = snprintf(buf, sizeof(buf), "%lu ", timings[i]); cli_write(cli, (uint8_t*)buf, buf_cnt); } - buf_cnt = sniprintf(buf, sizeof(buf), "\r\n"); + buf_cnt = snprintf(buf, sizeof(buf), "\r\n"); cli_write(cli, (uint8_t*)buf, buf_cnt); } } diff --git a/applications/infrared/infrared_i.h b/applications/infrared/infrared_i.h index d24cab06..4e9c79d5 100644 --- a/applications/infrared/infrared_i.h +++ b/applications/infrared/infrared_i.h @@ -115,10 +115,11 @@ typedef enum { InfraredNotificationMessageSuccess, InfraredNotificationMessageGreenOn, InfraredNotificationMessageGreenOff, - InfraredNotificationMessageBlinkRead, - InfraredNotificationMessageBlinkSend, InfraredNotificationMessageYellowOn, InfraredNotificationMessageYellowOff, + InfraredNotificationMessageBlinkStartRead, + InfraredNotificationMessageBlinkStartSend, + InfraredNotificationMessageBlinkStop, } InfraredNotificationMessage; bool infrared_add_remote_with_button(Infrared* infrared, const char* name, InfraredSignal* signal); @@ -132,7 +133,6 @@ void infrared_text_store_clear(Infrared* infrared, uint32_t bank); void infrared_play_notification_message(Infrared* infrared, uint32_t message); void infrared_show_loading_popup(Infrared* infrared, bool show); -void infrared_signal_sent_callback(void* context); void infrared_signal_received_callback(void* context, InfraredWorkerSignal* received_signal); void infrared_text_input_callback(void* context); void infrared_popup_closed_callback(void* context); diff --git a/applications/infrared/scenes/common/infrared_scene_universal_common.c b/applications/infrared/scenes/common/infrared_scene_universal_common.c index 7eff81ee..57ac8116 100644 --- a/applications/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/infrared/scenes/common/infrared_scene_universal_common.c @@ -21,12 +21,14 @@ static void infrared_scene_universal_common_show_popup(Infrared* infrared, uint3 infrared_progress_view_set_back_callback( progress, infrared_scene_universal_common_progress_back_callback, infrared); view_stack_add_view(view_stack, infrared_progress_view_get_view(progress)); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); } static void infrared_scene_universal_common_hide_popup(Infrared* infrared) { ViewStack* view_stack = infrared->view_stack; InfraredProgressView* progress = infrared->progress; view_stack_remove_view(view_stack, infrared_progress_view_get_view(progress)); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); } void infrared_scene_universal_common_on_enter(void* context) { @@ -42,7 +44,6 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e if(infrared_brute_force_is_started(brute_force)) { if(event.type == SceneManagerEventTypeTick) { - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkSend); bool success = infrared_brute_force_send_next(brute_force); if(success) { success = infrared_progress_view_increase_progress(infrared->progress); @@ -71,8 +72,6 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e brute_force, infrared_custom_event_get_value(event.event), &record_count)) { DOLPHIN_DEED(DolphinDeedIrBruteForce); infrared_scene_universal_common_show_popup(infrared, record_count); - infrared_play_notification_message( - infrared, InfraredNotificationMessageBlinkSend); } else { scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); } diff --git a/applications/infrared/scenes/infrared_scene_ask_back.c b/applications/infrared/scenes/infrared_scene_ask_back.c index c9c23684..493458ad 100644 --- a/applications/infrared/scenes/infrared_scene_ask_back.c +++ b/applications/infrared/scenes/infrared_scene_ask_back.c @@ -10,13 +10,13 @@ void infrared_scene_ask_back_on_enter(void* context) { DialogEx* dialog_ex = infrared->dialog_ex; if(infrared->app_state.is_learning_new_remote) { - dialog_ex_set_header(dialog_ex, "Exit to Infrared menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to Infrared Menu?", 64, 0, AlignCenter, AlignTop); } else { - dialog_ex_set_header(dialog_ex, "Exit to remote menu?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to Remote Menu?", 64, 0, AlignCenter, AlignTop); } dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost.", 64, 31, AlignCenter, AlignCenter); + dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter); dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_center_button_text(dialog_ex, NULL); diff --git a/applications/infrared/scenes/infrared_scene_ask_retry.c b/applications/infrared/scenes/infrared_scene_ask_retry.c index 5157ee88..c87d9e6d 100644 --- a/applications/infrared/scenes/infrared_scene_ask_retry.c +++ b/applications/infrared/scenes/infrared_scene_ask_retry.c @@ -9,9 +9,9 @@ void infrared_scene_ask_retry_on_enter(void* context) { Infrared* infrared = context; DialogEx* dialog_ex = infrared->dialog_ex; - dialog_ex_set_header(dialog_ex, "Return to reading?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Return to Reading?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost.", 64, 31, AlignCenter, AlignCenter); + dialog_ex, "All unsaved data\nwill be lost!", 64, 31, AlignCenter, AlignCenter); dialog_ex_set_icon(dialog_ex, 0, 0, NULL); dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_center_button_text(dialog_ex, NULL); diff --git a/applications/infrared/scenes/infrared_scene_debug.c b/applications/infrared/scenes/infrared_scene_debug.c index ddb85644..dd0609b5 100644 --- a/applications/infrared/scenes/infrared_scene_debug.c +++ b/applications/infrared/scenes/infrared_scene_debug.c @@ -65,4 +65,5 @@ void infrared_scene_debug_on_exit(void* context) { InfraredWorker* worker = infrared->worker; infrared_worker_rx_stop(worker); infrared_worker_rx_enable_blink_on_receiving(worker, false); + infrared_worker_rx_set_received_signal_callback(worker, NULL, NULL); } diff --git a/applications/infrared/scenes/infrared_scene_edit_delete.c b/applications/infrared/scenes/infrared_scene_edit_delete.c index 0842cd61..4dfc054f 100644 --- a/applications/infrared/scenes/infrared_scene_edit_delete.c +++ b/applications/infrared/scenes/infrared_scene_edit_delete.c @@ -16,7 +16,7 @@ void infrared_scene_edit_delete_on_enter(void* context) { int32_t current_button_index = infrared->app_state.current_button_index; furi_assert(current_button_index != InfraredButtonIndexNone); - dialog_ex_set_header(dialog_ex, "Delete button?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Delete Button?", 64, 0, AlignCenter, AlignTop); InfraredRemoteButton* current_button = infrared_remote_get_button(remote, current_button_index); InfraredSignal* signal = infrared_remote_button_get_signal(current_button); @@ -45,7 +45,7 @@ void infrared_scene_edit_delete_on_enter(void* context) { } } else if(edit_target == InfraredEditTargetRemote) { - dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Delete Remote?", 64, 0, AlignCenter, AlignTop); infrared_text_store_set( infrared, 0, @@ -97,7 +97,7 @@ bool infrared_scene_edit_delete_on_event(void* context, SceneManagerEvent event) } else { const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } consumed = true; } diff --git a/applications/infrared/scenes/infrared_scene_edit_delete_done.c b/applications/infrared/scenes/infrared_scene_edit_delete_done.c index 4502abbd..49a299d2 100644 --- a/applications/infrared/scenes/infrared_scene_edit_delete_done.c +++ b/applications/infrared/scenes/infrared_scene_edit_delete_done.c @@ -28,8 +28,10 @@ bool infrared_scene_edit_delete_done_on_event(void* context, SceneManagerEvent e scene_manager, InfraredSceneRemote); } else if(edit_target == InfraredEditTargetRemote) { const uint32_t possible_scenes[] = {InfraredSceneStart, InfraredSceneRemoteList}; - scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + if(!scene_manager_search_and_switch_to_previous_scene_one_of( + scene_manager, possible_scenes, COUNT_OF(possible_scenes))) { + view_dispatcher_stop(infrared->view_dispatcher); + } } else { furi_assert(0); } diff --git a/applications/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/infrared/scenes/infrared_scene_edit_rename_done.c index 54b18ab6..6c7096e1 100644 --- a/applications/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/infrared/scenes/infrared_scene_edit_rename_done.c @@ -21,7 +21,10 @@ bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent e if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypePopupClosed) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + if(!scene_manager_search_and_switch_to_previous_scene( + infrared->scene_manager, InfraredSceneRemote)) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + } consumed = true; } } diff --git a/applications/infrared/scenes/infrared_scene_learn.c b/applications/infrared/scenes/infrared_scene_learn.c index d91b8676..0edb74ca 100644 --- a/applications/infrared/scenes/infrared_scene_learn.c +++ b/applications/infrared/scenes/infrared_scene_learn.c @@ -8,6 +8,7 @@ void infrared_scene_learn_on_enter(void* context) { infrared_worker_rx_set_received_signal_callback( worker, infrared_signal_received_callback, context); infrared_worker_rx_start(worker); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartRead); popup_set_icon(popup, 0, 32, &I_InfraredLearnShort_128x31); popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignCenter); @@ -22,10 +23,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { Infrared* infrared = context; bool consumed = false; - if(event.type == SceneManagerEventTypeTick) { - infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkRead); - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { + if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypeSignalReceived) { infrared_worker_rx_set_received_signal_callback(infrared->worker, NULL, NULL); infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); @@ -41,6 +39,7 @@ void infrared_scene_learn_on_exit(void* context) { Infrared* infrared = context; Popup* popup = infrared->popup; infrared_worker_rx_stop(infrared->worker); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); popup_set_icon(popup, 0, 0, NULL); popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter); } diff --git a/applications/infrared/scenes/infrared_scene_learn_done.c b/applications/infrared/scenes/infrared_scene_learn_done.c index eb6cced8..7d357171 100644 --- a/applications/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/infrared/scenes/infrared_scene_learn_done.c @@ -29,7 +29,10 @@ bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { if(event.event == InfraredCustomEventTypePopupClosed) { - scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + if(!scene_manager_search_and_switch_to_previous_scene( + infrared->scene_manager, InfraredSceneRemote)) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); + } consumed = true; } } diff --git a/applications/infrared/scenes/infrared_scene_learn_success.c b/applications/infrared/scenes/infrared_scene_learn_success.c index 1297ebcf..49e2f45f 100644 --- a/applications/infrared/scenes/infrared_scene_learn_success.c +++ b/applications/infrared/scenes/infrared_scene_learn_success.c @@ -23,8 +23,6 @@ void infrared_scene_learn_success_on_enter(void* context) { infrared_worker_tx_set_get_signal_callback( infrared->worker, infrared_worker_tx_get_signal_steady_callback, context); - infrared_worker_tx_set_signal_sent_callback( - infrared->worker, infrared_signal_sent_callback, context); if(infrared_signal_is_raw(signal)) { InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); @@ -104,6 +102,8 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even InfraredSceneLearnSuccess, InfraredSceneLearnSuccessStateSending); infrared_tx_start_received(infrared); + infrared_play_notification_message( + infrared, InfraredNotificationMessageBlinkStartSend); } consumed = true; } else if(event.event == DialogExReleaseCenter) { @@ -111,6 +111,7 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even scene_manager_set_scene_state( scene_manager, InfraredSceneLearnSuccess, InfraredSceneLearnSuccessStateIdle); infrared_tx_stop(infrared); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); } consumed = true; @@ -124,7 +125,7 @@ void infrared_scene_learn_success_on_exit(void* context) { Infrared* infrared = context; InfraredWorker* worker = infrared->worker; dialog_ex_reset(infrared->dialog_ex); + infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); infrared_worker_tx_set_get_signal_callback(worker, NULL, NULL); - infrared_worker_tx_set_signal_sent_callback(worker, NULL, NULL); } diff --git a/applications/infrared/scenes/infrared_scene_remote.c b/applications/infrared/scenes/infrared_scene_remote.c index 50f71488..bcac5881 100644 --- a/applications/infrared/scenes/infrared_scene_remote.c +++ b/applications/infrared/scenes/infrared_scene_remote.c @@ -33,8 +33,6 @@ void infrared_scene_remote_on_enter(void* context) { infrared_worker_tx_set_get_signal_callback( infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); - infrared_worker_tx_set_signal_sent_callback( - infrared->worker, infrared_signal_sent_callback, infrared); size_t button_count = infrared_remote_get_button_count(remote); for(size_t i = 0; i < button_count; ++i) { @@ -80,7 +78,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeBack) { const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; consumed = scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t)); + scene_manager, possible_scenes, COUNT_OF(possible_scenes)); } else if(event.type == SceneManagerEventTypeCustom) { const uint16_t custom_type = infrared_custom_event_get_type(event.event); const int16_t button_index = infrared_custom_event_get_value(event.event); @@ -112,7 +110,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { void infrared_scene_remote_on_exit(void* context) { Infrared* infrared = context; + infrared_tx_stop(infrared); infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL); - infrared_worker_tx_set_signal_sent_callback(infrared->worker, NULL, NULL); button_menu_reset(infrared->button_menu); } diff --git a/applications/infrared/scenes/infrared_scene_rpc.c b/applications/infrared/scenes/infrared_scene_rpc.c index 1d970f6a..cb5535fe 100644 --- a/applications/infrared/scenes/infrared_scene_rpc.c +++ b/applications/infrared/scenes/infrared_scene_rpc.c @@ -1,20 +1,28 @@ #include "../infrared_i.h" #include "gui/canvas.h" +typedef enum { + InfraredRpcStateIdle, + InfraredRpcStateLoaded, + InfraredRpcStateSending, +} InfraredRpcState; + void infrared_scene_rpc_on_enter(void* context) { Infrared* infrared = context; Popup* popup = infrared->popup; - popup_set_header(popup, "Infrared", 82, 28, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); + popup_set_header(popup, "Infrared", 89, 42, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); - popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon + popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); popup_set_context(popup, context); popup_set_callback(popup, infrared_popup_closed_callback); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); + scene_manager_set_scene_state(infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateIdle); + notification_message(infrared->notifications, &sequence_display_backlight_on); } @@ -24,6 +32,8 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; + InfraredRpcState state = + scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc); if(event.event == InfraredCustomEventTypeBackPressed) { view_dispatcher_stop(infrared->view_dispatcher); } else if(event.event == InfraredCustomEventTypePopupClosed) { @@ -31,41 +41,51 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { } else if(event.event == InfraredCustomEventTypeRpcLoad) { bool result = false; const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); - if(arg) { + if(arg && (state == InfraredRpcStateIdle)) { string_set_str(infrared->file_path, arg); result = infrared_remote_load(infrared->remote, infrared->file_path); infrared_worker_tx_set_get_signal_callback( infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); - infrared_worker_tx_set_signal_sent_callback( - infrared->worker, infrared_signal_sent_callback, infrared); + if(result) { + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); + } } const char* remote_name = infrared_remote_get_name(infrared->remote); infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); popup_set_text( - infrared->popup, infrared->text_store[0], 82, 32, AlignCenter, AlignTop); + infrared->popup, infrared->text_store[0], 89, 44, AlignCenter, AlignTop); rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result); } else if(event.event == InfraredCustomEventTypeRpcButtonPress) { bool result = false; const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); - if(arg) { + if(arg && (state == InfraredRpcStateLoaded)) { size_t button_index = 0; if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { infrared_tx_start_button_index(infrared, button_index); result = true; + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateSending); } } rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { - infrared_tx_stop(infrared); - rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, true); + bool result = false; + if(state == InfraredRpcStateSending) { + infrared_tx_stop(infrared); + result = true; + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); + } + rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); } else if(event.event == InfraredCustomEventTypeRpcExit) { + scene_manager_stop(infrared->scene_manager); view_dispatcher_stop(infrared->view_dispatcher); rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true); } else if(event.event == InfraredCustomEventTypeRpcSessionClose) { - rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); - infrared->rpc_ctx = NULL; + scene_manager_stop(infrared->scene_manager); view_dispatcher_stop(infrared->view_dispatcher); } } @@ -74,5 +94,9 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { void infrared_scene_rpc_on_exit(void* context) { Infrared* infrared = context; + if(scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneRpc) == + InfraredRpcStateSending) { + infrared_tx_stop(infrared); + } popup_reset(infrared->popup); } diff --git a/applications/input/input.c b/applications/input/input.c index 27e7bf21..7b8433ae 100644 --- a/applications/input/input.c +++ b/applications/input/input.c @@ -64,7 +64,8 @@ const char* input_get_type_name(InputType type) { return "Unknown"; } -int32_t input_srv() { +int32_t input_srv(void* p) { + UNUSED(p); input = malloc(sizeof(Input)); input->thread_id = furi_thread_get_current_id(); input->event_pubsub = furi_pubsub_alloc(); diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp index 29e99b74..f1a575de 100644 --- a/applications/lfrfid/lfrfid_app.cpp +++ b/applications/lfrfid/lfrfid_app.cpp @@ -56,6 +56,9 @@ static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { LfRfidApp::Event event; event.type = LfRfidApp::EventType::RpcSessionClose; app->view_controller.send_event(&event); + // Detach RPC + rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); + app->rpc_ctx = NULL; } else if(rpc_event == RpcAppEventAppExit) { LfRfidApp::Event event; event.type = LfRfidApp::EventType::Exit; @@ -74,22 +77,25 @@ void LfRfidApp::run(void* _args) { make_app_folder(); - if(strlen(args)) { + if(args && strlen(args)) { uint32_t rpc_ctx_ptr = 0; if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr; rpc_system_app_set_callback(rpc_ctx, rpc_command_callback, this); rpc_system_app_send_started(rpc_ctx); + view_controller.attach_to_gui(ViewDispatcherTypeDesktop); scene_controller.add_scene(SceneType::Rpc, new LfRfidAppSceneRpc()); scene_controller.process(100, SceneType::Rpc); } else { string_set_str(file_path, args); load_key_data(file_path, &worker.key, true); + view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); scene_controller.process(100, SceneType::Emulate); } } else { + view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead()); scene_controller.add_scene(SceneType::RetryConfirm, new LfRfidAppSceneRetryConfirm()); diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h index b0d4c589..db022c9a 100644 --- a/applications/lfrfid/lfrfid_app.h +++ b/applications/lfrfid/lfrfid_app.h @@ -101,5 +101,4 @@ public: bool save_key_data(string_t path, RfidKey* key); void make_app_folder(); - //bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context); }; diff --git a/applications/lfrfid/lfrfid_cli.cpp b/applications/lfrfid/lfrfid_cli.cpp index 7d1bb6d4..732197e9 100644 --- a/applications/lfrfid/lfrfid_cli.cpp +++ b/applications/lfrfid/lfrfid_cli.cpp @@ -107,7 +107,7 @@ static void lfrfid_cli_write(Cli* cli, string_t args) { UNUSED(cli); UNUSED(args); // TODO implement rfid write - printf("Not implemented :(\r\n"); + printf("Not Implemented :(\r\n"); } static void lfrfid_cli_emulate(Cli* cli, string_t args) { diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp index 183361a0..cad4f17c 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp @@ -2,18 +2,6 @@ #include #include -static const NotificationSequence sequence_blink_start_magenta = { - &message_blink_start_10, - &message_blink_set_color_magenta, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) { string_init(data_string); diff --git a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp index bac0247d..be070b40 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp @@ -17,9 +17,9 @@ void LfRfidAppSceneExitConfirm::on_enter(LfRfidApp* app, bool /* need_restore */ auto line_1 = container->add(); auto line_2 = container->add(); - line_1->set_text("Exit to RFID menu?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); + line_1->set_text("Exit to RFID Menu?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); line_2->set_text( - "All unsaved data will be lost.", 64, 31, 0, AlignCenter, AlignBottom, FontSecondary); + "All unsaved data will be lost!", 64, 31, 0, AlignCenter, AlignBottom, FontSecondary); app->view_controller.switch_to(); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp index 39430b97..c1812232 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp @@ -17,9 +17,9 @@ void LfRfidAppSceneRetryConfirm::on_enter(LfRfidApp* app, bool /* need_restore * auto line_1 = container->add(); auto line_2 = container->add(); - line_1->set_text("Return to reading?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); + line_1->set_text("Return to Reading?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); line_2->set_text( - "All unsaved data will be lost.", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); + "All unsaved data will be lost!", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); app->view_controller.switch_to(); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp index a32982af..54a57c9a 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp @@ -3,24 +3,12 @@ #include #include -static const NotificationSequence sequence_blink_start_magenta = { - &message_blink_start_10, - &message_blink_set_color_magenta, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - void LfRfidAppSceneRpc::on_enter(LfRfidApp* app, bool /* need_restore */) { auto popup = app->view_controller.get(); - popup->set_header("LF RFID", 89, 30, AlignCenter, AlignTop); - popup->set_text("RPC mode", 89, 43, AlignCenter, AlignTop); - popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); + popup->set_header("LF RFID", 89, 42, AlignCenter, AlignBottom); + popup->set_text("RPC mode", 89, 44, AlignCenter, AlignTop); + popup->set_icon(0, 12, &I_RFIDDolphinSend_97x61); app->view_controller.switch_to(); @@ -39,33 +27,25 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { app->view_controller.send_event(&view_event); rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true); } else if(event->type == LfRfidApp::EventType::RpcSessionClose) { - // Detach RPC - rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); - app->rpc_ctx = NULL; - consumed = true; LfRfidApp::Event view_event; view_event.type = LfRfidApp::EventType::Back; app->view_controller.send_event(&view_event); - } else if(event->type == LfRfidApp::EventType::EmulateStart) { - auto popup = app->view_controller.get(); - consumed = true; - emulating = true; - - app->text_store.set("emulating\n%s", app->worker.key.get_name()); - popup->set_text(app->text_store.text, 89, 43, AlignCenter, AlignTop); - - notification_message(app->notification, &sequence_blink_start_magenta); } else if(event->type == LfRfidApp::EventType::RpcLoadFile) { const char* arg = rpc_system_app_get_data(app->rpc_ctx); + consumed = true; bool result = false; - if(arg) { + if(arg && !emulating) { string_set_str(app->file_path, arg); if(app->load_key_data(app->file_path, &(app->worker.key), false)) { - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::EmulateStart; - app->view_controller.send_event(&event); app->worker.start_emulate(); + emulating = true; + + auto popup = app->view_controller.get(); + app->text_store.set("emulating\n%s", app->worker.key.get_name()); + popup->set_text(app->text_store.text, 89, 44, AlignCenter, AlignTop); + + notification_message(app->notification, &sequence_blink_start_magenta); result = true; } } diff --git a/applications/lfrfid_debug/lfrfid_debug_app.cpp b/applications/lfrfid_debug/lfrfid_debug_app.cpp index 9cd9dcad..ef970e36 100644 --- a/applications/lfrfid_debug/lfrfid_debug_app.cpp +++ b/applications/lfrfid_debug/lfrfid_debug_app.cpp @@ -10,6 +10,7 @@ LfRfidDebugApp::~LfRfidDebugApp() { } void LfRfidDebugApp::run() { + view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); scene_controller.add_scene(SceneType::Start, new LfRfidDebugAppSceneStart()); scene_controller.add_scene(SceneType::TuneScene, new LfRfidDebugAppSceneTune()); scene_controller.process(100); diff --git a/applications/loader/application.fam b/applications/loader/application.fam index 16351f8d..c1ba4e54 100644 --- a/applications/loader/application.fam +++ b/applications/loader/application.fam @@ -5,6 +5,6 @@ App( entry_point="loader_srv", cdefines=["SRV_LOADER"], requires=["gui"], - stack_size=1 * 1024, + stack_size=2 * 1024, order=90, ) diff --git a/applications/music_player/music_player.c b/applications/music_player/music_player.c index ffdd2bea..121efa0f 100644 --- a/applications/music_player/music_player.c +++ b/applications/music_player/music_player.c @@ -300,7 +300,7 @@ int32_t music_player_app(void* p) { string_init(file_path); do { - if(p) { + if(p && strlen(p)) { string_cat_str(file_path, p); } else { string_set_str(file_path, MUSIC_PLAYER_APP_PATH_FOLDER); diff --git a/applications/nfc/application.fam b/applications/nfc/application.fam index f43ed314..ce21bfd2 100644 --- a/applications/nfc/application.fam +++ b/applications/nfc/application.fam @@ -10,7 +10,7 @@ App( ], provides=["nfc_start"], icon="A_NFC_14", - stack_size=4 * 1024, + stack_size=5 * 1024, order=30, ) diff --git a/applications/nfc/helpers/nfc_generators.c b/applications/nfc/helpers/nfc_generators.c index 066ac9d1..3ec78a12 100644 --- a/applications/nfc/helpers/nfc_generators.c +++ b/applications/nfc/helpers/nfc_generators.c @@ -55,6 +55,7 @@ static void nfc_generate_mf_ul_orig(NfcDeviceData* data) { MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeUnknown; mful->data_size = 16 * 4; + mful->data_read = mful->data_size; nfc_generate_mf_ul_copy_uid_with_bcc(data); // TODO: what's internal byte on page 2? memset(&mful->data[4 * 4], 0xFF, 4); @@ -67,6 +68,7 @@ static void nfc_generate_mf_ul_ntag203(NfcDeviceData* data) { MfUltralightData* mful = &data->mf_ul_data; mful->type = MfUltralightTypeNTAG203; mful->data_size = 42 * 4; + mful->data_read = mful->data_size; nfc_generate_mf_ul_copy_uid_with_bcc(data); mful->data[9] = 0x48; // Internal byte memcpy(&mful->data[3 * 4], default_data_ntag203, sizeof(default_data_ntag203)); @@ -78,6 +80,7 @@ static void nfc_generate_mf_ul_with_config_common(NfcDeviceData* data, uint8_t n MfUltralightData* mful = &data->mf_ul_data; mful->data_size = num_pages * 4; + mful->data_read = mful->data_size; nfc_generate_mf_ul_copy_uid_with_bcc(data); uint16_t config_index = (num_pages - 4) * 4; mful->data[config_index] = 0x04; // STRG_MOD_EN @@ -180,6 +183,7 @@ static void mful->type = type; memcpy(&mful->version, version_bytes_ntag_i2c, sizeof(version_bytes_ntag_i2c)); mful->data_size = num_pages * 4; + mful->data_read = mful->data_size; memcpy(mful->data, data->nfc_data.uid, data->nfc_data.uid_len); mful->data[7] = data->nfc_data.sak; mful->data[8] = data->nfc_data.atqa[0]; diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c index 32e74e8f..6b8843db 100644 --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -21,6 +21,8 @@ static void nfc_rpc_command_callback(RpcAppSystemEvent event, void* context) { if(event == RpcAppEventSessionClose) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose); + rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); + nfc->rpc_ctx = NULL; } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } else if(event == RpcAppEventLoadFile) { @@ -199,18 +201,6 @@ void nfc_text_store_clear(Nfc* nfc) { memset(nfc->text_store, 0, sizeof(nfc->text_store)); } -static const NotificationSequence sequence_blink_start_blue = { - &message_blink_start_10, - &message_blink_set_color_blue, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - void nfc_blink_start(Nfc* nfc) { notification_message(nfc->notifications, &sequence_blink_start_blue); } @@ -238,7 +228,7 @@ int32_t nfc_app(void* p) { char* args = p; // Check argument and run corresponding scene - if((*args != '\0')) { + if(args && strlen(args)) { nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h old mode 100755 new mode 100644 diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index de8de6b4..2b5cb5cf 100755 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -15,11 +15,17 @@ ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate) +ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) +ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) +ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) +ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) +ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu) ADD_SCENE(nfc, mf_desfire_data, MfDesfireData) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess) +ADD_SCENE(nfc, mf_classic_info, MfClassicInfo) ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu) ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate) ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys) diff --git a/applications/nfc/scenes/nfc_scene_detect_reader.c b/applications/nfc/scenes/nfc_scene_detect_reader.c index 4639735d..f734f04c 100644 --- a/applications/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/nfc/scenes/nfc_scene_detect_reader.c @@ -37,7 +37,7 @@ static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) widget_add_icon_element(widget, 0, 14, &I_Reader_detect); widget_add_string_element( - widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold near reader"); + widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader"); widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating..."); if(data_received) { diff --git a/applications/nfc/scenes/nfc_scene_exit_confirm.c b/applications/nfc/scenes/nfc_scene_exit_confirm.c index 24942bcd..b22dd42a 100644 --- a/applications/nfc/scenes/nfc_scene_exit_confirm.c +++ b/applications/nfc/scenes/nfc_scene_exit_confirm.c @@ -12,9 +12,9 @@ void nfc_scene_exit_confirm_on_enter(void* context) { dialog_ex_set_left_button_text(dialog_ex, "Exit"); dialog_ex_set_right_button_text(dialog_ex, "Stay"); - dialog_ex_set_header(dialog_ex, "Exit to NFC menu?", 64, 11, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Exit to NFC Menu?", 64, 11, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, "All unsaved data\nwill be lost.", 64, 25, AlignCenter, AlignTop); + dialog_ex, "All unsaved data\nwill be lost!", 64, 25, AlignCenter, AlignTop); dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_exit_confirm_dialog_callback); diff --git a/applications/nfc/scenes/nfc_scene_extra_actions.c b/applications/nfc/scenes/nfc_scene_extra_actions.c index 823a4ace..43e49e5a 100644 --- a/applications/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/nfc/scenes/nfc_scene_extra_actions.c @@ -2,6 +2,7 @@ enum SubmenuIndex { SubmenuIndexMfClassicKeys, + SubmenuIndexMfUltralightUnlock, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -20,6 +21,12 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfClassicKeys, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_add_item( + submenu, + "Unlock NTAG/Ultralight", + SubmenuIndexMfUltralightUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } @@ -35,6 +42,8 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } consumed = true; + } else if(event.event == SubmenuIndexMfUltralightUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index 0736f0f1..d821c182 100644 --- a/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -1,5 +1,7 @@ #include "../nfc_i.h" +#define TAG "NfcMfClassicDictAttack" + typedef enum { DictAttackStateIdle, DictAttackStateUserDictInProgress, @@ -32,7 +34,9 @@ static void nfc_scene_mf_classic_dict_attack_update_view(Nfc* nfc) { static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackState state) { MfClassicData* data = &nfc->dev->dev_data.mf_classic_data; + NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; NfcWorkerState worker_state = NfcWorkerStateReady; + MfClassicDict* dict = NULL; // Identify scene state if(state == DictAttackStateIdle) { @@ -47,16 +51,36 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(Nfc* nfc, DictAttackSt // Setup view if(state == DictAttackStateUserDictInProgress) { - worker_state = NfcWorkerStateMfClassicUserDictAttack; + worker_state = NfcWorkerStateMfClassicDictAttack; dict_attack_set_header(nfc->dict_attack, "Mf Classic User Dict."); - } else if(state == DictAttackStateFlipperDictInProgress) { - worker_state = NfcWorkerStateMfClassicFlipperDictAttack; - dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); + dict = mf_classic_dict_alloc(MfClassicDictTypeUser); + + // If failed to load user dictionary - try flipper dictionary + if(!dict) { + FURI_LOG_E(TAG, "User dictionary not found"); + state = DictAttackStateFlipperDictInProgress; + } } + if(state == DictAttackStateFlipperDictInProgress) { + worker_state = NfcWorkerStateMfClassicDictAttack; + dict_attack_set_header(nfc->dict_attack, "Mf Classic Flipper Dict."); + dict = mf_classic_dict_alloc(MfClassicDictTypeFlipper); + if(!dict) { + FURI_LOG_E(TAG, "Flipper dictionary not found"); + // Pass through to let worker handle the failure + } + } + // Free previous dictionary + if(dict_attack_data->dict) { + mf_classic_dict_free(dict_attack_data->dict); + } + dict_attack_data->dict = dict; scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicDictAttack, state); dict_attack_set_callback(nfc->dict_attack, nfc_dict_attack_dict_attack_result_callback, nfc); dict_attack_set_current_sector(nfc->dict_attack, 0); dict_attack_set_card_detected(nfc->dict_attack, data->type); + dict_attack_set_total_dict_keys( + nfc->dict_attack, dict ? mf_classic_dict_get_total_keys(dict) : 0); nfc_scene_mf_classic_dict_attack_update_view(nfc); nfc_worker_start( nfc->worker, worker_state, &nfc->dev->dev_data, nfc_dict_attack_worker_callback, nfc); @@ -112,6 +136,10 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent nfc_scene_mf_classic_dict_attack_update_view(nfc); dict_attack_inc_current_sector(nfc->dict_attack); consumed = true; + } else if(event.event == NfcWorkerEventNewDictKeyBatch) { + nfc_scene_mf_classic_dict_attack_update_view(nfc); + dict_attack_inc_current_dict_key(nfc->dict_attack, NFC_DICT_KEY_BATCH_SIZE); + consumed = true; } else if(event.event == NfcCustomEventDictAttackSkip) { if(state == DictAttackStateUserDictInProgress) { nfc_worker_stop(nfc->worker); @@ -130,8 +158,13 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent void nfc_scene_mf_classic_dict_attack_on_exit(void* context) { Nfc* nfc = context; + NfcMfClassicDictAttackData* dict_attack_data = &nfc->dev->dev_data.mf_classic_dict_attack_data; // Stop worker nfc_worker_stop(nfc->worker); + if(dict_attack_data->dict) { + mf_classic_dict_free(dict_attack_data->dict); + dict_attack_data->dict = NULL; + } dict_attack_reset(nfc->dict_attack); nfc_blink_stop(nfc); } diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_info.c b/applications/nfc/scenes/nfc_scene_mf_classic_info.c new file mode 100644 index 00000000..b658dfa4 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_classic_info.c @@ -0,0 +1,72 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_classic_info_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_classic_info_on_enter(void* context) { + Nfc* nfc = context; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + MfClassicData* mf_data = &dev_data->mf_classic_data; + string_t str_tmp; + string_init(str_tmp); + + // Setup view + Widget* widget = nfc->widget; + + widget_add_string_element( + widget, 0, 0, AlignLeft, AlignTop, FontSecondary, mf_classic_get_type_str(mf_data->type)); + widget_add_string_element( + widget, 0, 11, AlignLeft, AlignTop, FontSecondary, "ISO 14443-3 (Type A)"); + string_printf(str_tmp, "UID:"); + for(size_t i = 0; i < dev_data->nfc_data.uid_len; i++) { + string_cat_printf(str_tmp, " %02X", dev_data->nfc_data.uid[i]); + } + widget_add_string_element( + widget, 0, 22, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + string_printf( + str_tmp, + "ATQA: %02X %02X SAK: %02X", + dev_data->nfc_data.atqa[0], + dev_data->nfc_data.atqa[1], + dev_data->nfc_data.sak); + widget_add_string_element( + widget, 0, 33, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + uint8_t sectors_total = mf_classic_get_total_sectors_num(mf_data->type); + uint8_t keys_total = sectors_total * 2; + uint8_t keys_found = 0; + uint8_t sectors_read = 0; + mf_classic_get_read_sectors_and_keys(mf_data, §ors_read, &keys_found); + string_printf(str_tmp, "Keys Found: %d/%d", keys_found, keys_total); + widget_add_string_element( + widget, 0, 44, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + string_printf(str_tmp, "Sectors Read: %d/%d", sectors_read, sectors_total); + widget_add_string_element( + widget, 0, 55, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp)); + + string_clear(str_tmp); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_classic_info_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_mf_classic_info_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/nfc/scenes/nfc_scene_mf_classic_menu.c index a5370ce7..6ee0ad86 100644 --- a/applications/nfc/scenes/nfc_scene_mf_classic_menu.c +++ b/applications/nfc/scenes/nfc_scene_mf_classic_menu.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexSave, SubmenuIndexEmulate, + SubmenuIndexInfo, }; void nfc_scene_mf_classic_menu_submenu_callback(void* context, uint32_t index) { @@ -19,6 +20,9 @@ void nfc_scene_mf_classic_menu_on_enter(void* context) { submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc); submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_classic_menu_submenu_callback, nfc); + submenu_set_selected_item( nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicMenu)); @@ -43,6 +47,11 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicInfo); + consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { consumed = scene_manager_previous_scene(nfc->scene_manager); diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c new file mode 100644 index 00000000..089187d5 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c @@ -0,0 +1,44 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_ultralight_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_mf_ultralight_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter the password in hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_mf_ultralight_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_mf_ultralight_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_mf_ultralight_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c index e3d256a5..9174a8b1 100644 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -1,6 +1,7 @@ #include "../nfc_i.h" enum SubmenuIndex { + SubmenuIndexUnlock, SubmenuIndexSave, SubmenuIndexEmulate, }; @@ -14,7 +15,16 @@ void nfc_scene_mf_ultralight_menu_submenu_callback(void* context, uint32_t index void nfc_scene_mf_ultralight_menu_on_enter(void* context) { Nfc* nfc = context; Submenu* submenu = nfc->submenu; + MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; + if(data->data_read != data->data_size) { + submenu_add_item( + submenu, + "Unlock With Password", + SubmenuIndexUnlock, + nfc_scene_mf_ultralight_menu_submenu_callback, + nfc); + } submenu_add_item( submenu, "Save", SubmenuIndexSave, nfc_scene_mf_ultralight_menu_submenu_callback, nfc); submenu_add_item( @@ -35,19 +45,20 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightMenu, SubmenuIndexSave); nfc->dev->format = NfcDeviceSaveFormatMifareUl; // Clear device name nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; } else if(event.event == SubmenuIndexEmulate) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightMenu, SubmenuIndexEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); consumed = true; + } else if(event.event == SubmenuIndexUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + consumed = true; } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu, event.event); + } else if(event.type == SceneManagerEventTypeBack) { consumed = scene_manager_previous_scene(nfc->scene_manager); } diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c new file mode 100644 index 00000000..968157bd --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c @@ -0,0 +1,107 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneMfUlReadStateIdle, + NfcSceneMfUlReadStateDetecting, + NfcSceneMfUlReadStateReading, + NfcSceneMfUlReadStateNotSupportedCard, +} NfcSceneMfUlReadState; + +bool nfc_scene_mf_ultralight_read_auth_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + + if(event == NfcWorkerEventMfUltralightPassKey) { + memcpy(nfc->dev->dev_data.mf_ul_data.auth_key, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState state) { + uint32_t curr_state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + if(curr_state != state) { + if(state == NfcSceneMfUlReadStateDetecting) { + popup_reset(nfc->popup); + popup_set_text( + nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual); + } else if(state == NfcSceneMfUlReadStateReading) { + popup_reset(nfc->popup); + popup_set_header( + nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); + popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); + } else if(state == NfcSceneMfUlReadStateNotSupportedCard) { + popup_reset(nfc->popup); + popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop); + popup_set_text( + nfc->popup, + "Only MIFARE\nUltralight & NTAG\n are supported", + 4, + 22, + AlignLeft, + AlignTop); + popup_set_icon(nfc->popup, 73, 17, &I_DolphinFirstStart8_56x51); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state); + } +} + +void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcRead); + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateReadMfUltralightReadAuth, + &nfc->dev->dev_data, + nfc_scene_mf_ultralight_read_auth_worker_callback, + nfc); + + nfc_blink_start(nfc); +} + +bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if((event.event == NfcWorkerEventSuccess) || (event.event == NfcWorkerEventFail)) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuthResult); + consumed = true; + } else if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateReading); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state(nfc, NfcSceneMfUlReadStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_mf_ultralight_read_auth_set_state( + nfc, NfcSceneMfUlReadStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } + return consumed; +} + +void nfc_scene_mf_ultralight_read_auth_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneMfUltralightReadAuth, NfcSceneMfUlReadStateIdle); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c new file mode 100644 index 00000000..b9421545 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth_result.c @@ -0,0 +1,98 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mf_ultralight_read_auth_result_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) { + Nfc* nfc = context; + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + // Setup dialog view + FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); + Widget* widget = nfc->widget; + string_t temp_str; + string_init(temp_str); + + if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { + widget_add_string_element( + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); + } else { + widget_add_string_element( + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!"); + } + string_set_str(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + widget_add_string_element( + widget, 0, 17, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + if(mf_ul_data->auth_success) { + string_printf( + temp_str, + "Password: %02X %02X %02X %02X", + config_pages->auth_data.pwd.raw[0], + config_pages->auth_data.pwd.raw[1], + config_pages->auth_data.pwd.raw[2], + config_pages->auth_data.pwd.raw[3]); + widget_add_string_element( + widget, 0, 28, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + string_printf( + temp_str, + "PACK: %02X %02X", + config_pages->auth_data.pack.raw[0], + config_pages->auth_data.pack.raw[1]); + widget_add_string_element( + widget, 0, 39, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + } + string_printf( + temp_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); + widget_add_string_element( + widget, 0, 50, AlignLeft, AlignTop, FontSecondary, string_get_cstr(temp_str)); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Save", + nfc_scene_mf_ultralight_read_auth_result_widget_callback, + nfc); + + string_clear(temp_str); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + nfc->dev->format = NfcDeviceSaveFormatMifareUl; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + } + + return consumed; +} + +void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) { + Nfc* nfc = context; + + // Clean views + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c old mode 100755 new mode 100644 index 8b1b6c00..65750b96 --- a/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c @@ -1,51 +1,67 @@ #include "../nfc_i.h" #include -#define NFC_SCENE_READ_SUCCESS_SHIFT " " - enum { - ReadMifareUlStateShowUID, + ReadMifareUlStateShowInfo, ReadMifareUlStateShowData, }; -void nfc_scene_mf_ultralight_read_success_dialog_callback(DialogExResult result, void* context) { +void nfc_scene_mf_ultralight_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } } void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { Nfc* nfc = context; DOLPHIN_DEED(DolphinDeedNfcReadSuccess); - // Setup dialog view + // Setup widget view FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; - DialogEx* dialog_ex = nfc->dialog_ex; - dialog_ex_set_left_button_text(dialog_ex, "Retry"); - dialog_ex_set_right_button_text(dialog_ex, "More"); - dialog_ex_set_center_button_text(dialog_ex, "Data"); - dialog_ex_set_header( - dialog_ex, nfc_mf_ul_type(mf_ul_data->type, true), 64, 8, AlignCenter, AlignCenter); - dialog_ex_set_icon(dialog_ex, 8, 13, &I_Medium_chip_22x21); - // Display UID - nfc_text_store_set( - nfc, - NFC_SCENE_READ_SUCCESS_SHIFT "ATQA: %02X%02X\n" NFC_SCENE_READ_SUCCESS_SHIFT - "SAK: %02X\nUID: %02X %02X %02X %02X %02X %02X %02X", - data->atqa[0], - data->atqa[1], - data->sak, - data->uid[0], - data->uid[1], - data->uid[2], - data->uid[3], - data->uid[4], - data->uid[5], - data->uid[6]); - dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 16, AlignLeft, AlignTop); - dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_read_success_dialog_callback); + Widget* widget = nfc->widget; + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + widget_add_button_element( + widget, + GuiButtonTypeCenter, + "Data", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + widget_add_button_element( + widget, + GuiButtonTypeRight, + "More", + nfc_scene_mf_ultralight_read_success_widget_callback, + nfc); + + widget_add_string_element( + widget, 0, 0, AlignLeft, AlignTop, FontSecondary, nfc_mf_ul_type(mf_ul_data->type, true)); + string_t data_str; + string_init_printf(data_str, "UID:"); + for(size_t i = 0; i < data->uid_len; i++) { + string_cat_printf(data_str, " %02X", data->uid[i]); + } + widget_add_string_element( + widget, 0, 13, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); + string_printf( + data_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4); + widget_add_string_element( + widget, 0, 24, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str)); + if(mf_ul_data->data_read != mf_ul_data->data_size) { + widget_add_string_element( + widget, 0, 35, AlignLeft, AlignTop, FontSecondary, "Password-protected pages!"); + } + string_clear(data_str); // Setup TextBox view TextBox* text_box = nfc->text_box; @@ -60,8 +76,8 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { text_box_set_text(text_box, string_get_cstr(nfc->text_box_store)); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowUID); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); + nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowInfo); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); } bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEvent event) { @@ -71,13 +87,13 @@ bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEv scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); if(event.type == SceneManagerEventTypeCustom) { - if(state == ReadMifareUlStateShowUID && event.event == DialogExResultLeft) { + if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeLeft) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); consumed = true; - } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultRight) { + } else if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeRight) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu); consumed = true; - } else if(state == ReadMifareUlStateShowUID && event.event == DialogExResultCenter) { + } else if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeCenter) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowData); @@ -85,9 +101,9 @@ bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEv } } else if(event.type == SceneManagerEventTypeBack) { if(state == ReadMifareUlStateShowData) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowUID); + nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowInfo); consumed = true; } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); @@ -102,7 +118,7 @@ void nfc_scene_mf_ultralight_read_success_on_exit(void* context) { Nfc* nfc = context; // Clean views - dialog_ex_reset(nfc->dialog_ex); + widget_reset(nfc->widget); text_box_reset(nfc->text_box); string_reset(nfc->text_box_store); } diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c new file mode 100644 index 00000000..648aa31d --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -0,0 +1,70 @@ +#include "../nfc_i.h" + +enum SubmenuIndex { + SubmenuIndexMfUlUnlockMenuManual, + SubmenuIndexMfUlUnlockMenuAmeebo, + SubmenuIndexMfUlUnlockMenuXiaomi, +}; + +void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); + submenu_add_item( + submenu, + "Enter Password Manually", + SubmenuIndexMfUlUnlockMenuManual, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As Ameebo", + SubmenuIndexMfUlUnlockMenuAmeebo, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As Xiaomi", + SubmenuIndexMfUlUnlockMenuXiaomi, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMfUlUnlockMenuManual) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuAmeebo) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAmeebo; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } else if(event.event == SubmenuIndexMfUlUnlockMenuXiaomi) { + nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); + } + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c new file mode 100644 index 00000000..00df98e7 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -0,0 +1,45 @@ +#include "../nfc_i.h" + +void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result, void* context) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); +} + +void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { + Nfc* nfc = context; + DialogEx* dialog_ex = nfc->dialog_ex; + + dialog_ex_set_context(dialog_ex, nfc); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); + + dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); + dialog_ex_set_text( + dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); + dialog_ex_set_icon(dialog_ex, 73, 17, &I_DolphinFirstStart8_56x51); + dialog_ex_set_center_button_text(dialog_ex, "OK"); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); +} + +bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DialogExResultCenter) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) { + Nfc* nfc = context; + + dialog_ex_reset(nfc->dialog_ex); + submenu_reset(nfc->submenu); +} diff --git a/applications/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/nfc/scenes/nfc_scene_restore_original_confirm.c index 70d161dd..2c12749d 100644 --- a/applications/nfc/scenes/nfc_scene_restore_original_confirm.c +++ b/applications/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -10,7 +10,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { Nfc* nfc = context; DialogEx* dialog_ex = nfc->dialog_ex; - dialog_ex_set_header(dialog_ex, "Restore card data?", 64, 0, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring); dialog_ex_set_text( dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); diff --git a/applications/nfc/scenes/nfc_scene_retry_confirm.c b/applications/nfc/scenes/nfc_scene_retry_confirm.c index f7b3991e..366582ea 100644 --- a/applications/nfc/scenes/nfc_scene_retry_confirm.c +++ b/applications/nfc/scenes/nfc_scene_retry_confirm.c @@ -12,9 +12,9 @@ void nfc_scene_retry_confirm_on_enter(void* context) { dialog_ex_set_left_button_text(dialog_ex, "Retry"); dialog_ex_set_right_button_text(dialog_ex, "Stay"); - dialog_ex_set_header(dialog_ex, "Retry reading?", 64, 11, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Retry Reading?", 64, 11, AlignCenter, AlignTop); dialog_ex_set_text( - dialog_ex, "All unsaved data will be\nlost.", 64, 25, AlignCenter, AlignTop); + dialog_ex, "All unsaved data will be\nlost!", 64, 25, AlignCenter, AlignTop); dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_result_callback(dialog_ex, nfc_scene_retry_confirm_dialog_callback); diff --git a/applications/nfc/scenes/nfc_scene_rpc.c b/applications/nfc/scenes/nfc_scene_rpc.c index 94beccc6..7a9eb450 100644 --- a/applications/nfc/scenes/nfc_scene_rpc.c +++ b/applications/nfc/scenes/nfc_scene_rpc.c @@ -4,10 +4,10 @@ void nfc_scene_rpc_on_enter(void* context) { Nfc* nfc = context; Popup* popup = nfc->popup; - popup_set_header(popup, "NFC", 82, 28, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); + popup_set_header(popup, "NFC", 89, 42, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); - popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon + popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); @@ -31,13 +31,11 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == NfcCustomEventViewExit) { rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventAppExit, true); + scene_manager_stop(nfc->scene_manager); view_dispatcher_stop(nfc->view_dispatcher); - nfc_blink_stop(nfc); } else if(event.event == NfcCustomEventRpcSessionClose) { - rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); - nfc->rpc_ctx = NULL; + scene_manager_stop(nfc->scene_manager); view_dispatcher_stop(nfc->view_dispatcher); - nfc_blink_stop(nfc); } else if(event.event == NfcCustomEventRpcLoad) { bool result = false; const char* arg = rpc_system_app_get_data(nfc->rpc_ctx); @@ -66,7 +64,7 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { nfc_blink_start(nfc); nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name); - popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop); + popup_set_text(popup, nfc->text_store, 89, 44, AlignCenter, AlignTop); } } diff --git a/applications/nfc/scenes/nfc_scene_saved_menu.c b/applications/nfc/scenes/nfc_scene_saved_menu.c index 7c390f0b..e6b08e71 100644 --- a/applications/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/nfc/scenes/nfc_scene_saved_menu.c @@ -2,6 +2,7 @@ enum SubmenuIndex { SubmenuIndexEmulate, + SubmenuIndexEditUid, SubmenuIndexRename, SubmenuIndexDelete, SubmenuIndexInfo, @@ -27,6 +28,14 @@ void nfc_scene_saved_menu_on_enter(void* context) { SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); + if(nfc->dev->dev_data.protocol == NfcDeviceProtocolUnknown) { + submenu_add_item( + submenu, + "Edit UID", + SubmenuIndexEditUid, + nfc_scene_saved_menu_submenu_callback, + nfc); + } } else if( nfc->dev->format == NfcDeviceSaveFormatMifareUl || nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { @@ -71,6 +80,9 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexRename) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; + } else if(event.event == SubmenuIndexEditUid) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; } else if(event.event == SubmenuIndexDelete) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete); consumed = true; diff --git a/applications/nfc/scenes/nfc_scene_set_uid.c b/applications/nfc/scenes/nfc_scene_set_uid.c index 6fe807ce..0ff28971 100755 --- a/applications/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/nfc/scenes/nfc_scene_set_uid.c @@ -31,8 +31,16 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { DOLPHIN_DEED(DolphinDeedNfcAdd); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { + nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; + if(nfc_device_save(nfc->dev, nfc->dev->dev_name)) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); + consumed = true; + } + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } } } return consumed; diff --git a/applications/nfc/views/dict_attack.c b/applications/nfc/views/dict_attack.c index 25690005..b4674fd3 100644 --- a/applications/nfc/views/dict_attack.c +++ b/applications/nfc/views/dict_attack.c @@ -23,6 +23,8 @@ typedef struct { uint8_t sector_current; uint8_t keys_total; uint8_t keys_found; + uint16_t dict_keys_total; + uint16_t dict_keys_current; } DictAttackViewModel; static void dict_attack_draw_callback(Canvas* canvas, void* model) { @@ -38,8 +40,15 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, string_get_cstr(m->header)); canvas_set_font(canvas, FontSecondary); - float progress = - m->sectors_total == 0 ? 0 : (float)(m->sector_current) / (float)(m->sectors_total); + float dict_progress = m->dict_keys_total == 0 ? + 0 : + (float)(m->dict_keys_current) / (float)(m->dict_keys_total); + float progress = m->sectors_total == 0 ? 0 : + ((float)(m->sector_current) + dict_progress) / + (float)(m->sectors_total); + if(progress > 1.0) { + progress = 1.0; + } elements_progress_bar(canvas, 5, 15, 120, progress); canvas_set_font(canvas, FontSecondary); snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); @@ -100,6 +109,8 @@ void dict_attack_reset(DictAttack* dict_attack) { model->sector_current = 0; model->keys_total = 0; model->keys_found = 0; + model->dict_keys_total = 0; + model->dict_keys_current = 0; string_reset(model->header); return false; }); @@ -171,6 +182,7 @@ void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { with_view_model( dict_attack->view, (DictAttackViewModel * model) { model->sector_current = curr_sec; + model->dict_keys_current = 0; return true; }); } @@ -181,6 +193,7 @@ void dict_attack_inc_current_sector(DictAttack* dict_attack) { dict_attack->view, (DictAttackViewModel * model) { if(model->sector_current < model->sectors_total) { model->sector_current++; + model->dict_keys_current = 0; } return true; }); @@ -196,3 +209,23 @@ void dict_attack_inc_keys_found(DictAttack* dict_attack) { return true; }); } + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + model->dict_keys_total = dict_keys_total; + return true; + }); +} + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { + furi_assert(dict_attack); + with_view_model( + dict_attack->view, (DictAttackViewModel * model) { + if(model->dict_keys_current + keys_tried < model->dict_keys_total) { + model->dict_keys_current += keys_tried; + } + return true; + }); +} diff --git a/applications/nfc/views/dict_attack.h b/applications/nfc/views/dict_attack.h index 3f557b19..684f17f0 100644 --- a/applications/nfc/views/dict_attack.h +++ b/applications/nfc/views/dict_attack.h @@ -34,3 +34,7 @@ void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); void dict_attack_inc_current_sector(DictAttack* dict_attack); void dict_attack_inc_keys_found(DictAttack* dict_attack); + +void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); + +void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); diff --git a/applications/notification/notification_messages.c b/applications/notification/notification_messages.c index b469cb5f..d795c55d 100644 --- a/applications/notification/notification_messages.c +++ b/applications/notification/notification_messages.c @@ -431,6 +431,54 @@ const NotificationSequence sequence_blink_white_100 = { NULL, }; +// Hardware blink +const NotificationSequence sequence_blink_start_blue = { + &message_blink_start_10, + &message_blink_set_color_blue, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_red = { + &message_blink_start_10, + &message_blink_set_color_red, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_green = { + &message_blink_start_10, + &message_blink_set_color_green, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_yellow = { + &message_blink_start_10, + &message_blink_set_color_yellow, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_cyan = { + &message_blink_start_10, + &message_blink_set_color_cyan, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_start_magenta = { + &message_blink_start_10, + &message_blink_set_color_magenta, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_blink_stop = { + &message_blink_stop, + NULL, +}; + //General const NotificationSequence sequence_single_vibro = { &message_vibro_on, diff --git a/applications/notification/notification_messages.h b/applications/notification/notification_messages.h index 92a105f6..10079691 100644 --- a/applications/notification/notification_messages.h +++ b/applications/notification/notification_messages.h @@ -122,6 +122,15 @@ extern const NotificationSequence sequence_blink_cyan_100; extern const NotificationSequence sequence_blink_magenta_100; extern const NotificationSequence sequence_blink_white_100; +// Hardware blink +extern const NotificationSequence sequence_blink_start_blue; +extern const NotificationSequence sequence_blink_start_red; +extern const NotificationSequence sequence_blink_start_green; +extern const NotificationSequence sequence_blink_start_yellow; +extern const NotificationSequence sequence_blink_start_cyan; +extern const NotificationSequence sequence_blink_start_magenta; +extern const NotificationSequence sequence_blink_stop; + // General extern const NotificationSequence sequence_single_vibro; extern const NotificationSequence sequence_double_vibro; diff --git a/applications/picopass/scenes/picopass_scene_read_card.c b/applications/picopass/scenes/picopass_scene_read_card.c index add05e47..0867898a 100644 --- a/applications/picopass/scenes/picopass_scene_read_card.c +++ b/applications/picopass/scenes/picopass_scene_read_card.c @@ -13,7 +13,7 @@ void picopass_scene_read_card_on_enter(void* context) { // Setup view Popup* popup = picopass->popup; - popup_set_header(popup, "Detecting\npicopass card", 70, 34, AlignLeft, AlignTop); + popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop); popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); // Start worker diff --git a/applications/power/battery_test_app/battery_test_app.c b/applications/power/battery_test_app/battery_test_app.c index 0b5bc578..ab6889dc 100755 --- a/applications/power/battery_test_app/battery_test_app.c +++ b/applications/power/battery_test_app/battery_test_app.c @@ -57,7 +57,7 @@ BatteryTestApp* battery_test_alloc() { battery_info_get_view(app->batery_info)); app->dialog = dialog_ex_alloc(); - dialog_ex_set_header(app->dialog, "Close battery test?", 64, 12, AlignCenter, AlignTop); + dialog_ex_set_header(app->dialog, "Close Battery Test?", 64, 12, AlignCenter, AlignTop); dialog_ex_set_left_button_text(app->dialog, "Exit"); dialog_ex_set_right_button_text(app->dialog, "Stay"); dialog_ex_set_result_callback(app->dialog, battery_test_dialog_callback); diff --git a/applications/power/power_service/power.c b/applications/power/power_service/power.c index ac68bfd7..9036ae1c 100644 --- a/applications/power/power_service/power.c +++ b/applications/power/power_service/power.c @@ -200,7 +200,7 @@ static void power_check_battery_level_change(Power* power) { } int32_t power_srv(void* p) { - (void)p; + UNUSED(p); Power* power = power_alloc(); power_update_info(power); furi_record_create(RECORD_POWER, power); diff --git a/applications/power/power_settings_app/power_settings_app.c b/applications/power/power_settings_app/power_settings_app.c index 92c63704..b01f32f7 100644 --- a/applications/power/power_settings_app/power_settings_app.c +++ b/applications/power/power_settings_app/power_settings_app.c @@ -76,7 +76,7 @@ void power_settings_app_free(PowerSettingsApp* app) { int32_t power_settings_app(void* p) { uint32_t first_scene = PowerSettingsAppSceneStart; - if(p && !strcmp(p, "off")) { + if(p && strlen(p) && !strcmp(p, "off")) { first_scene = PowerSettingsAppScenePowerOff; } PowerSettingsApp* app = power_settings_app_alloc(first_scene); diff --git a/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c index 83046743..923ec250 100644 --- a/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/power/power_settings_app/scenes/power_settings_scene_power_off.c @@ -10,7 +10,7 @@ void power_settings_scene_power_off_on_enter(void* context) { PowerSettingsApp* app = context; DialogEx* dialog = app->dialog; - dialog_ex_set_header(dialog, "Turn off Device?", 64, 2, AlignCenter, AlignTop); + dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); dialog_ex_set_text( dialog, " I will be\nwaiting for\n you here...", 78, 16, AlignLeft, AlignTop); dialog_ex_set_icon(dialog, 21, 13, &I_Cry_dolph_55x52); diff --git a/applications/rpc/rpc_storage.c b/applications/rpc/rpc_storage.c index d2b43ff6..ad6191b2 100644 --- a/applications/rpc/rpc_storage.c +++ b/applications/rpc/rpc_storage.c @@ -297,36 +297,44 @@ static void rpc_system_storage_read_process(const PB_Main* request, void* contex const char* path = request->content.storage_read_request.path; Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); - bool result = false; + bool fs_operation_success = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING); - if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + if(fs_operation_success) { size_t size_left = storage_file_size(file); do { response->command_id = request->command_id; response->which_content = PB_Main_storage_read_response_tag; response->command_status = PB_CommandStatus_OK; - response->content.storage_read_response.has_file = true; - response->content.storage_read_response.file.data = - malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(MIN(size_left, MAX_DATA_SIZE))); - uint8_t* buffer = response->content.storage_read_response.file.data->bytes; - uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size; size_t read_size = MIN(size_left, MAX_DATA_SIZE); - *read_size_msg = storage_file_read(file, buffer, read_size); - size_left -= read_size; - result = (*read_size_msg == read_size); + if(read_size) { + response->content.storage_read_response.has_file = true; + response->content.storage_read_response.file.data = + malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(read_size)); + uint8_t* buffer = &response->content.storage_read_response.file.data->bytes[0]; + uint16_t* read_size_msg = &response->content.storage_read_response.file.data->size; - if(result) { - response->has_next = (size_left > 0); + *read_size_msg = storage_file_read(file, buffer, read_size); + size_left -= *read_size_msg; + fs_operation_success = (*read_size_msg == read_size); + + response->has_next = fs_operation_success && (size_left > 0); + } else { + response->content.storage_read_response.file.data = + malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(0)); + response->content.storage_read_response.file.data->size = 0; + response->content.storage_read_response.has_file = true; + response->has_next = false; + fs_operation_success = true; + } + + if(fs_operation_success) { rpc_send_and_release(session, response); } - } while((size_left != 0) && result); + } while((size_left != 0) && fs_operation_success); + } - if(!result) { - rpc_send_and_release_empty( - session, request->command_id, rpc_system_storage_get_file_error(file)); - } - } else { + if(!fs_operation_success) { rpc_send_and_release_empty( session, request->command_id, rpc_system_storage_get_file_error(file)); } @@ -349,7 +357,7 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte RpcSession* session = rpc_storage->session; furi_assert(session); - bool result = true; + bool fs_operation_success = true; if(!path_contains_only_ascii(request->content.storage_write_request.path)) { rpc_storage->current_command_id = request->command_id; @@ -370,28 +378,34 @@ static void rpc_system_storage_write_process(const PB_Main* request, void* conte rpc_storage->current_command_id = request->command_id; rpc_storage->state = RpcStorageStateWriting; const char* path = request->content.storage_write_request.path; - result = storage_file_open(rpc_storage->file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS); + fs_operation_success = + storage_file_open(rpc_storage->file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS); } File* file = rpc_storage->file; + bool send_response = false; - if(result) { - uint8_t* buffer = request->content.storage_write_request.file.data->bytes; - size_t buffer_size = request->content.storage_write_request.file.data->size; - - uint16_t written_size = storage_file_write(file, buffer, buffer_size); - result = (written_size == buffer_size); - - if(result && !request->has_next) { - rpc_send_and_release_empty( - session, rpc_storage->current_command_id, PB_CommandStatus_OK); - rpc_system_storage_reset_state(rpc_storage, session, false); + if(fs_operation_success) { + if(request->content.storage_write_request.has_file && + request->content.storage_write_request.file.data && + request->content.storage_write_request.file.data->size) { + uint8_t* buffer = request->content.storage_write_request.file.data->bytes; + size_t buffer_size = request->content.storage_write_request.file.data->size; + uint16_t written_size = storage_file_write(file, buffer, buffer_size); + fs_operation_success = (written_size == buffer_size); } + + send_response = !request->has_next; } - if(!result) { - rpc_send_and_release_empty( - session, rpc_storage->current_command_id, rpc_system_storage_get_file_error(file)); + PB_CommandStatus command_status = PB_CommandStatus_OK; + if(!fs_operation_success) { + send_response = true; + command_status = rpc_system_storage_get_file_error(file); + } + + if(send_response) { + rpc_send_and_release_empty(session, rpc_storage->current_command_id, command_status); rpc_system_storage_reset_state(rpc_storage, session, false); } } @@ -541,7 +555,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont (void)md5sum_size; furi_assert(hash_size <= ((md5sum_size - 1) / 2)); for(uint8_t i = 0; i < hash_size; i++) { - md5sum += sprintf(md5sum, "%02x", hash[i]); + md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); } free(hash); diff --git a/applications/rpc/rpc_system.c b/applications/rpc/rpc_system.c index 38a28828..0538aa64 100644 --- a/applications/rpc/rpc_system.c +++ b/applications/rpc/rpc_system.c @@ -78,6 +78,8 @@ static void rpc_system_system_device_info_callback( furi_assert(value); RpcSystemContext* ctx = context; + furi_assert(key); + furi_assert(value); char* str_key = strdup(key); char* str_value = strdup(value); @@ -232,6 +234,8 @@ static void rpc_system_system_power_info_callback( furi_assert(value); RpcSystemContext* ctx = context; + furi_assert(key); + furi_assert(value); char* str_key = strdup(key); char* str_value = strdup(value); diff --git a/applications/storage/storage_cli.c b/applications/storage/storage_cli.c index 63b9a54b..802ebd54 100644 --- a/applications/storage/storage_cli.c +++ b/applications/storage/storage_cli.c @@ -82,7 +82,7 @@ static void storage_cli_format(Cli* cli, string_t path) { if(string_cmp_str(path, STORAGE_INT_PATH_PREFIX) == 0) { storage_cli_print_error(FSE_NOT_IMPLEMENTED); } else if(string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { - printf("Formatting SD card, all data will be lost. Are you sure (y/n)?\r\n"); + printf("Formatting SD card, All data will be lost! Are you sure (y/n)?\r\n"); char answer = cli_getc(cli); if(answer == 'y' || answer == 'Y') { Storage* api = furi_record_open(RECORD_STORAGE); @@ -274,24 +274,26 @@ static void storage_cli_read_chunks(Cli* cli, string_t path, string_t args) { if(parsed_count == EOF || parsed_count != 1) { storage_cli_print_usage(); } else if(storage_file_open(file, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint8_t* data = malloc(buffer_size); uint64_t file_size = storage_file_size(file); printf("Size: %lu\r\n", (uint32_t)file_size); - while(file_size > 0) { - printf("\r\nReady?\r\n"); - cli_getc(cli); + if(buffer_size) { + uint8_t* data = malloc(buffer_size); + while(file_size > 0) { + printf("\r\nReady?\r\n"); + cli_getc(cli); - uint16_t read_size = storage_file_read(file, data, buffer_size); - for(uint16_t i = 0; i < read_size; i++) { - putchar(data[i]); + uint16_t read_size = storage_file_read(file, data, buffer_size); + for(uint16_t i = 0; i < read_size; i++) { + putchar(data[i]); + } + file_size -= read_size; } - file_size -= read_size; + free(data); } printf("\r\n"); - free(data); } else { storage_cli_print_error(storage_file_get_error(file)); } @@ -315,19 +317,21 @@ static void storage_cli_write_chunk(Cli* cli, string_t path, string_t args) { if(storage_file_open(file, string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_APPEND)) { printf("Ready\r\n"); - uint8_t* buffer = malloc(buffer_size); + if(buffer_size) { + uint8_t* buffer = malloc(buffer_size); - for(uint32_t i = 0; i < buffer_size; i++) { - buffer[i] = cli_getc(cli); + for(uint32_t i = 0; i < buffer_size; i++) { + buffer[i] = cli_getc(cli); + } + + uint16_t written_size = storage_file_write(file, buffer, buffer_size); + + if(written_size != buffer_size) { + storage_cli_print_error(storage_file_get_error(file)); + } + + free(buffer); } - - uint16_t written_size = storage_file_write(file, buffer, buffer_size); - - if(written_size != buffer_size) { - storage_cli_print_error(storage_file_get_error(file)); - } - - free(buffer); } else { storage_cli_print_error(storage_file_get_error(file)); } @@ -581,7 +585,7 @@ void storage_cli(Cli* cli, string_t args, void* context) { static void storage_cli_factory_reset(Cli* cli, string_t args, void* context) { UNUSED(args); UNUSED(context); - printf("All data will be lost. Are you sure (y/n)?\r\n"); + printf("All data will be lost! Are you sure (y/n)?\r\n"); char c = cli_getc(cli); if(c == 'y' || c == 'Y') { printf("Data will be wiped after reboot.\r\n"); diff --git a/applications/storage_settings/scenes/storage_settings_scene_benchmark.c b/applications/storage_settings/scenes/storage_settings_scene_benchmark.c index 610696af..615e07f8 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_benchmark.c +++ b/applications/storage_settings/scenes/storage_settings_scene_benchmark.c @@ -75,7 +75,7 @@ static bool static void storage_settings_scene_benchmark(StorageSettings* app) { DialogEx* dialog_ex = app->dialog_ex; uint8_t* bench_data; - dialog_ex_set_header(dialog_ex, "Preparing data...", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Preparing Data...", 64, 32, AlignCenter, AlignCenter); bench_data = malloc(BENCH_DATA_SIZE); for(size_t i = 0; i < BENCH_DATA_SIZE; i++) { @@ -123,7 +123,7 @@ void storage_settings_scene_benchmark_on_enter(void* context) { if(sd_status != FSE_OK) { dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); - dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); diff --git a/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c b/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c index 84119422..a6947968 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c +++ b/applications/storage_settings/scenes/storage_settings_scene_factory_reset.c @@ -24,7 +24,7 @@ void storage_settings_scene_factory_reset_on_enter(void* context) { dialog_ex_set_header(dialog_ex, "Confirm Factory Reset", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, - "Internal storage will be erased\r\nData and setting will be lost", + "Internal storage will be erased\r\nData and setting will be lost!", 64, 32, AlignCenter, diff --git a/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c b/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c index 6388a682..ebf7dece 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c +++ b/applications/storage_settings/scenes/storage_settings_scene_format_confirm.c @@ -15,13 +15,13 @@ void storage_settings_scene_format_confirm_on_enter(void* context) { if(sd_status == FSE_NOT_READY) { dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); - dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { - dialog_ex_set_header(dialog_ex, "Format SD card?", 64, 10, AlignCenter, AlignCenter); - dialog_ex_set_text(dialog_ex, "All data will be lost", 64, 32, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Format SD Card?", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_text(dialog_ex, "All data will be lost!", 64, 32, AlignCenter, AlignCenter); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); dialog_ex_set_right_button_text(dialog_ex, "Format"); } diff --git a/applications/storage_settings/scenes/storage_settings_scene_formatting.c b/applications/storage_settings/scenes/storage_settings_scene_formatting.c index c4e15b26..e0d8dfca 100755 --- a/applications/storage_settings/scenes/storage_settings_scene_formatting.c +++ b/applications/storage_settings/scenes/storage_settings_scene_formatting.c @@ -43,7 +43,7 @@ void storage_settings_scene_formatting_on_enter(void* context) { dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_formatting_dialog_callback); if(error != FSE_OK) { - dialog_ex_set_header(dialog_ex, "Cannot format SD Card", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Cannot Format SD Card", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { diff --git a/applications/storage_settings/scenes/storage_settings_scene_internal_info.c b/applications/storage_settings/scenes/storage_settings_scene_internal_info.c index 74eecdb9..76c7fd0e 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_internal_info.c +++ b/applications/storage_settings/scenes/storage_settings_scene_internal_info.c @@ -21,7 +21,7 @@ void storage_settings_scene_internal_info_on_enter(void* context) { if(error != FSE_OK) { dialog_ex_set_header( - dialog_ex, "Internal storage error", 64, 10, AlignCenter, AlignCenter); + dialog_ex, "Internal Storage Error", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter); } else { diff --git a/applications/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/storage_settings/scenes/storage_settings_scene_sd_info.c index 1c861538..485368c5 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -19,7 +19,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { if(sd_status != FSE_OK) { dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); - dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); diff --git a/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c b/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c index 27f55251..97187071 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c +++ b/applications/storage_settings/scenes/storage_settings_scene_unmount_confirm.c @@ -15,12 +15,12 @@ void storage_settings_scene_unmount_confirm_on_enter(void* context) { if(sd_status == FSE_NOT_READY) { dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); - dialog_ex_set_header(dialog_ex, "SD card not mounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text( dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { - dialog_ex_set_header(dialog_ex, "Unmount SD card?", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_header(dialog_ex, "Unmount SD Card?", 64, 10, AlignCenter, AlignCenter); dialog_ex_set_text( dialog_ex, "SD card will be\nunavailable", 64, 32, AlignCenter, AlignCenter); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); diff --git a/applications/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/storage_settings/scenes/storage_settings_scene_unmounted.c index e5f69d45..43f44583 100644 --- a/applications/storage_settings/scenes/storage_settings_scene_unmounted.c +++ b/applications/storage_settings/scenes/storage_settings_scene_unmounted.c @@ -16,11 +16,11 @@ void storage_settings_scene_unmounted_on_enter(void* context) { dialog_ex_set_icon(dialog_ex, 72, 14, &I_DolphinFirstStart8_56x51); if(error == FSE_OK) { - dialog_ex_set_header(dialog_ex, "SD card unmounted", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "SD Card Unmounted", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text(dialog_ex, "You can remove\nSD card now.", 3, 22, AlignLeft, AlignTop); notification_message(app->notification, &sequence_blink_green_100); } else { - dialog_ex_set_header(dialog_ex, "Cannot unmount SD Card", 64, 3, AlignCenter, AlignTop); + dialog_ex_set_header(dialog_ex, "Cannot Unmount SD Card", 64, 3, AlignCenter, AlignTop); dialog_ex_set_text(dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop); notification_message(app->notification, &sequence_blink_red_100); } diff --git a/applications/subghz/scenes/subghz_scene_receiver_config.c b/applications/subghz/scenes/subghz_scene_receiver_config.c index 590b51d1..bf2f0cdb 100644 --- a/applications/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/subghz/scenes/subghz_scene_receiver_config.c @@ -73,8 +73,9 @@ static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { if(subghz->txrx->hopper_state == SubGhzHopperStateOFF) { char text_buf[10] = {0}; - sprintf( + snprintf( text_buf, + sizeof(text_buf), "%lu.%02lu", subghz_setting_get_frequency(subghz->setting, index) / 1000000, (subghz_setting_get_frequency(subghz->setting, index) % 1000000) / 10000); @@ -106,8 +107,9 @@ static void subghz_scene_receiver_config_set_hopping_runing(VariableItem* item) variable_item_set_current_value_text(item, hopping_text[index]); if(hopping_value[index] == SubGhzHopperStateOFF) { char text_buf[10] = {0}; - sprintf( + snprintf( text_buf, + sizeof(text_buf), "%lu.%02lu", subghz_setting_get_default_frequency(subghz->setting) / 1000000, (subghz_setting_get_default_frequency(subghz->setting) % 1000000) / 10000); @@ -160,8 +162,9 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz->scene_manager, SubGhzSceneReceiverConfig, (uint32_t)item); variable_item_set_current_value_index(item, value_index); char text_buf[10] = {0}; - sprintf( + snprintf( text_buf, + sizeof(text_buf), "%lu.%02lu", subghz_setting_get_frequency(subghz->setting, value_index) / 1000000, (subghz_setting_get_frequency(subghz->setting, value_index) % 1000000) / 10000); diff --git a/applications/subghz/scenes/subghz_scene_rpc.c b/applications/subghz/scenes/subghz_scene_rpc.c index c6f7df26..652499d0 100644 --- a/applications/subghz/scenes/subghz_scene_rpc.c +++ b/applications/subghz/scenes/subghz_scene_rpc.c @@ -1,16 +1,23 @@ #include "../subghz_i.h" +typedef enum { + SubGhzRpcStateIdle, + SubGhzRpcStateLoaded, +} SubGhzRpcState; + void subghz_scene_rpc_on_enter(void* context) { SubGhz* subghz = context; Popup* popup = subghz->popup; - popup_set_header(popup, "Sub-GHz", 82, 28, AlignCenter, AlignBottom); - popup_set_text(popup, "RPC mode", 82, 32, AlignCenter, AlignTop); + popup_set_header(popup, "Sub-GHz", 89, 42, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); - popup_set_icon(popup, 2, 14, &I_Warning_30x23); // TODO: icon + popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdPopup); + scene_manager_set_scene_state(subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); + notification_message(subghz->notifications, &sequence_display_backlight_on); } @@ -18,28 +25,21 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; Popup* popup = subghz->popup; bool consumed = false; + SubGhzRpcState state = scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneRpc); if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == SubGhzCustomEventSceneExit) { - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - } + scene_manager_stop(subghz->scene_manager); view_dispatcher_stop(subghz->view_dispatcher); rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventAppExit, true); } else if(event.event == SubGhzCustomEventSceneRpcSessionClose) { - rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); - subghz->rpc_ctx = NULL; - subghz_blink_stop(subghz); - if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { - subghz_tx_stop(subghz); - subghz_sleep(subghz); - } - view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); + scene_manager_stop(subghz->scene_manager); + view_dispatcher_stop(subghz->view_dispatcher); } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { bool result = false; - if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { + if((subghz->txrx->txrx_state == SubGhzTxRxStateSleep) && + (state == SubGhzRpcStateLoaded)) { subghz_blink_start(subghz); result = subghz_tx_start(subghz, subghz->txrx->fff_data); result = true; @@ -57,8 +57,10 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubGhzCustomEventSceneRpcLoad) { bool result = false; const char* arg = rpc_system_app_get_data(subghz->rpc_ctx); - if(arg) { + if(arg && (state == SubGhzRpcStateIdle)) { if(subghz_key_load(subghz, arg, false)) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateLoaded); string_set_str(subghz->file_path, arg); result = true; string_t file_name; @@ -70,7 +72,7 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { SUBGHZ_MAX_LEN_NAME, "loaded\n%s", string_get_cstr(file_name)); - popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop); + popup_set_text(popup, subghz->file_name_tmp, 89, 44, AlignCenter, AlignTop); string_clear(file_name); } @@ -83,6 +85,13 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { void subghz_scene_rpc_on_exit(void* context) { SubGhz* subghz = context; + + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + subghz_sleep(subghz); + subghz_blink_stop(subghz); + } + Popup* popup = subghz->popup; popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c index 272cb681..12ec9868 100644 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ b/applications/subghz/scenes/subghz_scene_save_name.c @@ -59,8 +59,8 @@ void subghz_scene_save_name_on_enter(void* context) { MAX_TEXT_INPUT_LEN, // buffer size dev_name_empty); - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - string_get_cstr(subghz->file_path), SUBGHZ_APP_EXTENSION, NULL); + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(string_get_cstr(subghz->file_path), SUBGHZ_APP_EXTENSION, ""); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); string_clear(file_name); diff --git a/applications/subghz/scenes/subghz_scene_show_only_rx.c b/applications/subghz/scenes/subghz_scene_show_only_rx.c index 48fa751e..22e68c97 100644 --- a/applications/subghz/scenes/subghz_scene_show_only_rx.c +++ b/applications/subghz/scenes/subghz_scene_show_only_rx.c @@ -12,13 +12,11 @@ void subghz_scene_show_only_rx_on_enter(void* context) { // Setup view Popup* popup = subghz->popup; popup_set_icon(popup, 67, 12, &I_DolphinFirstStart7_61x51); - popup_set_text( - popup, - "This frequency can\nonly be used for RX\nin your region", - 38, - 40, - AlignCenter, - AlignBottom); + const char* text = "This frequency can\nonly be used for RX\nin your region"; + if(!furi_hal_region_is_provisioned()) { + text = "Update Flipper to unlock frequencies allowed in your region"; + } + popup_set_text(popup, text, 38, 40, AlignCenter, AlignBottom); popup_set_timeout(popup, 1500); popup_set_context(popup, subghz); popup_set_callback(popup, subghz_scene_show_only_rx_popup_callback); diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c index 4631d7a3..4eb4bbd6 100644 --- a/applications/subghz/subghz.c +++ b/applications/subghz/subghz.c @@ -5,18 +5,6 @@ #include "subghz_i.h" #include -static const NotificationSequence sequence_blink_start_magenta = { - &message_blink_start_10, - &message_blink_set_color_magenta, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - bool subghz_custom_event_callback(void* context, uint32_t event) { furi_assert(context); SubGhz* subghz = context; @@ -44,6 +32,8 @@ static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) if(event == RpcAppEventSessionClose) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose); + rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); + subghz->rpc_ctx = NULL; } else if(event == RpcAppEventAppExit) { view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); } else if(event == RpcAppEventLoadFile) { @@ -320,7 +310,7 @@ int32_t subghz_app(void* p) { subghz_environment_load_keystore( subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); // Check argument and run corresponding scene - if(p) { + if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { subghz->rpc_ctx = (void*)rpc_ctx; diff --git a/applications/subghz/subghz_cli.c b/applications/subghz/subghz_cli.c index 2b6fdd84..09dae048 100644 --- a/applications/subghz/subghz_cli.c +++ b/applications/subghz/subghz_cli.c @@ -16,9 +16,14 @@ #include #include +#include +#include + #define SUBGHZ_FREQUENCY_RANGE_STR \ "299999755...348000000 or 386999938...464000000 or 778999847...928000000" +#define SUBGHZ_REGION_FILENAME "/int/.region_data" + void subghz_cli_command_tx_carrier(Cli* cli, string_t args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -533,7 +538,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) { return; } } - if(!furi_hal_subghz_is_tx_allowed(frequency)) { + if(!furi_hal_region_is_frequency_allowed(frequency)) { printf( "In your region, only reception on this frequency (%lu) is allowed,\r\n" "the actual operation of the application is not possible\r\n ", @@ -756,6 +761,46 @@ static void subghz_cli_command(Cli* cli, string_t args, void* context) { string_clear(cmd); } +static bool + subghz_on_system_start_istream_read(pb_istream_t* istream, pb_byte_t* buf, size_t count) { + File* file = istream->state; + uint16_t ret = storage_file_read(file, buf, count); + return (count == ret); +} + +static bool subghz_on_system_start_istream_decode_band( + pb_istream_t* stream, + const pb_field_t* field, + void** arg) { + (void)field; + FuriHalRegion* region = *arg; + + PB_Region_Band band = {0}; + if(!pb_decode(stream, PB_Region_Band_fields, &band)) { + FURI_LOG_E("SubGhzOnStart", "PB Region band decode error: %s", PB_GET_ERROR(stream)); + return false; + } + + region->bands_count += 1; + region = + realloc(region, sizeof(FuriHalRegion) + sizeof(FuriHalRegionBand) * region->bands_count); + size_t pos = region->bands_count - 1; + region->bands[pos].start = band.start; + region->bands[pos].end = band.end; + region->bands[pos].power_limit = band.power_limit; + region->bands[pos].duty_cycle = band.duty_cycle; + *arg = region; + + FURI_LOG_I( + "SubGhzOnStart", + "Add allowed band: start %dHz, stop %dHz, power_limit %ddBm, duty_cycle %d%%", + band.start, + band.end, + band.power_limit, + band.duty_cycle); + return true; +} + void subghz_on_system_start() { #ifdef SRV_CLI Cli* cli = furi_record_open(RECORD_CLI); @@ -766,4 +811,52 @@ void subghz_on_system_start() { #else UNUSED(subghz_cli_command); #endif + +#ifdef SRV_STORAGE + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + FileInfo fileinfo = {0}; + PB_Region pb_region = {0}; + pb_region.bands.funcs.decode = subghz_on_system_start_istream_decode_band; + + do { + if(storage_common_stat(storage, SUBGHZ_REGION_FILENAME, &fileinfo) != FSE_OK || + fileinfo.size == 0) { + FURI_LOG_W("SubGhzOnStart", "Region data is missing or empty"); + break; + } + + if(!storage_file_open(file, SUBGHZ_REGION_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E("SubGhzOnStart", "Unable to open region data"); + break; + } + + pb_istream_t istream = { + .callback = subghz_on_system_start_istream_read, + .state = file, + .errmsg = NULL, + .bytes_left = fileinfo.size, + }; + + pb_region.bands.arg = malloc(sizeof(FuriHalRegion)); + if(!pb_decode(&istream, PB_Region_fields, &pb_region)) { + FURI_LOG_E("SubGhzOnStart", "Invalid region data"); + free(pb_region.bands.arg); + break; + } + + FuriHalRegion* region = pb_region.bands.arg; + memcpy( + region->country_code, + pb_region.country_code->bytes, + pb_region.country_code->size < 4 ? pb_region.country_code->size : 3); + furi_hal_region_set(region); + } while(0); + + pb_release(PB_Region_fields, &pb_region); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +#else + UNUSED(subghz_cli_command); +#endif } diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 00cc922c..b08b658a 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -278,7 +278,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { break; } - if(!furi_hal_subghz_is_tx_allowed(temp_data32)) { + if(!furi_hal_region_is_frequency_allowed(temp_data32)) { FURI_LOG_E(TAG, "This frequency can only be used for RX in your region"); load_key_state = SubGhzLoadKeyStateOnlyRx; break; diff --git a/applications/unit_tests/rpc/rpc_test.c b/applications/unit_tests/rpc/rpc_test.c index 1b9c5b0b..d31311af 100644 --- a/applications/unit_tests/rpc/rpc_test.c +++ b/applications/unit_tests/rpc/rpc_test.c @@ -189,8 +189,9 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) { FileInfo fileinfo; char* name = malloc(MAX_NAME_LENGTH + 1); while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { - char* fullname = malloc(strlen(clean_dir) + strlen(name) + 1 + 1); - sprintf(fullname, "%s/%s", clean_dir, name); + size_t size = strlen(clean_dir) + strlen(name) + 1 + 1; + char* fullname = malloc(size); + snprintf(fullname, size, "%s/%s", clean_dir, name); if(fileinfo.flags & FSF_DIRECTORY) { clean_directory(fs_api, fullname); } @@ -420,10 +421,12 @@ static void mu_check(result_msg_file->size == expected_msg_file->size); mu_check(result_msg_file->type == expected_msg_file->type); - mu_check(!result_msg_file->data == !expected_msg_file->data); - mu_check(result_msg_file->data->size == expected_msg_file->data->size); - for(int i = 0; i < result_msg_file->data->size; ++i) { - mu_check(result_msg_file->data->bytes[i] == expected_msg_file->data->bytes[i]); + if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) { + mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF??? + mu_check(result_msg_file->data->size == expected_msg_file->data->size); + for(int i = 0; i < result_msg_file->data->size; ++i) { + mu_check(result_msg_file->data->bytes[i] == expected_msg_file->data->bytes[i]); + } } } @@ -1224,7 +1227,7 @@ MU_TEST(test_storage_mkdir) { mu_check(test_is_exists(TEST_DIR "dir2")); } -static void test_storage_calculate_md5sum(const char* path, char* md5sum) { +static void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) { Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); @@ -1245,7 +1248,7 @@ static void test_storage_calculate_md5sum(const char* path, char* md5sum) { free(md5_ctx); for(uint8_t i = 0; i < hash_size; i++) { - md5sum += sprintf(md5sum, "%02x", hash[i]); + md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); } free(hash); @@ -1297,9 +1300,9 @@ MU_TEST(test_storage_md5sum) { test_create_file(TEST_DIR "file1.txt", 0); test_create_file(TEST_DIR "file2.txt", 1); test_create_file(TEST_DIR "file3.txt", 512); - test_storage_calculate_md5sum(TEST_DIR "file1.txt", md5sum1); - test_storage_calculate_md5sum(TEST_DIR "file2.txt", md5sum2); - test_storage_calculate_md5sum(TEST_DIR "file3.txt", md5sum3); + test_storage_calculate_md5sum(TEST_DIR "file1.txt", md5sum1, MD5SUM_SIZE * 2 + 1); + test_storage_calculate_md5sum(TEST_DIR "file2.txt", md5sum2, MD5SUM_SIZE * 2 + 1); + test_storage_calculate_md5sum(TEST_DIR "file3.txt", md5sum3, MD5SUM_SIZE * 2 + 1); test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK); test_storage_md5sum_run(TEST_DIR "file1.txt", ++command_id, md5sum1, PB_CommandStatus_OK); @@ -1346,8 +1349,7 @@ static void test_rpc_storage_rename_run( } MU_TEST(test_storage_rename) { - test_rpc_storage_rename_run( - NULL, NULL, ++command_id, PB_CommandStatus_ERROR_STORAGE_INVALID_NAME); + test_rpc_storage_rename_run("", "", ++command_id, PB_CommandStatus_ERROR_STORAGE_INVALID_NAME); furi_check(!test_is_exists(TEST_DIR "empty.txt")); test_create_file(TEST_DIR "empty.txt", 0); diff --git a/applications/unit_tests/subghz/subghz_test.c b/applications/unit_tests/subghz/subghz_test.c index 4799d235..f91d2723 100644 --- a/applications/unit_tests/subghz/subghz_test.c +++ b/applications/unit_tests/subghz/subghz_test.c @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 119 +#define TEST_RANDOM_COUNT_PARSE 188 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -377,6 +377,41 @@ MU_TEST(subghz_decoder_power_smart_test) { "Test decoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n"); } +MU_TEST(subghz_decoder_marantec_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/marantec_raw.sub"), SUBGHZ_PROTOCOL_MARANTEC_NAME), + "Test decoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_bett_test) { + mu_assert( + subghz_decoder_test(EXT_PATH("unit_tests/subghz/bett_raw.sub"), SUBGHZ_PROTOCOL_BETT_NAME), + "Test decoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_doitrand_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/doitrand_raw.sub"), SUBGHZ_PROTOCOL_DOITRAND_NAME), + "Test decoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_phoenix_v2_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/phoenix_v2_raw.sub"), SUBGHZ_PROTOCOL_PHOENIX_V2_NAME), + "Test decoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n"); +} + +MU_TEST(subghz_decoder_honeywell_wdb_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/honeywell_wdb_raw.sub"), + SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME), + "Test decoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -450,6 +485,36 @@ MU_TEST(subghz_encoder_power_smart_test) { "Test encoder " SUBGHZ_PROTOCOL_POWER_SMART_NAME " error\r\n"); } +MU_TEST(subghz_encoder_marantec_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/marantec.sub")), + "Test encoder " SUBGHZ_PROTOCOL_MARANTEC_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_bett_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/bett.sub")), + "Test encoder " SUBGHZ_PROTOCOL_BETT_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_doitrand_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/doitrand.sub")), + "Test encoder " SUBGHZ_PROTOCOL_DOITRAND_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_phoenix_v2_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/phoenix_v2.sub")), + "Test encoder " SUBGHZ_PROTOCOL_PHOENIX_V2_NAME " error\r\n"); +} + +MU_TEST(subghz_encoder_honeywell_wdb_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/honeywell_wdb.sub")), + "Test encoder " SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -482,6 +547,11 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_secplus_v2_test); MU_RUN_TEST(subghz_decoder_holtek_test); MU_RUN_TEST(subghz_decoder_power_smart_test); + MU_RUN_TEST(subghz_decoder_marantec_test); + MU_RUN_TEST(subghz_decoder_bett_test); + MU_RUN_TEST(subghz_decoder_doitrand_test); + MU_RUN_TEST(subghz_decoder_phoenix_v2_test); + MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -495,6 +565,11 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_secplus_v1_test); MU_RUN_TEST(subghz_encoder_secplus_v2_test); MU_RUN_TEST(subghz_encoder_power_smart_test); + MU_RUN_TEST(subghz_encoder_marantec_test); + MU_RUN_TEST(subghz_encoder_bett_test); + MU_RUN_TEST(subghz_encoder_doitrand_test); + MU_RUN_TEST(subghz_encoder_phoenix_v2_test); + MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/applications/updater/updater.c b/applications/updater/updater.c index daba9eaf..e9bedc72 100644 --- a/applications/updater/updater.c +++ b/applications/updater/updater.c @@ -34,7 +34,7 @@ static void Updater* updater_alloc(const char* arg) { Updater* updater = malloc(sizeof(Updater)); - if(arg) { + if(arg && strlen(arg)) { string_init_set_str(updater->startup_arg, arg); string_replace_str(updater->startup_arg, ANY_PATH(""), EXT_PATH("")); } else { diff --git a/assets/ReadMe.md b/assets/ReadMe.md index 09ec4cff..2cd99d56 100644 --- a/assets/ReadMe.md +++ b/assets/ReadMe.md @@ -38,10 +38,10 @@ Good starting point: https://docs.unrealengine.com/4.27/en-US/ProductionPipeline Don't include assets that you are not using, compiler is not going to strip unused assets. # Structure - -- `compiled` - Output folder for compiled assets. -- `dolphin` - Dolphin game assets sources. Goes to `compiled` and `resources` folders. -- `icons` - Icons sources. Goes to `compiled` folder. -- `protobuf` - Protobuf sources. Goes to `compiled` folder. +- `compiled` - Output folder made for compiled assets, after building project, in `build` directory. +- `dolphin` - Dolphin game assets sources. Goes to `compiled` and `resources` folders in `build` directory. +- `icons` - Icons sources. Goes to `compiled` folder in `build` directory. +- `protobuf` - Protobuf sources. Goes to `compiled` folder in `build` directory. - `resources` - Assets that is going to be provisioned to SD card. -- `slideshow` - One-time slideshows for desktop +- `slideshow` - One-time slideshows for desktop +- `unit_tests` - Some pre-defined signals for testing purposes. diff --git a/assets/dolphin/ReadMe.md b/assets/dolphin/ReadMe.md index f6959255..6b59d231 100644 --- a/assets/dolphin/ReadMe.md +++ b/assets/dolphin/ReadMe.md @@ -2,7 +2,7 @@ Dolphin assets are split into 3 parts: -- essential - Essential animations that are used for blocking system notifications. They are packed to `assets_dolphin_essential.[h,c]`. +- blocking - Essential animations that are used for blocking system notifications. They are packed to `assets_dolphin_blocking.[h,c]`. - internal - Internal animations that are used for idle dolphin animation. Converted to `assets_dolphin_internal.[h,c]`. - external - External animations that are used for idle dolphin animation. Packed to resource folder and placed on SD card. @@ -10,7 +10,7 @@ Dolphin assets are split into 3 parts: - `manifest.txt` - contains animations enumeration that is used for random animation selection. Starting point for Dolphin. - `meta.txt` - contains data that describes how animation is drawn. -- `frame_X.bm` - Flipper Compressed Bitmap. +- `frame_X.png` - animation frame. ## File manifest.txt diff --git a/assets/protobuf b/assets/protobuf index cc5918dc..6727eaf2 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit cc5918dc488ac3617012ce5377114e086b447324 +Subproject commit 6727eaf287db077dcd28719cd764f5804712223e diff --git a/assets/resources/Manifest b/assets/resources/Manifest index b353e342..8bfdcef7 100644 --- a/assets/resources/Manifest +++ b/assets/resources/Manifest @@ -1,5 +1,5 @@ V:0 -T:1658906571 +T:1660218073 D:badusb D:dolphin D:infrared @@ -8,7 +8,7 @@ D:nfc D:subghz D:u2f F:0e41ba26498b7511d7c9e6e6b5e3b149:1592:badusb/demo_macos.txt -F:e538ad2ce5a06ec45e1b5b24824901b1:1552:badusb/demo_windows.txt +F:46a332993ca94b9aa692030ebaa19c70:1552:badusb/demo_windows.txt D:dolphin/L1_Boxing_128x64 D:dolphin/L1_Cry_128x64 D:dolphin/L1_Furippa1_128x64 @@ -223,7 +223,7 @@ F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin/L3_Lab_research_128x54/frame_8.bm F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt D:infrared/assets -F:d895fda2f48c6cc4c55e8a398ff52e43:74300:infrared/assets/tv.ir +F:a565c3a381695a5f2ba7a0698460238c:74833:infrared/assets/tv.ir F:a157a80f5a668700403d870c23b9567d:470:music_player/Marble_Machine.fmf D:nfc/assets F:81dc04c7b181f94b644079a71476dff4:4742:nfc/assets/aid.nfc diff --git a/assets/resources/badusb/demo_windows.txt b/assets/resources/badusb/demo_windows.txt index 013e99e2..df435351 100644 --- a/assets/resources/badusb/demo_windows.txt +++ b/assets/resources/badusb/demo_windows.txt @@ -13,7 +13,7 @@ STRING Hello World! ENTER DEFAULT_DELAY 50 -REM Copy-Paste previuos string +REM Copy-Paste previous string UP HOME SHIFT DOWN diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 98292a8e..79478e7e 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -135,6 +135,42 @@ command: 02 00 00 00 # name: POWER type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: E6 00 00 00 +# +name: VOL+ +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 07 00 00 00 +# +name: VOL- +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0B 00 00 00 +# +name: CH+ +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 12 00 00 00 +# +name: CH- +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 10 00 00 00 +# +name: MUTE +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0F 00 00 00 +# +name: POWER +type: parsed protocol: NEC address: 50 00 00 00 command: 17 00 00 00 diff --git a/assets/unit_tests/subghz/bett.sub b/assets/unit_tests/subghz/bett.sub new file mode 100644 index 00000000..6d5dfb0d --- /dev/null +++ b/assets/unit_tests/subghz/bett.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: BETT +Bit: 18 +Key: 00 00 00 00 00 00 B8 B8 diff --git a/assets/unit_tests/subghz/bett_raw.sub b/assets/unit_tests/subghz/bett_raw.sub new file mode 100644 index 00000000..375723dd --- /dev/null +++ b/assets/unit_tests/subghz/bett_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok270Async +Protocol: RAW +RAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307 +RAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035 +RAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001 diff --git a/assets/unit_tests/subghz/doitrand.sub b/assets/unit_tests/subghz/doitrand.sub new file mode 100644 index 00000000..ef8fb3c9 --- /dev/null +++ b/assets/unit_tests/subghz/doitrand.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Doitrand +Bit: 37 +Key: 00 00 00 1E 60 08 2F 5F diff --git a/assets/unit_tests/subghz/doitrand_raw.sub b/assets/unit_tests/subghz/doitrand_raw.sub new file mode 100644 index 00000000..a04d77ac --- /dev/null +++ b/assets/unit_tests/subghz/doitrand_raw.sub @@ -0,0 +1,15 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 1549 -1166 409 -1138 363 -1144 407 -1144 385 -408 1599 -1138 377 -430 1593 -1138 421 -1110 393 -1126 409 -1136 377 -1162 353 -24626 823 -1176 379 -1150 353 -1174 349 -1158 377 -454 1541 -450 1561 -1190 383 -1118 411 -398 1557 -460 1571 -450 1547 -482 1537 -450 1561 -482 1547 -470 1537 -450 1547 -482 1539 -1176 387 -422 1541 -474 1537 -448 1545 -444 1539 -450 1543 -1136 375 -414 1549 -1142 417 -1102 387 -1118 385 -1140 381 -406 1549 -1180 383 -392 1549 -1140 417 -1122 383 -1116 383 -1140 415 -1102 355 -24628 823 -1188 357 -1180 343 -1172 385 -1150 361 -440 1513 -450 1563 -1184 369 -1158 381 -448 1539 -448 1527 -482 1545 -456 1545 -450 1533 -454 1579 -450 1561 -458 1513 -450 1555 -1182 389 -390 1571 -436 1571 -418 1581 -446 1505 -456 1511 -1160 391 -390 1339 -1108 381 -1098 361 -1150 309 -1124 331 -416 1065 -1142 353 -420 1049 -1148 319 -1156 345 -1128 347 -1134 317 -1138 319 -24716 687 -1178 309 -1138 317 -1166 319 -1162 327 -408 1039 -448 1067 -1152 321 -1132 313 -436 1065 -426 1039 -442 1039 -448 1049 -418 1041 -450 1035 -426 1039 -444 1041 -418 1047 -1144 339 -402 1069 -414 1071 -422 1051 -450 1029 -424 1069 -1144 335 -418 1043 -1178 319 -1134 321 -1174 309 -1164 313 -454 1049 -1148 317 -410 1051 -1160 353 -1124 321 -1166 349 -1122 329 -1178 277 -24734 677 -1178 299 -1182 309 -1154 337 -1144 307 -430 1047 -448 1039 -1174 319 -1164 289 -452 1033 -444 1031 -450 1035 -426 1063 -412 1047 -448 1041 -446 1033 -426 1041 -438 1049 -1152 289 -438 1071 -414 1031 -448 1039 -460 1033 -414 1079 -1146 309 -428 1049 -1178 311 -1154 337 -1144 329 -1162 319 -408 1051 -1160 353 -404 1059 -1164 317 -1154 325 -1140 347 -1152 333 -1138 285 -24770 657 -1178 315 -1162 289 -1184 313 -1152 329 -446 1039 -416 1047 -1170 343 -1128 351 -418 1021 -454 1047 -416 1061 -422 1033 -454 1043 -446 1039 -416 1065 -414 1041 -424 1033 -1178 301 -450 1041 -416 1051 -452 1045 -422 1033 -454 1043 -1172 283 -436 1065 -1146 329 -1176 311 -1150 337 -1148 309 -430 1049 -1174 299 -432 1061 -1180 289 -1178 311 -1162 347 -1150 307 -1160 311 -24722 607 -1296 203 -1264 207 -1266 227 -1252 239 -498 969 -506 1013 -1214 275 -1166 289 -480 1005 -484 1003 -476 1009 -454 1009 -480 1025 -458 1003 -448 1045 -448 1009 -450 1035 -1146 321 -444 1033 -444 1029 -444 1041 -442 1041 -416 1081 -1148 343 -396 1081 -1146 345 -1120 341 -1150 345 -1120 343 -420 1077 -1146 321 -420 1053 -1152 355 -1122 321 -1162 349 -1150 297 -1178 311 -24720 523 -4340 133 -1370 99 -636 67 -1406 393 -98 397 -1320 207 -1260 225 -498 975 -522 937 -540 977 -488 973 -510 987 -490 1005 -456 1003 -476 +RAW_Data: 1015 -450 1005 -1194 289 -454 1011 -478 1009 -478 1007 -450 1033 -446 1035 -1158 353 -404 1057 -1168 309 -1162 349 -1150 309 -1158 313 -452 1047 -1146 321 -420 1059 -1152 355 -1126 319 -1160 345 -1128 353 -1152 309 -78850 165 -1196 97 -3256 65 -626 165 -130 525 -66 625 -430 891 -492 163 -792 163 -66 197 -100 595 -132 229 -15220 165 -464 97 -66 197 -264 99 -998 67 -198 195 -132 65 -296 163 -198 65 -198 691 -66 985 -134 97 -66 1485 -15420 197 -200 63 -132 97 -526 231 -64 263 -754 425 -198 97 -166 97 -132 495 -100 555 -164 391 -98 261 -98 1221 -9074 97 -2604 65 -1942 195 -590 853 -132 1225 -66 987 -1058 97 -100 131 -132 593 -98 425 -66 3965 -11360 65 -730 299 -98 99 -66 133 -232 327 -132 65 -456 163 -132 97 -196 327 -364 561 -264 1851 -234 1191 -8710 65 -596 163 -134 99 -234 97 -168 131 -496 165 -202 65 -20716 231 -68 231 -134 363 -100 133 -132 133 -198 231 -168 131 -166 99 -162 131 -196 295 -66 261 -166 955 -98 695 -66 1215 -9510 65 -3698 165 -328 65 -492 131 -66 129 -692 231 -64 163 -132 163 -98 229 -132 131 -134 97 -100 563 -15732 197 -796 165 -132 99 -562 97 -168 295 -462 99 -66 131 -332 133 -100 299 -66 329 -132 301 -68 267 -12994 299 -98 197 -228 295 -922 63 -988 65 -100 129 -164 823 -98 931 -66 331 -98 955 -9454 231 -100 163 -134 99 -796 133 -628 263 -430 67 -364 399 -98 365 -66 889 -66 5041 -4044 131 -166 265 -298 231 -98 197 -232 197 -164 163 -198 197 -98 65 -132 3525 -15068 67 -698 133 -1056 199 -2322 959 -200 297 -232 1223 -13532 65 -1650 197 -198 65 -330 129 -100 295 -164 65 -66 131 -1290 99 -134 231 -262 889 -98 231 -64 489 -66 663 -66 563 -15458 97 -362 229 -98 165 -496 131 -266 65 -98 165 -832 729 -66 133 -18682 231 -298 263 -132 363 -132 195 -132 361 -232 197 -1480 131 -164 163 -12854 65 -2522 299 -166 1357 -132 99 -98 399 -166 329 -264 395 -64 195 -196 1055 -132 1417 -14994 197 -132 459 -824 131 -428 133 -832 231 -200 65 -432 231 -100 467 -66 1151 -100 1741 -8616 97 -6904 1085 -198 261 -196 261 -232 265 -132 563 -166 65 -166 197 -100 331 -134 2009 -8664 97 -164 65 -64 263 -132 357 -64 97 -166 493 -166 165 -98 195 -130 361 -854 891 -66 365 -166 1019 -15320 97 -198 331 -166 1359 -266 229 -134 65 -100 331 -98 65 -132 265 -20774 65 -2222 97 -66 229 -132 161 -162 133 -18134 229 -198 65 -132 197 -200 263 -364 97 -100 427 -526 65 -460 131 -428 +RAW_Data: 655 -98 2625 -8562 99 -432 97 -1924 67 -632 199 -498 65 -100 1819 -132 197 -228 263 -232 133 -296 229 -66 97 -20434 199 -432 65 -764 65 -232 233 -232 133 -334 463 -232 329 -98 357 -130 131 -132 1423 -4018 133 -1984 65 -2926 99 -930 97 -430 1293 -100 531 -66 493 -100 131 -100 429 -134 465 -132 3063 -9636 67 -3696 97 -132 229 -298 131 -694 627 -132 1247 -132 297 -166 133 -66 199 -166 663 -21440 133 -400 131 -130 99 -66 531 -232 229 -134 131 -202 165 -564 131 -1258 65 -100 133 -132 299 -166 1325 -66 833 -66 1521 -9032 97 -1544 295 -98 231 -164 261 -66 131 -394 361 -296 163 -298 463 -66 195 -96 721 -13330 65 -468 99 -134 65 -4696 199 -166 659 -98 361 -198 229 -132 557 -166 625 -164 229 -66 329 -100 131 -130 263 -66 1221 -8436 329 -198 99 -66 99 -66 165 -398 65 -1326 97 -794 165 -592 131 -66 265 -266 99 -430 2421 -100 465 -100 199 -66 699 -100 65 -132 1351 -66 897 -130 1653 -15200 231 -264 195 -296 99 -328 295 -296 163 -98 129 -98 295 -264 131 -398 65 -232 97 -98 887 -132 3157 -12396 199 -134 131 -66 231 -200 267 -132 265 -500 97 -732 131 -200 165 -396 763 -166 859 -66 1391 -9164 65 -3808 165 -66 131 -1692 65 -100 1017 -330 65 -132 65 -196 685 -198 65 -198 165 -98 231 -68 99 -22854 231 -168 67 -200 65 -66 263 -100 201 -302 65 -134 65 -22948 165 -396 263 -134 131 -68 165 -862 231 -1494 1261 -66 399 -302 3089 -6572 65 -396 65 -4140 7317 -5010 7415 -15982 811 -1174 381 -1122 381 -1148 351 -1164 345 -466 1509 -482 1541 -1176 387 -1144 363 -416 1519 -478 1525 -470 1533 -460 1539 -444 1535 -460 1531 -476 1527 -448 1513 -482 1509 -1178 379 -398 1549 -444 1539 -450 1547 -446 1541 -450 1545 -1142 381 -426 1529 -1172 381 -1140 387 -1114 395 -1130 379 -438 1531 -1172 381 -398 1555 -1192 351 -1146 407 -1122 381 -1146 381 -1126 349 -24662 821 -1174 379 -1122 383 -1146 415 -1102 385 -426 1567 -432 1567 -1178 381 -1120 385 -444 1511 -482 1515 -480 1539 -452 1545 -446 1545 -486 1541 -442 1555 -456 1495 -466 1531 -1148 387 -422 1509 -466 1533 -456 1539 -470 1503 -458 1539 -1164 351 -424 1509 -1190 353 -1148 369 -1130 379 -1136 317 -404 1331 -1128 319 -406 1231 -1140 337 -1114 349 -1122 329 -1140 349 -1120 293 -24712 627 -1236 237 -1226 233 -1230 271 -1228 239 -480 1017 -452 1039 -1184 293 -1172 275 -464 1041 -444 1007 -450 1033 -448 1035 -430 1047 -450 1031 -428 1047 -444 1005 -466 1015 -1158 289 -460 1047 -430 1033 -440 1039 -442 1033 -442 1037 -1168 309 -436 1051 -1140 +RAW_Data: 343 -1158 311 -1152 347 -1120 341 -420 1041 -1178 319 -422 1051 -1150 321 -1152 345 -1126 351 -1118 345 -1158 313 -24714 689 -1170 317 -1154 323 -1138 349 -1154 301 -448 1037 -416 1083 -1150 345 -1124 313 -454 1047 -418 1031 -446 1045 -448 1037 -416 1061 -412 1043 -442 1053 -426 1039 -436 1033 -1170 309 -418 1039 -454 1049 -418 1059 -424 1049 -452 1029 -1158 319 -454 1031 -1152 353 -1152 289 -1176 311 -1162 349 -418 1051 -1148 319 -454 1027 -1152 353 -1124 319 -1162 347 -1152 333 -1144 311 -24722 685 -1138 343 -1138 329 -1162 353 -1130 325 -412 1069 -414 1085 -1150 311 -1160 311 -422 1081 -416 1031 -444 1049 -414 1071 -416 1063 -430 1037 -440 1039 -412 1049 -450 1039 -1140 351 -420 1049 -418 1065 -420 1045 -448 1041 -414 1053 -1148 345 -398 1085 -1140 315 -1166 355 -1126 319 -1158 351 -398 1071 -1140 317 -432 1071 -1138 343 -1130 339 -1144 331 -1160 319 -1162 289 -24740 693 -1130 353 -1156 319 -1136 347 -1150 301 -448 1041 -416 1063 -1178 297 -1180 311 -428 1047 -448 1037 -416 1065 -428 1037 -442 1051 -428 1039 -440 1051 -428 1037 -434 1033 -1172 311 -414 1071 -420 1049 -428 1035 -440 1069 -414 1051 -1150 343 -398 1083 -1148 345 -1120 341 -1148 347 -1122 341 -416 1071 -1148 319 -420 1061 -1152 353 -1126 321 -1158 349 -1150 297 -1176 277 -24762 631 -1238 239 -1232 271 -1230 243 -1210 271 -470 1007 -476 993 -1222 281 -1202 269 -480 1011 -454 1035 -446 1039 -446 1041 -416 1047 -450 1045 -420 1033 -454 1015 -444 1051 -1154 321 -408 1071 -414 1065 -398 1065 -440 1049 -428 1037 -1172 317 -434 1069 -1138 317 -1152 327 -1170 315 -1164 319 -410 1081 -1162 319 -404 1061 -1166 317 -1154 325 -1140 347 -1152 333 -1148 311 -24742 691 -1138 341 -1138 317 -1164 353 -1124 319 -434 1069 -412 1069 -1148 325 -1140 347 -398 1075 -414 1069 -414 1051 -452 1047 -422 1051 -416 1057 -428 1049 -450 1029 -420 1033 -1166 335 -418 1041 -416 1063 -412 1079 -412 1071 -414 1065 -1148 325 -410 1069 -1142 351 -1152 309 -1160 349 -1116 329 -436 1051 -1162 319 -426 1051 -1148 327 -1172 311 -1156 337 -1150 345 -1120 305 -83468 65 -1920 133 -98 397 -1062 199 -464 165 -728 165 -168 97 -466 497 -132 1123 -66 3163 -12612 229 -198 65 -134 363 -66 67 -132 97 -896 197 -132 199 -166 165 -166 67 -134 165 -334 365 -134 2963 -15534 231 -132 231 -168 165 -262 97 -266 65 -724 65 -166 97 -198 295 -98 131 -132 563 -100 1483 -8892 99 -954 131 -234 67 -432 1087 -98 687 -132 163 -8794 165 -6460 165 -332 331 -498 99 -200 199 -266 67 -100 131 -596 165 -332 67 -98 299 -100 265 -68 633 -100 5793 -4102 99 -198 65 -566 +RAW_Data: 65 -658 99 -132 165 -332 167 -198 99 -132 133 -2530 65 -166 795 -300 197 -366 227 -262 361 -66 2073 -20762 65 -598 231 -264 97 -592 327 -132 295 -132 297 -100 363 -68 763 -20474 63 -166 757 -200 391 -100 97 -134 131 -134 591 -98 261 -262 229 -64 195 -100 195 -100 65 -132 261 -100 919 -66 333 -13642 65 -2324 131 -130 193 -462 195 -930 865 -66 2157 -100 1923 -12176 65 -168 197 -196 131 -1250 65 -132 391 -232 659 -66 393 -100 459 -230 197 -296 3479 -12728 99 -166 131 -134 131 -366 131 -98 97 -198 263 -164 231 -2074 693 -66 597 -66 433 -98 2509 -15286 65 -198 65 -198 165 -200 461 -132 757 -16060 65 -6768 199 -98 131 -262 163 -130 197 -198 165 -398 233 -334 65 -132 131 -166 331 -134 231 -564 365 -66 265 -100 465 -66 433 -100 6915 -6612 99 -264 65 -230 63 -4800 97 -66 393 -134 65 -100 165 -100 265 -66 165 -1818 99 -230 331 -66 265 -13068 131 -100 295 -298 427 -132 131 -1530 65 -100 165 -500 165 -132 995 -68 1685 -15810 263 -130 163 -462 231 -100 233 -632 165 -528 327 -196 197 -198 1253 -100 3583 -6278 97 -788 99 -3200 65 -166 133 -438 99 -132 165 -300 329 -166 1723 -134 725 -164 133 -16880 97 -960 99 -696 65 -200 231 -132 299 -66 297 -164 131 -730 197 -23052 365 -66 99 -332 131 -166 297 -494 429 -1326 265 -132 295 -66 367 -232 263 -66 857 -15396 395 -130 229 -98 233 -132 331 -364 65 -100 529 -68 231 -830 297 -100 233 -66 8533 -5582 101 -168 67 -3968 231 -66 129 -132 163 -1154 97 -166 199 -166 233 -132 467 -134 263 -66 431 -66 363 -66 957 -100 1821 -132 497 -132 1159 -15358 397 -66 99 -98 65 -66 265 -564 65 -68 97 -166 99 -100 165 -134 297 -896 165 -330 97 -134 963 -132 4737 -7096 65 -5664 265 -266 97 -166 265 -598 65 -332 65 -66 65 -198 231 -132 329 -100 65 -17202 99 -1564 595 -98 329 -198 227 -66 459 -230 97 -1480 63 -66 131 -166 99 -166 931 -8662 133 -398 99 -134 265 -98 299 -264 233 -66 99 -100 99 -66 97 -166 199 -166 265 -234 5785 -10012 163 -2324 331 -196 331 -134 65 -100 297 -100 165 -100 131 -332 163 -302 297 -164 199 -300 199 -166 229 -68 99 -68 959 -15384 365 -134 263 -1818 229 -698 99 -232 131 -100 789 -66 491 -132 4039 -12084 99 -134 331 -796 265 -132 265 -362 167 -2310 65 -98 131 -100 525 -164 295 -15032 297 -560 197 -330 131 -196 397 -266 197 -200 263 -68 261 -496 329 -166 361 -166 1517 -66 331 -10126 97 -3736 99 -1626 131 -100 +RAW_Data: 299 -298 1025 -68 165 -66 165 -102 333 -164 4245 -8938 99 -6070 1159 -132 199 -66 265 -66 363 -266 863 -100 97 -100 2781 -11408 6995 -5040 7341 -15994 819 -1130 383 -1136 385 -1150 379 -1114 353 -432 1531 -446 1539 -1162 375 -1144 385 -394 1537 -456 1511 -450 1513 -448 1559 -442 1519 -454 1541 -446 1517 -450 1513 -476 1505 -1184 365 -410 1509 -482 1511 -478 1511 -448 1547 -444 1541 -1178 381 -414 1533 -1178 385 -1112 393 -1134 381 -1144 353 -452 1507 -1182 383 -410 1535 -1164 381 -1152 379 -1122 383 -1136 377 -1158 343 -24642 815 -1138 377 -1146 353 -1148 355 -1136 345 -428 1495 -448 1573 -1142 417 -1112 393 -416 1515 -448 1563 -450 1533 -452 1543 -448 1547 -448 1529 -466 1537 -460 1513 -450 1519 -1176 383 -398 1559 -456 1537 -460 1541 -470 1533 -450 1543 -1172 377 -426 1543 -1190 351 -1138 415 -1138 387 -1116 357 -416 1503 -1166 369 -418 1521 -1174 379 -1120 383 -1136 415 -1104 385 -1114 321 -24704 635 -1244 243 -1242 237 -1230 243 -1226 257 -492 973 -490 1007 -1204 271 -1200 273 -478 1013 -456 1011 -478 1025 -460 1003 -454 1043 -446 1039 -416 1063 -414 1041 -440 1037 -1170 309 -420 1045 -452 1049 -408 1067 -436 1051 -400 1065 -1172 317 -404 1065 -1172 317 -1158 325 -1138 341 -1158 311 -416 1075 -1152 321 -408 1083 -1126 353 -1156 321 -1138 351 -1150 331 -1144 311 -24740 659 -1174 311 -1140 329 -1162 349 -1130 327 -442 1035 -416 1063 -1148 329 -1168 315 -438 1053 -410 1049 -430 1051 -452 1029 -426 1047 -438 1059 -434 1017 -450 1037 -426 1047 -1132 321 -432 1069 -414 1067 -414 1037 -432 1053 -450 1033 -1158 319 -456 1027 -1154 353 -1120 321 -1178 317 -1152 325 -444 1037 -1174 319 -418 1053 -1148 355 -1122 319 -1160 349 -1150 331 -1144 311 -24724 631 -1238 269 -1202 269 -1224 239 -1230 257 -484 1005 -482 1007 -1204 271 -1190 309 -446 1019 -456 1003 -492 1005 -472 1007 -452 1043 -448 1013 -454 1037 -424 1041 -440 1049 -1150 321 -408 1071 -414 1033 -432 1063 -438 1049 -428 1035 -1172 317 -434 1037 -1166 311 -1164 349 -1136 315 -1172 307 -406 1083 -1138 315 -438 1051 -1150 351 -1148 319 -1156 325 -1136 345 -1130 317 -24770 675 -1162 317 -1150 343 -1130 339 -1138 317 -436 1035 -452 1041 -1172 303 -1158 349 -412 1043 -428 1049 -450 1029 -428 1049 -436 1057 -436 1051 -414 1037 -430 1049 -418 1059 -1150 319 -420 1061 -422 1031 -444 1049 -448 1039 -416 1065 -1146 329 -446 1037 -1142 351 -1152 309 -1156 349 -1116 345 -428 1053 -1140 345 -396 1079 -1148 311 -1154 341 -1148 345 -1120 341 -1150 311 -24748 633 -1238 269 -1230 225 -1238 243 -1244 257 -488 973 -482 1007 -1208 289 -1178 283 -476 1003 -478 1029 -462 1013 -444 +RAW_Data: 1035 -462 1013 -446 1035 -428 1049 -450 1031 -424 1047 -1148 309 -432 1063 -412 1041 -440 1069 -416 1049 -416 1071 -1148 321 -410 1081 -1130 355 -1120 355 -1132 343 -1130 353 -420 1053 -1150 355 -386 1065 -1156 355 -1120 345 -1132 353 -1120 345 -1160 313 -24732 705 -1144 345 -1126 351 -1152 309 -1160 313 -454 1013 -454 1051 -1166 289 -1176 311 -438 1065 -400 1065 -438 1051 -398 1067 -440 1051 -398 1063 -440 1051 -414 1043 -426 1047 -1150 343 -400 1051 -450 1015 -454 1047 -418 1057 -424 1065 -1142 339 -418 1041 -1176 319 -1160 317 -1158 317 -1154 325 -446 1037 -1176 319 -418 1053 -1150 355 -1118 321 -1176 315 -1154 325 -1172 275 -82942 97 -1816 67 -3324 65 -756 297 -100 229 -132 197 -230 165 -332 97 -66 67 -264 363 -100 131 -17052 165 -828 131 -400 133 -232 167 -132 267 -298 329 -464 67 -166 65 -1394 331 -132 265 -166 397 -132 693 -15932 97 -166 65 -100 199 -300 165 -1064 99 -298 67 -298 199 -100 997 -66 1291 -15382 131 -296 99 -364 133 -496 99 -328 363 -164 99 -16946 97 -830 65 -200 233 -132 99 -266 131 -266 397 -166 1557 -66 567 -762 231 -330 265 -10908 65 -1192 99 -698 229 -198 65 -134 99 -66 265 -98 199 -134 131 -166 167 -368 65 -264 197 -234 99 -98 165 -134 297 -66 429 -166 2215 -15050 299 -166 97 -68 263 -132 293 -462 97 -98 97 -132 65 -460 65 -198 625 -132 1493 -68 3543 -10060 65 -1958 201 -200 99 -98 67 -364 165 -66 99 -232 131 -332 97 -928 491 -100 627 -98 1151 -132 1385 -15094 163 -164 695 -66 299 -134 695 -98 99 -132 431 -234 199 -8336 97 -822 65 -628 65 -5300 199 -268 231 -66 197 -98 199 -134 165 -1062 195 -230 2117 -66 693 -15212 197 -98 259 -526 327 -198 163 -822 165 -726 131 -328 163 -22598 97 -132 65 -726 63 -164 165 -66 163 -560 131 -132 131 -198 193 -396 65 -132 65 -1022 855 -132 2663 -68 1465 -11998 67 -298 199 -132 431 -166 99 -198 199 -100 397 -16212 99 -828 65 -4854 65 -100 199 -200 561 -1460 65 -66 493 -332 629 -232 561 -66 2939 -9276 99 -5942 263 -132 65 -166 329 -164 163 -98 229 -494 129 -164 427 -98 131 -164 195 -296 65 -66 625 -100 1833 -15214 263 -362 265 -598 229 -296 65 -196 163 -98 261 -560 65 -724 199 -17234 199 -68 99 -330 97 -1188 559 -662 199 -68 165 -828 65 -100 131 -21754 261 -1618 65 -134 299 -166 133 -232 131 -66 131 -266 199 -98 99 -1092 527 -64 825 -98 1213 -15578 65 -64 165 -66 131 -398 65 -66 133 -2192 131 -396 133 -66 727 -66 295 -66 1781 -15222 229 -300 +RAW_Data: 229 -432 131 -166 133 -198 331 -100 297 -232 133 -530 199 -100 651 -66 2073 -9794 97 -2386 65 -464 99 -100 133 -364 99 -166 65 -328 197 -100 885 -134 165 -132 227 -164 855 -10348 97 -234 99 -132 265 -166 231 -102 65 -234 67 -134 133 -100 401 -66 99 -100 131 -100 297 -66 165 -132 233 -232 65 -20682 131 -362 131 -394 229 -362 559 -130 1091 -166 1195 -68 463 -66 835 -9542 263 -100 165 -100 197 -132 199 -502 131 -200 131 -68 1527 -100 265 -168 99 -66 99 -166 863 -134 231 -200 129 -166 2183 -16600 297 -164 197 -232 163 -230 229 -232 99 -196 295 -166 165 -200 231 -98 165 -428 395 -132 231 -166 1227 -5128 99 -4674 165 -328 197 -328 465 -234 199 -68 331 -98 331 -166 199 -300 3685 -18958 197 -64 163 -66 229 -228 131 -130 197 -262 131 -164 165 -766 333 -332 629 -66 759 -98 299 -3478 3113 -5094 7403 -16004 829 -1146 395 -1148 383 -1140 401 -1146 385 -422 1539 -476 1569 -1180 387 -1142 407 -400 1561 -462 1599 -448 1565 -484 1563 -472 1569 -468 1537 -482 1565 -458 1571 -444 1571 -1180 387 -420 1543 -474 1571 -488 1579 -462 1567 -444 1607 -1152 387 -452 1575 -1166 379 -1136 407 -1158 385 -1148 381 -428 1567 -1166 383 -426 1581 -1168 383 -1138 417 -1126 383 -1150 391 -1130 349 -24636 853 -1170 377 -1158 407 -1118 409 -1146 387 -422 1537 -474 1575 -1178 421 -1108 409 -400 1559 -460 1565 -444 1545 -482 1539 -450 1559 -456 1573 -444 1569 -450 1511 -448 1549 -1178 383 -402 1525 -452 1545 -450 1551 -450 1531 -466 1535 -1158 387 -392 1539 -1160 387 -1150 367 -1130 383 -1132 383 -428 1543 -1180 381 -392 1549 -1170 347 -1136 353 -1100 351 -1138 343 -1126 303 -24692 669 -1164 311 -1162 333 -1138 317 -1164 317 -420 1059 -420 1067 -1142 337 -1148 311 -430 1045 -448 1037 -414 1061 -412 1077 -410 1037 -444 1065 -414 1039 -428 1049 -408 1051 -1152 309 -440 1047 -450 1041 -414 1053 -420 1047 -454 1047 -1150 345 -398 1051 -1152 343 -1158 309 -1154 345 -1118 343 -420 1077 -1142 321 -420 1055 -1148 355 -1126 317 -1156 345 -1126 353 -1152 311 -24734 665 -1156 355 -1130 319 -1158 351 -1118 327 -416 1069 -414 1065 -1152 325 -1144 349 -398 1077 -412 1071 -412 1065 -416 1037 -428 1065 -418 1051 -448 1041 -414 1051 -420 1045 -1144 351 -414 1041 -422 1069 -418 1049 -450 1037 -416 1063 -1148 327 -448 1039 -1140 353 -1132 347 -1146 311 -1160 351 -418 1051 -1148 319 -456 1029 -1158 355 -1116 321 -1176 311 -1162 349 -1148 273 -24754 637 -1238 241 -1242 271 -1228 243 -1210 271 -470 1011 -476 993 -1224 281 -1180 289 -492 975 -492 1005 -472 1009 -448 1037 -434 1027 -466 +RAW_Data: 1043 -414 1039 -444 1041 -416 1047 -1144 349 -400 1075 -412 1037 -446 1063 -410 1041 -440 1069 -1142 313 -418 1071 -1150 355 -1128 317 -1156 351 -1124 325 -410 1071 -1140 353 -420 1047 -1152 321 -1166 319 -1146 311 -1160 351 -1118 309 -24768 691 -1140 349 -1120 345 -1122 343 -1152 345 -394 1049 -450 1041 -1140 353 -1148 317 -440 1033 -418 1051 -450 1041 -416 1049 -448 1041 -422 1067 -418 1047 -414 1039 -416 1061 -1148 345 -400 1063 -428 1037 -442 1053 -426 1039 -442 1041 -1172 311 -422 1075 -1146 319 -1156 343 -1126 353 -1134 345 -400 1063 -1148 327 -412 1073 -1140 353 -1152 311 -1158 347 -1120 345 -1122 309 -24768 579 -1302 201 -1300 173 -1278 195 -1296 203 -528 941 -538 947 -1252 277 -1196 257 -522 975 -494 999 -472 1011 -456 1007 -474 1007 -450 1037 -444 1037 -462 1001 -454 1043 -1138 315 -434 1037 -442 1041 -414 1085 -416 1071 -420 1051 -1166 319 -408 1069 -1140 353 -1120 345 -1158 347 -1142 309 -430 1051 -1142 345 -398 1081 -1148 315 -1164 355 -1130 319 -1160 351 -1122 295 -24776 667 -1146 345 -1158 311 -1150 345 -1156 311 -418 1045 -454 1049 -1150 345 -1124 351 -410 1043 -426 1069 -420 1045 -448 1039 -414 1067 -428 1035 -440 1047 -428 1035 -438 1017 -1154 321 -434 1071 -414 1065 -414 1037 -464 1017 -434 1061 -1170 315 -436 1031 -1170 317 -1154 327 -1172 311 -1156 337 -416 1043 -1178 319 -418 1055 -1150 353 -1124 319 -1160 351 -1150 297 -1176 275 -87940 229 -164 361 -68 131 -66 265 -132 133 -66 429 -132 131 -232 199 -19674 65 -2866 97 -266 97 -68 99 -232 165 -498 99 -132 67 -232 99 -862 65 -100 199 -68 65 -134 99 -166 199 -200 595 -100 365 -17268 97 -732 365 -164 397 -826 65 -166 331 -298 65 -66 297 -334 397 -66 561 -164 65 -100 265 -100 299 -98 1193 -8592 99 -6652 265 -132 131 -934 163 -134 165 -398 167 -100 829 -100 229 -11976 65 -66 329 -366 131 -430 199 -266 97 -164 65 -164 161 -526 129 -66 99 -164 65 -66 131 -130 1493 -15384 165 -398 97 -100 129 -164 131 -294 229 -264 131 -17598 133 -5180 261 -434 231 -232 65 -198 165 -66 97 -100 231 -264 263 -134 99 -430 5485 -4838 231 -100 131 -66 429 -496 65 -100 65 -166 265 -696 233 -132 231 -98 997 -98 1249 -15252 97 -1558 761 -134 99 -66 431 -164 133 -20388 65 -66 65 -530 97 -888 65 -330 97 -200 133 -330 99 -330 399 -396 231 -366 65 -428 295 -558 657 -98 165 -66 4377 -6898 97 -3848 397 -466 197 -66 65 -398 165 -200 231 -564 163 -402 67 -66 595 -132 459 -66 1085 -12238 195 -134 231 -894 231 -68 165 -698 1559 -66 361 -66 +RAW_Data: 2315 -8340 101 -498 65 -1882 267 -430 99 -498 131 -230 133 -266 165 -98 97 -266 99 -398 131 -462 99 -100 3937 -130 3803 -8272 97 -4604 229 -164 97 -164 131 -1250 361 -298 561 -166 629 -68 429 -166 363 -166 231 -98 131 -8864 65 -592 97 -3130 195 -296 65 -696 1059 -134 299 -98 995 -100 365 -100 197 -66 199 -100 2455 -12500 131 -1030 65 -3104 65 -100 263 -1520 295 -162 195 -132 527 -98 493 -66 1053 -132 163 -66 97 -196 3319 -13102 97 -100 267 -366 265 -762 197 -132 1281 -234 129 -264 165 -22650 97 -100 429 -66 163 -198 493 -264 297 -100 1295 -66 131 -66 395 -132 1697 -66 5577 -10084 65 -1894 67 -132 131 -264 259 -132 365 -98 263 -164 201 -166 199 -200 299 -132 165 -134 231 -166 131 -132 1291 -98 65 -98 661 -8376 65 -296 263 -232 165 -166 327 -494 65 -100 65 -66 263 -98 195 -98 65 -22842 99 -132 65 -464 361 -296 99 -130 195 -296 163 -132 97 -460 65 -132 163 -856 1327 -66 265 -15278 131 -468 99 -100 65 -464 97 -200 199 -130 65 -134 133 -164 297 -1390 499 -166 919 -13096 197 -4346 97 -196 97 -262 97 -330 97 -528 133 -198 131 -166 197 -962 65 -98 299 -98 99 -200 1665 -16846 65 -2444 165 -564 397 -498 65 -100 163 -66 365 -66 297 -364 65 -266 65 -134 165 -17172 67 -300 131 -262 99 -166 231 -726 197 -130 689 -200 97 -132 65 -396 97 -98 99 -98 229 -398 163 -168 263 -528 1019 -66 467 -100 533 -15150 97 -500 331 -198 67 -100 131 -566 99 -66 331 -100 197 -366 99 -898 1057 -66 863 -132 757 -66 559 -8454 99 -1196 97 -3724 131 -130 131 -264 97 -594 363 -1060 99 -100 165 -362 163 -460 195 -98 129 -922 99 -328 691 -66 5965 -12210 131 -132 65 -98 263 -164 65 -230 97 -230 163 -532 97 -394 263 -362 65 -98 163 -426 65 -100 1213 -100 495 -164 461 -8616 129 -3792 133 -132 265 -132 131 -100 161 -132 359 -196 361 -790 63 -164 1889 -15496 165 -66 265 -300 299 -334 297 -298 131 -132 97 -100 99 -232 65 -300 65 -132 233 -564 431 -66 533 -98 1459 -9390 97 -3462 231 -266 163 -202 131 -166 301 -66 465 -66 799 -200 199 -266 427 -132 799 -66 4333 -13748 65 -1298 165 -234 133 -168 165 -696 329 -956 327 -166 163 -98 263 -426 1247 -132 3303 -10574 65 -428 263 -832 199 -498 65 -200 165 -924 851 -66 393 -66 563 -134 2149 -66 1291 -10298 67 -1264 7383 -5036 7419 -15962 823 -1198 347 -1188 349 -1150 373 -1186 349 -442 1543 -452 1579 -1148 381 -1168 347 -458 1535 -458 1577 -444 1577 -486 diff --git a/assets/unit_tests/subghz/honeywell_wdb.sub b/assets/unit_tests/subghz/honeywell_wdb.sub new file mode 100644 index 00000000..bce11cd0 --- /dev/null +++ b/assets/unit_tests/subghz/honeywell_wdb.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 868350000 +Preset: FuriHalSubGhzPreset2FSKDev476Async +Protocol: Honeywell +Bit: 48 +Key: 00 00 0E DB 70 20 00 01 diff --git a/assets/unit_tests/subghz/honeywell_wdb_raw.sub b/assets/unit_tests/subghz/honeywell_wdb_raw.sub new file mode 100644 index 00000000..0fbd6d23 --- /dev/null +++ b/assets/unit_tests/subghz/honeywell_wdb_raw.sub @@ -0,0 +1,22 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 868250000 +Preset: FuriHalSubGhzPreset2FSKDev476Async +Protocol: RAW +RAW_Data: 414 -1374 51 -108 79 -216 189 -584 53 -214 53 -400 215 -264 129 -322 77 -288 79 -54 107 -184 51 -446 53 -160 161 -305 263 -104 264 -134 53 -806 267 -80 187 -314 53 -158 83 -1172 243 -833 79 -188 345 -208 77 -182 225 -100 107 -106 187 -190 105 -432 176 -174 119 -328 79 -432 79 -102 99 -514 127 -218 79 -184 77 -52 101 -356 53 -615 243 -266 181 -102 212 -138 81 -486 205 -574 77 -256 155 -152 217 -78 75 -1010 393 -346 79 -488 107 -284 121 -72 215 -174 183 -54 257 -332 179 -52 79 -238 53 -636 161 -80 227 -950 346 -160 109 -564 295 -160 299 -160 435 -487 312 -52 105 -292 77 -246 137 -498 366 -104 255 -362 97 -210 53 -555 374 -206 129 -96 303 -280 179 -172 75 -102 99 -76 353 -228 131 -252 147 -130 79 -304 103 -82 139 -52 103 -183 77 -284 75 -454 55 -188 77 -222 161 -128 107 -136 187 -270 53 -371 184 -364 103 -284 129 -52 103 -321 81 -554 261 -1048 107 -106 243 -280 103 -478 215 -530 53 -108 53 -236 203 -180 51 -78 77 -338 81 -82 53 -231 75 -124 256 -232 227 -448 131 -340 131 -266 107 -346 51 -254 75 -134 210 -182 103 -280 127 -122 305 -310 255 -528 77 -513 79 -214 209 -102 53 -80 133 -1727 237 -78 79 -242 53 -296 133 -532 53 -513 53 -54 131 -190 53 -661 129 -218 107 -394 103 -554 157 -112 343 -314 103 -283 79 -304 135 -56 111 -272 189 -370 271 -270 105 -956 79 -184 77 -868 307 -156 129 -731 51 -200 161 -84 81 -326 51 -54 157 -168 369 -152 101 -188 81 -398 239 -132 215 -54 105 -182 317 -206 99 -198 97 -274 157 -271 121 -268 200 -330 499 -184 55 -434 133 -646 283 -152 255 -428 101 -350 199 -124 145 -304 308 -102 75 -386 107 -186 129 -534 101 -180 175 -100 151 -98 53 -78 133 -794 183 -150 83 -220 185 -280 83 -537 107 -308 55 -138 79 -714 79 -538 77 -106 133 -242 263 -236 75 -884 215 -136 81 -270 133 -624 105 -132 79 -848 291 -373 79 -244 55 -619 203 -202 77 -156 103 -176 99 -408 107 -318 77 -316 79 -500 55 -691 51 -340 129 -266 53 -486 103 -376 103 -185 51 -156 81 -106 77 -104 79 -502 208 -292 133 -432 105 -52 105 -80 51 -158 317 -372 181 -844 51 -270 107 -312 79 -302 83 -444 53 -640 77 -268 183 -138 85 -192 79 -158 131 -132 155 -220 81 -245 127 -386 185 -296 53 -608 77 -308 51 -822 105 -832 51 -850 155 -242 105 -422 79 -270 155 -76 77 -162 55 -414 +RAW_Data: 109 -630 77 -190 53 -52 131 -860 53 -1300 83 -1122 99 -432 53 -724 81 -390 159 -106 81 -2607 79 -474 75 -694 151 -146 73 -104 53 -380 77 -278 77 -128 73 -148 73 -248 99 -314 75 -154 97 -2225 51 -952 183 -970 77 -1210 51 -326 103 -368 151 -252 121 -784 51 -108 187 -256 227 -242 105 -104 150 -128 77 -1287 173 -2513 77 -1144 105 -1268 111 -3964 101 -1348 81 -2718 81 -302 79 -1999 55 -1268 57 -7269 53 -424 83 -3736 105 -1552 51 -8460 57 -5224 101 -244 121 -4124 103 -10775 133 -1858 73 -20679 51 -5532 97 -1718 103 -3764 51 -6496 79 -792 53 -13269 81 -18787 57 -9140 83 -3302 53 -11039 55 -26162 79 -1713 103 -5860 53 -9589 55 -8389 81 -254 85 -16287 73 -8336 53 -6237 75 -828 55 -19389 73 -8028 79 -6284 83 -3460 77 -13504 75 -3198 75 -1280 51 -1948 51 -758 79 -22076 75 -1681 73 -21693 57 -8106 53 -838 51 -10074 51 -4760 55 -492 79 -6558 79 -22996 75 -13904 53 -5564 55 -16578 83 -25603 77 -410 75 -16694 81 -606 53 -2987 53 -3898 107 -3248 79 -5168 55 -754 81 -20662 53 -11066 51 -8624 79 -26384 79 -2214 81 -7442 79 -12488 53 -1656 71 -4508 55 -15680 57 -6669 51 -5410 71 -6411 53 -6082 79 -13772 53 -5945 55 -574 73 -11921 51 -3472 55 -2323 55 -10414 51 -16069 81 -2678 77 -10775 53 -2106 110 -11794 55 -17082 51 -6184 71 -8376 79 -7152 77 -2691 53 -6332 53 -2074 57 -6804 51 -166 53 -2254 53 -452 51 -6771 53 -42749 53 -6658 73 -2034 111 -3440 109 -626 53 -7291 85 -2546 53 -5287 51 -9902 53 -10040 53 -366 79 -13848 51 -3739 77 -47536 51 -354 77 -228 77 -712 53 -530 53 -162 55 -12640 71 -1708 51 -1034 77 -12891 79 -3334 51 -7644 79 -12676 81 -3036 53 -2038 53 -180 57 -818 55 -21459 71 -3009 51 -6572 53 -4015 105 -642 77 -23618 53 -4921 83 -3026 73 -3672 53 -12654 81 -8632 51 -9419 171 -19195 105 -13041 55 -21910 51 -1051 77 -10292 51 -12884 51 -6589 53 -8718 51 -2510 103 -15406 55 -6014 99 -966 73 -2725 53 -12715 51 -4228 55 -3192 57 -8672 51 -14740 51 -17032 75 -11111 1761 -182 399 -106 215 -270 355 -132 329 -150 177 -312 315 -170 169 -290 331 -162 173 -286 187 -312 163 -300 329 -164 311 -162 171 -316 289 -176 297 -194 311 -164 143 -324 167 -314 163 -310 161 -312 163 -300 175 -300 331 -162 173 -312 161 -312 163 -314 161 -298 189 -300 165 -312 161 -312 189 -276 173 -316 193 -310 163 -312 161 -310 163 -300 175 -296 193 -284 191 -284 187 -310 163 -300 175 -300 169 -320 177 -314 485 -464 177 -292 353 -134 +RAW_Data: 171 -316 311 -154 187 -292 343 -134 337 -136 199 -286 311 -182 157 -294 327 -160 171 -316 161 -312 159 -314 333 -170 311 -164 141 -324 305 -164 299 -176 301 -170 167 -340 133 -338 163 -312 159 -294 189 -298 165 -314 311 -180 155 -294 189 -300 165 -312 187 -284 189 -312 159 -294 189 -296 167 -314 161 -310 189 -312 159 -292 189 -296 167 -312 161 -312 159 -314 193 -268 175 -304 193 -314 163 -312 159 -312 193 -294 495 -458 161 -338 301 -168 165 -304 331 -134 199 -286 309 -184 309 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 163 -312 309 -184 311 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 187 -284 189 -312 159 -292 189 -298 167 -312 161 -312 189 -300 173 -294 195 -282 191 -310 485 -472 167 -300 331 -134 171 -312 333 -170 169 -290 331 -162 299 -176 155 -300 333 -162 171 -300 323 -150 145 -340 159 -320 167 -302 319 -150 319 -166 169 -312 331 -162 299 -150 319 -194 139 -314 165 -308 163 -340 133 -338 163 -312 157 -296 327 -160 171 -316 163 -310 161 -312 163 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 169 -318 177 -290 189 -300 165 -312 161 -312 189 -284 185 -292 189 -300 191 -286 187 -284 191 -312 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 319 -150 319 -166 169 -312 331 -162 143 -314 311 -180 157 -294 189 -300 165 -314 309 -182 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 171 -316 161 -312 159 -314 191 -270 201 -278 193 -314 163 -310 161 -312 163 -300 175 -300 169 -316 177 -292 189 -300 165 -312 161 -312 189 -284 183 -294 189 -298 193 -284 187 -312 163 -312 497 -460 145 -320 325 -168 171 -290 333 -162 171 -314 305 -160 317 -168 169 -290 333 -162 173 -312 283 -182 159 -294 189 -302 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 321 -194 141 -312 167 -334 135 -340 161 -310 163 -312 159 -294 327 -160 171 -316 163 -310 161 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -300 165 -312 161 -310 189 -284 185 -292 189 -300 191 -286 187 -312 161 -300 487 -484 151 -318 335 -138 169 -322 305 -164 171 -314 305 -190 295 -166 171 -290 333 -162 173 -312 283 -182 159 -296 191 -300 +RAW_Data: 165 -312 333 -160 311 -170 141 -318 331 -164 299 -148 321 -194 139 -316 165 -336 135 -340 133 -338 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -300 175 -296 501 -458 187 -288 347 -154 151 -316 325 -162 171 -316 313 -152 301 -194 141 -316 331 -162 143 -314 311 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -174 295 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -306 173 -314 165 -306 191 -284 189 -284 189 -312 157 -294 191 -300 163 -314 187 -284 189 -284 187 -292 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -492 167 -298 323 -150 175 -302 331 -164 171 -300 321 -150 317 -166 167 -308 331 -134 171 -316 309 -182 129 -318 189 -302 163 -314 309 -182 311 -170 141 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -312 159 -294 327 -160 171 -316 161 -312 159 -312 193 -270 175 -304 193 -314 163 -312 159 -312 161 -314 161 -296 189 -304 165 -312 187 -284 189 -276 173 -318 195 -278 193 -284 187 -312 163 -300 175 -298 169 -318 511 -460 147 -318 327 -168 169 -292 331 -164 171 -314 305 -160 315 -168 171 -290 333 -162 171 -314 283 -182 159 -294 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 157 -294 327 -160 171 -316 163 -310 161 -304 175 -314 167 -304 191 -286 187 -284 189 -284 187 -292 189 -300 165 -312 189 -284 187 -286 185 -294 189 -296 165 -314 161 -312 187 -286 185 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -292 331 -164 299 -174 151 -316 325 -162 171 -316 289 -176 151 -316 187 -296 167 -312 311 -180 307 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 161 -310 163 -312 161 -296 189 -302 191 -286 187 -284 189 -312 159 -292 189 -296 167 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -296 323 -150 175 -312 313 -170 169 -292 333 -162 299 -176 151 -314 327 -160 171 -316 291 -176 149 -316 189 -296 165 -314 309 -182 307 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 161 -312 +RAW_Data: 161 -302 147 -320 331 -162 171 -314 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -296 195 -284 189 -284 189 -312 159 -296 513 -468 159 -300 333 -162 171 -300 323 -150 171 -314 325 -162 311 -164 171 -314 305 -160 159 -296 327 -160 171 -316 161 -312 161 -312 335 -170 311 -164 141 -324 321 -154 299 -194 309 -164 143 -326 167 -314 161 -312 159 -312 163 -302 173 -300 331 -162 173 -312 161 -312 163 -300 175 -300 167 -320 153 -312 191 -296 167 -312 161 -312 189 -312 159 -292 189 -298 167 -312 161 -312 159 -314 193 -268 177 -302 193 -316 161 -312 159 -312 193 -294 497 -456 187 -312 303 -166 165 -306 331 -132 201 -286 309 -182 313 -168 143 -318 331 -162 143 -314 311 -182 157 -294 189 -302 165 -312 309 -182 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -300 149 -320 331 -162 171 -314 161 -310 163 -302 173 -298 193 -310 165 -310 161 -310 163 -312 161 -296 189 -300 193 -286 187 -284 189 -312 157 -294 189 -298 165 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -298 321 -150 171 -318 323 -162 171 -314 289 -176 297 -194 141 -314 331 -162 143 -316 309 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -312 161 -296 513 -468 159 -300 333 -162 171 -302 321 -150 175 -300 333 -162 311 -162 171 -316 289 -176 153 -314 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -320 307 -164 297 -176 301 -194 141 -318 165 -336 137 -338 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 161 -312 163 -298 177 -302 169 -318 177 -292 189 -298 167 -312 189 -284 189 -312 157 -294 189 -298 167 -312 161 -312 159 -312 193 -270 203 -302 169 -314 161 -312 189 -310 467 -484 145 -318 327 -168 143 -316 331 -162 173 -312 307 -160 313 -170 141 -318 331 -164 143 -342 281 -184 157 -294 189 -302 165 -312 307 -184 313 -168 141 -320 331 -162 299 -150 319 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 +RAW_Data: 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 189 -310 163 -312 497 -460 145 -320 325 -170 169 -290 333 -162 171 -314 307 -158 317 -168 169 -290 333 -162 171 -314 281 -182 159 -296 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 175 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 193 -284 187 -312 161 -314 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 321 -156 329 -170 127 -318 321 -176 155 -300 333 -164 169 -302 165 -314 161 -310 329 -168 299 -166 151 -328 311 -154 331 -168 299 -178 143 -318 153 -322 167 -332 149 -320 151 -318 165 -304 321 -156 181 -290 189 -300 173 -294 177 -294 189 -302 173 -294 177 -294 189 -302 173 -294 177 -318 193 -278 173 -294 179 -292 191 -302 173 -294 177 -294 189 -302 173 -320 177 -294 195 -278 175 -318 491 -460 163 -314 333 -158 159 -294 327 -160 171 -316 311 -180 307 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 135 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -306 173 -314 165 -304 193 -284 187 -286 189 -276 197 -294 193 -310 163 -312 161 -310 163 -300 175 -296 193 -310 165 -284 187 -310 163 -300 175 -300 169 -318 177 -316 483 -466 187 -292 341 -134 171 -308 319 -150 179 -300 333 -164 309 -162 171 -316 313 -152 153 -316 327 -160 171 -316 163 -310 161 -312 307 -166 337 -136 171 -322 305 -164 299 -174 301 -194 141 -318 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 165 -312 189 -284 189 -284 185 -294 189 -296 165 -314 161 -310 189 -284 187 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -290 333 -162 299 -176 151 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 305 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -302 149 -320 329 -164 171 -286 187 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -302 191 -286 187 -284 189 -312 159 -292 191 -296 165 -314 161 -310 189 -284 187 -292 +RAW_Data: 189 -300 193 -284 485 -494 167 -298 321 -150 177 -300 327 -150 167 -328 305 -160 319 -168 171 -290 333 -162 171 -314 281 -184 159 -296 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 139 -314 167 -334 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 167 -318 177 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -284 189 -300 487 -484 149 -320 333 -138 171 -322 321 -154 151 -316 327 -160 311 -164 173 -312 307 -160 161 -298 355 -132 173 -314 163 -310 161 -312 335 -170 311 -164 143 -322 307 -164 323 -150 301 -196 141 -318 165 -334 137 -338 161 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 173 -298 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -468 193 -296 323 -150 147 -340 313 -168 171 -292 331 -164 299 -174 151 -316 325 -160 173 -316 289 -176 149 -316 189 -294 165 -314 309 -182 307 -170 141 -318 331 -164 299 -148 321 -194 139 -314 167 -306 165 -338 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 161 -310 165 -300 175 -300 169 -318 177 -290 189 -298 165 -314 187 -284 189 -312 159 -294 189 -298 165 -314 161 -310 161 -306 173 -316 193 -280 191 -312 159 -312 163 -300 515 -460 161 -312 305 -190 135 -328 329 -134 201 -286 309 -182 311 -168 143 -318 331 -162 143 -316 309 -182 157 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -312 161 -298 189 -300 165 -312 161 -312 189 -312 159 -292 191 -300 191 -286 485 -468 193 -298 323 -148 147 -342 323 -160 173 -314 289 -176 297 -196 139 -314 331 -162 143 -314 311 -180 157 -292 191 -300 165 -312 311 -182 309 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -310 161 -310 163 -302 173 -300 169 -318 175 -292 189 -300 165 -312 161 -310 189 -278 173 -316 195 -280 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -328 329 -134 199 -288 309 -182 311 -168 143 -318 +RAW_Data: 331 -162 143 -316 309 -182 157 -292 191 -300 165 -312 309 -182 313 -168 141 -318 331 -164 299 -174 295 -194 141 -314 165 -334 137 -340 133 -338 163 -312 159 -292 327 -160 171 -316 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 163 -300 175 -300 169 -316 177 -292 189 -298 167 -312 161 -310 189 -278 173 -316 193 -282 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -326 331 -132 201 -286 309 -184 309 -170 141 -318 333 -162 143 -314 311 -180 159 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 175 -294 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 153 -314 191 -296 193 -286 187 -284 189 -312 161 -294 509 -468 159 -298 335 -162 169 -304 323 -150 175 -300 335 -162 311 -160 173 -316 313 -152 151 -316 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -322 305 -164 299 -176 299 -194 141 -316 167 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 177 -290 189 -296 193 -286 187 -310 163 -300 175 -296 501 -484 161 -288 347 -154 151 -316 325 -160 173 -314 313 -152 301 -196 141 -314 331 -162 143 -316 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 163 -298 177 -302 193 -314 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 481 -460 173 -320 303 -168 169 -294 331 -164 171 -314 307 -160 319 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -184 311 -168 143 -318 331 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 165 -312 187 -284 189 -284 185 -294 189 -296 165 -314 187 -284 189 -302 173 -294 195 -310 491 -458 163 -318 319 -156 153 -314 327 -160 171 -316 311 -152 327 -170 141 -316 331 -164 143 -314 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -316 333 -162 299 -150 +RAW_Data: 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 159 -314 163 -300 175 -302 193 -314 163 -310 161 -310 163 -300 175 -300 167 -318 177 -290 191 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -310 163 -300 487 -484 149 -320 335 -138 169 -322 305 -164 171 -314 305 -190 293 -168 169 -292 333 -162 171 -314 281 -184 159 -296 189 -300 165 -312 309 -184 311 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -310 163 -314 161 -296 189 -302 165 -312 187 -286 189 -276 173 -318 193 -280 191 -284 189 -310 163 -300 175 -300 167 -318 513 -460 167 -314 311 -152 161 -324 311 -162 169 -310 329 -160 293 -190 137 -330 329 -132 173 -316 311 -180 129 -318 189 -300 165 -312 309 -182 311 -168 141 -320 329 -164 299 -174 295 -194 141 -314 167 -334 137 -340 133 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 189 -300 165 -312 161 -312 189 -284 183 -292 189 -300 193 -284 187 -284 189 -314 471 -486 145 -318 327 -168 171 -290 331 -162 173 -312 307 -158 317 -168 169 -290 333 -162 143 -342 283 -182 159 -294 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 299 -176 293 -196 139 -314 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 193 -284 187 -284 189 -302 149 -318 193 -280 191 -284 189 -284 189 -300 173 -300 169 -316 177 -316 483 -476 153 -320 329 -134 201 -286 309 -182 157 -294 355 -132 339 -136 169 -320 315 -162 171 -302 323 -148 171 -318 159 -318 147 -318 327 -168 313 -164 169 -302 321 -150 317 -166 305 -164 169 -324 169 -314 163 -310 161 -310 163 -300 175 -296 331 -164 171 -314 161 -312 163 -300 175 -298 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 189 -298 195 -284 187 -284 189 -314 159 -294 513 -470 159 -300 333 -162 171 -300 323 -150 169 -316 325 -160 311 -164 173 -312 307 -158 161 -294 329 -158 173 -314 163 -312 159 -312 335 -170 311 -164 141 -324 305 -164 323 -152 301 -194 141 -318 165 -336 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 +RAW_Data: 165 -298 175 -304 169 -318 175 -290 191 -298 165 -314 187 -284 189 -312 159 -294 191 -296 167 -312 161 -312 159 -314 193 -270 201 -304 167 -314 163 -312 159 -312 493 -486 145 -318 325 -170 141 -316 331 -162 173 -314 305 -160 313 -170 141 -318 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -162 301 -148 321 -194 141 -314 165 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -310 163 -302 173 -296 193 -312 161 -312 159 -312 163 -300 175 -300 167 -320 177 -290 189 -298 193 -286 187 -284 189 -312 161 -294 513 -468 159 -300 333 -162 171 -302 321 -150 171 -316 323 -162 311 -164 171 -314 307 -158 159 -296 327 -160 171 -316 163 -310 161 -306 321 -168 339 -134 171 -306 321 -150 317 -166 337 -164 143 -324 167 -314 163 -312 159 -312 163 -300 175 -298 331 -162 173 -312 161 -312 163 -300 175 -300 193 -310 163 -312 161 -310 163 -312 161 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 187 -286 185 -292 191 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -146 167 -344 305 -158 315 -168 143 -318 331 -162 143 -342 283 -182 159 -294 189 -302 163 -314 307 -184 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 167 -334 137 -338 161 -312 161 -312 159 -294 329 -160 171 -316 163 -310 161 -312 163 -300 175 -302 169 -318 175 -290 191 -298 165 -312 189 -284 189 -312 159 -292 189 -298 165 -314 161 -312 189 -282 195 -268 203 -304 169 -312 163 -312 187 -278 507 -460 173 -320 303 -166 171 -294 331 -164 171 -314 305 -160 319 -168 169 -292 331 -164 171 -314 281 -182 159 -294 191 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 159 -314 163 -300 175 -302 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -292 189 -300 165 -312 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 483 -458 173 -320 303 -168 169 -294 331 -164 171 -314 307 -158 319 -168 169 -292 331 -164 171 -314 283 -182 159 -294 191 -300 165 -312 309 -182 313 -168 143 -316 331 -164 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -312 159 -294 327 -160 171 -316 161 -312 159 -314 163 -298 177 -304 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 163 -314 161 -310 189 -284 +RAW_Data: 185 -294 189 -296 165 -314 189 -284 189 -312 157 -294 189 -300 497 -456 189 -284 345 -154 153 -316 325 -160 173 -314 315 -150 325 -170 141 -314 331 -164 143 -314 309 -182 157 -294 189 -302 163 -314 307 -184 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -300 165 -314 187 -284 189 -312 157 -294 189 -298 165 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -468 193 -296 323 -150 175 -312 313 -168 171 -292 331 -164 299 -176 149 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 307 -168 143 -316 331 -164 299 -150 319 -194 141 -312 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 173 -296 169 -318 177 -292 189 -300 165 -312 161 -312 189 -284 183 -294 189 -294 195 -284 187 -284 191 -300 173 -296 499 -482 163 -314 321 -154 153 -316 325 -160 173 -314 313 -152 301 -196 139 -316 331 -162 143 -314 311 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 169 -316 177 -292 189 -300 165 -314 161 -310 189 -284 185 -292 189 -296 193 -286 187 -284 189 -300 175 -294 499 -482 163 -314 321 -154 153 -316 325 -160 171 -316 313 -152 325 -170 141 -314 331 -164 143 -314 311 -180 157 -294 191 -300 165 -312 309 -182 311 -168 141 -320 329 -164 299 -174 295 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -310 163 -312 159 -312 163 -300 175 -298 169 -318 177 -292 189 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 187 -286 189 -312 471 -486 145 -320 325 -168 171 -290 331 -162 173 -312 307 -160 315 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -182 311 -170 141 -318 331 -164 299 -148 319 -196 139 -314 167 -334 135 -340 161 -310 163 -300 149 -320 331 -162 171 -314 161 -310 163 -314 161 -296 189 -296 165 -314 161 -310 189 -284 185 -292 189 -298 165 -312 161 -312 189 -276 173 -318 193 -278 191 -286 187 -284 189 -300 175 -298 169 -318 177 -314 483 -466 187 -294 339 -136 169 -306 +RAW_Data: 321 -150 177 -300 333 -164 311 -160 173 -314 313 -152 155 -314 327 -160 171 -316 163 -312 159 -312 307 -166 337 -138 169 -322 305 -164 299 -176 301 -194 141 -316 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 175 -294 169 -318 177 -292 189 -300 165 -312 161 -312 189 -284 183 -292 189 -296 193 -286 187 -284 189 -302 173 -296 499 -458 187 -312 321 -154 153 -316 325 -162 171 -316 313 -152 301 -194 141 -314 331 -162 143 -316 309 -182 157 -292 189 -302 163 -314 309 -182 311 -170 141 -318 331 -162 311 -160 295 -190 163 -302 165 -304 165 -312 161 -338 163 -312 157 -294 327 -160 171 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 159 -312 189 -284 185 -290 189 -300 193 -284 189 -284 189 -312 471 -460 483 -2128 125 -52 75 -102 75 -154 101 -100 147 -272 149 -328 75 -228 511 -441 77 -212 87 -114 189 -54 263 -156 171 -393 127 -102 183 -110 293 -242 395 -320 107 -52 81 -346 53 -572 155 -381 305 -234 101 -126 228 -156 195 -102 281 -126 518 -52 313 -240 187 -54 79 -316 133 -136 213 -108 79 -80 185 -464 181 -76 379 -80 81 -506 233 -318 103 -78 53 -559 51 -240 53 -106 207 -136 163 -136 81 -218 187 -104 349 -266 51 -266 129 -80 107 -450 131 -331 127 -359 641 -108 472 -100 547 -82 79 -246 107 -560 567 -130 403 -389 149 -179 77 -526 235 -108 81 -78 51 -455 75 -106 105 -240 101 -557 183 -184 255 -102 311 -130 147 -174 457 -354 75 -442 81 -344 331 -76 51 -106 133 -54 161 -560 77 -130 77 -294 257 -52 99 -126 73 -565 51 -242 131 -497 55 -354 347 -404 81 -108 540 -286 185 -465 219 -264 296 -106 77 -294 193 -110 567 -52 287 -134 53 -106 55 -330 263 -210 83 -86 109 -134 349 -80 221 -80 207 -260 279 -76 75 -74 75 -76 99 -130 443 -78 77 -338 129 -226 179 -280 129 -228 75 -617 185 -324 423 -160 259 -673 159 -188 362 -212 205 -383 151 -316 235 -240 183 -188 79 -56 107 -80 53 -154 417 -158 211 -238 109 -80 79 -218 215 -108 105 -354 215 -112 191 -56 103 -132 77 -236 605 -152 51 -78 185 -78 127 -126 551 -156 135 -186 127 -724 131 -56 81 -378 345 -182 109 -316 107 -316 337 -128 151 -294 101 -1002 101 -52 335 -256 51 -330 81 -448 153 -158 127 -272 131 -160 105 -220 767 -132 +RAW_Data: 75 -210 131 -78 181 -243 345 -108 107 -80 105 -160 107 -218 127 -82 537 -266 239 -340 181 -76 105 -78 421 -482 53 -132 321 -134 51 -262 77 -480 103 -271 475 -154 159 -80 313 -132 79 -282 235 -126 75 -202 175 -226 151 -198 901 -210 51 -78 235 -348 79 -130 103 -584 107 -456 105 -54 431 -54 79 -106 133 -242 105 -486 155 -104 129 -228 147 -100 77 -618 357 -154 531 -104 51 -160 105 -136 109 -140 269 -366 51 -832 53 -270 135 -108 51 -186 51 -160 535 -130 77 -296 51 -182 131 -106 341 -56 135 -194 530 -294 181 -562 75 -179 127 -364 207 -74 125 -332 149 -76 129 -102 103 -102 51 -128 173 -122 482 -474 229 -200 97 -790 105 -104 79 -104 341 -262 101 -110 111 -82 109 -433 271 -210 51 -346 245 -54 209 -284 157 -106 293 -234 77 -826 157 -52 127 -228 299 -490 159 -140 137 -164 247 -294 135 -52 131 -266 77 -128 385 -158 81 -276 356 -476 53 -170 55 -463 79 -190 53 -808 51 -80 159 -106 161 -162 83 -225 187 -341 101 -520 159 -54 428 -182 341 -108 79 -274 77 -108 431 -392 77 -132 131 -138 83 -58 135 -274 305 -300 83 -398 79 -534 131 -528 263 -52 261 -264 437 -342 77 -102 389 -332 103 -130 131 -322 105 -106 53 -134 183 -188 79 -82 81 -104 53 -316 269 -56 347 -286 341 -270 161 -52 131 -136 223 -110 341 -318 469 -138 143 -110 83 -80 53 -54 183 -346 385 -906 51 -382 75 -154 51 -52 207 -186 53 -78 51 -106 293 -52 155 -126 306 -130 412 -416 187 -82 339 -160 135 -194 163 -136 81 -78 281 -426 229 -76 227 -532 177 -528 51 -244 269 -616 75 -52 418 -136 53 -82 51 -154 97 -126 233 -52 51 -740 77 -162 81 -304 263 -162 217 -448 275 -164 185 -138 139 -54 277 -348 531 -642 376 -274 267 -182 75 -126 103 -615 103 -236 81 -380 77 -52 241 -80 105 -438 111 -192 181 -54 135 -186 113 -58 243 -140 57 -385 189 -158 133 -675 231 -80 241 -236 131 -106 109 -108 55 -136 555 -132 303 -494 341 -316 135 -112 302 -366 97 -98 101 -310 177 -78 427 -297 151 -579 163 -314 129 -78 51 -233 105 -210 103 -202 217 -180 121 -229 125 -302 221 -52 235 -158 187 -110 135 -162 51 -108 105 -398 221 -82 297 -110 53 -1400 213 -106 51 -454 723 -368 79 -104 79 -134 81 -323 51 -372 79 -80 235 -132 549 -164 81 -104 129 -602 371 -160 129 -536 179 -234 133 -210 236 -489 159 -52 237 -210 129 -264 53 -242 105 -106 53 -114 +RAW_Data: 193 -290 191 -436 343 -311 207 -322 53 -400 51 -882 135 -678 375 -244 317 -192 380 -558 260 -154 710 -80 237 -218 109 -250 81 -372 107 -160 646 -286 125 -178 269 -726 51 -106 79 -298 523 -682 159 -498 265 -465 127 -592 177 -100 149 -687 103 -78 357 -102 53 -158 105 -240 79 -618 105 -130 79 -478 77 -154 450 -186 53 -192 137 -1038 105 -160 266 -136 51 -345 53 -80 157 -108 235 -102 483 -54 109 -234 327 -376 51 -462 366 -289 163 -108 75 -262 261 -134 81 -78 105 -136 81 -378 105 -272 139 -106 209 -80 205 -184 101 -76 101 -180 229 -427 99 -430 125 -100 103 -469 125 -102 221 -130 209 -106 105 -104 201 -274 332 -232 105 -166 161 -104 275 -80 131 -1000 321 -156 189 -166 81 -130 367 -262 153 -258 75 -260 127 -542 127 -464 107 -80 107 -108 303 -240 77 -238 181 -282 77 -76 224 -104 75 -74 225 -554 131 -56 55 -164 51 -318 247 -130 159 -158 77 -76 285 -354 133 -52 135 -420 155 -256 129 -78 265 -831 103 -231 107 -674 285 -653 337 -102 257 -182 101 -146 147 -303 75 -206 365 -268 157 -54 81 -504 133 -472 107 -82 161 -80 133 -461 81 -825 79 -798 101 -206 207 -80 185 -980 209 -56 247 -246 51 -106 131 -78 79 -236 53 -106 51 -98 101 -206 127 -78 151 -98 279 -155 157 -468 131 -72 173 -372 329 -212 79 -52 101 -375 77 -286 51 -188 185 -308 191 -56 555 -320 179 -86 193 -490 105 -214 81 -138 53 -216 211 -112 57 -134 213 -212 131 -714 73 -128 155 -590 51 -452 189 -261 247 -162 105 -404 167 -56 77 -354 81 -56 215 -316 129 -372 366 -288 133 -56 295 -294 51 -822 53 -482 295 -340 321 -188 459 -631 147 -212 201 -100 173 -258 305 -261 51 -208 97 -124 103 -598 159 -206 77 -710 213 -190 241 -158 133 -186 385 -266 219 -350 133 -52 157 -78 51 -156 403 -162 189 -300 259 -202 103 -760 179 -258 173 -82 431 -164 161 -373 133 -208 191 -82 237 -52 135 -384 131 -192 55 -52 285 -52 79 -274 57 -140 109 -306 53 -132 133 -240 53 -104 51 -214 79 -158 123 -98 99 -126 77 -178 173 -334 178 -518 129 -236 337 -206 230 -100 225 -52 152 -102 103 -308 51 -52 203 -114 139 -318 211 -396 183 -78 51 -106 77 -164 83 -380 133 -234 237 -54 79 -158 83 -84 137 -136 323 -158 365 -128 157 -108 81 -194 109 -618 155 -363 191 -190 109 -110 55 -80 163 -224 79 -82 105 -270 133 -314 159 -106 153 -356 77 -76 75 -458 149 -74 +RAW_Data: 101 -128 53 -130 153 -212 447 -230 171 -636 75 -98 324 -362 51 -160 217 -218 51 -310 101 -152 379 -280 151 -176 203 -210 51 -130 336 -232 179 -596 629 -298 55 -1302 261 -108 109 -80 77 -162 237 -52 125 -52 75 -100 125 -148 297 -426 107 -110 133 -238 155 -228 123 -100 247 -74 179 -126 99 -148 99 -134 81 -194 81 -248 53 -164 135 -82 109 -717 53 -220 81 -234 79 -344 53 -474 79 -154 51 -618 109 -142 83 -593 135 -134 105 -136 81 -243 413 -228 121 -174 125 -328 53 -658 361 -74 401 -280 131 -132 85 -56 139 -298 157 -481 51 -52 129 -76 75 -152 127 -507 321 -486 185 -264 103 -54 325 -76 183 -104 53 -246 81 -78 185 -210 155 -112 55 -80 77 -720 103 -54 51 -450 51 -104 107 -246 185 -78 267 -482 221 -580 113 -222 365 -80 135 -138 199 -296 261 -164 107 -80 131 -134 107 -272 323 -1120 51 -476 81 -106 211 -132 107 -352 183 -134 133 -108 215 -220 55 -666 131 -218 79 -82 253 -56 103 -52 235 -106 103 -134 185 -532 111 -188 361 -399 183 -693 238 -104 567 -106 113 -280 107 -52 287 -106 161 -908 103 -376 339 -180 73 -200 101 -330 209 -54 107 -220 315 -132 216 -138 159 -438 135 -190 73 -130 510 -244 53 -294 81 -84 171 -138 53 -460 515 -82 219 -315 291 -54 107 -270 107 -54 53 -160 79 -374 209 -154 97 -74 233 -454 125 -144 119 -96 99 -542 75 -1200 103 -204 99 -172 95 -98 127 -78 75 -102 103 -320 241 -820 77 -78 105 -326 135 -264 53 -216 133 -517 211 -228 103 -52 257 -455 127 -74 77 -78 235 -436 151 -102 75 -152 75 -76 75 -303 51 -254 177 -284 103 -152 207 -156 79 -302 53 -610 79 -214 133 -80 243 -110 107 -108 185 -286 155 -212 187 -384 75 -128 77 -80 129 -130 99 -76 156 -104 185 -82 81 -242 217 -380 81 -294 157 -182 161 -134 131 -104 77 -52 181 -150 75 -98 147 -230 129 -760 161 -273 137 -84 111 -674 53 -80 127 -317 265 -210 79 -242 297 -238 75 -328 105 -244 345 -342 131 -254 151 -378 103 -335 101 -288 75 -154 275 -498 75 -224 372 -80 157 -190 395 -268 81 -52 105 -160 277 -510 107 -262 105 -192 109 -188 81 -586 105 -78 123 -98 201 -182 55 -614 101 -170 175 -285 79 -222 191 -658 161 -194 135 -532 107 -810 77 -134 241 -128 147 -150 276 -200 97 -230 157 -104 171 -282 77 -212 77 -80 406 -714 259 -182 131 -128 161 -112 81 -380 79 -290 135 -777 345 -370 385 -178 51 -364 +RAW_Data: 215 -189 53 -108 83 -320 209 -530 51 -265 51 -54 109 -242 351 -362 51 -102 51 -156 173 -247 255 -100 99 -176 99 -52 224 -262 53 -80 279 -356 51 -76 379 -176 75 -220 77 -52 129 -208 129 -304 101 -378 157 -52 53 -102 179 -228 255 -311 131 -192 235 -52 51 -126 273 -721 53 -294 161 -170 55 -376 81 -268 131 -98 129 -268 81 -158 107 -80 161 -214 129 -110 501 -220 185 -688 79 -132 325 -212 53 -295 105 -104 51 -106 79 -372 153 -106 137 -84 193 -741 51 -561 51 -402 390 -126 149 -250 327 -338 177 -202 177 -931 105 -271 423 -768 53 -430 159 -110 217 -104 161 -460 249 -82 79 -186 79 -104 53 -132 79 -218 55 -298 53 -214 81 -354 51 -108 107 -196 273 -80 79 -104 153 -100 175 -76 131 -220 81 -56 131 -368 217 -116 137 -82 169 -342 209 -106 319 -396 75 -104 253 -130 149 -430 127 -130 81 -196 55 -404 105 -726 181 -224 199 -106 79 -220 159 -180 75 -126 201 -128 75 -833 127 -128 125 -230 249 -184 51 -218 79 -240 163 -222 135 -82 103 -80 159 -765 107 -276 237 -438 207 -162 133 -128 135 -316 105 -312 137 -486 181 -158 77 -769 183 -764 533 -186 77 -100 173 -128 202 -174 174 -102 185 -52 51 -128 177 -358 101 -96 145 -126 75 -184 135 -558 51 -186 211 -241 237 -154 203 -508 103 -562 171 -320 99 -148 127 -212 53 -472 77 -128 147 -100 105 -136 111 -322 206 -238 79 -162 51 -559 109 -198 113 -461 53 -849 159 -230 51 -306 121 -316 105 -78 105 -182 153 -234 131 -226 83 -800 105 -104 215 -52 185 -572 235 -660 97 -922 53 -517 53 -80 133 -302 53 -134 345 -78 111 -374 133 -80 157 -106 185 -184 373 -110 55 -292 129 -140 247 -240 347 -130 73 -227 75 -648 153 -128 75 -1002 155 -335 169 -404 101 -76 175 -126 103 -373 79 -402 217 -218 101 -171 301 -158 204 -76 123 -100 51 -148 251 -334 77 -211 103 -304 359 -78 105 -52 99 -560 101 -288 303 -436 131 -234 179 -252 131 -656 51 -260 129 -160 79 -106 51 -184 203 -152 103 -234 155 -200 235 -52 133 -164 324 -106 107 -82 107 -348 313 -188 79 -372 79 -549 185 -82 79 -246 265 -720 107 -788 77 -585 79 -128 75 -130 203 -76 75 -206 228 -234 79 -180 101 -532 210 -282 229 -412 103 -140 55 -54 77 -316 151 -356 51 -102 75 -272 51 -506 51 -744 329 -340 129 -104 135 -300 167 -1318 107 -186 51 -136 107 -603 107 -338 105 -492 131 -250 107 -80 109 -170 79 -162 +RAW_Data: 101 -102 109 -306 181 -330 209 -76 101 -286 79 -126 51 -156 103 -78 357 -248 77 -260 51 -76 99 -320 323 -76 99 -124 199 -632 105 -220 290 -260 125 -234 75 -272 103 -252 423 -623 81 -80 105 -264 77 -1040 51 -106 107 -1561 81 -732 107 -82 167 -327 159 -626 53 -162 53 -162 107 -238 235 -106 163 -108 105 -104 105 -136 133 -306 51 -430 129 -152 109 -86 111 -56 81 -616 161 -130 51 -514 107 -236 208 -110 193 -638 133 -82 109 -543 216 -457 111 -84 169 -1168 201 -52 75 -78 289 -284 103 -874 105 -166 81 -244 129 -54 214 -398 81 -56 137 -410 81 -268 159 -372 103 -106 83 -814 207 -230 177 -280 75 -966 51 -823 77 -102 105 -394 139 -112 81 -1292 75 -727 183 -734 51 -590 83 -242 77 -1674 105 -562 107 -484 103 -104 107 -2114 77 -2326 51 -1052 207 -594 129 -232 177 -895 51 -506 203 -671 75 -206 101 -328 77 -104 105 -132 77 -370 186 -82 79 -846 77 -1272 53 -1609 177 -406 51 -226 121 -330 51 -280 73 -3094 75 -802 236 -3347 53 -2218 51 -1176 107 -212 75 -1460 51 -714 81 -1643 105 -4782 55 -5096 51 -7167 53 -17412 53 -6530 51 -20019 51 -12370 71 -442 103 -10796 55 -1320 105 -660 51 -1854 135 -16801 77 -5863 87 -12342 119 -24656 81 -7111 101 -15641 79 -3617 75 -10578 127 -5573 77 -16152 53 -5624 51 -13975 71 -7720 79 -3760 51 -9739 77 -1892 73 -2638 153 -4570 51 -5265 53 -3429 79 -1982 53 -2154 51 -7158 53 -12468 161 -12934 79 -14244 77 -3871 105 -2482 79 -4568 99 -218 79 -17125 53 -2368 53 -10101 55 -15958 81 -2358 109 -3584 51 -6886 85 -6834 51 -4154 53 -218 53 -826 55 -7583 51 -11429 216 -1346 71 -1856 79 -982 55 -2072 51 -4033 105 -7186 79 -1506 81 -8082 53 -6530 83 -6380 109 -192 135 -6058 51 -3274 79 -6105 57 -18103 53 -1142 55 -4687 57 -12931 99 -344 53 -9300 79 -7717 75 -3774 81 -3691 105 -2780 51 -2764 75 -1030 79 -29043 79 -3611 53 -1934 79 -4819 75 -6538 53 -7104 53 -3542 55 -1969 53 -2426 105 -7239 81 -516 79 -14563 123 -2246 53 -3012 71 -900 55 -1196 51 -7049 51 -1790 53 -10358 53 -3988 81 -1214 107 diff --git a/assets/unit_tests/subghz/marantec.sub b/assets/unit_tests/subghz/marantec.sub new file mode 100644 index 00000000..80f1b053 --- /dev/null +++ b/assets/unit_tests/subghz/marantec.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Marantec +Bit: 49 +Key: 00 01 30 07 10 DF 86 9F diff --git a/assets/unit_tests/subghz/marantec_raw.sub b/assets/unit_tests/subghz/marantec_raw.sub new file mode 100644 index 00000000..f4c2d6d4 --- /dev/null +++ b/assets/unit_tests/subghz/marantec_raw.sub @@ -0,0 +1,14 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 3487 -68 18413 -66 6689 -17938 65 -68 495 -98 1129 -860 697 -166 395 -100 463 -100 163 -14692 263 -794 65 -64 99 -2922 131 -200 97 -168 99 -100 199 -962 495 -68 165 -98 299 -198 133 -168 3917 -98 963 -132 461 -100 1089 -166 331 -134 633 -164 201 -100 363 -164 335 -200 265 -68 531 -166 699 -132 529 -166 397 -98 895 -168 65 -100 399 -366 463 -66 197 -134 431 -66 163 -98 623 -166 301 -98 263 -98 263 -66 197 -98 329 -98 525 -66 331 -200 1025 -66 629 -132 763 -166 233 -66 431 -132 133 -100 429 -264 165 -132 299 -166 429 -68 4605 -134 593 -134 4917 -60834 167 -10282 1941 -1122 865 -2086 921 -1072 1921 -1042 939 -1056 945 -1044 939 -1052 945 -1024 971 -1024 973 -1002 969 -1010 989 -1996 1007 -994 979 -1014 1965 -1014 977 -1020 969 -2004 1993 -982 1011 -982 1005 -984 977 -2008 981 -1006 1993 -1966 999 -994 1007 -982 1005 -984 1007 -996 975 -1016 1987 -996 973 -1016 969 -1004 1013 -1970 1011 -984 1999 -1970 1995 -984 1021 -1968 1005 -994 1003 -970 999 -984 1023 -974 1015 -8920 1995 -1014 1001 -1966 1013 -986 1991 -986 1007 -996 973 -1012 999 -978 985 -1006 1005 -984 997 -978 1015 -1000 973 -1982 1025 -982 985 -1008 1973 -1004 1007 -982 983 -1998 2009 -972 1009 -980 999 -978 1011 -1970 1011 -986 1999 -1978 1007 -988 1007 -994 975 -1012 1003 -972 1009 -978 1987 -986 1007 -982 1017 -994 971 -1990 1009 -984 1997 -1978 1999 -1010 993 -1964 1005 -1000 1007 -980 997 -984 1017 -972 999 -8932 2031 -980 989 -1988 985 -1016 1983 -986 1007 -994 975 -1016 975 -998 977 -1012 999 -982 985 -1006 983 -1018 993 -1968 1017 -996 975 -1016 1965 -1012 979 -1018 971 -2004 1991 -978 1003 -984 1015 -992 973 -2004 1005 -976 1991 -2008 975 -986 1005 -998 977 -1012 999 -978 985 -1010 1973 -1004 1013 -980 981 -1004 1007 -1996 975 -986 1993 -2006 1961 -1002 1013 -1974 985 -1008 1011 -978 997 -1006 981 -1012 991 -8918 8005 -3924 1887 -174620 131 -3974 131 -298 559 -230 65 -132 265 -98 863 -168 333 -66 299 -234 463 -166 331 -102 697 -200 199 -98 265 -100 663 -166 331 -66 599 -132 99 -66 1261 -66 399 -100 265 -100 1199 -66 265 -166 101 -66 599 -232 197 -100 561 -66 499 -132 797 -132 427 -66 265 -298 465 -66 565 -198 97 -100 695 -100 531 -132 267 -66 429 -98 231 -100 331 -100 333 -98 363 -198 67 -100 165 -100 231 -100 729 -134 1061 -100 493 -164 665 -132 259 -128 257 -130 329 -98 361 -66 263 -100 227 -66 491 -98 295 -166 229 -132 229 -130 263 -130 233 -100 331 -232 99 -100 165 -66 667 -164 795 -66 695 -68 429 -132 +RAW_Data: 559 -66 16863 -66 19215 -12276 65 -2086 131 -2188 65 -132 67 -166 299 -364 65 -100 131 -468 233 -200 497 -498 297 -100 363 -198 329 -130 327 -164 229 -64 427 -164 657 -132 829 -532 895 -66 297 -130 425 -166 363 -132 329 -100 465 -100 465 -198 65 -262 429 -98 495 -100 295 -528 131 -132 1129 -134 259 -130 327 -100 397 -66 261 -230 99 -66 229 -132 625 -68 131 -100 587 -66 329 -64 293 -130 1741 -132 325 -132 197 -98 261 -98 99 -132 561 -166 165 -66 391 -66 329 -98 231 -132 465 -100 331 -300 199 -132 299 -132 165 -164 229 -98 565 -100 395 -132 131 -100 327 -130 97 -98 163 -132 293 -98 557 -66 199 -166 233 -132 265 -166 627 -100 265 -132 595 -166 99 -98 199 -100 267 -166 795 -166 265 -166 297 -66 131 -166 331 -134 793 -164 263 -198 299 -100 265 -166 257 -100 197 -198 599 -132 333 -66 265 -100 229 -100 165 -204 465 -166 97 -232 365 -166 431 -68 199 -166 99 -236 365 -132 1297 -66 1361 -100 989 -98 297 -132 12467 -232 361 -100 263 -132 261 -196 229 -296 65 -66 329 -164 229 -164 263 -66 563 -232 3415 -132 11115 -100 97 -100 363 -164 99 -66 199 -100 99 -66 561 -164 259 -168 233 -132 333 -166 567 -98 297 -66 399 -66 399 -298 327 -132 685 -98 393 -98 819 -100 231 -270 429 -68 461 -132 131 -134 1121 -230 787 -132 1393 -132 233 -100 331 -66 497 -166 563 -196 425 -98 525 -66 723 -98 265 -134 365 -66 1297 -166 433 -98 165 -134 231 -134 263 -100 369 -198 99 -100 631 -634 331 -132 565 -132 265 -102 267 -98 467 -200 297 -130 731 -100 367 -98 229 -130 99 -98 263 -164 493 -100 297 -198 429 -66 425 -66 1021 -164 295 -198 97 -198 263 -132 425 -98 493 -98 197 -98 593 -264 65 -294 197 -66 295 -66 561 -196 565 -166 2675 -132 19393 -134 5403 -164 369 -166 467 -132 361 -526 395 -66 361 -132 529 -66 431 -66 465 -166 363 -98 65 -98 229 -98 359 -100 197 -132 201 -100 467 -166 397 -100 297 -130 229 -98 361 -98 199 -130 297 -66 659 -132 261 -98 819 -98 393 -132 329 -132 557 -132 163 -130 657 -66 295 -134 1189 -100 399 -102 861 -100 369 -132 331 -134 263 -666 65 -134 427 -64 197 -98 559 -68 297 -98 493 -164 197 -164 525 -66 131 -98 493 -66 595 -100 233 -100 265 -132 65 -66 465 -266 527 -98 855 -132 493 -66 393 -298 331 -100 131 -66 859 -198 495 -100 265 -98 367 -66 727 -196 535 -66 263 -66 397 -332 65 -198 331 -134 297 -330 +RAW_Data: 261 -100 3291 -68 2553 -19050 1855 -100 1259 -200 165 -100 39479 -100 233 -68 1027 -166 595 -66 231 -168 199 -98 265 -232 563 -166 795 -98 99 -460 65 -362 1017 -98 229 -98 625 -66 231 -98 161 -196 363 -98 363 -100 631 -132 297 -66 333 -300 99 -66 295 -230 985 -100 623 -132 1319 -100 65 -68 231 -232 197 -232 331 -100 199 -168 567 -166 133 -232 823 -396 327 -132 855 -232 165 -100 401 -166 599 -198 167 -132 299 -198 663 -132 165 -134 67 -66 1687 -66 1705 -100 265 -232 297 -100 531 -66 333 -100 1263 -66 297 -164 299 -98 431 -398 97 -66 199 -296 231 -232 925 -100 131 -132 229 -164 361 -66 267 -166 197 -100 491 -132 261 -132 1183 -98 891 -100 265 -100 99 -100 725 -100 303 -60886 131 -10310 1911 -1226 749 -2146 899 -1084 1905 -1070 917 -1044 973 -1042 927 -1050 949 -1044 965 -1012 949 -1046 965 -1014 977 -2008 981 -980 995 -1012 1969 -1018 971 -1014 975 -2014 1967 -1016 971 -1014 977 -984 1003 -2006 973 -1000 2001 -1978 1013 -980 1001 -972 1009 -978 991 -1010 983 -1004 1989 -1012 983 -978 995 -1014 983 -2002 977 -984 2023 -1968 1997 -986 999 -1970 1003 -1000 1009 -978 1017 -974 1009 -978 1001 -8948 1975 -1016 973 -2004 999 -978 2001 -986 1001 -978 1009 -986 1001 -1010 975 -998 1011 -992 975 -982 1005 -1000 1009 -1970 1011 -982 1009 -984 2003 -978 1001 -976 1015 -1966 1999 -1010 989 -982 989 -1004 1001 -1978 1013 -978 2005 -1966 1019 -972 1011 -980 997 -976 1011 -1004 999 -966 1997 -1000 999 -1002 975 -986 999 -1994 1007 -980 1993 -1978 2001 -1008 991 -1970 1009 -998 975 -1016 971 -1006 1013 -980 1001 -8922 2001 -994 1009 -1972 997 -1012 1971 -984 1005 -1012 977 -998 1009 -978 1011 -984 977 -1012 979 -1016 977 -1010 977 -2010 981 -1006 971 -1020 1985 -984 1005 -978 999 -1990 1997 -980 997 -1010 981 -1014 989 -1976 1005 -966 1995 -1998 997 -982 1021 -972 1003 -986 997 -1012 975 -982 1995 -1014 999 -980 983 -1004 1003 -1976 1007 -980 1983 -2012 1983 -998 977 -2010 973 -1014 973 -1018 973 -1014 975 -1016 971 -8956 7999 -3926 1865 -177334 165 -166 199 -496 431 -66 1319 -132 297 -164 457 -68 231 -100 863 -98 603 -100 663 -100 789 -262 491 -166 729 -100 795 -364 365 -132 293 -100 197 -98 625 -200 231 -132 229 -132 227 -100 755 -526 329 -134 793 -100 167 -200 335 -66 65 -134 265 -66 201 -66 1023 -100 63 -21396 133 -198 65 -66 263 -98 393 -134 65 -66 329 -68 199 -432 263 -394 131 -130 195 -66 687 -66 295 -164 229 -196 97 -98 559 -98 1283 -98 531 -66 331 -68 435 -100 393 -628 431 -132 99 -66 +RAW_Data: 329 -66 2519 -100 6759 -12690 463 -232 97 -3252 65 -2592 65 -132 99 -198 67 -496 99 -300 231 -132 233 -66 165 -132 131 -164 201 -66 4113 -66 1987 -68 15385 -98 8199 -16614 165 -1258 495 -1354 229 -166 297 -134 3653 -100 12533 -66 3759 -162 591 -132 361 -130 527 -100 327 -200 65 -66 461 -132 327 -132 461 -100 559 -198 263 -132 161 -98 329 -132 327 -68 395 -66 165 -132 163 -196 261 -66 361 -98 229 -132 301 -134 761 -66 399 -166 99 -66 461 -98 857 -98 131 -262 359 -132 199 -100 299 -166 363 -132 297 -132 199 -132 265 -166 133 -100 301 -268 463 -98 231 -134 265 -98 527 -298 265 -136 531 -132 363 -132 129 -130 527 -132 297 -66 297 -68 397 -466 99 -132 697 -98 233 -464 131 -132 365 -134 465 -98 635 -98 299 -66 497 -132 197 -132 489 -166 327 -98 1151 -130 295 -132 623 -164 97 -68 297 -100 1293 -100 1759 -66 1259 -132 729 -100 797 -66 99 -66 231 -98 529 -166 331 -166 299 -202 431 -134 467 -462 329 -462 1051 -98 625 -66 195 -100 461 -196 429 -132 197 -66 129 -66 229 -166 589 -164 629 -66 1053 -166 231 -98 263 -132 329 -132 267 -132 427 -130 65 -130 229 -166 4121 -66 15899 -19980 495 -264 165 -432 65 -132 65 -662 663 -100 793 -66 723 -164 565 -100 363 -166 199 -98 465 -100 299 -168 265 -198 695 -132 131 -268 497 -66 265 -134 199 -98 231 -134 499 -100 989 -132 429 -196 327 -132 131 -164 427 -66 263 -66 525 -132 1091 -100 793 -132 491 -66 195 -526 921 -134 363 -98 693 -230 165 -98 1711 -132 293 -66 659 -196 231 -66 261 -66 163 -66 1349 -166 363 -100 65 -100 299 -100 393 -66 495 -134 229 -98 131 -98 463 -362 361 -132 231 -66 491 -396 361 -132 97 -66 163 -198 229 -294 229 -198 165 -164 65 -100 623 -66 195 -98 261 -130 65 -98 563 -66 267 -166 1057 -632 531 -132 463 -100 663 -166 133 -100 569 -164 197 -66 431 -164 261 -132 363 -100 427 -164 263 -198 623 -66 589 -166 133 -166 199 -66 199 -268 65 -98 427 -66 163 -132 563 -198 363 -166 397 -98 1025 -66 197 -66 163 -132 693 -164 195 -66 427 -100 131 -66 433 -132 199 -402 231 -66 335 -100 993 -100 463 -232 65 -100 65 -66 7165 -66 6785 -66 895 -66 923 -198 97 -164 689 -166 361 -198 99 -66 753 -498 365 -66 567 -232 397 -66 199 -132 233 -200 995 -296 365 -166 597 -100 199 -134 99 -166 197 -332 431 -166 331 -66 199 -100 431 -232 493 -166 457 -98 231 -132 427 -230 559 -98 +RAW_Data: 261 -132 1051 -66 7501 -98 22107 -19232 2271 -166 133 -60834 165 -10284 595 -5446 885 -1106 1891 -1088 897 -1068 949 -1050 929 -1040 947 -1048 959 -1008 985 -1008 973 -1012 993 -2004 979 -998 975 -1014 1987 -998 977 -1014 971 -1998 2007 -974 1013 -976 999 -982 1023 -1962 1005 -1000 1969 -2010 977 -1016 971 -1004 1009 -980 985 -1002 979 -1014 1977 -1012 979 -1016 971 -1012 977 -1986 1021 -974 2005 -1968 2007 -976 1011 -1966 1009 -984 1009 -984 985 -1020 969 -1002 997 -8952 1995 -986 1017 -1988 989 -976 1979 -1028 975 -988 1009 -986 1007 -992 973 -1014 1001 -974 1011 -978 995 -1006 979 -2004 975 -1022 973 -984 1999 -1014 977 -984 1003 -2004 1995 -976 1005 -998 999 -982 981 -2002 983 -986 1989 -1998 1009 -976 999 -1010 991 -978 1013 -968 1011 -986 1995 -982 1023 -974 981 -1020 993 -1970 1007 -996 1995 -1982 1999 -986 993 -2002 985 -998 975 -1014 969 -1004 1009 -978 997 -8962 1989 -990 1009 -1954 1025 -984 2007 -962 1011 -980 1001 -982 1009 -1002 1007 -980 1005 -976 1007 -980 997 -976 1009 -2002 977 -988 1005 -982 2001 -976 1009 -988 1005 -1968 2029 -974 1003 -990 1011 -958 1005 -1972 1039 -978 1999 -1978 1009 -954 1037 -978 1007 -954 1039 -980 1011 -958 1999 -974 1031 -990 993 -972 1011 -1980 1011 -978 2009 -1968 1993 -982 1003 -1974 1007 -1006 1001 -986 985 -986 1001 -982 1033 -8924 8013 -3924 1851 -170400 493 -132 131 -164 65 -724 461 -164 693 -100 265 -100 229 -100 199 -98 363 -232 363 -100 131 -68 531 -166 165 -134 265 -66 727 -300 131 -198 99 -132 99 -134 597 -102 295 -130 131 -230 129 -98 689 -264 263 -134 231 -134 199 -132 131 -68 295 -100 163 -130 591 -98 327 -164 231 -66 589 -98 2077 -134 1389 -66 2913 -66 3045 -66 4463 -98 391 -132 489 -164 295 -66 523 -166 329 -98 195 -98 165 -100 165 -66 231 -100 895 -98 265 -132 595 -132 297 -494 261 -198 331 -100 131 -134 429 -68 231 -298 1193 -66 99 -100 627 -132 331 -98 393 -98 657 -132 163 -392 359 -132 163 -64 655 -164 263 -64 531 -98 265 -164 499 -100 229 -64 459 -100 199 -66 165 -130 197 -132 393 -66 163 -228 229 -132 65 -132 263 -100 357 -98 991 -328 595 -132 197 -130 759 -66 131 -166 267 -100 1251 -64 361 -66 395 -954 559 -132 295 -98 327 -100 295 -100 367 -198 391 -298 363 -132 495 -132 523 -66 97 -132 763 -198 131 -66 197 -100 525 -130 1059 -64 461 -166 563 -134 331 -66 463 -134 329 -198 199 -166 265 -266 1061 -66 331 -366 65 -66 1225 -100 299 -66 299 -132 133 -298 265 -100 1129 -364 163 -460 297 -132 297 -100 299 -100 +RAW_Data: 529 -166 2423 -100 199 -66 22473 -20988 131 -100 395 -166 165 -268 133 -100 17947 -68 8295 -132 267 -66 295 -100 627 -98 199 -200 597 -168 363 -266 365 -68 597 -66 431 -166 233 -98 465 -100 533 -132 299 -66 533 -66 361 -98 229 -98 459 -132 129 -232 63 -66 427 -132 395 -98 361 -98 359 -100 131 -134 1721 -66 431 -132 67 -398 395 -100 361 -66 1693 -230 1381 -98 1879 -100 693 -66 299 -100 233 -132 133 -134 2807 -132 5113 -66 1317 -68 5575 -98 199 -168 235 -432 463 -100 1679 -66 6115 -134 10653 -19120 97 -426 99 -162 99 -64 263 -166 227 -132 727 -100 99 -98 1163 -66 497 -132 197 -66 293 -98 789 -66 1051 -66 395 -64 293 -132 983 -66 1225 -66 65 -168 99 -100 299 -164 491 -166 365 -132 265 -100 397 -132 99 -100 265 -234 299 -100 233 -100 433 -232 363 -66 197 -130 789 -164 525 -132 331 -132 199 -66 195 -66 459 -98 163 -66 197 -164 229 -132 261 -130 65 -98 361 -64 625 -66 591 -66 863 -198 465 -132 631 -66 699 -100 131 -100 197 -166 535 -66 131 -100 297 -132 499 -66 599 -266 295 -132 359 -66 197 -196 263 -132 99 -98 261 -164 163 -66 493 -132 433 -134 495 -98 197 -132 391 -230 393 -66 263 -66 425 -66 261 -66 427 -200 397 -198 97 -132 99 -132 959 -100 13369 -66 1227 -100 295 -164 263 -232 261 -196 393 -230 227 -132 297 -100 97 -100 195 -262 893 -66 725 -66 331 -100 267 -134 1091 -132 133 -166 265 -100 461 -200 729 -100 431 -98 1093 -66 197 -168 265 -100 663 -98 363 -132 97 -132 523 -328 693 -98 397 -396 229 -164 491 -66 131 -132 591 -66 197 -66 823 -66 331 -66 459 -394 97 -100 529 -98 199 -100 563 -132 461 -166 231 -596 163 -132 687 -164 165 -164 165 -132 265 -68 563 -66 131 -66 233 -166 657 -166 395 -100 1147 -66 229 -132 199 -236 65 -66 263 -66 233 -66 235 -66 199 -266 561 -166 133 -166 199 -132 731 -100 363 -198 227 -132 261 -166 657 -98 459 -98 393 -100 229 -100 229 -130 297 -98 233 -98 463 -200 401 -64 99 -132 199 -132 131 -68 629 -66 265 -66 163 -100 529 -66 297 -166 299 -132 233 -100 199 -134 763 -66 297 -100 865 -236 99 -66 265 -432 265 -198 295 -68 463 -198 365 -166 131 -68 197 -168 731 -68 1293 -168 365 -132 725 -66 199 -64 627 -66 299 -134 231 -264 2051 -98 131 -66 425 -66 391 -100 163 -132 97 -132 523 -164 129 -162 427 -66 231 -100 335 -166 1423 -600 789 -164 165 -66 329 -262 691 -132 +RAW_Data: 197 -162 4471 -64 9599 -60872 143 -10288 613 -890 131 -166 97 -4144 867 -1130 1871 -1104 897 -1048 941 -1076 933 -1048 941 -1046 939 -1048 943 -1012 973 -1048 945 -2002 987 -1008 985 -1014 1983 -984 1005 -998 975 -2008 1999 -978 1001 -978 1011 -976 997 -1984 1009 -990 1997 -1966 1005 -1000 977 -1012 999 -984 983 -1006 999 -988 1989 -982 1007 -1000 973 -1002 1019 -1966 1015 -986 1989 -1980 1991 -984 1009 -1990 985 -982 1021 -972 999 -996 1011 -980 1003 -8954 1991 -992 1007 -1972 1009 -978 2003 -982 985 -978 1019 -986 1019 -970 1015 -958 1025 -974 1011 -990 997 -978 1013 -1980 1007 -978 1019 -976 1995 -972 1007 -976 1009 -1994 2009 -952 1007 -1010 1011 -952 1005 -2000 1003 -974 2003 -1982 1007 -976 999 -978 1009 -978 1023 -986 1017 -972 2011 -972 1009 -978 1001 -974 1011 -1964 1015 -982 2023 -1950 1999 -988 1025 -1964 1021 -958 1005 -982 1029 -984 981 -1004 1001 -8932 1999 -986 1015 -1982 1001 -986 1991 -984 1007 -994 973 -1016 971 -1006 1011 -980 985 -1004 979 -1014 983 -1004 977 -2006 1005 -984 973 -1018 1969 -1016 969 -1006 1013 -1972 1999 -974 999 -1006 997 -974 1003 -2010 977 -1002 1991 -1970 1011 -1002 971 -1014 973 -1016 973 -1014 975 -1016 1995 -980 999 -976 1013 -978 995 -2006 975 -1018 1969 -2008 1967 -1014 969 -1990 1001 -1018 973 -1014 975 -986 1001 -1008 969 -8960 8009 -3874 1797 -165864 327 -132 955 -130 557 -100 229 -98 263 -66 129 -66 461 -198 265 -134 559 -100 165 -266 263 -166 529 -132 327 -130 65 -196 395 -100 165 -98 327 -66 395 -130 521 -66 693 -66 331 -66 365 -132 499 -66 331 -66 231 -164 659 -66 493 -198 263 -98 131 -100 695 -100 133 -98 431 -100 333 -234 265 -166 165 -68 729 -66 99 -66 459 -98 197 -98 327 -100 295 -166 231 -132 299 -66 331 -66 199 -132 197 -100 663 -98 461 -68 329 -196 295 -98 393 -100 459 -100 1701 -98 13057 -100 395 -66 397 -100 663 -100 563 -100 131 -66 331 -100 297 -100 593 -164 701 -66 297 -66 365 -66 331 -98 459 -130 361 -132 495 -264 331 -66 329 -64 165 -66 329 -266 331 -164 301 -266 97 -68 529 -66 233 -132 431 -132 231 -68 267 -66 199 -66 599 -134 65 -100 197 -66 1317 -98 165 -132 523 -130 131 -100 295 -460 65 -164 623 -164 293 -132 331 -166 1051 -130 227 -230 329 -134 265 -66 827 -98 525 -132 97 -66 497 -66 1097 -100 595 -66 99 -166 495 -66 525 -132 327 -100 329 -66 331 -68 263 -436 65 -100 929 -168 465 -266 731 -100 261 -66 525 -66 65 -66 295 -132 591 -98 623 -100 231 -166 231 -396 97 -66 565 -66 499 -100 +RAW_Data: 231 -66 363 -66 2355 -16412 431 -66 1061 -926 99 -594 229 -528 97 -68 501 -100 197 -100 67 -166 22923 -64 3651 -66 25605 -100 23205 -68 10819 -98 327 -164 195 -132 425 -166 231 -134 597 -200 131 -68 431 -66 331 -132 329 -66 301 -100 297 -100 233 -132 529 -66 561 -232 395 -98 295 -98 521 -66 329 -166 467 -200 231 -364 99 -100 299 -198 395 -98 625 -68 131 -98 229 -98 295 -98 329 -98 557 -98 131 -66 491 -134 65 -100 233 -200 365 -232 459 -198 759 -98 165 -100 297 -134 431 -66 397 -134 195 -166 597 -66 1755 -66 955 -132 361 -132 293 -98 65 -66 197 -298 231 -100 559 -98 427 -132 655 -66 6771 -66 8835 -66 567 -68 625 -66 663 -100 493 -130 725 -66 63 -100 263 -134 299 -364 429 -100 395 -100 163 -100 297 -100 199 -66 531 -98 497 -132 595 -132 363 -66 261 -198 395 -130 425 -198 131 -166 429 -264 397 -166 727 -66 725 -132 361 -164 721 -132 195 -134 1151 -132 397 -98 21075 -134 267 -200 1097 -98 297 -100 725 -132 265 -200 597 -66 333 -198 97 -598 197 -66 233 -134 297 -234 659 -64 165 -66 227 -98 197 -100 327 -130 559 -98 991 -100 263 -130 229 -132 359 -100 325 -132 263 -100 399 -98 963 -132 431 -66 761 -68 723 -66 395 -130 493 -100 165 -66 297 -334 97 -132 787 -100 491 -232 949 -100 431 -232 99 -132 233 -164 299 -100 329 -64 233 -100 333 -66 365 -66 661 -200 393 -134 263 -102 297 -132 467 -66 493 -100 631 -66 265 -68 331 -100 295 -162 163 -164 395 -66 697 -66 663 -134 463 -168 65 -66 963 -66 265 -132 199 -134 723 -100 821 -66 163 -130 457 -362 97 -66 461 -98 563 -66 725 -132 727 -100 661 -66 1231 -100 231 -366 2455 -98 689 -132 635 -132 99 -166 729 -66 233 -100 465 -132 561 -168 697 -200 295 -298 65 -198 131 -66 329 -66 231 -134 465 -166 265 -132 299 -200 825 -66 361 -66 129 -198 667 -98 1021 -66 697 -100 397 -68 265 -100 429 -234 99 -98 953 -98 985 -166 293 -66 263 -66 131 -132 295 -66 723 -66 497 -66 265 -166 565 -68 465 -132 65 -66 531 -132 597 -166 331 -66 299 -66 65 -100 1461 -100 463 -66 293 -328 133 -132 499 -66 263 -100 167 -198 267 -66 993 -100 327 -98 493 -164 97 -98 263 -100 427 -66 229 -64 261 -98 325 -100 361 -66 393 -132 595 -164 363 -164 163 -66 131 -134 231 -66 731 -66 1025 -100 563 -134 131 -66 229 -134 233 -100 693 -100 299 -166 299 -134 693 -66 359 -166 499 -100 +RAW_Data: 231 -100 23109 -60820 133 -10320 631 -5396 883 -1106 1887 -1072 943 -1040 957 -1012 987 -1010 957 -1012 983 -1002 999 -1012 977 -984 1003 -2002 999 -978 983 -1010 1977 -1002 1007 -968 999 -1996 1999 -986 995 -1012 977 -984 1001 -2000 999 -982 1979 -2010 983 -1010 989 -978 991 -1012 985 -982 1021 -976 1979 -1006 1013 -982 997 -976 1019 -1968 1005 -1000 1965 -2014 1967 -1016 993 -1970 1005 -998 973 -1014 999 -974 1011 -976 995 -8958 2009 -984 1007 -1968 997 -1008 1965 -994 1009 -984 1007 -986 1011 -962 1009 -984 1009 -1002 1007 -978 1003 -976 1009 -1970 1011 -982 1009 -988 2001 -972 997 -1000 1005 -1968 1985 -1006 1007 -978 999 -976 1009 -1996 983 -986 1989 -2014 973 -1014 1003 -974 1009 -974 997 -986 1023 -970 1987 -1014 981 -1010 989 -984 985 -2002 1009 -960 1995 -2002 1995 -986 997 -1970 1003 -1004 1007 -978 999 -980 1013 -978 1017 -8916 2003 -1016 973 -2002 1001 -976 2013 -988 975 -1020 975 -986 1005 -1000 977 -1014 1001 -974 1007 -1000 967 -1006 997 -2002 971 -1006 1009 -980 1993 -980 1003 -986 1005 -1992 1977 -1002 975 -1014 1001 -974 1009 -1994 975 -988 1993 -2010 973 -1014 997 -980 987 -1008 983 -1018 995 -978 1975 -1006 1009 -978 999 -1008 991 -1966 1005 -998 1993 -1980 2005 -988 995 -1968 1005 -1000 1009 -978 1001 -976 1009 -980 1015 -8920 8017 -3892 1773 -165206 65 -396 99 -332 65 -100 133 -364 329 -100 199 -102 791 -100 261 -264 131 -164 295 -130 261 -100 197 -130 625 -98 299 -166 199 -100 531 -66 823 -100 327 -98 395 -98 825 -200 889 -100 295 -328 131 -98 295 -166 329 -64 393 -100 523 -66 263 -130 427 -98 463 -100 363 -166 67 -66 231 -134 331 -98 629 -132 265 -130 99 -66 953 -98 919 -296 591 -132 129 -132 591 -330 361 -166 763 -98 167 -68 265 -132 265 -130 133 -100 333 -132 295 -132 459 -132 393 -66 555 -132 263 -164 97 -64 229 -100 131 -100 329 -66 425 -64 1717 -132 7375 -66 5079 -230 199 -132 467 -66 925 -100 197 -100 231 -66 829 -166 231 -166 333 -200 359 -100 393 -164 291 -134 461 -132 325 -166 99 -100 329 -100 195 -166 399 -232 165 -98 729 -66 531 -132 99 -98 661 -332 65 -100 761 -66 427 -162 629 -132 559 -66 327 -166 597 -100 163 -66 263 -100 553 -100 789 -230 591 -198 65 -362 263 -66 327 -98 425 -66 261 -166 753 -196 889 -134 429 -134 333 -134 429 -66 231 -100 131 -100 463 -130 131 -66 753 -164 361 -64 131 -260 263 -66 359 -98 295 -100 295 -166 1163 -66 267 -68 329 -134 99 -198 233 -134 231 -132 557 -196 65 -100 263 -98 391 -98 329 -98 129 -66 diff --git a/assets/unit_tests/subghz/phoenix_v2.sub b/assets/unit_tests/subghz/phoenix_v2.sub new file mode 100644 index 00000000..3adf8aa2 --- /dev/null +++ b/assets/unit_tests/subghz/phoenix_v2.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Phoenix_V2 +Bit: 52 +Key: 00 0F 0F BD 7E 7F 10 AE diff --git a/assets/unit_tests/subghz/phoenix_v2_raw.sub b/assets/unit_tests/subghz/phoenix_v2_raw.sub new file mode 100644 index 00000000..11e155bb --- /dev/null +++ b/assets/unit_tests/subghz/phoenix_v2_raw.sub @@ -0,0 +1,9 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -465 9659 -100 885 -162 2507 -98 1805 -64 427 -98 1879 -198 489 -66 525 -98 851 -100 525 -66 819 -66 395 -98 459 -66 1155 -66 863 -68 265 -98 2359 -68 435 -166 2031 -66 829 -100 561 -66 701 -66 1365 -134 731 -132 655 -68 1623 -66 263 -98 1087 -98 591 -64 987 -132 299 -66 885 -66 1087 -66 2605 -100 303 -100 731 -132 1323 -66 267 -100 627 -66 431 -100 397 -100 1027 -66 559 -102 1565 -100 865 -66 629 -100 327 -66 1281 -98 851 -132 593 -132 429 -100 2931 -134 759 -298 2985 -132 1129 -66 1131 -166 235 -100 725 -66 199 -134 365 -64 293 -66 489 -130 15135 -132 899 -100 691 -100 1271 -134 1161 -66 1789 -100 293 -64 651 -130 719 -100 1061 -66 529 -66 501 -132 1523 -68 231 -68 895 -100 1557 -134 895 -66 2297 -68 4497 -134 1319 -64 525 -98 949 -66 463 -132 399 -134 1129 -66 263 -170 1027 -100 763 -98 265 -68 603 -66 563 -166 1221 -68 1253 -66 1163 -66 697 -134 699 -98 1929 -68 931 -100 399 -100 1589 -98 663 -98 261 -66 337 -100 497 -98 767 -66 471 -66 365 -66 1363 -68 967 -300 461 -66 879 -98 623 -98 623 -100 725 -98 1685 -66 1061 -264 1523 -66 665 -130 2239 -66 229 -130 1509 -66 763 -100 1301 -132 12207 -98 261 -98 3707 -66 323 -298 821 -66 623 -98 2467 -66 2177 -164 625 -100 629 -132 1263 -132 1027 -100 2093 -132 397 -98 1855 -66 2515 -64 391 -132 1163 -66 503 -68 499 -98 2383 -98 1143 -66 523 -66 821 -98 525 -98 163 -164 1775 -68 231 -134 2159 -100 3065 -66 401 -134 925 -200 165 -18904 99 -296 261 -1018 99 -198 235 -166 329 -298 133 -266 1877 -132 227 -198 797 -66 933 -100 1093 -98 197 -66 723 -98 293 -132 953 -66 1507 -132 261 -64 1183 -98 393 -66 461 -100 863 -66 463 -100 865 -66 2721 -200 11457 -66 4203 -100 969 -66 765 -234 229 -100 491 -66 433 -66 231 -134 1189 -100 1015 -96 1349 -66 687 -132 655 -100 365 -132 397 -132 761 -100 1393 -66 525 -100 397 -132 327 -68 495 -66 429 -68 533 -134 529 -66 565 -102 695 -102 1353 -66 793 -228 197 -100 801 -64 657 -98 1439 -98 393 -98 359 -130 2707 -100 1101 -166 1523 -100 961 -100 397 -66 1217 -66 531 -166 199 -102 365 -166 1863 -66 367 -66 361 -68 703 -100 763 -100 299 -66 501 -200 231 -100 2879 -98 2005 -66 825 -134 263 -66 1155 -132 929 -66 335 -100 13393 -100 4113 -266 99 -198 1357 -66 1015 -100 1627 -66 999 -132 1329 -132 529 -66 2591 -100 2545 -68 429 -100 499 -166 961 -66 467 -66 699 +RAW_Data: -100 44175 -12032 99 -400 99 -334 65 -490 165 -328 65 -298 367 -1488 131 -166 131 -264 65 -66 99 -558 197 -198 129 -164 263 -100 2897 -66 33999 -98 1313 -166 657 -132 261 -66 197 -98 1477 -98 229 -66 1319 -66 231 -68 1199 -66 763 -100 857 -134 1963 -18216 99 -500 165 -330 65 -360 163 -202 65 -100 99 -366 267 -402 163 -68 233 -164 65 -66 167 -396 365 -100 1527 -66 393 -132 757 -66 1759 -100 821 -98 233 -66 1197 -66 399 -130 495 -100 435 -100 365 -66 199 -66 6399 -66 3541 -132 369 -132 1361 -98 597 -132 729 -68 733 -66 693 -100 199 -68 565 -134 363 -66 917 -68 267 -134 757 -132 1415 -132 327 -98 1053 -68 897 -166 263 -296 793 -102 297 -98 391 -100 893 -100 233 -68 563 -100 729 -66 431 -66 403 -68 397 -66 601 -164 563 -66 861 -132 1793 -66 637 -134 297 -66 525 -66 1421 -66 265 -100 463 -66 835 -100 699 -100 1029 -132 265 -166 567 -164 2155 -66 263 -66 1945 -100 10073 -132 463 -100 799 -66 729 -100 1655 -66 233 -66 265 -134 1961 -132 733 -98 301 -134 367 -134 1033 -64 889 -166 1463 -100 1327 -100 573 -132 497 -100 903 -100 1951 -66 427 -132 895 -198 1387 -98 723 -66 363 -100 263 -134 263 -166 561 -300 363 -198 929 -132 999 -168 65 -134 867 -132 719 -64 6625 -100 3415 -66 1553 -132 757 -130 329 -66 1625 -66 199 -66 265 -100 365 -132 267 -66 235 -100 703 -100 431 -100 601 -68 565 -100 399 -100 365 -100 365 -98 633 -100 2067 -100 495 -98 1121 -66 1253 -100 433 -132 199 -132 567 -100 99 -98 829 -198 363 -98 1717 -66 301 -132 403 -66 1791 -66 401 -100 633 -98 697 -66 1127 -66 433 -66 965 -100 2485 -66 461 -66 899 -98 231 -100 763 -100 523 -198 361 -132 1911 -66 2307 -168 265 -100 659 -134 461 -66 1165 -68 1423 -66 2693 -66 499 -66 733 -134 893 -98 435 -134 2359 -132 197 -68 661 -100 231 -68 369 -100 231 -66 495 -132 1751 -68 233 -68 1765 -132 233 -130 491 -162 855 -98 2113 -100 333 -134 765 -132 529 -66 293 -66 987 -132 329 -64 293 -98 99 -98 361 -100 367 -166 267 -68 261 -100 997 -408 855 -456 845 -868 417 -882 397 -900 411 -416 869 -884 417 -844 441 -846 427 -456 843 -452 831 -876 409 -898 409 -430 837 -462 839 -462 847 -854 417 -876 437 -880 397 -886 385 -486 841 -874 411 -868 415 -892 383 -878 439 -418 847 -466 835 -882 427 -440 843 -870 417 -882 411 -878 435 -846 427 -886 385 -886 415 -880 427 -870 409 -454 837 -888 419 +RAW_Data: -448 837 -448 835 -880 413 -448 869 -414 885 -420 849 -450 843 -876 443 -440 841 -438 873 -862 413 -448 855 -876 413 -25804 2597 -422 879 -418 867 -884 419 -844 445 -876 399 -454 849 -886 417 -878 415 -872 419 -418 893 -410 865 -876 415 -876 437 -418 851 -450 875 -418 877 -874 405 -866 419 -884 413 -878 437 -418 883 -850 415 -882 425 -874 409 -884 419 -452 851 -418 885 -850 415 -452 859 -876 409 -898 407 -862 449 -844 449 -874 409 -872 435 -870 397 -882 425 -426 875 -868 413 -452 871 -418 881 -874 413 -448 839 -460 847 -424 879 -420 867 -890 417 -416 887 -418 885 -860 413 -450 869 -848 437 -25854 475 -3978 135 -398 65 -1128 225 -1058 225 -1032 299 -562 749 -994 307 -956 341 -946 369 -488 809 -494 803 -930 353 -932 383 -474 815 -510 815 -468 811 -928 377 -914 381 -914 407 -882 399 -454 839 -890 413 -900 409 -870 417 -880 425 -442 843 -476 841 -864 417 -454 867 -882 417 -876 411 -876 435 -846 429 -882 421 -886 419 -850 445 -872 411 -450 853 -878 419 -430 881 -418 867 -890 417 -416 889 -418 885 -432 871 -416 855 -878 451 -438 845 -458 843 -884 419 -446 837 -880 449 -25864 497 -9138 659 -1080 233 -1046 297 -976 295 -558 737 -578 763 -942 339 -972 341 -530 785 -492 807 -486 807 -916 397 -918 357 -922 387 -928 383 -482 801 -906 395 -898 407 -888 415 -882 411 -480 841 -448 839 -878 415 -448 871 -876 411 -880 437 -880 397 -884 423 -884 419 -882 413 -886 387 -912 397 -466 843 -866 413 -448 869 -456 837 -888 419 -450 837 -450 851 -450 871 -454 837 -888 419 -450 835 -450 871 -884 417 -448 837 -880 415 -172818 65 -1022 197 -1852 10287 -134 695 -132 263 -130 1717 -66 625 -100 1031 -68 301 -68 265 -100 665 -98 1059 -100 931 -66 1093 -132 333 -68 3011 -132 459 -66 659 -98 521 -98 1511 -98 163 -98 857 -132 231 -98 491 -66 587 -66 393 -98 1513 -98 263 -130 529 -100 299 -100 1545 -132 1313 -66 399 -68 299 -134 201 -68 265 -98 691 -132 1099 -66 427 -100 461 -264 427 -134 327 -100 227 -64 493 -66 633 -66 501 -66 2067 -228 595 -132 97 -66 231 -66 299 -66 925 -98 661 -100 433 -134 231 -166 999 -98 691 -66 197 -66 293 -66 265 -362 1557 -100 231 -134 265 -68 433 -66 2483 -66 333 -100 233 -166 917 -132 295 -132 949 -164 1775 -100 7629 -66 7259 -66 263 -202 1263 -100 265 -68 861 -166 365 -98 233 -164 569 -66 199 -100 399 -98 1189 -130 261 -100 655 -164 723 -264 231 -100 327 -130 395 +RAW_Data: -130 84629 -16530 199 -594 163 -562 65 -164 65 -230 1383 -100 1031 -66 427 -66 401 -68 265 -102 233 -134 923 -100 493 -66 555 -132 619 -66 3165 -66 463 -198 1025 -68 233 -100 957 -132 793 -134 1233 -100 1553 -98 431 -100 1429 -100 393 -164 259 -166 65 -100 297 -134 263 -68 1797 -66 887 -100 497 -100 565 -134 363 -96 1649 -130 393 -98 327 -100 563 -68 891 -68 24545 -68 231 -134 167 -100 131 -68 863 -102 627 -164 267 -166 631 -66 569 -100 797 -66 231 -166 301 -102 569 -134 1423 -66 1115 -66 759 -66 599 -68 1265 -268 527 -134 531 -98 269 -68 231 -66 325 -98 329 -66 399 -64 393 -98 459 -98 921 -66 457 -100 2541 -266 927 -68 231 -64 357 -98 495 -166 133 -100 529 -98 1091 -100 199 -66 597 -100 931 -66 663 -134 333 -166 763 -68 599 -100 333 -102 333 -232 363 -66 1491 -132 697 -134 629 -66 369 -66 561 -134 863 -98 895 -100 199 -98 763 -100 299 -168 561 -100 331 -100 467 -100 1231 -100 1821 -66 229 -66 325 -66 1419 -66 195 -66 553 -132 363 -164 857 -66 491 -66 227 -98 227 -98 529 -100 2093 -164 501 -132 1441 -98 1095 -100 263 -66 931 -66 1397 -66 1627 -98 433 -66 697 -200 363 -266 297 -100 301 -200 263 -98 565 -134 65 -102 297 -100 165 -100 929 -266 1325 -66 3213 -100 4403 -100 665 -134 1859 -66 631 -66 967 -200 165 -134 1551 -66 791 -100 331 -132 163 -64 163 -98 361 -132 557 -98 629 -66 885 -100 1087 -100 959 -100 535 -66 265 -66 1931 -66 465 -66 1165 -100 565 -100 865 -100 493 -66 597 -166 1293 -100 463 -68 563 -66 565 -66 595 -100 197 -68 929 -66 665 -134 399 -66 665 -100 1255 -166 935 -98 1791 -98 265 -136 501 -100 1581 -66 265 -66 567 -98 699 -134 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index df36e125..0a7d529c 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -102,4 +102,29 @@ RAW_Data: -450 1019 -482 1035 -480 1033 -460 1047 -476 1017 -484 1007 -484 1051 RAW_Data: -66 597 -100 231 -68 167 -66 1659 -100 733 -66 1631 -100 165 -66 199 -66 233 -166 165 -100 1925 -68 595 -198 1785 -134 2177 -134 131 -66 1049 -98 3087 -132 195 -64 589 -66 397 -134 329 -66 2565 -164 327 -100 689 -64 1775 -100 5183 -132 1187 -66 329 -66 395 -132 165 -98 261 -98 1247 -64 1217 -66 927 -66 997 -66 199 -98 1419 -66 531 -166 1231 -66 697 -100 97 -66 563 -66 161 -264 3205 -200 525 -98 293 -100 291 -100 133 -66 759 -66 659 -100 983 -64 523 -130 431 -166 919 -66 1097 -100 1757 -66 1119 -66 917 -98 2647 -166 1247 -66 165 -264 1189 -100 899 -134 597 -68 2323 -66 1893 -66 1095 -100 533 -64 965 -100 1817 -130 1215 -66 1879 -64 821 -164 1117 -132 263 -132 131 -66 557 -66 431 -132 661 -100 1183 -98 629 -100 1679 -132 259 -66 623 -98 431 -66 399 -164 923 -100 297 -66 165 -166 2521 -198 99 -66 431 -132 1225 -66 1063 -68 131 -136 631 -66 163 -100 99 -298 965 -68 465 -68 465 -298 2545 -134 2639 -230 1489 -66 299 -66 1991 -234 65 -132 693 -134 429 -102 101 -68 461 -66 3333 -64 1229 -68 333 -66 265 -66 885 -64 3163 -100 467 -66 2651 -164 1221 -100 1527 -66 1259 -134 431 -232 1259 -100 6029 -164 297 -98 1151 -66 1415 -100 5289 -66 2467 -100 493 -132 495 -200 1121 -66 129 -66 757 -166 327 -130 5477 -66 1227 -230 395 -100 265 -132 497 -132 1133 -132 361 -100 1051 -164 3089 -132 1583 -100 65 -68 2315 -100 529 -132 2157 -68 1257 -66 1975 -98 427 -98 1347 -66 719 -164 857 -66 165 -66 1029 -132 297 -132 467 -100 731 -130 1985 -98 199 -166 899 -100 1391 -166 3425 -100 261 -132 721 -66 4845 -98 1193 -68 1225 -66 721 -100 1015 -64 983 -66 557 -130 693 -98 99 -64 1091 -98 197 -100 2321 -66 431 -134 727 -66 467 -102 891 -98 167 -134 2619 -66 393 -64 97 -100 589 -98 1583 -164 301 -68 1481 -98 295 -98 959 -66 365 -98 1253 -66 231 -100 1255 -132 1813 -132 1645 -100 361 -132 395 -100 427 -164 1197 -98 1001 -100 861 -66 1161 -98 195 -100 197 -66 1429 -66 663 -66 1427 -98 665 -66 699 -100 663 -66 855 -196 161 -100 361 -98 823 -66 227 -66 621 -132 1853 -230 461 -230 623 -100 557 -98 229 -98 133 -134 1291 -66 533 -166 627 -134 195 -134 593 -64 591 -66 1019 -66 1049 -262 297 -100 2921 -66 133 -66 963 -134 165 -100 RAW_Data: -68 521674 -200 213 -258 217 -218 217 -246 193 -242 211 -466 451 -450 225 -212 211 -242 211 -238 243 -200 223 -254 419 -226 221 -432 251 -212 467 -240 203 -450 449 -246 205 -238 217 -218 217 -444 439 -478 431 -230 211 -462 247 -214 435 -454 449 -456 441 -442 471 -448 449 -246 205 -238 215 -220 217 -244 201 -254 217 -430 235 -220 461 -198 223 -442 275 -212 429 -458 245 -212 203 -228 253 -218 431 -448 451 -442 235 -218 463 -426 485 -214 209 -234 221 -254 219 -214 207 -228 253 -428 459 -418 245 -216 243 -194 241 -212 241 -212 211 -244 441 -228 215 -460 217 -244 435 -236 235 -440 439 -246 213 -208 229 -256 217 -430 445 -450 445 -236 253 -420 225 -232 429 -450 465 -448 439 -448 449 -454 453 -230 225 -214 241 -212 211 -242 211 -244 237 -442 237 -218 429 -230 221 -442 241 -246 425 -458 245 -212 203 -228 255 -218 427 -448 451 -446 235 -256 425 -462 417 -244 251 -208 201 -256 217 -218 209 -232 255 -420 451 -448 217 -242 211 -238 209 -234 221 -256 217 -218 437 -238 217 -464 203 -256 419 -226 223 -464 441 -244 213 -240 197 -254 219 -430 445 -452 479 -204 253 -420 225 -222 463 -452 449 -450 449 -452 447 -450 451 -238 215 -220 217 -244 199 -256 217 -218 207 -440 241 -254 423 -238 217 -462 197 -224 471 -448 225 -220 213 -242 211 -212 465 -446 449 -456 245 -214 437 -456 449 -232 233 -216 219 -254 207 -204 253 -218 219 -440 451 -456 237 -216 217 -254 209 -202 253 -218 219 -210 437 -244 253 -422 237 -218 423 -226 247 -430 451 -242 203 -228 253 -220 217 -442 449 -454 449 -240 217 -430 237 -218 461 -426 453 -450 465 -448 451 -440 443 -246 215 -250 201 -224 255 -218 215 -208 227 -442 239 -208 457 -238 219 -464 201 -256 425 -464 201 -256 217 -210 241 -216 451 -426 445 -486 207 -236 441 -430 479 -194 223 -256 217 -218 209 -234 253 -218 217 -438 449 -454 239 -216 217 -256 207 -202 255 -218 217 -210 439 -244 253 -424 235 -218 457 -224 229 -436 449 -230 221 -214 211 -242 211 -468 447 -450 449 -250 207 -454 239 -218 431 -446 451 -448 443 -486 447 -448 431 -240 207 -232 223 -254 219 -216 209 -230 253 -426 233 -234 433 -244 213 -432 237 -218 463 -446 221 -212 211 -242 239 -208 457 -456 451 -456 223 -212 467 -450 453 -204 253 -218 211 -240 215 -218 255 -208 201 -442 479 -452 203 -254 217 -212 241 -216 217 -256 207 -202 439 -240 245 -430 237 -218 463 -240 217 -428 465 -202 253 -218 213 -234 219 RAW_Data: -444 443 -452 455 -244 213 -440 237 -254 427 -450 429 -480 417 -454 477 -430 445 -244 217 -250 203 -224 253 -220 215 -206 221 -462 217 -212 467 -234 231 -440 239 -220 427 -446 219 -242 211 -244 237 -206 451 -458 451 -458 203 -254 427 -450 441 -246 213 -242 193 -244 211 -242 211 -212 241 -442 451 -454 245 -214 241 -198 255 -218 217 -210 225 -212 461 -246 213 -440 225 -242 435 -240 209 -456 457 -246 211 -204 229 -254 219 -428 445 -452 445 -238 253 -426 231 -232 435 -448 455 -448 439 -446 449 -456 451 -230 223 -214 241 -212 211 -242 211 -244 237 -442 237 -218 427 -232 221 -442 239 -246 427 -458 245 -212 205 -228 253 -220 427 -446 471 -442 237 -216 451 -462 411 -244 251 -216 205 -222 211 -242 211 -242 211 -480 453 -418 245 -252 207 -202 253 -218 219 -210 229 -256 425 -232 221 -430 251 -212 469 -204 235 -446 441 -246 213 -242 193 -244 211 -464 451 -456 441 -248 215 -450 201 -256 427 -452 457 -450 455 -450 439 -446 451 -238 217 -256 207 -202 255 -218 217 -210 231 -442 237 -244 423 -240 217 -464 203 -254 419 -454 227 -210 243 -212 241 -212 477 -418 453 -484 199 -226 431 -267004 97 -422 97 -164 65 -490 97 -2222 559 -66 163 -64 295 -100 497 -100 263 -98 361 -460 793 -66 1803 -100 339 -68 1733 - +RAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307 +RAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035 +RAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001 +RAW_Data: 1549 -1166 409 -1138 363 -1144 407 -1144 385 -408 1599 -1138 377 -430 1593 -1138 421 -1110 393 -1126 409 -1136 377 -1162 353 -24626 823 -1176 379 -1150 353 -1174 349 -1158 377 -454 1541 -450 1561 -1190 383 -1118 411 -398 1557 -460 1571 -450 1547 -482 1537 -450 1561 -482 1547 -470 1537 -450 1547 -482 1539 -1176 387 -422 1541 -474 1537 -448 1545 -444 1539 -450 1543 -1136 375 -414 1549 -1142 417 -1102 387 -1118 385 -1140 381 -406 1549 -1180 383 -392 1549 -1140 417 -1122 383 -1116 383 -1140 415 -1102 355 -24628 823 -1188 357 -1180 343 -1172 385 -1150 361 -440 1513 -450 1563 -1184 369 -1158 381 -448 1539 -448 1527 -482 1545 -456 1545 -450 1533 -454 1579 -450 1561 -458 1513 -450 1555 -1182 389 -390 1571 -436 1571 -418 1581 -446 1505 -456 1511 -1160 391 -390 1339 -1108 381 -1098 361 -1150 309 -1124 331 -416 1065 -1142 353 -420 1049 -1148 319 -1156 345 -1128 347 -1134 317 -1138 319 -24716 687 -1178 309 -1138 317 -1166 319 -1162 327 -408 1039 -448 1067 -1152 321 -1132 313 -436 1065 -426 1039 -442 1039 -448 1049 -418 1041 -450 1035 -426 1039 -444 1041 -418 1047 -1144 339 -402 1069 -414 1071 -422 1051 -450 1029 -424 1069 -1144 335 -418 1043 -1178 319 -1134 321 -1174 309 -1164 313 -454 1049 -1148 317 -410 1051 -1160 353 -1124 321 -1166 349 -1122 329 -1178 277 -24734 677 -1178 299 -1182 309 -1154 337 -1144 307 -430 1047 -448 1039 -1174 319 -1164 289 -452 1033 -444 1031 -450 1035 -426 1063 -412 1047 -448 1041 -446 1033 -426 1041 -438 1049 -1152 289 -438 1071 -414 1031 -448 1039 -460 1033 -414 1079 -1146 309 -428 1049 -1178 311 -1154 337 -1144 329 -1162 319 -408 1051 -1160 353 -404 1059 -1164 317 -1154 325 -1140 347 -1152 333 -1138 285 -24770 657 -1178 315 -1162 289 -1184 313 -1152 329 -446 1039 -416 1047 -1170 343 -1128 351 -418 1021 -454 1047 -416 1061 -422 1033 -454 1043 -446 1039 -416 1065 -414 1041 -424 1033 -1178 301 -450 1041 -416 1051 -452 1045 -422 1033 -454 1043 -1172 283 -436 1065 -1146 329 -1176 311 -1150 337 -1148 309 -430 1049 -1174 299 -432 1061 -1180 289 -1178 311 -1162 347 -1150 307 -1160 311 -24722 607 -1296 203 -1264 207 -1266 227 -1252 239 -498 969 -506 1013 -1214 275 -1166 289 -480 1005 -484 1003 -476 1009 -454 1009 -480 1025 -458 1003 -448 1045 -448 1009 -450 1035 -1146 321 -444 1033 -444 1029 -444 1041 -442 1041 -416 1081 -1148 343 -396 1081 -1146 345 -1120 341 -1150 345 -1120 343 -420 1077 -1146 321 -420 1053 -1152 355 -1122 321 -1162 349 -1150 297 -1178 311 -24720 523 -4340 133 -1370 99 -636 67 -1406 393 -98 397 -1320 207 -1260 225 -498 975 -522 937 -540 977 -488 973 -510 987 -490 1005 -456 1003 -476 +RAW_Data: 1015 -450 1005 -1194 289 -454 1011 -478 1009 -478 1007 -450 1033 -446 1035 -1158 353 -404 1057 -1168 309 -1162 349 -1150 309 -1158 313 -452 1047 -1146 321 -420 1059 -1152 355 -1126 319 -1160 345 -1128 353 -1152 309 -78850 165 -1196 97 -3256 65 -626 165 -130 525 -66 625 -430 891 -492 163 -792 163 -66 197 -100 595 -132 229 -15220 165 -464 97 -66 197 -264 99 -998 67 -198 195 -132 65 -296 163 -198 65 -198 691 -66 985 -134 97 -66 1485 -15420 197 -200 63 -132 97 -526 231 -64 263 -754 425 -198 97 -166 97 -132 495 -100 555 -164 391 -98 261 -98 1221 -9074 97 -2604 65 -1942 195 -590 853 -132 1225 -66 987 -1058 97 -100 131 -132 593 -98 425 -66 3965 -11360 65 -730 299 -98 99 -66 133 -232 327 -132 65 -456 163 -132 97 -196 327 -364 561 -264 1851 -234 1191 -8710 65 -596 163 -134 99 -234 97 -168 131 -496 165 -202 65 -20716 231 -68 231 -134 363 -100 133 -132 133 -198 231 -168 131 -166 99 -162 131 -196 295 -66 261 -166 955 -98 695 -66 1215 -9510 65 -3698 165 -328 65 -492 131 -66 129 -692 231 -64 163 -132 163 -98 229 -132 131 -134 97 -100 563 -15732 197 -796 165 -132 99 -562 97 -168 295 -462 99 -66 131 -332 133 -100 299 -66 329 -132 301 -68 267 -12994 299 -98 197 -228 295 -922 63 -988 65 -100 129 -164 823 -98 931 -66 331 -98 955 -9454 231 -100 163 -134 99 -796 133 -628 263 -430 67 -364 399 -98 365 -66 889 -66 5041 -4044 131 -166 265 -298 231 -98 197 -232 197 -164 163 -198 197 -98 65 -132 3525 -15068 67 -698 133 -1056 199 -2322 959 -200 297 -232 1223 -13532 65 -1650 197 -198 65 -330 129 -100 295 -164 65 -66 131 -1290 99 -134 231 -262 889 -98 231 -64 489 -66 663 -66 563 -15458 97 -362 229 -98 165 -496 131 -266 65 -98 165 -832 729 -66 133 -18682 231 -298 263 -132 363 -132 195 -132 361 -232 197 -1480 131 -164 163 -12854 65 -2522 299 -166 1357 -132 99 -98 399 -166 329 -264 395 -64 195 -196 1055 -132 1417 -14994 197 -132 459 -824 131 -428 133 -832 231 -200 65 -432 231 -100 467 -66 1151 -100 1741 -8616 97 -6904 1085 -198 261 -196 261 -232 265 -132 563 -166 65 -166 197 -100 331 -134 2009 -8664 97 -164 65 -64 263 -132 357 -64 97 -166 493 -166 165 -98 195 -130 361 -854 891 -66 365 -166 1019 -15320 97 -198 331 -166 1359 -266 229 -134 65 -100 331 -98 65 -132 265 -20774 65 -2222 97 -66 229 -132 161 -162 133 -18134 229 -198 65 -132 197 -200 263 -364 97 -100 427 -526 65 -460 131 -428 +RAW_Data: 655 -98 2625 -8562 99 -432 97 -1924 67 -632 199 -498 65 -100 1819 -132 197 -228 263 -232 133 -296 229 -66 97 -20434 199 -432 65 -764 65 -232 233 -232 133 -334 463 -232 329 -98 357 -130 131 -132 1423 -4018 133 -1984 65 -2926 99 -930 97 -430 1293 -100 531 -66 493 -100 131 -100 429 -134 465 -132 3063 -9636 67 -3696 97 -132 229 -298 131 -694 627 -132 1247 -132 297 -166 133 -66 199 -166 663 -21440 133 -400 131 -130 99 -66 531 -232 229 -134 131 -202 165 -564 131 -1258 65 -100 133 -132 299 -166 1325 -66 833 -66 1521 -9032 97 -1544 295 -98 231 -164 261 -66 131 -394 361 -296 163 -298 463 -66 195 -96 721 -13330 65 -468 99 -134 65 -4696 199 -166 659 -98 361 -198 229 -132 557 -166 625 -164 229 -66 329 -100 131 -130 263 -66 1221 -8436 329 -198 99 -66 99 -66 165 -398 65 -1326 97 -794 165 -592 131 -66 265 -266 99 -430 2421 -100 465 -100 199 -66 699 -100 65 -132 1351 -66 897 -130 1653 -15200 231 -264 195 -296 99 -328 295 -296 163 -98 129 -98 295 -264 131 -398 65 -232 97 -98 887 -132 3157 -12396 199 -134 131 -66 231 -200 267 -132 265 -500 97 -732 131 -200 165 -396 763 -166 859 -66 1391 -9164 65 -3808 165 -66 131 -1692 65 -100 1017 -330 65 -132 65 -196 685 -198 65 -198 165 -98 231 -68 99 -22854 231 -168 67 -200 65 -66 263 -100 201 -302 65 -134 65 -22948 165 -396 263 -134 131 -68 165 -862 231 -1494 1261 -66 399 -302 3089 -6572 65 -396 65 -4140 7317 -5010 7415 -15982 811 -1174 381 -1122 381 -1148 351 -1164 345 -466 1509 -482 1541 -1176 387 -1144 363 -416 1519 -478 1525 -470 1533 -460 1539 -444 1535 -460 1531 -476 1527 -448 1513 -482 1509 -1178 379 -398 1549 -444 1539 -450 1547 -446 1541 -450 1545 -1142 381 -426 1529 -1172 381 -1140 387 -1114 395 -1130 379 -438 1531 -1172 381 -398 1555 -1192 351 -1146 407 -1122 381 -1146 381 -1126 349 -24662 821 -1174 379 -1122 383 -1146 415 -1102 385 -426 1567 -432 1567 -1178 381 -1120 385 -444 1511 -482 1515 -480 1539 -452 1545 -446 1545 -486 1541 -442 1555 -456 1495 -466 1531 -1148 387 -422 1509 -466 1533 -456 1539 -470 1503 -458 1539 -1164 351 -424 1509 -1190 353 -1148 369 -1130 379 -1136 317 -404 1331 -1128 319 -406 1231 -1140 337 -1114 349 -1122 329 -1140 349 -1120 293 -24712 627 -1236 237 -1226 233 -1230 271 -1228 239 -480 1017 -452 1039 -1184 293 -1172 275 -464 1041 -444 1007 -450 1033 -448 1035 -430 1047 -450 1031 -428 1047 -444 1005 -466 1015 -1158 289 -460 1047 -430 1033 -440 1039 -442 1033 -442 1037 -1168 309 -436 1051 -1140 +RAW_Data: 343 -1158 311 -1152 347 -1120 341 -420 1041 -1178 319 -422 1051 -1150 321 -1152 345 -1126 351 -1118 345 -1158 313 -24714 689 -1170 317 -1154 323 -1138 349 -1154 301 -448 1037 -416 1083 -1150 345 -1124 313 -454 1047 -418 1031 -446 1045 -448 1037 -416 1061 -412 1043 -442 1053 -426 1039 -436 1033 -1170 309 -418 1039 -454 1049 -418 1059 -424 1049 -452 1029 -1158 319 -454 1031 -1152 353 -1152 289 -1176 311 -1162 349 -418 1051 -1148 319 -454 1027 -1152 353 -1124 319 -1162 347 -1152 333 -1144 311 -24722 685 -1138 343 -1138 329 -1162 353 -1130 325 -412 1069 -414 1085 -1150 311 -1160 311 -422 1081 -416 1031 -444 1049 -414 1071 -416 1063 -430 1037 -440 1039 -412 1049 -450 1039 -1140 351 -420 1049 -418 1065 -420 1045 -448 1041 -414 1053 -1148 345 -398 1085 -1140 315 -1166 355 -1126 319 -1158 351 -398 1071 -1140 317 -432 1071 -1138 343 -1130 339 -1144 331 -1160 319 -1162 289 -24740 693 -1130 353 -1156 319 -1136 347 -1150 301 -448 1041 -416 1063 -1178 297 -1180 311 -428 1047 -448 1037 -416 1065 -428 1037 -442 1051 -428 1039 -440 1051 -428 1037 -434 1033 -1172 311 -414 1071 -420 1049 -428 1035 -440 1069 -414 1051 -1150 343 -398 1083 -1148 345 -1120 341 -1148 347 -1122 341 -416 1071 -1148 319 -420 1061 -1152 353 -1126 321 -1158 349 -1150 297 -1176 277 -24762 631 -1238 239 -1232 271 -1230 243 -1210 271 -470 1007 -476 993 -1222 281 -1202 269 -480 1011 -454 1035 -446 1039 -446 1041 -416 1047 -450 1045 -420 1033 -454 1015 -444 1051 -1154 321 -408 1071 -414 1065 -398 1065 -440 1049 -428 1037 -1172 317 -434 1069 -1138 317 -1152 327 -1170 315 -1164 319 -410 1081 -1162 319 -404 1061 -1166 317 -1154 325 -1140 347 -1152 333 -1148 311 -24742 691 -1138 341 -1138 317 -1164 353 -1124 319 -434 1069 -412 1069 -1148 325 -1140 347 -398 1075 -414 1069 -414 1051 -452 1047 -422 1051 -416 1057 -428 1049 -450 1029 -420 1033 -1166 335 -418 1041 -416 1063 -412 1079 -412 1071 -414 1065 -1148 325 -410 1069 -1142 351 -1152 309 -1160 349 -1116 329 -436 1051 -1162 319 -426 1051 -1148 327 -1172 311 -1156 337 -1150 345 -1120 305 -83468 65 -1920 133 -98 397 -1062 199 -464 165 -728 165 -168 97 -466 497 -132 1123 -66 3163 -12612 229 -198 65 -134 363 -66 67 -132 97 -896 197 -132 199 -166 165 -166 67 -134 165 -334 365 -134 2963 -15534 231 -132 231 -168 165 -262 97 -266 65 -724 65 -166 97 -198 295 -98 131 -132 563 -100 1483 -8892 99 -954 131 -234 67 -432 1087 -98 687 -132 163 -8794 165 -6460 165 -332 331 -498 99 -200 199 -266 67 -100 131 -596 165 -332 67 -98 299 -100 265 -68 633 -100 5793 -4102 99 -198 65 -566 +RAW_Data: 65 -658 99 -132 165 -332 167 -198 99 -132 133 -2530 65 -166 795 -300 197 -366 227 -262 361 -66 2073 -20762 65 -598 231 -264 97 -592 327 -132 295 -132 297 -100 363 -68 763 -20474 63 -166 757 -200 391 -100 97 -134 131 -134 591 -98 261 -262 229 -64 195 -100 195 -100 65 -132 261 -100 919 -66 333 -13642 65 -2324 131 -130 193 -462 195 -930 865 -66 2157 -100 1923 -12176 65 -168 197 -196 131 -1250 65 -132 391 -232 659 -66 393 -100 459 -230 197 -296 3479 -12728 99 -166 131 -134 131 -366 131 -98 97 -198 263 -164 231 -2074 693 -66 597 -66 433 -98 2509 -15286 65 -198 65 -198 165 -200 461 -132 757 -16060 65 -6768 199 -98 131 -262 163 -130 197 -198 165 -398 233 -334 65 -132 131 -166 331 -134 231 -564 365 -66 265 -100 465 -66 433 -100 6915 -6612 99 -264 65 -230 63 -4800 97 -66 393 -134 65 -100 165 -100 265 -66 165 -1818 99 -230 331 -66 265 -13068 131 -100 295 -298 427 -132 131 -1530 65 -100 165 -500 165 -132 995 -68 1685 -15810 263 -130 163 -462 231 -100 233 -632 165 -528 327 -196 197 -198 1253 -100 3583 -6278 97 -788 99 -3200 65 -166 133 -438 99 -132 165 -300 329 -166 1723 -134 725 -164 133 -16880 97 -960 99 -696 65 -200 231 -132 299 -66 297 -164 131 -730 197 -23052 365 -66 99 -332 131 -166 297 -494 429 -1326 265 -132 295 -66 367 -232 263 -66 857 -15396 395 -130 229 -98 233 -132 331 -364 65 -100 529 -68 231 -830 297 -100 233 -66 8533 -5582 101 -168 67 -3968 231 -66 129 -132 163 -1154 97 -166 199 -166 233 -132 467 -134 263 -66 431 -66 363 -66 957 -100 1821 -132 497 -132 1159 -15358 397 -66 99 -98 65 -66 265 -564 65 -68 97 -166 99 -100 165 -134 297 -896 165 -330 97 -134 963 -132 4737 -7096 65 -5664 265 -266 97 -166 265 -598 65 -332 65 -66 65 -198 231 -132 329 -100 65 -17202 99 -1564 595 -98 329 -198 227 -66 459 -230 97 -1480 63 -66 131 -166 99 -166 931 -8662 133 -398 99 -134 265 -98 299 -264 233 -66 99 -100 99 -66 97 -166 199 -166 265 -234 5785 -10012 163 -2324 331 -196 331 -134 65 -100 297 -100 165 -100 131 -332 163 -302 297 -164 199 -300 199 -166 229 -68 99 -68 959 -15384 365 -134 263 -1818 229 -698 99 -232 131 -100 789 -66 491 -132 4039 -12084 99 -134 331 -796 265 -132 265 -362 167 -2310 65 -98 131 -100 525 -164 295 -15032 297 -560 197 -330 131 -196 397 -266 197 -200 263 -68 261 -496 329 -166 361 -166 1517 -66 331 -10126 97 -3736 99 -1626 131 -100 +RAW_Data: 3487 -68 18413 -66 6689 -17938 65 -68 495 -98 1129 -860 697 -166 395 -100 463 -100 163 -14692 263 -794 65 -64 99 -2922 131 -200 97 -168 99 -100 199 -962 495 -68 165 -98 299 -198 133 -168 3917 -98 963 -132 461 -100 1089 -166 331 -134 633 -164 201 -100 363 -164 335 -200 265 -68 531 -166 699 -132 529 -166 397 -98 895 -168 65 -100 399 -366 463 -66 197 -134 431 -66 163 -98 623 -166 301 -98 263 -98 263 -66 197 -98 329 -98 525 -66 331 -200 1025 -66 629 -132 763 -166 233 -66 431 -132 133 -100 429 -264 165 -132 299 -166 429 -68 4605 -134 593 -134 4917 -60834 167 -10282 1941 -1122 865 -2086 921 -1072 1921 -1042 939 -1056 945 -1044 939 -1052 945 -1024 971 -1024 973 -1002 969 -1010 989 -1996 1007 -994 979 -1014 1965 -1014 977 -1020 969 -2004 1993 -982 1011 -982 1005 -984 977 -2008 981 -1006 1993 -1966 999 -994 1007 -982 1005 -984 1007 -996 975 -1016 1987 -996 973 -1016 969 -1004 1013 -1970 1011 -984 1999 -1970 1995 -984 1021 -1968 1005 -994 1003 -970 999 -984 1023 -974 1015 -8920 1995 -1014 1001 -1966 1013 -986 1991 -986 1007 -996 973 -1012 999 -978 985 -1006 1005 -984 997 -978 1015 -1000 973 -1982 1025 -982 985 -1008 1973 -1004 1007 -982 983 -1998 2009 -972 1009 -980 999 -978 1011 -1970 1011 -986 1999 -1978 1007 -988 1007 -994 975 -1012 1003 -972 1009 -978 1987 -986 1007 -982 1017 -994 971 -1990 1009 -984 1997 -1978 1999 -1010 993 -1964 1005 -1000 1007 -980 997 -984 1017 -972 999 -8932 2031 -980 989 -1988 985 -1016 1983 -986 1007 -994 975 -1016 975 -998 977 -1012 999 -982 985 -1006 983 -1018 993 -1968 1017 -996 975 -1016 1965 -1012 979 -1018 971 -2004 1991 -978 1003 -984 1015 -992 973 -2004 1005 -976 1991 -2008 975 -986 1005 -998 977 -1012 999 -978 985 -1010 1973 -1004 1013 -980 981 -1004 1007 -1996 975 -986 1993 -2006 1961 -1002 1013 -1974 985 -1008 1011 -978 997 -1006 981 -1012 991 -8918 8005 -3924 1887 -174620 131 -3974 131 -298 559 -230 65 -132 265 -98 863 -168 333 -66 299 -234 463 -166 331 -102 697 -200 199 -98 265 -100 663 -166 331 -66 599 -132 99 -66 1261 -66 399 -100 265 -100 1199 -66 265 -166 101 -66 599 -232 197 -100 561 -66 499 -132 797 -132 427 -66 265 -298 465 -66 565 -198 97 -100 695 -100 531 -132 267 -66 429 -98 231 -100 331 -100 333 -98 363 -198 67 -100 165 -100 231 -100 729 -134 1061 -100 493 -164 665 -132 259 -128 257 -130 329 -98 361 -66 263 -100 227 -66 491 -98 295 -166 229 -132 229 -130 263 -130 233 -100 331 -232 99 -100 165 -66 667 -164 795 -66 695 -68 429 -132 +RAW_Data: 559 -66 16863 -66 19215 -12276 65 -2086 131 -2188 65 -132 67 -166 299 -364 65 -100 131 -468 233 -200 497 -498 297 -100 363 -198 329 -130 327 -164 229 -64 427 -164 657 -132 829 -532 895 -66 297 -130 425 -166 363 -132 329 -100 465 -100 465 -198 65 -262 429 -98 495 -100 295 -528 131 -132 1129 -134 259 -130 327 -100 397 -66 261 -230 99 -66 229 -132 625 -68 131 -100 587 -66 329 -64 293 -130 1741 -132 325 -132 197 -98 261 -98 99 -132 561 -166 165 -66 391 -66 329 -98 231 -132 465 -100 331 -300 199 -132 299 -132 165 -164 229 -98 565 -100 395 -132 131 -100 327 -130 97 -98 163 -132 293 -98 557 -66 199 -166 233 -132 265 -166 627 -100 265 -132 595 -166 99 -98 199 -100 267 -166 795 -166 265 -166 297 -66 131 -166 331 -134 793 -164 263 -198 299 -100 265 -166 257 -100 197 -198 599 -132 333 -66 265 -100 229 -100 165 -204 465 -166 97 -232 365 -166 431 -68 199 -166 99 -236 365 -132 1297 -66 1361 -100 989 -98 297 -132 12467 -232 361 -100 263 -132 261 -196 229 -296 65 -66 329 -164 229 -164 263 -66 563 -232 3415 -132 11115 -100 97 -100 363 -164 99 -66 199 -100 99 -66 561 -164 259 -168 233 -132 333 -166 567 -98 297 -66 399 -66 399 -298 327 -132 685 -98 393 -98 819 -100 231 -270 429 -68 461 -132 131 -134 1121 -230 787 -132 1393 -132 233 -100 331 -66 497 -166 563 -196 425 -98 525 -66 723 -98 265 -134 365 -66 1297 -166 433 -98 165 -134 231 -134 263 -100 369 -198 99 -100 631 -634 331 -132 565 -132 265 -102 267 -98 467 -200 297 -130 731 -100 367 -98 229 -130 99 -98 263 -164 493 -100 297 -198 429 -66 425 -66 1021 -164 295 -198 97 -198 263 -132 425 -98 493 -98 197 -98 593 -264 65 -294 197 -66 295 -66 561 -196 565 -166 2675 -132 19393 -134 5403 -164 369 -166 467 -132 361 -526 395 -66 361 -132 529 -66 431 -66 465 -166 363 -98 65 -98 229 -98 359 -100 197 -132 201 -100 467 -166 397 -100 297 -130 229 -98 361 -98 199 -130 297 -66 659 -132 261 -98 819 -98 393 -132 329 -132 557 -132 163 -130 657 -66 295 -134 1189 -100 399 -102 861 -100 369 -132 331 -134 263 -666 65 -134 427 -64 197 -98 559 -68 297 -98 493 -164 197 -164 525 -66 131 -98 493 -66 595 -100 233 -100 265 -132 65 -66 465 -266 527 -98 855 -132 493 -66 393 -298 331 -100 131 -66 859 -198 495 -100 265 -98 367 -66 727 -196 535 -66 263 -66 397 -332 65 -198 331 -134 297 -330 +RAW_Data: 261 -100 3291 -68 2553 -19050 1855 -100 1259 -200 165 -100 39479 -100 233 -68 1027 -166 595 -66 231 -168 199 -98 265 -232 563 -166 795 -98 99 -460 65 -362 1017 -98 229 -98 625 -66 231 -98 161 -196 363 -98 363 -100 631 -132 297 -66 333 -300 99 -66 295 -230 985 -100 623 -132 1319 -100 65 -68 231 -232 197 -232 331 -100 199 -168 567 -166 133 -232 823 -396 327 -132 855 -232 165 -100 401 -166 599 -198 167 -132 299 -198 663 -132 165 -134 67 -66 1687 -66 1705 -100 265 -232 297 -100 531 -66 333 -100 1263 -66 297 -164 299 -98 431 -398 97 -66 199 -296 231 -232 925 -100 131 -132 229 -164 361 -66 267 -166 197 -100 491 -132 261 -132 1183 -98 891 -100 265 -100 99 -100 725 -100 303 -60886 131 -10310 1911 -1226 749 -2146 899 -1084 1905 -1070 917 -1044 973 -1042 927 -1050 949 -1044 965 -1012 949 -1046 965 -1014 977 -2008 981 -980 995 -1012 1969 -1018 971 -1014 975 -2014 1967 -1016 971 -1014 977 -984 1003 -2006 973 -1000 2001 -1978 1013 -980 1001 -972 1009 -978 991 -1010 983 -1004 1989 -1012 983 -978 995 -1014 983 -2002 977 -984 2023 -1968 1997 -986 999 -1970 1003 -1000 1009 -978 1017 -974 1009 -978 1001 -8948 1975 -1016 973 -2004 999 -978 2001 -986 1001 -978 1009 -986 1001 -1010 975 -998 1011 -992 975 -982 1005 -1000 1009 -1970 1011 -982 1009 -984 2003 -978 1001 -976 1015 -1966 1999 -1010 989 -982 989 -1004 1001 -1978 1013 -978 2005 -1966 1019 -972 1011 -980 997 -976 1011 -1004 999 -966 1997 -1000 999 -1002 975 -986 999 -1994 1007 -980 1993 -1978 2001 -1008 991 -1970 1009 -998 975 -1016 971 -1006 1013 -980 1001 -8922 2001 -994 1009 -1972 997 -1012 1971 -984 1005 -1012 977 -998 1009 -978 1011 -984 977 -1012 979 -1016 977 -1010 977 -2010 981 -1006 971 -1020 1985 -984 1005 -978 999 -1990 1997 -980 997 -1010 981 -1014 989 -1976 1005 -966 1995 -1998 997 -982 1021 -972 1003 -986 997 -1012 975 -982 1995 -1014 999 -980 983 -1004 1003 -1976 1007 -980 1983 -2012 1983 -998 977 -2010 973 -1014 973 -1018 973 -1014 975 -1016 971 -8956 7999 -3926 1865 -177334 165 -166 199 -496 431 -66 1319 -132 297 -164 457 -68 231 -100 863 -98 603 -100 663 -100 789 -262 491 -166 729 -100 795 -364 365 -132 293 -100 197 -98 625 -200 231 -132 229 -132 227 -100 755 -526 329 -134 793 -100 167 -200 335 -66 65 -134 265 -66 201 -66 1023 -100 63 -21396 133 -198 65 -66 263 -98 393 -134 65 -66 329 -68 199 -432 263 -394 131 -130 195 -66 687 -66 295 -164 229 -196 97 -98 559 -98 1283 -98 531 -66 331 -68 435 -100 393 -628 431 -132 99 -66 +RAW_Data: 329 -66 2519 -100 6759 -12690 463 -232 97 -3252 65 -2592 65 -132 99 -198 67 -496 99 -300 231 -132 233 -66 165 -132 131 -164 201 -66 4113 -66 1987 -68 15385 -98 8199 -16614 165 -1258 495 -1354 229 -166 297 -134 3653 -100 12533 -66 3759 -162 591 -132 361 -130 527 -100 327 -200 65 -66 461 -132 327 -132 461 -100 559 -198 263 -132 161 -98 329 -132 327 -68 395 -66 165 -132 163 -196 261 -66 361 -98 229 -132 301 -134 761 -66 399 -166 99 -66 461 -98 857 -98 131 -262 359 -132 199 -100 299 -166 363 -132 297 -132 199 -132 265 -166 133 -100 301 -268 463 -98 231 -134 265 -98 527 -298 265 -136 531 -132 363 -132 129 -130 527 -132 297 -66 297 -68 397 -466 99 -132 697 -98 233 -464 131 -132 365 -134 465 -98 635 -98 299 -66 497 -132 197 -132 489 -166 327 -98 1151 -130 295 -132 623 -164 97 -68 297 -100 1293 -100 1759 -66 1259 -132 729 -100 797 -66 99 -66 231 -98 529 -166 331 -166 299 -202 431 -134 467 -462 329 -462 1051 -98 625 -66 195 -100 461 -196 429 -132 197 -66 129 -66 229 -166 589 -164 629 -66 1053 -166 231 -98 263 -132 329 -132 267 -132 427 -130 65 -130 229 -166 4121 -66 15899 -19980 495 -264 165 -432 65 -132 65 -662 663 -100 793 -66 723 -164 565 -100 363 -166 199 -98 465 -100 299 -168 265 -198 695 -132 131 -268 497 -66 265 -134 199 -98 231 -134 499 -100 989 -132 429 -196 327 -132 131 -164 427 -66 263 -66 525 -132 1091 -100 793 -132 491 -66 195 -526 921 -134 363 -98 693 -230 165 -98 1711 -132 293 -66 659 -196 231 -66 261 -66 163 -66 1349 -166 363 -100 65 -100 299 -100 393 -66 495 -134 229 -98 131 -98 463 -362 361 -132 231 -66 491 -396 361 -132 97 -66 163 -198 229 -294 229 -198 165 -164 65 -100 623 -66 195 -98 261 -130 65 -98 563 -66 267 -166 1057 -632 531 -132 463 -100 663 -166 133 -100 569 -164 197 -66 431 -164 261 -132 363 -100 427 -164 263 -198 623 -66 589 -166 133 -166 199 -66 199 -268 65 -98 427 -66 163 -132 563 -198 363 -166 397 -98 1025 -66 197 -66 163 -132 693 -164 195 -66 427 -100 131 -66 433 -132 199 -402 231 -66 335 -100 993 -100 463 -232 65 -100 65 -66 7165 -66 6785 -66 895 -66 923 -198 97 -164 689 -166 361 -198 99 -66 753 -498 365 -66 567 -232 397 -66 199 -132 233 -200 995 -296 365 -166 597 -100 199 -134 99 -166 197 -332 431 -166 331 -66 199 -100 431 -232 493 -166 457 -98 231 -132 427 -230 559 -98 +RAW_Data: 261 -132 1051 -66 7501 -98 22107 -19232 2271 -166 133 -60834 165 -10284 595 -5446 885 -1106 1891 -1088 897 -1068 949 -1050 929 -1040 947 -1048 959 -1008 985 -1008 973 -1012 993 -2004 979 -998 975 -1014 1987 -998 977 -1014 971 -1998 2007 -974 1013 -976 999 -982 1023 -1962 1005 -1000 1969 -2010 977 -1016 971 -1004 1009 -980 985 -1002 979 -1014 1977 -1012 979 -1016 971 -1012 977 -1986 1021 -974 2005 -1968 2007 -976 1011 -1966 1009 -984 1009 -984 985 -1020 969 -1002 997 -8952 1995 -986 1017 -1988 989 -976 1979 -1028 975 -988 1009 -986 1007 -992 973 -1014 1001 -974 1011 -978 995 -1006 979 -2004 975 -1022 973 -984 1999 -1014 977 -984 1003 -2004 1995 -976 1005 -998 999 -982 981 -2002 983 -986 1989 -1998 1009 -976 999 -1010 991 -978 1013 -968 1011 -986 1995 -982 1023 -974 981 -1020 993 -1970 1007 -996 1995 -1982 1999 -986 993 -2002 985 -998 975 -1014 969 -1004 1009 -978 997 -8962 1989 -990 1009 -1954 1025 -984 2007 -962 1011 -980 1001 -982 1009 -1002 1007 -980 1005 -976 1007 -980 997 -976 1009 -2002 977 -988 1005 -982 2001 -976 1009 -988 1005 -1968 2029 -974 1003 -990 1011 -958 1005 -1972 1039 -978 1999 -1978 1009 -954 1037 -978 1007 -954 1039 -980 1011 -958 1999 -974 1031 -990 993 -972 1011 -1980 1011 -978 2009 -1968 1993 -982 1003 -1974 1007 -1006 1001 -986 985 -986 1001 -982 1033 -8924 8013 -3924 1851 -170400 493 -132 131 -164 65 -724 461 -164 693 -100 265 -100 229 -100 199 -98 363 -232 363 -100 131 -68 531 -166 165 -134 265 -66 727 -300 131 -198 99 -132 99 -134 597 -102 295 -130 131 -230 129 -98 689 -264 263 -134 231 -134 199 -132 131 -68 295 -100 163 -130 591 -98 327 -164 231 -66 589 -98 2077 -134 1389 -66 2913 -66 3045 -66 4463 -98 391 -132 489 -164 295 -66 523 -166 329 -98 195 -98 165 -100 165 -66 231 -100 895 -98 265 -132 595 -132 297 -494 261 -198 331 -100 131 -134 429 -68 231 -298 1193 -66 99 -100 627 -132 331 -98 393 -98 657 -132 163 -392 359 -132 163 -64 655 -164 263 -64 531 -98 265 -164 499 -100 229 -64 459 -100 199 -66 165 -130 197 -132 393 -66 163 -228 229 -132 65 -132 263 -100 357 -98 991 -328 595 -132 197 -130 759 -66 131 -166 267 -100 1251 -64 361 -66 395 -954 559 -132 295 -98 327 -100 295 -100 367 -198 391 -298 363 -132 495 -132 523 -66 97 -132 763 -198 131 -66 197 -100 525 -130 1059 -64 461 -166 563 -134 331 -66 463 -134 329 -198 199 -166 265 -266 1061 -66 331 -366 65 -66 1225 -100 299 -66 299 -132 133 -298 265 -100 1129 -364 163 -460 297 -132 297 -100 299 -100 +RAW_Data: -465 9659 -100 885 -162 2507 -98 1805 -64 427 -98 1879 -198 489 -66 525 -98 851 -100 525 -66 819 -66 395 -98 459 -66 1155 -66 863 -68 265 -98 2359 -68 435 -166 2031 -66 829 -100 561 -66 701 -66 1365 -134 731 -132 655 -68 1623 -66 263 -98 1087 -98 591 -64 987 -132 299 -66 885 -66 1087 -66 2605 -100 303 -100 731 -132 1323 -66 267 -100 627 -66 431 -100 397 -100 1027 -66 559 -102 1565 -100 865 -66 629 -100 327 -66 1281 -98 851 -132 593 -132 429 -100 2931 -134 759 -298 2985 -132 1129 -66 1131 -166 235 -100 725 -66 199 -134 365 -64 293 -66 489 -130 15135 -132 899 -100 691 -100 1271 -134 1161 -66 1789 -100 293 -64 651 -130 719 -100 1061 -66 529 -66 501 -132 1523 -68 231 -68 895 -100 1557 -134 895 -66 2297 -68 4497 -134 1319 -64 525 -98 949 -66 463 -132 399 -134 1129 -66 263 -170 1027 -100 763 -98 265 -68 603 -66 563 -166 1221 -68 1253 -66 1163 -66 697 -134 699 -98 1929 -68 931 -100 399 -100 1589 -98 663 -98 261 -66 337 -100 497 -98 767 -66 471 -66 365 -66 1363 -68 967 -300 461 -66 879 -98 623 -98 623 -100 725 -98 1685 -66 1061 -264 1523 -66 665 -130 2239 -66 229 -130 1509 -66 763 -100 1301 -132 12207 -98 261 -98 3707 -66 323 -298 821 -66 623 -98 2467 -66 2177 -164 625 -100 629 -132 1263 -132 1027 -100 2093 -132 397 -98 1855 -66 2515 -64 391 -132 1163 -66 503 -68 499 -98 2383 -98 1143 -66 523 -66 821 -98 525 -98 163 -164 1775 -68 231 -134 2159 -100 3065 -66 401 -134 925 -200 165 -18904 99 -296 261 -1018 99 -198 235 -166 329 -298 133 -266 1877 -132 227 -198 797 -66 933 -100 1093 -98 197 -66 723 -98 293 -132 953 -66 1507 -132 261 -64 1183 -98 393 -66 461 -100 863 -66 463 -100 865 -66 2721 -200 11457 -66 4203 -100 969 -66 765 -234 229 -100 491 -66 433 -66 231 -134 1189 -100 1015 -96 1349 -66 687 -132 655 -100 365 -132 397 -132 761 -100 1393 -66 525 -100 397 -132 327 -68 495 -66 429 -68 533 -134 529 -66 565 -102 695 -102 1353 -66 793 -228 197 -100 801 -64 657 -98 1439 -98 393 -98 359 -130 2707 -100 1101 -166 1523 -100 961 -100 397 -66 1217 -66 531 -166 199 -102 365 -166 1863 -66 367 -66 361 -68 703 -100 763 -100 299 -66 501 -200 231 -100 2879 -98 2005 -66 825 -134 263 -66 1155 -132 929 -66 335 -100 13393 -100 4113 -266 99 -198 1357 -66 1015 -100 1627 -66 999 -132 1329 -132 529 -66 2591 -100 2545 -68 429 -100 499 -166 961 -66 467 -66 699 +RAW_Data: -100 44175 -12032 99 -400 99 -334 65 -490 165 -328 65 -298 367 -1488 131 -166 131 -264 65 -66 99 -558 197 -198 129 -164 263 -100 2897 -66 33999 -98 1313 -166 657 -132 261 -66 197 -98 1477 -98 229 -66 1319 -66 231 -68 1199 -66 763 -100 857 -134 1963 -18216 99 -500 165 -330 65 -360 163 -202 65 -100 99 -366 267 -402 163 -68 233 -164 65 -66 167 -396 365 -100 1527 -66 393 -132 757 -66 1759 -100 821 -98 233 -66 1197 -66 399 -130 495 -100 435 -100 365 -66 199 -66 6399 -66 3541 -132 369 -132 1361 -98 597 -132 729 -68 733 -66 693 -100 199 -68 565 -134 363 -66 917 -68 267 -134 757 -132 1415 -132 327 -98 1053 -68 897 -166 263 -296 793 -102 297 -98 391 -100 893 -100 233 -68 563 -100 729 -66 431 -66 403 -68 397 -66 601 -164 563 -66 861 -132 1793 -66 637 -134 297 -66 525 -66 1421 -66 265 -100 463 -66 835 -100 699 -100 1029 -132 265 -166 567 -164 2155 -66 263 -66 1945 -100 10073 -132 463 -100 799 -66 729 -100 1655 -66 233 -66 265 -134 1961 -132 733 -98 301 -134 367 -134 1033 -64 889 -166 1463 -100 1327 -100 573 -132 497 -100 903 -100 1951 -66 427 -132 895 -198 1387 -98 723 -66 363 -100 263 -134 263 -166 561 -300 363 -198 929 -132 999 -168 65 -134 867 -132 719 -64 6625 -100 3415 -66 1553 -132 757 -130 329 -66 1625 -66 199 -66 265 -100 365 -132 267 -66 235 -100 703 -100 431 -100 601 -68 565 -100 399 -100 365 -100 365 -98 633 -100 2067 -100 495 -98 1121 -66 1253 -100 433 -132 199 -132 567 -100 99 -98 829 -198 363 -98 1717 -66 301 -132 403 -66 1791 -66 401 -100 633 -98 697 -66 1127 -66 433 -66 965 -100 2485 -66 461 -66 899 -98 231 -100 763 -100 523 -198 361 -132 1911 -66 2307 -168 265 -100 659 -134 461 -66 1165 -68 1423 -66 2693 -66 499 -66 733 -134 893 -98 435 -134 2359 -132 197 -68 661 -100 231 -68 369 -100 231 -66 495 -132 1751 -68 233 -68 1765 -132 233 -130 491 -162 855 -98 2113 -100 333 -134 765 -132 529 -66 293 -66 987 -132 329 -64 293 -98 99 -98 361 -100 367 -166 267 -68 261 -100 997 -408 855 -456 845 -868 417 -882 397 -900 411 -416 869 -884 417 -844 441 -846 427 -456 843 -452 831 -876 409 -898 409 -430 837 -462 839 -462 847 -854 417 -876 437 -880 397 -886 385 -486 841 -874 411 -868 415 -892 383 -878 439 -418 847 -466 835 -882 427 -440 843 -870 417 -882 411 -878 435 -846 427 -886 385 -886 415 -880 427 -870 409 -454 837 -888 419 +RAW_Data: -448 837 -448 835 -880 413 -448 869 -414 885 -420 849 -450 843 -876 443 -440 841 -438 873 -862 413 -448 855 -876 413 -25804 2597 -422 879 -418 867 -884 419 -844 445 -876 399 -454 849 -886 417 -878 415 -872 419 -418 893 -410 865 -876 415 -876 437 -418 851 -450 875 -418 877 -874 405 -866 419 -884 413 -878 437 -418 883 -850 415 -882 425 -874 409 -884 419 -452 851 -418 885 -850 415 -452 859 -876 409 -898 407 -862 449 -844 449 -874 409 -872 435 -870 397 -882 425 -426 875 -868 413 -452 871 -418 881 -874 413 -448 839 -460 847 -424 879 -420 867 -890 417 -416 887 -418 885 -860 413 -450 869 -848 437 -25854 475 -3978 135 -398 65 -1128 225 -1058 225 -1032 299 -562 749 -994 307 -956 341 -946 369 -488 809 -494 803 -930 353 -932 383 -474 815 -510 815 -468 811 -928 377 -914 381 -914 407 -882 399 -454 839 -890 413 -900 409 -870 417 -880 425 -442 843 -476 841 -864 417 -454 867 -882 417 -876 411 -876 435 -846 429 -882 421 -886 419 -850 445 -872 411 -450 853 -878 419 -430 881 -418 867 -890 417 -416 889 -418 885 -432 871 -416 855 -878 451 -438 845 -458 843 -884 419 -446 837 -880 449 -25864 497 -9138 659 -1080 233 -1046 297 -976 295 -558 737 -578 763 -942 339 -972 341 -530 785 -492 807 -486 807 -916 397 -918 357 -922 387 -928 383 -482 801 -906 395 -898 407 -888 415 -882 411 -480 841 -448 839 -878 415 -448 871 -876 411 -880 437 -880 397 -884 423 -884 419 -882 413 -886 387 -912 397 -466 843 -866 413 -448 869 -456 837 -888 419 -450 837 -450 851 -450 871 -454 837 -888 419 -450 835 -450 871 -884 417 -448 837 -880 415 -172818 65 -1022 197 -1852 10287 -134 695 -132 263 -130 1717 -66 625 -100 1031 -68 301 -68 265 -100 665 -98 1059 -100 931 -66 1093 -132 333 -68 3011 -132 459 -66 659 -98 521 -98 1511 -98 163 -98 857 -132 231 -98 491 -66 587 -66 393 -98 1513 -98 263 -130 529 -100 299 -100 1545 -132 1313 -66 399 -68 299 -134 201 -68 265 -98 691 -132 1099 -66 427 -100 461 -264 427 -134 327 -100 227 -64 493 -66 633 -66 501 -66 2067 -228 595 -132 97 -66 231 -66 299 -66 925 -98 661 -100 433 -134 231 -166 999 -98 691 -66 197 -66 293 -66 265 -362 1557 -100 231 -134 265 -68 433 -66 2483 -66 333 -100 233 -166 917 -132 295 -132 949 -164 1775 -100 7629 -66 7259 -66 263 -202 1263 -100 265 -68 861 -166 365 -98 233 -164 569 -66 199 -100 399 -98 1189 -130 261 -100 655 -164 723 -264 231 -100 327 -130 395 +RAW_Data: -130 84629 -16530 199 -594 163 -562 65 -164 65 -230 1383 -100 1031 -66 427 -66 401 -68 265 -102 233 -134 923 -100 493 -66 555 -132 619 -66 3165 -66 463 -198 1025 -68 233 -100 957 -132 793 -134 1233 -100 1553 -98 431 -100 1429 -100 393 -164 259 -166 65 -100 297 -134 263 -68 1797 -66 887 -100 497 -100 565 -134 363 -96 1649 -130 393 -98 327 -100 563 -68 891 -68 24545 -68 231 -134 167 -100 131 -68 863 -102 627 -164 267 -166 631 -66 569 -100 797 -66 231 -166 301 -102 569 -134 1423 -66 1115 -66 759 -66 599 -68 1265 -268 527 -134 531 -98 269 -68 231 -66 325 -98 329 -66 399 -64 393 -98 459 -98 921 -66 457 -100 2541 -266 927 -68 231 -64 357 -98 495 -166 133 -100 529 -98 1091 -100 199 -66 597 -100 931 -66 663 -134 333 -166 763 -68 599 -100 333 -102 333 -232 363 -66 1491 -132 697 -134 629 -66 369 -66 561 -134 863 -98 895 -100 199 -98 763 -100 299 -168 561 -100 331 -100 467 -100 1231 -100 1821 -66 229 -66 325 -66 1419 -66 195 -66 553 -132 363 -164 857 -66 491 -66 227 -98 227 -98 529 -100 2093 -164 501 -132 1441 -98 1095 -100 263 -66 931 -66 1397 -66 1627 -98 433 -66 697 -200 363 -266 297 -100 301 -200 263 -98 565 -134 65 -102 297 -100 165 -100 929 -266 1325 -66 3213 -100 4403 -100 665 -134 1859 -66 631 -66 967 -200 165 -134 1551 -66 791 -100 331 -132 163 -64 163 -98 361 -132 557 -98 629 -66 885 -100 1087 -100 959 -100 535 -66 265 -66 1931 -66 465 -66 1165 -100 565 -100 865 -100 493 -66 597 -166 1293 -100 463 -68 563 -66 565 -66 595 -100 197 -68 929 -66 665 -134 399 -66 665 -100 1255 -166 935 -98 1791 -98 265 -136 501 -100 1581 -66 265 -66 567 -98 699 -134 +RAW_Data: 12477 16271 -64 12623 -102 40819 -98 3729 -66 14371 -66 14943 -64 5931 -66 11147 -68 74641 -102 54299 -70 18441 -66 82993 -100 66161 -68 61869 -66 6627 -66 12987 -68 30427 -68 25761 -98 6305 -66 5019 -64 30857 -132 23929 -68 25129 -39378 317 -2020 311 -2010 2033 -312 2017 -282 325 -2018 291 -2024 2035 -316 2023 -276 319 -2028 297 -2032 2029 -284 2023 -314 321 -2016 319 -2012 2003 -310 2015 -318 321 -1984 327 -16000 351 -1974 321 -2020 2041 -276 2047 -288 327 -1992 327 -2000 2055 -282 2053 -274 305 -2022 301 -2014 2049 -286 2055 -274 319 -1992 329 -2006 2065 -282 2023 -316 323 -1988 303 -16008 323 -2014 315 -1990 2053 -284 2061 -274 319 -1988 319 -2038 2033 -286 2055 -264 339 -2008 289 -2034 2035 -284 2061 -262 339 -2008 287 -2040 2037 -286 2035 -268 347 -2006 317 -15988 311 -2014 343 -2014 2027 -300 2031 -258 311 -2024 323 -2028 2023 -280 2051 -318 285 -2024 309 -2002 2039 -312 2017 -318 289 -2020 293 -2028 2035 -316 2023 -276 321 -2030 299 -15982 345 -1988 345 -2014 2029 -268 2069 -258 337 -1998 321 -2024 2025 -320 2025 -276 321 -1994 331 -2000 2057 -282 2043 -282 305 -2034 291 -2024 2039 -320 2019 -278 323 -1992 333 -15976 389 -1978 321 -1986 2069 -276 2053 -288 303 -2020 315 -1980 2067 -258 2075 -258 337 -1996 321 -2030 2027 -284 2059 -274 319 -2004 319 -2016 2033 -282 2059 -264 345 -2006 289 -16006 355 -1984 315 -2018 2033 -312 2015 -284 327 -2000 329 -2000 2063 -282 2049 -284 327 -1984 319 -2018 2035 -284 2057 -264 343 -2004 289 -2032 2059 -280 2019 -316 323 -1984 321 -15968 387 -1978 321 -1984 2069 -276 2049 -288 325 -1996 303 -2004 2055 -284 2049 -278 325 -2018 321 -1984 2075 -276 2049 -288 327 -1990 325 -1996 2031 -318 2025 -278 323 -2018 289 -16010 357 -1982 315 -2018 2035 -278 2053 -288 307 -2022 321 -2000 2049 -274 2045 -284 327 -2022 311 -2014 2033 -276 2053 -286 327 -2000 323 -2000 2061 -282 2015 -316 325 -2002 289 -15994 353 -1980 357 -1990 2035 -312 2015 -286 327 -2020 289 -2022 2033 -318 2019 -278 323 -2004 319 -2016 2059 -282 2023 -316 327 -1970 321 -2018 2059 -280 2019 -318 325 -1972 321 -15988 353 -1982 357 -1972 2071 -276 2051 -288 327 -1988 323 -1996 2061 -280 2053 -284 325 -1984 321 -2014 2031 -282 2059 -276 321 -2018 289 -2036 2031 -282 2059 -276 321 -2018 319 -15984 341 -1984 343 -2016 2029 -298 2033 -258 309 -2028 321 -2032 2035 -284 2017 -298 337 -2008 289 -2034 2027 -314 2023 -310 285 -2016 321 -2014 2035 -282 2027 -308 317 -2026 297 -15982 357 -1984 345 -1982 2067 -264 2057 -258 341 -1996 317 -2026 2057 -282 2017 -318 325 -1972 319 -2018 2059 -280 2029 -302 315 -2006 319 -2006 2059 -274 2047 -276 307 +RAW_Data: -2024 311 -15984 349 -2004 317 -2012 2029 -276 2047 -312 305 -2018 311 -2018 2037 -258 2077 -258 337 -1998 321 -2026 2031 -278 2043 -284 327 -2020 311 -2018 2031 -278 2051 -288 325 -2004 323 -15962 353 -2018 317 -2014 2007 -308 2019 -320 287 -2024 325 -1998 2035 -314 2025 -302 311 -2004 319 -2010 2027 -312 2011 -310 309 -2028 303 -2018 2027 -304 2007 -294 309 -2024 323 -16002 361 -1946 347 -2008 2027 -286 2045 -312 325 -1978 319 -2024 2043 -276 2049 -286 325 -1988 341 -1978 2061 -278 2049 -284 327 -1990 321 -2020 2029 -318 2021 -278 323 -2008 321 -15990 355 -1982 355 -1984 2031 -284 2049 -278 325 -2018 321 -2000 2047 -276 2049 -286 327 -1994 341 -1976 2067 -274 2043 -284 303 -2018 341 -1982 2061 -266 2069 -258 339 -1998 321 -16002 311 -2014 329 -2012 2027 -318 1999 -298 339 -2008 287 -2042 2031 -282 2027 -306 317 -2012 291 -2044 2037 -284 2033 -302 315 -2004 321 -2012 2037 -282 2027 -306 317 -2028 297 -15980 345 -2018 285 -2026 2037 -320 2017 -278 323 -2024 299 -2002 2069 -278 2055 -286 325 -2002 289 -2024 2037 -282 2063 -276 321 -2016 287 -2036 2025 -320 2023 -278 323 -2018 287 -16006 355 -1982 355 -1986 2033 -302 2039 -282 307 -2032 309 -2014 2043 -264 2057 -258 341 -1992 319 -2036 2033 -284 2029 -306 319 -2000 319 -2014 2035 -282 2061 -266 343 -2006 289 -16018 331 -2010 313 -2014 2021 -320 2017 -278 323 -2010 321 -2016 2037 -284 2033 -304 317 -2018 287 -2036 2037 -284 2025 -296 339 -1978 321 -2028 2057 -282 2021 -278 359 -1972 321 -15990 355 -1978 355 -1990 2035 -300 2015 -312 305 -2016 315 -2016 2053 -290 2013 -316 277 -2020 353 -1980 2059 -256 2077 -276 307 -2020 311 -2012 2047 -258 2073 -256 341 -1998 321 -15976 369 -1976 313 -2018 2051 -280 2043 -290 317 -2004 321 -2024 2023 -282 2053 -282 327 -2020 313 -2020 2035 -278 2055 -284 325 -1986 321 -2016 2033 -280 2049 -282 325 -2020 313 -15986 315 -2026 319 -2028 2003 -318 2013 -294 339 -2008 289 -2032 2027 -316 2019 -302 311 -2004 321 -2014 2033 -314 1995 -308 317 -2028 297 -2032 2035 -276 2023 -316 321 -2014 289 -15998 347 -2014 283 -2020 2035 -292 2031 -288 311 -2024 321 -2036 2001 -310 2027 -318 275 -2054 277 -2026 2029 -296 2027 -292 311 -2024 321 -2030 2031 -282 2033 -304 313 -2004 321 -15992 329 -2010 349 -1980 2037 -292 2051 -256 339 -1998 319 -2034 2029 -286 2055 -264 339 -1978 321 -2030 2027 -280 2043 -316 327 -1984 315 -2016 2063 -278 2041 -288 327 -1990 325 -15964 389 -1978 319 -1984 2063 -256 2055 -292 307 -2016 323 -2028 2027 -286 2057 -274 319 -2010 319 -2010 2033 -282 2061 -276 321 -2018 289 -2032 2027 -318 2021 -278 321 -2020 319 -15974 355 -1980 357 -2000 2035 +RAW_Data: -254 2079 -276 309 -2016 309 -2012 2035 -296 2063 -258 337 -1992 321 -2030 2037 -286 2027 -294 333 -1978 321 -2034 2029 -286 2031 -306 315 -2008 321 -15972 355 -1984 315 -2014 2037 -310 2011 -318 323 -1984 329 -2002 2033 -316 2029 -278 323 -2026 299 -2030 2035 -278 2019 -316 325 -1980 323 -2018 2053 -252 2075 -280 327 -1984 355 -15954 345 -2008 309 -2016 2045 -266 2065 -258 341 -235740 101 -202 65 -734 133 -372 401 -68 269 -236 505 -68 235 -234 875 -68 13969 -100 14297 -70 3863 -96 59337 -104 11859 -68 17409 -68 7317 -66 11443 -64 15589 -66 4381 -98 32297 -168 45445 -100 59295 -100 41417 -66 1539 -66 23001 +RAW_Data: 171 -316 311 -154 187 -292 343 -134 337 -136 199 -286 311 -182 157 -294 327 -160 171 -316 161 -312 159 -314 333 -170 311 -164 141 -324 305 -164 299 -176 301 -170 167 -340 133 -338 163 -312 159 -294 189 -298 165 -314 311 -180 155 -294 189 -300 165 -312 187 -284 189 -312 159 -294 189 -296 167 -314 161 -310 189 -312 159 -292 189 -296 167 -312 161 -312 159 -314 193 -268 175 -304 193 -314 163 -312 159 -312 193 -294 495 -458 161 -338 301 -168 165 -304 331 -134 199 -286 309 -184 309 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 163 -312 309 -184 311 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 187 -284 189 -312 159 -292 189 -298 167 -312 161 -312 189 -300 173 -294 195 -282 191 -310 485 -472 167 -300 331 -134 171 -312 333 -170 169 -290 331 -162 299 -176 155 -300 333 -162 171 -300 323 -150 145 -340 159 -320 167 -302 319 -150 319 -166 169 -312 331 -162 299 -150 319 -194 139 -314 165 -308 163 -340 133 -338 163 -312 157 -296 327 -160 171 -316 163 -310 161 -312 163 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 169 -318 177 -290 189 -300 165 -312 161 -312 189 -284 185 -292 189 -300 191 -286 187 -284 191 -312 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 319 -150 319 -166 169 -312 331 -162 143 -314 311 -180 157 -294 189 -300 165 -314 309 -182 311 -168 141 -318 333 -162 299 -174 295 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 171 -316 161 -312 159 -314 191 -270 201 -278 193 -314 163 -310 161 -312 163 -300 175 -300 169 -316 177 -292 189 -300 165 -312 161 -312 189 -284 183 -294 189 -298 193 -284 187 -312 163 -312 497 -460 145 -320 325 -168 171 -290 333 -162 171 -314 305 -160 317 -168 169 -290 333 -162 173 -312 283 -182 159 -294 189 -302 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 321 -194 141 -312 167 -334 135 -340 161 -310 163 -312 159 -294 327 -160 171 -316 163 -310 161 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -300 165 -312 161 -310 189 -284 185 -292 189 -300 191 -286 187 -312 161 -300 487 -484 151 -318 335 -138 169 -322 305 -164 171 -314 305 -190 295 -166 171 -290 333 -162 173 -312 283 -182 159 -296 191 -300 +RAW_Data: 165 -312 333 -160 311 -170 141 -318 331 -164 299 -148 321 -194 139 -316 165 -336 135 -340 133 -338 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -300 175 -296 501 -458 187 -288 347 -154 151 -316 325 -162 171 -316 313 -152 301 -194 141 -316 331 -162 143 -314 311 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -174 295 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -306 173 -314 165 -306 191 -284 189 -284 189 -312 157 -294 191 -300 163 -314 187 -284 189 -284 187 -292 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -492 167 -298 323 -150 175 -302 331 -164 171 -300 321 -150 317 -166 167 -308 331 -134 171 -316 309 -182 129 -318 189 -302 163 -314 309 -182 311 -170 141 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -312 159 -294 327 -160 171 -316 161 -312 159 -312 193 -270 175 -304 193 -314 163 -312 159 -312 161 -314 161 -296 189 -304 165 -312 187 -284 189 -276 173 -318 195 -278 193 -284 187 -312 163 -300 175 -298 169 -318 511 -460 147 -318 327 -168 169 -292 331 -164 171 -314 305 -160 315 -168 171 -290 333 -162 171 -314 283 -182 159 -294 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 157 -294 327 -160 171 -316 163 -310 161 -304 175 -314 167 -304 191 -286 187 -284 189 -284 187 -292 189 -300 165 -312 189 -284 187 -286 185 -294 189 -296 165 -314 161 -312 187 -286 185 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -292 331 -164 299 -174 151 -316 325 -162 171 -316 289 -176 151 -316 187 -296 167 -312 311 -180 307 -170 141 -318 331 -162 299 -150 319 -196 139 -314 165 -334 137 -340 159 -312 161 -302 149 -320 329 -164 171 -314 159 -312 163 -300 175 -298 193 -310 163 -312 161 -310 163 -312 161 -296 189 -302 191 -286 187 -284 189 -312 159 -292 189 -296 167 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -296 323 -150 175 -312 313 -170 169 -292 333 -162 299 -176 151 -314 327 -160 171 -316 291 -176 149 -316 189 -296 165 -314 309 -182 307 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 161 -312 +RAW_Data: 161 -302 147 -320 331 -162 171 -314 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -296 195 -284 189 -284 189 -312 159 -296 513 -468 159 -300 333 -162 171 -300 323 -150 171 -314 325 -162 311 -164 171 -314 305 -160 159 -296 327 -160 171 -316 161 -312 161 -312 335 -170 311 -164 141 -324 321 -154 299 -194 309 -164 143 -326 167 -314 161 -312 159 -312 163 -302 173 -300 331 -162 173 -312 161 -312 163 -300 175 -300 167 -320 153 -312 191 -296 167 -312 161 -312 189 -312 159 -292 189 -298 167 -312 161 -312 159 -314 193 -268 177 -302 193 -316 161 -312 159 -312 193 -294 497 -456 187 -312 303 -166 165 -306 331 -132 201 -286 309 -182 313 -168 143 -318 331 -162 143 -314 311 -182 157 -294 189 -302 165 -312 309 -182 311 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -300 149 -320 331 -162 171 -314 161 -310 163 -302 173 -298 193 -310 165 -310 161 -310 163 -312 161 -296 189 -300 193 -286 187 -284 189 -312 157 -294 189 -298 165 -314 161 -310 189 -284 187 -292 189 -300 193 -284 485 -494 167 -298 321 -150 171 -318 323 -162 171 -314 289 -176 297 -194 141 -314 331 -162 143 -316 309 -182 155 -294 189 -302 163 -314 309 -182 311 -168 143 -318 331 -162 301 -148 321 -194 139 -314 167 -334 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -300 175 -296 193 -312 163 -312 159 -312 163 -300 175 -298 167 -320 175 -290 191 -296 193 -286 187 -310 163 -312 161 -296 513 -468 159 -300 333 -162 171 -302 321 -150 175 -300 333 -162 311 -162 171 -316 289 -176 153 -314 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -320 307 -164 297 -176 301 -194 141 -318 165 -336 137 -338 133 -338 163 -312 159 -294 327 -160 171 -316 161 -312 161 -312 163 -298 177 -302 169 -318 177 -292 189 -298 167 -312 189 -284 189 -312 157 -294 189 -298 167 -312 161 -312 159 -312 193 -270 203 -302 169 -314 161 -312 189 -310 467 -484 145 -318 327 -168 143 -316 331 -162 173 -312 307 -160 313 -170 141 -318 331 -164 143 -342 281 -184 157 -294 189 -302 165 -312 307 -184 313 -168 141 -320 331 -162 299 -150 319 -194 141 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 +RAW_Data: 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 185 -292 189 -298 193 -284 189 -310 163 -312 497 -460 145 -320 325 -170 169 -290 333 -162 171 -314 307 -158 317 -168 169 -290 333 -162 171 -314 281 -182 159 -296 189 -300 165 -312 309 -182 313 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 135 -338 161 -312 159 -294 327 -160 173 -314 163 -312 159 -312 163 -300 175 -302 195 -312 163 -312 159 -312 163 -300 175 -298 169 -318 175 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 193 -284 187 -312 161 -314 471 -486 167 -314 313 -152 159 -300 335 -162 169 -308 321 -156 329 -170 127 -318 321 -176 155 -300 333 -164 169 -302 165 -314 161 -310 329 -168 299 -166 151 -328 311 -154 331 -168 299 -178 143 -318 153 -322 167 -332 149 -320 151 -318 165 -304 321 -156 181 -290 189 -300 173 -294 177 -294 189 -302 173 -294 177 -294 189 -302 173 -294 177 -318 193 -278 173 -294 179 -292 191 -302 173 -294 177 -294 189 -302 173 -320 177 -294 195 -278 175 -318 491 -460 163 -314 333 -158 159 -294 327 -160 171 -316 311 -180 307 -170 141 -318 331 -162 143 -316 309 -182 157 -294 189 -302 165 -312 309 -182 313 -168 141 -318 331 -164 299 -148 321 -194 141 -314 165 -334 137 -338 135 -338 161 -312 159 -294 329 -158 173 -314 163 -310 161 -306 173 -314 165 -304 193 -284 187 -286 189 -276 197 -294 193 -310 163 -312 161 -310 163 -300 175 -296 193 -310 165 -284 187 -310 163 -300 175 -300 169 -318 177 -316 483 -466 187 -292 341 -134 171 -308 319 -150 179 -300 333 -164 309 -162 171 -316 313 -152 153 -316 327 -160 171 -316 163 -310 161 -312 307 -166 337 -136 171 -322 305 -164 299 -174 301 -194 141 -318 165 -336 137 -338 161 -310 163 -300 149 -320 331 -162 171 -314 161 -312 163 -300 173 -296 195 -310 163 -312 159 -312 163 -312 159 -296 189 -302 165 -312 189 -284 189 -284 185 -294 189 -296 165 -314 161 -310 189 -284 187 -294 189 -298 193 -284 485 -494 167 -298 321 -150 177 -312 313 -168 171 -290 333 -162 299 -176 151 -316 325 -162 171 -316 289 -176 151 -314 189 -296 165 -314 309 -182 305 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -340 133 -338 161 -302 149 -320 329 -164 171 -286 187 -312 163 -300 173 -298 193 -310 163 -312 159 -312 163 -312 159 -296 189 -302 191 -286 187 -284 189 -312 159 -292 191 -296 165 -314 161 -310 189 -284 187 -292 +RAW_Data: 189 -300 193 -284 485 -494 167 -298 321 -150 177 -300 327 -150 167 -328 305 -160 319 -168 171 -290 333 -162 171 -314 281 -184 159 -296 189 -300 165 -314 307 -184 311 -170 141 -318 333 -162 299 -150 319 -194 139 -314 167 -334 135 -340 133 -338 163 -312 159 -294 327 -158 173 -314 163 -312 159 -312 165 -298 177 -302 193 -314 163 -310 161 -312 163 -300 173 -300 167 -318 177 -292 189 -300 165 -312 161 -312 187 -284 185 -292 189 -300 191 -286 187 -284 189 -300 487 -484 149 -320 333 -138 171 -322 321 -154 151 -316 327 -160 311 -164 173 -312 307 -160 161 -298 355 -132 173 -314 163 -310 161 -312 335 -170 311 -164 143 -322 307 -164 323 -150 301 -196 141 -318 165 -334 137 -338 161 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 173 -298 195 -310 163 -312 159 -312 163 -312 159 -296 189 -300 193 -284 189 -284 189 -284 185 -294 189 -296 165 -314 161 -312 189 -284 185 -294 189 -298 193 -286 485 -468 193 -296 323 -150 147 -340 313 -168 171 -292 331 -164 299 -174 151 -316 325 -160 173 -316 289 -176 149 -316 189 -294 165 -314 309 -182 307 -170 141 -318 331 -164 299 -148 321 -194 139 -314 167 -306 165 -338 133 -338 163 -312 159 -294 327 -160 173 -314 163 -312 161 -310 165 -300 175 -300 169 -318 177 -290 189 -298 165 -314 187 -284 189 -312 159 -294 189 -298 165 -314 161 -310 161 -306 173 -316 193 -280 191 -312 159 -312 163 -300 515 -460 161 -312 305 -190 135 -328 329 -134 201 -286 309 -182 311 -168 143 -318 331 -162 143 -316 309 -182 157 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -334 137 -340 159 -312 163 -300 149 -318 331 -162 173 -312 161 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 173 -296 193 -312 163 -312 159 -312 163 -312 161 -298 189 -300 165 -312 161 -312 189 -312 159 -292 191 -300 191 -286 485 -468 193 -298 323 -148 147 -342 323 -160 173 -314 289 -176 297 -196 139 -314 331 -162 143 -314 311 -180 157 -292 191 -300 165 -312 311 -182 309 -170 141 -318 333 -162 299 -150 319 -194 141 -314 165 -334 137 -338 161 -312 161 -312 159 -294 329 -158 173 -314 163 -310 161 -312 165 -298 175 -304 193 -312 163 -310 161 -310 163 -302 173 -300 169 -318 175 -292 189 -300 165 -312 161 -310 189 -278 173 -316 195 -280 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -328 329 -134 199 -288 309 -182 311 -168 143 -318 +RAW_Data: 331 -162 143 -316 309 -182 157 -292 191 -300 165 -312 309 -182 313 -168 141 -318 331 -164 299 -174 295 -194 141 -314 165 -334 137 -340 133 -338 163 -312 159 -292 327 -160 171 -316 163 -310 161 -312 165 -298 175 -304 193 -312 163 -312 159 -312 163 -300 175 -300 169 -316 177 -292 189 -298 167 -312 161 -310 189 -278 173 -316 193 -282 191 -312 159 -312 163 -300 515 -460 163 -310 305 -190 137 -326 331 -132 201 -286 309 -184 309 -170 141 -318 333 -162 143 -314 311 -180 159 -294 189 -300 165 -312 309 -184 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 135 -340 159 -312 163 -300 149 -320 329 -164 171 -314 159 -312 163 -300 175 -296 195 -310 163 -312 159 -312 163 -300 175 -294 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 153 -314 191 -296 193 -286 187 -284 189 -312 161 -294 509 -468 159 -298 335 -162 169 -304 323 -150 175 -300 335 -162 311 -160 173 -316 313 -152 151 -316 327 -160 171 -316 163 -312 159 -312 307 -168 335 -138 169 -322 305 -164 299 -176 299 -194 141 -316 167 -334 137 -340 159 -312 161 -302 147 -320 331 -162 173 -312 161 -312 163 -300 175 -296 193 -310 165 -310 161 -312 161 -302 173 -296 195 -310 163 -312 161 -310 163 -300 175 -298 169 -318 177 -290 189 -296 193 -286 187 -310 163 -300 175 -296 501 -484 161 -288 347 -154 151 -316 325 -160 173 -314 313 -152 301 -196 141 -314 331 -162 143 -316 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -318 331 -162 301 -148 319 -196 139 -314 165 -336 137 -338 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 163 -298 177 -302 193 -314 163 -312 159 -312 163 -300 175 -298 169 -318 177 -290 191 -298 165 -314 161 -310 189 -284 193 -292 179 -308 167 -314 161 -312 189 -302 481 -460 173 -320 303 -168 169 -294 331 -164 171 -314 307 -160 319 -168 169 -292 331 -162 143 -342 283 -182 159 -294 189 -302 165 -312 309 -184 311 -168 143 -318 331 -162 299 -150 319 -194 141 -314 165 -336 135 -340 133 -338 163 -312 159 -294 327 -158 173 -316 161 -312 159 -312 165 -298 177 -302 193 -312 163 -312 161 -310 163 -314 161 -296 189 -302 165 -312 187 -284 189 -284 185 -294 189 -296 165 -314 187 -284 189 -302 173 -294 195 -310 491 -458 163 -318 319 -156 153 -314 327 -160 171 -316 311 -152 327 -170 141 -316 331 -164 143 -314 309 -182 157 -294 189 -300 165 -314 309 -182 311 -168 143 -316 333 -162 299 -150 diff --git a/documentation/fbt.md b/documentation/fbt.md index c658ce20..53fc4b5e 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -24,12 +24,25 @@ To build with FBT, call it specifying configuration options & targets to build. To run cleanup (think of `make clean`) for specified targets, add `-c` option. +## VSCode integration + +`fbt` includes basic development environment configuration for VSCode. To deploy it, run `./fbt vscode_dist`. That will copy initial environment configuration to `.vscode` folder. After that, you can use that configuration by starting VSCode and choosing firmware root folder in "File > Open Folder" menu. + + * On first start, you'll be prompted to install recommended plug-ins. Please install them for best development experience. _You can find a list of them in `.vscode/extensions.json`._ + * Basic build tasks are invoked in Ctrl+Shift+B menu. + * Debugging requires a supported probe. That includes: + * Wi-Fi devboard with stock firmware (blackmagic), + * ST-Link and compatible devices, + * J-Link for flashing and debugging (in VSCode only). _Note that J-Link tools are not included with our toolchain and you have to [download](https://www.segger.com/downloads/jlink/) them yourself and put on your system's PATH._ + * Without a supported probe, you can install firmware on Flipper using USB installation method. + + ## FBT targets FBT keeps track of internal dependencies, so you only need to build the highest-level target you need, and FBT will make sure everything they depend on is up-to-date. ### High-level (what you most likely need) - + - `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified - `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card - `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper diff --git a/firmware.scons b/firmware.scons index 76f0b52d..064e0dda 100644 --- a/firmware.scons +++ b/firmware.scons @@ -97,7 +97,7 @@ else: ], ) -# Invoke child SCopscripts to populate global `env` + build their own part of the code +# Invoke child SConscripts to populate global `env` + build their own part of the code lib_targets = env.BuildModules( [ "lib", @@ -164,8 +164,6 @@ fwenv.AppendUnique( "-Wl,--wrap,_free_r", "-Wl,--wrap,_calloc_r", "-Wl,--wrap,_realloc_r", - "-u", - "_printf_float", "-n", "-Xlinker", "-Map=${TARGET}.map", @@ -181,6 +179,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( "${FIRMWARE_BUILD_CFG}", sources, LIBS=[ + "print", "flipper${TARGET_HW}", "furi", "freertos", diff --git a/firmware/targets/f7/Inc/FreeRTOSConfig.h b/firmware/targets/f7/Inc/FreeRTOSConfig.h index 4f9d1fcf..f54d774c 100644 --- a/firmware/targets/f7/Inc/FreeRTOSConfig.h +++ b/firmware/targets/f7/Inc/FreeRTOSConfig.h @@ -32,7 +32,7 @@ extern uint32_t SystemCoreClock; #define configUSE_16_BIT_TICKS 0 #define configUSE_MUTEXES 1 #define configQUEUE_REGISTRY_SIZE 0 -#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configCHECK_FOR_STACK_OVERFLOW 0 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_COUNTING_SEMAPHORES 1 #define configENABLE_BACKWARD_COMPATIBILITY 0 @@ -145,3 +145,7 @@ standard names. */ #define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 1 #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION \ 1 /* required only for Keil but does not hurt otherwise */ + +#define traceTASK_SWITCHED_IN() \ + extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \ + furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack) diff --git a/firmware/targets/f7/Src/dfu.c b/firmware/targets/f7/Src/dfu.c index 889c3c1e..f32ac2ac 100644 --- a/firmware/targets/f7/Src/dfu.c +++ b/firmware/targets/f7/Src/dfu.c @@ -19,7 +19,7 @@ void flipper_boot_dfu_show_splash() { u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data); u8g2_SetFont(fb, u8g2_font_helvB08_tr); u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode"); - u8g2_DrawStr(fb, 2, 21, "DFU started"); + u8g2_DrawStr(fb, 2, 21, "DFU Started"); u8g2_SetPowerSave(fb, 0); u8g2_SendBuffer(fb); } diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 0cef33dd..d0856127 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -1,4 +1,5 @@ #include +#include #include @@ -35,6 +36,7 @@ void furi_hal_deinit_early() { } void furi_hal_init() { + furi_hal_mpu_init(); furi_hal_clock_init(); furi_hal_console_init(); furi_hal_rtc_init(); @@ -47,6 +49,7 @@ void furi_hal_init() { FURI_LOG_I(TAG, "GPIO OK"); furi_hal_version_init(); + furi_hal_region_init(); furi_hal_spi_init(); @@ -80,17 +83,6 @@ void furi_hal_init() { // FatFS driver initialization MX_FATFS_Init(); FURI_LOG_I(TAG, "FATFS OK"); - - // Partial null pointer dereference protection - LL_MPU_Disable(); - LL_MPU_ConfigRegion( - LL_MPU_REGION_NUMBER0, - 0x00, - 0x0, - LL_MPU_REGION_SIZE_1MB | LL_MPU_REGION_PRIV_RO_URO | LL_MPU_ACCESS_BUFFERABLE | - LL_MPU_ACCESS_CACHEABLE | LL_MPU_ACCESS_SHAREABLE | LL_MPU_TEX_LEVEL1 | - LL_MPU_INSTRUCTION_ACCESS_ENABLE); - LL_MPU_Enable(LL_MPU_CTRL_PRIVILEGED_DEFAULT); } void furi_hal_switch(void* address) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index cf7140eb..1f75ea33 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -1,6 +1,11 @@ #include -#include +#include +#include +#include +#include + #include +#include #include void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { @@ -45,6 +50,7 @@ void furi_hal_info_get(FuriHalInfoValueCallback out, void* context) { out("hardware_color", string_get_cstr(value), false, context); string_printf(value, "%d", furi_hal_version_get_hw_region()); out("hardware_region", string_get_cstr(value), false, context); + out("hardware_region_provisioned", furi_hal_region_get_name(), false, context); const char* name = furi_hal_version_get_name_ptr(); if(name) { out("hardware_name", name, false, context); diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index fa595921..038ae948 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -6,6 +6,7 @@ #include #include #include +#include #define TAG "FuriHalInterrupt" @@ -95,6 +96,10 @@ void furi_hal_interrupt_init() { LL_SYSCFG_DisableIT_FPU_IDC(); LL_SYSCFG_DisableIT_FPU_IXC(); + LL_HANDLER_EnableFault(LL_HANDLER_FAULT_USG); + LL_HANDLER_EnableFault(LL_HANDLER_FAULT_BUS); + LL_HANDLER_EnableFault(LL_HANDLER_FAULT_MEM); + FURI_LOG_I(TAG, "Init OK"); } @@ -241,6 +246,20 @@ void HardFault_Handler() { } void MemManage_Handler() { + if(FURI_BIT(SCB->CFSR, SCB_CFSR_MMARVALID_Pos)) { + uint32_t memfault_address = SCB->MMFAR; + if(memfault_address < (1024 * 1024)) { + // from 0x00 to 1MB, see FuriHalMpuRegionNULL + furi_crash("NULL pointer dereference"); + } else { + // write or read of MPU region 1 (FuriHalMpuRegionStack) + furi_crash("MPU fault, possibly stack overflow"); + } + } else if(FURI_BIT(SCB->CFSR, SCB_CFSR_MSTKERR_Pos)) { + // push to stack on MPU region 1 (FuriHalMpuRegionStack) + furi_crash("MemManage fault, possibly stack overflow"); + } + furi_crash("MemManage"); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_mpu.c b/firmware/targets/f7/furi_hal/furi_hal_mpu.c new file mode 100644 index 00000000..ea6cd55b --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_mpu.c @@ -0,0 +1,66 @@ +#include +#include + +#define FURI_HAL_MPU_ATTRIBUTES \ + (LL_MPU_ACCESS_BUFFERABLE | LL_MPU_ACCESS_CACHEABLE | LL_MPU_ACCESS_SHAREABLE | \ + LL_MPU_TEX_LEVEL1 | LL_MPU_INSTRUCTION_ACCESS_ENABLE) + +#define FURI_HAL_MPU_STACK_PROTECT_REGION FuriHalMPURegionSize32B + +void furi_hal_mpu_init() { + furi_hal_mpu_enable(); + + // NULL pointer dereference protection + furi_hal_mpu_protect_no_access(FuriHalMpuRegionNULL, 0x00, FuriHalMPURegionSize1MB); +} + +void furi_hal_mpu_enable() { + LL_MPU_Enable(LL_MPU_CTRL_PRIVILEGED_DEFAULT); +} + +void furi_hal_mpu_disable() { + LL_MPU_Disable(); +} + +void furi_hal_mpu_protect_no_access( + FuriHalMpuRegion region, + uint32_t address, + FuriHalMPURegionSize size) { + uint32_t size_ll = size; + size_ll = size_ll << MPU_RASR_SIZE_Pos; + + furi_hal_mpu_disable(); + LL_MPU_ConfigRegion( + region, 0x00, address, FURI_HAL_MPU_ATTRIBUTES | LL_MPU_REGION_NO_ACCESS | size_ll); + furi_hal_mpu_enable(); +} + +void furi_hal_mpu_protect_read_only( + FuriHalMpuRegion region, + uint32_t address, + FuriHalMPURegionSize size) { + uint32_t size_ll = size; + size_ll = size_ll << MPU_RASR_SIZE_Pos; + + furi_hal_mpu_disable(); + LL_MPU_ConfigRegion( + region, 0x00, address, FURI_HAL_MPU_ATTRIBUTES | LL_MPU_REGION_PRIV_RO_URO | size_ll); + furi_hal_mpu_enable(); +} + +void furi_hal_mpu_protect_disable(FuriHalMpuRegion region) { + furi_hal_mpu_disable(); + LL_MPU_DisableRegion(region); + furi_hal_mpu_enable(); +} + +void furi_hal_mpu_set_stack_protection(uint32_t* stack) { + // Protection area address must be aligned to region size + uint32_t stack_ptr = (uint32_t)stack; + uint32_t mask = ((1 << (FURI_HAL_MPU_STACK_PROTECT_REGION + 2)) - 1); + stack_ptr &= ~mask; + if(stack_ptr < (uint32_t)stack) stack_ptr += (mask + 1); + + furi_hal_mpu_protect_read_only( + FuriHalMpuRegionStack, stack_ptr, FURI_HAL_MPU_STACK_PROTECT_REGION); +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 7b7d5f27..2d6db8fb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -341,13 +341,13 @@ void furi_hal_nfc_listen_start(FuriHalNfcDevData* nfc_data) { if(nfc_data->uid_len == 4) { pt_memory[12] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; } else { - pt_memory[12] = nfc_data->sak | FURI_HAL_NFC_UID_INCOMPLETE; + pt_memory[12] = FURI_HAL_NFC_UID_INCOMPLETE; } pt_memory[13] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; pt_memory[14] = nfc_data->sak & ~FURI_HAL_NFC_UID_INCOMPLETE; st25r3916WritePTMem(pt_memory, sizeof(pt_memory)); - // Go to sence + // Go to sense st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SENSE); } @@ -507,6 +507,9 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ uint8_t rxe = 0; uint32_t start = DWT->CYCCNT; while(true) { + if(!rfalIsExtFieldOn()) { + return false; + } if(furi_hal_gpio_read(&gpio_nfc_irq_rfid_pull) == true) { st25r3916ReadRegister(ST25R3916_REG_IRQ_MAIN, &rxe); if(rxe & (1 << 4)) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_region.c b/firmware/targets/f7/furi_hal/furi_hal_region.c new file mode 100644 index 00000000..143d5c18 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_region.c @@ -0,0 +1,135 @@ +#include +#include + +const FuriHalRegion furi_hal_region_zero = { + .country_code = "00", + .bands_count = 1, + .bands = { + { + .start = 0, + .end = 1000000000, + .power_limit = 12, + .duty_cycle = 50, + }, + }}; + +const FuriHalRegion furi_hal_region_eu_ru = { + .country_code = "EU", + .bands_count = 2, + .bands = { + { + .start = 433050000, + .end = 434790000, + .power_limit = 12, + .duty_cycle = 50, + }, + { + .start = 868150000, + .end = 868550000, + .power_limit = 12, + .duty_cycle = 50, + }}}; + +const FuriHalRegion furi_hal_region_us_ca_au = { + .country_code = "US", + .bands_count = 3, + .bands = { + { + .start = 304100000, + .end = 321950000, + .power_limit = 12, + .duty_cycle = 50, + }, + { + .start = 433050000, + .end = 434790000, + .power_limit = 12, + .duty_cycle = 50, + }, + { + .start = 915000000, + .end = 928000000, + .power_limit = 12, + .duty_cycle = 50, + }}}; + +const FuriHalRegion furi_hal_region_jp = { + .country_code = "JP", + .bands_count = 2, + .bands = { + { + .start = 312000000, + .end = 315250000, + .power_limit = 12, + .duty_cycle = 50, + }, + { + .start = 920500000, + .end = 923500000, + .power_limit = 12, + .duty_cycle = 50, + }}}; + +static const FuriHalRegion* furi_hal_region = NULL; + +void furi_hal_region_init() { + FuriHalVersionRegion region = furi_hal_version_get_hw_region(); + + if(region == FuriHalVersionRegionUnknown) { + furi_hal_region = &furi_hal_region_zero; + } else if(region == FuriHalVersionRegionEuRu) { + furi_hal_region = &furi_hal_region_eu_ru; + } else if(region == FuriHalVersionRegionUsCaAu) { + furi_hal_region = &furi_hal_region_us_ca_au; + } else if(region == FuriHalVersionRegionJp) { + furi_hal_region = &furi_hal_region_jp; + } +} + +const FuriHalRegion* furi_hal_region_get() { + return furi_hal_region; +} + +void furi_hal_region_set(FuriHalRegion* region) { + furi_hal_region = region; +} + +bool furi_hal_region_is_provisioned() { + return furi_hal_region != NULL; +} + +const char* furi_hal_region_get_name() { + if(furi_hal_region) { + return furi_hal_region->country_code; + } else { + return "--"; + } +} + +bool furi_hal_region_is_frequency_allowed(uint32_t frequency) { + if(!furi_hal_region) { + return false; + } + + const FuriHalRegionBand* band = furi_hal_region_get_band(frequency); + if(!band) { + return false; + } + + return true; +} + +const FuriHalRegionBand* furi_hal_region_get_band(uint32_t frequency) { + if(!furi_hal_region) { + return NULL; + } + + for(size_t i = 0; i < furi_hal_region->bands_count; i++) { + if(furi_hal_region->bands[i].start <= frequency && + furi_hal_region->bands[i].end >= frequency) { + return &furi_hal_region->bands[i]; + } + } + + return NULL; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 650f8ac3..ade46238 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,6 +1,7 @@ #include "furi_hal_subghz.h" #include "furi_hal_subghz_configs.h" +#include #include #include #include @@ -308,52 +309,8 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { return value; } -bool furi_hal_subghz_is_tx_allowed(uint32_t value) { - //checking regional settings - bool is_allowed = false; - 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 >= 304100000 && 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; - } - return is_allowed; -} - uint32_t furi_hal_subghz_set_frequency(uint32_t value) { - if(furi_hal_subghz_is_tx_allowed(value)) { + if(furi_hal_region_is_frequency_allowed(value)) { furi_hal_subghz.regulation = SubGhzRegulationTxRx; } else { furi_hal_subghz.regulation = SubGhzRegulationOnlyRx; diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 4d1fc970..2a372a6c 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -18,6 +18,7 @@ template struct STOP_EXTERNING_ME {}; #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" diff --git a/firmware/targets/furi_hal_include/furi_hal_mpu.h b/firmware/targets/furi_hal_include/furi_hal_mpu.h new file mode 100644 index 00000000..5dddadeb --- /dev/null +++ b/firmware/targets/furi_hal_include/furi_hal_mpu.h @@ -0,0 +1,86 @@ +/** + * @file furi_hal_light.h + * Light control HAL API + */ + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FuriHalMpuRegionNULL = 0x00, // region 0 used to protect null pointer dereference + FuriHalMpuRegionStack = 0x01, // region 1 used to protect stack + FuriHalMpuRegion2 = 0x02, + FuriHalMpuRegion3 = 0x03, + FuriHalMpuRegion4 = 0x04, + FuriHalMpuRegion5 = 0x05, + FuriHalMpuRegion6 = 0x06, + FuriHalMpuRegion7 = 0x07, +} FuriHalMpuRegion; + +typedef enum { + FuriHalMPURegionSize32B = 0x04U, + FuriHalMPURegionSize64B = 0x05U, + FuriHalMPURegionSize128B = 0x06U, + FuriHalMPURegionSize256B = 0x07U, + FuriHalMPURegionSize512B = 0x08U, + FuriHalMPURegionSize1KB = 0x09U, + FuriHalMPURegionSize2KB = 0x0AU, + FuriHalMPURegionSize4KB = 0x0BU, + FuriHalMPURegionSize8KB = 0x0CU, + FuriHalMPURegionSize16KB = 0x0DU, + FuriHalMPURegionSize32KB = 0x0EU, + FuriHalMPURegionSize64KB = 0x0FU, + FuriHalMPURegionSize128KB = 0x10U, + FuriHalMPURegionSize256KB = 0x11U, + FuriHalMPURegionSize512KB = 0x12U, + FuriHalMPURegionSize1MB = 0x13U, + FuriHalMPURegionSize2MB = 0x14U, + FuriHalMPURegionSize4MB = 0x15U, + FuriHalMPURegionSize8MB = 0x16U, + FuriHalMPURegionSize16MB = 0x17U, + FuriHalMPURegionSize32MB = 0x18U, + FuriHalMPURegionSize64MB = 0x19U, + FuriHalMPURegionSize128MB = 0x1AU, + FuriHalMPURegionSize256MB = 0x1BU, + FuriHalMPURegionSize512MB = 0x1CU, + FuriHalMPURegionSize1GB = 0x1DU, + FuriHalMPURegionSize2GB = 0x1EU, + FuriHalMPURegionSize4GB = 0x1FU, +} FuriHalMPURegionSize; + +/** + * @brief Initialize memory protection unit + */ +void furi_hal_mpu_init(); + +/** +* @brief Enable memory protection unit +*/ +void furi_hal_mpu_enable(); + +/** +* @brief Disable memory protection unit +*/ +void furi_hal_mpu_disable(); + +void furi_hal_mpu_protect_no_access( + FuriHalMpuRegion region, + uint32_t address, + FuriHalMPURegionSize size); + +void furi_hal_mpu_protect_read_only( + FuriHalMpuRegion region, + uint32_t address, + FuriHalMPURegionSize size); + +void furi_hal_mpu_protect_disable(FuriHalMpuRegion region); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/furi_hal_include/furi_hal_region.h b/firmware/targets/furi_hal_include/furi_hal_region.h new file mode 100644 index 00000000..10d519ab --- /dev/null +++ b/firmware/targets/furi_hal_include/furi_hal_region.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include + +typedef struct { + uint32_t start; + uint32_t end; + int8_t power_limit; + uint8_t duty_cycle; +} FuriHalRegionBand; + +typedef struct { + char country_code[4]; + uint16_t bands_count; + FuriHalRegionBand bands[]; +} FuriHalRegion; + +/** Initialize region */ +void furi_hal_region_init(); + +/** Get Region Data. + * + * Region data may be allocated in Flash or in RAM. + * Keep in mind that we don't do memory management on our side. + * + * @return pointer to FuriHalRegion instance (in RAM or Flash, check before freeing on region update) + */ +const FuriHalRegion* furi_hal_region_get(); + +/** Set device region data + * + * @param region pointer to the FuriHalRegion + */ +void furi_hal_region_set(FuriHalRegion* region); + +/** Check if region data provisioned + * + * @return true if provisioned, false otherwise + */ +bool furi_hal_region_is_provisioned(); + +/** Get region name + * + * 2 letter Region code according to iso 3166 standard + * There are 2 extra values that we use in special cases: + * - "00" - developer edition, unlocked + * - "WW" - world wide, region provisioned by default + * - "--" - no provisioned region + * + * @return Pointer to string + */ +const char* furi_hal_region_get_name(); + +/** Сheck if transmission is allowed on this frequency for your flipper region + * + * @param[in] frequency The frequency + * @param value frequency in Hz + * + * @return true if allowed + */ +bool furi_hal_region_is_frequency_allowed(uint32_t frequency); + +/** Get band data for frequency + * + * + * + * @param[in] frequency The frequency + * + * @return { description_of_the_return_value } + */ +const FuriHalRegionBand* furi_hal_region_get_band(uint32_t frequency); diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h index b6d132ac..d610b01b 100644 --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h @@ -180,14 +180,6 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value); */ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value); -/** Сheck if transmission is allowed on this frequency for your flipper region - * - * @param value frequency in Hz - * - * @return true if allowed - */ -bool furi_hal_subghz_is_tx_allowed(uint32_t value); - /** Set frequency * * @param value frequency in Hz diff --git a/furi/core/log.c b/furi/core/log.c index 8cf90485..8a36a930 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -22,24 +22,60 @@ void furi_log_init() { furi_log.mutex = furi_mutex_alloc(FuriMutexTypeNormal); } -void furi_log_print(FuriLogLevel level, const char* format, ...) { +void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) { if(level <= furi_log.log_level && furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) { string_t string; + string_init(string); + + const char* color = FURI_LOG_CLR_RESET; + const char* log_letter = " "; + switch(level) { + case FuriLogLevelError: + color = FURI_LOG_CLR_E; + log_letter = "E"; + break; + case FuriLogLevelWarn: + color = FURI_LOG_CLR_W; + log_letter = "W"; + break; + case FuriLogLevelInfo: + color = FURI_LOG_CLR_I; + log_letter = "I"; + break; + case FuriLogLevelDebug: + color = FURI_LOG_CLR_D; + log_letter = "D"; + break; + case FuriLogLevelTrace: + color = FURI_LOG_CLR_T; + log_letter = "T"; + break; + default: + break; + } // Timestamp - string_init_printf(string, "%lu ", furi_log.timetamp()); + string_printf( + string, + "%lu %s[%s][%s] " FURI_LOG_CLR_RESET, + furi_log.timetamp(), + color, + log_letter, + tag); furi_log.puts(string_get_cstr(string)); - string_clear(string); + string_reset(string); va_list args; va_start(args, format); - string_init_vprintf(string, format, args); + string_vprintf(string, format, args); va_end(args); furi_log.puts(string_get_cstr(string)); string_clear(string); + furi_log.puts("\r\n"); + furi_mutex_release(furi_log.mutex); } } diff --git a/furi/core/log.h b/furi/core/log.h index 6d23a9c2..30026fc4 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -44,13 +44,14 @@ typedef uint32_t (*FuriLogTimestamp)(void); /** Initialize logging */ void furi_log_init(); -/** Log record - * - * @param[in] level The level - * @param[in] format The format - * @param[in] VA args +/** Print log record + * + * @param level + * @param tag + * @param format + * @param ... */ -void furi_log_print(FuriLogLevel level, const char* format, ...); +void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...); /** Set log level * @@ -76,11 +77,6 @@ void furi_log_set_puts(FuriLogPuts puts); */ void furi_log_set_timestamp(FuriLogTimestamp timestamp); -#define FURI_LOG_FORMAT(log_letter, tag, format) \ - FURI_LOG_CLR_##log_letter "[" #log_letter "][" tag "]: " FURI_LOG_CLR_RESET format "\r\n" -#define FURI_LOG_SHOW(tag, format, log_level, log_letter, ...) \ - furi_log_print(log_level, FURI_LOG_FORMAT(log_letter, tag, format), ##__VA_ARGS__) - /** Log methods * * @param tag The application tag @@ -88,13 +84,15 @@ void furi_log_set_timestamp(FuriLogTimestamp timestamp); * @param ... VA Args */ #define FURI_LOG_E(tag, format, ...) \ - FURI_LOG_SHOW(tag, format, FuriLogLevelError, E, ##__VA_ARGS__) -#define FURI_LOG_W(tag, format, ...) FURI_LOG_SHOW(tag, format, FuriLogLevelWarn, W, ##__VA_ARGS__) -#define FURI_LOG_I(tag, format, ...) FURI_LOG_SHOW(tag, format, FuriLogLevelInfo, I, ##__VA_ARGS__) + furi_log_print_format(FuriLogLevelError, tag, format, ##__VA_ARGS__) +#define FURI_LOG_W(tag, format, ...) \ + furi_log_print_format(FuriLogLevelWarn, tag, format, ##__VA_ARGS__) +#define FURI_LOG_I(tag, format, ...) \ + furi_log_print_format(FuriLogLevelInfo, tag, format, ##__VA_ARGS__) #define FURI_LOG_D(tag, format, ...) \ - FURI_LOG_SHOW(tag, format, FuriLogLevelDebug, D, ##__VA_ARGS__) + furi_log_print_format(FuriLogLevelDebug, tag, format, ##__VA_ARGS__) #define FURI_LOG_T(tag, format, ...) \ - FURI_LOG_SHOW(tag, format, FuriLogLevelTrace, T, ##__VA_ARGS__) + furi_log_print_format(FuriLogLevelTrace, tag, format, ##__VA_ARGS__) #ifdef __cplusplus } diff --git a/furi/core/memmgr.c b/furi/core/memmgr.c index 01cf573e..80f87b93 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -36,10 +36,8 @@ void* calloc(size_t count, size_t size) { } char* strdup(const char* s) { - const char* s_null = s; - if(s_null == NULL) { - return NULL; - } + // arg s marked as non-null, so we need hack to check for NULL + furi_check(((uint32_t)s << 2) != 0); size_t siz = strlen(s) + 1; char* y = pvPortMalloc(siz); diff --git a/furi/core/stdglue.c b/furi/core/stdglue.c deleted file mode 100644 index 573277aa..00000000 --- a/furi/core/stdglue.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "stdglue.h" -#include "check.h" -#include "memmgr.h" - -#include -#include - -#include -#include - -DICT_DEF2( - FuriStdglueCallbackDict, - uint32_t, - M_DEFAULT_OPLIST, - FuriStdglueWriteCallback, - M_PTR_OPLIST) - -typedef struct { - FuriMutex* mutex; - FuriStdglueCallbackDict_t thread_outputs; -} FuriStdglue; - -static FuriStdglue* furi_stdglue = NULL; - -static ssize_t stdout_write(void* _cookie, const char* data, size_t size) { - furi_assert(furi_stdglue); - bool consumed = false; - FuriThreadId task_id = furi_thread_get_current_id(); - if(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING && task_id && - furi_mutex_acquire(furi_stdglue->mutex, FuriWaitForever) == FuriStatusOk) { - // We are in the thread context - // Handle thread callbacks - FuriStdglueWriteCallback* callback_ptr = - FuriStdglueCallbackDict_get(furi_stdglue->thread_outputs, (uint32_t)task_id); - if(callback_ptr) { - (*callback_ptr)(_cookie, data, size); - consumed = true; - } - furi_check(furi_mutex_release(furi_stdglue->mutex) == FuriStatusOk); - } - // Flush - if(data == 0) { - /* - * This means that we should flush internal buffers. Since we - * don't we just return. (Remember, "handle" == -1 means that all - * handles should be flushed.) - */ - return 0; - } - // Debug uart - if(!consumed) furi_hal_console_tx((const uint8_t*)data, size); - // All data consumed - return size; -} - -void furi_stdglue_init() { - furi_stdglue = malloc(sizeof(FuriStdglue)); - // Init outputs structures - furi_stdglue->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(furi_stdglue->mutex); - FuriStdglueCallbackDict_init(furi_stdglue->thread_outputs); - // Prepare and set stdout descriptor - FILE* fp = fopencookie( - NULL, - "w", - (cookie_io_functions_t){ - .read = NULL, - .write = stdout_write, - .seek = NULL, - .close = NULL, - }); - setvbuf(fp, NULL, _IOLBF, 0); - stdout = fp; -} - -bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback) { - furi_assert(furi_stdglue); - FuriThreadId task_id = furi_thread_get_current_id(); - if(task_id) { - furi_check(furi_mutex_acquire(furi_stdglue->mutex, FuriWaitForever) == FuriStatusOk); - if(callback) { - FuriStdglueCallbackDict_set_at( - furi_stdglue->thread_outputs, (uint32_t)task_id, callback); - } else { - FuriStdglueCallbackDict_erase(furi_stdglue->thread_outputs, (uint32_t)task_id); - } - furi_check(furi_mutex_release(furi_stdglue->mutex) == FuriStatusOk); - return true; - } else { - return false; - } -} - -void __malloc_lock(struct _reent* REENT) { - UNUSED(REENT); - vTaskSuspendAll(); -} - -void __malloc_unlock(struct _reent* REENT) { - UNUSED(REENT); - xTaskResumeAll(); -} diff --git a/furi/core/stdglue.h b/furi/core/stdglue.h deleted file mode 100644 index 800fcf92..00000000 --- a/furi/core/stdglue.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @file stdglue.h - * Furi: stdlibc glue - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Write callback - * @param _cookie pointer to cookie (see stdio gnu extension) - * @param data pointer to data - * @param size data size @warnign your handler must consume everything - */ -typedef void (*FuriStdglueWriteCallback)(void* _cookie, const char* data, size_t size); - -/** Initialized std library glue code */ -void furi_stdglue_init(); - -/** Set STDOUT callback for your thread - * - * @param callback callback or NULL to clear - * - * @return true on success, otherwise fail - * @warning function is thread aware, use this API from the same thread - */ -bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback); - -#ifdef __cplusplus -} -#endif diff --git a/furi/core/thread.c b/furi/core/thread.c index 097cedef..044f8371 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -4,12 +4,21 @@ #include "memmgr_heap.h" #include "check.h" #include "common_defines.h" +#include "mutex.h" #include #include +#include #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers +typedef struct FuriThreadStdout FuriThreadStdout; + +struct FuriThreadStdout { + FuriThreadStdoutWriteCallback write_callback; + string_t buffer; +}; + struct FuriThread { FuriThreadState state; int32_t ret; @@ -27,8 +36,13 @@ struct FuriThread { TaskHandle_t task_handle; bool heap_trace_enabled; size_t heap_size; + + FuriThreadStdout output; }; +static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size); +static int32_t __furi_thread_stdout_flush(FuriThread* thread); + /** Catch threads that are trying to exit wrong way */ __attribute__((__noreturn__)) void furi_thread_catch() { asm volatile("nop"); // extra magic @@ -47,6 +61,10 @@ static void furi_thread_body(void* context) { furi_assert(context); FuriThread* thread = context; + // store thread instance to thread local storage + furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) == NULL); + vTaskSetThreadLocalStoragePointer(NULL, 0, thread); + furi_assert(thread->state == FuriThreadStateStarting); furi_thread_set_state(thread, FuriThreadStateRunning); @@ -66,12 +84,18 @@ static void furi_thread_body(void* context) { furi_assert(thread->state == FuriThreadStateRunning); furi_thread_set_state(thread, FuriThreadStateStopped); + // clear thread local storage + __furi_thread_stdout_flush(thread); + furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL); + vTaskSetThreadLocalStoragePointer(NULL, 0, NULL); + vTaskDelete(thread->task_handle); furi_thread_catch(); } FuriThread* furi_thread_alloc() { FuriThread* thread = malloc(sizeof(FuriThread)); + string_init(thread->output.buffer); return thread; } @@ -81,6 +105,8 @@ void furi_thread_free(FuriThread* thread) { furi_assert(thread->state == FuriThreadStateStopped); if(thread->name) free((void*)thread->name); + string_clear(thread->output.buffer); + free(thread); } @@ -88,7 +114,7 @@ void furi_thread_set_name(FuriThread* thread, const char* name) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); if(thread->name) free((void*)thread->name); - thread->name = strdup(name); + thread->name = name ? strdup(name) : NULL; } void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) { @@ -199,6 +225,12 @@ FuriThreadId furi_thread_get_current_id() { return xTaskGetCurrentTaskHandle(); } +FuriThread* furi_thread_get_current() { + FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0); + furi_assert(thread != NULL); + return thread; +} + void furi_thread_yield() { furi_assert(!FURI_IS_IRQ_MODE()); taskYIELD(); @@ -408,3 +440,59 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) { return (sz); } + +static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) { + if(thread->output.write_callback != NULL) { + thread->output.write_callback(data, size); + } else { + furi_hal_console_tx((const uint8_t*)data, size); + } + return size; +} + +static int32_t __furi_thread_stdout_flush(FuriThread* thread) { + string_ptr buffer = thread->output.buffer; + size_t size = string_size(buffer); + if(size > 0) { + __furi_thread_stdout_write(thread, string_get_cstr(buffer), size); + string_reset(buffer); + } + return 0; +} + +bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { + FuriThread* thread = furi_thread_get_current(); + + __furi_thread_stdout_flush(thread); + thread->output.write_callback = callback; + + return true; +} + +size_t furi_thread_stdout_write(const char* data, size_t size) { + FuriThread* thread = furi_thread_get_current(); + + if(size == 0 || data == NULL) { + return __furi_thread_stdout_flush(thread); + } else { + if(data[size - 1] == '\n') { + // if the last character is a newline, we can flush buffer and write data as is, wo buffers + __furi_thread_stdout_flush(thread); + __furi_thread_stdout_write(thread, data, size); + } else { + // string_cat doesn't work here because we need to write the exact size data + for(size_t i = 0; i < size; i++) { + string_push_back(thread->output.buffer, data[i]); + if(data[i] == '\n') { + __furi_thread_stdout_flush(thread); + } + } + } + } + + return size; +} + +int32_t furi_thread_stdout_flush() { + return __furi_thread_stdout_flush(furi_thread_get_current()); +} \ No newline at end of file diff --git a/furi/core/thread.h b/furi/core/thread.h index 34eb39f0..7f746f03 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -42,6 +42,12 @@ typedef void* FuriThreadId; */ typedef int32_t (*FuriThreadCallback)(void* context); +/** Write to stdout callback + * @param data pointer to data + * @param size data size @warning your handler must consume everything + */ +typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); + /** FuriThread state change calback called upon thread state change * @param state new thread state * @param context callback context @@ -177,6 +183,12 @@ int32_t furi_thread_get_return_code(FuriThread* thread); */ FuriThreadId furi_thread_get_current_id(); +/** Get FuriThread instance for current thread + * + * @return FuriThread* + */ +FuriThread* furi_thread_get_current(); + /** Return control to scheduler */ void furi_thread_yield(); @@ -194,6 +206,29 @@ const char* furi_thread_get_name(FuriThreadId thread_id); uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); +/** Set STDOUT callback for thread + * + * @param callback callback or NULL to clear + * + * @return true on success, otherwise fail + */ +bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); + +/** Write data to buffered STDOUT + * + * @param data input data + * @param size input data size + * + * @return size_t written data size + */ +size_t furi_thread_stdout_write(const char* data, size_t size); + +/** Flush data to STDOUT + * + * @return int32_t error code + */ +int32_t furi_thread_stdout_flush(); + #ifdef __cplusplus } #endif diff --git a/furi/furi.c b/furi/furi.c index e6848624..76aed024 100644 --- a/furi/furi.c +++ b/furi/furi.c @@ -8,7 +8,6 @@ void furi_init() { furi_log_init(); furi_record_init(); - furi_stdglue_init(); } void furi_run() { diff --git a/furi/furi.h b/furi/furi.h index d78129a8..68914b50 100644 --- a/furi/furi.h +++ b/furi/furi.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/ReadMe.md b/lib/ReadMe.md index 0bdf3d63..9cd846a0 100644 --- a/lib/ReadMe.md +++ b/lib/ReadMe.md @@ -15,7 +15,7 @@ - `microtar` - TAR archive support library - `mlib` - Algorithms and containers - `nanopb` - Nano Protobuf library -- `nfc_protocols` - Nfc protocols library +- `nfc` - Nfc library - `one_wire` - One wire library - `qrcode` - Qr code generator library - `ST25RFAL002` - ST253916 driver and NFC hal diff --git a/lib/SConscript b/lib/SConscript index c5bc3947..5e5bb2ea 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -7,13 +7,14 @@ env.Append( "lib/drivers", "lib/flipper_format", "lib/infrared", - "lib/nfc_protocols", + "lib/nfc", "lib/one_wire", "lib/ST25RFAL002", "lib/subghz", "lib/toolbox", "lib/u8g2", "lib/update_util", + "lib/print", ] ) @@ -43,7 +44,7 @@ env.Append( # fnv1a-hash # micro-ecc # microtar -# nfc_protocols +# nfc # one_wire # qrcode # u8g2 @@ -60,6 +61,7 @@ libs = env.BuildModules( [ "STM32CubeWB", "freertos", + "print", "microtar", "toolbox", "ST25RFAL002", @@ -69,11 +71,11 @@ libs = env.BuildModules( "flipper_format", "infrared", "littlefs", + "mbedtls", "subghz", "nfc", "appframe", "misc", - "mbedtls", "loclass", ], ) diff --git a/lib/app-scened-template/view_controller.hpp b/lib/app-scened-template/view_controller.hpp index d08751c5..15028f53 100644 --- a/lib/app-scened-template/view_controller.hpp +++ b/lib/app-scened-template/view_controller.hpp @@ -26,7 +26,6 @@ public: 0)...); gui = static_cast(furi_record_open("gui")); - view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); }; ~ViewController() { @@ -96,6 +95,10 @@ public: furi_check(result == FuriStatusOk); } + void attach_to_gui(ViewDispatcherType type) { + view_dispatcher_attach_to_gui(view_dispatcher, gui, type); + } + private: /** * @brief ViewModulesHolder diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 09928c18..6163dee0 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -116,7 +116,7 @@ FlipperFormat* flipper_format_string_alloc(); FlipperFormat* flipper_format_file_alloc(Storage* storage); /** - * Allocate FlipperFormat as file, buffered read-only mode. + * Allocate FlipperFormat as file, buffered mode. * @return FlipperFormat* pointer to a FlipperFormat instance */ FlipperFormat* flipper_format_buffered_file_alloc(Storage* storage); @@ -131,7 +131,7 @@ FlipperFormat* flipper_format_buffered_file_alloc(Storage* storage); bool flipper_format_file_open_existing(FlipperFormat* flipper_format, const char* path); /** - * Open existing file, read-only with buffered read operations. + * Open existing file, buffered mode. * Use only if FlipperFormat allocated as a file. * @param flipper_format Pointer to a FlipperFormat instance * @param path File path diff --git a/lib/mbedtls.scons b/lib/mbedtls.scons index 1b0bec87..e39d9dae 100644 --- a/lib/mbedtls.scons +++ b/lib/mbedtls.scons @@ -11,7 +11,11 @@ env.Append( libenv = env.Clone(FW_LIB_NAME="mbedtls") libenv.ApplyLibFlags() -sources = ["mbedtls/library/des.c", "mbedtls/library/platform_util.c"] +sources = [ + "mbedtls/library/des.c", + "mbedtls/library/sha1.c", + "mbedtls/library/platform_util.c", +] lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 649a2c5f..9da89132 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -19,6 +19,7 @@ static const uint32_t nfc_keys_file_version = 1; // Protocols format versions static const uint32_t nfc_mifare_classic_data_format_version = 2; +static const uint32_t nfc_mifare_ultralight_data_format_version = 1; NfcDevice* nfc_device_alloc() { NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); @@ -97,6 +98,9 @@ static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) // Save Mifare Ultralight specific data do { if(!flipper_format_write_comment_cstr(file, "Mifare Ultralight specific data")) break; + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_mifare_ultralight_data_format_version, 1)) + break; if(!flipper_format_write_hex(file, "Signature", data->signature, sizeof(data->signature))) break; if(!flipper_format_write_hex( @@ -121,6 +125,8 @@ static bool nfc_device_save_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) // Write pages data uint32_t pages_total = data->data_size / 4; if(!flipper_format_write_uint32(file, "Pages total", &pages_total, 1)) break; + uint32_t pages_read = data->data_read / 4; + if(!flipper_format_write_uint32(file, "Pages read", &pages_read, 1)) break; bool pages_saved = true; for(uint16_t i = 0; i < data->data_size; i += 4) { string_printf(temp_str, "Page %d", i / 4); @@ -148,8 +154,14 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { MfUltralightData* data = &dev->dev_data.mf_ul_data; string_t temp_str; string_init(temp_str); + uint32_t data_format_version = 0; do { + // Read Mifare Ultralight format version + if(!flipper_format_read_uint32(file, "Data format version", &data_format_version, 1)) { + if(!flipper_format_rewind(file)) break; + } + // Read signature if(!flipper_format_read_hex(file, "Signature", data->signature, sizeof(data->signature))) break; @@ -173,11 +185,18 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { } if(!counters_parsed) break; // Read pages - uint32_t pages = 0; - if(!flipper_format_read_uint32(file, "Pages total", &pages, 1)) break; - data->data_size = pages * 4; + uint32_t pages_total = 0; + if(!flipper_format_read_uint32(file, "Pages total", &pages_total, 1)) break; + uint32_t pages_read = 0; + if(data_format_version < nfc_mifare_ultralight_data_format_version) { + pages_read = pages_total; + } else { + if(!flipper_format_read_uint32(file, "Pages read", &pages_read, 1)) break; + } + data->data_size = pages_total * 4; + data->data_read = pages_read * 4; bool pages_parsed = true; - for(uint16_t i = 0; i < pages; i++) { + for(uint16_t i = 0; i < pages_total; i++) { string_printf(temp_str, "Page %d", i); if(!flipper_format_read_hex(file, string_get_cstr(temp_str), &data->data[i * 4], 4)) { pages_parsed = false; @@ -1186,7 +1205,7 @@ void nfc_device_data_clear(NfcDeviceData* dev_data) { } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { memset(&dev_data->mf_classic_data, 0, sizeof(MfClassicData)); } else if(dev_data->protocol == NfcDeviceProtocolMifareUl) { - memset(&dev_data->mf_ul_data, 0, sizeof(MfUltralightData)); + mf_ul_reset(&dev_data->mf_ul_data); } else if(dev_data->protocol == NfcDeviceProtocolEMV) { memset(&dev_data->emv_data, 0, sizeof(EmvData)); } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index e1ff6d42..f9a0b24b 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #define NFC_DEV_NAME_MAX_LEN 22 #define NFC_READER_DATA_MAX_SIZE 64 +#define NFC_DICT_KEY_BATCH_SIZE 50 #define NFC_APP_FOLDER ANY_PATH("nfc") #define NFC_APP_EXTENSION ".nfc" @@ -41,10 +43,17 @@ typedef struct { uint16_t size; } NfcReaderRequestData; +typedef struct { + MfClassicDict* dict; +} NfcMfClassicDictAttackData; + typedef struct { FuriHalNfcDevData nfc_data; NfcProtocol protocol; - NfcReaderRequestData reader_data; + union { + NfcReaderRequestData reader_data; + NfcMfClassicDictAttackData mf_classic_dict_attack_data; + }; union { EmvData emv_data; MfUltralightData mf_ul_data; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 3a45c363..a92f148a 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -101,10 +101,10 @@ int32_t nfc_worker_task(void* context) { nfc_worker_emulate_mf_ultralight(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { nfc_worker_emulate_mf_classic(nfc_worker); - } else if(nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) { - nfc_worker_mf_classic_dict_attack(nfc_worker, MfClassicDictTypeUser); - } else if(nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack) { - nfc_worker_mf_classic_dict_attack(nfc_worker, MfClassicDictTypeFlipper); + } else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { + nfc_worker_mf_ultralight_read_auth(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { + nfc_worker_mf_classic_dict_attack(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -397,11 +397,13 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { } } -void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType type) { +void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { furi_assert(nfc_worker); furi_assert(nfc_worker->callback); MfClassicData* data = &nfc_worker->dev_data->mf_classic_data; + NfcMfClassicDictAttackData* dict_attack_data = + &nfc_worker->dev_data->mf_classic_dict_attack_data; uint32_t total_sectors = mf_classic_get_total_sectors_num(data->type); uint64_t key = 0; FuriHalNfcTxRxContext tx_rx = {}; @@ -409,15 +411,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType bool card_removed_notified = false; // Load dictionary - MfClassicDict* dict = mf_classic_dict_alloc(type); + MfClassicDict* dict = dict_attack_data->dict; if(!dict) { FURI_LOG_E(TAG, "Dictionary not found"); nfc_worker->callback(NfcWorkerEventNoDictFound, nfc_worker->context); - mf_classic_dict_free(dict); return; } - FURI_LOG_D(TAG, "Start Dictionary attack"); + FURI_LOG_D(TAG, "Start Dictionary attack, Key Count %d", mf_classic_dict_get_total_keys(dict)); for(size_t i = 0; i < total_sectors; i++) { FURI_LOG_I(TAG, "Sector %d", i); nfc_worker->callback(NfcWorkerEventNewSector, nfc_worker->context); @@ -425,7 +426,11 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType if(mf_classic_is_sector_read(data, i)) continue; bool is_key_a_found = mf_classic_is_key_found(data, i, MfClassicKeyA); bool is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); + uint16_t key_index = 0; while(mf_classic_dict_get_next_key(dict, &key)) { + if(++key_index % NFC_DICT_KEY_BATCH_SIZE == 0) { + nfc_worker->callback(NfcWorkerEventNewDictKeyBatch, nfc_worker->context); + } furi_hal_nfc_sleep(); if(furi_hal_nfc_activate_nfca(200, NULL)) { furi_hal_nfc_sleep(); @@ -456,29 +461,21 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType } } if(is_key_a_found && is_key_b_found) break; - if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || - (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) - break; + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } else { if(!card_removed_notified) { nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); card_removed_notified = true; card_found_notified = false; } - if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || - (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) - break; + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } } - if(!((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || - (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack))) - break; + if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; mf_classic_read_sector(&tx_rx, data, i); mf_classic_dict_rewind(dict); } - mf_classic_dict_free(dict); - if((nfc_worker->state == NfcWorkerStateMfClassicUserDictAttack) || - (nfc_worker->state == NfcWorkerStateMfClassicFlipperDictAttack)) { + if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); } else { nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); @@ -517,3 +514,57 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { rfal_platform_spi_release(); } + +void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + MfUltralightData* data = &nfc_worker->dev_data->mf_ul_data; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + FuriHalNfcTxRxContext tx_rx = {}; + MfUltralightReader reader = {}; + mf_ul_reset(data); + + uint32_t key = 0; + uint16_t pack = 0; + while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { + furi_hal_nfc_sleep(); + if(furi_hal_nfc_detect(nfc_data, 300) && nfc_data->type == FuriHalNfcTypeA) { + if(mf_ul_check_card_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak)) { + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + if(data->auth_method == MfUltralightAuthMethodManual) { + nfc_worker->callback(NfcWorkerEventMfUltralightPassKey, nfc_worker->context); + key = nfc_util_bytes2num(data->auth_key, 4); + } else if(data->auth_method == MfUltralightAuthMethodAmeebo) { + key = mf_ul_pwdgen_amiibo(nfc_data); + } else if(data->auth_method == MfUltralightAuthMethodXiaomi) { + key = mf_ul_pwdgen_xiaomi(nfc_data); + } else { + FURI_LOG_E(TAG, "Incorrect auth method"); + break; + } + + data->auth_success = mf_ultralight_authenticate(&tx_rx, key, &pack); + mf_ul_read_card(&tx_rx, &reader, data); + if(data->auth_success) { + MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data); + if(config_pages != NULL) { + config_pages->auth_data.pwd.value = REVERSE_BYTES_U32(key); + config_pages->auth_data.pack.value = pack; + } + nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context); + break; + } else { + nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context); + break; + } + } else { + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + furi_delay_ms(10); + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + furi_delay_ms(10); + } + } +} diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index f6df406b..22cbc3dc 100755 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -14,8 +14,8 @@ typedef enum { NfcWorkerStateUidEmulate, NfcWorkerStateMfUltralightEmulate, NfcWorkerStateMfClassicEmulate, - NfcWorkerStateMfClassicUserDictAttack, - NfcWorkerStateMfClassicFlipperDictAttack, + NfcWorkerStateReadMfUltralightReadAuth, + NfcWorkerStateMfClassicDictAttack, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -45,12 +45,17 @@ typedef enum { NfcWorkerEventAborted, NfcWorkerEventCardDetected, NfcWorkerEventNoCardDetected, + NfcWorkerEventWrongCardDetected, // Mifare Classic events NfcWorkerEventNoDictFound, NfcWorkerEventNewSector, + NfcWorkerEventNewDictKeyBatch, NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + + // Mifare Ultralight events + NfcWorkerEventMfUltralightPassKey, } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index f19f58d5..1e98879a 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -13,7 +13,6 @@ #include #include -#include "helpers/mf_classic_dict.h" #include "helpers/nfc_debug_pcap.h" struct NfcWorker { @@ -43,6 +42,10 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker); void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker); -void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker, MfClassicDictType type); +void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker); + +void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker); + +void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker); void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); diff --git a/lib/nfc/parsers/nfc_supported_card.c b/lib/nfc/parsers/nfc_supported_card.c index 44eee838..59482a12 100644 --- a/lib/nfc/parsers/nfc_supported_card.c +++ b/lib/nfc/parsers/nfc_supported_card.c @@ -3,10 +3,11 @@ #include "troyka_parser.h" NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { - [NfcSupportedCardTypeTroyka] = { - .protocol = NfcDeviceProtocolMifareClassic, - .verify = troyka_parser_verify, - .read = troyka_parser_read, - .parse = troyka_parser_parse, - }, + [NfcSupportedCardTypeTroyka] = + { + .protocol = NfcDeviceProtocolMifareClassic, + .verify = troyka_parser_verify, + .read = troyka_parser_read, + .parse = troyka_parser_parse, + }, }; diff --git a/lib/nfc/parsers/troyka_parser.c b/lib/nfc/parsers/troyka_parser.c index 653887cb..3167b518 100644 --- a/lib/nfc/parsers/troyka_parser.c +++ b/lib/nfc/parsers/troyka_parser.c @@ -39,7 +39,8 @@ bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { MfClassicReader reader = {}; FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; - mf_classic_get_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak, &reader); + reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); + for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) { mf_classic_reader_add_sector( &reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 93fe6f69..3b534393 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -324,6 +324,9 @@ 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)) { return true; + } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { + //skylanders support + return true; } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { return true; } else { @@ -335,27 +338,15 @@ MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t S UNUSED(ATQA1); if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { return MfClassicType1k; + } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { + //skylanders support + return MfClassicType1k; } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { return MfClassicType4k; } return MfClassicType1k; } -bool mf_classic_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK, MfClassicReader* reader) { - UNUSED(ATQA1); - furi_assert(reader); - memset(reader, 0, sizeof(MfClassicReader)); - - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { - reader->type = MfClassicType1k; - } else if((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) { - reader->type = MfClassicType4k; - } else { - return false; - } - return true; -} - void mf_classic_reader_add_sector( MfClassicReader* reader, uint8_t sector, @@ -780,6 +771,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ // Read command while(!command_processed) { if(!is_encrypted) { + crypto1_reset(&emulator->crypto); memcpy(plain_data, tx_rx->rx_data, tx_rx->rx_bits / 8); } else { if(!furi_hal_nfc_tx_rx(tx_rx, 300)) { @@ -812,7 +804,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ access_key = MfClassicKeyB; } - uint32_t nonce = prng_successor(DWT->CYCCNT, 32); + uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; uint8_t nt[4]; uint8_t nt_keystream[4]; nfc_util_num2bytes(nonce, 4, nt); @@ -867,7 +859,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); if(cardRr != prng_successor(nonce, 64)) { FURI_LOG_T(TAG, "Wrong AUTH! %08X != %08X", cardRr, prng_successor(nonce, 64)); - // Don't send NACK, as tag don't send it + // Don't send NACK, as the tag doesn't send it command_processed = true; break; } @@ -906,7 +898,18 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } else { if(!mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionDataRead)) { - memset(block_data, 0, 16); + // Send NACK + uint8_t nack = 0x04; + if(is_encrypted) { + mf_crypto1_encrypt( + &emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); + } else { + tx_rx->tx_data[0] = nack; + } + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_bits = 4; + furi_hal_nfc_tx_rx(tx_rx, 300); + break; } } nfca_append_crc16(block_data, 16); diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index 85f67b11..b9921fb1 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -82,8 +82,6 @@ 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); -bool mf_classic_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK, MfClassicReader* reader); - uint8_t mf_classic_get_total_sectors_num(MfClassicType type); uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector); diff --git a/lib/nfc/protocols/mifare_common.c b/lib/nfc/protocols/mifare_common.c index fd622765..90b57e1f 100644 --- a/lib/nfc/protocols/mifare_common.c +++ b/lib/nfc/protocols/mifare_common.c @@ -7,7 +7,8 @@ MifareType mifare_common_get_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { type = MifareTypeUltralight; } else if( ((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) || - ((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18))) { + ((ATQA0 == 0x42 || ATQA0 == 0x02) && (SAK == 0x18)) || + ((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01))) { type = MifareTypeClassic; } else if(ATQA0 == 0x44 && ATQA1 == 0x03 && SAK == 0x20) { type = MifareTypeDesfire; diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index 9dcd1d6a..c043f206 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -1,10 +1,39 @@ #include +#include #include "mifare_ultralight.h" +#include "nfc_util.h" #include +#include "furi_hal_nfc.h" #include #define TAG "MfUltralight" +// Algorithms from: https://github.com/RfidResearchGroup/proxmark3/blob/0f6061c16f072372b7d4d381911f1542afbc3a69/common/generator.c#L110 +uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data) { + uint8_t hash[20]; + mbedtls_sha1(data->uid, data->uid_len, hash); + + uint32_t pwd = 0; + pwd |= (hash[hash[0] % 20]) << 24; + pwd |= (hash[(hash[0] + 5) % 20]) << 16; + pwd |= (hash[(hash[0] + 13) % 20]) << 8; + pwd |= (hash[(hash[0] + 17) % 20]); + + return pwd; +} + +uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data) { + uint8_t* uid = data->uid; + + uint32_t pwd = 0; + pwd |= (uid[1] ^ uid[3] ^ 0xAA) << 24; + pwd |= (uid[2] ^ uid[4] ^ 0x55) << 16; + pwd |= (uid[3] ^ uid[5] ^ 0xAA) << 8; + pwd |= uid[4] ^ uid[6] ^ 0x55; + + return pwd; +} + bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { if((ATQA0 == 0x44) && (ATQA1 == 0x00) && (SAK == 0x00)) { return true; @@ -12,6 +41,20 @@ bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { return false; } +void mf_ul_reset(MfUltralightData* data) { + furi_assert(data); + data->type = MfUltralightTypeUnknown; + memset(&data->version, 0, sizeof(MfUltralightVersion)); + memset(data->signature, 0, sizeof(data->signature)); + memset(data->counter, 0, sizeof(data->counter)); + memset(data->tearing, 0, sizeof(data->tearing)); + memset(data->data, 0, sizeof(data->data)); + data->data_size = 0; + data->data_read = 0; + data->curr_authlim = 0; + data->has_auth = false; +} + static MfUltralightFeatures mf_ul_get_features(MfUltralightType type) { switch(type) { case MfUltralightTypeUL11: @@ -127,6 +170,37 @@ bool mf_ultralight_read_version( return version_read; } +bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack) { + bool authenticated = false; + + do { + FURI_LOG_D(TAG, "Authenticating"); + tx_rx->tx_data[0] = MF_UL_AUTH; + nfc_util_num2bytes(key, 4, &tx_rx->tx_data[1]); + tx_rx->tx_bits = 40; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(!furi_hal_nfc_tx_rx(tx_rx, 50)) { + FURI_LOG_D(TAG, "Tag did not respond to authentication"); + break; + } + + // PACK + if(tx_rx->rx_bits < 2 * 8) { + FURI_LOG_D(TAG, "Authentication failed"); + break; + } + + if(pack != NULL) { + *pack = (tx_rx->rx_data[0] << 8) | tx_rx->rx_data[1]; + } + + FURI_LOG_I(TAG, "Auth success. Password: %08X. PACK: %04X", key, *pack); + authenticated = true; + } while(false); + + return authenticated; +} + static int16_t mf_ultralight_page_addr_to_tag_addr(uint8_t sector, uint8_t page) { return sector * 256 + page; } @@ -413,7 +487,7 @@ static int16_t mf_ultralight_ntag_i2c_addr_tag_to_lin( } } -static MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data) { +MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data) { if(data->type >= MfUltralightTypeUL11 && data->type <= MfUltralightTypeNTAG216) { return (MfUltralightConfigPages*)&data->data[data->data_size - 4 * 4]; } else if( @@ -516,6 +590,7 @@ bool mf_ultralight_read_pages( tx_rx->tx_data[1] = tag_page; tx_rx->tx_bits = 16; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeDefault; + if(!furi_hal_nfc_tx_rx(tx_rx, 50) || tx_rx->rx_bits < 16 * 8) { FURI_LOG_D( TAG, @@ -524,17 +599,19 @@ bool mf_ultralight_read_pages( i + (valid_pages > 4 ? 4 : valid_pages) - 1); break; } + if(valid_pages > 4) { pages_read_cnt = 4; } else { pages_read_cnt = valid_pages; } reader->pages_read += pages_read_cnt; - data->data_size = reader->pages_read * 4; memcpy(&data->data[i * 4], tx_rx->rx_data, pages_read_cnt * 4); } + data->data_size = reader->pages_to_read * 4; + data->data_read = reader->pages_read * 4; - return reader->pages_read == reader->pages_to_read; + return reader->pages_read > 0; } bool mf_ultralight_fast_read_pages( @@ -620,6 +697,48 @@ bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* return counter_read == (is_single_counter ? 1 : 3); } +int16_t mf_ultralight_get_authlim( + FuriHalNfcTxRxContext* tx_rx, + MfUltralightReader* reader, + MfUltralightData* data) { + mf_ultralight_read_version(tx_rx, reader, data); + if(!(reader->supported_features & MfUltralightSupportAuth)) { + // No authentication + return -2; + } + + uint8_t config_pages_index; + if(data->type >= MfUltralightTypeUL11 && data->type <= MfUltralightTypeNTAG216) { + config_pages_index = reader->pages_to_read - 4; + } else if( + data->type >= MfUltralightTypeNTAGI2CPlus1K && + data->type <= MfUltralightTypeNTAGI2CPlus1K) { + config_pages_index = 0xe3; + } else { + // No config pages + return -2; + } + + if(!mf_ultralight_read_pages_direct(tx_rx, config_pages_index, data->data)) { + // Config pages are not readable due to protection + return -1; + } + + MfUltralightConfigPages* config_pages = (MfUltralightConfigPages*)&data->data; + if(config_pages->auth0 >= reader->pages_to_read) { + // Authentication is not configured + return -2; + } + + int16_t authlim = config_pages->access.authlim; + if(authlim > 0 && data->type >= MfUltralightTypeNTAGI2CPlus1K && + data->type <= MfUltralightTypeNTAGI2CPlus2K) { + authlim = 1 << authlim; + } + + return authlim; +} + bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) { uint8_t flag_read = 0; diff --git a/lib/nfc/protocols/mifare_ultralight.h b/lib/nfc/protocols/mifare_ultralight.h index 77dbd1e4..727bffab 100644 --- a/lib/nfc/protocols/mifare_ultralight.h +++ b/lib/nfc/protocols/mifare_ultralight.h @@ -28,6 +28,12 @@ #define MF_UL_NTAG203_COUNTER_PAGE (41) +typedef enum { + MfUltralightAuthMethodManual, + MfUltralightAuthMethodAmeebo, + MfUltralightAuthMethodXiaomi, +} MfUltralightAuthMethod; + // Important: order matters; some features are based on positioning in this enum typedef enum { MfUltralightTypeUnknown, @@ -50,6 +56,13 @@ typedef enum { MfUltralightTypeNum, } MfUltralightType; +typedef enum { + MfUltralightAuthLimitUnknown, + MfUltralightAuthLimitNotSupported, + MfUltralightAuthLimitConfigured, + MfUltralightAuthLimitNotConfigured, +} MfUltralightAuthLimit; + typedef enum { MfUltralightSupportNone = 0, MfUltralightSupportFastRead = 1 << 0, @@ -104,9 +117,14 @@ typedef struct { uint8_t signature[32]; uint32_t counter[3]; uint8_t tearing[3]; + bool has_auth; + MfUltralightAuthMethod auth_method; + uint8_t auth_key[4]; + bool auth_success; uint16_t curr_authlim; uint16_t data_size; uint8_t data[MF_UL_MAX_DUMP_SIZE]; + uint16_t data_read; } MfUltralightData; typedef struct __attribute__((packed)) { @@ -176,6 +194,8 @@ typedef struct { bool read_counter_incremented; } MfUltralightEmulator; +void mf_ul_reset(MfUltralightData* data); + bool mf_ul_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); bool mf_ultralight_read_version( @@ -204,6 +224,10 @@ bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data); +bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint16_t* pack); + +MfUltralightConfigPages* mf_ultralight_get_config_pages(MfUltralightData* data); + bool mf_ul_read_card( FuriHalNfcTxRxContext* tx_rx, MfUltralightReader* reader, @@ -220,3 +244,12 @@ bool mf_ul_prepare_emulation_response( uint16_t* buff_tx_len, uint32_t* data_type, void* context); + +int16_t mf_ultralight_get_authlim( + FuriHalNfcTxRxContext* tx_rx, + MfUltralightReader* reader, + MfUltralightData* data); + +uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data); + +uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data); diff --git a/lib/print/SConscript b/lib/print/SConscript new file mode 100644 index 00000000..412d17a6 --- /dev/null +++ b/lib/print/SConscript @@ -0,0 +1,107 @@ +Import("env") + +wrapped_fn_list = [ + # + # used by our firmware, so we provide their realizations + # + "fflush", + "printf", + "putc", # fallback from printf, thanks gcc + "putchar", # storage cli + "puts", # fallback from printf, thanks gcc + "snprintf", + "vsnprintf", # m-string + "__assert", # ??? + "__assert_func", # ??? + # + # wrap other functions to make sure they are not called + # realization is not provided + # + "setbuf", + "setvbuf", + "fprintf", + "vfprintf", + "vprintf", + "fputc", + "fputs", + "sprintf", # specially, because this function is dangerous + "asprintf", + "vasprintf", + "asiprintf", + "asniprintf", + "asnprintf", + "diprintf", + "fiprintf", + "iprintf", + "siprintf", + "sniprintf", + "vasiprintf", + "vasniprintf", + "vasnprintf", + "vdiprintf", + "vfiprintf", + "viprintf", + "vsiprintf", + "vsniprintf", + # + # Scanf is not implemented 4 now + # + # "fscanf", + # "scanf", + # "sscanf", + # "vsprintf", + # "fgetc", + # "fgets", + # "getc", + # "getchar", + # "gets", + # "ungetc", + # "vfscanf", + # "vscanf", + # "vsscanf", + # "fiscanf", + # "iscanf", + # "siscanf", + # "vfiscanf", + # "viscanf", + # "vsiscanf", + # + # File management + # + # "fclose", + # "freopen", + # "fread", + # "fwrite", + # "fgetpos", + # "fseek", + # "fsetpos", + # "ftell", + # "rewind", + # "feof", + # "ferror", + # "fopen", + # "remove", + # "rename", + # "fseeko", + # "ftello", +] + +for wrapped_fn in wrapped_fn_list: + env.Append( + LINKFLAGS=[ + "-Wl,--wrap," + wrapped_fn, + "-Wl,--wrap," + wrapped_fn + "_unlocked", + "-Wl,--wrap,_" + wrapped_fn + "_r", + "-Wl,--wrap,_" + wrapped_fn + "_unlocked_r", + ] + ) + +libenv = env.Clone(FW_LIB_NAME="print") +libenv.ApplyLibFlags() +libenv.Append(CCFLAGS=["-Wno-double-promotion"]) + +sources = libenv.GlobRecursive("*.c*", ".") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/print/printf_tiny.c b/lib/print/printf_tiny.c new file mode 100644 index 00000000..0db11922 --- /dev/null +++ b/lib/print/printf_tiny.c @@ -0,0 +1,1037 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// 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. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "printf_tiny.h" + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + +// internal buffer output +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) { + if(idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + +// internal null output +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) { + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + +// internal _putchar wrapper +static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) { + (void)buffer; + (void)idx; + (void)maxlen; + if(character) { + _putchar(character); + } +} + +// internal output function wrapper +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) { + (void)idx; + (void)maxlen; + if(character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char* str, size_t maxsize) { + const char* s; + for(s = str; *s && maxsize--; ++s) + ; + return (unsigned int)(s - str); +} + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) { + return (ch >= '0') && (ch <= '9'); +} + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char** str) { + unsigned int i = 0U; + while(_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + const char* buf, + size_t len, + unsigned int width, + unsigned int flags) { + const size_t start_idx = idx; + + // pad spaces up to given width + if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for(size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while(len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if(flags & FLAGS_LEFT) { + while(idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +static size_t _ntoa_format( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + char* buf, + size_t len, + bool negative, + unsigned int base, + unsigned int prec, + unsigned int width, + unsigned int flags) { + // pad leading zeros + if(!(flags & FLAGS_LEFT)) { + if(width && (flags & FLAGS_ZEROPAD) && + (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if(flags & FLAGS_HASH) { + if(!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if(len && (base == 16U)) { + len--; + } + } + if((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } else if((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } else if((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if(len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if(len < PRINTF_NTOA_BUFFER_SIZE) { + if(negative) { + buf[len++] = '-'; + } else if(flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if(flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +static size_t _ntoa_long( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + unsigned long value, + bool negative, + unsigned long base, + unsigned int prec, + unsigned int width, + unsigned int flags) { + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if(!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if(!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : + (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while(value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format( + out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + unsigned long long value, + bool negative, + unsigned long long base, + unsigned int prec, + unsigned int width, + unsigned int flags) { + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if(!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if(!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : + (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; + value /= base; + } while(value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format( + out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + double value, + unsigned int prec, + unsigned int width, + unsigned int flags); +#endif + +// internal ftoa for fixed decimal floating point +static size_t _ftoa( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + double value, + unsigned int prec, + unsigned int width, + unsigned int flags) { + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; + + // test for special values + if(value != value) return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if(value < -DBL_MAX) return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if(value > DBL_MAX) + return _out_rev( + out, + buffer, + idx, + maxlen, + (flags & FLAGS_PLUS) ? "fni+" : "fni", + (flags & FLAGS_PLUS) ? 4U : 3U, + width, + flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if(value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if(!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if(diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if(frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } else if(diff < 0.5) { + } else if((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if(prec == 0U) { + diff = value - (double)whole; + if((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while(len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if(!(frac /= 10U)) { + break; + } + } + // add extra 0s + while((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if(len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while(len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if(!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if(!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if(width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if(len < PRINTF_FTOA_BUFFER_SIZE) { + if(negative) { + buf[len++] = '-'; + } else if(flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if(flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa( + out_fct_type out, + char* buffer, + size_t idx, + size_t maxlen, + double value, + unsigned int prec, + unsigned int width, + unsigned int flags) { + // check for NaN and special values + if((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if(negative) { + value = -value; + } + + // default precision + if(!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | + (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = + (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if(value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if(flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if((value >= 1e-4) && (value < 1e6)) { + if((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } else { + // we use one sigfig for the whole part + if((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if(width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if(expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa( + out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if(minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + (expval < 0) ? -expval : expval, + expval < 0, + 10, + 0, + minwidth - 1, + FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if(flags & FLAGS_LEFT) { + while(idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + +// internal vsnprintf +static int + _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) { + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if(!buffer) { + // use null output function + out = _out_null; + } + + while(*format) { + // format specifier? %[flags][width][.precision][length] + if(*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch(*format) { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default: + n = 0U; + break; + } + } while(n); + + // evaluate width field + width = 0U; + if(_is_digit(*format)) { + width = _atoi(&format); + } else if(*format == '*') { + const int w = va_arg(va, int); + if(w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int)-w; + } else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if(*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if(_is_digit(*format)) { + precision = _atoi(&format); + } else if(*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch(*format) { + case 'l': + flags |= FLAGS_LONG; + format++; + if(*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h': + flags |= FLAGS_SHORT; + format++; + if(*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't': + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j': + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z': + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default: + break; + } + + // evaluate specifier + switch(*format) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': { + // set the base + unsigned int base; + if(*format == 'x' || *format == 'X') { + base = 16U; + } else if(*format == 'o') { + base = 8U; + } else if(*format == 'b') { + base = 2U; + } else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if(*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if(flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if((*format == 'i') || (*format == 'd')) { + // signed + if(flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long( + out, + buffer, + idx, + maxlen, + (unsigned long long)(value > 0 ? value : 0 - value), + value < 0, + base, + precision, + width, + flags); +#endif + } else if(flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + (unsigned long)(value > 0 ? value : 0 - value), + value < 0, + base, + precision, + width, + flags); + } else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : + (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : + va_arg(va, int); + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + (unsigned int)(value > 0 ? value : 0 - value), + value < 0, + base, + precision, + width, + flags); + } + } else { + // unsigned + if(flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long( + out, + buffer, + idx, + maxlen, + va_arg(va, unsigned long long), + false, + base, + precision, + width, + flags); +#endif + } else if(flags & FLAGS_LONG) { + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + va_arg(va, unsigned long), + false, + base, + precision, + width, + flags); + } else { + const unsigned int value = + (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : + (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : + va_arg(va, unsigned int); + idx = _ntoa_long( + out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f': + case 'F': + if(*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c': { + unsigned int l = 1U; + // pre padding + if(!(flags & FLAGS_LEFT)) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if(flags & FLAGS_LEFT) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's': { + const char* p = va_arg(va, char*); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); + // pre padding + if(flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if(!(flags & FLAGS_LEFT)) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if(flags & FLAGS_LEFT) { + while(l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p': { + width = sizeof(void*) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if(is_ll) { + idx = _ntoa_long_long( + out, + buffer, + idx, + maxlen, + (uintptr_t)va_arg(va, void*), + false, + 16U, + precision, + width, + flags); + } else { +#endif + idx = _ntoa_long( + out, + buffer, + idx, + maxlen, + (unsigned long)((uintptr_t)va_arg(va, void*)), + false, + 16U, + precision, + width, + flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%': + out('%', buffer, idx++, maxlen); + format++; + break; + + default: + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char* format, ...) { + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int sprintf_(char* buffer, const char* format, ...) { + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int snprintf_(char* buffer, size_t count, const char* format, ...) { + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int vprintf_(const char* format, va_list va) { + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); +} + +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) { + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) { + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = {out, arg}; + const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} diff --git a/lib/print/printf_tiny.h b/lib/print/printf_tiny.h new file mode 100644 index 00000000..8f292819 --- /dev/null +++ b/lib/print/printf_tiny.h @@ -0,0 +1,103 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// 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. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ +void _putchar(char character); + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +int printf_(const char* format, ...); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +int sprintf_(char* buffer, const char* format, ...); + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +int snprintf_(char* buffer, size_t count, const char* format, ...); +int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +int vprintf_(const char* format, va_list va); + +/** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ +int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); + +#ifdef __cplusplus +} +#endif + +#endif // _PRINTF_H_ diff --git a/lib/print/wrappers.c b/lib/print/wrappers.c new file mode 100644 index 00000000..3fe44665 --- /dev/null +++ b/lib/print/wrappers.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "printf_tiny.h" + +void _putchar(char character) { + furi_thread_stdout_write(&character, 1); +} + +int __wrap_printf(const char* format, ...) { + va_list args; + va_start(args, format); + int ret = vprintf_(format, args); + va_end(args); + + return ret; +} + +int __wrap_vsnprintf(char* str, size_t size, const char* format, va_list args) { + return vsnprintf_(str, size, format, args); +} + +int __wrap_puts(const char* str) { + size_t size = furi_thread_stdout_write(str, strlen(str)); + size += furi_thread_stdout_write("\n", 1); + return size; +} + +int __wrap_putchar(int ch) { + size_t size = furi_thread_stdout_write((char*)&ch, 1); + return size; +} + +int __wrap_putc(int ch, FILE* stream) { + UNUSED(stream); + size_t size = furi_thread_stdout_write((char*)&ch, 1); + return size; +} + +int __wrap_snprintf(char* str, size_t size, const char* format, ...) { + va_list args; + va_start(args, format); + int ret = __wrap_vsnprintf(str, size, format, args); + va_end(args); + + return ret; +} + +int __wrap_fflush(FILE* stream) { + UNUSED(stream); + furi_thread_stdout_flush(); + return 0; +} + +__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) { + UNUSED(file); + UNUSED(line); + // TODO: message file and line number + furi_crash(e); +} + +__attribute__((__noreturn__)) void + __wrap___assert_func(const char* file, int line, const char* func, const char* e) { + UNUSED(file); + UNUSED(line); + UNUSED(func); + // TODO: message file and line number + furi_crash(e); +} \ No newline at end of file diff --git a/lib/subghz/blocks/math.c b/lib/subghz/blocks/math.c index 55ad7cc8..fca50c8f 100644 --- a/lib/subghz/blocks/math.c +++ b/lib/subghz/blocks/math.c @@ -7,3 +7,11 @@ uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit) { } return key_reverse; } + +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit) { + uint8_t parity = 0; + for(uint8_t i = 0; i < count_bit; i++) { + parity += bit_read(key, i); + } + return parity & 0x01; +} \ No newline at end of file diff --git a/lib/subghz/blocks/math.h b/lib/subghz/blocks/math.h index fde5191b..85b146eb 100644 --- a/lib/subghz/blocks/math.h +++ b/lib/subghz/blocks/math.h @@ -17,3 +17,11 @@ * @return Reverse data */ uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t count_bit); + +/** + * Get parity the data bitwise. + * @param key In data + * @param count_bit number of data bits + * @return parity + */ +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t count_bit); diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c new file mode 100644 index 00000000..bd3b45fe --- /dev/null +++ b/lib/subghz/protocols/bett.c @@ -0,0 +1,357 @@ +#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; //max 24bit*2 + 2 (start, stop) + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_runing = 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); + + subghz_protocol_encoder_bett_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_bett_stop(void* context) { + SubGhzProtocolEncoderBETT* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_bett_yield(void* context) { + SubGhzProtocolEncoderBETT* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = 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))) { + //Found Preambula + 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)) { + instance->decoder.parser_step = BETTDecoderStepSaveDuration; + 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; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_bett_check_remote_controller(SubGhzBlockGeneric* instance) { + uint32_t code_found_reverse = + subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + + instance->serial = (code_found_reverse & 0xFF) << 12 | + ((code_found_reverse >> 8) & 0xFF) << 4 | + ((code_found_reverse >> 20) & 0x0F); + instance->btn = ((code_found_reverse >> 16) & 0x0F); +} + +uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + 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, + SubGhzPresetDefinition* 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, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + subghz_protocol_bett_check_remote_controller(&instance->generic); + uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); + 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, + (uint32_t)(instance->generic.data & 0xFFFFFF), + SHOW_DIP_P(data, DIP_P), + SHOW_DIP_P(data, DIP_O), + SHOW_DIP_P(data, DIP_N)); +} diff --git a/lib/subghz/protocols/bett.h b/lib/subghz/protocols/bett.h new file mode 100644 index 00000000..48f32b3e --- /dev/null +++ b/lib/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, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_bett_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* 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, string_t output); diff --git a/lib/subghz/protocols/came_twee.c b/lib/subghz/protocols/came_twee.c index ef352bf6..9bf87ea2 100644 --- a/lib/subghz/protocols/came_twee.c +++ b/lib/subghz/protocols/came_twee.c @@ -455,7 +455,7 @@ void subghz_protocol_decoder_came_twee_get_string(void* context, string_t output string_cat_printf( output, - "%s %dbit\r\n" + "%s %db\r\n" "Key:0x%lX%08lX\r\n" "Btn:%lX\r\n" "DIP:" DIP_PATTERN "\r\n", diff --git a/lib/subghz/protocols/doitrand.c b/lib/subghz/protocols/doitrand.c new file mode 100644 index 00000000..c26cbc5b --- /dev/null +++ b/lib/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_runing = 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); + + subghz_protocol_encoder_doitrand_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_doitrand_stop(void* context) { + SubGhzProtocolEncoderDoitrand* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_doitrand_yield(void* context) { + SubGhzProtocolEncoderDoitrand* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = 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, + SubGhzPresetDefinition* 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, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + subghz_protocol_doitrand_check_remote_controller(&instance->generic); + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%02lX%08lX\r\n" + "Btn:%lX\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/lib/subghz/protocols/doitrand.h b/lib/subghz/protocols/doitrand.h new file mode 100644 index 00000000..f94d7389 --- /dev/null +++ b/lib/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, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_doitrand_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* 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, string_t output); diff --git a/lib/subghz/protocols/holtek.c b/lib/subghz/protocols/holtek.c index eeb24026..ed5e4fb5 100644 --- a/lib/subghz/protocols/holtek.c +++ b/lib/subghz/protocols/holtek.c @@ -359,7 +359,7 @@ void subghz_protocol_decoder_holtek_get_string(void* context, string_t output) { output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" - "Sn:0x%05lX BTN:%X ", + "Sn:0x%05lX Btn:%X ", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), diff --git a/lib/subghz/protocols/honeywell_wdb.c b/lib/subghz/protocols/honeywell_wdb.c new file mode 100644 index 00000000..29222913 --- /dev/null +++ b/lib/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_runing = 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); + + subghz_protocol_encoder_honeywell_wdb_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_honeywell_wdb_stop(void* context) { + SubGhzProtocolEncoderHoneywell_WDB* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context) { + SubGhzProtocolEncoderHoneywell_WDB* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = 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, + SubGhzPresetDefinition* 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, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + subghz_protocol_honeywell_wdb_check_remote_controller(instance); + + 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:%01lX R:%01lX LBat:%01lX\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/lib/subghz/protocols/honeywell_wdb.h b/lib/subghz/protocols/honeywell_wdb.h new file mode 100644 index 00000000..012b3699 --- /dev/null +++ b/lib/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, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_honeywell_wdb_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* 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, string_t output); diff --git a/lib/subghz/protocols/marantec.c b/lib/subghz/protocols/marantec.c new file mode 100644 index 00000000..d4692724 --- /dev/null +++ b/lib/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_runing = 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_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_marantec_stop(void* context) { + SubGhzProtocolEncoderMarantec* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_marantec_yield(void* context) { + SubGhzProtocolEncoderMarantec* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = 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, + SubGhzPresetDefinition* 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, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + subghz_protocol_marantec_remote_controller(&instance->generic); + + string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%lX\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/lib/subghz/protocols/marantec.h b/lib/subghz/protocols/marantec.h new file mode 100644 index 00000000..2da3c88a --- /dev/null +++ b/lib/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, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_marantec_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* 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, string_t output); diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c new file mode 100644 index 00000000..e71e2834 --- /dev/null +++ b/lib/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_runing = 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); + + subghz_protocol_encoder_phoenix_v2_get_upload(instance); + instance->encoder.is_runing = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_phoenix_v2_stop(void* context) { + SubGhzProtocolEncoderPhoenix_V2* instance = context; + instance->encoder.is_runing = false; +} + +LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context) { + SubGhzProtocolEncoderPhoenix_V2* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { + instance->encoder.is_runing = 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, + SubGhzPresetDefinition* 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, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%02lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%lX\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/lib/subghz/protocols/phoenix_v2.h b/lib/subghz/protocols/phoenix_v2.h new file mode 100644 index 00000000..38cadc1a --- /dev/null +++ b/lib/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, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_phoenix_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* 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, string_t output); diff --git a/lib/subghz/protocols/princeton.c b/lib/subghz/protocols/princeton.c index 0f100137..3fdeaae9 100644 --- a/lib/subghz/protocols/princeton.c +++ b/lib/subghz/protocols/princeton.c @@ -256,8 +256,6 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t instance->generic.data = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; - instance->generic.serial = instance->decoder.decode_data >> 4; - instance->generic.btn = (uint8_t)instance->decoder.decode_data & 0x00000F; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); @@ -295,6 +293,15 @@ void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t } } +/** + * 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; @@ -347,25 +354,21 @@ bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* void subghz_protocol_decoder_princeton_get_string(void* context, string_t output) { furi_assert(context); SubGhzProtocolDecoderPrinceton* instance = context; - - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + 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); - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - string_cat_printf( output, "%s %dbit\r\n" "Key:0x%08lX\r\n" "Yek:0x%08lX\r\n" - "Sn:0x%05lX BTN:%02X\r\n" + "Sn:0x%05lX Btn:%01X\r\n" "Te:%dus\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, - code_found_lo, - code_found_reverse_lo, + (uint32_t)(instance->generic.data & 0xFFFFFF), + data_rev, instance->generic.serial, instance->generic.btn, instance->te); diff --git a/lib/subghz/protocols/registry.c b/lib/subghz/protocols/registry.c index 5a09ee8c..19b03ceb 100644 --- a/lib/subghz/protocols/registry.c +++ b/lib/subghz/protocols/registry.c @@ -1,15 +1,17 @@ #include "registry.h" const SubGhzProtocol* subghz_protocol_registry[] = { - &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_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, }; diff --git a/lib/subghz/protocols/registry.h b/lib/subghz/protocols/registry.h index d64b0cdd..d7569513 100644 --- a/lib/subghz/protocols/registry.h +++ b/lib/subghz/protocols/registry.h @@ -28,6 +28,11 @@ #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" /** * Registration by name SubGhzProtocol. diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 74a6183c..78a18693 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -237,7 +237,7 @@ bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { instance->worker_running = true; - if(furi_hal_subghz_is_tx_allowed(frequency)) { + if(furi_hal_region_is_frequency_allowed(frequency)) { instance->frequency = frequency; res = true; } diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index 38af1efe..1262362e 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -85,6 +85,10 @@ void path_concat(const char* path, const char* suffix, string_t out_path) { } bool path_contains_only_ascii(const char* path) { + if(!path) { + return false; + } + const char* name_pos = strrchr(path, '/'); if(name_pos == NULL) { name_pos = path; diff --git a/lib/toolbox/random_name.c b/lib/toolbox/random_name.c index b581bb97..5a537439 100644 --- a/lib/toolbox/random_name.c +++ b/lib/toolbox/random_name.c @@ -12,15 +12,10 @@ void set_random_name(char* name, uint8_t max_name_size) { rand_generator_inited = true; } const char* prefix[] = { - "ancient", - "hollow", - "strange", - "disappeared", - "unknown", - "unthinkable", - "unnamable", - "nameless", - "my", + "ancient", "hollow", "strange", "disappeared", "unknown", + "unthinkable", "unnamable", "nameless", "my", "concealed", + "forgotten", "hidden", "mysterious", "obscure", "random", + "remote", "uncharted", "undefined", "untravelled", "untold", }; const char* suffix[] = { @@ -32,11 +27,15 @@ void set_random_name(char* name, uint8_t max_name_size) { "entree", "opening", "crack", + "access", + "corridor", + "passage", + "port", }; uint8_t prefix_i = rand() % COUNT_OF(prefix); uint8_t suffix_i = rand() % COUNT_OF(suffix); - sniprintf(name, max_name_size, "%s_%s", prefix[prefix_i], suffix[suffix_i]); + snprintf(name, max_name_size, "%s_%s", prefix[prefix_i], suffix[suffix_i]); // Set first symbol to upper case name[0] = name[0] - 0x20; } diff --git a/lib/toolbox/stream/buffered_file_stream.c b/lib/toolbox/stream/buffered_file_stream.c index 2f2359a0..3b20a391 100644 --- a/lib/toolbox/stream/buffered_file_stream.c +++ b/lib/toolbox/stream/buffered_file_stream.c @@ -1,6 +1,5 @@ #include "buffered_file_stream.h" -#include "core/check.h" #include "stream_i.h" #include "file_stream.h" #include "stream_cache.h" @@ -9,6 +8,7 @@ typedef struct { Stream stream_base; Stream* file_stream; StreamCache* cache; + bool sync_pending; } BufferedFileStream; static void buffered_file_stream_free(BufferedFileStream* stream); @@ -27,6 +27,9 @@ static bool buffered_file_stream_delete_and_insert( StreamWriteCB write_callback, const void* ctx); +static bool buffered_file_stream_flush(BufferedFileStream* stream); +static bool buffered_file_stream_unread(BufferedFileStream* stream); + const StreamVTable buffered_file_stream_vtable = { .free = (StreamFreeFn)buffered_file_stream_free, .eof = (StreamEOFFn)buffered_file_stream_eof, @@ -39,13 +42,12 @@ const StreamVTable buffered_file_stream_vtable = { .delete_and_insert = (StreamDeleteAndInsertFn)buffered_file_stream_delete_and_insert, }; -static bool buffered_file_stream_unread(BufferedFileStream* stream); - Stream* buffered_file_stream_alloc(Storage* storage) { BufferedFileStream* stream = malloc(sizeof(BufferedFileStream)); stream->file_stream = file_stream_alloc(storage); stream->cache = stream_cache_alloc(); + stream->sync_pending = false; stream->stream_base.vtable = &buffered_file_stream_vtable; return (Stream*)stream; @@ -58,7 +60,6 @@ bool buffered_file_stream_open( FS_OpenMode open_mode) { furi_assert(_stream); BufferedFileStream* stream = (BufferedFileStream*)_stream; - stream_cache_drop(stream->cache); furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable); return file_stream_open(stream->file_stream, path, access_mode, open_mode); } @@ -67,7 +68,22 @@ bool buffered_file_stream_close(Stream* _stream) { furi_assert(_stream); BufferedFileStream* stream = (BufferedFileStream*)_stream; furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable); - return file_stream_close(stream->file_stream); + bool success = false; + do { + if(!(stream->sync_pending ? buffered_file_stream_flush(stream) : + buffered_file_stream_unread(stream))) + break; + if(!file_stream_close(stream->file_stream)) break; + success = true; + } while(false); + return success; +} + +bool buffered_file_stream_sync(Stream* _stream) { + furi_assert(_stream); + BufferedFileStream* stream = (BufferedFileStream*)_stream; + furi_check(stream->stream_base.vtable == &buffered_file_stream_vtable); + return stream->sync_pending ? buffered_file_stream_flush(stream) : true; } FS_Error buffered_file_stream_get_error(Stream* _stream) { @@ -85,10 +101,23 @@ static void buffered_file_stream_free(BufferedFileStream* stream) { } static bool buffered_file_stream_eof(BufferedFileStream* stream) { - return stream_cache_at_end(stream->cache) && stream_eof(stream->file_stream); + bool ret; + const bool file_stream_eof = stream_eof(stream->file_stream); + const bool cache_at_end = stream_cache_at_end(stream->cache); + if(!stream->sync_pending) { + ret = file_stream_eof && cache_at_end; + } else { + const size_t remaining_size = + stream_size(stream->file_stream) - stream_tell(stream->file_stream); + ret = stream_cache_size(stream->cache) >= + (remaining_size ? cache_at_end : file_stream_eof); + } + return ret; } static void buffered_file_stream_clean(BufferedFileStream* stream) { + // Not syncing because data will be deleted anyway + stream->sync_pending = false; stream_cache_drop(stream->cache); stream_clean(stream->file_stream); } @@ -97,7 +126,7 @@ static bool buffered_file_stream_seek( BufferedFileStream* stream, int32_t offset, StreamOffset offset_type) { - bool success = false; + bool success = true; int32_t new_offset = offset; if(offset_type == StreamOffsetFromCurrent) { @@ -108,47 +137,71 @@ static bool buffered_file_stream_seek( } if((new_offset != 0) || (offset_type != StreamOffsetFromCurrent)) { - stream_cache_drop(stream->cache); - success = stream_seek(stream->file_stream, new_offset, offset_type); - } else { - success = true; + if(stream->sync_pending) { + success = buffered_file_stream_sync((Stream*)stream); + } else { + stream_cache_drop(stream->cache); + } + if(success) { + success = stream_seek(stream->file_stream, new_offset, offset_type); + } } return success; } static size_t buffered_file_stream_tell(BufferedFileStream* stream) { - return stream_tell(stream->file_stream) + stream_cache_pos(stream->cache) - - stream_cache_size(stream->cache); + size_t pos = stream_tell(stream->file_stream) + stream_cache_pos(stream->cache); + if(!stream->sync_pending) { + pos -= stream_cache_size(stream->cache); + } + return pos; } static size_t buffered_file_stream_size(BufferedFileStream* stream) { - return stream_cache_size(stream->cache) + stream_size(stream->file_stream); + size_t size = stream_size(stream->file_stream); + if(stream->sync_pending) { + const size_t remaining_size = size - stream_tell(stream->file_stream); + const size_t cache_size = stream_cache_size(stream->cache); + if(cache_size > remaining_size) { + size += (cache_size - remaining_size); + } + } + return size; } static size_t buffered_file_stream_write(BufferedFileStream* stream, const uint8_t* data, size_t size) { size_t need_to_write = size; do { - if(!buffered_file_stream_unread(stream)) break; - need_to_write -= stream_write(stream->file_stream, data, size); + if(!stream->sync_pending) { + if(!buffered_file_stream_unread(stream)) break; + } + while(need_to_write) { + stream->sync_pending = true; + need_to_write -= + stream_cache_write(stream->cache, data + (size - need_to_write), need_to_write); + if(need_to_write) { + stream->sync_pending = false; + if(!stream_cache_flush(stream->cache, stream->file_stream)) break; + } + } } while(false); return size - need_to_write; } static size_t buffered_file_stream_read(BufferedFileStream* stream, uint8_t* data, size_t size) { size_t need_to_read = size; - while(need_to_read) { need_to_read -= stream_cache_read(stream->cache, data + (size - need_to_read), need_to_read); if(need_to_read) { - if(!stream_cache_fill(stream->cache, stream->file_stream)) { - break; + if(stream->sync_pending) { + if(!buffered_file_stream_flush(stream)) break; } + if(!stream_cache_fill(stream->cache, stream->file_stream)) break; } } - return size - need_to_read; } @@ -157,19 +210,43 @@ static bool buffered_file_stream_delete_and_insert( size_t delete_size, StreamWriteCB write_callback, const void* ctx) { - return buffered_file_stream_unread(stream) && - stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx); + bool success = false; + do { + if(!(stream->sync_pending ? buffered_file_stream_flush(stream) : + buffered_file_stream_unread(stream))) + break; + if(!stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx)) break; + success = true; + } while(false); + return success; +} + +// Write the cache into the underlying stream and adjust seek position +static bool buffered_file_stream_flush(BufferedFileStream* stream) { + bool success = false; + do { + const int32_t offset = stream_cache_size(stream->cache) - stream_cache_pos(stream->cache); + if(!stream_cache_flush(stream->cache, stream->file_stream)) break; + if(offset > 0) { + if(!stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent)) break; + } + success = true; + } while(false); + stream->sync_pending = false; + return success; } // Drop read cache and adjust the underlying stream seek position static bool buffered_file_stream_unread(BufferedFileStream* stream) { bool success = true; const size_t cache_size = stream_cache_size(stream->cache); - const size_t cache_pos = stream_cache_pos(stream->cache); - if(cache_pos < cache_size) { - const int32_t offset = cache_size - cache_pos; - success = stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent); + if(cache_size > 0) { + const size_t cache_pos = stream_cache_pos(stream->cache); + if(cache_pos < cache_size) { + const int32_t offset = cache_size - cache_pos; + success = stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent); + } + stream_cache_drop(stream->cache); } - stream_cache_drop(stream->cache); return success; } diff --git a/lib/toolbox/stream/buffered_file_stream.h b/lib/toolbox/stream/buffered_file_stream.h index e6ad7209..54917b6a 100644 --- a/lib/toolbox/stream/buffered_file_stream.h +++ b/lib/toolbox/stream/buffered_file_stream.h @@ -19,7 +19,7 @@ Stream* buffered_file_stream_alloc(Storage* storage); * @param path path to file * @param access_mode access mode from FS_AccessMode * @param open_mode open mode from FS_OpenMode - * @return success flag. You need to close the file even if the open operation failed. + * @return True on success, False on failure. You need to close the file even if the open operation failed. */ bool buffered_file_stream_open( Stream* stream, @@ -29,12 +29,18 @@ bool buffered_file_stream_open( /** * Closes the file. - * @param stream - * @return true - * @return false + * @param stream pointer to file stream object. + * @return True on success, False on failure. */ bool buffered_file_stream_close(Stream* stream); +/** + * Forces write from cache to the underlying file. + * @param stream pointer to file stream object. + * @return True on success, False on failure. + */ +bool buffered_file_stream_sync(Stream* stream); + /** * Retrieves the error id from the file object * @param stream pointer to stream object. diff --git a/lib/toolbox/stream/stream_cache.c b/lib/toolbox/stream/stream_cache.c index 164ac466..f5e147df 100644 --- a/lib/toolbox/stream/stream_cache.c +++ b/lib/toolbox/stream/stream_cache.c @@ -46,6 +46,14 @@ size_t stream_cache_fill(StreamCache* cache, Stream* stream) { return size_read; } +bool stream_cache_flush(StreamCache* cache, Stream* stream) { + const size_t size_written = stream_write(stream, cache->data, cache->data_size); + const bool success = (size_written == cache->data_size); + cache->data_size = 0; + cache->position = 0; + return success; +} + size_t stream_cache_read(StreamCache* cache, uint8_t* data, size_t size) { furi_assert(cache->data_size >= cache->position); const size_t size_read = MIN(size, cache->data_size - cache->position); @@ -56,6 +64,19 @@ size_t stream_cache_read(StreamCache* cache, uint8_t* data, size_t size) { return size_read; } +size_t stream_cache_write(StreamCache* cache, const uint8_t* data, size_t size) { + furi_assert(cache->data_size >= cache->position); + const size_t size_written = MIN(size, STREAM_CACHE_MAX_SIZE - cache->position); + if(size_written > 0) { + memcpy(cache->data + cache->position, data, size_written); + cache->position += size_written; + if(cache->position > cache->data_size) { + cache->data_size = cache->position; + } + } + return size_written; +} + int32_t stream_cache_seek(StreamCache* cache, int32_t offset) { furi_assert(cache->data_size >= cache->position); int32_t actual_offset = 0; @@ -63,7 +84,7 @@ int32_t stream_cache_seek(StreamCache* cache, int32_t offset) { if(offset > 0) { actual_offset = MIN(cache->data_size - cache->position, (size_t)offset); } else if(offset < 0) { - actual_offset = -MIN(cache->position, (size_t)abs(offset)); + actual_offset = MAX(-((int32_t)cache->position), offset); } cache->position += actual_offset; diff --git a/lib/toolbox/stream/stream_cache.h b/lib/toolbox/stream/stream_cache.h index 20c18d80..f61e5e8f 100644 --- a/lib/toolbox/stream/stream_cache.h +++ b/lib/toolbox/stream/stream_cache.h @@ -55,6 +55,14 @@ size_t stream_cache_pos(StreamCache* cache); */ size_t stream_cache_fill(StreamCache* cache, Stream* stream); +/** + * Write as much cached data as possible to a stream. + * @param cache Pointer to a StreamCache instance + * @param stream Pointer to a Stream instance + * @return True on success, False on failure. + */ +bool stream_cache_flush(StreamCache* cache, Stream* stream); + /** * Read cached data and advance the internal cursor. * @param cache Pointer to a StreamCache instance. @@ -64,6 +72,15 @@ size_t stream_cache_fill(StreamCache* cache, Stream* stream); */ size_t stream_cache_read(StreamCache* cache, uint8_t* data, size_t size); +/** + * Write to cached data and advance the internal cursor. + * @param cache Pointer to a StreamCache instance. + * @param data Pointer to a data buffer. + * @param size Maximum size in bytes to write to the cache. + * @return Actual size that was written. + */ +size_t stream_cache_write(StreamCache* cache, const uint8_t* data, size_t size); + /** * Move the internal cursor relatively to its current position. * @param cache Pointer to a StreamCache instance. diff --git a/scripts/otp.py b/scripts/otp.py index 7b2378d1..48056fd2 100755 --- a/scripts/otp.py +++ b/scripts/otp.py @@ -24,6 +24,7 @@ OTP_REGIONS = { "eu_ru": 0x01, "us_ca_au": 0x02, "jp": 0x03, + "world": 0x04, } OTP_DISPLAYS = {