Alex4386 hai 1 ano
pai
achega
e8e0ea02ca
Modificáronse 5 ficheiros con 191 adicións e 15 borrados
  1. 24 3
      README.md
  2. 127 12
      src/scenes/mtp/mtp.c
  3. 14 0
      src/scenes/mtp/mtp.h
  4. 23 0
      src/scenes/mtp/utils.c
  5. 3 0
      src/scenes/mtp/utils.h

+ 24 - 3
README.md

@@ -27,6 +27,24 @@ Due to limitation of the Flipper Zero's hardware, The MTP connection utilizes `F
 > So, the speed of the MTP connection probably will not the bottleneck here.  
 > <b>So, If you need quick copy, Use a proper SD Card Reader.</b>
 
+### Before using...
+Here are some things you should know before using this application:
+
+> [!WARNING]
+> **DO NOT transfer files over 64K** in one go.  
+> This will:
+> - **Crash the Flipper** if the file is too big.
+> - **Corrupt the SD Card filesystem** due to flipper zero's filesystem handler's limitation.
+>
+> This is applicable to both uploading and downloading files.
+
+> [!WARNING]
+> **DO NOT** use `UNICODE` characters in the file/directory names.  
+> Flipper Zero's filesystem isn't designed to handle `UNICODE` characters. such as:
+> - `한글.txt`
+> - `日本語.txt`
+> - `中文.txt`
+
 ### Features
 * Access Internal and SD card storages
 * List files and directories
@@ -35,12 +53,15 @@ Due to limitation of the Flipper Zero's hardware, The MTP connection utilizes `F
   - Large file transfer now **WORKS**!
     - It didn't work since the header did not have proper `size` defined, ignoring the further packets.
     - Now utilizing even less memory via `streaming` support!
+* Move Files into Flipper
+  - **NEW!** Now you can upload files to Flipper Zero!
+  - **Note:** Flipper Zero has limited memory, please refrain from moving files bigger than 64K on one go. (for me 80K was a hard limit)
 * Deleting Files/Directories
 
 ### Known Issues
-* Creating directories, files, uploading files are not supported yet.
-* Due to memory leak happening somewhere, sometimes the Flipper crashes during the file transfer.  
-  - I'm currently busy working on "Creating" part to at least work, so this won't be fixed soon.
+* Renaming directories, files are not supported yet.
+* Fix "memory leaks"  
+  - I'm currently busy working on code cleanup. So, I can't afford to do this right now.
 
 ## How to build
 See [HOW_TO_BUILD.md](HOW_TO_BUILD.md) for more information.  

+ 127 - 12
src/scenes/mtp/mtp.c

@@ -3,6 +3,7 @@
 #include "main.h"
 #include "mtp.h"
 #include "usb_desc.h"
+#include "utils.h"
 
 #define BLOCK_SIZE 65536
 
@@ -21,7 +22,15 @@ uint16_t supported_operations[] = {
     MTP_OP_SEND_OBJECT,
     MTP_OP_DELETE_OBJECT,
     MTP_OP_GET_DEVICE_PROP_DESC,
-    MTP_OP_GET_DEVICE_PROP_VALUE};
+    MTP_OP_GET_DEVICE_PROP_VALUE,
+    MTP_OP_GET_OBJECT_PROPS_SUPPORTED,
+    MTP_OP_SET_OBJECT_PROP_VALUE};
+
+uint16_t supported_object_props[] = {
+    MTP_PROP_STORAGE_ID,
+    MTP_PROP_OBJECT_FORMAT,
+    MTP_PROP_OBJECT_FILE_NAME,
+};
 
 // Supported device properties (example, add as needed)
 uint16_t supported_device_properties[] = {
@@ -108,11 +117,61 @@ void handle_mtp_data_packet(AppMTP* mtp, uint8_t* buffer, int32_t length, bool c
         ptr = (uint8_t*)buffer + sizeof(struct MTPHeader);
 
         switch(header->op) {
-        case MTP_OP_SEND_OBJECT_INFO: {
+        case MTP_OP_SEND_OBJECT_INFO:
+        case MTP_OP_SET_OBJECT_PROP_VALUE: {
             persistence.global_buffer = malloc(sizeof(uint8_t) * 256);
             persistence.buffer_offset = 0;
             break;
         }
+        case MTP_OP_SEND_OBJECT: {
+            persistence.buffer_offset = 0;
+            persistence.left_bytes = header->len - sizeof(struct MTPHeader);
+        }
+        }
+    }
+
+    // for speicific operations.
+    if(persistence.op == MTP_OP_SEND_OBJECT) {
+        uint32_t handle = persistence.prev_handle;
+        if(handle == 0) {
+            FURI_LOG_E("MTP", "Invalid handle for MTP_OP_SEND_OBJECT");
+            send_mtp_response(
+                mtp, 3, MTP_RESP_INVALID_OBJECT_HANDLE, persistence.transaction_id, NULL);
+            return;
+        }
+
+        char* path = get_path_from_handle(mtp, handle);
+        int bytes_length = length - (ptr - buffer);
+        int offset = persistence.buffer_offset;
+
+        FURI_LOG_I("MTP", "Writing to file: %s with offset %02x", path, offset);
+        if(persistence.current_file == NULL) {
+            persistence.current_file = storage_file_alloc(mtp->storage);
+            if(!storage_file_open(persistence.current_file, path, FSAM_WRITE, FSOM_OPEN_EXISTING)) {
+                FURI_LOG_E("MTP", "Failed to open file: %s", path);
+                send_mtp_response(
+                    mtp, 3, MTP_RESP_INVALID_OBJECT_HANDLE, persistence.transaction_id, NULL);
+                storage_file_free(persistence.current_file);
+                persistence.current_file = NULL;
+                return;
+            }
+        }
+
+        File* file = persistence.current_file;
+        // no need since the file is already opened
+        // storage_file_seek(file, offset, SEEK_SET);
+
+        storage_file_write(file, ptr, bytes_length);
+
+        persistence.buffer_offset += bytes_length;
+        persistence.left_bytes -= bytes_length;
+
+        if(persistence.left_bytes <= 0) {
+            send_mtp_response(mtp, 3, MTP_RESP_OK, persistence.transaction_id, NULL);
+            storage_file_close(file);
+            storage_file_free(file);
+
+            persistence.current_file = NULL;
         }
     }
 
@@ -134,16 +193,29 @@ void handle_mtp_data_packet(AppMTP* mtp, uint8_t* buffer, int32_t length, bool c
 }
 
 void handle_mtp_data_complete(AppMTP* mtp) {
-    UNUSED(mtp);
+    // dump the buffer
+    print_bytes("MTP Data", persistence.global_buffer, persistence.buffer_offset);
+
     switch(persistence.op) {
     case MTP_OP_SEND_OBJECT_INFO: {
-        struct ObjectInfoHeader* info =
-            (struct ObjectInfoHeader*)(persistence.global_buffer + sizeof(struct MTPHeader));
+        // parse the object info - persistence.global_buffer doesn't contain MTPHeader
+        struct ObjectInfoHeader* info = (struct ObjectInfoHeader*)(persistence.global_buffer);
+
+        uint8_t* ptr = persistence.global_buffer + sizeof(struct ObjectInfoHeader);
+        ptr += 12;
 
-        uint8_t* ptr =
-            persistence.global_buffer + sizeof(struct MTPHeader) + sizeof(struct ObjectInfoHeader);
-        ptr += 4;
-        char* name = ReadMTPString(ptr);
+        // dump the ptr
+        print_bytes(
+            "MTP FileName", ptr, persistence.buffer_offset - sizeof(struct ObjectInfoHeader));
+
+        bool is_dir = info->format == MTP_FORMAT_ASSOCIATION;
+        char* name = NULL;
+        if(CheckMTPStringHasUnicode(ptr)) {
+            // unicode isn't supported
+            name = is_dir ? "New Folder" : "New File";
+        } else {
+            name = ReadMTPString(ptr);
+        }
 
         // if the name is blank, generate random name
         if(*name == 0) {
@@ -184,7 +256,6 @@ void handle_mtp_data_complete(AppMTP* mtp) {
 
         FURI_LOG_I("MTP", "Format: %04x", info->format);
 
-        bool is_dir = info->format == MTP_FORMAT_ASSOCIATION;
         if(is_dir) {
             if(!storage_dir_exists(mtp->storage, full_path)) {
                 if(!storage_simply_mkdir(mtp->storage, full_path)) {
@@ -208,7 +279,10 @@ void handle_mtp_data_complete(AppMTP* mtp) {
                     break;
                 }
 
+                storage_file_close(file);
                 storage_file_free(file);
+
+                persistence.prev_handle = issue_object_handle(mtp, full_path);
             }
         }
 
@@ -329,6 +403,26 @@ void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
             mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
         break;
     }
+    case MTP_OP_GET_OBJECT_PROPS_SUPPORTED: {
+        FURI_LOG_I("MTP", "GetObjectPropsSupported operation");
+        uint8_t* buffer = malloc(sizeof(uint8_t) * 256);
+        uint32_t count = sizeof(supported_object_props) / sizeof(uint16_t);
+        memcpy(buffer, &count, sizeof(uint32_t));
+        memcpy(buffer + sizeof(uint32_t), supported_object_props, sizeof(uint16_t) * count);
+
+        int length = sizeof(uint32_t) + sizeof(uint16_t) * count;
+        send_mtp_response_buffer(
+            mtp,
+            MTP_TYPE_DATA,
+            MTP_OP_GET_OBJECT_PROPS_SUPPORTED,
+            container->header.transaction_id,
+            buffer,
+            length);
+        free(buffer);
+        send_mtp_response_buffer(
+            mtp, MTP_TYPE_RESPONSE, MTP_RESP_OK, container->header.transaction_id, NULL, 0);
+        break;
+    }
     case MTP_OP_DELETE_OBJECT:
         FURI_LOG_I("MTP", "DeleteObject operation");
         if(DeleteObject(mtp, container->params[0])) {
@@ -354,6 +448,10 @@ void handle_mtp_command(AppMTP* mtp, struct MTPContainer* container) {
         FURI_LOG_I("MTP", "SendObjectInfo operation");
         setup_persistence(container);
         break;
+    case MTP_OP_SEND_OBJECT:
+        FURI_LOG_I("MTP", "SendObject operation");
+        setup_persistence(container);
+        break;
     case MTP_OP_GET_OBJECT: {
         FURI_LOG_I("MTP", "GetObject operation");
         GetObject(mtp, container->header.transaction_id, container->params[0]);
@@ -664,6 +762,21 @@ int GetObjectInfo(AppMTP* mtp, uint32_t handle, uint8_t* buffer) {
     return ptr - buffer;
 }
 
+bool CheckMTPStringHasUnicode(uint8_t* buffer) {
+    uint8_t* base = buffer;
+    int len = *base;
+
+    uint16_t* ptr = (uint16_t*)(base + 1);
+
+    for(int i = 0; i < len; i++) {
+        if(ptr[i] > 0x7F) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 char* ReadMTPString(uint8_t* buffer) {
     int len16 = *(uint8_t*)buffer;
     if(len16 == 0) {
@@ -1116,7 +1229,7 @@ bool DeleteObject(AppMTP* mtp, uint32_t handle) {
     FileInfo fileinfo;
     FS_Error err = storage_common_stat(mtp->storage, path, &fileinfo);
 
-    if(err != FSE_OK) {
+    if(err == FSE_OK) {
         if(file_info_is_dir(&fileinfo)) {
             if(!storage_simply_remove_recursive(mtp->storage, path)) {
                 return false;
@@ -1126,7 +1239,9 @@ bool DeleteObject(AppMTP* mtp, uint32_t handle) {
                 return false;
             }
         }
+
+        return true;
     }
 
-    return true;
+    return false;
 }

+ 14 - 0
src/scenes/mtp/mtp.h

@@ -31,6 +31,16 @@
 #define MTP_OP_GET_DEVICE_PROP_DESC 0x1014
 #define MTP_OP_GET_DEVICE_PROP_VALUE 0x1015
 
+#define MTP_OP_GET_OBJECT_PROPS_SUPPORTED 0x9801
+#define MTP_OP_GET_OBJECT_PROP_DESC 0x9802
+#define MTP_OP_GET_OBJECT_PROP_VALUE 0x9803
+#define MTP_OP_SET_OBJECT_PROP_VALUE 0x9804
+
+// MTP Object Props
+#define MTP_PROP_STORAGE_ID 0xDC01
+#define MTP_PROP_OBJECT_FORMAT 0xDC02
+#define MTP_PROP_OBJECT_FILE_NAME 0xDC07
+
 // MTP Response Codes
 #define MTP_RESP_UNKNOWN 0x2000
 #define MTP_RESP_OK 0x2001
@@ -121,6 +131,9 @@ typedef struct MTPDataPersistence {
     uint16_t op;
     uint32_t transaction_id;
     uint32_t params[5];
+
+    uint32_t prev_handle;
+    File* current_file;
 } MTPDataPersistence;
 
 struct MTPContainer {
@@ -166,6 +179,7 @@ void send_mtp_response_stream(
     uint32_t length);
 int BuildDeviceInfo(uint8_t* buffer);
 
+bool CheckMTPStringHasUnicode(uint8_t* buffer);
 char* ReadMTPString(uint8_t* buffer);
 void WriteMTPString(uint8_t* buffer, const char* str, uint16_t* length);
 void send_device_info(AppMTP* mtp, uint32_t transaction_id);

+ 23 - 0
src/scenes/mtp/utils.c

@@ -0,0 +1,23 @@
+#include "utils.h"
+
+const char hex[] = "0123456789ABCDEF";
+
+void print_bytes(char* tag, uint8_t* bytes, size_t len) {
+    FURI_LOG_I("MTP", "Dumping bytes - TAG: %s", tag);
+    int lines = len / 16;
+    size_t last_line_len = len % 16;
+    char* line = (char*)malloc(16 * 3 + 1);
+    for(int i = 0; i < lines; i++) {
+        int line_len = i == lines - 1 ? last_line_len : 16;
+        for(int j = 0; j < line_len; j++) {
+            // write hex without sprintf
+            line[j * 3] = hex[bytes[i * 16 + j] >> 4];
+            line[j * 3 + 1] = hex[bytes[i * 16 + j] & 0x0F];
+            line[j * 3 + 2] = ' ';
+        }
+        line[16 * 3] = '\0';
+        FURI_LOG_I("MTP", line);
+    }
+    free(line);
+    FURI_LOG_I("MTP", "End of dump - TAG: %s", tag);
+}

+ 3 - 0
src/scenes/mtp/utils.h

@@ -0,0 +1,3 @@
+#pragma once
+#include <furi.h>
+void print_bytes(char* tag, uint8_t* bytes, size_t len);