main.c 16 KB

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