1515#include "ducky_parser.h"
1616#include "bad_usb.h"
1717#include "esp_log.h"
18+ // Assuming ble_hid_keyboard.h is available in include path via CMake
19+ #include "ble_hid_keyboard.h"
1820#include "freertos/FreeRTOS.h"
1921#include "freertos/task.h"
2022#include "class/hid/hid_device.h"
@@ -29,6 +31,12 @@ static const char *TAG = "DUCKY_PARSER";
2931static volatile bool s_abort_flag = false;
3032static ducky_progress_cb_t s_progress_cb = NULL ;
3133static ducky_layout_t s_layout = DUCKY_LAYOUT_US ;
34+ static ducky_output_mode_t s_output_mode = DUCKY_OUTPUT_USB ;
35+
36+ void ducky_set_output_mode (ducky_output_mode_t mode ) {
37+ s_output_mode = mode ;
38+ ESP_LOGI (TAG , "Output mode set to: %s" , mode == DUCKY_OUTPUT_USB ? "USB" : "BLUETOOTH" );
39+ }
3240
3341void ducky_set_progress_callback (ducky_progress_cb_t cb ) {
3442 s_progress_cb = cb ;
@@ -134,6 +142,14 @@ static bool is_modifier(const char* word, uint8_t* current_mod) {
134142 return false;
135143}
136144
145+ static void press_key_wrapper (uint8_t keycode , uint8_t modifiers ) {
146+ if (s_output_mode == DUCKY_OUTPUT_USB ) {
147+ bad_usb_press_key (keycode , modifiers );
148+ } else {
149+ ble_hid_send_key (keycode , modifiers );
150+ }
151+ }
152+
137153static void process_line (char * line ) {
138154 if (strlen (line ) < 2 || strncmp (line , "REM" , 3 ) == 0 ) return ;
139155
@@ -152,10 +168,152 @@ static void process_line(char* line) {
152168 else if (strcmp (cmd , "STRING" ) == 0 ) {
153169 char * next_token = strtok_r (NULL , "" , & saveptr ); // Get rest of string
154170 if (next_token ) {
155- if (s_layout == DUCKY_LAYOUT_ABNT2 ) {
156- type_string_abnt2 (next_token );
171+ // Temporarily, we need to adapt type_string functions or manually iterate here
172+ // Ideally type_string_XX should take a callback, but for now we will loop here
173+ // if mode is BT, or rely on BadUSB logic if USB.
174+
175+ // Simpler approach: Iterate char by char here using the wrapper?
176+ // No, type_string logic is complex (ABNT2).
177+ // Best approach: If USB, call type_string. If BT, implementing a simple typer loop or refactor type_string.
178+ // Let's refactor type_string usage by checking mode inside type_string functions?
179+ // No, bad_usb.c owns type_string.
180+
181+ // HACK: For now, if BT mode, we only support basic ASCII typing via simple map or
182+ // we force USB mode functions to use a callback?
183+ // Since I cannot easily modify bad_usb.c to call BLE without circular dependency,
184+ // I will implement a basic BT typer here.
185+
186+ if (s_output_mode == DUCKY_OUTPUT_USB ) {
187+ if (s_layout == DUCKY_LAYOUT_ABNT2 ) {
188+ type_string_abnt2 (next_token );
189+ } else {
190+ type_string_us (next_token );
191+ }
157192 } else {
158- type_string_us (next_token );
193+ // BLE Typing
194+ if (s_layout == DUCKY_LAYOUT_US ) {
195+ for (int i = 0 ; next_token [i ] != 0 ; i ++ ) {
196+ char c = next_token [i ];
197+ if (c >= 'a' && c <= 'z' ) press_key_wrapper (HID_KEY_A + (c - 'a' ), 0 );
198+ else if (c >= 'A' && c <= 'Z' ) press_key_wrapper (HID_KEY_A + (c - 'A' ), KEYBOARD_MODIFIER_LEFTSHIFT );
199+ else if (c >= '1' && c <= '9' ) press_key_wrapper (HID_KEY_1 + (c - '1' ), 0 );
200+ else if (c == '0' ) press_key_wrapper (HID_KEY_0 , 0 );
201+ else {
202+ switch (c ) {
203+ case ' ' : press_key_wrapper (HID_KEY_SPACE , 0 ); break ;
204+ case '!' : press_key_wrapper (HID_KEY_1 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
205+ case '@' : press_key_wrapper (HID_KEY_2 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
206+ case '#' : press_key_wrapper (HID_KEY_3 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
207+ case '$' : press_key_wrapper (HID_KEY_4 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
208+ case '%' : press_key_wrapper (HID_KEY_5 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
209+ case '^' : press_key_wrapper (HID_KEY_6 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
210+ case '&' : press_key_wrapper (HID_KEY_7 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
211+ case '*' : press_key_wrapper (HID_KEY_8 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
212+ case '(' : press_key_wrapper (HID_KEY_9 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
213+ case ')' : press_key_wrapper (HID_KEY_0 , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
214+ case '-' : press_key_wrapper (HID_KEY_MINUS , 0 ); break ;
215+ case '_' : press_key_wrapper (HID_KEY_MINUS , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
216+ case '=' : press_key_wrapper (HID_KEY_EQUAL , 0 ); break ;
217+ case '+' : press_key_wrapper (HID_KEY_EQUAL , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
218+ case '[' : press_key_wrapper (HID_KEY_BRACKET_LEFT , 0 ); break ;
219+ case '{' : press_key_wrapper (HID_KEY_BRACKET_LEFT , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
220+ case ']' : press_key_wrapper (HID_KEY_BRACKET_RIGHT , 0 ); break ;
221+ case '}' : press_key_wrapper (HID_KEY_BRACKET_RIGHT , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
222+ case '\\' : press_key_wrapper (HID_KEY_BACKSLASH , 0 ); break ;
223+ case '|' : press_key_wrapper (HID_KEY_BACKSLASH , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
224+ case ';' : press_key_wrapper (HID_KEY_SEMICOLON , 0 ); break ;
225+ case ':' : press_key_wrapper (HID_KEY_SEMICOLON , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
226+ case '\'' : press_key_wrapper (HID_KEY_APOSTROPHE , 0 ); break ;
227+ case '"' : press_key_wrapper (HID_KEY_APOSTROPHE , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
228+ case ',' : press_key_wrapper (HID_KEY_COMMA , 0 ); break ;
229+ case '<' : press_key_wrapper (HID_KEY_COMMA , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
230+ case '.' : press_key_wrapper (HID_KEY_PERIOD , 0 ); break ;
231+ case '>' : press_key_wrapper (HID_KEY_PERIOD , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
232+ case '/' : press_key_wrapper (HID_KEY_SLASH , 0 ); break ;
233+ case '?' : press_key_wrapper (HID_KEY_SLASH , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
234+ case '`' : press_key_wrapper (HID_KEY_GRAVE , 0 ); break ;
235+ case '~' : press_key_wrapper (HID_KEY_GRAVE , KEYBOARD_MODIFIER_LEFTSHIFT ); break ;
236+ case '\n' : press_key_wrapper (HID_KEY_ENTER , 0 ); break ;
237+ case '\t' : press_key_wrapper (HID_KEY_TAB , 0 ); break ;
238+ }
239+ }
240+ }
241+ } else {
242+ // BLE Typing (ABNT2 Layout)
243+ // Mirroring logic from bad_usb.c type_string_abnt2
244+ for (size_t i = 0 ; next_token [i ] != '\0' ; ++ i ) {
245+ uint8_t c1 = (uint8_t )next_token [i ];
246+ uint8_t c2 = (uint8_t )next_token [i + 1 ];
247+
248+ // Special cases for ABNT2 quotes
249+ if (c1 == '\'' ) { press_key_wrapper (HID_KEY_BRACKET_LEFT , 0 ); press_key_wrapper (HID_KEY_SPACE , 0 ); continue ; }
250+ if (c1 == '"' ) { press_key_wrapper (HID_KEY_BRACKET_LEFT , KEYBOARD_MODIFIER_LEFTSHIFT ); continue ; }
251+
252+ // UTF-8 Handling for Accents
253+ if ((c1 & 0xE0 ) == 0xC0 && c2 != '\0' ) {
254+ bool char_processed = true;
255+ if (c1 == 0xC3 && c2 == 0xA7 ) { press_key_wrapper (HID_KEY_SEMICOLON , 0 ); } // ç
256+ else if (c1 == 0xC3 && c2 == 0x87 ) { press_key_wrapper (HID_KEY_SEMICOLON , KEYBOARD_MODIFIER_LEFTSHIFT ); } // Ç
257+ else if (c1 == 0xC3 && c2 == 0xA1 ) { press_key_wrapper (HID_KEY_BRACKET_LEFT , 0 ); press_key_wrapper (HID_KEY_A , 0 ); } // á
258+ else if (c1 == 0xC3 && c2 == 0xA9 ) { press_key_wrapper (HID_KEY_BRACKET_LEFT , 0 ); press_key_wrapper (HID_KEY_E , 0 ); } // é
259+ else if (c1 == 0xC3 && c2 == 0xAD ) { press_key_wrapper (HID_KEY_BRACKET_LEFT , 0 ); press_key_wrapper (HID_KEY_I , 0 ); } // í
260+ else if (c1 == 0xC3 && c2 == 0xB3 ) { press_key_wrapper (HID_KEY_BRACKET_LEFT , 0 ); press_key_wrapper (HID_KEY_O , 0 ); } // ó
261+ else if (c1 == 0xC3 && c2 == 0xBA ) { press_key_wrapper (HID_KEY_BRACKET_LEFT , 0 ); press_key_wrapper (HID_KEY_U , 0 ); } // ú
262+ else if (c1 == 0xC3 && c2 == 0xA2 ) { press_key_wrapper (HID_KEY_APOSTROPHE , KEYBOARD_MODIFIER_LEFTSHIFT ); press_key_wrapper (HID_KEY_A , 0 ); } // â
263+ else if (c1 == 0xC3 && c2 == 0xAA ) { press_key_wrapper (HID_KEY_APOSTROPHE , KEYBOARD_MODIFIER_LEFTSHIFT ); press_key_wrapper (HID_KEY_E , 0 ); } // ê
264+ else if (c1 == 0xC3 && c2 == 0xB4 ) { press_key_wrapper (HID_KEY_APOSTROPHE , KEYBOARD_MODIFIER_LEFTSHIFT ); press_key_wrapper (HID_KEY_O , 0 ); } // ô
265+ else if (c1 == 0xC3 && c2 == 0xA3 ) { press_key_wrapper (HID_KEY_APOSTROPHE , 0 ); press_key_wrapper (HID_KEY_A , 0 ); } // ã
266+ else if (c1 == 0xC3 && c2 == 0xB5 ) { press_key_wrapper (HID_KEY_APOSTROPHE , 0 ); press_key_wrapper (HID_KEY_O , 0 ); } // õ
267+ else if (c1 == 0xC3 && c2 == 0xA0 ) { press_key_wrapper (HID_KEY_BRACKET_LEFT , KEYBOARD_MODIFIER_LEFTSHIFT ); press_key_wrapper (HID_KEY_A , 0 ); } // à
268+ else { char_processed = false; }
269+
270+ if (char_processed ) { i ++ ; continue ; }
271+ }
272+
273+ // Standard ASCII in ABNT2
274+ uint8_t keycode = 0 ;
275+ uint8_t modifier = 0 ;
276+
277+ if (c1 >= 'a' && c1 <= 'z' ) keycode = HID_KEY_A + (c1 - 'a' );
278+ else if (c1 >= 'A' && c1 <= 'Z' ) { modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_A + (c1 - 'A' ); }
279+ else if (c1 >= '1' && c1 <= '9' ) keycode = HID_KEY_1 + (c1 - '1' );
280+ else if (c1 == '0' ) keycode = HID_KEY_0 ;
281+ else {
282+ switch (c1 ) {
283+ case '!' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_1 ; break ;
284+ case '@' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_2 ; break ;
285+ case '#' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_3 ; break ;
286+ case '$' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_4 ; break ;
287+ case '%' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_5 ; break ;
288+ case '&' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_7 ; break ;
289+ case '*' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_8 ; break ;
290+ case '(' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_9 ; break ;
291+ case ')' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_0 ; break ;
292+ case ' ' : keycode = HID_KEY_SPACE ; break ;
293+ case '\n' : keycode = HID_KEY_ENTER ; break ;
294+ case '\t' : keycode = HID_KEY_TAB ; break ;
295+ case '-' : keycode = HID_KEY_MINUS ; break ;
296+ case '=' : keycode = HID_KEY_EQUAL ; break ;
297+ case '_' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_MINUS ; break ;
298+ case '+' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_EQUAL ; break ;
299+ case '.' : keycode = HID_KEY_PERIOD ; break ;
300+ case ',' : keycode = HID_KEY_COMMA ; break ;
301+ case ';' : keycode = HID_KEY_SLASH ; break ;
302+ case ':' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_SLASH ; break ;
303+ // ABNT2 / and ? are on International 1 key (0x87)
304+ case '/' : keycode = 0x87 ; break ;
305+ case '?' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = 0x87 ; break ;
306+ case '[' : modifier = KEYBOARD_MODIFIER_RIGHTALT ; keycode = HID_KEY_BRACKET_LEFT ; break ;
307+ case '{' : modifier = KEYBOARD_MODIFIER_RIGHTALT ; keycode = HID_KEY_BRACKET_LEFT ; modifier |= KEYBOARD_MODIFIER_LEFTSHIFT ; break ;
308+ case ']' : modifier = KEYBOARD_MODIFIER_RIGHTALT ; keycode = HID_KEY_BRACKET_RIGHT ; break ;
309+ case '}' : modifier = KEYBOARD_MODIFIER_RIGHTALT ; keycode = HID_KEY_BRACKET_RIGHT ; modifier |= KEYBOARD_MODIFIER_LEFTSHIFT ; break ;
310+ case '\\' : keycode = HID_KEY_BACKSLASH ; break ;
311+ case '|' : modifier = KEYBOARD_MODIFIER_LEFTSHIFT ; keycode = HID_KEY_BACKSLASH ; break ;
312+ }
313+ }
314+ if (keycode != 0 ) press_key_wrapper (keycode , modifier );
315+ }
316+ }
159317 }
160318 }
161319 }
@@ -172,13 +330,10 @@ static void process_line(char* line) {
172330 }
173331 } else {
174332 keycode = find_key_code (cmd );
175-
176- // Check for potential following modifiers or keys?
177- // Standard DuckyScript usually puts modifiers first or implies single key press.
178333 }
179334
180335 if (keycode != 0 || modifiers != 0 ) {
181- bad_usb_press_key (keycode , modifiers );
336+ press_key_wrapper (keycode , modifiers );
182337 }
183338 }
184339}
0 commit comments