mtp.c 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. #include <furi.h>
  2. #include <storage/storage.h>
  3. #include "main.h"
  4. #include "mtp.h"
  5. #include "usb_desc.h"
  6. #include "utils.h"
  7. #define BLOCK_SIZE 65536
  8. // Supported operations (example, add as needed)
  9. uint16_t supported_operations[] = {
  10. MTP_OP_GET_DEVICE_INFO,
  11. MTP_OP_OPEN_SESSION,
  12. MTP_OP_CLOSE_SESSION,
  13. MTP_OP_GET_STORAGE_IDS,
  14. MTP_OP_GET_STORAGE_INFO,
  15. MTP_OP_GET_NUM_OBJECTS,
  16. MTP_OP_GET_OBJECT_HANDLES,
  17. MTP_OP_GET_OBJECT_INFO,
  18. MTP_OP_GET_OBJECT,
  19. MTP_OP_SEND_OBJECT_INFO,
  20. MTP_OP_SEND_OBJECT,
  21. MTP_OP_DELETE_OBJECT,
  22. MTP_OP_GET_DEVICE_PROP_DESC,
  23. MTP_OP_GET_DEVICE_PROP_VALUE,
  24. MTP_OP_GET_OBJECT_PROPS_SUPPORTED,
  25. MTP_OP_SET_OBJECT_PROP_VALUE};
  26. uint16_t supported_object_props[] = {
  27. MTP_PROP_STORAGE_ID,
  28. MTP_PROP_OBJECT_FORMAT,
  29. MTP_PROP_OBJECT_FILE_NAME,
  30. };
  31. // Supported device properties (example, add as needed)
  32. uint16_t supported_device_properties[] = {
  33. 0xd402, // Device friendly name
  34. };
  35. uint16_t supported_playback_formats[] = {
  36. MTP_FORMAT_UNDEFINED,
  37. MTP_FORMAT_ASSOCIATION,
  38. };
  39. void merge_path(char* target, char* base, char* name) {
  40. // implement this way since strcat is unavailable
  41. char* ptr = target;
  42. strcpy(target, base);
  43. ptr += strlen(base);
  44. strcpy(ptr, "/");
  45. ptr++;
  46. strcpy(ptr, name);
  47. }
  48. // temporary storage for buffer
  49. MTPDataPersistence persistence;
  50. void mtp_handle_bulk(AppMTP* mtp, uint8_t* buffer, uint32_t length) {
  51. UNUSED(mtp);
  52. if(persistence.left_bytes > 0) {
  53. FURI_LOG_I("MTP", "Left bytes: %ld", persistence.left_bytes);
  54. handle_mtp_data_packet(mtp, buffer, length, 1);
  55. return;
  56. }
  57. if(length < 12) {
  58. FURI_LOG_E("MTP", "Invalid MTP packet");
  59. return;
  60. }
  61. struct MTPHeader* header = (struct MTPHeader*)buffer;
  62. uint16_t type = header->type;
  63. if(type == MTP_TYPE_COMMAND) {
  64. struct MTPContainer* container = (struct MTPContainer*)buffer;
  65. handle_mtp_command(mtp, container);
  66. } else if(type == MTP_TYPE_DATA) {
  67. if(header->len > length) {
  68. persistence.left_bytes = header->len;
  69. }
  70. handle_mtp_data_packet(mtp, buffer, length, 0);
  71. } else if(type == MTP_TYPE_RESPONSE) {
  72. handle_mtp_response(mtp, header);
  73. } else {
  74. FURI_LOG_W("MTP", "Unsupported MTP packet type: %d", type);
  75. }
  76. }
  77. void setup_persistence(struct MTPContainer* container) {
  78. persistence.transaction_id = container->header.transaction_id;
  79. memcpy(persistence.params, container->params, sizeof(uint32_t) * 5);
  80. }
  81. void handle_mtp_data_packet(AppMTP* mtp, uint8_t* buffer, int32_t length, bool cont) {
  82. UNUSED(mtp);
  83. uint8_t* ptr = buffer;
  84. if(!cont) {
  85. struct MTPHeader* header = (struct MTPHeader*)buffer;
  86. if(header->transaction_id != persistence.transaction_id) {
  87. // the params value can not be trusted.
  88. // reset the params
  89. for(int i = 0; i < 5; i++) {
  90. persistence.params[i] = 0;
  91. }
  92. }
  93. persistence.global_buffer = NULL;
  94. uint32_t transaction_id = header->transaction_id;
  95. persistence.op = header->op;
  96. FURI_LOG_I("MTP", "Handling MTP data: 0x%04x", header->op);
  97. FURI_LOG_I("MTP", "Transaction ID: %ld", transaction_id);
  98. FURI_LOG_I("MTP", "Length: %ld", length);
  99. ptr = (uint8_t*)buffer + sizeof(struct MTPHeader);
  100. switch(header->op) {
  101. case MTP_OP_SEND_OBJECT_INFO:
  102. case MTP_OP_SET_OBJECT_PROP_VALUE: {
  103. persistence.global_buffer = malloc(sizeof(uint8_t) * 256);
  104. persistence.buffer_offset = 0;
  105. break;
  106. }
  107. case MTP_OP_SEND_OBJECT: {
  108. persistence.buffer_offset = 0;
  109. persistence.left_bytes = header->len - sizeof(struct MTPHeader);
  110. }
  111. }
  112. }
  113. // for speicific operations.
  114. if(persistence.op == MTP_OP_SEND_OBJECT) {
  115. uint32_t handle = persistence.prev_handle;
  116. if(handle == 0) {
  117. FURI_LOG_E("MTP", "Invalid handle for MTP_OP_SEND_OBJECT");
  118. send_mtp_response(
  119. mtp, 3, MTP_RESP_INVALID_OBJECT_HANDLE, persistence.transaction_id, NULL);
  120. return;
  121. }
  122. char* path = get_path_from_handle(mtp, handle);
  123. int bytes_length = length - (ptr - buffer);
  124. int offset = persistence.buffer_offset;
  125. FURI_LOG_I("MTP", "Writing to file: %s with offset %02x", path, offset);
  126. if(persistence.current_file == NULL) {
  127. persistence.current_file = storage_file_alloc(mtp->storage);
  128. if(!storage_file_open(persistence.current_file, path, FSAM_WRITE, FSOM_OPEN_EXISTING)) {
  129. FURI_LOG_E("MTP", "Failed to open file: %s", path);
  130. send_mtp_response(
  131. mtp, 3, MTP_RESP_INVALID_OBJECT_HANDLE, persistence.transaction_id, NULL);
  132. storage_file_free(persistence.current_file);
  133. persistence.current_file = NULL;
  134. return;
  135. }
  136. }
  137. File* file = persistence.current_file;
  138. // no need since the file is already opened
  139. // storage_file_seek(file, offset, SEEK_SET);
  140. storage_file_write(file, ptr, bytes_length);
  141. persistence.buffer_offset += bytes_length;
  142. persistence.left_bytes -= bytes_length;
  143. if(persistence.left_bytes <= 0) {
  144. send_mtp_response(mtp, 3, MTP_RESP_OK, persistence.transaction_id, NULL);
  145. storage_file_close(file);
  146. storage_file_free(file);
  147. persistence.current_file = NULL;
  148. }
  149. }
  150. if(persistence.global_buffer != NULL) {
  151. memcpy(persistence.global_buffer + persistence.buffer_offset, ptr, length);
  152. persistence.buffer_offset += length;
  153. if(persistence.left_bytes > 0) {
  154. persistence.left_bytes -= length;
  155. }
  156. FURI_LOG_I("MTP", "Buffer offset: %ld", persistence.buffer_offset);
  157. FURI_LOG_I("MTP", "Left bytes: %ld", persistence.left_bytes);
  158. if(persistence.left_bytes == 0) {
  159. handle_mtp_data_complete(mtp);
  160. }
  161. }
  162. }
  163. void handle_mtp_data_complete(AppMTP* mtp) {
  164. // dump the buffer
  165. print_bytes("MTP Data", persistence.global_buffer, persistence.buffer_offset);
  166. switch(persistence.op) {
  167. case MTP_OP_SEND_OBJECT_INFO: {
  168. // parse the object info - persistence.global_buffer doesn't contain MTPHeader
  169. struct ObjectInfoHeader* info = (struct ObjectInfoHeader*)(persistence.global_buffer);
  170. uint8_t* ptr = persistence.global_buffer + sizeof(struct ObjectInfoHeader);
  171. ptr += 12;
  172. // dump the ptr
  173. print_bytes(
  174. "MTP FileName", ptr, persistence.buffer_offset - sizeof(struct ObjectInfoHeader));
  175. bool is_dir = info->format == MTP_FORMAT_ASSOCIATION;
  176. char* name = NULL;
  177. if(CheckMTPStringHasUnicode(ptr)) {
  178. // unicode isn't supported
  179. name = is_dir ? "New Folder" : "New File";
  180. } else {
  181. name = ReadMTPString(ptr);
  182. }
  183. // if the name is blank, generate random name
  184. if(*name == 0) {
  185. char* random_name = malloc(sizeof(char) * 10);
  186. strcpy(random_name, "file_");
  187. int random = rand() % 1000;
  188. char random_str[4];
  189. itoa(random, random_str, 10);
  190. strcpy(random_name + 5, random_str);
  191. name = random_name;
  192. }
  193. FURI_LOG_I("MTP", "Creating object: %s", name);
  194. uint32_t storage_id = persistence.params[0];
  195. uint32_t parent = persistence.params[1];
  196. char* base_path = get_base_path_from_storage_id(storage_id);
  197. if(base_path == NULL) {
  198. FURI_LOG_E("MTP", "Invalid storage ID: %ld", storage_id);
  199. send_mtp_response(
  200. mtp, 3, MTP_RESP_INVALID_STORAGE_ID, persistence.transaction_id, NULL);
  201. break;
  202. }
  203. if(parent != 0xffffffff) {
  204. base_path = get_path_from_handle(mtp, parent);
  205. if(base_path == NULL) {
  206. FURI_LOG_E("MTP", "Invalid parent handle: %ld", parent);
  207. send_mtp_response(
  208. mtp, 3, MTP_RESP_INVALID_OBJECT_HANDLE, persistence.transaction_id, NULL);
  209. break;
  210. }
  211. }
  212. char* full_path = malloc(sizeof(char) * 256);
  213. merge_path(full_path, base_path, name);
  214. FURI_LOG_I("MTP", "Format: %04x", info->format);
  215. if(is_dir) {
  216. if(!storage_dir_exists(mtp->storage, full_path)) {
  217. if(!storage_simply_mkdir(mtp->storage, full_path)) {
  218. FURI_LOG_E("MTP", "Failed to create directory: %s", full_path);
  219. send_mtp_response(
  220. mtp, 3, MTP_RESP_GENERAL_ERROR, persistence.transaction_id, NULL);
  221. free(full_path);
  222. break;
  223. }
  224. }
  225. } else {
  226. if(!storage_file_exists(mtp->storage, full_path)) {
  227. // create file
  228. File* file = storage_file_alloc(mtp->storage);
  229. if(!storage_file_open(file, full_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
  230. FURI_LOG_E("MTP", "Failed to create file: %s", full_path);
  231. send_mtp_response(
  232. mtp, 3, MTP_RESP_GENERAL_ERROR, persistence.transaction_id, NULL);
  233. storage_file_free(file);
  234. free(full_path);
  235. break;
  236. }
  237. storage_file_close(file);
  238. storage_file_free(file);
  239. persistence.prev_handle = issue_object_handle(mtp, full_path);
  240. }
  241. }
  242. uint32_t handle = issue_object_handle(mtp, full_path);
  243. persistence.params[2] = handle;
  244. free(name);
  245. free(full_path);
  246. send_mtp_response(mtp, 3, MTP_RESP_OK, persistence.transaction_id, persistence.params);
  247. break;
  248. }
  249. default:
  250. FURI_LOG_W("MTP", "Unsupported MTP operation in bulk transfer: 0x%04x", persistence.op);
  251. send_mtp_response(mtp, 3, MTP_RESP_UNKNOWN, persistence.transaction_id, NULL);
  252. break;
  253. }
  254. free(persistence.global_buffer);
  255. persistence.global_buffer = NULL;
  256. }
  257. void handle_mtp_response(AppMTP* mtp, struct MTPHeader* header) {
  258. UNUSED(mtp);
  259. FURI_LOG_I("MTP", "Handling MTP response: 0x%04x", header->op);
  260. FURI_LOG_I("MTP", "Transaction ID: %ld", header->transaction_id);
  261. FURI_LOG_I("MTP", "Has Data: %d", persistence.global_buffer != NULL);
  262. FURI_LOG_I("MTP", "Data length: %ld", persistence.buffer_offset);
  263. }
  264. void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
  265. uint16_t mtp_op = container->header.op;
  266. FURI_LOG_I("MTP", "Handling MTP operation: 0x%04x", mtp_op);
  267. switch(mtp_op) {
  268. case MTP_OP_GET_DEVICE_INFO:
  269. FURI_LOG_I("MTP", "GetDeviceInfo operation");
  270. send_device_info(mtp, container->header.transaction_id);
  271. // Process the GetDeviceInfo operation
  272. break;
  273. case MTP_OP_OPEN_SESSION:
  274. case MTP_OP_CLOSE_SESSION:
  275. //FURI_LOG_I("MTP", "Open/CloseSession operation (STUB)");
  276. send_mtp_response(mtp, 3, MTP_RESP_OK, container->header.transaction_id, NULL);
  277. break;
  278. case MTP_OP_GET_STORAGE_IDS:
  279. FURI_LOG_I("MTP", "GetStorageIDs operation");
  280. send_storage_ids(mtp, container->header.transaction_id);
  281. break;
  282. case MTP_OP_GET_STORAGE_INFO: {
  283. FURI_LOG_I("MTP", "GetStorageInfo operation");
  284. uint8_t* info = malloc(sizeof(uint8_t) * 256);
  285. int length = GetStorageInfo(mtp, container->params[0], info);
  286. send_mtp_response_buffer(
  287. mtp,
  288. MTP_TYPE_DATA,
  289. MTP_OP_GET_STORAGE_INFO,
  290. container->header.transaction_id,
  291. info,
  292. length);
  293. send_mtp_response_buffer(
  294. mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
  295. free(info);
  296. break;
  297. }
  298. case MTP_OP_GET_OBJECT_HANDLES:
  299. FURI_LOG_I("MTP", "GetObjectHandles operation");
  300. if(container->params[1]) {
  301. send_mtp_response_buffer(
  302. mtp,
  303. MTP_TYPE_RESPONSE,
  304. MTP_RESP_SPEC_BY_FORMAT_UNSUPPORTED,
  305. container->header.transaction_id,
  306. NULL,
  307. 0);
  308. break;
  309. } else {
  310. uint8_t* buffer = malloc(sizeof(uint8_t) * 256);
  311. int length = GetObjectHandles(mtp, container->params[0], container->params[2], buffer);
  312. send_mtp_response_buffer(
  313. mtp,
  314. MTP_TYPE_DATA,
  315. MTP_OP_GET_OBJECT_HANDLES,
  316. container->header.transaction_id,
  317. buffer,
  318. length);
  319. send_mtp_response_buffer(
  320. mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
  321. free(buffer);
  322. }
  323. break;
  324. case MTP_OP_GET_OBJECT_INFO: {
  325. FURI_LOG_I("MTP", "GetObjectInfo operation");
  326. uint8_t* buffer = malloc(sizeof(uint8_t) * 512);
  327. int length = GetObjectInfo(mtp, container->params[0], buffer);
  328. if(length < 0) {
  329. send_mtp_response(
  330. mtp,
  331. MTP_TYPE_RESPONSE,
  332. MTP_RESP_INVALID_OBJECT_HANDLE,
  333. container->header.transaction_id,
  334. NULL);
  335. break;
  336. }
  337. send_mtp_response_buffer(
  338. mtp,
  339. MTP_TYPE_DATA,
  340. MTP_OP_GET_OBJECT_INFO,
  341. container->header.transaction_id,
  342. buffer,
  343. length);
  344. free(buffer);
  345. send_mtp_response_buffer(
  346. mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
  347. break;
  348. }
  349. case MTP_OP_GET_OBJECT_PROPS_SUPPORTED: {
  350. FURI_LOG_I("MTP", "GetObjectPropsSupported operation");
  351. uint8_t* buffer = malloc(sizeof(uint8_t) * 256);
  352. uint32_t count = sizeof(supported_object_props) / sizeof(uint16_t);
  353. memcpy(buffer, &count, sizeof(uint32_t));
  354. memcpy(buffer + sizeof(uint32_t), supported_object_props, sizeof(uint16_t) * count);
  355. int length = sizeof(uint32_t) + sizeof(uint16_t) * count;
  356. send_mtp_response_buffer(
  357. mtp,
  358. MTP_TYPE_DATA,
  359. MTP_OP_GET_OBJECT_PROPS_SUPPORTED,
  360. container->header.transaction_id,
  361. buffer,
  362. length);
  363. free(buffer);
  364. send_mtp_response_buffer(
  365. mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
  366. break;
  367. }
  368. case MTP_OP_DELETE_OBJECT:
  369. FURI_LOG_I("MTP", "DeleteObject operation");
  370. if(DeleteObject(mtp, container->params[0])) {
  371. send_mtp_response(mtp, 3, MTP_RESP_OK, container->header.transaction_id, NULL);
  372. } else {
  373. send_mtp_response(
  374. mtp, 3, MTP_RESP_INVALID_OBJECT_HANDLE, container->header.transaction_id, NULL);
  375. }
  376. break;
  377. case MTP_OP_GET_DEVICE_PROP_VALUE:
  378. FURI_LOG_I("MTP", "GetDevicePropValue operation");
  379. send_device_prop_value(mtp, container->header.transaction_id, container->params[0]);
  380. // Process the GetDevicePropValue operation
  381. break;
  382. case MTP_OP_GET_DEVICE_PROP_DESC:
  383. FURI_LOG_I("MTP", "GetDevicePropDesc operation");
  384. send_device_prop_desc(mtp, container->header.transaction_id, container->params[0]);
  385. // Process the GetDevicePropDesc operation
  386. break;
  387. // Handle bulk transfer specific operations
  388. case MTP_OP_SEND_OBJECT_INFO:
  389. FURI_LOG_I("MTP", "SendObjectInfo operation");
  390. setup_persistence(container);
  391. break;
  392. case MTP_OP_SEND_OBJECT:
  393. FURI_LOG_I("MTP", "SendObject operation");
  394. setup_persistence(container);
  395. break;
  396. case MTP_OP_GET_OBJECT: {
  397. FURI_LOG_I("MTP", "GetObject operation");
  398. GetObject(mtp, container->header.transaction_id, container->params[0]);
  399. break;
  400. }
  401. // Handle other bulk operations here...
  402. default:
  403. FURI_LOG_W("MTP", "Unsupported MTP operation in bulk transfer: 0x%04x", mtp_op);
  404. send_mtp_response(mtp, 3, MTP_RESP_UNKNOWN, container->header.transaction_id, NULL);
  405. break;
  406. }
  407. }
  408. void send_storage_ids(AppMTP* mtp, uint32_t transaction_id) {
  409. uint32_t count;
  410. uint32_t storage_ids[3];
  411. GetStorageIDs(mtp, storage_ids, &count);
  412. FURI_LOG_I("MTP", "Sending storage IDs: %ld storages", count);
  413. uint32_t payload[3] = {count, storage_ids[0], storage_ids[1]};
  414. send_mtp_response_buffer(
  415. mtp,
  416. MTP_TYPE_DATA,
  417. MTP_OP_GET_STORAGE_IDS,
  418. transaction_id,
  419. (uint8_t*)payload,
  420. sizeof(uint32_t) * (count + 1));
  421. send_mtp_response_buffer(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL, 0);
  422. }
  423. void send_device_info(AppMTP* mtp, uint32_t transaction_id) {
  424. uint8_t* response = malloc(sizeof(uint8_t) * 256);
  425. int length = BuildDeviceInfo(response);
  426. send_mtp_response_buffer(
  427. mtp, MTP_TYPE_DATA, MTP_OP_GET_DEVICE_INFO, transaction_id, response, length);
  428. send_mtp_response_buffer(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL, 0);
  429. free(response);
  430. }
  431. void send_device_prop_value(AppMTP* mtp, uint32_t transaction_id, uint32_t prop_code) {
  432. uint8_t* response = malloc(sizeof(uint8_t) * 256);
  433. int length = GetDevicePropValue(prop_code, response);
  434. send_mtp_response_buffer(
  435. mtp, MTP_TYPE_DATA, MTP_OP_GET_DEVICE_PROP_VALUE, transaction_id, response, length);
  436. send_mtp_response_buffer(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL, 0);
  437. free(response);
  438. }
  439. void send_device_prop_desc(AppMTP* mtp, uint32_t transaction_id, uint32_t prop_code) {
  440. uint8_t* response = malloc(sizeof(uint8_t) * 256);
  441. int length = GetDevicePropDesc(prop_code, response);
  442. send_mtp_response_buffer(
  443. mtp, MTP_TYPE_DATA, MTP_OP_GET_DEVICE_PROP_DESC, transaction_id, response, length);
  444. send_mtp_response_buffer(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL, 0);
  445. free(response);
  446. }
  447. char* get_path_from_handle(AppMTP* mtp, uint32_t handle) {
  448. FileHandle* current = mtp->handles;
  449. while(current != NULL) {
  450. if(current->handle == handle) {
  451. return current->path;
  452. }
  453. current = current->next;
  454. }
  455. return NULL;
  456. }
  457. uint32_t issue_object_handle(AppMTP* mtp, char* path) {
  458. int handle = 0;
  459. int length = strlen(path);
  460. char* path_store = malloc(sizeof(char) * (length + 1));
  461. strcpy(path_store, path);
  462. if(mtp->handles == NULL) {
  463. mtp->handles = malloc(sizeof(FileHandle));
  464. mtp->handles->handle = handle;
  465. mtp->handles->path = path_store;
  466. mtp->handles->next = NULL;
  467. return handle;
  468. }
  469. FileHandle* current = mtp->handles;
  470. if(strcmp(current->path, path) == 0) {
  471. return current->handle;
  472. }
  473. while(current->next != NULL) {
  474. if(strcmp(current->path, path) == 0) {
  475. return current->handle;
  476. }
  477. current = current->next;
  478. handle++;
  479. }
  480. current->next = malloc(sizeof(FileHandle));
  481. current = current->next;
  482. handle++;
  483. current->handle = handle;
  484. current->path = path_store;
  485. current->next = NULL;
  486. return handle;
  487. }
  488. char* get_base_path_from_storage_id(uint32_t storage_id) {
  489. if(storage_id == INTERNAL_STORAGE_ID) {
  490. return STORAGE_INT_PATH_PREFIX;
  491. } else if(storage_id == EXTERNAL_STORAGE_ID) {
  492. return STORAGE_EXT_PATH_PREFIX;
  493. }
  494. return NULL;
  495. }
  496. int list_and_issue_handles(
  497. AppMTP* mtp,
  498. uint32_t storage_id,
  499. uint32_t association,
  500. uint32_t* handles) {
  501. Storage* storage = mtp->storage;
  502. char* base_path = get_base_path_from_storage_id(storage_id);
  503. if(base_path == NULL) {
  504. base_path = "";
  505. }
  506. File* dir = storage_file_alloc(storage);
  507. if(association == 0xffffffff) {
  508. // count the objects in the root directory
  509. storage_dir_open(dir, base_path);
  510. } else {
  511. char* path = get_path_from_handle(mtp, association);
  512. FURI_LOG_I("MTP", "Association path: %s", path);
  513. if(path == NULL) {
  514. return 0;
  515. }
  516. storage_dir_open(dir, path);
  517. base_path = path;
  518. }
  519. int count = 0;
  520. FileInfo fileinfo;
  521. char* file_name = malloc(sizeof(char) * 256);
  522. char* full_path = malloc(sizeof(char) * 256);
  523. while(storage_dir_read(dir, &fileinfo, file_name, 256)) {
  524. if(file_info_is_dir(&fileinfo)) {
  525. FURI_LOG_I("MTP", "Found directory: %s", file_name);
  526. } else {
  527. FURI_LOG_I("MTP", "Found file: %s", file_name);
  528. }
  529. merge_path(full_path, base_path, file_name);
  530. FURI_LOG_I("MTP", "Full path: %s", full_path);
  531. uint32_t handle = issue_object_handle(mtp, full_path);
  532. if(handles != NULL) {
  533. handles[count] = handle;
  534. }
  535. count++;
  536. }
  537. FURI_LOG_I("MTP", "Getting number of objects in storage %ld", storage_id);
  538. FURI_LOG_I("MTP", "Base path: %s", base_path);
  539. FURI_LOG_I("MTP", "Association: %ld", association);
  540. FURI_LOG_I("MTP", "Number of objects: %d", count);
  541. storage_dir_close(dir);
  542. storage_file_free(dir);
  543. free(file_name);
  544. free(full_path);
  545. return count;
  546. }
  547. struct GetObjectContext {
  548. File* file;
  549. };
  550. int GetObject_callback(void* ctx, uint8_t* buffer, int length) {
  551. struct GetObjectContext* obj_ctx = (struct GetObjectContext*)ctx;
  552. return storage_file_read(obj_ctx->file, buffer, length);
  553. }
  554. void GetObject(AppMTP* mtp, uint32_t transaction_id, uint32_t handle) {
  555. char* path = get_path_from_handle(mtp, handle);
  556. if(path == NULL) {
  557. send_mtp_response(
  558. mtp, MTP_TYPE_RESPONSE, MTP_RESP_INVALID_OBJECT_HANDLE, transaction_id, NULL);
  559. return;
  560. }
  561. FURI_LOG_I("MTP", "Getting object: %s", path);
  562. Storage* storage = mtp->storage;
  563. File* file = storage_file_alloc(storage);
  564. if(!storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  565. FURI_LOG_E("MTP", "Failed to open file: %s", path);
  566. send_mtp_response(
  567. mtp, MTP_TYPE_RESPONSE, MTP_RESP_INVALID_OBJECT_HANDLE, transaction_id, NULL);
  568. return;
  569. }
  570. uint32_t size = storage_file_size(file);
  571. struct GetObjectContext ctx = {
  572. .file = file,
  573. };
  574. send_mtp_response_stream(
  575. mtp, MTP_TYPE_DATA, MTP_OP_GET_OBJECT, transaction_id, &ctx, GetObject_callback, size);
  576. send_mtp_response(mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, transaction_id, NULL);
  577. storage_file_close(file);
  578. storage_file_free(file);
  579. }
  580. int GetObjectInfo(AppMTP* mtp, uint32_t handle, uint8_t* buffer) {
  581. ObjectInfoHeader* header = (ObjectInfoHeader*)buffer;
  582. uint8_t* ptr = buffer + sizeof(ObjectInfoHeader);
  583. char* path = get_path_from_handle(mtp, handle);
  584. if(path == NULL) {
  585. return -1;
  586. }
  587. FURI_LOG_I("MTP", "Getting object info for handle %ld", handle);
  588. FURI_LOG_I("MTP", "Path: %s", path);
  589. header->protection_status = 0;
  590. Storage* storage = mtp->storage;
  591. File* file = storage_file_alloc(storage);
  592. uint16_t length;
  593. FileInfo fileinfo;
  594. FS_Error err = storage_common_stat(storage, path, &fileinfo);
  595. if(err != FSE_OK) {
  596. FURI_LOG_E("MTP", "Failed to get file info: %s", filesystem_api_error_get_desc(err));
  597. return -1;
  598. }
  599. if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) {
  600. FURI_LOG_I("MTP", "Object in Internal storage");
  601. header->storage_id = INTERNAL_STORAGE_ID;
  602. } else if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) {
  603. FURI_LOG_I("MTP", "Object in External storage");
  604. header->storage_id = EXTERNAL_STORAGE_ID;
  605. } else {
  606. return -1;
  607. }
  608. if(file_info_is_dir(&fileinfo)) {
  609. FURI_LOG_I("MTP", "Directory");
  610. header->format = MTP_FORMAT_ASSOCIATION;
  611. header->association_type = 0x0001; // Generic folder
  612. } else {
  613. FURI_LOG_I("MTP", "Undefined type");
  614. header->format = MTP_FORMAT_UNDEFINED;
  615. }
  616. header->compressed_size = fileinfo.size;
  617. header->thumb_format = 0;
  618. header->thumb_compressed_size = 0;
  619. header->thumb_pix_width = 0;
  620. header->thumb_pix_height = 0;
  621. header->image_pix_width = 0;
  622. header->image_pix_height = 0;
  623. header->image_bit_depth = 0;
  624. /*
  625. // is on root directory (/int or /ext)
  626. if(strchr(path + 1, '/') == NULL) {
  627. header->parent_object = 0;
  628. } else {
  629. char* parent_path = malloc(sizeof(char) * 256);
  630. strcpy(parent_path, path);
  631. char* last_slash = strrchr(parent_path, '/');
  632. *last_slash = '\0';
  633. header->parent_object = issue_object_handle(mtp, parent_path);
  634. free(parent_path);
  635. }
  636. */
  637. char* file_name = strrchr(path, '/');
  638. FURI_LOG_I("MTP", "File name: %s", file_name + 1);
  639. WriteMTPString(ptr, file_name + 1, &length);
  640. ptr += length;
  641. // get created
  642. WriteMTPString(ptr, "20240608T010702", &length);
  643. ptr += length;
  644. // get last modified
  645. WriteMTPString(ptr, "20240608T010702", &length);
  646. ptr += length;
  647. // get keywords
  648. WriteMTPString(ptr, "", &length);
  649. ptr += length;
  650. storage_file_free(file);
  651. return ptr - buffer;
  652. }
  653. bool CheckMTPStringHasUnicode(uint8_t* buffer) {
  654. uint8_t* base = buffer;
  655. int len = *base;
  656. uint16_t* ptr = (uint16_t*)(base + 1);
  657. for(int i = 0; i < len; i++) {
  658. if(ptr[i] > 0x7F) {
  659. return true;
  660. }
  661. }
  662. return false;
  663. }
  664. char* ReadMTPString(uint8_t* buffer) {
  665. int len16 = *(uint8_t*)buffer;
  666. if(len16 == 0) {
  667. return "";
  668. }
  669. char* str = malloc(sizeof(char) * len16);
  670. uint8_t* base = buffer + 1;
  671. uint16_t* ptr = (uint16_t*)base;
  672. for(int i = 0; i < len16; i++) {
  673. str[i] = *ptr++;
  674. }
  675. return str;
  676. }
  677. int GetNumObjects(AppMTP* mtp, uint32_t storage_id, uint32_t association) {
  678. return list_and_issue_handles(mtp, storage_id, association, NULL);
  679. }
  680. int GetObjectHandles(AppMTP* mtp, uint32_t storage_id, uint32_t association, uint8_t* buffer) {
  681. uint8_t* ptr = buffer;
  682. uint16_t length;
  683. UNUSED(length);
  684. // For now, just return a single object handle
  685. int count = GetNumObjects(mtp, storage_id, association);
  686. *(uint32_t*)ptr = count;
  687. ptr += sizeof(uint32_t);
  688. uint32_t* handles = (uint32_t*)ptr;
  689. length = list_and_issue_handles(mtp, storage_id, association, handles);
  690. ptr += length * sizeof(uint32_t);
  691. return ptr - buffer;
  692. }
  693. int GetDevicePropValue(uint32_t prop_code, uint8_t* buffer) {
  694. uint8_t* ptr = buffer;
  695. uint16_t length;
  696. switch(prop_code) {
  697. case 0xd402:
  698. WriteMTPString(ptr, "Flipper Zero", &length);
  699. ptr += length;
  700. break;
  701. default:
  702. // Unsupported property
  703. break;
  704. }
  705. return ptr - buffer;
  706. }
  707. int GetDevicePropDesc(uint32_t prop_code, uint8_t* buffer) {
  708. uint8_t* ptr = buffer;
  709. uint16_t length;
  710. switch(prop_code) {
  711. case 0xd402:
  712. // Device friendly name
  713. *(uint16_t*)ptr = prop_code;
  714. ptr += 2;
  715. // type is string
  716. *(uint16_t*)ptr = 0xffff;
  717. ptr += 2;
  718. // read-only
  719. *(uint16_t*)ptr = 0x0000;
  720. ptr += 2;
  721. length = GetDevicePropValue(prop_code, ptr);
  722. ptr += length;
  723. length = GetDevicePropValue(prop_code, ptr);
  724. ptr += length;
  725. // no-form
  726. *(uint16_t*)ptr = 0x0000;
  727. ptr += 2;
  728. break;
  729. default:
  730. // Unsupported property
  731. break;
  732. }
  733. return ptr - buffer;
  734. }
  735. void send_mtp_response_stream(
  736. AppMTP* mtp,
  737. uint16_t resp_type,
  738. uint16_t resp_code,
  739. uint32_t transaction_id,
  740. void* callback_context,
  741. int (*callback)(void* ctx, uint8_t* buffer, int length),
  742. uint32_t length) {
  743. int chunk_idx = 0;
  744. size_t buffer_available = MTP_MAX_PACKET_SIZE;
  745. uint8_t* buffer = malloc(sizeof(uint8_t) * buffer_available);
  746. uint8_t* ptr = buffer;
  747. uint32_t sent_length = 0;
  748. FURI_LOG_I("MTP", "Sending MTP response stream: %ld bytes", length);
  749. do {
  750. buffer_available = MTP_MAX_PACKET_SIZE;
  751. ptr = buffer; // reset the pointer
  752. if(chunk_idx == 0) {
  753. struct MTPHeader* hdr = (struct MTPHeader*)buffer;
  754. hdr->len = length + sizeof(*hdr);
  755. hdr->type = resp_type;
  756. hdr->op = resp_code;
  757. hdr->transaction_id = transaction_id;
  758. ptr += sizeof(*hdr);
  759. buffer_available -= sizeof(*hdr);
  760. }
  761. FURI_LOG_I("MTP", "Remaining bytes for packet: %d", buffer_available);
  762. int read_bytes = callback(callback_context, ptr, buffer_available);
  763. uint32_t usb_bytes = (ptr - buffer) + read_bytes;
  764. FURI_LOG_I("MTP", "USB packet size: %ld", usb_bytes);
  765. usbd_ep_write(mtp->dev, MTP_EP_IN_ADDR, buffer, usb_bytes);
  766. sent_length += usb_bytes;
  767. chunk_idx++;
  768. FURI_LOG_I(
  769. "MTP",
  770. "Sent chunk %d (currently sent: %ld/%ld)",
  771. chunk_idx,
  772. sent_length - sizeof(struct MTPHeader),
  773. length);
  774. } while(sent_length < length + sizeof(struct MTPHeader));
  775. free(buffer);
  776. }
  777. int send_mtp_response_buffer_callback(void* ctx, uint8_t* buffer, int size) {
  778. struct MTPResponseBufferContext* context = (struct MTPResponseBufferContext*)ctx;
  779. if(context->buffer == NULL || context->size == 0) {
  780. return 0;
  781. }
  782. uint32_t remaining = context->size - context->sent;
  783. uint32_t to_send = size;
  784. if(remaining < to_send) {
  785. to_send = remaining;
  786. }
  787. memcpy(buffer, context->buffer + context->sent, to_send);
  788. context->sent += to_send;
  789. return to_send;
  790. }
  791. void send_mtp_response_buffer(
  792. AppMTP* mtp,
  793. uint16_t resp_type,
  794. uint16_t resp_code,
  795. uint32_t transaction_id,
  796. uint8_t* buffer,
  797. uint32_t size) {
  798. struct MTPResponseBufferContext* ctx = malloc(sizeof(struct MTPResponseBufferContext));
  799. ctx->buffer = buffer;
  800. ctx->size = size;
  801. ctx->sent = 0;
  802. send_mtp_response_stream(
  803. mtp, resp_type, resp_code, transaction_id, ctx, send_mtp_response_buffer_callback, size);
  804. free(ctx);
  805. }
  806. void send_mtp_response(
  807. AppMTP* mtp,
  808. uint16_t resp_type,
  809. uint16_t resp_code,
  810. uint32_t transaction_id,
  811. uint32_t* params) {
  812. uint32_t response[5] = {0};
  813. if(params != NULL) {
  814. memcpy(response, params, sizeof(uint32_t) * 5);
  815. }
  816. send_mtp_response_buffer(
  817. mtp, resp_type, resp_code, transaction_id, (uint8_t*)response, sizeof(response));
  818. }
  819. int mtp_handle_class_control(AppMTP* mtp, usbd_device* dev, usbd_ctlreq* req) {
  820. UNUSED(dev);
  821. int value = -1;
  822. uint32_t handle = req->wIndex;
  823. uint8_t* buffer = req->data;
  824. uint32_t size = req->wLength;
  825. UNUSED(handle);
  826. UNUSED(buffer);
  827. UNUSED(size);
  828. switch(req->bRequest) {
  829. case MTP_REQ_CANCEL:
  830. // Handle Cancel request
  831. value = 0;
  832. break;
  833. case MTP_REQ_GET_EXT_EVENT_DATA:
  834. // Handle GetExtEventData request
  835. value = 0;
  836. break;
  837. case MTP_REQ_RESET:
  838. // Handle Reset request
  839. mtp->state = MTPStateReady;
  840. value = 0;
  841. break;
  842. case MTP_REQ_GET_DEVICE_STATUS:
  843. // Handle GetDeviceStatus request
  844. struct mtp_device_status* status = (struct mtp_device_status*)req->data;
  845. status->wLength = sizeof(*status);
  846. status->wCode = (mtp->state == MTPStateBusy) ? MTP_RESP_DEVICE_BUSY : MTP_RESP_OK;
  847. value = status->wLength;
  848. break;
  849. default:
  850. // Unsupported request
  851. break;
  852. }
  853. return value;
  854. }
  855. int BuildDeviceInfo(uint8_t* buffer) {
  856. uint8_t* ptr = buffer;
  857. uint16_t length;
  858. // Standard version
  859. *(uint16_t*)ptr = 100;
  860. ptr += sizeof(uint16_t);
  861. // Vendor extension ID
  862. *(uint32_t*)ptr = MTP_VENDOR_EXTENSION_ID;
  863. ptr += sizeof(uint32_t);
  864. // Vendor extension version
  865. *(uint16_t*)ptr = MTP_VENDOR_EXTENSION_VERSION;
  866. ptr += sizeof(uint16_t);
  867. // Vendor extension description
  868. WriteMTPString(ptr, "microsoft.com: 1.0;", &length);
  869. ptr += length;
  870. // Functional mode
  871. *(uint16_t*)ptr = MTP_FUNCTIONAL_MODE;
  872. ptr += sizeof(uint16_t);
  873. // Operations supported
  874. length = sizeof(supported_operations) / sizeof(uint16_t);
  875. *(uint32_t*)ptr = length; // Number of supported operations
  876. ptr += sizeof(uint32_t);
  877. for(int i = 0; i < length; i++) {
  878. *(uint16_t*)ptr = supported_operations[i];
  879. ptr += sizeof(uint16_t);
  880. }
  881. // Supported events (example, add as needed)
  882. *(uint32_t*)ptr = 0; // Number of supported events
  883. ptr += sizeof(uint32_t);
  884. length = sizeof(supported_device_properties) / sizeof(uint16_t);
  885. *(uint32_t*)ptr = length; // Number of supported device properties
  886. ptr += sizeof(uint32_t);
  887. for(int i = 0; i < length; i++) {
  888. *(uint16_t*)ptr = supported_device_properties[i];
  889. ptr += sizeof(uint16_t);
  890. }
  891. // Supported capture formats (example, add as needed)
  892. *(uint32_t*)ptr = 0; // Number of supported capture formats
  893. ptr += sizeof(uint32_t);
  894. // Supported playback formats (example, add as needed)
  895. length = sizeof(supported_playback_formats) / sizeof(uint16_t);
  896. *(uint32_t*)ptr = length; // Number of supported playback formats
  897. ptr += sizeof(uint32_t);
  898. for(int i = 0; i < length; i++) {
  899. *(uint16_t*)ptr = supported_playback_formats[i];
  900. ptr += sizeof(uint16_t);
  901. }
  902. // Manufacturer
  903. WriteMTPString(ptr, USB_MANUFACTURER_STRING, &length);
  904. ptr += length;
  905. // Model
  906. WriteMTPString(ptr, USB_DEVICE_MODEL, &length);
  907. ptr += length;
  908. // Device version
  909. WriteMTPString(ptr, MTP_DEVICE_VERSION, &length);
  910. ptr += length;
  911. // Serial number
  912. WriteMTPString(ptr, MTP_DEVICE_SERIAL, &length);
  913. ptr += length;
  914. return ptr - buffer;
  915. }
  916. void GetStorageIDs(AppMTP* mtp, uint32_t* storage_ids, uint32_t* count) {
  917. SDInfo sd_info;
  918. FS_Error err = storage_sd_info(mtp->storage, &sd_info);
  919. // Due to filesystem change, we only have external storage.
  920. // storage_ids[0] = INTERNAL_STORAGE_ID;
  921. // // Check if SD card is present
  922. // if(err != FSE_OK) {
  923. // FURI_LOG_E("MTP", "SD Card not found");
  924. // *count = 1; // We have only one storage
  925. // return;
  926. // }
  927. // Check if SD card is present
  928. if(err != FSE_OK) {
  929. FURI_LOG_E("MTP", "SD Card not found");
  930. *count = 0; // No storage.
  931. return;
  932. }
  933. storage_ids[0] = EXTERNAL_STORAGE_ID;
  934. *count = 1;
  935. }
  936. int GetStorageInfo(AppMTP* mtp, uint32_t storage_id, uint8_t* buf) {
  937. MTPStorageInfoHeader* info = (MTPStorageInfoHeader*)buf;
  938. uint8_t* ptr = buf + sizeof(MTPStorageInfoHeader);
  939. uint16_t length;
  940. info->free_space_in_objects = 20ul;
  941. info->filesystem_type = 0x0002; // Generic hierarchical
  942. info->access_capability = 0x0000; // Read-write
  943. FURI_LOG_I("MTP", "Getting storage info for storage ID %04lx", storage_id);
  944. if(storage_id == INTERNAL_STORAGE_ID) {
  945. // Fill in details for internal storage
  946. info->storage_type = 0x0003; // Fixed RAM
  947. // Fill in details for internal storage
  948. uint64_t total_space;
  949. uint64_t free_space;
  950. FS_Error err = storage_common_fs_info(
  951. mtp->storage, STORAGE_INT_PATH_PREFIX, &total_space, &free_space);
  952. if(err != FSE_OK) {
  953. info->max_capacity = 0;
  954. info->free_space_in_bytes = 0;
  955. } else {
  956. info->max_capacity = total_space / BLOCK_SIZE;
  957. info->free_space_in_bytes = free_space / BLOCK_SIZE;
  958. }
  959. WriteMTPString(ptr, "Internal Storage", &length);
  960. ptr += length;
  961. WriteMTPString(ptr, "INT_STORAGE", &length);
  962. ptr += length;
  963. } else if(storage_id == EXTERNAL_STORAGE_ID) {
  964. SDInfo sd_info;
  965. FS_Error err = storage_sd_info(mtp->storage, &sd_info);
  966. // Fill in details for internal storage
  967. info->storage_type = 0x0004; // Removable RAM
  968. if(err != FSE_OK) {
  969. info->max_capacity = 0;
  970. info->free_space_in_bytes = 0;
  971. FURI_LOG_E("MTP", "SD Card not found");
  972. } else {
  973. // Fill in details for external storage
  974. info->max_capacity = (uint64_t)sd_info.kb_total * 1024 / BLOCK_SIZE;
  975. info->free_space_in_bytes = (uint64_t)sd_info.kb_free * 1024 / BLOCK_SIZE;
  976. }
  977. WriteMTPString(ptr, "SD Card", &length);
  978. ptr += length;
  979. WriteMTPString(ptr, "SD_CARD", &length);
  980. ptr += length;
  981. }
  982. // try to convert into big endian???
  983. //info->max_capacity = __builtin_bswap64(info->max_capacity);
  984. //info->free_space_in_bytes = __builtin_bswap64(info->free_space_in_bytes);
  985. return ptr - buf;
  986. }
  987. // Microsoft-style UTF-16LE string:
  988. void WriteMTPString(uint8_t* buffer, const char* str, uint16_t* length) {
  989. uint8_t* ptr = buffer;
  990. uint8_t str_len = strlen(str);
  991. FURI_LOG_I("MTP", "Writing MTP string: %s", str);
  992. FURI_LOG_I("MTP", "String length: %d", str_len);
  993. // extra handling for empty string
  994. if(str_len == 0) {
  995. *ptr = 0x00;
  996. // that's it!
  997. *length = 1;
  998. return;
  999. }
  1000. *ptr = str_len + 1; // Length byte (number of characters including the null terminator)
  1001. ptr++;
  1002. while(*str) {
  1003. *ptr++ = *str++;
  1004. *ptr++ = 0x00; // UTF-16LE encoding (add null byte for each character)
  1005. }
  1006. *ptr++ = 0x00; // Null terminator (UTF-16LE)
  1007. *ptr++ = 0x00;
  1008. FURI_LOG_I("MTP", "String byte length: %d", ptr - buffer);
  1009. *length = ptr - buffer;
  1010. }
  1011. void WriteMTPBEString(uint8_t* buffer, const char* str, uint16_t* length) {
  1012. uint8_t* ptr = buffer;
  1013. uint8_t str_len = strlen(str);
  1014. *ptr++ = str_len + 1; // Length byte (number of characters including the null terminator)
  1015. while(*str) {
  1016. *ptr++ = 0x00; // UTF-16BE encoding (add null byte for each character)
  1017. *ptr++ = *str++;
  1018. }
  1019. *ptr++ = 0x00; // Null terminator (UTF-16LE)
  1020. *ptr++ = 0x00;
  1021. *length = ptr - buffer;
  1022. }
  1023. bool DeleteObject(AppMTP* mtp, uint32_t handle) {
  1024. UNUSED(mtp);
  1025. FURI_LOG_I("MTP", "Deleting object %ld", handle);
  1026. char* path = get_path_from_handle(mtp, handle);
  1027. if(path == NULL) {
  1028. return false;
  1029. }
  1030. FileInfo fileinfo;
  1031. FS_Error err = storage_common_stat(mtp->storage, path, &fileinfo);
  1032. if(err == FSE_OK) {
  1033. if(file_info_is_dir(&fileinfo)) {
  1034. if(!storage_simply_remove_recursive(mtp->storage, path)) {
  1035. return false;
  1036. }
  1037. } else {
  1038. if(storage_common_remove(mtp->storage, path) != FSE_OK) {
  1039. return false;
  1040. }
  1041. }
  1042. return true;
  1043. }
  1044. return false;
  1045. }