main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  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_enable_queue(instance->view_dispatcher);
  113. view_dispatcher_attach_to_gui(
  114. instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen);
  115. instance->storage = furi_record_open(RECORD_STORAGE);
  116. return instance;
  117. }
  118. static void dataTransferApp_free(DataTransferApp* instance) {
  119. furi_record_close(RECORD_STORAGE);
  120. view_dispatcher_free(instance->view_dispatcher);
  121. furi_record_close(RECORD_GUI);
  122. if(instance->model->stream) buffered_file_stream_close(instance->model->stream);
  123. free(instance->model);
  124. free(instance);
  125. }
  126. void sendMessage(uint8_t* msg) {
  127. MessageHeader* header = (MessageHeader*)msg;
  128. uint32_t c = header->counter;
  129. c = c << 2 | header->messageType;
  130. void* sendBuf = malloc(64);
  131. memset(sendBuf, 0x0, 64);
  132. memcpy(sendBuf, &c, 3);
  133. if(header->messageType == MessageMetadata) {
  134. FileMetadataMessage* m = (FileMetadataMessage*)msg;
  135. memcpy(sendBuf + 3, &(m->fileSize), 4);
  136. strncpy(sendBuf + 7, m->fileName, 64 - 7);
  137. //memcpy(sendBuf + 7, m->fileName, strlen(m->fileName));
  138. } else if(header->messageType == MessageFullPayload) {
  139. FullPayloadMessage* m = (FullPayloadMessage*)msg;
  140. memcpy(sendBuf + 3, m->payload, 61);
  141. } else if(header->messageType == MessagePartPayload) {
  142. PartPayloadMessage* m = (PartPayloadMessage*)msg;
  143. memcpy(sendBuf + 3, &(m->payloadLength), 1);
  144. memcpy(sendBuf + 4, m->payload, m->payloadLength);
  145. }
  146. sendBulkData(sendBuf, 64);
  147. }
  148. void sendHeader(uint32_t fileSize, const char* fileName) {
  149. FileMetadataMessage md = {
  150. .header = {.counter = 0, .messageType = MessageMetadata},
  151. .fileSize = fileSize,
  152. .fileName = fileName};
  153. uint8_t* bytePtr = (uint8_t*)&md;
  154. sendMessage(bytePtr);
  155. }
  156. static void dispatch_view(void* contextd, uint32_t index) {
  157. DataTransferApp* context = (DataTransferApp*)contextd;
  158. if(index == VIEW_DISPATCHER_SEND || index == VIEW_DISPATCHER_SEND_SINGLE_THREADED) {
  159. initializeSendingData(index == VIEW_DISPATCHER_SEND ? NUM_OF_INTERFACES : 1);
  160. FuriString* browser_path;
  161. browser_path = furi_string_alloc();
  162. FuriString* selected_path;
  163. selected_path = furi_string_alloc();
  164. furi_string_set(browser_path, HEX_VIEWER_APP_PATH_FOLDER);
  165. DialogsFileBrowserOptions browser_options;
  166. dialog_file_browser_set_basic_options(&browser_options, HEX_VIEWER_APP_EXTENSION, NULL);
  167. browser_options.hide_ext = false;
  168. browser_options.base_path = furi_string_get_cstr(browser_path);
  169. DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
  170. bool res =
  171. dialog_file_browser_show(dialogs, selected_path, browser_path, &browser_options);
  172. furi_record_close(RECORD_DIALOGS);
  173. if(!res) {
  174. FURI_LOG_I(TAG, "No file selected");
  175. furi_string_free(browser_path);
  176. furi_string_free(selected_path);
  177. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_MENU);
  178. return;
  179. }
  180. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_SEND);
  181. size_t idx = furi_string_search_rchar(selected_path, '/');
  182. FuriString* path_copy = furi_string_alloc();
  183. furi_string_set_n(
  184. path_copy, selected_path, idx + 1, furi_string_size(selected_path) - idx - 1);
  185. const char* path = furi_string_get_cstr(selected_path);
  186. Stream* fs = buffered_file_stream_alloc(context->storage);
  187. buffered_file_stream_open(fs, path, FSAM_READ, FSOM_OPEN_EXISTING);
  188. size_t file_size = stream_size(fs);
  189. sendHeader(file_size, furi_string_get_cstr(path_copy));
  190. furi_string_free(path_copy);
  191. uint32_t sent = 0;
  192. uint8_t data[64];
  193. memset(data, 0, 64);
  194. uint32_t msgCounter = 1;
  195. while(sent < file_size) {
  196. memset(data, 0, 61);
  197. uint8_t to_read = 61;
  198. if(file_size - sent < 61) {
  199. to_read = file_size - sent;
  200. }
  201. sent += to_read;
  202. stream_read(fs, data, to_read);
  203. if(to_read == 61) {
  204. FullPayloadMessage msg = {
  205. .header = {.counter = msgCounter, .messageType = MessageFullPayload},
  206. .payload = data,
  207. };
  208. sendMessage((uint8_t*)&msg);
  209. } else {
  210. PartPayloadMessage msg = {
  211. .header = {.counter = msgCounter, .messageType = MessagePartPayload},
  212. .payloadLength = to_read,
  213. .payload = data};
  214. sendMessage((uint8_t*)&msg);
  215. }
  216. msgCounter++;
  217. //furi_hal_hid_u2f_send_response(data, 64);
  218. }
  219. FURI_LOG_D(TAG, "Finished sending packet");
  220. stopSendingData();
  221. buffered_file_stream_close(fs);
  222. free(fs);
  223. furi_string_free(browser_path);
  224. furi_string_free(selected_path);
  225. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_MENU);
  226. } else if(index == VIEW_DISPATCHER_RECEIVE) {
  227. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_RECEIVE);
  228. FuriString* textBoxText = furi_string_alloc_printf("Waiting for file...");
  229. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  230. FuriMessageQueue* queue = initializeReceivingData();
  231. ThreadMessage threadMsg;
  232. FileMetadataMessage* metadataMsg;
  233. furi_check(furi_message_queue_get(queue, &threadMsg, FuriWaitForever) == FuriStatusOk);
  234. MessageType msgtype;
  235. void* parsedMsg = parseMessage(&msgtype, threadMsg.dataPointer);
  236. FURI_LOG_D(TAG, "received %d", msgtype);
  237. //furi_check(msgtype == MessageMetadata);
  238. metadataMsg = (FileMetadataMessage*)parsedMsg;
  239. furi_string_printf(textBoxText, "Receiving %s...", metadataMsg->fileName);
  240. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  241. Stream* fs = buffered_file_stream_alloc(context->storage);
  242. storage_common_mkdir(context->storage, "/any/HIDTransfer");
  243. FuriString* filePath =
  244. furi_string_alloc_printf("/any/HIDTransfer/%s.raw", metadataMsg->fileName);
  245. if(!buffered_file_stream_open(
  246. fs, furi_string_get_cstr(filePath), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  247. FURI_LOG_D(TAG, "Could not open filestream");
  248. furi_string_printf(textBoxText, "Could not open filestream");
  249. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  250. furi_delay_ms(1500);
  251. furi_crash("FSEr");
  252. }
  253. // write metadata message to file
  254. stream_write(fs, threadMsg.dataPointer, 64);
  255. uint32_t expectedNumMsgs = (uint32_t)ceil(metadataMsg->fileSize / 61.0);
  256. uint32_t increamentStep = (uint32_t)floor(0.1 * expectedNumMsgs);
  257. int finished = 0;
  258. uint32_t numMsgs = 0;
  259. while(true) {
  260. ThreadMessage msg;
  261. furi_check(furi_message_queue_get(queue, &msg, FuriWaitForever) == FuriStatusOk);
  262. if(msg.dataPointer == NULL) {
  263. finished += 1;
  264. if(finished == NUM_OF_INTERFACES) {
  265. break;
  266. }
  267. continue;
  268. }
  269. numMsgs += 1;
  270. if(numMsgs % increamentStep == 0) {
  271. int percent = (int)ceil(numMsgs / (double)expectedNumMsgs * 100);
  272. furi_string_printf(
  273. textBoxText, "Receiving %s...\n%d%%", metadataMsg->fileName, percent);
  274. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  275. }
  276. //FURI_LOG_D(TAG, "after Queue");
  277. stream_write(fs, msg.dataPointer, 64);
  278. free(msg.dataPointer);
  279. }
  280. buffered_file_stream_close(fs);
  281. free(fs);
  282. free((void*)metadataMsg->fileName);
  283. free(metadataMsg);
  284. furi_string_free(filePath);
  285. int missingMsgs = expectedNumMsgs - numMsgs;
  286. if(missingMsgs != 0) {
  287. furi_string_cat_printf(
  288. textBoxText, "\nCAUTION: %d messages are missing.", missingMsgs);
  289. FuriString* txtMsg = furi_string_alloc_printf("%d messages are missing.", missingMsgs);
  290. popup_set_icon(popup, 4, 19, &I_Warning_30x23);
  291. popup_set_header(popup, "CAUTION", 53, 19, AlignLeft, AlignCenter);
  292. popup_set_text(popup, furi_string_get_cstr(txtMsg), 39, 28, AlignLeft, AlignTop);
  293. popup_set_callback(popup, &openMenu);
  294. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_POPUP);
  295. } else {
  296. furi_string_cat_printf(textBoxText, "\nFinished receiving!");
  297. text_box_set_text(textBoxReceive, furi_string_get_cstr(textBoxText));
  298. furi_delay_ms(5000);
  299. view_dispatcher_switch_to_view(context->view_dispatcher, VIEW_DISPATCHER_MENU);
  300. }
  301. furi_string_free(textBoxText);
  302. } else if(index == VIEW_DISPATCHER_DEBUG_SEND) {
  303. uint8_t buf[64];
  304. for(int i = 0; i < 64; i++) {
  305. buf[i] = i + 1;
  306. }
  307. sendViaEP(buf, 0);
  308. } else if(index == VIEW_DISPATCHER_DEBUG_RECEIVE) {
  309. uint8_t buf[64];
  310. receiveFromEP(buf, 0);
  311. FURI_LOG_D(TAG, "01: %d, last: %d", buf[0], buf[63]);
  312. }
  313. }
  314. static bool eventCallback(void* context) {
  315. UNUSED(context);
  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_LOG_D(TAG, "APP STARTED");
  331. FuriHalUsbInterface* mode = furi_hal_usb_get_config();
  332. furi_hal_usb_set_config(getUsbHidBulk(), NULL);
  333. app = dataTransferApp_alloc();
  334. Menu* mainMenu = menu_alloc();
  335. menu_add_item(
  336. mainMenu,
  337. "Send Client",
  338. &I_right_14,
  339. VIEW_DISPATCHER_SEND_SINGLE_THREADED,
  340. dispatch_view,
  341. app);
  342. menu_add_item(mainMenu, "Send File", &I_right_14, VIEW_DISPATCHER_SEND, dispatch_view, app);
  343. menu_add_item(
  344. mainMenu, "Receive File", &I_left_14, VIEW_DISPATCHER_RECEIVE, dispatch_view, app);
  345. //menu_add_item(mainMenu, "Debug Send", &I_Pin_arrow_right_9x7, VIEW_DISPATCHER_DEBUG_SEND, dispatch_view, app);
  346. //menu_add_item(mainMenu, "Debug Receive", &I_Pin_arrow_left_9x7, VIEW_DISPATCHER_DEBUG_RECEIVE, dispatch_view, app);
  347. // Sending View
  348. TextBox* textBoxSend = text_box_alloc();
  349. text_box_set_text(textBoxSend, "Sending file...");
  350. text_box_set_font(textBoxSend, TextBoxFontText);
  351. View* textBoxView = text_box_get_view(textBoxSend);
  352. view_set_input_callback(textBoxView, inputCallback);
  353. // Receive View
  354. textBoxReceive = text_box_alloc();
  355. text_box_set_text(textBoxReceive, "Receiveing file...");
  356. text_box_set_font(textBoxReceive, TextBoxFontText);
  357. View* textBoxRecView = text_box_get_view(textBoxReceive);
  358. view_set_input_callback(textBoxRecView, inputCallback);
  359. // Popup
  360. popup = popup_alloc();
  361. popup_disable_timeout(popup);
  362. view_dispatcher_add_view(app->view_dispatcher, VIEW_DISPATCHER_MENU, menu_get_view(mainMenu));
  363. view_dispatcher_add_view(app->view_dispatcher, VIEW_DISPATCHER_SEND, textBoxView);
  364. view_dispatcher_add_view(app->view_dispatcher, VIEW_DISPATCHER_RECEIVE, textBoxRecView);
  365. view_dispatcher_add_view(app->view_dispatcher, VIEW_DISPATCHER_POPUP, popup_get_view(popup));
  366. view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_DISPATCHER_MENU);
  367. view_dispatcher_set_navigation_event_callback(app->view_dispatcher, eventCallback);
  368. view_dispatcher_run(app->view_dispatcher);
  369. dataTransferApp_free(app);
  370. furi_hal_usb_set_config(mode, NULL);
  371. menu_free(mainMenu);
  372. text_box_free(textBoxSend);
  373. text_box_free(textBoxReceive);
  374. popup_free(popup);
  375. return 0;
  376. }