diff --git a/base_pack/bad_bt/application.fam b/base_pack/bad_bt/application.fam deleted file mode 100644 index 981c0c0c08d..00000000000 --- a/base_pack/bad_bt/application.fam +++ /dev/null @@ -1,16 +0,0 @@ -App( - appid="bad_bt", - name="Bad BT", - apptype=FlipperAppType.EXTERNAL, - entry_point="bad_bt_app", - requires=[ - "gui", - "dialogs", - ], - stack_size=2 * 1024, - order=70, - fap_libs=["assets"], - fap_category="Bluetooth", - fap_icon="images/badbt_10px.png", - fap_icon_assets="images", -) diff --git a/base_pack/bad_bt/bad_bt_app.c b/base_pack/bad_bt/bad_bt_app.c deleted file mode 100644 index 6ac7d4fa495..00000000000 --- a/base_pack/bad_bt/bad_bt_app.c +++ /dev/null @@ -1,333 +0,0 @@ -#include "bad_bt_app.h" -#include -#include -#include -#include -#include - -#include -#include - -#define BAD_BT_SETTINGS_FILE_NAME ".badbt.settings" -#define BAD_BT_APP_PATH_BOUND_KEYS_FOLDER EXT_PATH("badbt") -#define BAD_BT_APP_PATH_BOUND_KEYS_FILE BAD_BT_APP_PATH_BOUND_KEYS_FOLDER "/.badbt.keys" - -#define BAD_BT_SETTINGS_PATH BAD_BT_APP_BASE_CONFIG_FOLDER "/" BAD_BT_SETTINGS_FILE_NAME - -static bool bad_bt_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - BadBtApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool bad_bt_app_back_event_callback(void* context) { - furi_assert(context); - BadBtApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void bad_bt_app_tick_event_callback(void* context) { - furi_assert(context); - BadBtApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -static void bad_bt_load_settings(BadBtApp* app) { - furi_string_reset(app->keyboard_layout); - strcpy(app->config.bt_name, ""); - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_BT_MAC_ADDRESS_LEN); - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(file, BAD_BT_SETTINGS_PATH)) { - FuriString* tmp_str = furi_string_alloc(); - if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) { - furi_string_reset(app->keyboard_layout); - } - if(!flipper_format_read_bool(file, "BT_Remember", &(app->bt_remember), 1)) { - app->bt_remember = false; - } - if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) { - strcpy(app->config.bt_name, furi_string_get_cstr(tmp_str)); - } else { - strcpy(app->config.bt_name, ""); - } - if(!flipper_format_read_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN)) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_BT_MAC_ADDRESS_LEN); - } - furi_string_free(tmp_str); - flipper_format_file_close(file); - } - flipper_format_free(file); - - if(!furi_string_empty(app->keyboard_layout)) { - FileInfo layout_file_info; - FS_Error file_check_err = storage_common_stat( - storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); - if(file_check_err != FSE_OK) { - furi_string_reset(app->keyboard_layout); - return; - } - if(layout_file_info.size != 256) { - furi_string_reset(app->keyboard_layout); - } - } - - furi_record_close(RECORD_STORAGE); -} - -static void bad_bt_save_settings(BadBtApp* app) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_always(file, BAD_BT_SETTINGS_PATH)) { - flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); - flipper_format_write_bool(file, "BT_Remember", &(app->bt_remember), 1); - flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name); - flipper_format_write_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN); - flipper_format_file_close(file); - } - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); -} - -void bad_bt_reload_worker(BadBtApp* app) { - bad_bt_script_close(app->bad_bt_script); - app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app); - bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout); -} - -void bad_kb_config_refresh_menu(BadBtApp* app) { - scene_manager_next_scene(app->scene_manager, BadBtSceneConfig); - scene_manager_previous_scene(app->scene_manager); -} - -int32_t bad_bt_config_switch_mode(BadBtApp* app) { - bad_bt_reload_worker(app); - furi_hal_bt_start_advertising(); - bad_kb_config_refresh_menu(app); - return 0; -} - -void bad_bt_config_switch_remember_mode(BadBtApp* app) { - if(app->bt_remember) { - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS); - bt_enable_peer_key_update(app->bt); - } else { - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - bt_set_profile_mac_address(app->bt, app->config.bt_mac); - bt_disable_peer_key_update(app->bt); - } - bad_bt_reload_worker(app); -} - -int32_t bad_bt_connection_init(BadBtApp* app) { - // Set original name and mac address in prev config - strcpy( - app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - - memcpy(app->prev_config.bt_mac, furi_hal_version_get_ble_mac(), BAD_BT_MAC_ADDRESS_LEN); - - bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(app->bt); - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - bt_keys_storage_set_storage_path(app->bt, BAD_BT_APP_PATH_BOUND_KEYS_FILE); - if(strcmp(app->config.bt_name, "") != 0) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); - } - if(app->bt_remember) { - furi_hal_bt_set_profile_mac_addr( - FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - } else { - if(memcmp( - app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) != - 0) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); - } - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - } - bt_set_profile(app->bt, BtProfileHidKeyboard); - if(strcmp(app->config.bt_name, "") == 0) { - strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - } - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) == - 0) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_BT_MAC_ADDRESS_LEN); - } - - furi_hal_bt_start_advertising(); - if(app->bt_remember) { - bt_enable_peer_key_update(app->bt); - } else { - bt_disable_peer_key_update(app->bt); - } - - return 0; -} - -void bad_bt_connection_deinit(BadBtApp* app) { - bt_disconnect(app->bt); - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - bt_keys_storage_set_default_path(app->bt); - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - bt_set_profile(app->bt, BtProfileSerial); - bt_enable_peer_key_update(app->bt); -} - -BadBtApp* bad_bt_app_alloc(char* arg) { - BadBtApp* app = malloc(sizeof(BadBtApp)); - - app->bad_bt_script = NULL; - - app->file_path = furi_string_alloc(); - app->keyboard_layout = furi_string_alloc(); - if(arg && strlen(arg)) { - furi_string_set(app->file_path, arg); - } - - Storage* storage = furi_record_open(RECORD_STORAGE); - storage_simply_mkdir(storage, BAD_BT_APP_BASE_CONFIG_FOLDER); - furi_record_close(RECORD_STORAGE); - - bad_bt_load_settings(app); - - app->gui = furi_record_open(RECORD_GUI); - app->notifications = furi_record_open(RECORD_NOTIFICATION); - app->dialogs = furi_record_open(RECORD_DIALOGS); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - - app->scene_manager = scene_manager_alloc(&bad_bt_scene_handlers, app); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_bt_app_tick_event_callback, 500); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, bad_bt_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, bad_bt_app_back_event_callback); - - Bt* bt = furi_record_open(RECORD_BT); - app->bt = bt; - app->bt->suppress_pin_screen = true; - - // Custom Widget - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewError, widget_get_view(app->widget)); - - app->var_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewConfig, variable_item_list_get_view(app->var_item_list)); - - app->bad_bt_view = bad_bt_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewWork, bad_bt_get_view(app->bad_bt_view)); - - app->text_input = text_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewConfigName, text_input_get_view(app->text_input)); - - app->byte_input = byte_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewConfigMac, byte_input_get_view(app->byte_input)); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->conn_init_thread = furi_thread_alloc_ex( - "BadBtConnInit", 1024, (FuriThreadCallback)bad_bt_connection_init, app); - furi_thread_start(app->conn_init_thread); - if(!furi_string_empty(app->file_path)) { - app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app); - bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout); - scene_manager_next_scene(app->scene_manager, BadBtSceneWork); - } else { - furi_string_set(app->file_path, BAD_BT_APP_BASE_FOLDER); - scene_manager_next_scene(app->scene_manager, BadBtSceneFileSelect); - } - - return app; -} - -void bad_bt_app_free(BadBtApp* app) { - furi_assert(app); - - if(app->bad_bt_script) { - bad_bt_script_close(app->bad_bt_script); - app->bad_bt_script = NULL; - } - - // Views - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewWork); - bad_bt_free(app->bad_bt_view); - - // Custom Widget - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewError); - widget_free(app->widget); - - // Variable item list - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfig); - variable_item_list_free(app->var_item_list); - - // Text Input - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigName); - text_input_free(app->text_input); - - // Byte Input - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigMac); - byte_input_free(app->byte_input); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Restore bt config - app->bt->suppress_pin_screen = false; - if(app->conn_init_thread) { - furi_thread_join(app->conn_init_thread); - furi_thread_free(app->conn_init_thread); - bad_bt_connection_deinit(app); - } - - // Close records - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_DIALOGS); - furi_record_close(RECORD_BT); - - bad_bt_save_settings(app); - - furi_string_free(app->file_path); - furi_string_free(app->keyboard_layout); - - free(app); -} - -int32_t bad_bt_app(void* p) { - BadBtApp* bad_bt_app = bad_bt_app_alloc((char*)p); - - view_dispatcher_run(bad_bt_app->view_dispatcher); - - bad_bt_app_free(bad_bt_app); - return 0; -} diff --git a/base_pack/bad_bt/bad_bt_app.h b/base_pack/bad_bt/bad_bt_app.h deleted file mode 100644 index 13b0844b02e..00000000000 --- a/base_pack/bad_bt/bad_bt_app.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "scenes/bad_bt_scene.h" -#include "helpers/ducky_script.h" - -#include -#include -#include -#include -#include -#include "bad_bt_icons.h" - -#define BAD_BT_APP_BASE_FOLDER EXT_PATH("badusb") -#define BAD_BT_APP_BASE_CONFIG_FOLDER EXT_PATH("badbt") -#define BAD_BT_APP_PATH_LAYOUT_FOLDER BAD_BT_APP_BASE_FOLDER "/assets/layouts" -#define BAD_BT_APP_SCRIPT_EXTENSION ".txt" -#define BAD_BT_APP_LAYOUT_EXTENSION ".kl" - -typedef enum BadBtCustomEvent { - BadBtAppCustomEventTextEditResult, - BadBtAppCustomEventByteInputDone, - BadBtCustomEventErrorBack -} BadBtCustomEvent; - -typedef enum { - BadBtAppViewError, - BadBtAppViewWork, - BadBtAppViewConfig, - BadBtAppViewConfigMac, - BadBtAppViewConfigName -} BadBtAppView; - -void bad_bt_config_switch_remember_mode(BadBtApp* app); - -int32_t bad_bt_connection_init(BadBtApp* app); - -void bad_bt_connection_deinit(BadBtApp* app); - -void bad_kb_config_refresh_menu(BadBtApp* app); \ No newline at end of file diff --git a/base_pack/bad_bt/helpers/ducky_script.c b/base_pack/bad_bt/helpers/ducky_script.c deleted file mode 100644 index bef965d1e8d..00000000000 --- a/base_pack/bad_bt/helpers/ducky_script.c +++ /dev/null @@ -1,792 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "ducky_script.h" -#include "ducky_script_i.h" -#include -#include -#include "../bad_bt_app.h" - -const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] = - {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; -const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] = - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -#define TAG "BadBT" -#define WORKER_TAG TAG "Worker" - -#define BADBT_ASCII_TO_KEY(script, x) \ - (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) - -/** - * Delays for waiting between HID key press and key release -*/ -const uint8_t bt_hid_delays[LevelRssiNum] = { - 60, // LevelRssi122_100 - 55, // LevelRssi99_80 - 50, // LevelRssi79_60 - 47, // LevelRssi59_40 - 34, // LevelRssi39_0 -}; - -uint8_t bt_timeout = 0; - -static LevelRssiRange bt_remote_rssi_range(Bt* bt) { - uint8_t rssi; - - if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError; - - if(rssi <= 39) - return LevelRssi39_0; - else if(rssi <= 59) - return LevelRssi59_40; - else if(rssi <= 79) - return LevelRssi79_60; - else if(rssi <= 99) - return LevelRssi99_80; - else if(rssi <= 122) - return LevelRssi122_100; - - return LevelRssiError; -} - -static inline void update_bt_timeout(Bt* bt) { - LevelRssiRange r = bt_remote_rssi_range(bt); - if(r < LevelRssiNum) { - bt_timeout = bt_hid_delays[r]; - FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); - } -} - -typedef enum { - WorkerEvtStartStop = (1 << 0), - WorkerEvtPauseResume = (1 << 1), - WorkerEvtEnd = (1 << 2), - WorkerEvtConnect = (1 << 3), - WorkerEvtDisconnect = (1 << 4), -} WorkerEvtFlags; - -static const char ducky_cmd_id[] = {"ID"}; -static const char ducky_cmd_bt_id[] = {"BT_ID"}; - -static const uint8_t numpad_keys[10] = { - HID_KEYPAD_0, - HID_KEYPAD_1, - HID_KEYPAD_2, - HID_KEYPAD_3, - HID_KEYPAD_4, - HID_KEYPAD_5, - HID_KEYPAD_6, - HID_KEYPAD_7, - HID_KEYPAD_8, - HID_KEYPAD_9, -}; - -uint32_t ducky_get_command_len(const char* line) { - uint32_t len = strlen(line); - for(uint32_t i = 0; i < len; i++) { - if(line[i] == ' ') return i; - } - return 0; -} - -bool ducky_is_line_end(const char chr) { - return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); -} - -uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars) { - uint16_t keycode = ducky_get_keycode_by_name(param); - if(keycode != HID_KEYBOARD_NONE) { - return keycode; - } - - if((accept_chars) && (strlen(param) > 0)) { - return (BADBT_ASCII_TO_KEY(bad_bt, param[0]) & 0xFF); - } - return 0; -} - -bool ducky_get_number(const char* param, uint32_t* val) { - uint32_t value = 0; - if(sscanf(param, "%lu", &value) == 1) { - *val = value; - return true; - } - return false; -} - -void ducky_numlock_on(BadBtScript* bad_bt) { - UNUSED(bad_bt); - if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); - } -} - -bool ducky_numpad_press(BadBtScript* bad_bt, const char num) { - UNUSED(bad_bt); - if((num < '0') || (num > '9')) return false; - - uint16_t key = numpad_keys[num - '0']; - furi_hal_bt_hid_kb_press(key); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - - return true; -} - -bool ducky_altchar(BadBtScript* bad_bt, const char* charcode) { - uint8_t i = 0; - bool state = false; - - furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); - - while(!ducky_is_line_end(charcode[i])) { - state = ducky_numpad_press(bad_bt, charcode[i]); - if(state == false) break; - i++; - } - - furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT); - - return state; -} - -bool ducky_altstring(BadBtScript* bad_bt, const char* param) { - uint32_t i = 0; - bool state = false; - - while(param[i] != '\0') { - if((param[i] < ' ') || (param[i] > '~')) { - i++; - continue; // Skip non-printable chars - } - - char temp_str[4]; - snprintf(temp_str, 4, "%u", param[i]); - - state = ducky_altchar(bad_bt, temp_str); - if(state == false) break; - i++; - } - return state; -} - -int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(bad_bt->st.error, sizeof(bad_bt->st.error), text, args); - - va_end(args); - return SCRIPT_STATE_ERROR; -} - -bool ducky_string(BadBtScript* bad_bt, const char* param) { - uint32_t i = 0; - - while(param[i] != '\0') { - if(param[i] != '\n') { - uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, param[i]); - if(keycode != HID_KEYBOARD_NONE) { - furi_hal_bt_hid_kb_press(keycode); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(keycode); - } - } else { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN); - } - i++; - } - bad_bt->stringdelay = 0; - return true; -} - -static bool ducky_string_next(BadBtScript* bad_bt) { - if(bad_bt->string_print_pos >= furi_string_size(bad_bt->string_print)) { - return true; - } - - char print_char = furi_string_get_char(bad_bt->string_print, bad_bt->string_print_pos); - - if(print_char != '\n') { - uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, print_char); - if(keycode != HID_KEYBOARD_NONE) { - furi_hal_bt_hid_kb_press(keycode); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(keycode); - } - } else { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN); - } - - bad_bt->string_print_pos++; - - return false; -} - -static int32_t ducky_parse_line(BadBtScript* bad_bt, FuriString* line) { - uint32_t line_len = furi_string_size(line); - const char* line_tmp = furi_string_get_cstr(line); - - if(line_len == 0) { - return SCRIPT_STATE_NEXT_LINE; // Skip empty lines - } - FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); - - // Ducky Lang Functions - int32_t cmd_result = ducky_execute_cmd(bad_bt, line_tmp); - if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) { - return cmd_result; - } - - // Special keys + modifiers - uint16_t key = ducky_get_keycode(bad_bt, line_tmp, false); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_bt, "No keycode defined for %s", line_tmp); - } - if((key & 0xFF00) != 0) { - // It's a modifier key - uint32_t offset = ducky_get_command_len(line_tmp) + 1; - // ducky_get_command_len() returns 0 without space, so check for != 1 - if(offset != 1 && line_len > offset) { - // It's also a key combination - key |= ducky_get_keycode(bad_bt, line_tmp + offset, true); - } - } - furi_hal_bt_hid_kb_press(key); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - - return 0; -} - -static bool ducky_set_bt_id(BadBtScript* bad_bt, const char* line) { - size_t line_len = strlen(line); - size_t mac_len = BAD_BT_MAC_ADDRESS_LEN * 3; - if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name - - uint8_t mac[BAD_BT_MAC_ADDRESS_LEN]; - for(size_t i = 0; i < BAD_BT_MAC_ADDRESS_LEN; i++) { - char a = line[i * 3]; - char b = line[i * 3 + 1]; - if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || - (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) { - return false; - } - } - furi_hal_bt_reverse_mac_addr(mac); - - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len); - bt_set_profile_mac_address(bad_bt->bt, mac); - return true; -} - -static bool ducky_script_preload(BadBtScript* bad_bt, File* script_file) { - uint8_t ret = 0; - uint32_t line_len = 0; - - furi_string_reset(bad_bt->line); - - do { - ret = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN); - for(uint16_t i = 0; i < ret; i++) { - if(bad_bt->file_buf[i] == '\n' && line_len > 0) { - bad_bt->st.line_nb++; - line_len = 0; - } else { - if(bad_bt->st.line_nb == 0) { // Save first line - furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]); - } - line_len++; - } - } - if(storage_file_eof(script_file)) { - if(line_len > 0) { - bad_bt->st.line_nb++; - break; - } - } - } while(ret > 0); - - const char* line_tmp = furi_string_get_cstr(bad_bt->line); - if(bad_bt->app->switch_mode_thread) { - furi_thread_join(bad_bt->app->switch_mode_thread); - furi_thread_free(bad_bt->app->switch_mode_thread); - bad_bt->app->switch_mode_thread = NULL; - } - // Looking for ID or BT_ID command at first line - bad_bt->set_usb_id = false; - bad_bt->set_bt_id = false; - bad_bt->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; - // TODO: We setting has_usb_id to its value but ignoring it for now and not using anywhere here, may be used in a future to detect script type - bad_bt->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; - if(bad_bt->has_bt_id) { - if(!bad_bt->app->bt_remember) { - bad_bt->set_bt_id = ducky_set_bt_id(bad_bt, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); - } - } - - bad_kb_config_refresh_menu(bad_bt->app); - - if(!bad_bt->set_bt_id) { - const char* bt_name = bad_bt->app->config.bt_name; - const uint8_t* bt_mac = bad_bt->app->bt_remember ? (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS : - bad_bt->app->config.bt_mac; - bool reset_name = strncmp( - bt_name, - furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), - BAD_BT_ADV_NAME_MAX_LEN); - bool reset_mac = memcmp( - bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_BT_MAC_ADDRESS_LEN); - if(reset_name && reset_mac) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bt_name); - } else if(reset_name) { - bt_set_profile_adv_name(bad_bt->bt, bt_name); - } - if(reset_mac) { - bt_set_profile_mac_address(bad_bt->bt, bt_mac); - } - } - - storage_file_seek(script_file, 0, true); - furi_string_reset(bad_bt->line); - - return true; -} - -static int32_t ducky_script_execute_next(BadBtScript* bad_bt, File* script_file) { - int32_t delay_val = 0; - - if(bad_bt->repeat_cnt > 0) { - bad_bt->repeat_cnt--; - delay_val = ducky_parse_line(bad_bt, bad_bt->line_prev); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays - return delay_val; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button - return delay_val; - } else if(delay_val < 0) { // Script error - bad_bt->st.error_line = bad_bt->st.line_cur - 1; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur - 1U); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_bt->defdelay); - } - } - - furi_string_set(bad_bt->line_prev, bad_bt->line); - furi_string_reset(bad_bt->line); - - while(1) { - if(bad_bt->buf_len == 0) { - bad_bt->buf_len = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN); - if(storage_file_eof(script_file)) { - if((bad_bt->buf_len < FILE_BUFFER_LEN) && (bad_bt->file_end == false)) { - bad_bt->file_buf[bad_bt->buf_len] = '\n'; - bad_bt->buf_len++; - bad_bt->file_end = true; - } - } - - bad_bt->buf_start = 0; - if(bad_bt->buf_len == 0) return SCRIPT_STATE_END; - } - for(uint8_t i = bad_bt->buf_start; i < (bad_bt->buf_start + bad_bt->buf_len); i++) { - if(bad_bt->file_buf[i] == '\n' && furi_string_size(bad_bt->line) > 0) { - bad_bt->st.line_cur++; - bad_bt->buf_len = bad_bt->buf_len + bad_bt->buf_start - (i + 1); - bad_bt->buf_start = i + 1; - furi_string_trim(bad_bt->line); - delay_val = ducky_parse_line(bad_bt, bad_bt->line); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays - return delay_val; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button - return delay_val; - } else if(delay_val < 0) { - bad_bt->st.error_line = bad_bt->st.line_cur; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_bt->defdelay); - } - } else { - furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]); - } - } - bad_bt->buf_len = 0; - if(bad_bt->file_end) return SCRIPT_STATE_END; - } - - return 0; -} - -static void bad_bt_bt_hid_state_callback(BtStatus status, void* context) { - furi_assert(context); - BadBtScript* bad_bt = context; - bool state = (status == BtStatusConnected); - - if(state == true) { - LevelRssiRange r = bt_remote_rssi_range(bad_bt->bt); - if(r != LevelRssiError) { - bt_timeout = bt_hid_delays[r]; - } - furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtConnect); - } else { - furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtDisconnect); - } -} - -static uint32_t bad_bt_flags_get(uint32_t flags_mask, uint32_t timeout) { - uint32_t flags = furi_thread_flags_get(); - furi_check((flags & FuriFlagError) == 0); - if(flags == 0) { - flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); - furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); - } else { - uint32_t state = furi_thread_flags_clear(flags); - furi_check((state & FuriFlagError) == 0); - } - return flags; -} - -static int32_t bad_bt_worker(void* context) { - BadBtScript* bad_bt = context; - - BadBtWorkerState worker_state = BadBtStateInit; - int32_t delay_val = 0; - - FURI_LOG_I(WORKER_TAG, "Init"); - File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - bad_bt->line = furi_string_alloc(); - bad_bt->line_prev = furi_string_alloc(); - bad_bt->string_print = furi_string_alloc(); - - bt_set_status_changed_callback(bad_bt->bt, bad_bt_bt_hid_state_callback, bad_bt); - - while(1) { - if(worker_state == BadBtStateInit) { // State: initialization - if(storage_file_open( - script_file, - furi_string_get_cstr(bad_bt->file_path), - FSAM_READ, - FSOM_OPEN_EXISTING)) { - if((ducky_script_preload(bad_bt, script_file)) && (bad_bt->st.line_nb > 0)) { - if(furi_hal_bt_is_connected()) { - worker_state = BadBtStateIdle; // Ready to run - } else { - worker_state = BadBtStateNotConnected; // Not connected - } - - } else { - worker_state = BadBtStateScriptError; // Script preload error - } - } else { - FURI_LOG_E(WORKER_TAG, "File open error"); - worker_state = BadBtStateFileError; // File open error - } - bad_bt->st.state = worker_state; - - } else if(worker_state == BadBtStateNotConnected) { // State: Not connected - uint32_t flags = bad_bt_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriWaitForever); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { - worker_state = BadBtStateIdle; // Ready to run - } else if(flags & WorkerEvtStartStop) { - worker_state = BadBtStateWillRun; // Will run when connected - } - bad_bt->st.state = worker_state; - - } else if(worker_state == BadBtStateIdle) { // State: ready to start - uint32_t flags = bad_bt_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect, - FuriWaitForever); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { // Start executing script - delay_val = 0; - bad_bt->buf_len = 0; - bad_bt->st.line_cur = 0; - bad_bt->defdelay = 0; - bad_bt->stringdelay = 0; - bad_bt->repeat_cnt = 0; - bad_bt->key_hold_nb = 0; - bad_bt->file_end = false; - storage_file_seek(script_file, 0, true); - bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout); - worker_state = BadBtStateRunning; - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBtStateNotConnected; // Disconnected - } - bad_bt->st.state = worker_state; - - } else if(worker_state == BadBtStateWillRun) { // State: start on connection - uint32_t flags = bad_bt_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriWaitForever); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { // Start executing script - delay_val = 0; - bad_bt->buf_len = 0; - bad_bt->st.line_cur = 0; - bad_bt->defdelay = 0; - bad_bt->stringdelay = 0; - bad_bt->repeat_cnt = 0; - bad_bt->file_end = false; - storage_file_seek(script_file, 0, true); - // extra time for PC to recognize Flipper as keyboard - flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriFlagWaitAny | FuriFlagNoClear, - 1500); - if(flags == (unsigned)FuriFlagErrorTimeout) { - // If nothing happened - start script execution - worker_state = BadBtStateRunning; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadBtStateIdle; - furi_thread_flags_clear(WorkerEvtStartStop); - } - - update_bt_timeout(bad_bt->bt); - - bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout); - } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution - worker_state = BadBtStateNotConnected; - } - bad_bt->st.state = worker_state; - - } else if(worker_state == BadBtStateRunning) { // State: running - uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect, - FuriFlagWaitAny, - delay_cur); - - delay_val -= delay_cur; - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadBtStateIdle; // Stop executing script - - furi_hal_bt_hid_kb_release_all(); - - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBtStateNotConnected; // Disconnected - - furi_hal_bt_hid_kb_release_all(); - } - bad_bt->st.state = worker_state; - continue; - } else if( - (flags == (unsigned)FuriFlagErrorTimeout) || - (flags == (unsigned)FuriFlagErrorResource)) { - if(delay_val > 0) { - bad_bt->st.delay_remain--; - continue; - } - bad_bt->st.state = BadBtStateRunning; - delay_val = ducky_script_execute_next(bad_bt, script_file); - if(delay_val == SCRIPT_STATE_ERROR) { // Script error - delay_val = 0; - worker_state = BadBtStateScriptError; - bad_bt->st.state = worker_state; - - furi_hal_bt_hid_kb_release_all(); - - } else if(delay_val == SCRIPT_STATE_END) { // End of script - delay_val = 0; - worker_state = BadBtStateIdle; - bad_bt->st.state = BadBtStateDone; - - furi_hal_bt_hid_kb_release_all(); - - continue; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays - delay_val = bad_bt->defdelay; - bad_bt->string_print_pos = 0; - worker_state = BadBtStateStringDelay; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input - worker_state = BadBtStateWaitForBtn; - bad_bt->st.state = BadBtStateWaitForBtn; // Show long delays - } else if(delay_val > 1000) { - bad_bt->st.state = BadBtStateDelay; // Show long delays - bad_bt->st.delay_remain = delay_val / 1000; - } - } else { - furi_check((flags & FuriFlagError) == 0); - } - } else if(worker_state == BadBtStateWaitForBtn) { // State: Wait for button Press - uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | - WorkerEvtDisconnect, - FuriFlagWaitAny, - delay_cur); - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - delay_val = 0; - worker_state = BadBtStateRunning; - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBtStateNotConnected; // Disconnected - furi_hal_hid_kb_release_all(); - } - bad_bt->st.state = worker_state; - continue; - } - } else if(worker_state == BadBtStateStringDelay) { // State: print string with delays - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | - WorkerEvtDisconnect, - FuriFlagWaitAny, - bad_bt->stringdelay); - - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadBtStateIdle; // Stop executing script - - furi_hal_bt_hid_kb_release_all(); - - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBtStateNotConnected; // Disconnected - - furi_hal_bt_hid_kb_release_all(); - } - bad_bt->st.state = worker_state; - continue; - } else if( - (flags == (unsigned)FuriFlagErrorTimeout) || - (flags == (unsigned)FuriFlagErrorResource)) { - bool string_end = ducky_string_next(bad_bt); - if(string_end) { - bad_bt->stringdelay = 0; - worker_state = BadBtStateRunning; - } - } else { - furi_check((flags & FuriFlagError) == 0); - } - } else if( - (worker_state == BadBtStateFileError) || - (worker_state == BadBtStateScriptError)) { // State: error - uint32_t flags = - bad_bt_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command - - if(flags & WorkerEvtEnd) { - break; - } - } - - update_bt_timeout(bad_bt->bt); - } - - bt_set_status_changed_callback(bad_bt->bt, NULL, NULL); - - storage_file_close(script_file); - storage_file_free(script_file); - furi_string_free(bad_bt->line); - furi_string_free(bad_bt->line_prev); - furi_string_free(bad_bt->string_print); - - FURI_LOG_I(WORKER_TAG, "End"); - - return 0; -} - -static void bad_bt_script_set_default_keyboard_layout(BadBtScript* bad_bt) { - furi_assert(bad_bt); - furi_string_set_str(bad_bt->keyboard_layout, ""); - memset(bad_bt->layout, HID_KEYBOARD_NONE, sizeof(bad_bt->layout)); - memcpy(bad_bt->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_bt->layout))); -} - -BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app) { - furi_assert(file_path); - - BadBtScript* bad_bt = malloc(sizeof(BadBtScript)); - bad_bt->app = app; - bad_bt->file_path = furi_string_alloc(); - furi_string_set(bad_bt->file_path, file_path); - bad_bt->keyboard_layout = furi_string_alloc(); - bad_bt_script_set_default_keyboard_layout(bad_bt); - - bad_bt->st.state = BadBtStateInit; - bad_bt->st.error[0] = '\0'; - - bad_bt->bt = bt; - - bad_bt->thread = furi_thread_alloc_ex("BadBtWorker", 2048, bad_bt_worker, bad_bt); - furi_thread_start(bad_bt->thread); - return bad_bt; -} - -void bad_bt_script_close(BadBtScript* bad_bt) { - furi_assert(bad_bt); - furi_record_close(RECORD_STORAGE); - furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtEnd); - furi_thread_join(bad_bt->thread); - furi_thread_free(bad_bt->thread); - furi_string_free(bad_bt->file_path); - furi_string_free(bad_bt->keyboard_layout); - free(bad_bt); -} - -void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path) { - furi_assert(bad_bt); - - if((bad_bt->st.state == BadBtStateRunning) || (bad_bt->st.state == BadBtStateDelay)) { - // do not update keyboard layout while a script is running - return; - } - - File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(!furi_string_empty(layout_path)) { //-V1051 - furi_string_set(bad_bt->keyboard_layout, layout_path); - if(storage_file_open( - layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint16_t layout[128]; - if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { - memcpy(bad_bt->layout, layout, sizeof(layout)); - } - } - storage_file_close(layout_file); - } else { - bad_bt_script_set_default_keyboard_layout(bad_bt); - } - storage_file_free(layout_file); -} - -void bad_bt_script_toggle(BadBtScript* bad_bt) { - furi_assert(bad_bt); - furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtStartStop); -} - -BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt) { - furi_assert(bad_bt); - return &(bad_bt->st); -} \ No newline at end of file diff --git a/base_pack/bad_bt/helpers/ducky_script.h b/base_pack/bad_bt/helpers/ducky_script.h deleted file mode 100644 index ea0f91040b8..00000000000 --- a/base_pack/bad_bt/helpers/ducky_script.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include -#include "../views/bad_bt_view.h" - -#define FILE_BUFFER_LEN 16 - -typedef enum { - LevelRssi122_100, - LevelRssi99_80, - LevelRssi79_60, - LevelRssi59_40, - LevelRssi39_0, - LevelRssiNum, - LevelRssiError = 0xFF, -} LevelRssiRange; - -extern const uint8_t bt_hid_delays[LevelRssiNum]; - -extern uint8_t bt_timeout; - -typedef enum { - BadBtStateInit, - BadBtStateNotConnected, - BadBtStateIdle, - BadBtStateWillRun, - BadBtStateRunning, - BadBtStateDelay, - BadBtStateStringDelay, - BadBtStateWaitForBtn, - BadBtStateDone, - BadBtStateScriptError, - BadBtStateFileError, -} BadBtWorkerState; - -struct BadBtState { - BadBtWorkerState state; - uint32_t pin; - uint16_t line_cur; - uint16_t line_nb; - uint32_t delay_remain; - uint16_t error_line; - char error[64]; -}; - -typedef struct BadBtApp BadBtApp; - -typedef struct { - FuriHalUsbHidConfig hid_cfg; - FuriThread* thread; - BadBtState st; - - FuriString* file_path; - FuriString* keyboard_layout; - uint8_t file_buf[FILE_BUFFER_LEN + 1]; - uint8_t buf_start; - uint8_t buf_len; - bool file_end; - - uint32_t defdelay; - uint32_t stringdelay; - uint16_t layout[128]; - - FuriString* line; - FuriString* line_prev; - uint32_t repeat_cnt; - uint8_t key_hold_nb; - - bool set_usb_id; - bool set_bt_id; - bool has_usb_id; - bool has_bt_id; - - FuriString* string_print; - size_t string_print_pos; - - Bt* bt; - BadBtApp* app; -} BadBtScript; - -BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app); - -void bad_bt_script_close(BadBtScript* bad_bt); - -void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path); - -void bad_bt_script_start(BadBtScript* bad_bt); - -void bad_bt_script_stop(BadBtScript* bad_bt); - -void bad_bt_script_toggle(BadBtScript* bad_bt); - -BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt); - -#define BAD_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define BAD_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE - -// this is the MAC address used when we do not forget paired device (BOUND STATE) -extern const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN]; -extern const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN]; - -typedef enum { - BadBtAppErrorNoFiles, - BadBtAppErrorCloseRpc, -} BadBtAppError; - -typedef struct { - char bt_name[BAD_BT_ADV_NAME_MAX_LEN]; - uint8_t bt_mac[BAD_BT_MAC_ADDRESS_LEN]; - GapPairing bt_mode; -} BadBtConfig; - -struct BadBtApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Widget* widget; - VariableItemList* var_item_list; - TextInput* text_input; - ByteInput* byte_input; - - BadBtAppError error; - FuriString* file_path; - FuriString* keyboard_layout; - BadBt* bad_bt_view; - BadBtScript* bad_bt_script; - - Bt* bt; - bool bt_remember; - BadBtConfig config; - BadBtConfig prev_config; - FuriThread* conn_init_thread; - FuriThread* switch_mode_thread; -}; - -int32_t bad_bt_config_switch_mode(BadBtApp* app); - -#ifdef __cplusplus -} -#endif diff --git a/base_pack/bad_bt/helpers/ducky_script_commands.c b/base_pack/bad_bt/helpers/ducky_script_commands.c deleted file mode 100644 index eddab96ac47..00000000000 --- a/base_pack/bad_bt/helpers/ducky_script_commands.c +++ /dev/null @@ -1,201 +0,0 @@ -#include -#include -#include "ducky_script.h" -#include "ducky_script_i.h" - -typedef int32_t (*DuckyCmdCallback)(BadBtScript* bad_bt, const char* line, int32_t param); - -typedef struct { - char* name; - DuckyCmdCallback callback; - int32_t param; -} DuckyCmd; - -static int32_t ducky_fnc_delay(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint32_t delay_val = 0; - bool state = ducky_get_number(line, &delay_val); - if((state) && (delay_val > 0)) { - return (int32_t)delay_val; - } - - return ducky_error(bad_bt, "Invalid number %s", line); -} - -static int32_t ducky_fnc_defdelay(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_bt->defdelay); - if(!state) { - return ducky_error(bad_bt, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_strdelay(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_bt->stringdelay); - if(!state) { - return ducky_error(bad_bt, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_string(BadBtScript* bad_bt, const char* line, int32_t param) { - line = &line[ducky_get_command_len(line) + 1]; - furi_string_set_str(bad_bt->string_print, line); - if(param == 1) { - furi_string_cat(bad_bt->string_print, "\n"); - } - - if(bad_bt->stringdelay == 0) { // stringdelay not set - run command immidiately - bool state = ducky_string(bad_bt, furi_string_get_cstr(bad_bt->string_print)); - if(!state) { - return ducky_error(bad_bt, "Invalid string %s", line); - } - } else { // stringdelay is set - run command in thread to keep handling external events - return SCRIPT_STATE_STRING_START; - } - - return 0; -} - -static int32_t ducky_fnc_repeat(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_bt->repeat_cnt); - if((!state) || (bad_bt->repeat_cnt == 0)) { - return ducky_error(bad_bt, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_sysrq(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_bt, line, true); - - furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - furi_hal_bt_hid_kb_press(key); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - return 0; -} - -static int32_t ducky_fnc_altchar(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - ducky_numlock_on(bad_bt); - bool state = ducky_altchar(bad_bt, line); - if(!state) { - return ducky_error(bad_bt, "Invalid altchar %s", line); - } - return 0; -} - -static int32_t ducky_fnc_altstring(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - ducky_numlock_on(bad_bt); - bool state = ducky_altstring(bad_bt, line); - if(!state) { - return ducky_error(bad_bt, "Invalid altstring %s", line); - } - return 0; -} - -static int32_t ducky_fnc_hold(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_bt, line, true); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_bt, "No keycode defined for %s", line); - } - bad_bt->key_hold_nb++; - if(bad_bt->key_hold_nb > (HID_KB_MAX_KEYS - 1)) { - return ducky_error(bad_bt, "Too many keys are hold"); - } - furi_hal_bt_hid_kb_press(key); - - return 0; -} - -static int32_t ducky_fnc_release(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_bt, line, true); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_bt, "No keycode defined for %s", line); - } - if(bad_bt->key_hold_nb == 0) { - return ducky_error(bad_bt, "No keys are hold"); - } - bad_bt->key_hold_nb--; - furi_hal_bt_hid_kb_release(key); - return 0; -} - -static int32_t ducky_fnc_waitforbutton(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - UNUSED(bad_bt); - UNUSED(line); - - return SCRIPT_STATE_WAIT_FOR_BTN; -} - -static const DuckyCmd ducky_commands[] = { - {"REM", NULL, -1}, - {"ID", NULL, -1}, - {"BT_ID", NULL, -1}, - {"DELAY", ducky_fnc_delay, -1}, - {"STRING", ducky_fnc_string, 0}, - {"STRINGLN", ducky_fnc_string, 1}, - {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, - {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, - {"STRINGDELAY", ducky_fnc_strdelay, -1}, - {"STRING_DELAY", ducky_fnc_strdelay, -1}, - {"REPEAT", ducky_fnc_repeat, -1}, - {"SYSRQ", ducky_fnc_sysrq, -1}, - {"ALTCHAR", ducky_fnc_altchar, -1}, - {"ALTSTRING", ducky_fnc_altstring, -1}, - {"ALTCODE", ducky_fnc_altstring, -1}, - {"HOLD", ducky_fnc_hold, -1}, - {"RELEASE", ducky_fnc_release, -1}, - {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, -}; - -#define TAG "BadBT" -#define WORKER_TAG TAG "Worker" - -int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line) { - size_t cmd_word_len = strcspn(line, " "); - for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { - size_t cmd_compare_len = strlen(ducky_commands[i].name); - - if(cmd_compare_len != cmd_word_len) { - continue; - } - - if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { - if(ducky_commands[i].callback == NULL) { - return 0; - } else { - return ((ducky_commands[i].callback)(bad_bt, line, ducky_commands[i].param)); - } - } - } - - return SCRIPT_STATE_CMD_UNKNOWN; -} diff --git a/base_pack/bad_bt/helpers/ducky_script_i.h b/base_pack/bad_bt/helpers/ducky_script_i.h deleted file mode 100644 index 08afa65a4d5..00000000000 --- a/base_pack/bad_bt/helpers/ducky_script_i.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "ducky_script.h" - -#define SCRIPT_STATE_ERROR (-1) -#define SCRIPT_STATE_END (-2) -#define SCRIPT_STATE_NEXT_LINE (-3) -#define SCRIPT_STATE_CMD_UNKNOWN (-4) -#define SCRIPT_STATE_STRING_START (-5) -#define SCRIPT_STATE_WAIT_FOR_BTN (-6) - -uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars); - -uint32_t ducky_get_command_len(const char* line); - -bool ducky_is_line_end(const char chr); - -uint16_t ducky_get_keycode_by_name(const char* param); - -bool ducky_get_number(const char* param, uint32_t* val); - -void ducky_numlock_on(BadBtScript* bad_bt); - -bool ducky_numpad_press(BadBtScript* bad_bt, const char num); - -bool ducky_altchar(BadBtScript* bad_bt, const char* charcode); - -bool ducky_altstring(BadBtScript* bad_bt, const char* param); - -bool ducky_string(BadBtScript* bad_bt, const char* param); - -int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line); - -int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...); - -#ifdef __cplusplus -} -#endif diff --git a/base_pack/bad_bt/scenes/bad_bt_scene.c b/base_pack/bad_bt/scenes/bad_bt_scene.c deleted file mode 100644 index c207ae44bbb..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "bad_bt_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const bad_bt_scene_on_enter_handlers[])(void*) = { -#include "bad_bt_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const bad_bt_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "bad_bt_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const bad_bt_scene_on_exit_handlers[])(void* context) = { -#include "bad_bt_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers bad_bt_scene_handlers = { - .on_enter_handlers = bad_bt_scene_on_enter_handlers, - .on_event_handlers = bad_bt_scene_on_event_handlers, - .on_exit_handlers = bad_bt_scene_on_exit_handlers, - .scene_num = BadBtSceneNum, -}; diff --git a/base_pack/bad_bt/scenes/bad_bt_scene_config.c b/base_pack/bad_bt/scenes/bad_bt_scene_config.c deleted file mode 100644 index 5fc9c001218..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene_config.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "../bad_bt_app.h" -#include "../helpers/ducky_script.h" -#include "furi_hal_power.h" - -enum VarItemListIndex { - VarItemListIndexKeyboardLayout, - VarItemListIndexBtRemember, - VarItemListIndexBtDeviceName, - VarItemListIndexBtMacAddress, - VarItemListIndexRandomizeBtMac, -}; - -void bad_bt_scene_config_bt_remember_callback(VariableItem* item) { - BadBtApp* bad_bt = variable_item_get_context(item); - bad_bt->bt_remember = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF"); - view_dispatcher_send_custom_event(bad_bt->view_dispatcher, VarItemListIndexBtRemember); -} - -void bad_bt_scene_config_var_item_list_callback(void* context, uint32_t index) { - BadBtApp* bad_bt = context; - view_dispatcher_send_custom_event(bad_bt->view_dispatcher, index); -} - -void bad_bt_scene_config_on_enter(void* context) { - BadBtApp* bad_bt = context; - VariableItemList* var_item_list = bad_bt->var_item_list; - VariableItem* item; - - item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_bt); - - item = variable_item_list_add( - var_item_list, "BT Remember", 2, bad_bt_scene_config_bt_remember_callback, bad_bt); - variable_item_set_current_value_index(item, bad_bt->bt_remember); - variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF"); - - item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_bt); - if(bad_bt->bad_bt_script->set_bt_id) { - variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!"); - } - - item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_bt); - if(bad_bt->bt_remember) { - variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } else if(bad_bt->bad_bt_script->set_bt_id) { - variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); - } - - item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_bt); - if(bad_bt->bt_remember) { - variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } else if(bad_bt->bad_bt_script->set_bt_id) { - variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); - } - - variable_item_list_set_enter_callback( - var_item_list, bad_bt_scene_config_var_item_list_callback, bad_bt); - - variable_item_list_set_selected_item( - var_item_list, scene_manager_get_scene_state(bad_bt->scene_manager, BadBtSceneConfig)); - - view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfig); -} - -bool bad_bt_scene_config_on_event(void* context, SceneManagerEvent event) { - BadBtApp* bad_bt = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(bad_bt->scene_manager, BadBtSceneConfig, event.event); - consumed = true; - switch(event.event) { - case VarItemListIndexKeyboardLayout: - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigLayout); - break; - case VarItemListIndexBtRemember: - bad_bt_config_switch_remember_mode(bad_bt); - scene_manager_previous_scene(bad_bt->scene_manager); - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfig); - break; - case VarItemListIndexBtDeviceName: - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigName); - break; - case VarItemListIndexBtMacAddress: - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigMac); - break; - case VarItemListIndexRandomizeBtMac: - furi_hal_random_fill_buf(bad_bt->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN); - bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac); - break; - default: - break; - } - } - - return consumed; -} - -void bad_bt_scene_config_on_exit(void* context) { - BadBtApp* bad_bt = context; - VariableItemList* var_item_list = bad_bt->var_item_list; - - variable_item_list_reset(var_item_list); -} diff --git a/base_pack/bad_bt/scenes/bad_bt_scene_config.h b/base_pack/bad_bt/scenes/bad_bt_scene_config.h deleted file mode 100644 index f7914e6dd9e..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene_config.h +++ /dev/null @@ -1,7 +0,0 @@ -ADD_SCENE(bad_bt, file_select, FileSelect) -ADD_SCENE(bad_bt, work, Work) -ADD_SCENE(bad_bt, error, Error) -ADD_SCENE(bad_bt, config, Config) -ADD_SCENE(bad_bt, config_layout, ConfigLayout) -ADD_SCENE(bad_bt, config_name, ConfigName) -ADD_SCENE(bad_bt, config_mac, ConfigMac) diff --git a/base_pack/bad_bt/scenes/bad_bt_scene_config_layout.c b/base_pack/bad_bt/scenes/bad_bt_scene_config_layout.c deleted file mode 100644 index b0ce2d084e8..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene_config_layout.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../bad_bt_app.h" -#include "furi_hal_power.h" -#include - -static bool bad_bt_layout_select(BadBtApp* bad_bt) { - furi_assert(bad_bt); - - FuriString* predefined_path; - predefined_path = furi_string_alloc(); - if(!furi_string_empty(bad_bt->keyboard_layout)) { - furi_string_set(predefined_path, bad_bt->keyboard_layout); - } else { - furi_string_set(predefined_path, BAD_BT_APP_PATH_LAYOUT_FOLDER); - } - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_BT_APP_LAYOUT_EXTENSION, &I_keyboard_10px); - browser_options.base_path = BAD_BT_APP_PATH_LAYOUT_FOLDER; - browser_options.skip_assets = false; - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_bt->dialogs, bad_bt->keyboard_layout, predefined_path, &browser_options); - - furi_string_free(predefined_path); - return res; -} - -void bad_bt_scene_config_layout_on_enter(void* context) { - BadBtApp* bad_bt = context; - - if(bad_bt_layout_select(bad_bt)) { - bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout); - } - scene_manager_previous_scene(bad_bt->scene_manager); -} - -bool bad_bt_scene_config_layout_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void bad_bt_scene_config_layout_on_exit(void* context) { - UNUSED(context); -} diff --git a/base_pack/bad_bt/scenes/bad_bt_scene_config_mac.c b/base_pack/bad_bt/scenes/bad_bt_scene_config_mac.c deleted file mode 100644 index dcc783f0f91..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene_config_mac.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "../bad_bt_app.h" - -#define TAG "BadBtConfigMac" - -void bad_bt_scene_config_mac_byte_input_callback(void* context) { - BadBtApp* bad_bt = context; - - view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventByteInputDone); -} - -void bad_bt_scene_config_mac_on_enter(void* context) { - BadBtApp* bad_bt = context; - - furi_hal_bt_reverse_mac_addr(bad_bt->config.bt_mac); - - // Setup view - ByteInput* byte_input = bad_bt->byte_input; - byte_input_set_header_text(byte_input, "Set BT MAC address"); - byte_input_set_result_callback( - byte_input, - bad_bt_scene_config_mac_byte_input_callback, - NULL, - bad_bt, - bad_bt->config.bt_mac, - GAP_MAC_ADDR_SIZE); - view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigMac); -} - -bool bad_bt_scene_config_mac_on_event(void* context, SceneManagerEvent event) { - BadBtApp* bad_bt = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadBtAppCustomEventByteInputDone) { - scene_manager_previous_scene(bad_bt->scene_manager); - consumed = true; - } - } - return consumed; -} - -void bad_bt_scene_config_mac_on_exit(void* context) { - BadBtApp* bad_bt = context; - - furi_hal_bt_reverse_mac_addr(bad_bt->config.bt_mac); - - bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac); - - // Clear view - byte_input_set_result_callback(bad_bt->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(bad_bt->byte_input, ""); -} diff --git a/base_pack/bad_bt/scenes/bad_bt_scene_config_name.c b/base_pack/bad_bt/scenes/bad_bt_scene_config_name.c deleted file mode 100644 index 61d198b8cec..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene_config_name.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../bad_bt_app.h" - -static void bad_bt_scene_config_name_text_input_callback(void* context) { - BadBtApp* bad_bt = context; - - view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventTextEditResult); -} - -void bad_bt_scene_config_name_on_enter(void* context) { - BadBtApp* bad_bt = context; - TextInput* text_input = bad_bt->text_input; - - text_input_set_header_text(text_input, "Set BT device name"); - - text_input_set_result_callback( - text_input, - bad_bt_scene_config_name_text_input_callback, - bad_bt, - bad_bt->config.bt_name, - BAD_BT_ADV_NAME_MAX_LEN, - true); - - view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigName); -} - -bool bad_bt_scene_config_name_on_event(void* context, SceneManagerEvent event) { - BadBtApp* bad_bt = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == BadBtAppCustomEventTextEditResult) { - bt_set_profile_adv_name(bad_bt->bt, bad_bt->config.bt_name); - } - scene_manager_previous_scene(bad_bt->scene_manager); - } - return consumed; -} - -void bad_bt_scene_config_name_on_exit(void* context) { - BadBtApp* bad_bt = context; - TextInput* text_input = bad_bt->text_input; - - text_input_reset(text_input); -} diff --git a/base_pack/bad_bt/scenes/bad_bt_scene_error.c b/base_pack/bad_bt/scenes/bad_bt_scene_error.c deleted file mode 100644 index e25703e7db4..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene_error.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../bad_bt_app.h" - -static void - bad_bt_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - BadBtApp* app = context; - - if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(app->view_dispatcher, BadBtCustomEventErrorBack); - } -} - -void bad_bt_scene_error_on_enter(void* context) { - BadBtApp* app = context; - - if(app->error == BadBtAppErrorNoFiles) { - widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); - widget_add_string_multiline_element( - app->widget, - 81, - 4, - AlignCenter, - AlignTop, - FontSecondary, - "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", bad_bt_scene_error_event_callback, app); - } else if(app->error == BadBtAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } - - view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewError); -} - -bool bad_bt_scene_error_on_event(void* context, SceneManagerEvent event) { - BadBtApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadBtCustomEventErrorBack) { - view_dispatcher_stop(app->view_dispatcher); - consumed = true; - } - } - return consumed; -} - -void bad_bt_scene_error_on_exit(void* context) { - BadBtApp* app = context; - widget_reset(app->widget); -} diff --git a/base_pack/bad_bt/scenes/bad_bt_scene_file_select.c b/base_pack/bad_bt/scenes/bad_bt_scene_file_select.c deleted file mode 100644 index b86dc6d713e..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene_file_select.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "../bad_bt_app.h" -#include -#include - -static bool bad_bt_file_select(BadBtApp* bad_bt) { - furi_assert(bad_bt); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_BT_APP_SCRIPT_EXTENSION, &I_badbt_10px); - browser_options.base_path = BAD_BT_APP_BASE_FOLDER; - browser_options.skip_assets = true; - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_bt->dialogs, bad_bt->file_path, bad_bt->file_path, &browser_options); - - return res; -} - -void bad_bt_scene_file_select_on_enter(void* context) { - BadBtApp* bad_bt = context; - - if(bad_bt->bad_bt_script) { - bad_bt_script_close(bad_bt->bad_bt_script); - bad_bt->bad_bt_script = NULL; - } - - if(bad_bt_file_select(bad_bt)) { - bad_bt->bad_bt_script = bad_bt_script_open(bad_bt->file_path, bad_bt->bt, bad_bt); - bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout); - - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneWork); - } else { - view_dispatcher_stop(bad_bt->view_dispatcher); - } -} - -bool bad_bt_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - // BadBtApp* bad_bt = context; - return false; -} - -void bad_bt_scene_file_select_on_exit(void* context) { - UNUSED(context); - // BadBtApp* bad_bt = context; -} diff --git a/base_pack/bad_bt/scenes/bad_bt_scene_work.c b/base_pack/bad_bt/scenes/bad_bt_scene_work.c deleted file mode 100644 index 684bb8b74dd..00000000000 --- a/base_pack/bad_bt/scenes/bad_bt_scene_work.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "../helpers/ducky_script.h" -#include "../bad_bt_app.h" -#include "../views/bad_bt_view.h" -#include -#include "toolbox/path.h" - -void bad_bt_scene_work_button_callback(InputKey key, void* context) { - furi_assert(context); - BadBtApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, key); -} - -bool bad_bt_scene_work_on_event(void* context, SceneManagerEvent event) { - BadBtApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InputKeyLeft) { - if(bad_bt_is_idle_state(app->bad_bt_view)) { - scene_manager_next_scene(app->scene_manager, BadBtSceneConfig); - } - consumed = true; - } else if(event.event == InputKeyOk) { - bad_bt_script_toggle(app->bad_bt_script); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script)); - } - return consumed; -} - -void bad_bt_scene_work_on_enter(void* context) { - BadBtApp* app = context; - - FuriString* file_name; - file_name = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); - bad_bt_set_file_name(app->bad_bt_view, furi_string_get_cstr(file_name)); - furi_string_free(file_name); - - FuriString* layout; - layout = furi_string_alloc(); - path_extract_filename(app->keyboard_layout, layout, true); - bad_bt_set_layout(app->bad_bt_view, furi_string_get_cstr(layout)); - furi_string_free(layout); - - bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script)); - - bad_bt_set_button_callback(app->bad_bt_view, bad_bt_scene_work_button_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewWork); -} - -void bad_bt_scene_work_on_exit(void* context) { - UNUSED(context); -} diff --git a/base_pack/bad_bt/views/bad_bt_view.c b/base_pack/bad_bt/views/bad_bt_view.c deleted file mode 100644 index 4a9bf589c9b..00000000000 --- a/base_pack/bad_bt/views/bad_bt_view.c +++ /dev/null @@ -1,233 +0,0 @@ -#include "bad_bt_view.h" -#include "../helpers/ducky_script.h" -#include "../bad_bt_app.h" -#include -#include -#include - -#define MAX_NAME_LEN 64 - -typedef struct { - char file_name[MAX_NAME_LEN]; - char layout[MAX_NAME_LEN]; - BadBtState state; - uint8_t anim_frame; -} BadBtModel; - -static void bad_bt_draw_callback(Canvas* canvas, void* _model) { - BadBtModel* model = _model; - - FuriString* disp_str; - disp_str = furi_string_alloc_set("(BT) "); - furi_string_cat_str(disp_str, model->file_name); - elements_string_fit_width(canvas, disp_str, 128 - 2); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); - - if(strlen(model->layout) == 0) { - furi_string_set(disp_str, "(default)"); - } else { - furi_string_reset(disp_str); - furi_string_push_back(disp_str, '('); - for(size_t i = 0; i < strlen(model->layout); i++) - furi_string_push_back(disp_str, model->layout[i]); - furi_string_push_back(disp_str, ')'); - } - if(model->state.pin) { - furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin); - } - elements_string_fit_width(canvas, disp_str, 128 - 2); - canvas_draw_str( - canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); - - furi_string_reset(disp_str); - - if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) || - (model->state.state == BadBtStateNotConnected)) { - elements_button_center(canvas, "Run"); - elements_button_left(canvas, "Config"); - } else if((model->state.state == BadBtStateRunning) || (model->state.state == BadBtStateDelay)) { - elements_button_center(canvas, "Stop"); - } else if(model->state.state == BadBtStateWaitForBtn) { - elements_button_center(canvas, "Press to continue"); - } else if(model->state.state == BadBtStateWillRun) { - elements_button_center(canvas, "Cancel"); - } - - if(model->state.state == BadBtStateNotConnected) { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device"); - } else if(model->state.state == BadBtStateWillRun) { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); - } else if(model->state.state == BadBtStateFileError) { - canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); - } else if(model->state.state == BadBtStateScriptError) { - canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "line %u", model->state.error_line); - canvas_draw_str_aligned( - canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - furi_string_set_str(disp_str, model->state.error); - elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); - canvas_draw_str_aligned( - canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - } else if(model->state.state == BadBtStateIdle) { - canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBtStateRunning) { - if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); - } else { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); - } - canvas_set_font(canvas, FontBigNumbers); - furi_string_printf( - disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); - canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBtStateDone) { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBtStateDelay) { - if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); - } else { - canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); - } - canvas_set_font(canvas, FontBigNumbers); - furi_string_printf( - disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); - canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); - canvas_draw_str_aligned( - canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - } else { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - } - - furi_string_free(disp_str); -} - -static bool bad_bt_input_callback(InputEvent* event, void* context) { - furi_assert(context); - BadBt* bad_bt = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { - consumed = true; - furi_assert(bad_bt->callback); - bad_bt->callback(event->key, bad_bt->context); - } - } - - return consumed; -} - -BadBt* bad_bt_alloc() { - BadBt* bad_bt = malloc(sizeof(BadBt)); - - bad_bt->view = view_alloc(); - view_allocate_model(bad_bt->view, ViewModelTypeLocking, sizeof(BadBtModel)); - view_set_context(bad_bt->view, bad_bt); - view_set_draw_callback(bad_bt->view, bad_bt_draw_callback); - view_set_input_callback(bad_bt->view, bad_bt_input_callback); - - return bad_bt; -} - -void bad_bt_free(BadBt* bad_bt) { - furi_assert(bad_bt); - view_free(bad_bt->view); - free(bad_bt); -} - -View* bad_bt_get_view(BadBt* bad_bt) { - furi_assert(bad_bt); - return bad_bt->view; -} - -void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context) { - furi_assert(bad_bt); - furi_assert(callback); - with_view_model( - bad_bt->view, - BadBtModel * model, - { - UNUSED(model); - bad_bt->callback = callback; - bad_bt->context = context; - }, - true); -} - -void bad_bt_set_file_name(BadBt* bad_bt, const char* name) { - furi_assert(name); - with_view_model( - bad_bt->view, BadBtModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true); -} - -void bad_bt_set_layout(BadBt* bad_bt, const char* layout) { - furi_assert(layout); - with_view_model( - bad_bt->view, BadBtModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true); -} - -void bad_bt_set_state(BadBt* bad_bt, BadBtState* st) { - furi_assert(st); - uint32_t pin = 0; - if(bad_bt->context != NULL) { - BadBtApp* app = bad_bt->context; - if(app->bt != NULL) { - pin = app->bt->pin; - } - } - st->pin = pin; - with_view_model( - bad_bt->view, - BadBtModel * model, - { - memcpy(&(model->state), st, sizeof(BadBtState)); - model->anim_frame ^= 1; - }, - true); -} - -bool bad_bt_is_idle_state(BadBt* bad_bt) { - bool is_idle = false; - with_view_model( - bad_bt->view, - BadBtModel * model, - { - if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) || - (model->state.state == BadBtStateNotConnected)) { - is_idle = true; - } - }, - false); - return is_idle; -} diff --git a/base_pack/bad_bt/views/bad_bt_view.h b/base_pack/bad_bt/views/bad_bt_view.h deleted file mode 100644 index 850a7105777..00000000000 --- a/base_pack/bad_bt/views/bad_bt_view.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -typedef void (*BadBtButtonCallback)(InputKey key, void* context); - -typedef struct { - View* view; - BadBtButtonCallback callback; - void* context; -} BadBt; - -typedef struct BadBtState BadBtState; - -BadBt* bad_bt_alloc(); - -void bad_bt_free(BadBt* bad_bt); - -View* bad_bt_get_view(BadBt* bad_bt); - -void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context); - -void bad_bt_set_file_name(BadBt* bad_bt, const char* name); - -void bad_bt_set_layout(BadBt* bad_bt, const char* layout); - -void bad_bt_set_state(BadBt* bad_bt, BadBtState* st); - -bool bad_bt_is_idle_state(BadBt* bad_bt); diff --git a/base_pack/bad_kb/application.fam b/base_pack/bad_kb/application.fam new file mode 100644 index 00000000000..2b573532166 --- /dev/null +++ b/base_pack/bad_kb/application.fam @@ -0,0 +1,11 @@ +App( + appid="bad_kb", + name="Bad KB", + apptype=FlipperAppType.EXTERNAL, + entry_point="bad_kb_app", + stack_size=2 * 1024, + order=70, + fap_icon="icon.png", + fap_category="Tools", + fap_libs=["assets"], +) diff --git a/base_pack/bad_kb/bad_kb_app.c b/base_pack/bad_kb/bad_kb_app.c new file mode 100644 index 00000000000..3b57600c4a9 --- /dev/null +++ b/base_pack/bad_kb/bad_kb_app.c @@ -0,0 +1,440 @@ +#include "bad_kb_app_i.h" +#include +#include +#include +#include +#include +#include +#include "helpers/ducky_script_i.h" + +// Adjusts to serial MAC +2 in app init +uint8_t BAD_KB_BOUND_MAC[GAP_MAC_ADDR_SIZE] = {0}; + +static bool bad_kb_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + BadKbApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool bad_kb_app_back_event_callback(void* context) { + furi_assert(context); + BadKbApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void bad_kb_app_tick_event_callback(void* context) { + furi_assert(context); + BadKbApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static void bad_kb_load_settings(BadKbApp* app) { + furi_string_reset(app->keyboard_layout); + BadKbConfig* cfg = &app->config; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_existing(file, BAD_KB_SETTINGS_PATH)) { + FuriString* tmp_str = furi_string_alloc(); + if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) { + furi_string_reset(app->keyboard_layout); + } + if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) { + strlcpy(cfg->ble.name, furi_string_get_cstr(tmp_str), sizeof(cfg->ble.name)); + } else { + strcpy(cfg->ble.name, ""); + } + if(!flipper_format_read_hex( + file, "Bt_Mac", (uint8_t*)&cfg->ble.mac, sizeof(cfg->ble.mac))) { + memset(cfg->ble.mac, 0, sizeof(cfg->ble.mac)); + } + if(flipper_format_read_string(file, "Usb_Manuf", tmp_str) && !furi_string_empty(tmp_str)) { + strlcpy(cfg->usb.manuf, furi_string_get_cstr(tmp_str), sizeof(cfg->usb.manuf)); + } else { + strcpy(cfg->usb.manuf, ""); + } + if(flipper_format_read_string(file, "Usb_Product", tmp_str) && + !furi_string_empty(tmp_str)) { + strlcpy(cfg->usb.product, furi_string_get_cstr(tmp_str), sizeof(cfg->usb.product)); + } else { + strcpy(cfg->usb.product, ""); + } + if(!flipper_format_read_uint32(file, "Usb_Vid", &cfg->usb.vid, 1)) { + cfg->usb.vid = 0; + } + if(!flipper_format_read_uint32(file, "Usb_Pid", &cfg->usb.pid, 1)) { + cfg->usb.pid = 0; + } + if(!flipper_format_read_bool(file, "Bt_Remember", &cfg->bt_remember, 1)) { + cfg->bt_remember = false; + } + if(!flipper_format_read_bool(file, "Is_Ble", &cfg->is_ble, 1)) { + cfg->is_ble = false; + } + furi_string_free(tmp_str); + flipper_format_file_close(file); + } + flipper_format_free(file); + + if(!furi_string_empty(app->keyboard_layout)) { + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat( + storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); + if(file_check_err != FSE_OK) { + furi_string_reset(app->keyboard_layout); + return; + } + if(layout_file_info.size != 256) { + furi_string_reset(app->keyboard_layout); + } + } + + furi_record_close(RECORD_STORAGE); +} + +static void bad_kb_save_settings(BadKbApp* app) { + BadKbConfig* cfg = &app->config; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + if(flipper_format_file_open_always(file, BAD_KB_SETTINGS_PATH)) { + flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); + flipper_format_write_string_cstr(file, "Bt_Name", cfg->ble.name); + flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&cfg->ble.mac, sizeof(cfg->ble.mac)); + flipper_format_write_string_cstr(file, "Usb_Manuf", cfg->usb.manuf); + flipper_format_write_string_cstr(file, "Usb_Product", cfg->usb.product); + flipper_format_write_uint32(file, "Usb_Vid", &cfg->usb.vid, 1); + flipper_format_write_uint32(file, "Usb_Pid", &cfg->usb.pid, 1); + flipper_format_write_bool(file, "Bt_Remember", &cfg->bt_remember, 1); + flipper_format_write_bool(file, "Is_Ble", &cfg->is_ble, 1); + flipper_format_file_close(file); + } + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); +} + +void bad_kb_app_show_loading_popup(BadKbApp* app, bool show) { + if(show) { + // Raise timer priority so that animations can play + furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated); + view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewLoading); + } else { + // Restore default timer priority + furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal); + } +} + +int32_t bad_kb_conn_apply(BadKbApp* app) { + if(app->is_bt) { + bt_timeout = bt_hid_delays[LevelRssi39_0]; + bt_disconnect(app->bt); + furi_delay_ms(200); + bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); + + // Setup new config + BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; + memcpy(&app->cur_ble_cfg, &cfg->ble, sizeof(cfg->ble)); + app->cur_ble_cfg.bonding = app->bt_remember; + if(app->bt_remember) { + app->cur_ble_cfg.pairing = GapPairingPinCodeVerifyYesNo; + } else { + app->cur_ble_cfg.pairing = GapPairingNone; + memcpy(app->cur_ble_cfg.mac, BAD_KB_BOUND_MAC, sizeof(BAD_KB_BOUND_MAC)); + } + + // Set profile + app->ble_hid = bt_profile_start(app->bt, ble_profile_hid, &app->cur_ble_cfg); + furi_check(app->ble_hid); + + // Advertise even if BT is off in settings + furi_hal_bt_start_advertising(); + + app->conn_mode = BadKbConnModeBt; + + } else { + // Unlock RPC connections + furi_hal_usb_unlock(); + + // Context will apply with set_config only if pointer address is different, so we use a copy + FuriHalUsbHidConfig* cur_usb_cfg = malloc(sizeof(FuriHalUsbHidConfig)); + + // Setup new config + BadKbConfig* cfg = app->set_usb_id ? &app->id_config : &app->config; + memcpy(cur_usb_cfg, &cfg->usb, sizeof(cfg->usb)); + + // Set profile + furi_check(furi_hal_usb_set_config(&usb_hid, cur_usb_cfg)); + if(app->cur_usb_cfg) free(app->cur_usb_cfg); + app->cur_usb_cfg = cur_usb_cfg; + + app->conn_mode = BadKbConnModeUsb; + } + + return 0; +} + +void bad_kb_conn_reset(BadKbApp* app) { + if(app->conn_mode == BadKbConnModeBt) { + bt_disconnect(app->bt); + furi_delay_ms(200); + bt_keys_storage_set_default_path(app->bt); + furi_check(bt_profile_restore_default(app->bt)); + } else if(app->conn_mode == BadKbConnModeUsb) { + // TODO: maybe also restore USB context? + furi_check(furi_hal_usb_set_config(app->prev_usb_mode, NULL)); + } + + app->conn_mode = BadKbConnModeNone; +} + +void bad_kb_config_adjust(BadKbConfig* cfg) { + // Avoid empty name + if(cfg->ble.name[0] == '\0') { + snprintf( + cfg->ble.name, sizeof(cfg->ble.name), "Control %s", furi_hal_version_get_name_ptr()); + } + + const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); + uint8_t empty_mac[sizeof(cfg->ble.mac)] = {0}; + uint8_t default_mac[sizeof(cfg->ble.mac)] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72}; //furi_hal_bt + if(memcmp(cfg->ble.mac, empty_mac, sizeof(cfg->ble.mac)) == 0 || + memcmp(cfg->ble.mac, normal_mac, sizeof(cfg->ble.mac)) == 0 || + memcmp(cfg->ble.mac, default_mac, sizeof(cfg->ble.mac)) == 0) { + memcpy(cfg->ble.mac, normal_mac, sizeof(cfg->ble.mac)); + cfg->ble.mac[2]++; + } + + // Use defaults if vid or pid are unset + if(cfg->usb.vid == 0) cfg->usb.vid = 0x046D; + if(cfg->usb.pid == 0) cfg->usb.pid = 0xC529; +} + +void bad_kb_config_refresh(BadKbApp* app) { + bt_set_status_changed_callback(app->bt, NULL, NULL); + furi_hal_hid_set_state_callback(NULL, NULL); + if(app->bad_kb_script) { + furi_thread_flags_set(furi_thread_get_id(app->bad_kb_script->thread), WorkerEvtDisconnect); + } + if(app->conn_init_thread) { + furi_thread_join(app->conn_init_thread); + } + + bool apply = false; + if(app->is_bt) { + BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; + bad_kb_config_adjust(cfg); + + if(app->conn_mode != BadKbConnModeBt) { + apply = true; + bad_kb_conn_reset(app); + } else { + BleProfileHidParams* cur = &app->cur_ble_cfg; + apply = apply || cfg->ble.bonding != app->bt_remember; + apply = apply || strncmp(cfg->ble.name, cur->name, sizeof(cfg->ble.name)); + apply = apply || memcmp(cfg->ble.mac, cur->mac, sizeof(cfg->ble.mac)); + } + } else { + BadKbConfig* cfg = app->set_usb_id ? &app->id_config : &app->config; + bad_kb_config_adjust(cfg); + + if(app->conn_mode != BadKbConnModeUsb) { + apply = true; + bad_kb_conn_reset(app); + } else { + FuriHalUsbHidConfig* cur = app->cur_usb_cfg; + apply = apply || cfg->usb.vid != cur->vid; + apply = apply || cfg->usb.pid != cur->pid; + apply = apply || strncmp(cfg->usb.manuf, cur->manuf, sizeof(cur->manuf)); + apply = apply || strncmp(cfg->usb.product, cur->product, sizeof(cur->product)); + } + } + + if(apply) { + bad_kb_conn_apply(app); + } + + if(app->bad_kb_script) { + BadKbScript* script = app->bad_kb_script; + script->st.is_bt = app->is_bt; + script->bt = app->is_bt ? app->bt : NULL; + bool connected; + if(app->is_bt) { + bt_set_status_changed_callback(app->bt, bad_kb_bt_hid_state_callback, script); + connected = furi_hal_bt_is_connected(); + } else { + furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, script); + connected = furi_hal_hid_is_connected(); + } + if(connected) { + furi_thread_flags_set(furi_thread_get_id(script->thread), WorkerEvtConnect); + } + } + + // Reload config page + scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); + scene_manager_previous_scene(app->scene_manager); + + // Update settings + if((app->config.bt_remember != app->bt_remember) || (app->config.is_ble != app->is_bt)) { + app->config.bt_remember = app->bt_remember; + app->config.is_ble = app->is_bt; + bad_kb_save_settings(app); + } +} + +BadKbApp* bad_kb_app_alloc(char* arg) { + BadKbApp* app = malloc(sizeof(BadKbApp)); + + app->bad_kb_script = NULL; + + app->file_path = furi_string_alloc(); + app->keyboard_layout = furi_string_alloc(); + if(arg && strlen(arg)) { + furi_string_set(app->file_path, arg); + } + + bad_kb_load_settings(app); + + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->dialogs = furi_record_open(RECORD_DIALOGS); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&bad_kb_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, bad_kb_app_tick_event_callback, 250); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, bad_kb_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, bad_kb_app_back_event_callback); + + Bt* bt = furi_record_open(RECORD_BT); + app->bt = bt; + app->bt->suppress_pin_screen = true; + app->is_bt = app->config.is_ble; + app->bt_remember = app->config.bt_remember; + bad_kb_config_adjust(&app->config); + + // Save prev config + app->prev_usb_mode = furi_hal_usb_get_config(); + + // Adjust BT remember MAC to be serial MAC +2 + memcpy(BAD_KB_BOUND_MAC, furi_hal_version_get_ble_mac(), sizeof(BAD_KB_BOUND_MAC)); + BAD_KB_BOUND_MAC[2] += 2; + + // Custom Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadKbAppViewWidget, widget_get_view(app->widget)); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + BadKbAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + app->bad_kb_view = bad_kb_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadKbAppViewWork, bad_kb_get_view(app->bad_kb_view)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadKbAppViewTextInput, text_input_get_view(app->text_input)); + + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadKbAppViewByteInput, byte_input_get_view(app->byte_input)); + + app->loading = loading_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadKbAppViewLoading, loading_get_view(app->loading)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->conn_mode = BadKbConnModeNone; + app->conn_init_thread = + furi_thread_alloc_ex("BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_conn_apply, app); + furi_thread_start(app->conn_init_thread); + if(!furi_string_empty(app->file_path)) { + app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); + bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); + scene_manager_next_scene(app->scene_manager, BadKbSceneWork); + } else { + furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); + } + + return app; +} + +void bad_kb_app_free(BadKbApp* app) { + furi_assert(app); + + if(app->bad_kb_script) { + bad_kb_script_close(app->bad_kb_script); + app->bad_kb_script = NULL; + } + + // Views + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWork); + bad_kb_free(app->bad_kb_view); + + // Custom Widget + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWidget); + widget_free(app->widget); + + // Variable item list + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewVarItemList); + variable_item_list_free(app->var_item_list); + + // Text Input + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewTextInput); + text_input_free(app->text_input); + + // Byte Input + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewByteInput); + byte_input_free(app->byte_input); + + // Loading + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewLoading); + loading_free(app->loading); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Restore connection config + app->bt->suppress_pin_screen = false; + if(app->conn_init_thread) { + furi_thread_join(app->conn_init_thread); + furi_thread_free(app->conn_init_thread); + app->conn_init_thread = NULL; + } + bad_kb_conn_reset(app); + if(app->cur_usb_cfg) free(app->cur_usb_cfg); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_BT); + + bad_kb_save_settings(app); + + furi_string_free(app->file_path); + furi_string_free(app->keyboard_layout); + + free(app); +} + +int32_t bad_kb_app(void* p) { + BadKbApp* bad_kb_app = bad_kb_app_alloc((char*)p); + + view_dispatcher_run(bad_kb_app->view_dispatcher); + + bad_kb_app_free(bad_kb_app); + return 0; +} diff --git a/base_pack/bad_kb/bad_kb_app.h b/base_pack/bad_kb/bad_kb_app.h new file mode 100644 index 00000000000..af64be25391 --- /dev/null +++ b/base_pack/bad_kb/bad_kb_app.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BadKbApp BadKbApp; + +#ifdef __cplusplus +} +#endif diff --git a/base_pack/bad_kb/bad_kb_app_i.h b/base_pack/bad_kb/bad_kb_app_i.h new file mode 100644 index 00000000000..b9501918dbc --- /dev/null +++ b/base_pack/bad_kb/bad_kb_app_i.h @@ -0,0 +1,113 @@ +#pragma once + +#include "bad_kb_app.h" +#include "scenes/bad_kb_scene.h" +#include "helpers/ducky_script.h" +#include "helpers/ble_hid.h" +#include "bad_kb_paths.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/bad_kb_view.h" +#include + +#define BAD_KB_APP_SCRIPT_EXTENSION ".txt" +#define BAD_KB_APP_LAYOUT_EXTENSION ".kl" + +extern uint8_t BAD_KB_BOUND_MAC[GAP_MAC_ADDR_SIZE]; // For remember mode + +typedef enum BadKbCustomEvent { + BadKbAppCustomEventTextInputDone, + BadKbAppCustomEventByteInputDone, + BadKbCustomEventErrorBack +} BadKbCustomEvent; + +typedef enum { + BadKbAppErrorNoFiles, +} BadKbAppError; + +typedef struct { + BleProfileHidParams ble; + FuriHalUsbHidConfig usb; + bool bt_remember; + bool is_ble; +} BadKbConfig; + +typedef enum { + BadKbConnModeNone, + BadKbConnModeUsb, + BadKbConnModeBt, +} BadKbConnMode; + +struct BadKbApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + VariableItemList* var_item_list; + TextInput* text_input; + ByteInput* byte_input; + Loading* loading; + + char bt_name_buf[FURI_HAL_BT_ADV_NAME_LENGTH]; + uint8_t bt_mac_buf[GAP_MAC_ADDR_SIZE]; + char usb_name_buf[32]; + uint16_t usb_vidpid_buf[2]; + + BadKbAppError error; + FuriString* file_path; + FuriString* keyboard_layout; + BadKb* bad_kb_view; + BadKbScript* bad_kb_script; + + Bt* bt; + bool is_bt; + bool bt_remember; + BadKbConfig config; // User options + BadKbConfig id_config; // ID and BT_ID values + + bool set_bt_id; + bool set_usb_id; + bool has_bt_id; + bool has_usb_id; + + FuriHalBleProfileBase* ble_hid; + FuriHalUsbInterface* prev_usb_mode; + + BleProfileHidParams cur_ble_cfg; + FuriHalUsbHidConfig* cur_usb_cfg; + + BadKbConnMode conn_mode; + FuriThread* conn_init_thread; +}; + +typedef enum { + BadKbAppViewWidget, + BadKbAppViewWork, + BadKbAppViewVarItemList, + BadKbAppViewByteInput, + BadKbAppViewTextInput, + BadKbAppViewLoading, +} BadKbAppView; + +void bad_kb_app_show_loading_popup(BadKbApp* app, bool show); + +int32_t bad_kb_conn_apply(BadKbApp* app); + +void bad_kb_conn_reset(BadKbApp* app); + +void bad_kb_config_refresh(BadKbApp* app); + +void bad_kb_config_adjust(BadKbConfig* cfg); diff --git a/base_pack/bad_kb/bad_kb_paths.h b/base_pack/bad_kb/bad_kb_paths.h new file mode 100644 index 00000000000..84df34789ce --- /dev/null +++ b/base_pack/bad_kb/bad_kb_paths.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#define BAD_KB_APP_BASE_FOLDER EXT_PATH("badkb") +#define BAD_USB_APP_BASE_FOLDER EXT_PATH("badusb") +#define BAD_KB_KEYS_PATH BAD_KB_APP_BASE_FOLDER "/.bad_kb.keys" +#define BAD_KB_SETTINGS_PATH BAD_KB_APP_BASE_FOLDER "/.bad_kb.settings" +#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts" diff --git a/base_pack/bad_kb/helpers/ble_hid.c b/base_pack/bad_kb/helpers/ble_hid.c new file mode 100644 index 00000000000..2d9cb8a38c5 --- /dev/null +++ b/base_pack/bad_kb/helpers/ble_hid.c @@ -0,0 +1,416 @@ +#include "ble_hid.h" + +#include +#include +#include +#include "ble_hid_svc.h" + +#include +#include +#include + +#define HID_INFO_BASE_USB_SPECIFICATION (0x0101) +#define HID_INFO_COUNTRY_CODE (0x00) +#define BLE_PROFILE_HID_INFO_FLAG_REMOTE_WAKE_MSK (0x01) +#define BLE_PROFILE_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK (0x02) + +#define BLE_PROFILE_HID_KB_MAX_KEYS (6) +#define BLE_PROFILE_CONSUMER_MAX_KEYS (1) + +// Report ids cant be 0 +enum HidReportId { + ReportIdKeyboard = 1, + ReportIdMouse = 2, + ReportIdConsumer = 3, +}; +// Report numbers corresponded to the report id with an offset of 1 +enum HidInputNumber { + ReportNumberKeyboard = 0, + ReportNumberMouse = 1, + ReportNumberConsumer = 2, +}; + +typedef struct { + uint8_t mods; + uint8_t reserved; + uint8_t key[BLE_PROFILE_HID_KB_MAX_KEYS]; +} FURI_PACKED FuriHalBtHidKbReport; + +typedef struct { + uint8_t btn; + int8_t x; + int8_t y; + int8_t wheel; +} FURI_PACKED FuriHalBtHidMouseReport; + +typedef struct { + uint16_t key[BLE_PROFILE_CONSUMER_MAX_KEYS]; +} FURI_PACKED FuriHalBtHidConsumerReport; + +// keyboard+mouse+consumer hid report +static const uint8_t ble_profile_hid_report_map_data[] = { + // Keyboard Report + HID_USAGE_PAGE(HID_PAGE_DESKTOP), + HID_USAGE(HID_DESKTOP_KEYBOARD), + HID_COLLECTION(HID_APPLICATION_COLLECTION), + HID_REPORT_ID(ReportIdKeyboard), + HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), + HID_USAGE_MINIMUM(HID_KEYBOARD_L_CTRL), + HID_USAGE_MAXIMUM(HID_KEYBOARD_R_GUI), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(1), + HID_REPORT_SIZE(1), + HID_REPORT_COUNT(8), + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_REPORT_COUNT(1), + HID_REPORT_SIZE(8), + HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_USAGE_PAGE(HID_PAGE_LED), + HID_REPORT_COUNT(8), + HID_REPORT_SIZE(1), + HID_USAGE_MINIMUM(1), + HID_USAGE_MAXIMUM(8), + HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_REPORT_COUNT(BLE_PROFILE_HID_KB_MAX_KEYS), + HID_REPORT_SIZE(8), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(101), + HID_USAGE_PAGE(HID_DESKTOP_KEYPAD), + HID_USAGE_MINIMUM(0), + HID_USAGE_MAXIMUM(101), + HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_END_COLLECTION, + // Mouse Report + HID_USAGE_PAGE(HID_PAGE_DESKTOP), + HID_USAGE(HID_DESKTOP_MOUSE), + HID_COLLECTION(HID_APPLICATION_COLLECTION), + HID_USAGE(HID_DESKTOP_POINTER), + HID_COLLECTION(HID_PHYSICAL_COLLECTION), + HID_REPORT_ID(ReportIdMouse), + HID_USAGE_PAGE(HID_PAGE_BUTTON), + HID_USAGE_MINIMUM(1), + HID_USAGE_MAXIMUM(3), + HID_LOGICAL_MINIMUM(0), + HID_LOGICAL_MAXIMUM(1), + HID_REPORT_COUNT(3), + HID_REPORT_SIZE(1), + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_REPORT_SIZE(1), + HID_REPORT_COUNT(5), + HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_USAGE_PAGE(HID_PAGE_DESKTOP), + HID_USAGE(HID_DESKTOP_X), + HID_USAGE(HID_DESKTOP_Y), + HID_USAGE(HID_DESKTOP_WHEEL), + HID_LOGICAL_MINIMUM(-127), + HID_LOGICAL_MAXIMUM(127), + HID_REPORT_SIZE(8), + HID_REPORT_COUNT(3), + HID_INPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), + HID_END_COLLECTION, + HID_END_COLLECTION, + // Consumer Report + HID_USAGE_PAGE(HID_PAGE_CONSUMER), + HID_USAGE(HID_CONSUMER_CONTROL), + HID_COLLECTION(HID_APPLICATION_COLLECTION), + HID_REPORT_ID(ReportIdConsumer), + HID_LOGICAL_MINIMUM(0), + HID_RI_LOGICAL_MAXIMUM(16, 0x3FF), + HID_USAGE_MINIMUM(0), + HID_RI_USAGE_MAXIMUM(16, 0x3FF), + HID_REPORT_COUNT(BLE_PROFILE_CONSUMER_MAX_KEYS), + HID_REPORT_SIZE(16), + HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_END_COLLECTION, +}; + +typedef struct { + FuriHalBleProfileBase base; + + FuriHalBtHidKbReport* kb_report; + FuriHalBtHidMouseReport* mouse_report; + FuriHalBtHidConsumerReport* consumer_report; + + BleServiceBattery* battery_svc; + BleServiceDevInfo* dev_info_svc; + BleServiceHid* hid_svc; +} BleProfileHid; +_Static_assert(offsetof(BleProfileHid, base) == 0, "Wrong layout"); + +static FuriHalBleProfileBase* ble_profile_hid_start(FuriHalBleProfileParams profile_params) { + UNUSED(profile_params); + + BleProfileHid* profile = malloc(sizeof(BleProfileHid)); + + profile->base.config = ble_profile_hid; + + profile->battery_svc = ble_svc_battery_start(true); + profile->dev_info_svc = ble_svc_dev_info_start(); + profile->hid_svc = ble_svc_hid_start(); + + // Configure HID Keyboard + profile->kb_report = malloc(sizeof(FuriHalBtHidKbReport)); + profile->mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); + profile->consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); + + // Configure Report Map characteristic + ble_svc_hid_update_report_map( + profile->hid_svc, + ble_profile_hid_report_map_data, + sizeof(ble_profile_hid_report_map_data)); + // Configure HID Information characteristic + uint8_t hid_info_val[4] = { + HID_INFO_BASE_USB_SPECIFICATION & 0x00ff, + (HID_INFO_BASE_USB_SPECIFICATION & 0xff00) >> 8, + HID_INFO_COUNTRY_CODE, + BLE_PROFILE_HID_INFO_FLAG_REMOTE_WAKE_MSK | + BLE_PROFILE_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, + }; + ble_svc_hid_update_info(profile->hid_svc, hid_info_val); + + return &profile->base; +} + +static void ble_profile_hid_stop(FuriHalBleProfileBase* profile) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + ble_svc_battery_stop(hid_profile->battery_svc); + ble_svc_dev_info_stop(hid_profile->dev_info_svc); + ble_svc_hid_stop(hid_profile->hid_svc); + + free(hid_profile->kb_report); + free(hid_profile->mouse_report); + free(hid_profile->consumer_report); +} + +bool ble_profile_hid_kb_press(FuriHalBleProfileBase* profile, uint16_t button) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidKbReport* kb_report = hid_profile->kb_report; + for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) { + if(kb_report->key[i] == 0) { + kb_report->key[i] = button & 0xFF; + break; + } + } + kb_report->mods |= (button >> 8); + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberKeyboard, + (uint8_t*)kb_report, + sizeof(FuriHalBtHidKbReport)); +} + +bool ble_profile_hid_kb_release(FuriHalBleProfileBase* profile, uint16_t button) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + + FuriHalBtHidKbReport* kb_report = hid_profile->kb_report; + for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) { + if(kb_report->key[i] == (button & 0xFF)) { + kb_report->key[i] = 0; + break; + } + } + kb_report->mods &= ~(button >> 8); + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberKeyboard, + (uint8_t*)kb_report, + sizeof(FuriHalBtHidKbReport)); +} + +bool ble_profile_hid_kb_release_all(FuriHalBleProfileBase* profile) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidKbReport* kb_report = hid_profile->kb_report; + for(uint8_t i = 0; i < BLE_PROFILE_HID_KB_MAX_KEYS; i++) { + kb_report->key[i] = 0; + } + kb_report->mods = 0; + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberKeyboard, + (uint8_t*)kb_report, + sizeof(FuriHalBtHidKbReport)); +} + +bool ble_profile_hid_consumer_key_press(FuriHalBleProfileBase* profile, uint16_t button) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report; + for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008 + if(consumer_report->key[i] == 0) { + consumer_report->key[i] = button; + break; + } + } + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberConsumer, + (uint8_t*)consumer_report, + sizeof(FuriHalBtHidConsumerReport)); +} + +bool ble_profile_hid_consumer_key_release(FuriHalBleProfileBase* profile, uint16_t button) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report; + for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008 + if(consumer_report->key[i] == button) { + consumer_report->key[i] = 0; + break; + } + } + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberConsumer, + (uint8_t*)consumer_report, + sizeof(FuriHalBtHidConsumerReport)); +} + +bool ble_profile_hid_consumer_key_release_all(FuriHalBleProfileBase* profile) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidConsumerReport* consumer_report = hid_profile->consumer_report; + for(uint8_t i = 0; i < BLE_PROFILE_CONSUMER_MAX_KEYS; i++) { //-V1008 + consumer_report->key[i] = 0; + } + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberConsumer, + (uint8_t*)consumer_report, + sizeof(FuriHalBtHidConsumerReport)); +} + +bool ble_profile_hid_mouse_move(FuriHalBleProfileBase* profile, int8_t dx, int8_t dy) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report; + mouse_report->x = dx; + mouse_report->y = dy; + bool state = ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberMouse, + (uint8_t*)mouse_report, + sizeof(FuriHalBtHidMouseReport)); + mouse_report->x = 0; + mouse_report->y = 0; + return state; +} + +bool ble_profile_hid_mouse_press(FuriHalBleProfileBase* profile, uint8_t button) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report; + mouse_report->btn |= button; + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberMouse, + (uint8_t*)mouse_report, + sizeof(FuriHalBtHidMouseReport)); +} + +bool ble_profile_hid_mouse_release(FuriHalBleProfileBase* profile, uint8_t button) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report; + mouse_report->btn &= ~button; + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberMouse, + (uint8_t*)mouse_report, + sizeof(FuriHalBtHidMouseReport)); +} + +bool ble_profile_hid_mouse_release_all(FuriHalBleProfileBase* profile) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report; + mouse_report->btn = 0; + return ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberMouse, + (uint8_t*)mouse_report, + sizeof(FuriHalBtHidMouseReport)); +} + +bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta) { + furi_check(profile); + furi_check(profile->config == ble_profile_hid); + + BleProfileHid* hid_profile = (BleProfileHid*)profile; + FuriHalBtHidMouseReport* mouse_report = hid_profile->mouse_report; + mouse_report->wheel = delta; + bool state = ble_svc_hid_update_input_report( + hid_profile->hid_svc, + ReportNumberMouse, + (uint8_t*)mouse_report, + sizeof(FuriHalBtHidMouseReport)); + mouse_report->wheel = 0; + return state; +} + +static GapConfig template_config = { + .adv_service_uuid = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, + .appearance_char = GAP_APPEARANCE_KEYBOARD, + .bonding_mode = true, + .pairing_method = GapPairingPinCodeVerifyYesNo, + .conn_param = + { + .conn_int_min = 0x18, // 30 ms + .conn_int_max = 0x24, // 45 ms + .slave_latency = 0, + .supervisor_timeout = 0, + }, +}; + +static void ble_profile_hid_get_config(GapConfig* config, FuriHalBleProfileParams profile_params) { + BleProfileHidParams* hid_profile_params = profile_params; + + furi_check(config); + memcpy(config, &template_config, sizeof(GapConfig)); + + // Set mac address + memcpy(config->mac_address, hid_profile_params->mac, sizeof(config->mac_address)); + + // Set advertise name + config->adv_name[0] = furi_hal_version_get_ble_local_device_name_ptr()[0]; + strlcpy(config->adv_name + 1, hid_profile_params->name, sizeof(config->adv_name) - 1); + + // Set bonding mode + config->bonding_mode = hid_profile_params->bonding; + + // Set pairing method + config->pairing_method = hid_profile_params->pairing; +} + +static const FuriHalBleProfileTemplate profile_callbacks = { + .start = ble_profile_hid_start, + .stop = ble_profile_hid_stop, + .get_gap_config = ble_profile_hid_get_config, +}; + +const FuriHalBleProfileTemplate* ble_profile_hid = &profile_callbacks; diff --git a/base_pack/bad_kb/helpers/ble_hid.h b/base_pack/bad_kb/helpers/ble_hid.h new file mode 100644 index 00000000000..717625354e2 --- /dev/null +++ b/base_pack/bad_kb/helpers/ble_hid.h @@ -0,0 +1,107 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Optional arguments to pass along with profile template as + * FuriHalBleProfileParams for tuning profile behavior + **/ +typedef struct { + char name[FURI_HAL_BT_ADV_NAME_LENGTH]; /**< Full device name */ + uint8_t mac[GAP_MAC_ADDR_SIZE]; /**< Full device address */ + bool bonding; /**< Save paired devices */ + GapPairing pairing; /**< Pairing security method */ +} BleProfileHidParams; + +/** Hid Keyboard Profile descriptor */ +extern const FuriHalBleProfileTemplate* ble_profile_hid; + +/** Press keyboard button + * + * @param profile profile instance + * @param button button code from HID specification + * + * @return true on success + */ +bool ble_profile_hid_kb_press(FuriHalBleProfileBase* profile, uint16_t button); + +/** Release keyboard button + * + * @param profile profile instance + * @param button button code from HID specification + * + * @return true on success + */ +bool ble_profile_hid_kb_release(FuriHalBleProfileBase* profile, uint16_t button); + +/** Release all keyboard buttons + * + * @param profile profile instance + * @return true on success + */ +bool ble_profile_hid_kb_release_all(FuriHalBleProfileBase* profile); + +/** Set the following consumer key to pressed state and send HID report + * + * @param profile profile instance + * @param button key code + */ +bool ble_profile_hid_consumer_key_press(FuriHalBleProfileBase* profile, uint16_t button); + +/** Set the following consumer key to released state and send HID report + * + * @param profile profile instance + * @param button key code + */ +bool ble_profile_hid_consumer_key_release(FuriHalBleProfileBase* profile, uint16_t button); + +/** Set consumer key to released state and send HID report + * + * @param profile profile instance + * @param button key code + */ +bool ble_profile_hid_consumer_key_release_all(FuriHalBleProfileBase* profile); + +/** Set mouse movement and send HID report + * + * @param profile profile instance + * @param dx x coordinate delta + * @param dy y coordinate delta + */ +bool ble_profile_hid_mouse_move(FuriHalBleProfileBase* profile, int8_t dx, int8_t dy); + +/** Set mouse button to pressed state and send HID report + * + * @param profile profile instance + * @param button key code + */ +bool ble_profile_hid_mouse_press(FuriHalBleProfileBase* profile, uint8_t button); + +/** Set mouse button to released state and send HID report + * + * @param profile profile instance + * @param button key code + */ +bool ble_profile_hid_mouse_release(FuriHalBleProfileBase* profile, uint8_t button); + +/** Set mouse button to released state and send HID report + * + * @param profile profile instance + * @param button key code + */ +bool ble_profile_hid_mouse_release_all(FuriHalBleProfileBase* profile); + +/** Set mouse wheel position and send HID report + * + * @param profile profile instance + * @param delta number of scroll steps + */ +bool ble_profile_hid_mouse_scroll(FuriHalBleProfileBase* profile, int8_t delta); + +#ifdef __cplusplus +} +#endif diff --git a/base_pack/bad_kb/helpers/ble_hid_svc.c b/base_pack/bad_kb/helpers/ble_hid_svc.c new file mode 100644 index 00000000000..71270e15574 --- /dev/null +++ b/base_pack/bad_kb/helpers/ble_hid_svc.c @@ -0,0 +1,320 @@ +#include "ble_hid_svc.h" +#include "app_common.h" +#include +#include +#include + +#include +#include + +#define TAG "BleHid" + +#define BLE_SVC_HID_REPORT_MAP_MAX_LEN (255) +#define BLE_SVC_HID_REPORT_MAX_LEN (255) +#define BLE_SVC_HID_REPORT_REF_LEN (2) +#define BLE_SVC_HID_INFO_LEN (4) +#define BLE_SVC_HID_CONTROL_POINT_LEN (1) + +#define BLE_SVC_HID_INPUT_REPORT_COUNT (3) +#define BLE_SVC_HID_OUTPUT_REPORT_COUNT (0) +#define BLE_SVC_HID_FEATURE_REPORT_COUNT (0) +#define BLE_SVC_HID_REPORT_COUNT \ + (BLE_SVC_HID_INPUT_REPORT_COUNT + BLE_SVC_HID_OUTPUT_REPORT_COUNT + \ + BLE_SVC_HID_FEATURE_REPORT_COUNT) + +typedef enum { + HidSvcGattCharacteristicProtocolMode = 0, + HidSvcGattCharacteristicReportMap, + HidSvcGattCharacteristicInfo, + HidSvcGattCharacteristicCtrlPoint, + HidSvcGattCharacteristicCount, +} HidSvcGattCharacteristicId; + +typedef struct { + uint8_t report_idx; + uint8_t report_type; +} HidSvcReportId; + +static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); + +static const Service_UUID_t ble_svc_hid_uuid = { + .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, +}; + +static bool ble_svc_hid_char_desc_data_callback( + const void* context, + const uint8_t** data, + uint16_t* data_len) { + const HidSvcReportId* report_id = context; + *data_len = sizeof(HidSvcReportId); + if(data) { + *data = (const uint8_t*)report_id; + } + return false; +} + +typedef struct { + const void* data_ptr; + uint16_t data_len; +} HidSvcDataWrapper; + +static bool ble_svc_hid_report_data_callback( + const void* context, + const uint8_t** data, + uint16_t* data_len) { + const HidSvcDataWrapper* report_data = context; + if(data) { + *data = report_data->data_ptr; + *data_len = report_data->data_len; + } else { + *data_len = BLE_SVC_HID_REPORT_MAP_MAX_LEN; + } + return false; +} + +static const BleGattCharacteristicParams ble_svc_hid_chars[HidSvcGattCharacteristicCount] = { + [HidSvcGattCharacteristicProtocolMode] = + {.name = "Protocol Mode", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicReportMap] = + {.name = "Report Map", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = ble_svc_hid_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [HidSvcGattCharacteristicInfo] = + {.name = "HID Information", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = BLE_SVC_HID_INFO_LEN, + .data.fixed.ptr = NULL, + .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicCtrlPoint] = + {.name = "HID Control Point", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = BLE_SVC_HID_CONTROL_POINT_LEN, + .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, +}; + +static const BleGattCharacteristicDescriptorParams ble_svc_hid_char_descr_template = { + .uuid_type = UUID_TYPE_16, + .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, + .max_length = BLE_SVC_HID_REPORT_REF_LEN, + .data_callback.fn = ble_svc_hid_char_desc_data_callback, + .security_permissions = ATTR_PERMISSION_NONE, + .access_permissions = ATTR_ACCESS_READ_WRITE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT, +}; + +static const BleGattCharacteristicParams ble_svc_hid_report_template = { + .name = "Report", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = ble_svc_hid_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE, +}; + +struct BleServiceHid { + uint16_t svc_handle; + BleGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; + BleGattCharacteristicInstance input_report_chars[BLE_SVC_HID_INPUT_REPORT_COUNT]; + BleGattCharacteristicInstance output_report_chars[BLE_SVC_HID_OUTPUT_REPORT_COUNT]; + BleGattCharacteristicInstance feature_report_chars[BLE_SVC_HID_FEATURE_REPORT_COUNT]; + GapSvcEventHandler* event_handler; +}; + +static BleEventAckStatus ble_svc_hid_event_handler(void* event, void* context) { + UNUSED(context); + + BleEventAckStatus ret = BleEventNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); + evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; + // aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + // Process modification events + ret = BleEventAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { + // Process notification confirmation + ret = BleEventAckFlowEnable; + } + } + return ret; +} + +BleServiceHid* ble_svc_hid_start() { + BleServiceHid* hid_svc = malloc(sizeof(BleServiceHid)); + + // Register event handler + hid_svc->event_handler = + ble_event_dispatcher_register_svc_handler(ble_svc_hid_event_handler, hid_svc); + /** + * Add Human Interface Device Service + */ + if(!ble_gatt_service_add( + UUID_TYPE_16, + &ble_svc_hid_uuid, + PRIMARY_SERVICE, + 2 + /* protocol mode */ + (4 * BLE_SVC_HID_INPUT_REPORT_COUNT) + (3 * BLE_SVC_HID_OUTPUT_REPORT_COUNT) + + (3 * BLE_SVC_HID_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + + 2, /* Service + Report Map + HID Information + HID Control Point */ + &hid_svc->svc_handle)) { + free(hid_svc); + return NULL; + } + + // Maintain previously defined characteristic order + ble_gatt_characteristic_init( + hid_svc->svc_handle, + &ble_svc_hid_chars[HidSvcGattCharacteristicProtocolMode], + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode]); + + uint8_t protocol_mode = 1; + ble_gatt_characteristic_update( + hid_svc->svc_handle, + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], + &protocol_mode); + + // reports + BleGattCharacteristicDescriptorParams ble_svc_hid_char_descr; + BleGattCharacteristicParams report_char; + HidSvcReportId report_id; + + memcpy( + &ble_svc_hid_char_descr, &ble_svc_hid_char_descr_template, sizeof(ble_svc_hid_char_descr)); + memcpy(&report_char, &ble_svc_hid_report_template, sizeof(report_char)); + + ble_svc_hid_char_descr.data_callback.context = &report_id; + report_char.descriptor_params = &ble_svc_hid_char_descr; + + typedef struct { + uint8_t report_type; + uint8_t report_count; + BleGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {0x01, BLE_SVC_HID_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {0x02, BLE_SVC_HID_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {0x03, BLE_SVC_HID_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + report_id.report_type = hid_report_chars[report_type_idx].report_type; + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + report_id.report_idx = report_idx + 1; + ble_gatt_characteristic_init( + hid_svc->svc_handle, + &report_char, + &hid_report_chars[report_type_idx].chars[report_idx]); + } + } + + // Setup remaining characteristics + for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) { + ble_gatt_characteristic_init( + hid_svc->svc_handle, &ble_svc_hid_chars[i], &hid_svc->chars[i]); + } + + return hid_svc; +} + +bool ble_svc_hid_update_report_map(BleServiceHid* hid_svc, const uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return ble_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); +} + +bool ble_svc_hid_update_input_report( + BleServiceHid* hid_svc, + uint8_t input_report_num, + uint8_t* data, + uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + furi_assert(input_report_num < BLE_SVC_HID_INPUT_REPORT_COUNT); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return ble_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); +} + +bool ble_svc_hid_update_info(BleServiceHid* hid_svc, uint8_t* data) { + furi_assert(data); + furi_assert(hid_svc); + + return ble_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); +} + +void ble_svc_hid_stop(BleServiceHid* hid_svc) { + furi_assert(hid_svc); + ble_event_dispatcher_unregister_svc_handler(hid_svc->event_handler); + // Delete characteristics + for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { + ble_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); + } + + typedef struct { + uint8_t report_count; + BleGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {BLE_SVC_HID_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {BLE_SVC_HID_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {BLE_SVC_HID_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + ble_gatt_characteristic_delete( + hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); + } + } + + // Delete service + ble_gatt_service_delete(hid_svc->svc_handle); + free(hid_svc); +} diff --git a/base_pack/bad_kb/helpers/ble_hid_svc.h b/base_pack/bad_kb/helpers/ble_hid_svc.h new file mode 100644 index 00000000000..8e9cc297507 --- /dev/null +++ b/base_pack/bad_kb/helpers/ble_hid_svc.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BleServiceHid BleServiceHid; + +BleServiceHid* ble_svc_hid_start(); + +void ble_svc_hid_stop(BleServiceHid* service); + +bool ble_svc_hid_update_report_map(BleServiceHid* service, const uint8_t* data, uint16_t len); + +bool ble_svc_hid_update_input_report( + BleServiceHid* service, + uint8_t input_report_num, + uint8_t* data, + uint16_t len); + +// Expects data to be of length BLE_SVC_HID_INFO_LEN (4 bytes) +bool ble_svc_hid_update_info(BleServiceHid* service, uint8_t* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/base_pack/bad_kb/helpers/ducky_script.c b/base_pack/bad_kb/helpers/ducky_script.c new file mode 100644 index 00000000000..a7c08e879da --- /dev/null +++ b/base_pack/bad_kb/helpers/ducky_script.c @@ -0,0 +1,935 @@ +#include "../bad_kb_app_i.h" +#include +#include +#include +#include +#include +#include +#include "ble_hid.h" +#include +#include "ducky_script.h" +#include "ducky_script_i.h" +#include +#include + +#define TAG "BadKb" +#define WORKER_TAG TAG "Worker" + +#define BADKB_ASCII_TO_KEY(script, x) \ + (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) + +// Delays for waiting between HID key press and key release +const uint8_t bt_hid_delays[LevelRssiNum] = { + 60, // LevelRssi122_100 + 55, // LevelRssi99_80 + 50, // LevelRssi79_60 + 47, // LevelRssi59_40 + 34, // LevelRssi39_0 +}; + +uint8_t bt_timeout = 0; + +static LevelRssiRange bt_remote_rssi_range(Bt* bt) { + uint8_t rssi; + + if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError; + + if(rssi <= 39) + return LevelRssi39_0; + else if(rssi <= 59) + return LevelRssi59_40; + else if(rssi <= 79) + return LevelRssi79_60; + else if(rssi <= 99) + return LevelRssi99_80; + else if(rssi <= 122) + return LevelRssi122_100; + + return LevelRssiError; +} + +static inline void update_bt_timeout(Bt* bt) { + LevelRssiRange r = bt_remote_rssi_range(bt); + if(r < LevelRssiNum) { + bt_timeout = bt_hid_delays[r]; + FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); + } +} + +static const char ducky_cmd_id[] = {"ID"}; +static const char ducky_cmd_bt_id[] = {"BT_ID"}; + +static const uint8_t numpad_keys[10] = { + HID_KEYPAD_0, + HID_KEYPAD_1, + HID_KEYPAD_2, + HID_KEYPAD_3, + HID_KEYPAD_4, + HID_KEYPAD_5, + HID_KEYPAD_6, + HID_KEYPAD_7, + HID_KEYPAD_8, + HID_KEYPAD_9, +}; + +uint32_t ducky_get_command_len(const char* line) { + uint32_t len = strlen(line); + for(uint32_t i = 0; i < len; i++) { + if(line[i] == ' ') return i; + } + return 0; +} + +bool ducky_is_line_end(const char chr) { + return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); +} + +uint16_t ducky_get_keycode(BadKbScript* bad_kb, const char* param, bool accept_chars) { + uint16_t keycode = ducky_get_keycode_by_name(param); + if(keycode != HID_KEYBOARD_NONE) { + return keycode; + } + + if((accept_chars) && (strlen(param) > 0)) { + return (BADKB_ASCII_TO_KEY(bad_kb, param[0]) & 0xFF); + } + return 0; +} + +bool ducky_get_number(const char* param, uint32_t* val) { + uint32_t value = 0; + if(sscanf(param, "%lu", &value) == 1) { + *val = value; + return true; + } + return false; +} + +uint8_t furi_hal_bt_hid_get_led_state() { + // FIXME + return 0; +} + +void ducky_numlock_on(BadKbScript* bad_kb) { + if(bad_kb->bt) { + if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, HID_KEYBOARD_LOCK_NUM_LOCK); + furi_delay_ms(bt_timeout); + ble_profile_hid_kb_release(bad_kb->app->ble_hid, HID_KEYBOARD_LOCK_NUM_LOCK); + } + } else { + if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { + furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); + furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); + } + } +} + +bool ducky_numpad_press(BadKbScript* bad_kb, const char num) { + if((num < '0') || (num > '9')) return false; + + uint16_t key = numpad_keys[num - '0']; + if(bad_kb->bt) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); + furi_delay_ms(bt_timeout); + ble_profile_hid_kb_release(bad_kb->app->ble_hid, key); + } else { + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release(key); + } + + return true; +} + +bool ducky_altchar(BadKbScript* bad_kb, const char* charcode) { + uint8_t i = 0; + bool state = false; + + if(bad_kb->bt) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, KEY_MOD_LEFT_ALT); + } else { + furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); + } + + while(!ducky_is_line_end(charcode[i])) { + state = ducky_numpad_press(bad_kb, charcode[i]); + if(state == false) break; + i++; + } + + if(bad_kb->bt) { + ble_profile_hid_kb_release(bad_kb->app->ble_hid, KEY_MOD_LEFT_ALT); + } else { + furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); + } + return state; +} + +bool ducky_altstring(BadKbScript* bad_kb, const char* param) { + uint32_t i = 0; + bool state = false; + + while(param[i] != '\0') { + if((param[i] < ' ') || (param[i] > '~')) { + i++; + continue; // Skip non-printable chars + } + + char temp_str[4]; + snprintf(temp_str, 4, "%u", param[i]); + + state = ducky_altchar(bad_kb, temp_str); + if(state == false) break; + i++; + } + return state; +} + +int32_t ducky_error(BadKbScript* bad_kb, const char* text, ...) { + va_list args; + va_start(args, text); + + vsnprintf(bad_kb->st.error, sizeof(bad_kb->st.error), text, args); + + va_end(args); + return SCRIPT_STATE_ERROR; +} + +bool ducky_string(BadKbScript* bad_kb, const char* param) { + uint32_t i = 0; + + while(param[i] != '\0') { + if(param[i] != '\n') { + uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]); + if(keycode != HID_KEYBOARD_NONE) { + if(bad_kb->bt) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, keycode); + furi_delay_ms(bt_timeout); + ble_profile_hid_kb_release(bad_kb->app->ble_hid, keycode); + } else { + furi_hal_hid_kb_press(keycode); + furi_hal_hid_kb_release(keycode); + } + } + } else { + if(bad_kb->bt) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, HID_KEYBOARD_RETURN); + furi_delay_ms(bt_timeout); + ble_profile_hid_kb_release(bad_kb->app->ble_hid, HID_KEYBOARD_RETURN); + } else { + furi_hal_hid_kb_press(HID_KEYBOARD_RETURN); + furi_hal_hid_kb_release(HID_KEYBOARD_RETURN); + } + } + i++; + } + bad_kb->stringdelay = 0; + return true; +} + +static bool ducky_string_next(BadKbScript* bad_kb) { + if(bad_kb->string_print_pos >= furi_string_size(bad_kb->string_print)) { + return true; + } + + char print_char = furi_string_get_char(bad_kb->string_print, bad_kb->string_print_pos); + + if(print_char != '\n') { + uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, print_char); + if(keycode != HID_KEYBOARD_NONE) { + if(bad_kb->bt) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, keycode); + furi_delay_ms(bt_timeout); + ble_profile_hid_kb_release(bad_kb->app->ble_hid, keycode); + } else { + furi_hal_hid_kb_press(keycode); + furi_hal_hid_kb_release(keycode); + } + } + } else { + if(bad_kb->bt) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, HID_KEYBOARD_RETURN); + furi_delay_ms(bt_timeout); + ble_profile_hid_kb_release(bad_kb->app->ble_hid, HID_KEYBOARD_RETURN); + } else { + furi_hal_hid_kb_press(HID_KEYBOARD_RETURN); + furi_hal_hid_kb_release(HID_KEYBOARD_RETURN); + } + } + + bad_kb->string_print_pos++; + + return false; +} + +static int32_t ducky_parse_line(BadKbScript* bad_kb, FuriString* line) { + uint32_t line_len = furi_string_size(line); + const char* line_tmp = furi_string_get_cstr(line); + + if(line_len == 0) { + return SCRIPT_STATE_NEXT_LINE; // Skip empty lines + } + FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); + + // Ducky Lang Functions + int32_t cmd_result = ducky_execute_cmd(bad_kb, line_tmp); + if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) { + return cmd_result; + } + + // Special keys + modifiers + uint16_t key = ducky_get_keycode(bad_kb, line_tmp, false); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_kb, "No keycode defined for %s", line_tmp); + } + if((key & 0xFF00) != 0) { + // It's a modifier key + uint32_t offset = ducky_get_command_len(line_tmp) + 1; + // ducky_get_command_len() returns 0 without space, so check for != 1 + if(offset != 1 && line_len > offset) { + // It's also a key combination + key |= ducky_get_keycode(bad_kb, line_tmp + offset, true); + } + } + if(bad_kb->bt) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); + furi_delay_ms(bt_timeout); + ble_profile_hid_kb_release(bad_kb->app->ble_hid, key); + } else { + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release(key); + } + return 0; +} + +static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) { + FuriHalUsbHidConfig* cfg = &bad_kb->app->id_config.usb; + + if(sscanf(line, "%lX:%lX", &cfg->vid, &cfg->pid) == 2) { + cfg->manuf[0] = '\0'; + cfg->product[0] = '\0'; + + uint8_t id_len = ducky_get_command_len(line); + if(!ducky_is_line_end(line[id_len + 1])) { + sscanf(&line[id_len + 1], "%31[^\r\n:]:%31[^\r\n]", cfg->manuf, cfg->product); + } + FURI_LOG_D( + WORKER_TAG, + "set usb id: %04lX:%04lX mfr:%s product:%s", + cfg->vid, + cfg->pid, + cfg->manuf, + cfg->product); + return true; + } + return false; +} + +static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { + BadKbConfig* cfg = &bad_kb->app->id_config; + + size_t line_len = strlen(line); + size_t mac_len = sizeof(cfg->ble.mac) * 3; // 2 text chars + separator per byte + if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name + + for(size_t i = 0; i < sizeof(cfg->ble.mac); i++) { + char a = line[i * 3]; + char b = line[i * 3 + 1]; + if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || + (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &cfg->ble.mac[i])) { + return false; + } + } + furi_hal_bt_reverse_mac_addr(cfg->ble.mac); + + strlcpy(cfg->ble.name, line + mac_len, sizeof(cfg->ble.name)); + FURI_LOG_D(WORKER_TAG, "set bt id: %s", line); + return true; +} + +static void ducky_script_preload(BadKbScript* bad_kb, File* script_file) { + BadKbApp* app = bad_kb->app; + uint8_t ret = 0; + uint32_t line_len = 0; + + furi_string_reset(bad_kb->line); + + do { + ret = storage_file_read(script_file, bad_kb->file_buf, FILE_BUFFER_LEN); + for(uint16_t i = 0; i < ret; i++) { + if(bad_kb->file_buf[i] == '\n' && line_len > 0) { + bad_kb->st.line_nb++; + line_len = 0; + } else { + if(bad_kb->st.line_nb == 0) { // Save first line + furi_string_push_back(bad_kb->line, bad_kb->file_buf[i]); + } + line_len++; + } + } + if(storage_file_eof(script_file)) { + if(line_len > 0) { + bad_kb->st.line_nb++; + break; + } + } + } while(ret > 0); + + // Looking for ID or BT_ID command at first line + const char* line_tmp = furi_string_get_cstr(bad_kb->line); + app->set_usb_id = false; + app->set_bt_id = false; + app->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; + app->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; + + if(app->has_usb_id) { + //app->is_bt = false; + app->set_usb_id = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); + } else if(app->has_bt_id) { + //app->is_bt = true; + app->set_bt_id = ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); + } + + storage_file_seek(script_file, 0, true); + furi_string_reset(bad_kb->line); +} + +static int32_t ducky_script_execute_next(BadKbScript* bad_kb, File* script_file) { + int32_t delay_val = 0; + + if(bad_kb->repeat_cnt > 0) { + bad_kb->repeat_cnt--; + delay_val = ducky_parse_line(bad_kb, bad_kb->line_prev); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays + return delay_val; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button + return delay_val; + } else if(delay_val < 0) { // Script error + bad_kb->st.error_line = bad_kb->st.line_cur - 1; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_kb->st.line_cur - 1U); + return SCRIPT_STATE_ERROR; + } else { + return (delay_val + bad_kb->defdelay); + } + } + + furi_string_set(bad_kb->line_prev, bad_kb->line); + furi_string_reset(bad_kb->line); + + while(1) { + if(bad_kb->buf_len == 0) { + bad_kb->buf_len = storage_file_read(script_file, bad_kb->file_buf, FILE_BUFFER_LEN); + if(storage_file_eof(script_file)) { + if((bad_kb->buf_len < FILE_BUFFER_LEN) && (bad_kb->file_end == false)) { + bad_kb->file_buf[bad_kb->buf_len] = '\n'; + bad_kb->buf_len++; + bad_kb->file_end = true; + } + } + + bad_kb->buf_start = 0; + if(bad_kb->buf_len == 0) return SCRIPT_STATE_END; + } + for(uint8_t i = bad_kb->buf_start; i < (bad_kb->buf_start + bad_kb->buf_len); i++) { + if(bad_kb->file_buf[i] == '\n' && furi_string_size(bad_kb->line) > 0) { + bad_kb->st.line_cur++; + bad_kb->buf_len = bad_kb->buf_len + bad_kb->buf_start - (i + 1); + bad_kb->buf_start = i + 1; + furi_string_trim(bad_kb->line); + delay_val = ducky_parse_line(bad_kb, bad_kb->line); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays + return delay_val; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button + return delay_val; + } else if(delay_val < 0) { + bad_kb->st.error_line = bad_kb->st.line_cur; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %zu", bad_kb->st.line_cur); + return SCRIPT_STATE_ERROR; + } else { + return (delay_val + bad_kb->defdelay); + } + } else { + furi_string_push_back(bad_kb->line, bad_kb->file_buf[i]); + } + } + bad_kb->buf_len = 0; + if(bad_kb->file_end) return SCRIPT_STATE_END; + } + + return 0; +} + +void bad_kb_bt_hid_state_callback(BtStatus status, void* context) { + furi_assert(context); + BadKbScript* bad_kb = context; + bool state = (status == BtStatusConnected); + + if(state == true) { + LevelRssiRange r = bt_remote_rssi_range(bad_kb->bt); + if(r != LevelRssiError) { + bt_timeout = bt_hid_delays[r]; + } + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); + } else { + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); + } +} + +void bad_kb_usb_hid_state_callback(bool state, void* context) { + furi_assert(context); + BadKbScript* bad_kb = context; + + if(state == true) { + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); + } else { + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); + } +} + +static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) { + uint32_t flags = furi_thread_flags_get(); + furi_check((flags & FuriFlagError) == 0); + if(flags == 0) { + flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); + furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); + } else { + uint32_t state = furi_thread_flags_clear(flags); + furi_check((state & FuriFlagError) == 0); + } + return flags; +} + +static int32_t bad_kb_worker(void* context) { + BadKbScript* bad_kb = context; + + BadKbWorkerState worker_state = BadKbStateInit; + BadKbWorkerState pause_state = BadKbStateRunning; + int32_t delay_val = 0; + + FURI_LOG_I(WORKER_TAG, "Init"); + File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + bad_kb->line = furi_string_alloc(); + bad_kb->line_prev = furi_string_alloc(); + bad_kb->string_print = furi_string_alloc(); + bad_kb->st.elapsed = 0; + + while(1) { + uint32_t start = furi_get_tick(); + if(worker_state == BadKbStateInit) { // State: initialization + start = 0; + FURI_LOG_D(WORKER_TAG, "init start"); + if(storage_file_open( + script_file, + furi_string_get_cstr(bad_kb->file_path), + FSAM_READ, + FSOM_OPEN_EXISTING)) { + ducky_script_preload(bad_kb, script_file); + if(bad_kb->st.line_nb > 0) { + bad_kb_config_refresh(bad_kb->app); + worker_state = BadKbStateNotConnected; // Refresh will set connected flag + } else { + worker_state = BadKbStateScriptError; // Script preload error + } + } else { + FURI_LOG_E(WORKER_TAG, "File open error"); + worker_state = BadKbStateFileError; // File open error + } + bad_kb->st.state = worker_state; + FURI_LOG_D(WORKER_TAG, "init done"); + + } else if(worker_state == BadKbStateNotConnected) { // State: Not connected + start = 0; + FURI_LOG_D(WORKER_TAG, "not connected wait"); + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "not connected flags: %lu", flags); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { + worker_state = BadKbStateIdle; // Ready to run + } else if(flags & WorkerEvtStartStop) { + worker_state = BadKbStateWillRun; // Will run when connected + } + bad_kb->st.state = worker_state; + + } else if(worker_state == BadKbStateIdle) { // State: ready to start + start = 0; + FURI_LOG_D(WORKER_TAG, "idle wait"); + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "idle flags: %lu", flags); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { // Start executing script + //dolphin_deed(DolphinDeedBadKbPlayScript); + delay_val = 0; + bad_kb->buf_len = 0; + bad_kb->st.line_cur = 0; + bad_kb->defdelay = 0; + bad_kb->stringdelay = 0; + bad_kb->repeat_cnt = 0; + bad_kb->key_hold_nb = 0; + bad_kb->file_end = false; + storage_file_seek(script_file, 0, true); + bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); + worker_state = BadKbStateRunning; + bad_kb->st.elapsed = 0; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadKbStateNotConnected; // Disconnected + } + bad_kb->st.state = worker_state; + + } else if(worker_state == BadKbStateWillRun) { // State: start on connection + start = 0; + FURI_LOG_D(WORKER_TAG, "will run wait"); + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "will run flags: %lu", flags); + + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { // Start executing script + //dolphin_deed(DolphinDeedBadKbPlayScript); + delay_val = 0; + bad_kb->buf_len = 0; + bad_kb->st.line_cur = 0; + bad_kb->defdelay = 0; + bad_kb->stringdelay = 0; + bad_kb->repeat_cnt = 0; + bad_kb->file_end = false; + storage_file_seek(script_file, 0, true); + // extra time for PC to recognize Flipper as keyboard + flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriFlagWaitAny | FuriFlagNoClear, + bad_kb->bt ? 3000 : 1500); + if(flags == (unsigned)FuriFlagErrorTimeout) { + // If nothing happened - start script execution + worker_state = BadKbStateRunning; + bad_kb->st.elapsed = 0; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadKbStateIdle; + furi_thread_flags_clear(WorkerEvtStartStop); + } + if(bad_kb->bt) { + update_bt_timeout(bad_kb->bt); + } + bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); + } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution + worker_state = BadKbStateNotConnected; + } + bad_kb->st.state = worker_state; + + } else if(worker_state == BadKbStateRunning) { // State: running + FURI_LOG_D(WORKER_TAG, "running"); + uint16_t delay_cur = (delay_val > 100) ? (100) : (delay_val); + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, + FuriFlagWaitAny, + delay_cur); + FURI_LOG_D(WORKER_TAG, "running flags: %lu", flags); + + delay_val -= delay_cur; + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadKbStateIdle; // Stop executing script + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadKbStateNotConnected; // Disconnected + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadKbStateRunning; + worker_state = BadKbStatePaused; // Pause + } + bad_kb->st.state = worker_state; + bad_kb->st.elapsed += (furi_get_tick() - start); + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + if(delay_val > 0) { + bad_kb->st.delay_remain--; + bad_kb->st.elapsed += (furi_get_tick() - start); + continue; + } + bad_kb->st.state = BadKbStateRunning; + delay_val = ducky_script_execute_next(bad_kb, script_file); + if(delay_val == SCRIPT_STATE_ERROR) { // Script error + delay_val = 0; + worker_state = BadKbStateScriptError; + bad_kb->st.state = worker_state; + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + } else if(delay_val == SCRIPT_STATE_END) { // End of script + delay_val = 0; + worker_state = BadKbStateIdle; + bad_kb->st.state = BadKbStateDone; + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + bad_kb->st.elapsed += (furi_get_tick() - start); + continue; + } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays + delay_val = bad_kb->defdelay; + bad_kb->string_print_pos = 0; + worker_state = BadKbStateStringDelay; + } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input + worker_state = BadKbStateWaitForBtn; + bad_kb->st.state = BadKbStateWaitForBtn; // Show long delays + } else if(delay_val > 100) { + bad_kb->st.state = BadKbStateDelay; // Show long delays + bad_kb->st.delay_remain = delay_val / 100; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + } else if(worker_state == BadKbStateWaitForBtn) { // State: Wait for button Press + start = 0; + FURI_LOG_D(WORKER_TAG, "button wait"); + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "button flags: %lu", flags); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + delay_val = 0; + worker_state = BadKbStateRunning; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadKbStateNotConnected; // Disconnected + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + } + bad_kb->st.state = worker_state; + continue; + } + } else if(worker_state == BadKbStatePaused) { // State: Paused + start = 0; + FURI_LOG_D(WORKER_TAG, "paused wait"); + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, + FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "paused flags: %lu", flags); + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadKbStateIdle; // Stop executing script + bad_kb->st.state = worker_state; + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadKbStateNotConnected; // Disconnected + bad_kb->st.state = worker_state; + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + } else if(flags & WorkerEvtPauseResume) { + if(pause_state == BadKbStateRunning) { + if(delay_val > 0) { + bad_kb->st.state = BadKbStateDelay; + bad_kb->st.delay_remain = delay_val / 100; + } else { + bad_kb->st.state = BadKbStateRunning; + delay_val = 0; + } + worker_state = BadKbStateRunning; // Resume + } else if(pause_state == BadKbStateStringDelay) { + bad_kb->st.state = BadKbStateRunning; + worker_state = BadKbStateStringDelay; // Resume + } + } + continue; + } + } else if(worker_state == BadKbStateStringDelay) { // State: print string with delays + FURI_LOG_D(WORKER_TAG, "delay wait"); + uint32_t flags = bad_kb_flags_get( + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, + bad_kb->stringdelay); + FURI_LOG_D(WORKER_TAG, "delay flags: %lu", flags); + + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtStartStop) { + worker_state = BadKbStateIdle; // Stop executing script + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadKbStateNotConnected; // Disconnected + if(bad_kb->bt) { + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_release_all(); + } + } else if(flags & WorkerEvtPauseResume) { + pause_state = BadKbStateStringDelay; + worker_state = BadKbStatePaused; // Pause + } + bad_kb->st.state = worker_state; + bad_kb->st.elapsed += (furi_get_tick() - start); + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + bool string_end = ducky_string_next(bad_kb); + if(string_end) { + bad_kb->stringdelay = 0; + worker_state = BadKbStateRunning; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + } else if( + (worker_state == BadKbStateFileError) || + (worker_state == BadKbStateScriptError)) { // State: error + start = 0; + FURI_LOG_D(WORKER_TAG, "error wait"); + uint32_t flags = + bad_kb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command + FURI_LOG_D(WORKER_TAG, "error flags: %lu", flags); + + if(flags & WorkerEvtEnd) { + break; + } + } + if(bad_kb->bt) { + update_bt_timeout(bad_kb->bt); + } + if(start) { + bad_kb->st.elapsed += (furi_get_tick() - start); + } + } + + bt_set_status_changed_callback(bad_kb->app->bt, NULL, NULL); + furi_hal_hid_set_state_callback(NULL, NULL); + + storage_file_close(script_file); + storage_file_free(script_file); + furi_string_free(bad_kb->line); + furi_string_free(bad_kb->line_prev); + furi_string_free(bad_kb->string_print); + + FURI_LOG_I(WORKER_TAG, "End"); + + return 0; +} + +static void bad_kb_script_set_default_keyboard_layout(BadKbScript* bad_kb) { + furi_assert(bad_kb); + furi_string_set_str(bad_kb->keyboard_layout, ""); + memset(bad_kb->layout, HID_KEYBOARD_NONE, sizeof(bad_kb->layout)); + memcpy(bad_kb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_kb->layout))); +} + +BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt, BadKbApp* app) { + furi_assert(file_path); + + BadKbScript* bad_kb = malloc(sizeof(BadKbScript)); + bad_kb->app = app; + bad_kb->file_path = furi_string_alloc(); + furi_string_set(bad_kb->file_path, file_path); + bad_kb->keyboard_layout = furi_string_alloc(); + bad_kb_script_set_default_keyboard_layout(bad_kb); + + bad_kb->st.state = BadKbStateInit; + bad_kb->st.error[0] = '\0'; + bad_kb->st.is_bt = !!bt; + + bad_kb->bt = bt; + + bad_kb->thread = furi_thread_alloc_ex("BadKbWorker", 2048, bad_kb_worker, bad_kb); + furi_thread_start(bad_kb->thread); + return bad_kb; +} //-V773 + +void bad_kb_script_close(BadKbScript* bad_kb) { + furi_assert(bad_kb); + furi_record_close(RECORD_STORAGE); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtEnd); + furi_thread_join(bad_kb->thread); + furi_thread_free(bad_kb->thread); + furi_string_free(bad_kb->file_path); + furi_string_free(bad_kb->keyboard_layout); + free(bad_kb); +} + +void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_path) { + furi_assert(bad_kb); + + if((bad_kb->st.state == BadKbStateRunning) || (bad_kb->st.state == BadKbStateDelay)) { + // do not update keyboard layout while a script is running + return; + } + + File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(!furi_string_empty(layout_path)) { //-V1051 + furi_string_set(bad_kb->keyboard_layout, layout_path); + if(storage_file_open( + layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(bad_kb->layout, layout, sizeof(layout)); + } + } + storage_file_close(layout_file); + } else { + bad_kb_script_set_default_keyboard_layout(bad_kb); + } + storage_file_free(layout_file); +} + +void bad_kb_script_start_stop(BadKbScript* bad_kb) { + furi_assert(bad_kb); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtStartStop); +} + +void bad_kb_script_pause_resume(BadKbScript* bad_kb) { + furi_assert(bad_kb); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtPauseResume); +} + +BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb) { + furi_assert(bad_kb); + return &(bad_kb->st); +} diff --git a/base_pack/bad_kb/helpers/ducky_script.h b/base_pack/bad_kb/helpers/ducky_script.h new file mode 100644 index 00000000000..28c931bc60c --- /dev/null +++ b/base_pack/bad_kb/helpers/ducky_script.h @@ -0,0 +1,86 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "../bad_kb_app.h" + +typedef enum { + LevelRssi122_100, + LevelRssi99_80, + LevelRssi79_60, + LevelRssi59_40, + LevelRssi39_0, + LevelRssiNum, + LevelRssiError = 0xFF, +} LevelRssiRange; + +extern const uint8_t bt_hid_delays[LevelRssiNum]; + +extern uint8_t bt_timeout; + +typedef enum { + WorkerEvtStartStop = (1 << 0), + WorkerEvtPauseResume = (1 << 1), + WorkerEvtEnd = (1 << 2), + WorkerEvtConnect = (1 << 3), + WorkerEvtDisconnect = (1 << 4), +} WorkerEvtFlags; + +typedef enum { + BadKbStateInit, + BadKbStateNotConnected, + BadKbStateIdle, + BadKbStateWillRun, + BadKbStateRunning, + BadKbStateDelay, + BadKbStateStringDelay, + BadKbStateWaitForBtn, + BadKbStatePaused, + BadKbStateDone, + BadKbStateScriptError, + BadKbStateFileError, +} BadKbWorkerState; + +typedef struct { + BadKbWorkerState state; + bool is_bt; + uint32_t pin; + size_t line_cur; + size_t line_nb; + uint32_t delay_remain; + size_t error_line; + char error[64]; + uint32_t elapsed; +} BadKbState; + +typedef struct BadKbScript BadKbScript; + +BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt, BadKbApp* app); + +void bad_kb_script_close(BadKbScript* bad_kb); + +void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_path); + +void bad_kb_script_start(BadKbScript* bad_kb); + +void bad_kb_script_stop(BadKbScript* bad_kb); + +void bad_kb_script_start_stop(BadKbScript* bad_kb); + +void bad_kb_script_pause_resume(BadKbScript* bad_kb); + +BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); + +void bad_kb_bt_hid_state_callback(BtStatus status, void* context); + +void bad_kb_usb_hid_state_callback(bool state, void* context); + +#ifdef __cplusplus +} +#endif diff --git a/base_pack/bad_kb/helpers/ducky_script_commands.c b/base_pack/bad_kb/helpers/ducky_script_commands.c new file mode 100644 index 00000000000..0cb696c6ca9 --- /dev/null +++ b/base_pack/bad_kb/helpers/ducky_script_commands.c @@ -0,0 +1,215 @@ +#include "../bad_kb_app_i.h" +#include +#include +#include "ble_hid.h" +#include "ducky_script.h" +#include "ducky_script_i.h" + +typedef int32_t (*DuckyCmdCallback)(BadKbScript* bad_kb, const char* line, int32_t param); + +typedef struct { + char* name; + DuckyCmdCallback callback; + int32_t param; +} DuckyCmd; + +static int32_t ducky_fnc_delay(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint32_t delay_val = 0; + bool state = ducky_get_number(line, &delay_val); + if((state) && (delay_val > 0)) { + return (int32_t)delay_val; + } + + return ducky_error(bad_kb, "Invalid number %s", line); +} + +static int32_t ducky_fnc_defdelay(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_kb->defdelay); + if(!state) { + return ducky_error(bad_kb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_strdelay(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_kb->stringdelay); + if(!state) { + return ducky_error(bad_kb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_string(BadKbScript* bad_kb, const char* line, int32_t param) { + line = &line[ducky_get_command_len(line) + 1]; + furi_string_set_str(bad_kb->string_print, line); + if(param == 1) { + furi_string_cat(bad_kb->string_print, "\n"); + } + + if(bad_kb->stringdelay == 0) { // stringdelay not set - run command immediately + bool state = ducky_string(bad_kb, furi_string_get_cstr(bad_kb->string_print)); + if(!state) { + return ducky_error(bad_kb, "Invalid string %s", line); + } + } else { // stringdelay is set - run command in thread to keep handling external events + return SCRIPT_STATE_STRING_START; + } + + return 0; +} + +static int32_t ducky_fnc_repeat(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + bool state = ducky_get_number(line, &bad_kb->repeat_cnt); + if((!state) || (bad_kb->repeat_cnt == 0)) { + return ducky_error(bad_kb, "Invalid number %s", line); + } + return 0; +} + +static int32_t ducky_fnc_sysrq(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_kb, line, true); + if(bad_kb->bt) { + ble_profile_hid_kb_press( + bad_kb->app->ble_hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); + furi_delay_ms(bt_timeout); + ble_profile_hid_kb_release_all(bad_kb->app->ble_hid); + } else { + furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release_all(); + } + return 0; +} + +static int32_t ducky_fnc_altchar(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + ducky_numlock_on(bad_kb); + bool state = ducky_altchar(bad_kb, line); + if(!state) { + return ducky_error(bad_kb, "Invalid altchar %s", line); + } + return 0; +} + +static int32_t ducky_fnc_altstring(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + ducky_numlock_on(bad_kb); + bool state = ducky_altstring(bad_kb, line); + if(!state) { + return ducky_error(bad_kb, "Invalid altstring %s", line); + } + return 0; +} + +static int32_t ducky_fnc_hold(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_kb, line, true); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_kb, "No keycode defined for %s", line); + } + bad_kb->key_hold_nb++; + if(bad_kb->key_hold_nb > (HID_KB_MAX_KEYS - 1)) { + return ducky_error(bad_kb, "Too many keys are hold"); + } + if(bad_kb->bt) { + ble_profile_hid_kb_press(bad_kb->app->ble_hid, key); + } else { + furi_hal_hid_kb_press(key); + } + return 0; +} + +static int32_t ducky_fnc_release(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + + line = &line[ducky_get_command_len(line) + 1]; + uint16_t key = ducky_get_keycode(bad_kb, line, true); + if(key == HID_KEYBOARD_NONE) { + return ducky_error(bad_kb, "No keycode defined for %s", line); + } + if(bad_kb->key_hold_nb == 0) { + return ducky_error(bad_kb, "No keys are hold"); + } + bad_kb->key_hold_nb--; + if(bad_kb->bt) { + ble_profile_hid_kb_release(bad_kb->app->ble_hid, key); + } else { + furi_hal_hid_kb_release(key); + } + return 0; +} + +static int32_t ducky_fnc_waitforbutton(BadKbScript* bad_kb, const char* line, int32_t param) { + UNUSED(param); + UNUSED(bad_kb); + UNUSED(line); + + return SCRIPT_STATE_WAIT_FOR_BTN; +} + +static const DuckyCmd ducky_commands[] = { + {"REM", NULL, -1}, + {"ID", NULL, -1}, + {"BT_ID", NULL, -1}, + {"DELAY", ducky_fnc_delay, -1}, + {"STRING", ducky_fnc_string, 0}, + {"STRINGLN", ducky_fnc_string, 1}, + {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, + {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, + {"STRINGDELAY", ducky_fnc_strdelay, -1}, + {"STRING_DELAY", ducky_fnc_strdelay, -1}, + {"REPEAT", ducky_fnc_repeat, -1}, + {"SYSRQ", ducky_fnc_sysrq, -1}, + {"ALTCHAR", ducky_fnc_altchar, -1}, + {"ALTSTRING", ducky_fnc_altstring, -1}, + {"ALTCODE", ducky_fnc_altstring, -1}, + {"HOLD", ducky_fnc_hold, -1}, + {"RELEASE", ducky_fnc_release, -1}, + {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, +}; + +#define TAG "BadKb" +#define WORKER_TAG TAG "Worker" + +int32_t ducky_execute_cmd(BadKbScript* bad_kb, const char* line) { + size_t cmd_word_len = strcspn(line, " "); + for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { + size_t cmd_compare_len = strlen(ducky_commands[i].name); + + if(cmd_compare_len != cmd_word_len) { + continue; + } + + if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { + if(ducky_commands[i].callback == NULL) { + return 0; + } else { + return ((ducky_commands[i].callback)(bad_kb, line, ducky_commands[i].param)); + } + } + } + + return SCRIPT_STATE_CMD_UNKNOWN; +} diff --git a/base_pack/bad_kb/helpers/ducky_script_i.h b/base_pack/bad_kb/helpers/ducky_script_i.h new file mode 100644 index 00000000000..fcc061de671 --- /dev/null +++ b/base_pack/bad_kb/helpers/ducky_script_i.h @@ -0,0 +1,73 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "ducky_script.h" + +#define SCRIPT_STATE_ERROR (-1) +#define SCRIPT_STATE_END (-2) +#define SCRIPT_STATE_NEXT_LINE (-3) +#define SCRIPT_STATE_CMD_UNKNOWN (-4) +#define SCRIPT_STATE_STRING_START (-5) +#define SCRIPT_STATE_WAIT_FOR_BTN (-6) + +#define FILE_BUFFER_LEN 16 + +struct BadKbScript { + FuriThread* thread; + BadKbState st; + + FuriString* file_path; + FuriString* keyboard_layout; + uint8_t file_buf[FILE_BUFFER_LEN + 1]; + uint8_t buf_start; + uint8_t buf_len; + bool file_end; + + uint32_t defdelay; + uint32_t stringdelay; + uint16_t layout[128]; + + FuriString* line; + FuriString* line_prev; + uint32_t repeat_cnt; + uint8_t key_hold_nb; + + FuriString* string_print; + size_t string_print_pos; + + Bt* bt; + BadKbApp* app; +}; + +uint16_t ducky_get_keycode(BadKbScript* bad_kb, const char* param, bool accept_chars); + +uint32_t ducky_get_command_len(const char* line); + +bool ducky_is_line_end(const char chr); + +uint16_t ducky_get_keycode_by_name(const char* param); + +bool ducky_get_number(const char* param, uint32_t* val); + +void ducky_numlock_on(BadKbScript* bad_kb); + +bool ducky_numpad_press(BadKbScript* bad_kb, const char num); + +bool ducky_altchar(BadKbScript* bad_kb, const char* charcode); + +bool ducky_altstring(BadKbScript* bad_kb, const char* param); + +bool ducky_string(BadKbScript* bad_kb, const char* param); + +int32_t ducky_execute_cmd(BadKbScript* bad_kb, const char* line); + +int32_t ducky_error(BadKbScript* bad_kb, const char* text, ...); + +#ifdef __cplusplus +} +#endif diff --git a/base_pack/bad_bt/helpers/ducky_script_keycodes.c b/base_pack/bad_kb/helpers/ducky_script_keycodes.c similarity index 98% rename from base_pack/bad_bt/helpers/ducky_script_keycodes.c rename to base_pack/bad_kb/helpers/ducky_script_keycodes.c index 55c52810fa3..da2fc22f79f 100644 --- a/base_pack/bad_bt/helpers/ducky_script_keycodes.c +++ b/base_pack/bad_kb/helpers/ducky_script_keycodes.c @@ -1,4 +1,5 @@ #include +#include #include "ducky_script_i.h" typedef struct { diff --git a/base_pack/bad_bt/images/badbt_10px.png b/base_pack/bad_kb/icon.png similarity index 100% rename from base_pack/bad_bt/images/badbt_10px.png rename to base_pack/bad_kb/icon.png diff --git a/base_pack/bad_kb/scenes/bad_kb_scene.c b/base_pack/bad_kb/scenes/bad_kb_scene.c new file mode 100644 index 00000000000..f90d23a7774 --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene.c @@ -0,0 +1,30 @@ +#include "bad_kb_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const bad_kb_scene_on_enter_handlers[])(void*) = { +#include "bad_kb_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const bad_kb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "bad_kb_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const bad_kb_scene_on_exit_handlers[])(void* context) = { +#include "bad_kb_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers bad_kb_scene_handlers = { + .on_enter_handlers = bad_kb_scene_on_enter_handlers, + .on_event_handlers = bad_kb_scene_on_event_handlers, + .on_exit_handlers = bad_kb_scene_on_exit_handlers, + .scene_num = BadKbSceneNum, +}; diff --git a/base_pack/bad_bt/scenes/bad_bt_scene.h b/base_pack/bad_kb/scenes/bad_kb_scene.h similarity index 68% rename from base_pack/bad_bt/scenes/bad_bt_scene.h rename to base_pack/bad_kb/scenes/bad_kb_scene.h index a316034effd..82db02873db 100644 --- a/base_pack/bad_bt/scenes/bad_bt_scene.h +++ b/base_pack/bad_kb/scenes/bad_kb_scene.h @@ -3,27 +3,27 @@ #include // Generate scene id and total number -#define ADD_SCENE(prefix, name, id) BadBtScene##id, +#define ADD_SCENE(prefix, name, id) BadKbScene##id, typedef enum { -#include "bad_bt_scene_config.h" - BadBtSceneNum, -} BadBtScene; +#include "bad_kb_scene_config.h" + BadKbSceneNum, +} BadKbScene; #undef ADD_SCENE -extern const SceneManagerHandlers bad_bt_scene_handlers; +extern const SceneManagerHandlers bad_kb_scene_handlers; // Generate scene on_enter handlers declaration #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "bad_bt_scene_config.h" +#include "bad_kb_scene_config.h" #undef ADD_SCENE // Generate scene on_event handlers declaration #define ADD_SCENE(prefix, name, id) \ bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "bad_bt_scene_config.h" +#include "bad_kb_scene_config.h" #undef ADD_SCENE // Generate scene on_exit handlers declaration #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "bad_bt_scene_config.h" +#include "bad_kb_scene_config.h" #undef ADD_SCENE diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_config.c b/base_pack/bad_kb/scenes/bad_kb_scene_config.c new file mode 100644 index 00000000000..8f7e68e97b3 --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_config.c @@ -0,0 +1,186 @@ +#include "../bad_kb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum VarItemListIndex { + VarItemListIndexKeyboardLayout, + VarItemListIndexConnection, +}; + +enum VarItemListIndexBt { + VarItemListIndexBtRemember = VarItemListIndexConnection + 1, + VarItemListIndexBtDeviceName, + VarItemListIndexBtMacAddress, + VarItemListIndexBtRandomizeMac, +}; + +enum VarItemListIndexUsb { + VarItemListIndexUsbManufacturer = VarItemListIndexConnection + 1, + VarItemListIndexUsbProductName, + VarItemListIndexUsbVidPid, + VarItemListIndexUsbRandomizeVidPid, +}; + +void bad_kb_scene_config_connection_callback(VariableItem* item) { + BadKbApp* bad_kb = variable_item_get_context(item); + bad_kb->is_bt = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); +} + +void bad_kb_scene_config_bt_remember_callback(VariableItem* item) { + BadKbApp* bad_kb = variable_item_get_context(item); + bad_kb->bt_remember = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexBtRemember); +} + +void bad_kb_scene_config_var_item_list_callback(void* context, uint32_t index) { + BadKbApp* bad_kb = context; + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index); +} + +void bad_kb_scene_config_on_enter(void* context) { + BadKbApp* bad_kb = context; + VariableItemList* var_item_list = bad_kb->var_item_list; + VariableItem* item; + + item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb); + + item = variable_item_list_add( + var_item_list, "Connection", 2, bad_kb_scene_config_connection_callback, bad_kb); + variable_item_set_current_value_index(item, bad_kb->is_bt); + variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); + /*if(bad_kb->has_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nUSB Mode!"); + } else if(bad_kb->has_bt_id) { + variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nBT Mode!"); + }*/ + + if(bad_kb->is_bt) { + item = variable_item_list_add( + var_item_list, "BT Remember", 2, bad_kb_scene_config_bt_remember_callback, bad_kb); + variable_item_set_current_value_index(item, bad_kb->bt_remember); + variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); + + item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_kb); + if(bad_kb->set_bt_id) { + variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!"); + } + + item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_kb); + if(bad_kb->bt_remember) { + variable_item_set_locked(item, true, "Remember\nmust be Off!"); + } else if(bad_kb->set_bt_id) { + variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); + } + + item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_kb); + if(bad_kb->bt_remember) { + variable_item_set_locked(item, true, "Remember\nmust be Off!"); + } else if(bad_kb->set_bt_id) { + variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); + } + } else { + item = variable_item_list_add(var_item_list, "USB Manufacturer", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Mname!"); + } + + item = variable_item_list_add(var_item_list, "USB Product Name", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Pname!"); + } + + item = variable_item_list_add(var_item_list, "USB VID and PID", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset IDs!"); + } + + item = variable_item_list_add(var_item_list, "Randomize USB VID:PID", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset IDs!"); + } + } + + variable_item_list_set_enter_callback( + var_item_list, bad_kb_scene_config_var_item_list_callback, bad_kb); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfig)); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewVarItemList); +} + +bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_kb->scene_manager, BadKbSceneConfig, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexKeyboardLayout: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); + break; + case VarItemListIndexConnection: + bad_kb_config_refresh(bad_kb); + break; + default: + break; + } + if(bad_kb->is_bt) { + switch(event.event) { + case VarItemListIndexBtRemember: + bad_kb_config_refresh(bad_kb); + break; + case VarItemListIndexBtDeviceName: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtName); + break; + case VarItemListIndexBtMacAddress: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtMac); + break; + case VarItemListIndexBtRandomizeMac: + furi_hal_random_fill_buf(bad_kb->config.ble.mac, sizeof(bad_kb->config.ble.mac)); + bad_kb_config_refresh(bad_kb); + break; + default: + break; + } + } else { + switch(event.event) { + case VarItemListIndexUsbManufacturer: + scene_manager_set_scene_state( + bad_kb->scene_manager, BadKbSceneConfigUsbName, true); + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); + break; + case VarItemListIndexUsbProductName: + scene_manager_set_scene_state( + bad_kb->scene_manager, BadKbSceneConfigUsbName, false); + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); + break; + case VarItemListIndexUsbVidPid: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbVidPid); + break; + case VarItemListIndexUsbRandomizeVidPid: + furi_hal_random_fill_buf( + (void*)bad_kb->usb_vidpid_buf, sizeof(bad_kb->usb_vidpid_buf)); + bad_kb->config.usb.vid = bad_kb->usb_vidpid_buf[0]; + bad_kb->config.usb.pid = bad_kb->usb_vidpid_buf[1]; + bad_kb_config_refresh(bad_kb); + break; + default: + break; + } + } + } + + return consumed; +} + +void bad_kb_scene_config_on_exit(void* context) { + BadKbApp* bad_kb = context; + VariableItemList* var_item_list = bad_kb->var_item_list; + + variable_item_list_reset(var_item_list); +} diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_config.h b/base_pack/bad_kb/scenes/bad_kb_scene_config.h new file mode 100644 index 00000000000..034a898a44d --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_config.h @@ -0,0 +1,9 @@ +ADD_SCENE(bad_kb, file_select, FileSelect) +ADD_SCENE(bad_kb, work, Work) +ADD_SCENE(bad_kb, error, Error) +ADD_SCENE(bad_kb, config, Config) +ADD_SCENE(bad_kb, config_layout, ConfigLayout) +ADD_SCENE(bad_kb, config_bt_name, ConfigBtName) +ADD_SCENE(bad_kb, config_bt_mac, ConfigBtMac) +ADD_SCENE(bad_kb, config_usb_name, ConfigUsbName) +ADD_SCENE(bad_kb, config_usb_vidpid, ConfigUsbVidPid) diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_config_bt_mac.c b/base_pack/bad_kb/scenes/bad_kb_scene_config_bt_mac.c new file mode 100644 index 00000000000..5439c0bc4c4 --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_config_bt_mac.c @@ -0,0 +1,50 @@ +#include "../bad_kb_app_i.h" + +void bad_kb_scene_config_bt_mac_byte_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); +} + +void bad_kb_scene_config_bt_mac_on_enter(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + memcpy(bad_kb->bt_mac_buf, bad_kb->config.ble.mac, sizeof(bad_kb->bt_mac_buf)); + furi_hal_bt_reverse_mac_addr(bad_kb->bt_mac_buf); + byte_input_set_header_text(byte_input, "Set BT MAC address"); + + byte_input_set_result_callback( + byte_input, + bad_kb_scene_config_bt_mac_byte_input_callback, + NULL, + bad_kb, + bad_kb->bt_mac_buf, + sizeof(bad_kb->bt_mac_buf)); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); +} + +bool bad_kb_scene_config_bt_mac_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventByteInputDone) { + furi_hal_bt_reverse_mac_addr(bad_kb->bt_mac_buf); + memcpy(bad_kb->config.ble.mac, bad_kb->bt_mac_buf, sizeof(bad_kb->config.ble.mac)); + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_bt_mac_on_exit(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(byte_input, ""); +} diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_config_bt_name.c b/base_pack/bad_kb/scenes/bad_kb_scene_config_bt_name.c new file mode 100644 index 00000000000..04a02aed403 --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_config_bt_name.c @@ -0,0 +1,47 @@ +#include "../bad_kb_app_i.h" + +static void bad_kb_scene_config_bt_name_text_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); +} + +void bad_kb_scene_config_bt_name_on_enter(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + strlcpy(bad_kb->bt_name_buf, bad_kb->config.ble.name, sizeof(bad_kb->bt_name_buf)); + text_input_set_header_text(text_input, "Set BT device name"); + + text_input_set_result_callback( + text_input, + bad_kb_scene_config_bt_name_text_input_callback, + bad_kb, + bad_kb->bt_name_buf, + sizeof(bad_kb->bt_name_buf), + true); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); +} + +bool bad_kb_scene_config_bt_name_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventTextInputDone) { + strlcpy(bad_kb->config.ble.name, bad_kb->bt_name_buf, sizeof(bad_kb->config.ble.name)); + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_bt_name_on_exit(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + text_input_reset(text_input); +} diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_config_layout.c b/base_pack/bad_kb/scenes/bad_kb_scene_config_layout.c new file mode 100644 index 00000000000..380bde30e63 --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_config_layout.c @@ -0,0 +1,48 @@ +#include "../bad_kb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" +#include + +static bool bad_kb_layout_select(BadKbApp* bad_kb) { + furi_assert(bad_kb); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_kb->keyboard_layout)) { + furi_string_set(predefined_path, bad_kb->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_KB_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_KB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + browser_options.base_path = BAD_KB_APP_PATH_LAYOUT_FOLDER; + browser_options.skip_assets = false; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_kb->dialogs, bad_kb->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_kb_scene_config_layout_on_enter(void* context) { + BadKbApp* bad_kb = context; + + if(bad_kb_layout_select(bad_kb)) { + bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); + } + scene_manager_previous_scene(bad_kb->scene_manager); +} + +bool bad_kb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void bad_kb_scene_config_layout_on_exit(void* context) { + UNUSED(context); +} diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_config_usb_name.c b/base_pack/bad_kb/scenes/bad_kb_scene_config_usb_name.c new file mode 100644 index 00000000000..c7dd7a2fa30 --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_config_usb_name.c @@ -0,0 +1,62 @@ +#include "../bad_kb_app_i.h" + +static void bad_kb_scene_config_usb_name_text_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); +} + +void bad_kb_scene_config_usb_name_on_enter(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { + strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb.manuf, sizeof(bad_kb->usb_name_buf)); + text_input_set_header_text(text_input, "Set USB manufacturer name"); + } else { + strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb.product, sizeof(bad_kb->usb_name_buf)); + text_input_set_header_text(text_input, "Set USB product name"); + } + + text_input_set_result_callback( + text_input, + bad_kb_scene_config_usb_name_text_input_callback, + bad_kb, + bad_kb->usb_name_buf, + sizeof(bad_kb->usb_name_buf), + true); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); +} + +bool bad_kb_scene_config_usb_name_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventTextInputDone) { + if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { + strlcpy( + bad_kb->config.usb.manuf, + bad_kb->usb_name_buf, + sizeof(bad_kb->config.usb.product)); + } else { + strlcpy( + bad_kb->config.usb.product, + bad_kb->usb_name_buf, + sizeof(bad_kb->config.usb.product)); + } + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_usb_name_on_exit(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + text_input_reset(text_input); +} diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c b/base_pack/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c new file mode 100644 index 00000000000..33f899f4102 --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c @@ -0,0 +1,50 @@ +#include "../bad_kb_app_i.h" + +void bad_kb_scene_config_usb_vidpid_byte_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); +} + +void bad_kb_scene_config_usb_vidpid_on_enter(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + bad_kb->usb_vidpid_buf[0] = __REVSH(bad_kb->config.usb.vid); + bad_kb->usb_vidpid_buf[1] = __REVSH(bad_kb->config.usb.pid); + byte_input_set_header_text(byte_input, "Set USB VID:PID"); + + byte_input_set_result_callback( + byte_input, + bad_kb_scene_config_usb_vidpid_byte_input_callback, + NULL, + bad_kb, + (void*)bad_kb->usb_vidpid_buf, + sizeof(bad_kb->usb_vidpid_buf)); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); +} + +bool bad_kb_scene_config_usb_vidpid_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventByteInputDone) { + bad_kb->config.usb.vid = __REVSH(bad_kb->usb_vidpid_buf[0]); + bad_kb->config.usb.pid = __REVSH(bad_kb->usb_vidpid_buf[1]); + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_usb_vidpid_on_exit(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(byte_input, ""); +} diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_error.c b/base_pack/bad_kb/scenes/bad_kb_scene_error.c new file mode 100644 index 00000000000..b0edac36cf1 --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_error.c @@ -0,0 +1,49 @@ +#include "../bad_kb_app_i.h" + +static void + bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + BadKbApp* app = context; + + if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event(app->view_dispatcher, BadKbCustomEventErrorBack); + } +} + +void bad_kb_scene_error_on_enter(void* context) { + BadKbApp* app = context; + + if(app->error == BadKbAppErrorNoFiles) { + widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); + widget_add_string_multiline_element( + app->widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", bad_kb_scene_error_event_callback, app); + } + + view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWidget); +} + +bool bad_kb_scene_error_on_event(void* context, SceneManagerEvent event) { + BadKbApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == BadKbCustomEventErrorBack) { + view_dispatcher_stop(app->view_dispatcher); + consumed = true; + } + } + return consumed; +} + +void bad_kb_scene_error_on_exit(void* context) { + BadKbApp* app = context; + widget_reset(app->widget); +} diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_file_select.c b/base_pack/bad_kb/scenes/bad_kb_scene_file_select.c new file mode 100644 index 00000000000..59af5a42ceb --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_file_select.c @@ -0,0 +1,55 @@ +#include "../bad_kb_app_i.h" +#include +#include +#include + +static bool bad_kb_file_select(BadKbApp* bad_kb) { + furi_assert(bad_kb); + + bad_kb_app_show_loading_popup(bad_kb, true); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_mkdir(storage, BAD_KB_APP_BASE_FOLDER); + furi_record_close(RECORD_STORAGE); + bad_kb_app_show_loading_popup(bad_kb, false); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_KB_APP_SCRIPT_EXTENSION, &I_badusb_10px); + browser_options.base_path = BAD_USB_APP_BASE_FOLDER; + browser_options.skip_assets = true; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_kb->dialogs, bad_kb->file_path, bad_kb->file_path, &browser_options); + + return res; +} + +void bad_kb_scene_file_select_on_enter(void* context) { + BadKbApp* bad_kb = context; + + if(bad_kb->bad_kb_script) { + bad_kb_script_close(bad_kb->bad_kb_script); + bad_kb->bad_kb_script = NULL; + } + + if(bad_kb_file_select(bad_kb)) { + bad_kb->bad_kb_script = + bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL, bad_kb); + bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); + + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork); + } else { + view_dispatcher_stop(bad_kb->view_dispatcher); + } +} + +bool bad_kb_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void bad_kb_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/base_pack/bad_kb/scenes/bad_kb_scene_work.c b/base_pack/bad_kb/scenes/bad_kb_scene_work.c new file mode 100644 index 00000000000..bea285e5eea --- /dev/null +++ b/base_pack/bad_kb/scenes/bad_kb_scene_work.c @@ -0,0 +1,59 @@ +#include "../helpers/ducky_script.h" +#include "../bad_kb_app_i.h" +#include "../views/bad_kb_view.h" +#include +#include "toolbox/path.h" + +void bad_kb_scene_work_button_callback(InputKey key, void* context) { + furi_assert(context); + BadKbApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, key); +} + +bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) { + BadKbApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InputKeyLeft) { + if(bad_kb_is_idle_state(app->bad_kb_view)) { + scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); + } + consumed = true; + } else if(event.event == InputKeyOk) { + bad_kb_script_start_stop(app->bad_kb_script); + consumed = true; + } else if(event.event == InputKeyRight) { + bad_kb_script_pause_resume(app->bad_kb_script); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + bad_kb_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script)); + } + return consumed; +} + +void bad_kb_scene_work_on_enter(void* context) { + BadKbApp* app = context; + + FuriString* file_name; + file_name = furi_string_alloc(); + path_extract_filename(app->file_path, file_name, true); + bad_kb_set_file_name(app->bad_kb_view, furi_string_get_cstr(file_name)); + furi_string_free(file_name); + + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_kb_set_layout(app->bad_kb_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + + bad_kb_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script)); + + bad_kb_set_button_callback(app->bad_kb_view, bad_kb_scene_work_button_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWork); +} + +void bad_kb_scene_work_on_exit(void* context) { + UNUSED(context); +} diff --git a/base_pack/bad_kb/views/bad_kb_view.c b/base_pack/bad_kb/views/bad_kb_view.c new file mode 100644 index 00000000000..a5dea1a19fe --- /dev/null +++ b/base_pack/bad_kb/views/bad_kb_view.c @@ -0,0 +1,299 @@ +#include "../bad_kb_app_i.h" +#include "bad_kb_view.h" +#include "../helpers/ducky_script.h" +#include +#include +#include +#include + +#define MAX_NAME_LEN 64 + +struct BadKb { + View* view; + BadKbButtonCallback callback; + void* context; +}; + +typedef struct { + char file_name[MAX_NAME_LEN]; + char layout[MAX_NAME_LEN]; + BadKbState state; + bool pause_wait; + uint8_t anim_frame; +} BadKbModel; + +static void bad_kb_draw_callback(Canvas* canvas, void* _model) { + BadKbModel* model = _model; + BadKbWorkerState state = model->state.state; + + FuriString* disp_str = furi_string_alloc_set( + state == BadKbStateInit ? "( . . . )" : + model->state.is_bt ? "(BT) " : + "(USB) "); + furi_string_cat_str(disp_str, model->file_name); + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + + if(strlen(model->layout) == 0) { + furi_string_set(disp_str, "(default)"); + } else { + furi_string_printf(disp_str, "(%s)", model->layout); + } + if(model->state.pin) { + furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin); + } else { + uint32_t e = model->state.elapsed; + furi_string_cat_printf(disp_str, " %02lu:%02lu.%ld", e / 60 / 1000, e / 1000, e % 1000); + } + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_draw_str( + canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); + + furi_string_reset(disp_str); + + canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); + + if((state == BadKbStateIdle) || (state == BadKbStateDone) || + (state == BadKbStateNotConnected)) { + elements_button_center(canvas, "Run"); + elements_button_left(canvas, "Config"); + } else if((state == BadKbStateRunning) || (state == BadKbStateDelay)) { + elements_button_center(canvas, "Stop"); + if(!model->pause_wait) { + elements_button_right(canvas, "Pause"); + } + } else if(state == BadKbStatePaused) { + elements_button_center(canvas, "End"); + elements_button_right(canvas, "Resume"); + } else if(state == BadKbStateWaitForBtn) { + elements_button_center(canvas, "Press to continue"); + } else if(state == BadKbStateWillRun) { + elements_button_center(canvas, "Cancel"); + } + + if(state == BadKbStateNotConnected) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device"); + } else if(state == BadKbStateWillRun) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); + } else if(state == BadKbStateFileError) { + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); + } else if(state == BadKbStateScriptError) { + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); + furi_string_printf(disp_str, "line %zu", model->state.error_line); + canvas_draw_str_aligned( + canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_set_str(disp_str, model->state.error); + elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); + canvas_draw_str_aligned( + canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); + } else if(state == BadKbStateIdle) { + canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); + furi_string_printf(disp_str, "0/%zu", model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); + } else if(state == BadKbStateRunning) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); + } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); + } else if(state == BadKbStateDone) { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + furi_string_printf(disp_str, "%zu/%zu", model->state.line_nb, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 112, 37, AlignRight, AlignBottom, "100"); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); + } else if(state == BadKbStateDelay) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); + } + uint32_t delay = model->state.delay_remain / 10; + if(delay) { + furi_string_printf(disp_str, "Delay %lus", delay); + canvas_draw_str_aligned( + canvas, 4, 61, AlignLeft, AlignBottom, furi_string_get_cstr(disp_str)); + } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); + } else if((state == BadKbStatePaused) || (state == BadKbStateWaitForBtn)) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); + } + if(state != BadKbStateWaitForBtn) { + canvas_draw_str_aligned(canvas, 4, 61, AlignLeft, AlignBottom, "Paused"); + } + furi_string_printf(disp_str, "%zu/%zu", model->state.line_cur, model->state.line_nb); + canvas_draw_str_aligned( + canvas, 124, 47, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%zu", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 112, 37, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas_draw_icon(canvas, 115, 23, &I_Percent_10x14); + } else { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + } + + furi_string_free(disp_str); +} + +static bool bad_kb_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BadKb* bad_kb = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + consumed = true; + furi_assert(bad_kb->callback); + bad_kb->callback(event->key, bad_kb->context); + } else if(event->key == InputKeyOk) { + with_view_model( + bad_kb->view, BadKbModel * model, { model->pause_wait = false; }, true); + consumed = true; + furi_assert(bad_kb->callback); + bad_kb->callback(event->key, bad_kb->context); + } else if(event->key == InputKeyRight) { + with_view_model( + bad_kb->view, + BadKbModel * model, + { + if((model->state.state == BadKbStateRunning) || + (model->state.state == BadKbStateDelay)) { + model->pause_wait = true; + } + }, + true); + consumed = true; + furi_assert(bad_kb->callback); + bad_kb->callback(event->key, bad_kb->context); + } + } + + return consumed; +} + +BadKb* bad_kb_alloc() { + BadKb* bad_kb = malloc(sizeof(BadKb)); + + bad_kb->view = view_alloc(); + view_allocate_model(bad_kb->view, ViewModelTypeLocking, sizeof(BadKbModel)); + view_set_context(bad_kb->view, bad_kb); + view_set_draw_callback(bad_kb->view, bad_kb_draw_callback); + view_set_input_callback(bad_kb->view, bad_kb_input_callback); + + return bad_kb; +} + +void bad_kb_free(BadKb* bad_kb) { + furi_assert(bad_kb); + view_free(bad_kb->view); + free(bad_kb); +} + +View* bad_kb_get_view(BadKb* bad_kb) { + furi_assert(bad_kb); + return bad_kb->view; +} + +void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context) { + furi_assert(bad_kb); + furi_assert(callback); + with_view_model( + bad_kb->view, + BadKbModel * model, + { + UNUSED(model); + bad_kb->callback = callback; + bad_kb->context = context; + }, + true); +} + +void bad_kb_set_file_name(BadKb* bad_kb, const char* name) { + furi_assert(name); + with_view_model( + bad_kb->view, BadKbModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true); +} + +void bad_kb_set_layout(BadKb* bad_kb, const char* layout) { + furi_assert(layout); + with_view_model( + bad_kb->view, BadKbModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true); +} + +void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) { + furi_assert(st); + uint32_t pin = 0; + if(bad_kb->context != NULL) { + BadKbApp* app = bad_kb->context; + if(app->bt != NULL) { + pin = app->bt->pin; + } + } + st->pin = pin; + with_view_model( + bad_kb->view, + BadKbModel * model, + { + memcpy(&(model->state), st, sizeof(BadKbState)); + model->anim_frame ^= 1; + if(model->state.state == BadKbStatePaused) { + model->pause_wait = false; + } + }, + true); +} + +bool bad_kb_is_idle_state(BadKb* bad_kb) { + bool is_idle = false; + with_view_model( + bad_kb->view, + BadKbModel * model, + { + if((model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone) || + (model->state.state == BadKbStateNotConnected)) { + is_idle = true; + } + }, + false); + return is_idle; +} diff --git a/base_pack/bad_kb/views/bad_kb_view.h b/base_pack/bad_kb/views/bad_kb_view.h new file mode 100644 index 00000000000..d8f2559bb33 --- /dev/null +++ b/base_pack/bad_kb/views/bad_kb_view.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include "../helpers/ducky_script.h" + +typedef struct BadKb BadKb; +typedef void (*BadKbButtonCallback)(InputKey key, void* context); + +BadKb* bad_kb_alloc(); + +void bad_kb_free(BadKb* bad_kb); + +View* bad_kb_get_view(BadKb* bad_kb); + +void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context); + +void bad_kb_set_file_name(BadKb* bad_kb, const char* name); + +void bad_kb_set_layout(BadKb* bad_kb, const char* layout); + +void bad_kb_set_state(BadKb* bad_kb, BadKbState* st); + +bool bad_kb_is_idle_state(BadKb* bad_kb);