From b1f581239bbc83af1720e7180e6bf4a7c319288e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:01:00 +0300 Subject: [PATCH] BadUSB: Keyboard Layouts (#2256) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BadUSB: Keyboard Layouts * Apply requested changes pt1 * Add layout file check when we loading config Co-authored-by: Nikolay Minaylov Co-authored-by: あく --- applications/main/bad_usb/bad_usb_app.c | 72 +++++++++++++- applications/main/bad_usb/bad_usb_app_i.h | 11 ++- applications/main/bad_usb/bad_usb_script.c | 51 ++++++++-- applications/main/bad_usb/bad_usb_script.h | 2 + .../main/bad_usb/bad_usb_settings_filename.h | 3 + .../bad_usb/scenes/bad_usb_scene_config.c | 53 ++++++++++ .../bad_usb/scenes/bad_usb_scene_config.h | 2 + .../scenes/bad_usb_scene_config_layout.c | 50 ++++++++++ .../scenes/bad_usb_scene_file_select.c | 14 ++- .../main/bad_usb/scenes/bad_usb_scene_work.c | 27 ++++-- .../main/bad_usb/views/bad_usb_view.c | 91 ++++++++++++------ .../main/bad_usb/views/bad_usb_view.h | 6 +- assets/icons/Archive/keyboard_10px.png | Bin 0 -> 147 bytes .../resources/badusb/assets/layouts/ba-BA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/cz_CS.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/da-DA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-DE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/dvorak.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-UK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-US.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/es-ES.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-BE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-FR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hr-HR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hu-HU.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/it-IT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nb-NO.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nl-NL.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-BR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-PT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/si-SI.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sk-SK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sv-SE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/tr-TR.kl | Bin 0 -> 256 bytes 36 files changed, 323 insertions(+), 59 deletions(-) create mode 100644 applications/main/bad_usb/bad_usb_settings_filename.h create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c create mode 100644 assets/icons/Archive/keyboard_10px.png create mode 100644 assets/resources/badusb/assets/layouts/ba-BA.kl create mode 100644 assets/resources/badusb/assets/layouts/cz_CS.kl create mode 100644 assets/resources/badusb/assets/layouts/da-DA.kl create mode 100644 assets/resources/badusb/assets/layouts/de-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/de-DE.kl create mode 100644 assets/resources/badusb/assets/layouts/dvorak.kl create mode 100644 assets/resources/badusb/assets/layouts/en-UK.kl create mode 100644 assets/resources/badusb/assets/layouts/en-US.kl create mode 100644 assets/resources/badusb/assets/layouts/es-ES.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-BE.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-FR.kl create mode 100644 assets/resources/badusb/assets/layouts/hr-HR.kl create mode 100644 assets/resources/badusb/assets/layouts/hu-HU.kl create mode 100644 assets/resources/badusb/assets/layouts/it-IT.kl create mode 100644 assets/resources/badusb/assets/layouts/nb-NO.kl create mode 100644 assets/resources/badusb/assets/layouts/nl-NL.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-BR.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-PT.kl create mode 100644 assets/resources/badusb/assets/layouts/si-SI.kl create mode 100755 assets/resources/badusb/assets/layouts/sk-SK.kl create mode 100644 assets/resources/badusb/assets/layouts/sv-SE.kl create mode 100644 assets/resources/badusb/assets/layouts/tr-TR.kl diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index 6fd29cd7..5f2aa478 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -1,9 +1,12 @@ #include "bad_usb_app_i.h" +#include "bad_usb_settings_filename.h" #include #include #include #include +#define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME + static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); BadUsbApp* app = context; @@ -22,15 +25,62 @@ static void bad_usb_app_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } +static void bad_usb_load_settings(BadUsbApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + char chr; + while((storage_file_read(settings_file, &chr, 1) == 1) && + !storage_file_eof(settings_file) && !isspace(chr)) { + furi_string_push_back(app->keyboard_layout, chr); + } + } else { + furi_string_reset(app->keyboard_layout); + } + storage_file_close(settings_file); + storage_file_free(settings_file); + + if(!furi_string_empty(app->keyboard_layout)) { + Storage* fs_api = furi_record_open(RECORD_STORAGE); + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat( + fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); + furi_record_close(RECORD_STORAGE); + if(file_check_err != FSE_OK) { + furi_string_reset(app->keyboard_layout); + return; + } + if(layout_file_info.size != 256) { + furi_string_reset(app->keyboard_layout); + } + } +} + +static void bad_usb_save_settings(BadUsbApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + storage_file_write( + settings_file, + furi_string_get_cstr(app->keyboard_layout), + furi_string_size(app->keyboard_layout)); + storage_file_write(settings_file, "\n", 1); + } + storage_file_close(settings_file); + storage_file_free(settings_file); +} + BadUsbApp* bad_usb_app_alloc(char* arg) { BadUsbApp* app = malloc(sizeof(BadUsbApp)); - app->file_path = furi_string_alloc(); + app->bad_usb_script = NULL; + app->file_path = furi_string_alloc(); + app->keyboard_layout = furi_string_alloc(); if(arg && strlen(arg)) { furi_string_set(app->file_path, arg); } + bad_usb_load_settings(app); + app->gui = furi_record_open(RECORD_GUI); app->notifications = furi_record_open(RECORD_NOTIFICATION); app->dialogs = furi_record_open(RECORD_DIALOGS); @@ -53,6 +103,10 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget)); + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu)); + app->bad_usb_view = bad_usb_alloc(); view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view)); @@ -64,9 +118,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { scene_manager_next_scene(app->scene_manager, BadUsbSceneError); } else { if(!furi_string_empty(app->file_path)) { + app->bad_usb_script = bad_usb_script_open(app->file_path); + bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); } else { - furi_string_set(app->file_path, BAD_USB_APP_PATH_FOLDER); + furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER); scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); } } @@ -77,6 +133,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { void bad_usb_app_free(BadUsbApp* app) { furi_assert(app); + if(app->bad_usb_script) { + bad_usb_script_close(app->bad_usb_script); + app->bad_usb_script = NULL; + } + // Views view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); bad_usb_free(app->bad_usb_view); @@ -85,6 +146,10 @@ void bad_usb_app_free(BadUsbApp* app) { view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError); widget_free(app->widget); + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig); + submenu_free(app->submenu); + // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); @@ -94,7 +159,10 @@ void bad_usb_app_free(BadUsbApp* app) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); + bad_usb_save_settings(app); + furi_string_free(app->file_path); + furi_string_free(app->keyboard_layout); free(app); } diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index f333c36d..1bd1964c 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -15,8 +15,10 @@ #include #include "views/bad_usb_view.h" -#define BAD_USB_APP_PATH_FOLDER ANY_PATH("badusb") -#define BAD_USB_APP_EXTENSION ".txt" +#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb") +#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts" +#define BAD_USB_APP_SCRIPT_EXTENSION ".txt" +#define BAD_USB_APP_LAYOUT_EXTENSION ".kl" typedef enum { BadUsbAppErrorNoFiles, @@ -30,9 +32,11 @@ struct BadUsbApp { NotificationApp* notifications; DialogsApp* dialogs; Widget* widget; + Submenu* submenu; BadUsbAppError error; FuriString* file_path; + FuriString* keyboard_layout; BadUsb* bad_usb_view; BadUsbScript* bad_usb_script; }; @@ -40,4 +44,5 @@ struct BadUsbApp { typedef enum { BadUsbAppViewError, BadUsbAppViewWork, -} BadUsbAppView; + BadUsbAppViewConfig, +} BadUsbAppView; \ No newline at end of file diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index e2281133..1416acfe 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -16,6 +16,9 @@ #define SCRIPT_STATE_END (-2) #define SCRIPT_STATE_NEXT_LINE (-3) +#define BADUSB_ASCII_TO_KEY(script, x) \ + (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) + typedef enum { WorkerEvtToggle = (1 << 0), WorkerEvtEnd = (1 << 1), @@ -28,6 +31,7 @@ struct BadUsbScript { BadUsbState st; FuriString* file_path; uint32_t defdelay; + uint16_t layout[128]; FuriThread* thread; uint8_t file_buf[FILE_BUFFER_LEN + 1]; uint8_t buf_start; @@ -205,10 +209,10 @@ static bool ducky_altstring(const char* param) { return state; } -static bool ducky_string(const char* param) { +static bool ducky_string(BadUsbScript* bad_usb, const char* param) { uint32_t i = 0; while(param[i] != '\0') { - uint16_t keycode = HID_ASCII_TO_KEY(param[i]); + uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); if(keycode != HID_KEYBOARD_NONE) { furi_hal_hid_kb_press(keycode); furi_hal_hid_kb_release(keycode); @@ -218,7 +222,7 @@ static bool ducky_string(const char* param) { return true; } -static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { +static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) { for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { size_t key_cmd_len = strlen(ducky_keys[i].name); if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && @@ -227,7 +231,7 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { } } if((accept_chars) && (strlen(param) > 0)) { - return (HID_ASCII_TO_KEY(param[0]) & 0xFF); + return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF); } return 0; } @@ -276,7 +280,7 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { // STRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_string(line_tmp); + state = ducky_string(bad_usb, line_tmp); if(!state && error != NULL) { snprintf(error, error_len, "Invalid string %s", line_tmp); } @@ -312,14 +316,14 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) { // SYSRQ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - uint16_t key = ducky_get_keycode(line_tmp, true); + uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true); furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_hid_kb_press(key); furi_hal_hid_kb_release_all(); return (0); } else { // Special keys + modifiers - uint16_t key = ducky_get_keycode(line_tmp, false); + uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false); if(key == HID_KEYBOARD_NONE) { if(error != NULL) { snprintf(error, error_len, "No keycode defined for %s", line_tmp); @@ -329,7 +333,7 @@ static int32_t if((key & 0xFF00) != 0) { // It's a modifier key line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - key |= ducky_get_keycode(line_tmp, true); + key |= ducky_get_keycode(bad_usb, line_tmp, true); } furi_hal_hid_kb_press(key); furi_hal_hid_kb_release(key); @@ -650,12 +654,19 @@ static int32_t bad_usb_worker(void* context) { return 0; } +static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout)); + memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout))); +} + BadUsbScript* bad_usb_script_open(FuriString* file_path) { furi_assert(file_path); BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); bad_usb->file_path = furi_string_alloc(); furi_string_set(bad_usb->file_path, file_path); + bad_usb_script_set_default_keyboard_layout(bad_usb); bad_usb->st.state = BadUsbStateInit; bad_usb->st.error[0] = '\0'; @@ -674,6 +685,30 @@ void bad_usb_script_close(BadUsbScript* bad_usb) { free(bad_usb); } +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) { + furi_assert(bad_usb); + + if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) { + // do not update keyboard layout while a script is running + return; + } + + File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(!furi_string_empty(layout_path)) { + if(storage_file_open( + layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(bad_usb->layout, layout, sizeof(layout)); + } + } + storage_file_close(layout_file); + } else { + bad_usb_script_set_default_keyboard_layout(bad_usb); + } + storage_file_free(layout_file); +} + void bad_usb_script_toggle(BadUsbScript* bad_usb) { furi_assert(bad_usb); furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle); diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h index 188142db..1e4d98fe 100644 --- a/applications/main/bad_usb/bad_usb_script.h +++ b/applications/main/bad_usb/bad_usb_script.h @@ -33,6 +33,8 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path); void bad_usb_script_close(BadUsbScript* bad_usb); +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path); + void bad_usb_script_start(BadUsbScript* bad_usb); void bad_usb_script_stop(BadUsbScript* bad_usb); diff --git a/applications/main/bad_usb/bad_usb_settings_filename.h b/applications/main/bad_usb/bad_usb_settings_filename.h new file mode 100644 index 00000000..12ba8f31 --- /dev/null +++ b/applications/main/bad_usb/bad_usb_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define BAD_USB_SETTINGS_FILE_NAME ".badusb.settings" diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c new file mode 100644 index 00000000..2a9f2f76 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.c @@ -0,0 +1,53 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum SubmenuIndex { + SubmenuIndexKeyboardLayout, +}; + +void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) { + BadUsbApp* bad_usb = context; + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); +} + +void bad_usb_scene_config_on_enter(void* context) { + BadUsbApp* bad_usb = context; + Submenu* submenu = bad_usb->submenu; + + submenu_add_item( + submenu, + "Keyboard layout", + SubmenuIndexKeyboardLayout, + bad_usb_scene_config_submenu_callback, + bad_usb); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig); +} + +bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); + consumed = true; + if(event.event == SubmenuIndexKeyboardLayout) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + } else { + furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_usb_scene_config_on_exit(void* context) { + BadUsbApp* bad_usb = context; + Submenu* submenu = bad_usb->submenu; + + submenu_reset(submenu); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h index 0ab8f54f..423aedc5 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.h +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.h @@ -1,3 +1,5 @@ ADD_SCENE(bad_usb, file_select, FileSelect) ADD_SCENE(bad_usb, work, Work) ADD_SCENE(bad_usb, error, Error) +ADD_SCENE(bad_usb, config, Config) +ADD_SCENE(bad_usb, config_layout, ConfigLayout) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c new file mode 100644 index 00000000..7708ed1d --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c @@ -0,0 +1,50 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" +#include + +static bool bad_usb_layout_select(BadUsbApp* bad_usb) { + furi_assert(bad_usb); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_usb->keyboard_layout)) { + furi_string_set(predefined_path, bad_usb->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + browser_options.base_path = BAD_USB_APP_PATH_LAYOUT_FOLDER; + browser_options.skip_assets = false; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_usb_scene_config_layout_on_enter(void* context) { + BadUsbApp* bad_usb = context; + + if(bad_usb_layout_select(bad_usb)) { + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); + } + scene_manager_previous_scene(bad_usb->scene_manager); +} + +bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadUsbApp* bad_usb = context; + return false; +} + +void bad_usb_scene_config_layout_on_exit(void* context) { + UNUSED(context); + // BadUsbApp* bad_usb = context; +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index f1f34f5b..b0466925 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -7,8 +7,10 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) { furi_assert(bad_usb); DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, BAD_USB_APP_EXTENSION, &I_badusb_10px); - browser_options.base_path = BAD_USB_APP_PATH_FOLDER; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px); + browser_options.base_path = BAD_USB_APP_BASE_FOLDER; + browser_options.skip_assets = true; // Input events and views are managed by file_browser bool res = dialog_file_browser_show( @@ -21,12 +23,18 @@ void bad_usb_scene_file_select_on_enter(void* context) { BadUsbApp* bad_usb = context; furi_hal_usb_disable(); + if(bad_usb->bad_usb_script) { + bad_usb_script_close(bad_usb->bad_usb_script); + bad_usb->bad_usb_script = NULL; + } if(bad_usb_file_select(bad_usb)) { + bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path); + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); } else { furi_hal_usb_enable(); - //scene_manager_previous_scene(bad_usb->scene_manager); view_dispatcher_stop(bad_usb->view_dispatcher); } } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 1e353482..187b83bd 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -4,10 +4,10 @@ #include #include "toolbox/path.h" -void bad_usb_scene_work_ok_callback(InputType type, void* context) { +void bad_usb_scene_work_button_callback(InputKey key, void* context) { furi_assert(context); BadUsbApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, type); + view_dispatcher_send_custom_event(app->view_dispatcher, key); } bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { @@ -15,8 +15,13 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - bad_usb_script_toggle(app->bad_usb_script); - consumed = true; + if(event.event == InputKeyLeft) { + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); + consumed = true; + } else if(event.event == InputKeyOk) { + bad_usb_script_toggle(app->bad_usb_script); + consumed = true; + } } else if(event.type == SceneManagerEventTypeTick) { bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); } @@ -28,20 +33,22 @@ void bad_usb_scene_work_on_enter(void* context) { FuriString* file_name; file_name = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); bad_usb_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name)); - app->bad_usb_script = bad_usb_script_open(app->file_path); - furi_string_free(file_name); + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_usb_set_layout(app->bad_usb_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); - bad_usb_set_ok_callback(app->bad_usb_view, bad_usb_scene_work_ok_callback, app); + bad_usb_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork); } void bad_usb_scene_work_on_exit(void* context) { - BadUsbApp* app = context; - bad_usb_script_close(app->bad_usb_script); + UNUSED(context); } diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index b3eb9bb5..bb9dc3b7 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -1,5 +1,6 @@ #include "bad_usb_view.h" #include "../bad_usb_script.h" +#include #include #include @@ -7,12 +8,13 @@ struct BadUsb { View* view; - BadUsbOkCallback callback; + BadUsbButtonCallback callback; void* context; }; typedef struct { char file_name[MAX_NAME_LEN]; + char layout[MAX_NAME_LEN]; BadUsbState state; uint8_t anim_frame; } BadUsbModel; @@ -25,9 +27,23 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + + if(strlen(model->layout) == 0) { + furi_string_set(disp_str, "(default)"); + } else { + furi_string_reset(disp_str); + furi_string_push_back(disp_str, '('); + for(size_t i = 0; i < strlen(model->layout); i++) + furi_string_push_back(disp_str, model->layout[i]); + furi_string_push_back(disp_str, ')'); + } + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_draw_str( + canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); - canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22); + canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { @@ -38,23 +54,28 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_button_center(canvas, "Cancel"); } + if((model->state.state == BadUsbStateNotConnected) || + (model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { + elements_button_left(canvas, "Config"); + } + if(model->state.state == BadUsbStateNotConnected) { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB"); } else if(model->state.state == BadUsbStateWillRun) { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Will run"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "on connect"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); } else if(model->state.state == BadUsbStateFileError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "File"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "ERROR"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); } else if(model->state.state == BadUsbStateScriptError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); canvas_set_font(canvas, FontSecondary); @@ -64,49 +85,49 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { furi_string_reset(disp_str); canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error); } else if(model->state.state == BadUsbStateIdle) { - canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateRunning) { if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile2_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); } canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 36, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateDone) { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "100"); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateDelay) { if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 19, &I_EviWaiting1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { - canvas_draw_icon(canvas, 4, 19, &I_EviWaiting2_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 36, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); canvas_set_font(canvas, FontSecondary); furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); canvas_draw_str_aligned( - canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); } else { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); } furi_string_free(disp_str); @@ -118,10 +139,10 @@ static bool bad_usb_input_callback(InputEvent* event, void* context) { bool consumed = false; if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { + if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { consumed = true; furi_assert(bad_usb->callback); - bad_usb->callback(InputTypeShort, bad_usb->context); + bad_usb->callback(event->key, bad_usb->context); } } @@ -151,7 +172,7 @@ View* bad_usb_get_view(BadUsb* bad_usb) { return bad_usb->view; } -void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context) { +void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context) { furi_assert(bad_usb); furi_assert(callback); with_view_model( @@ -174,6 +195,14 @@ void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { true); } +void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) { + furi_assert(layout); + with_view_model( + bad_usb->view, + BadUsbModel * model, + { strlcpy(model->layout, layout, MAX_NAME_LEN); }, + true); +} void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { furi_assert(st); with_view_model( diff --git a/applications/main/bad_usb/views/bad_usb_view.h b/applications/main/bad_usb/views/bad_usb_view.h index 80a47e2c..8447fb05 100644 --- a/applications/main/bad_usb/views/bad_usb_view.h +++ b/applications/main/bad_usb/views/bad_usb_view.h @@ -4,7 +4,7 @@ #include "../bad_usb_script.h" typedef struct BadUsb BadUsb; -typedef void (*BadUsbOkCallback)(InputType type, void* context); +typedef void (*BadUsbButtonCallback)(InputKey key, void* context); BadUsb* bad_usb_alloc(); @@ -12,8 +12,10 @@ void bad_usb_free(BadUsb* bad_usb); View* bad_usb_get_view(BadUsb* bad_usb); -void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context); +void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context); void bad_usb_set_file_name(BadUsb* bad_usb, const char* name); +void bad_usb_set_layout(BadUsb* bad_usb, const char* layout); + void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st); diff --git a/assets/icons/Archive/keyboard_10px.png b/assets/icons/Archive/keyboard_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..74a10e6db2e784486a6781c0db8346373b9c7409 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIasB`QKad%E=yDy9;wgN=z( mnGc5;aHhO3XW%q4U|?wcz_Hx?k)a$=ErX}4pUXO@geCyYPARJZ literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/ba-BA.kl b/assets/resources/badusb/assets/layouts/ba-BA.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHkcVK4j(2-*&PMujecVX$$ zm1{Rv)^6RqGmA3vIK<6U^hikQGaw^p$cQl$rpzdqv!GYm`cD?ePm*T4GZVB*m| zTln^pGi$gvZr&YZh&>B_Ym yx9;4VM-@dp{CKxUod!)>wCT{LN1p*hMvR#-WyYKZOIEDeuw}=dEbdXpBj69fFAeVi literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-CH.kl b/assets/resources/badusb/assets/layouts/de-CH.kl new file mode 100644 index 0000000000000000000000000000000000000000..1704bc9dbaae9fcc90213cb33b0132bca1b71866 GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5i<8!$oa;st31l>ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-DE.kl b/assets/resources/badusb/assets/layouts/de-DE.kl new file mode 100644 index 0000000000000000000000000000000000000000..67b05c042f1649ab6aa17c4d369b096c88ec2785 GIT binary patch literal 256 zcmaLLHx9y30KiboqW2Pd4WuBN!ay1SVRHX7Ftgz88Gbu+;q<%y>dMxg4`L4uK0?Afwu&6#5(8&;hd+qL7+fvJ5TwhjIGHgM$E s3wH`~pT4NjqeYV%GsZ+Ts4^j@ONSu?Qu;h-6Y}9rz=$sw?-ss^@Y|jPyWjO!M`q5wk-2p3gN<8r z&%QYE=EIg5a~5PQS+OQ(!`(YC&%FKkuw>baRci*;Z5Z0LW!sLCv58$%d-feTbmZ8H tQ!f-;S$XnFi82+c)Cj24Af!o)HXR~j61t@H=rdr*h%pnUTr9kL6TT^X3*7(! literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/en-US.kl b/assets/resources/badusb/assets/layouts/en-US.kl new file mode 100644 index 0000000000000000000000000000000000000000..8089d8257881765fe67691a206b6bf6e9cb2c97d GIT binary patch literal 256 zcmaLL#}dH+007aQ9YGK+QKLn)QG!wb|I3_nvA4HS?#PKldHvOyg-=&zuHE=#=iaS_ zZ!UcJvS-1P6&Y)CHf-7P@WI=Hk6)Ko46ItSZfL`%EhF1@?Ao($Y~sM7BgamhnmTjt sl{+_bFTSV{P^CtlkOoa!M6~J9rAMEbgaJcFjF~VcWyYMVcW=Ig9}}MoYybcN literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/es-ES.kl b/assets/resources/badusb/assets/layouts/es-ES.kl new file mode 100644 index 0000000000000000000000000000000000000000..15e9d7997c3f5178982fe212b5341f09469486bc GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5lA#$@e+Y@}VF{DLgOx;B0)}2=l z9xW`m^W?$a6A39BGPdm4bKuCsyDv_@yqZSIvK6b=tlO|@%eEc6_Ut=w=*Y1Xr_P+a zaOujmdHhEaxleOSl&MgqMx6#tTD0lVrAMCuLq?35FlEM^1xr?}`HLiOkp;W~w>u5X literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-BE.kl b/assets/resources/badusb/assets/layouts/fr-BE.kl new file mode 100644 index 0000000000000000000000000000000000000000..ea9e553e894a470639ee48648a386898ed5cfa67 GIT binary patch literal 256 zcmaLLNm9Z96adk#F6Mcj2}uy4P%#!3Wcd%1`=7xz8+-Q}Uc2(a<#qk5PfnzyQSfU* zM$Q+7r6X=Ue0nzVU}|RW(Kk=N`{Y4IN#)2x%&gn6Y0I`9yY}omaOmB!6Q|akyKw2s zwR^Yj+?YodMSOGfCuY=X(4ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-FR.kl b/assets/resources/badusb/assets/layouts/fr-FR.kl new file mode 100644 index 0000000000000000000000000000000000000000..f9193297e58722fd4f1547cb9ef62474beb2487b GIT binary patch literal 256 zcmaLLH*Nv}6adlDh@5i{8(2U#EVi(~}3$%7XqF9)7tV%3^;8#Zm(wqw_xeeVt(Ikw`|nR6E| z-MV(;$}Ebw#*KnQOsG<$PJ<>b+H~mBqtBfoBgRyiGGoqyC0o{PSdm5^W$Xj~0L-fm A_W%F@ literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/hr-HR.kl b/assets/resources/badusb/assets/layouts/hr-HR.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk)x=fVv{{L5{w#jy9;k{43_;_Fc>YIyakMR>% z$I*KC;Silq2439!rR7Y=g)29D?mT$;j68aqcv{8C#IX~nre@BZyKw2s+`_dROSkUa zd$6+hXyeH=%E+RN!9GSLjF~VcC1b{%1xr@s6s*}$vSr7f0~Iw#8crlpM-gqnA2aL@ AGynhq literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/it-IT.kl b/assets/resources/badusb/assets/layouts/it-IT.kl new file mode 100644 index 0000000000000000000000000000000000000000..059e428808a07b26d0da37d7f37d5d8d4614365b GIT binary patch literal 256 zcmaKnw++Go0KhCO)P&wbCv;IGQOAf|`2PmP$bvU}GyJk+&-OR{wF48UUbDox3y-W^ zo0@Xx$%CaECQO-;FlWJ%6>A>eeY5fH(MuMXGjGA7CCi3JR;*gHZo{T6+ji{QGq&%* yp(Dqhvm0r4@+k!5$Wx$5i83J(6{^&z)1XO96ECB#K@^L=Pq0t xyK-$Ft`y%!)Nxcub+H~j=&?BVJfFUEsOqeobPMlv%@{{ldJ?{*0 literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/pt-BR.kl b/assets/resources/badusb/assets/layouts/pt-BR.kl new file mode 100644 index 0000000000000000000000000000000000000000..d36421cfc45747687dba3cabe05ff84a4550050a GIT binary patch literal 256 zcmaKnM-ssR004J(1W9z!q7!8hWkx%CjQ_uZtBbwu-WGn@v1j|6{@Q_wH)qaWn0aIE z#+66!wV)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk17S!45fq0~DYekR@*l?gpTRX7d-ob{d*zLn+x4&B`SxrR z=Qx?hB0l}{*Orbe7e0Kp_0GKq?|ty-qfcJ^p^Fo{=&Yh=Vrt)knYlwpj-5DlX5rk0 zrAt??-B?-MxN~b3W#qAstHmjD0& literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/sv-SE.kl b/assets/resources/badusb/assets/layouts/sv-SE.kl new file mode 100644 index 0000000000000000000000000000000000000000..5c55bb9ef1df3785fb143eb463a5375be10ce96f GIT binary patch literal 256 zcmaLLH*x|26u?l8hMaSTC2U}9uLl~S{D;~5pMjbNbx-iw55HVL*H`^JB+m2m(_8mBMwCT{LN1p*hMvR#-WyYKZOIEDeuw}=dG^!}#7VrkLXASNE literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/tr-TR.kl b/assets/resources/badusb/assets/layouts/tr-TR.kl new file mode 100644 index 0000000000000000000000000000000000000000..6377b770365ed544824108765ce2587f315bc1da GIT binary patch literal 256 zcmaLLM{dFZ6adlDh~A6oU~F)Sk{HDcB$od$x&IlmW|P^yf%iW7;^Te&t8Y@DWRb_s zyH`sq8**D7yeOY}m46&w(Q+&Ri(sH|qEa_ygc8 B4s!qi literal 0 HcmV?d00001