main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include "usbif.h"
  4. #include <gui/gui.h>
  5. #include <gui/elements.h>
  6. #include <gui/view_dispatcher.h>
  7. #include <gui/modules/menu.h>
  8. #include <gui/modules/popup.h>
  9. #include <gui/modules/text_box.h>
  10. #include <dialogs/dialogs.h>
  11. #include <storage/storage.h>
  12. #include <stream/stream.h>
  13. #include <stream/buffered_file_stream.h>
  14. #include <toolbox/stream/file_stream.h>
  15. #include "filelogger.h"
  16. #include "constants.h"
  17. #include <assets_icons.h>
  18. #include "hidtransfer_icons.h"
  19. #define HEX_VIEWER_APP_PATH_FOLDER "/any"
  20. #define HEX_VIEWER_APP_EXTENSION "*"
  21. #define HEX_VIEWER_BYTES_PER_LINE 4u
  22. #define HEX_VIEWER_LINES_ON_SCREEN 4u
  23. #define HEX_VIEWER_BUF_SIZE (HEX_VIEWER_LINES_ON_SCREEN * HEX_VIEWER_BYTES_PER_LINE)
  24. #define VIEW_DISPATCHER_MENU 0
  25. #define VIEW_DISPATCHER_SEND 1
  26. #define VIEW_DISPATCHER_SEND_SINGLE_THREADED 3
  27. #define VIEW_DISPATCHER_RECEIVE 2
  28. #define VIEW_DISPATCHER_POPUP 3
  29. #define VIEW_DISPATCHER_DEBUG_SEND 99
  30. #define VIEW_DISPATCHER_DEBUG_RECEIVE 98
  31. typedef struct {
  32. uint8_t file_bytes[HEX_VIEWER_LINES_ON_SCREEN][HEX_VIEWER_BYTES_PER_LINE];
  33. uint32_t file_offset;
  34. uint32_t file_read_bytes;
  35. uint32_t file_size;
  36. Stream* stream;
  37. bool mode; // Print address or content
  38. } DataTransferAppModel;
  39. typedef struct {
  40. DataTransferAppModel* model;
  41. ViewDispatcher* view_dispatcher;
  42. Gui* gui;
  43. Storage* storage;
  44. } DataTransferApp;
  45. typedef enum MessageType {
  46. MessageMetadata = 0,
  47. MessageFullPayload = 1,
  48. MessagePartPayload = 2
  49. } MessageType;
  50. // 3 byte
  51. typedef struct {
  52. uint32_t counter; // 22 bit LSB
  53. MessageType messageType; // 2 bit
  54. } MessageHeader;
  55. // 5 byte + len(fileName)
  56. typedef struct {
  57. MessageHeader header;
  58. uint32_t fileSize;
  59. const char* fileName;
  60. } FileMetadataMessage;
  61. // max. 64 byte, see payloadLength
  62. typedef struct {
  63. // 3 byte
  64. MessageHeader header;
  65. // 61 byte
  66. uint8_t* payload;
  67. } FullPayloadMessage;
  68. // max. 64 byte, see payloadLength
  69. typedef struct {
  70. // 3 byte
  71. MessageHeader header;
  72. uint8_t payloadLength;
  73. // 61 byte
  74. uint8_t* payload;
  75. } PartPayloadMessage;
  76. TextBox* textBoxReceive;
  77. Popup* popup;
  78. DataTransferApp* app;
  79. void openMenu(void* bla);
  80. static void* parseMessage(MessageType* outMsg, void* msgBuffer) {
  81. uint32_t header = 0;
  82. memcpy(&header, msgBuffer, 3);
  83. FURI_LOG_D(TAG, "Parse message, header: %lu", header);
  84. MessageType msgType = header & 3;
  85. uint32_t counter = header >> 2;
  86. *outMsg = msgType;
  87. if(msgType == MessageMetadata) {
  88. FURI_LOG_D(TAG, "Parse Metadata message");
  89. furi_check(counter == 0);
  90. uint32_t fileSize;
  91. int strl = strlen(msgBuffer + 7);
  92. char* fileName = malloc(strl + 1);
  93. memcpy(&fileSize, msgBuffer + 3, sizeof(fileSize));
  94. strncpy(fileName, msgBuffer + 7, strl);
  95. FileMetadataMessage* msg = malloc(sizeof(FileMetadataMessage));
  96. memset(msg, 0, sizeof(FileMetadataMessage));
  97. *msg = (FileMetadataMessage){
  98. .header = {.counter = counter, .messageType = MessageMetadata},
  99. .fileName = fileName,
  100. .fileSize = fileSize};
  101. return msg;
  102. }
  103. FURI_LOG_E(TAG, "Tried to parse unknown msg! %d", msgType);
  104. furi_check(false);
  105. }
  106. static DataTransferApp* dataTransferApp_alloc() {
  107. FURI_LOG_D(TAG, "alloc");
  108. DataTransferApp* instance = malloc(sizeof(DataTransferApp));
  109. instance->model = malloc(sizeof(DataTransferAppModel));
  110. memset(instance->model, 0x0, sizeof(DataTransferAppModel));
  111. instance->view_dispatcher = view_dispatcher_alloc();
  112. instance->gui = furi_record_open(RECORD_GUI);
  113. view_dispatcher_enable_queue(instance->view_dispatcher);
  114. view_dispatcher_attach_to_gui(
  115. instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
  116. instance->storage = furi_record_open(RECORD_STORAGE);
  117. return instance;
  118. }
  119. static void dataTransferApp_free(DataTransferApp* instance) {
  120. furi_record_close(RECORD_STORAGE);
  121. view_dispatcher_free(instance->view_dispatcher);
  122. furi_record_close(RECORD_GUI);
  123. if(instance->model->stream) buffered_file_stream_close(instance->model->stream);
  124. free(instance->model);
  125. free(instance);
  126. }
  127. void sendMessage(uint8_t* msg) {
  128. MessageHeader* header = (MessageHeader*)msg;
  129. uint32_t c = header->counter;
  130. c = c << 2 | header->messageType;
  131. void* sendBuf = malloc(64);
  132. memset(sendBuf, 0x0, 64);
  133. memcpy(sendBuf, &c, 3);
  134. if(header->messageType == MessageMetadata) {
  135. FileMetadataMessage* m = (FileMetadataMessage*)msg;
  136. memcpy(sendBuf + 3, &(m->fileSize), 4);
  137. strncpy(sendBuf + 7, m->fileName, 64 - 7);
  138. //memcpy(sendBuf + 7, m->fileName, strlen(m->fileName));
  139. } else if(header->messageType == MessageFullPayload) {
  140. FullPayloadMessage* m = (FullPayloadMessage*)msg;
  141. memcpy(sendBuf + 3, m->payload, 61);
  142. } else if(header->messageType == MessagePartPayload) {
  143. PartPayloadMessage* m = (PartPayloadMessage*)msg;
  144. memcpy(sendBuf + 3, &(m->payloadLength), 1);
  145. memcpy(sendBuf + 4, m->payload, m->payloadLength);
  146. }
  147. sendBulkData(sendBuf, 64);
  148. }
  149. void sendHeader(uint32_t fileSize, const char* fileName) {
  150. FileMetadataMessage md = {
  151. .header = {.counter = 0, .messageType = MessageMetadata},
  152. .fileSize = fileSize,
  153. .fileName = fileName};
  154. uint8_t* bytePtr = (uint8_t*)&md;
  155. sendMessage(bytePtr);
  156. }
  157. static void dispatch_view(void* contextd, uint32_t index) {
  158. DataTransferApp* context = (DataTransferApp*)contextd;
  159. if(index == VIEW_DISPATCHER_SEND || index == VIEW_DISPATCHER_SEND_SINGLE_THREADED) {
  160. initializeSendingData(index == VIEW_DISPATCHER_SEND ? NUM_OF_INTERFACES : 1);
  161. FuriString* browser_path;
  162. browser_path = furi_string_alloc();
  163. FuriString* selected_path;
  164. selected_path = furi_string_alloc();
  165. furi_string_set(browser_path, HEX_VIEWER_APP_PATH_FOLDER);
  166. DialogsFileBrowserOptions browser_options;
  167. dialog_file_browser_set_basic_options(&browser_options, HEX_VIEWER_APP_EXTENSION, NULL);
  168. browser_options.hide_ext = false;
  169. browser_options.base_path = furi_string_get_cstr(browser_path);
  170. DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
  171. bool res =
  172. dialog_file_browser_show(dialogs, selected_path, browser_path, &browser_options);
  173. furi_record_close(RECORD_DIALOGS);
  174. if(!res) {
  175. FURI_LOG_I(TAG, "No file selected");
  176. furi_string_free(browser_path);
  177. furi_string_free(selected_path);
  178. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_MENU);
  179. return;
  180. }
  181. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_SEND);
  182. size_t idx = furi_string_search_rchar(selected_path, '/');
  183. FuriString* path_copy = furi_string_alloc();
  184. furi_string_set_n(
  185. path_copy, selected_path, idx + 1, furi_string_size(selected_path) - idx - 1);
  186. const char* path = furi_string_get_cstr(selected_path);
  187. Stream* fs = buffered_file_stream_alloc(context->storage);
  188. buffered_file_stream_open(fs, path, FSAM_READ, FSOM_OPEN_EXISTING);
  189. size_t file_size = stream_size(fs);
  190. sendHeader(file_size, furi_string_get_cstr(path_copy));
  191. furi_string_free(path_copy);
  192. uint32_t sent = 0;
  193. uint8_t data[64];
  194. memset(data, 0, 64);
  195. uint32_t msgCounter = 1;
  196. while(sent < file_size) {
  197. memset(data, 0, 61);
  198. uint8_t to_read = 61;
  199. if(file_size - sent < 61) {
  200. to_read = file_size - sent;
  201. }
  202. sent += to_read;
  203. stream_read(fs, data, to_read);
  204. if(to_read == 61) {
  205. FullPayloadMessage msg = {
  206. .header = {.counter = msgCounter, .messageType = MessageFullPayload},
  207. .payload = data,
  208. };
  209. sendMessage((uint8_t*)&msg);
  210. } else {
  211. PartPayloadMessage msg = {
  212. .header = {.counter = msgCounter, .messageType = MessagePartPayload},
  213. .payloadLength = to_read,
  214. .payload = data};
  215. sendMessage((uint8_t*)&msg);
  216. }
  217. msgCounter++;
  218. //furi_hal_hid_u2f_send_response(data, 64);
  219. }
  220. FURI_LOG_D(TAG, "Finished sending packet");
  221. stopSendingData();
  222. buffered_file_stream_close(fs);
  223. free(fs);
  224. furi_string_free(browser_path);
  225. furi_string_free(selected_path);
  226. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_MENU);
  227. } else if(index == VIEW_DISPATCHER_RECEIVE) {
  228. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_RECEIVE);
  229. FuriString* textBoxText = furi_string_alloc_printf("Waiting for file...");
  230. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  231. FuriMessageQueue* queue = initializeReceivingData();
  232. ThreadMessage threadMsg;
  233. FileMetadataMessage* metadataMsg;
  234. furi_check(furi_message_queue_get(queue, &threadMsg, FuriWaitForever) == FuriStatusOk);
  235. MessageType msgtype;
  236. void* parsedMsg = parseMessage(&msgtype, threadMsg.dataPointer);
  237. FURI_LOG_D(TAG, "received %d", msgtype);
  238. //furi_check(msgtype == MessageMetadata);
  239. metadataMsg = (FileMetadataMessage*)parsedMsg;
  240. furi_string_printf(textBoxText, "Receiving %s...", metadataMsg->fileName);
  241. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  242. Stream* fs = buffered_file_stream_alloc(context->storage);
  243. storage_common_mkdir(context->storage, "/any/HIDTransfer");
  244. FuriString* filePath =
  245. furi_string_alloc_printf("/any/HIDTransfer/%s.raw", metadataMsg->fileName);
  246. if(!buffered_file_stream_open(
  247. fs, furi_string_get_cstr(filePath), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  248. FURI_LOG_D(TAG, "Could not open filestream");
  249. furi_string_printf(textBoxText, "Could not open filestream");
  250. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  251. furi_delay_ms(1500);
  252. furi_crash("FSEr");
  253. }
  254. // write metadata message to file
  255. stream_write(fs, threadMsg.dataPointer, 64);
  256. uint32_t expectedNumMsgs = (uint32_t)ceil(metadataMsg->fileSize / 61.0);
  257. uint32_t increamentStep = (uint32_t)floor(0.1 * expectedNumMsgs);
  258. int finished = 0;
  259. uint32_t numMsgs = 0;
  260. while(true) {
  261. ThreadMessage msg;
  262. furi_check(furi_message_queue_get(queue, &msg, FuriWaitForever) == FuriStatusOk);
  263. if(msg.dataPointer == NULL) {
  264. finished += 1;
  265. if(finished == NUM_OF_INTERFACES) {
  266. break;
  267. }
  268. continue;
  269. }
  270. numMsgs += 1;
  271. if(numMsgs % increamentStep == 0) {
  272. int percent = (int)ceil(numMsgs / (double)expectedNumMsgs * 100);
  273. furi_string_printf(
  274. textBoxText, "Receiving %s...\n%d%%", metadataMsg->fileName, percent);
  275. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  276. }
  277. //FURI_LOG_D(TAG, "after Queue");
  278. stream_write(fs, msg.dataPointer, 64);
  279. free(msg.dataPointer);
  280. }
  281. buffered_file_stream_close(fs);
  282. free(fs);
  283. free((void*)metadataMsg->fileName);
  284. free(metadataMsg);
  285. furi_string_free(filePath);
  286. int missingMsgs = expectedNumMsgs - numMsgs;
  287. if(missingMsgs != 0) {
  288. furi_string_cat_printf(
  289. textBoxText, "\nCAUTION: %d messages are missing.", missingMsgs);
  290. FuriString* txtMsg = furi_string_alloc_printf("%d messages are missing.", missingMsgs);
  291. popup_set_icon(popup, 4, 19, &I_Warning_30x23);
  292. popup_set_header(popup, "CAUTION", 53, 19, AlignLeft, AlignCenter);
  293. popup_set_text(popup, furi_string_get_cstr(txtMsg), 39, 28, AlignLeft, AlignTop);
  294. popup_set_callback(popup, &openMenu);
  295. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_POPUP);
  296. } else {
  297. furi_string_cat_printf(textBoxText, "\nFinished receiving!");
  298. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  299. furi_delay_ms(5000);
  300. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_MENU);
  301. }
  302. furi_string_free(textBoxText);
  303. } else if(index == VIEW_DISPATCHER_DEBUG_SEND) {
  304. uint8_t buf[64];
  305. for(int i = 0; i < 64; i++) {
  306. buf[i] = i + 1;
  307. }
  308. sendViaEP(buf, 0);
  309. } else if(index == VIEW_DISPATCHER_DEBUG_RECEIVE) {
  310. uint8_t buf[64];
  311. receiveFromEP(buf, 0);
  312. FURI_LOG_D(TAG, "01: %d, last: %d", buf[0], buf[63]);
  313. }
  314. }
  315. bool eventCallback() {
  316. return false;
  317. }
  318. void openMenu(void* bla) {
  319. UNUSED(bla);
  320. view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_DISPATCHER_MENU);
  321. }
  322. bool inputCallback(InputEvent* event, void* context) {
  323. UNUSED(context);
  324. UNUSED(event);
  325. FURI_LOG_D(TAG, "Back button pressend on sending view");
  326. return true;
  327. }
  328. int32_t hidtransfer_app() {
  329. furi_log_set_level(FuriLogLevelDebug);
  330. furi_hal_console_enable();
  331. FURI_LOG_D(TAG, "APP STARTED");
  332. FuriHalUsbInterface* mode = furi_hal_usb_get_config();
  333. furi_hal_usb_set_config(getUsbHidBulk(), NULL);
  334. app = dataTransferApp_alloc();
  335. Menu* mainMenu = menu_alloc();
  336. menu_add_item(
  337. mainMenu,
  338. "Send Client",
  339. &I_right_14,
  340. VIEW_DISPATCHER_SEND_SINGLE_THREADED,
  341. dispatch_view,
  342. app);
  343. menu_add_item(mainMenu, "Send File", &I_right_14, VIEW_DISPATCHER_SEND, dispatch_view, app);
  344. menu_add_item(
  345. mainMenu, "Receive File", &I_left_14, VIEW_DISPATCHER_RECEIVE, dispatch_view, app);
  346. //menu_add_item(mainMenu, "Debug Send", &I_Pin_arrow_right_9x7, VIEW_DISPATCHER_DEBUG_SEND, dispatch_view, app);
  347. //menu_add_item(mainMenu, "Debug Receive", &I_Pin_arrow_left_9x7, VIEW_DISPATCHER_DEBUG_RECEIVE, dispatch_view, app);
  348. // Sending View
  349. TextBox* textBoxSend = text_box_alloc();
  350. text_box_set_text(textBoxSend, "Sending file...");
  351. text_box_set_font(textBoxSend, TextBoxFontText);
  352. View* textBoxView = text_box_get_view(textBoxSend);
  353. view_set_input_callback(textBoxView, inputCallback);
  354. // Receive View
  355. textBoxReceive = text_box_alloc();
  356. text_box_set_text(textBoxReceive, "Receiveing file...");
  357. text_box_set_font(textBoxReceive, TextBoxFontText);
  358. View* textBoxRecView = text_box_get_view(textBoxReceive);
  359. view_set_input_callback(textBoxRecView, inputCallback);
  360. // Popup
  361. popup = popup_alloc();
  362. popup_disable_timeout(popup);
  363. view_dispatcher_add_view(app->view_dispatcher, VIEW_DISPATCHER_MENU, menu_get_view(mainMenu));
  364. view_dispatcher_add_view(app->view_dispatcher, VIEW_DISPATCHER_SEND, textBoxView);
  365. view_dispatcher_add_view(app->view_dispatcher, VIEW_DISPATCHER_RECEIVE, textBoxRecView);
  366. view_dispatcher_add_view(app->view_dispatcher, VIEW_DISPATCHER_POPUP, popup_get_view(popup));
  367. view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_DISPATCHER_MENU);
  368. view_dispatcher_set_navigation_event_callback(app->view_dispatcher, eventCallback);
  369. view_dispatcher_run(app->view_dispatcher);
  370. dataTransferApp_free(app);
  371. furi_hal_usb_set_config(mode, NULL);
  372. menu_free(mainMenu);
  373. text_box_free(textBoxSend);
  374. text_box_free(textBoxReceive);
  375. popup_free(popup);
  376. return 0;
  377. }