Merge branch 'release-candidate' into release
This commit is contained in:
		
						commit
						7a6720384f
					
				
							
								
								
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -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; | ||||
|  | ||||
							
								
								
									
										4
									
								
								.vscode/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.vscode/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| ./c_cpp_properties.json | ||||
| ./launch.json | ||||
| ./settings.json | ||||
| ./tasks.json | ||||
							
								
								
									
										17
									
								
								.vscode/ReadMe.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								.vscode/ReadMe.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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. | ||||
							
								
								
									
										32
									
								
								.vscode/example/c_cpp_properties.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.vscode/example/c_cpp_properties.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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 | ||||
| } | ||||
							
								
								
									
										87
									
								
								.vscode/example/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								.vscode/example/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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": [] | ||||
|         } | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										22
									
								
								.vscode/example/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								.vscode/example/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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", | ||||
|     } | ||||
| } | ||||
							
								
								
									
										103
									
								
								.vscode/example/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								.vscode/example/tasks.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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" | ||||
|         }, | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										15
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								.vscode/extensions.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -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": [] | ||||
| } | ||||
| @ -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) | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include <gui/modules/empty_screen.h> | ||||
| #include <m-string.h> | ||||
| #include <furi_hal_version.h> | ||||
| #include <furi_hal_region.h> | ||||
| #include <furi_hal_bt.h> | ||||
| 
 | ||||
| typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message); | ||||
| @ -45,7 +46,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); | ||||
| @ -83,21 +84,22 @@ static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* | ||||
| 
 | ||||
|     string_cat_printf( | ||||
|         buffer, | ||||
|         "%d.F%dB%dC%d %s %s\n", | ||||
|         "%d.F%dB%dC%d %s:%s %s\n", | ||||
|         furi_hal_version_get_hw_version(), | ||||
|         furi_hal_version_get_hw_target(), | ||||
|         furi_hal_version_get_hw_body(), | ||||
|         furi_hal_version_get_hw_connect(), | ||||
|         furi_hal_version_get_hw_region_name(), | ||||
|         furi_hal_region_get_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 +135,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); | ||||
|  | ||||
| @ -32,14 +32,14 @@ void AccessorApp::run(void) { | ||||
| } | ||||
| 
 | ||||
| AccessorApp::AccessorApp() { | ||||
|     notification = static_cast<NotificationApp*>(furi_record_open("notification")); | ||||
|     notification = static_cast<NotificationApp*>(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); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -15,7 +15,7 @@ AccessorAppViewManager::AccessorAppViewManager() { | ||||
|     popup = popup_alloc(); | ||||
|     add_view(ViewType::Popup, popup_get_view(popup)); | ||||
| 
 | ||||
|     gui = static_cast<Gui*>(furi_record_open("gui")); | ||||
|     gui = static_cast<Gui*>(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<uint32_t>(AccessorAppViewManager::ViewType::Popup)); | ||||
| 
 | ||||
|     // free view modules
 | ||||
|     furi_record_close(RECORD_GUI); | ||||
|     submenu_free(submenu); | ||||
|     popup_free(popup); | ||||
| 
 | ||||
|  | ||||
| @ -18,6 +18,8 @@ typedef struct { | ||||
| 
 | ||||
| typedef void (*FlipperOnStartHook)(void); | ||||
| 
 | ||||
| extern const char* FLIPPER_AUTORUN_APP_NAME; | ||||
| 
 | ||||
| /* Services list
 | ||||
|  * Spawned on startup | ||||
|  */ | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -38,6 +38,5 @@ struct BadUsbApp { | ||||
| 
 | ||||
| typedef enum { | ||||
|     BadUsbAppViewError, | ||||
|     BadUsbAppViewFileSelect, | ||||
|     BadUsbAppViewWork, | ||||
| } BadUsbAppView; | ||||
|  | ||||
| @ -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)); | ||||
| 
 | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
| 
 | ||||
| /* 
 | ||||
|  | ||||
| @ -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); | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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"); | ||||
|  | ||||
| @ -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,6 +151,7 @@ static void bubble_animation_activate(BubbleAnimationView* view, bool force) { | ||||
|         activate = false; | ||||
|     } | ||||
| 
 | ||||
|     if(model->current != NULL) { | ||||
|         if(!force) { | ||||
|             if((model->active_ended_at + model->current->active_cooldown * 1000) > | ||||
|                xTaskGetTickCount()) { | ||||
| @ -161,6 +162,7 @@ static void bubble_animation_activate(BubbleAnimationView* view, bool force) { | ||||
|                 activate = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     view_commit_model(view->view, false); | ||||
| 
 | ||||
|     if(!activate && !force) { | ||||
| @ -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) { | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
| @ -36,12 +36,13 @@ void desktop_debug_render(Canvas* canvas, void* model) { | ||||
|         snprintf( | ||||
|             buffer, | ||||
|             sizeof(buffer), | ||||
|             "%d.F%dB%dC%d %s %s", | ||||
|             "%d.F%dB%dC%d %s:%s %s", | ||||
|             furi_hal_version_get_hw_version(), | ||||
|             furi_hal_version_get_hw_target(), | ||||
|             furi_hal_version_get_hw_body(), | ||||
|             furi_hal_version_get_hw_connect(), | ||||
|             furi_hal_version_get_hw_region_name(), | ||||
|             furi_hal_region_get_name(), | ||||
|             my_name ? my_name : "Unknown"); | ||||
|         canvas_draw_str(canvas, 5, 19 + STATUS_BAR_Y_SHIFT, buffer); | ||||
| 
 | ||||
| @ -78,7 +79,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 +87,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"); | ||||
|     } | ||||
|  | ||||
| @ -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( | ||||
|  | ||||
| @ -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"); | ||||
|  | ||||
| @ -185,6 +185,7 @@ static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { | ||||
|             return false; | ||||
|         }); | ||||
| 
 | ||||
|     if(item) { | ||||
|         if(item->type == ButtonMenuItemTypeControl) { | ||||
|             if(type == InputTypeShort) { | ||||
|                 if(item && item->callback) { | ||||
| @ -199,6 +200,7 @@ static void button_menu_process_ok(ButtonMenu* button_menu, InputType type) { | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool button_menu_view_input_callback(InputEvent* event, void* context) { | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| // #include <gui/view.h>
 | ||||
| #include <m-string.h> | ||||
| #include <core/common_defines.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "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"); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
| @ -291,6 +295,13 @@ bool infrared_rename_current_remote(Infrared* infrared, const char* name) { | ||||
| } | ||||
| 
 | ||||
| void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { | ||||
|     if(infrared->app_state.is_transmitting) { | ||||
|         FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already active"); | ||||
|         return; | ||||
|     } else { | ||||
|         infrared->app_state.is_transmitting = true; | ||||
|     } | ||||
| 
 | ||||
|     if(infrared_signal_is_raw(signal)) { | ||||
|         InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); | ||||
|         infrared_worker_set_raw_signal(infrared->worker, raw->timings, raw->timings_size); | ||||
| @ -300,6 +311,10 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { | ||||
|     } | ||||
| 
 | ||||
|     DOLPHIN_DEED(DolphinDeedIrSend); | ||||
|     infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); | ||||
| 
 | ||||
|     infrared_worker_tx_set_get_signal_callback( | ||||
|         infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); | ||||
|     infrared_worker_tx_start(infrared->worker); | ||||
| } | ||||
| 
 | ||||
| @ -310,14 +325,26 @@ 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) { | ||||
|     if(!infrared->app_state.is_transmitting) { | ||||
|         FURI_LOG_D(INFRARED_LOG_TAG, "Transmitter is already stopped"); | ||||
|         return; | ||||
|     } else { | ||||
|         infrared->app_state.is_transmitting = false; | ||||
|     } | ||||
| 
 | ||||
|     infrared_worker_tx_stop(infrared->worker); | ||||
|     infrared_worker_tx_set_get_signal_callback(infrared->worker, NULL, NULL); | ||||
| 
 | ||||
|     infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStop); | ||||
| } | ||||
| 
 | ||||
| void infrared_text_store_set(Infrared* infrared, uint32_t bank, const char* text, ...) { | ||||
| @ -354,12 +381,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 +426,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; | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -43,6 +43,7 @@ | ||||
| #define INFRARED_APP_EXTENSION ".ir" | ||||
| 
 | ||||
| #define INFRARED_DEFAULT_REMOTE_NAME "Remote" | ||||
| #define INFRARED_LOG_TAG "InfraredApp" | ||||
| 
 | ||||
| typedef enum { | ||||
|     InfraredButtonIndexNone = -1, | ||||
| @ -63,6 +64,7 @@ typedef enum { | ||||
| typedef struct { | ||||
|     bool is_learning_new_remote; | ||||
|     bool is_debug_enabled; | ||||
|     bool is_transmitting; | ||||
|     InfraredEditTarget edit_target : 8; | ||||
|     InfraredEditMode edit_mode : 8; | ||||
|     int32_t current_button_index; | ||||
| @ -115,10 +117,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 +135,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); | ||||
|  | ||||
| @ -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); | ||||
|                 } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|         } | ||||
|  | ||||
| @ -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); | ||||
|             } | ||||
|  | ||||
| @ -21,7 +21,10 @@ bool infrared_scene_edit_rename_done_on_event(void* context, SceneManagerEvent e | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == InfraredCustomEventTypePopupClosed) { | ||||
|             if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                    infrared->scene_manager, InfraredSceneRemote)) { | ||||
|                 scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -29,7 +29,10 @@ bool infrared_scene_learn_done_on_event(void* context, SceneManagerEvent event) | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == InfraredCustomEventTypePopupClosed) { | ||||
|             if(!scene_manager_search_and_switch_to_previous_scene( | ||||
|                    infrared->scene_manager, InfraredSceneRemote)) { | ||||
|                 scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -2,11 +2,6 @@ | ||||
| 
 | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     InfraredSceneLearnSuccessStateIdle = 0, | ||||
|     InfraredSceneLearnSuccessStateSending = 1, | ||||
| } InfraredSceneLearnSuccessState; | ||||
| 
 | ||||
| static void | ||||
|     infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { | ||||
|     Infrared* infrared = context; | ||||
| @ -21,11 +16,6 @@ void infrared_scene_learn_success_on_enter(void* context) { | ||||
|     DOLPHIN_DEED(DolphinDeedIrLearnSuccess); | ||||
|     infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn); | ||||
| 
 | ||||
|     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); | ||||
|         dialog_ex_set_header(dialog_ex, "Unknown", 95, 10, AlignCenter, AlignCenter); | ||||
| @ -65,54 +55,42 @@ void infrared_scene_learn_success_on_enter(void* context) { | ||||
|     dialog_ex_set_context(dialog_ex, context); | ||||
|     dialog_ex_enable_extended_events(dialog_ex); | ||||
| 
 | ||||
|     scene_manager_set_scene_state( | ||||
|         infrared->scene_manager, InfraredSceneLearnSuccess, InfraredSceneLearnSuccessStateIdle); | ||||
|     view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx); | ||||
| } | ||||
| 
 | ||||
| bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     Infrared* infrared = context; | ||||
|     SceneManager* scene_manager = infrared->scene_manager; | ||||
|     uint32_t scene_state = scene_manager_get_scene_state(scene_manager, InfraredSceneLearnSuccess); | ||||
|     const bool is_transmitter_idle = !infrared->app_state.is_transmitting; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeTick) { | ||||
|         if(scene_state == InfraredSceneLearnSuccessStateIdle) { | ||||
|         if(is_transmitter_idle) { | ||||
|             infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOn); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(scene_state == InfraredSceneLearnSuccessStateIdle) { | ||||
|         if(is_transmitter_idle) { | ||||
|             scene_manager_next_scene(scene_manager, InfraredSceneAskBack); | ||||
|         } | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == DialogExResultLeft) { | ||||
|             if(scene_state == InfraredSceneLearnSuccessStateIdle) { | ||||
|             if(is_transmitter_idle) { | ||||
|                 scene_manager_next_scene(scene_manager, InfraredSceneAskRetry); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event.event == DialogExResultRight) { | ||||
|             if(scene_state == InfraredSceneLearnSuccessStateIdle) { | ||||
|             if(is_transmitter_idle) { | ||||
|                 scene_manager_next_scene(scene_manager, InfraredSceneLearnEnterName); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event.event == DialogExPressCenter) { | ||||
|             if(scene_state == InfraredSceneLearnSuccessStateIdle) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     scene_manager, | ||||
|                     InfraredSceneLearnSuccess, | ||||
|                     InfraredSceneLearnSuccessStateSending); | ||||
|             infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); | ||||
|             infrared_tx_start_received(infrared); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } else if(event.event == DialogExReleaseCenter) { | ||||
|             if(scene_state == InfraredSceneLearnSuccessStateSending) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     scene_manager, InfraredSceneLearnSuccess, InfraredSceneLearnSuccessStateIdle); | ||||
|             infrared_tx_stop(infrared); | ||||
|                 infrared_play_notification_message(infrared, InfraredNotificationMessageGreenOff); | ||||
|             } | ||||
|             consumed = true; | ||||
|         } | ||||
|     } | ||||
| @ -122,9 +100,6 @@ bool infrared_scene_learn_success_on_event(void* context, SceneManagerEvent even | ||||
| 
 | ||||
| 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, InfraredNotificationMessageGreenOff); | ||||
|     infrared_worker_tx_set_get_signal_callback(worker, NULL, NULL); | ||||
|     infrared_worker_tx_set_signal_sent_callback(worker, NULL, NULL); | ||||
| } | ||||
|  | ||||
| @ -31,11 +31,6 @@ void infrared_scene_remote_on_enter(void* context) { | ||||
|     ButtonMenu* button_menu = infrared->button_menu; | ||||
|     SceneManager* scene_manager = infrared->scene_manager; | ||||
| 
 | ||||
|     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) { | ||||
|         InfraredRemoteButton* button = infrared_remote_get_button(remote, i); | ||||
| @ -75,12 +70,17 @@ void infrared_scene_remote_on_enter(void* context) { | ||||
| bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { | ||||
|     Infrared* infrared = context; | ||||
|     SceneManager* scene_manager = infrared->scene_manager; | ||||
|     const bool is_transmitter_idle = !infrared->app_state.is_transmitting; | ||||
|     bool consumed = false; | ||||
| 
 | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         if(is_transmitter_idle) { | ||||
|             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 { | ||||
|             consumed = true; | ||||
|         } | ||||
|     } 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); | ||||
| @ -94,6 +94,7 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { | ||||
|             consumed = true; | ||||
|         } else if(custom_type == InfraredCustomEventTypeMenuSelected) { | ||||
|             furi_assert(button_index < 0); | ||||
|             if(is_transmitter_idle) { | ||||
|                 scene_manager_set_scene_state( | ||||
|                     scene_manager, InfraredSceneRemote, (unsigned)button_index); | ||||
|                 if(button_index == ButtonIndexPlus) { | ||||
| @ -104,6 +105,10 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { | ||||
|                     scene_manager_next_scene(scene_manager, InfraredSceneEdit); | ||||
|                     consumed = true; | ||||
|                 } | ||||
| 
 | ||||
|             } else { | ||||
|                 consumed = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -112,7 +117,5 @@ bool infrared_scene_remote_on_event(void* context, SceneManagerEvent event) { | ||||
| 
 | ||||
| void infrared_scene_remote_on_exit(void* context) { | ||||
|     Infrared* infrared = context; | ||||
|     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); | ||||
| } | ||||
|  | ||||
| @ -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,49 @@ 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) { | ||||
|             bool result = false; | ||||
|             if(state == InfraredRpcStateSending) { | ||||
|                 infrared_tx_stop(infrared); | ||||
|             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, true); | ||||
|                 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 +92,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); | ||||
| } | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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()); | ||||
|  | ||||
| @ -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);
 | ||||
| }; | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -2,18 +2,6 @@ | ||||
| #include <core/common_defines.h> | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
|  | ||||
| @ -17,9 +17,9 @@ void LfRfidAppSceneExitConfirm::on_enter(LfRfidApp* app, bool /* need_restore */ | ||||
|     auto line_1 = container->add<StringElement>(); | ||||
|     auto line_2 = container->add<StringElement>(); | ||||
| 
 | ||||
|     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<ContainerVM>(); | ||||
| } | ||||
|  | ||||
| @ -17,9 +17,9 @@ void LfRfidAppSceneRetryConfirm::on_enter(LfRfidApp* app, bool /* need_restore * | ||||
|     auto line_1 = container->add<StringElement>(); | ||||
|     auto line_2 = container->add<StringElement>(); | ||||
| 
 | ||||
|     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<ContainerVM>(); | ||||
| } | ||||
|  | ||||
| @ -3,24 +3,12 @@ | ||||
| #include <dolphin/dolphin.h> | ||||
| #include <rpc/rpc_app.h> | ||||
| 
 | ||||
| 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<PopupVM>(); | ||||
| 
 | ||||
|     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<PopupVM>(); | ||||
| 
 | ||||
| @ -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<PopupVM>(); | ||||
|         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<PopupVM>(); | ||||
|                 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; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -5,6 +5,6 @@ App( | ||||
|     entry_point="loader_srv", | ||||
|     cdefines=["SRV_LOADER"], | ||||
|     requires=["gui"], | ||||
|     stack_size=1 * 1024, | ||||
|     stack_size=2 * 1024, | ||||
|     order=90, | ||||
| ) | ||||
|  | ||||
| @ -466,9 +466,9 @@ int32_t loader_srv(void* p) { | ||||
| 
 | ||||
|     furi_record_create(RECORD_LOADER, loader_instance); | ||||
| 
 | ||||
| #ifdef LOADER_AUTOSTART | ||||
|     loader_start(loader_instance, LOADER_AUTOSTART, NULL); | ||||
| #endif | ||||
|     if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { | ||||
|         loader_start(loader_instance, FLIPPER_AUTORUN_APP_NAME, NULL); | ||||
|     } | ||||
| 
 | ||||
|     while(1) { | ||||
|         uint32_t flags = | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -10,7 +10,7 @@ App( | ||||
|     ], | ||||
|     provides=["nfc_start"], | ||||
|     icon="A_NFC_14", | ||||
|     stack_size=4 * 1024, | ||||
|     stack_size=5 * 1024, | ||||
|     order=30, | ||||
| ) | ||||
| 
 | ||||
|  | ||||
| @ -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]; | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
							
								
								
									
										0
									
								
								applications/nfc/nfc_i.h
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										0
									
								
								applications/nfc/nfc_i.h
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -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) | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
							
								
								
									
										72
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_info.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								applications/nfc/scenes/nfc_scene_mf_classic_info.c
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
| } | ||||
| @ -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); | ||||
|  | ||||
							
								
								
									
										44
									
								
								applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								applications/nfc/scenes/nfc_scene_mf_ultralight_key_input.c
									
									
									
									
									
										Normal file
									
								
							| @ -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, ""); | ||||
| } | ||||
| @ -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); | ||||
|     } | ||||
|  | ||||
							
								
								
									
										107
									
								
								applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								applications/nfc/scenes/nfc_scene_mf_ultralight_read_auth.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| @ -0,0 +1,98 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
							
								
								
									
										92
									
								
								applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										92
									
								
								applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -1,51 +1,67 @@ | ||||
| #include "../nfc_i.h" | ||||
| #include <dolphin/dolphin.h> | ||||
| 
 | ||||
| #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; | ||||
| 
 | ||||
|     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); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
| @ -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); | ||||
| } | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
| 
 | ||||
|  | ||||
| @ -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); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -31,10 +31,18 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == NfcCustomEventByteInputDone) { | ||||
|             DOLPHIN_DEED(DolphinDeedNfcAdd); | ||||
|             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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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; | ||||
|         }); | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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
 | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov