Esteban Fuentealba 2 лет назад
Родитель
Сommit
ebfa448099

+ 3 - 0
gb_cartridge_app.c

@@ -68,6 +68,9 @@ GBCartridge* gb_cartridge_app_app_alloc() {
     view_dispatcher_add_view(app->view_dispatcher, GBCartridgeViewIdScene3, gb_cartridge_scene_3_get_view(app->gb_cartridge_scene_3));
     view_dispatcher_add_view(app->view_dispatcher, GBCartridgeViewIdScene3, gb_cartridge_scene_3_get_view(app->gb_cartridge_scene_3));
     app->gb_cartridge_scene_4 = gb_cartridge_scene_4_alloc();
     app->gb_cartridge_scene_4 = gb_cartridge_scene_4_alloc();
     view_dispatcher_add_view(app->view_dispatcher, GBCartridgeViewIdScene4, gb_cartridge_scene_4_get_view(app->gb_cartridge_scene_4));
     view_dispatcher_add_view(app->view_dispatcher, GBCartridgeViewIdScene4, gb_cartridge_scene_4_get_view(app->gb_cartridge_scene_4));
+    app->gb_cartridge_scene_5 = gb_cartridge_scene_5_alloc();
+    view_dispatcher_add_view(app->view_dispatcher, GBCartridgeViewIdScene5, gb_cartridge_scene_5_get_view(app->gb_cartridge_scene_5));
+    
     // app->button_menu = button_menu_alloc();
     // app->button_menu = button_menu_alloc();
     // view_dispatcher_add_view(app->view_dispatcher, GBCartridgeViewIdScene3, button_menu_get_view(app->button_menu));
     // view_dispatcher_add_view(app->view_dispatcher, GBCartridgeViewIdScene3, button_menu_get_view(app->button_menu));
     
     

+ 9 - 0
gb_cartridge_app.h

@@ -19,6 +19,7 @@
 #include "views/gb_cartridge_scene_2.h"
 #include "views/gb_cartridge_scene_2.h"
 #include "views/gb_cartridge_scene_3.h"
 #include "views/gb_cartridge_scene_3.h"
 #include "views/gb_cartridge_scene_4.h"
 #include "views/gb_cartridge_scene_4.h"
+#include "views/gb_cartridge_scene_5.h"
 #include "helpers/gb_cartridge_storage.h"
 #include "helpers/gb_cartridge_storage.h"
 #include "uart.h"
 #include "uart.h"
 #include "helpers/cJSON.h"
 #include "helpers/cJSON.h"
@@ -40,6 +41,8 @@
 #define UI_PROGRESS_COLS 8
 #define UI_PROGRESS_COLS 8
 #define UI_PROGRESS_ROWS 4
 #define UI_PROGRESS_ROWS 4
 
 
+#define BUFFER_SIZE 64
+
 typedef struct {
 typedef struct {
     Gui* gui;
     Gui* gui;
     Storage* storage;
     Storage* storage;
@@ -54,6 +57,8 @@ typedef struct {
     GBCartridgeScene2* gb_cartridge_scene_2;
     GBCartridgeScene2* gb_cartridge_scene_2;
     GBCartridgeScene3* gb_cartridge_scene_3;
     GBCartridgeScene3* gb_cartridge_scene_3;
     GBCartridgeScene4* gb_cartridge_scene_4;
     GBCartridgeScene4* gb_cartridge_scene_4;
+    GBCartridgeScene5* gb_cartridge_scene_5;
+    
     DialogsApp* dialogs; // File Browser
     DialogsApp* dialogs; // File Browser
     FuriString* file_path; // File Browser
     FuriString* file_path; // File Browser
     uint32_t haptic; 
     uint32_t haptic; 
@@ -77,6 +82,10 @@ typedef struct {
 
 
     uint32_t gameboy_rom_option_selected_index;
     uint32_t gameboy_rom_option_selected_index;
     char* gameboy_rom_option_selected_text;
     char* gameboy_rom_option_selected_text;
+
+    FuriThread* thread;
+    bool is_writing_rom;
+    bool is_writing_ram;
 } GBCartridge;
 } GBCartridge;
 
 
 
 

+ 7 - 0
helpers/gb_cartridge_custom_event.h

@@ -34,6 +34,13 @@ typedef enum {
     GBCartridgeCustomEventScene4Right,
     GBCartridgeCustomEventScene4Right,
     GBCartridgeCustomEventScene4Ok,
     GBCartridgeCustomEventScene4Ok,
     GBCartridgeCustomEventScene4Back,
     GBCartridgeCustomEventScene4Back,
+
+    GBCartridgeCustomEventScene5Up,
+    GBCartridgeCustomEventScene5Down,
+    GBCartridgeCustomEventScene5Left,
+    GBCartridgeCustomEventScene5Right,
+    GBCartridgeCustomEventScene5Ok,
+    GBCartridgeCustomEventScene5Back,
 } GBCartridgeCustomEvent;
 } GBCartridgeCustomEvent;
 
 
 enum GBCartridgeCustomEventType {
 enum GBCartridgeCustomEventType {

+ 1 - 0
scenes/gb_cartridge_scene_config.h

@@ -4,4 +4,5 @@ ADD_SCENE(gb_cartridge, scene_1, Scene_1)
 ADD_SCENE(gb_cartridge, scene_2, Scene_2)
 ADD_SCENE(gb_cartridge, scene_2, Scene_2)
 ADD_SCENE(gb_cartridge, scene_3, Scene_3)
 ADD_SCENE(gb_cartridge, scene_3, Scene_3)
 ADD_SCENE(gb_cartridge, scene_4, Scene_4)
 ADD_SCENE(gb_cartridge, scene_4, Scene_4)
+ADD_SCENE(gb_cartridge, scene_5, Scene_5)
 ADD_SCENE(gb_cartridge, settings, Settings)
 ADD_SCENE(gb_cartridge, settings, Settings)

+ 5 - 0
scenes/gb_cartridge_scene_menu.c

@@ -73,6 +73,7 @@ void gb_cartridge_scene_menu_on_enter(void* context) {
     
     
 
 
     variable_item_list_add(app->submenu, "Write RAM", 1, NULL, NULL);
     variable_item_list_add(app->submenu, "Write RAM", 1, NULL, NULL);
+    variable_item_list_add(app->submenu, "Settings", 1, NULL, NULL);
 
 
     variable_item_list_set_enter_callback(app->submenu, gb_cartridge_scene_menu_submenu_callback, app);
     variable_item_list_set_enter_callback(app->submenu, gb_cartridge_scene_menu_submenu_callback, app);
 
 
@@ -106,6 +107,10 @@ bool gb_cartridge_scene_menu_on_event(void* context, SceneManagerEvent event) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
                 app->scene_manager, GBCartridgeSceneMenu, SubmenuIndexScene4);
                 app->scene_manager, GBCartridgeSceneMenu, SubmenuIndexScene4);
             scene_manager_next_scene(app->scene_manager, GBCartridgeSceneScene_4);
             scene_manager_next_scene(app->scene_manager, GBCartridgeSceneScene_4);
+        } else if (event.event == SubmenuIndexScene5) {
+            scene_manager_set_scene_state(
+                app->scene_manager, GBCartridgeSceneMenu, SubmenuIndexScene5);
+            scene_manager_next_scene(app->scene_manager, GBCartridgeSceneScene_5);
         } else if (event.event == SubmenuIndexSettings) {
         } else if (event.event == SubmenuIndexSettings) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
                 app->scene_manager, GBCartridgeSceneMenu, SubmenuIndexSettings);
                 app->scene_manager, GBCartridgeSceneMenu, SubmenuIndexSettings);

+ 51 - 0
scenes/gb_cartridge_scene_scene_5.c

@@ -0,0 +1,51 @@
+#include "../gb_cartridge_app.h"
+#include "../helpers/gb_cartridge_custom_event.h"
+#include "../views/gb_cartridge_scene_5.h"
+
+void gb_cartridge_scene_5_callback(GBCartridgeCustomEvent event, void* context) {
+    furi_assert(context);
+    GBCartridge* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void gb_cartridge_scene_scene_5_on_enter(void* context) {
+    furi_assert(context);
+    GBCartridge* app = context;
+    gb_cartridge_scene_5_set_callback(app->gb_cartridge_scene_5, gb_cartridge_scene_5_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, GBCartridgeViewIdScene5);
+}
+
+bool gb_cartridge_scene_scene_5_on_event(void* context, SceneManagerEvent event) {
+    GBCartridge* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+            case GBCartridgeCustomEventScene5Left:
+            case GBCartridgeCustomEventScene5Right:
+                break;
+            case GBCartridgeCustomEventScene5Up:
+            case GBCartridgeCustomEventScene5Down:
+                break;
+            case GBCartridgeCustomEventScene5Back:
+                notification_message(app->notification, &sequence_reset_red);
+                notification_message(app->notification, &sequence_reset_green);
+                notification_message(app->notification, &sequence_reset_blue);
+                if(!scene_manager_search_and_switch_to_previous_scene(
+                    app->scene_manager, GBCartridgeSceneMenu)) {
+                        scene_manager_stop(app->scene_manager);
+                        view_dispatcher_stop(app->view_dispatcher);
+                    }
+                consumed = true;
+                break;
+        }
+    }
+
+    return consumed;
+}
+
+void gb_cartridge_scene_scene_5_on_exit(void* context) {
+    GBCartridge* app = context;
+    UNUSED(app);
+}
+

+ 20 - 5
views/gb_cartridge_scene_2.c

@@ -37,7 +37,9 @@ void dump_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
     UNUSED(len);
     UNUSED(len);
     UNUSED(buf);
     UNUSED(buf);
     GBCartridge* instance = context;
     GBCartridge* instance = context;
-    storage_file_write(instance->cart_rom, buf, len);
+    if(instance->is_writing_rom) {
+        storage_file_write(instance->cart_rom, buf, len);
+    }
     with_view_model(
     with_view_model(
         instance->gb_cartridge_scene_2->view,
         instance->gb_cartridge_scene_2->view,
         GameBoyCartridgeROMBackupModel * model,
         GameBoyCartridgeROMBackupModel * model,
@@ -213,9 +215,10 @@ bool gb_cartridge_scene_2_input(InputEvent* event, void* context) {
                     {
                     {
                         UNUSED(model);
                         UNUSED(model);
                         GBCartridge* app = (GBCartridge*)instance->context;
                         GBCartridge* app = (GBCartridge*)instance->context;
-                        // Unregister rx callback
-                        uart_set_handle_rx_data_cb(app->uart, NULL);
-                        uart_set_handle_rx_data_cb(app->lp_uart, NULL);
+                        UNUSED(app);
+                        // // Unregister rx callback
+                        // uart_set_handle_rx_data_cb(app->uart, NULL);
+                        // uart_set_handle_rx_data_cb(app->lp_uart, NULL);
                         instance->callback(GBCartridgeCustomEventScene2Back, instance->context);
                         instance->callback(GBCartridgeCustomEventScene2Back, instance->context);
                     },
                     },
                     true);
                     true);
@@ -257,6 +260,18 @@ bool gb_cartridge_scene_2_input(InputEvent* event, void* context) {
 void gb_cartridge_scene_2_exit(void* context) {
 void gb_cartridge_scene_2_exit(void* context) {
     furi_assert(context);
     furi_assert(context);
     GBCartridge* app = context;
     GBCartridge* app = context;
+
+    // Automatically stop the scan when exiting view
+    // uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n"));
+    // furi_delay_ms(50);
+
+    uart_set_handle_rx_data_cb(app->uart, NULL);
+    uart_set_handle_rx_data_cb(app->lp_uart, NULL);
+
+    app->is_writing_rom = false;
+    if(app->cart_rom && storage_file_is_open(app->cart_rom)) {
+        storage_file_close(app->cart_rom);
+    }
     gb_cartridge_stop_all_sound(app);
     gb_cartridge_stop_all_sound(app);
 }
 }
 
 
@@ -276,7 +291,7 @@ void gb_cartridge_scene_2_enter(void* context) {
             // filename++;
             // filename++;
             char *filename = sequential_file_resolve_path(app->storage, MALVEKE_APP_FOLDER, app->cart_dump_rom_filename, model->cart_dump_rom_extension);
             char *filename = sequential_file_resolve_path(app->storage, MALVEKE_APP_FOLDER, app->cart_dump_rom_filename, model->cart_dump_rom_extension);
             model->cart_dump_rom_filename_sequential =  filename;
             model->cart_dump_rom_filename_sequential =  filename;
-
+            app->is_writing_rom = true;
              // Register callbacks to receive data
              // Register callbacks to receive data
             uart_set_handle_rx_data_cb(app->uart, gameboy_rom_backup_handle_rx_data_cb); // setup callback for general log rx thread
             uart_set_handle_rx_data_cb(app->uart, gameboy_rom_backup_handle_rx_data_cb); // setup callback for general log rx thread
             uart_set_handle_rx_data_cb(app->lp_uart, dump_handle_rx_data_cb); // setup callback for general log rx thread
             uart_set_handle_rx_data_cb(app->lp_uart, dump_handle_rx_data_cb); // setup callback for general log rx thread

+ 18 - 5
views/gb_cartridge_scene_3.c

@@ -53,7 +53,9 @@ void dump_ram_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
     UNUSED(len);
     UNUSED(len);
     UNUSED(buf);
     UNUSED(buf);
     GBCartridge* instance = context;
     GBCartridge* instance = context;
-    storage_file_write(instance->cart_ram, buf, len);
+    if(instance->is_writing_ram) {
+        storage_file_write(instance->cart_ram, buf, len);
+    }
     with_view_model(
     with_view_model(
         instance->gb_cartridge_scene_3->view,
         instance->gb_cartridge_scene_3->view,
         GameBoyCartridgeRAMBackupModel * model,
         GameBoyCartridgeRAMBackupModel * model,
@@ -121,9 +123,9 @@ void gameboy_ram_backup_handle_rx_data_cb(uint8_t* buf, size_t len, void* contex
             }
             }
             if (strcmp(model->event_type, "success") == 0) {
             if (strcmp(model->event_type, "success") == 0) {
                 model->progress = 100;
                 model->progress = 100;
-                if(instance->cart_ram && storage_file_is_open(instance->cart_ram)) {
-                    storage_file_close(instance->cart_ram);
-                }
+                // if(instance->cart_ram && storage_file_is_open(instance->cart_ram)) {
+                //     storage_file_close(instance->cart_ram);
+                // }
                 notification_success(instance->notification);
                 notification_success(instance->notification);
             }
             }
         },
         },
@@ -226,9 +228,15 @@ bool gb_cartridge_scene_3_input(InputEvent* event, void* context) {
                     {
                     {
                         UNUSED(model);
                         UNUSED(model);
                         GBCartridge* app = (GBCartridge*)instance->context;
                         GBCartridge* app = (GBCartridge*)instance->context;
+                        UNUSED(app);
                         // Unregister rx callback
                         // Unregister rx callback
                         uart_set_handle_rx_data_cb(app->uart, NULL);
                         uart_set_handle_rx_data_cb(app->uart, NULL);
                         uart_set_handle_rx_data_cb(app->lp_uart, NULL);
                         uart_set_handle_rx_data_cb(app->lp_uart, NULL);
+
+                        app->is_writing_ram = false;
+                        if(app->cart_ram && storage_file_is_open(app->cart_ram)) {
+                            storage_file_close(app->cart_ram);
+                        }
                         instance->callback(GBCartridgeCustomEventScene3Back, instance->context);
                         instance->callback(GBCartridgeCustomEventScene3Back, instance->context);
                     },
                     },
                     true);
                     true);
@@ -269,6 +277,11 @@ bool gb_cartridge_scene_3_input(InputEvent* event, void* context) {
 void gb_cartridge_scene_3_exit(void* context) {
 void gb_cartridge_scene_3_exit(void* context) {
     furi_assert(context);
     furi_assert(context);
     GBCartridge* app = context;
     GBCartridge* app = context;
+    // Automatically stop the scan when exiting view
+    // uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n"));
+    // furi_delay_ms(50);
+
+    
     gb_cartridge_stop_all_sound(app);
     gb_cartridge_stop_all_sound(app);
 }
 }
 
 
@@ -291,7 +304,7 @@ void gb_cartridge_scene_3_enter(void* context) {
             // filename++;
             // filename++;
             char *filename = sequential_file_resolve_path(app->storage, MALVEKE_APP_FOLDER, app->cart_dump_ram_filename, app->cart_dump_ram_extension);
             char *filename = sequential_file_resolve_path(app->storage, MALVEKE_APP_FOLDER, app->cart_dump_ram_filename, app->cart_dump_ram_extension);
             model->cart_dump_ram_filename_sequential =  filename;
             model->cart_dump_ram_filename_sequential =  filename;
-
+            app->is_writing_ram = true;
              // Register callbacks to receive data
              // Register callbacks to receive data
             uart_set_handle_rx_data_cb(app->uart, gameboy_ram_backup_handle_rx_data_cb); // setup callback for general log rx thread
             uart_set_handle_rx_data_cb(app->uart, gameboy_ram_backup_handle_rx_data_cb); // setup callback for general log rx thread
             uart_set_handle_rx_data_cb(app->lp_uart, dump_ram_handle_rx_data_cb); // setup callback for general log rx thread
             uart_set_handle_rx_data_cb(app->lp_uart, dump_ram_handle_rx_data_cb); // setup callback for general log rx thread

+ 48 - 125
views/gb_cartridge_scene_4.c

@@ -13,6 +13,7 @@
 #include "../helpers/sequential_file.h"
 #include "../helpers/sequential_file.h"
 #include <stdio.h>   // Para sprintf
 #include <stdio.h>   // Para sprintf
 #include <string.h>  // Para strlen
 #include <string.h>  // Para strlen
+#include <lib/toolbox/stream/file_stream.h>
 
 
 struct GBCartridgeScene4 {
 struct GBCartridgeScene4 {
     View* view;
     View* view;
@@ -20,7 +21,6 @@ struct GBCartridgeScene4 {
     void* context;
     void* context;
     GBCartridge* app;
     GBCartridge* app;
 };
 };
-// static uint64_t last_toggle_time = 0;
 
 
 typedef struct {
 typedef struct {
     char* event_type;
     char* event_type;
@@ -37,26 +37,6 @@ typedef struct {
     char* gameboy_rom_option_selected_text;
     char* gameboy_rom_option_selected_text;
 } GameBoyCartridgeROMWriteModel;
 } GameBoyCartridgeROMWriteModel;
 
 
-// void dump_rom_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
-//     UNUSED(len);
-//     UNUSED(buf);
-//     GBCartridge* instance = context;
-//     storage_file_write(instance->cart_rom, buf, len);
-//     with_view_model(
-//         instance->gb_cartridge_scene_4->view,
-//         GameBoyCartridgeROMWriteModel * model,
-//         { 
-//             model->transfered += len;
-//             uint64_t current_time = furi_hal_rtc_get_timestamp();
-//             model->elapsed_time = current_time - model->start_time;
-//             if (current_time - last_toggle_time >= 0.2) {
-//                 model->rx_active = !model->rx_active;
-//                 last_toggle_time = current_time;
-//             }
-//         },
-//         true);
-// }
-
 void gb_cartridge_scene_4_set_callback(
 void gb_cartridge_scene_4_set_callback(
     GBCartridgeScene4* instance,
     GBCartridgeScene4* instance,
     GBCartridgeScene4Callback callback,
     GBCartridgeScene4Callback callback,
@@ -66,57 +46,7 @@ void gb_cartridge_scene_4_set_callback(
     instance->callback = callback;
     instance->callback = callback;
     instance->context = context;
     instance->context = context;
 }
 }
-// void gameboy_rom_write_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) {
-//     furi_assert(context);
-//     UNUSED(len);
-//     UNUSED(buf);
-//     GBCartridge* instance = context;
-//     with_view_model(
-//         instance->gb_cartridge_scene_4->view,
-//         GameBoyCartridgeROMWriteModel * model,
-//         {
-//             UNUSED(model);
-//             cJSON* json = cJSON_Parse((char*)buf);
-//             if(json == NULL) {
-//             } else {
-//                 cJSON* type = cJSON_GetObjectItemCaseSensitive(json, "type");
-//                 if(cJSON_IsString(type) && (type->valuestring != NULL)) {
-//                     model->event_type = strdup(type->valuestring);
-//                 } else {
-//                     model->event_type = "None";
-//                 }
-//                 //  Total
-//                 cJSON* total = cJSON_GetObjectItemCaseSensitive(json, "total");
-//                 if(cJSON_IsNumber(total)) {
-//                     model->total_rom = total->valueint;
-//                 } else {
-//                     model->total_rom = 0;
-//                 }
-//                 //  Progress
-//                 cJSON* progress = cJSON_GetObjectItemCaseSensitive(json, "progress");
-//                 if(cJSON_IsNumber(progress)) {
-//                     model->progress = progress->valueint;
-//                 } else {
-//                     model->progress = 0;
-//                 }
-//                 //  RomBanks
-//                 cJSON* romBanks = cJSON_GetObjectItemCaseSensitive(json, "romBanks");
-//                 if(cJSON_IsNumber(romBanks)) {
-//                     model->romBanks = romBanks->valueint;
-//                 } else {
-//                     model->romBanks = 0;
-//                 }
-//             }
-//             if (strcmp(model->event_type, "success") == 0) {
-//                 model->progress = 100;
-//                 if(instance->cart_rom && storage_file_is_open(instance->cart_rom)) {
-//                     storage_file_close(instance->cart_rom);
-//                 }
-//                 notification_success(instance->notification);
-//             }
-//         },
-//         true);
-// }
+
 static void drawProgressBar(Canvas* canvas, int progress) {
 static void drawProgressBar(Canvas* canvas, int progress) {
     for(int x = 0; x < 64 - 14 - UI_PADDING - UI_PADDING -UI_PADDING - UI_PADDING; x += 5) {
     for(int x = 0; x < 64 - 14 - UI_PADDING - UI_PADDING -UI_PADDING - UI_PADDING; x += 5) {
         for(int row = 0; row < 20; row += 5) {
         for(int row = 0; row < 20; row += 5) {
@@ -199,7 +129,32 @@ static void gb_cartridge_scene_4_model_init(GameBoyCartridgeROMWriteModel* const
     model->elapsed_time = 0;
     model->elapsed_time = 0;
     model->start_time = 0;
     model->start_time = 0;
 }
 }
-
+// static bool select_rom_file(GBCartridge *app, Stream* stream) {
+//     bool result = false;
+//     FuriString* file_path = furi_string_alloc();
+//     furi_string_set(file_path, MALVEKE_APP_FOLDER);
+//     DialogsFileBrowserOptions browser_options;
+//     dialog_file_browser_set_basic_options(
+//         &browser_options, app->gameboy_rom_option_selected_text, NULL);
+//     browser_options.base_path = MALVEKE_APP_FOLDER;
+//     browser_options.skip_assets = true;
+
+//     // Input events and views are managed by file_browser
+//     bool res = dialog_file_browser_show( app->dialogs, file_path, file_path, &browser_options);
+//     UNUSED(res);
+//     FURI_LOG_I(TAG, "File selected: %s", furi_string_get_cstr(file_path));
+//     if(res) {
+//         if(!file_stream_open(stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+//             FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(file_path));
+//             file_stream_close(stream);
+//         } else {
+//             FURI_LOG_D(TAG, "Open file \"%s\"", furi_string_get_cstr(file_path));
+//             result = true;
+//         }
+//     }
+//     furi_string_free(file_path);
+//     return result;
+// }
 bool gb_cartridge_scene_4_input(InputEvent* event, void* context) {
 bool gb_cartridge_scene_4_input(InputEvent* event, void* context) {
     furi_assert(context);
     furi_assert(context);
     GBCartridgeScene4* instance = context;
     GBCartridgeScene4* instance = context;
@@ -234,32 +189,27 @@ bool gb_cartridge_scene_4_input(InputEvent* event, void* context) {
                         GBCartridge* app = ((GBCartridge*)instance->context);
                         GBCartridge* app = ((GBCartridge*)instance->context);
                         UNUSED(model);
                         UNUSED(model);
                         UNUSED(app);
                         UNUSED(app);
-
-
-                        FuriString* path = furi_string_alloc();
-                        furi_string_set(path, MALVEKE_APP_FOLDER);
-                        DialogsFileBrowserOptions browser_options;
-                        dialog_file_browser_set_basic_options(
-                            &browser_options, app->gameboy_rom_option_selected_text, NULL);
-                        browser_options.base_path = MALVEKE_APP_FOLDER;
-                        browser_options.skip_assets = true;
-
-                        // Input events and views are managed by file_browser
-                        bool res = dialog_file_browser_show( app->dialogs, path, path, &browser_options);
-
-                        UNUSED(res);
-
-                        
-                        // model->start_time = furi_hal_rtc_get_timestamp(); // Registra el tiempo de inicio
-                        // app->cart_rom = storage_file_alloc(app->storage);
-
-                        
-                        // if(storage_file_open(app->cart_rom, model->cart_dump_rom_filename_sequential, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
-                        //     const char gbcartridge_command[] = "gbcartridge -w -o\n";
-                        //     uart_tx((uint8_t*)gbcartridge_command, strlen(gbcartridge_command));
-                        // } else {
-                        //     dialog_message_show_storage_error(app->dialogs, "Cannot open dump file");
+                        // uint8_t buffer[BUFFER_SIZE];
+                        // size_t bytesRead;
+                        // Stream* file_stream = file_stream_alloc(app->storage);
+                       
+                        // if(select_rom_file(app, file_stream)) {
+                        //     const char gbcartridge_start_command[] = "gbcartridge -w -o -s\n";
+                        //     uart_tx((uint8_t*)gbcartridge_start_command, strlen(gbcartridge_start_command));
+                        //     furi_delay_ms(500);
+                            
+                        //     while (file_stream_read(file_stream, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
+                        //         // Send 64 bytes at a time
+                        //         lp_uart_tx((uint8_t*)buffer, bytesRead);
+                        //     }
+
+                        //     const char gbcartridge_end_command[] = "gbcartridge -w -o -e\n";
+                        //     uart_tx((uint8_t*)gbcartridge_end_command, strlen(gbcartridge_end_command));
+
+                            
+                        //     file_stream_close(file_stream);
                         // }
                         // }
+                        // stream_free(file_stream);
                     },
                     },
                     true);
                     true);
             break;
             break;
@@ -292,33 +242,6 @@ void gb_cartridge_scene_4_enter(void* context) {
             gb_cartridge_scene_4_model_init(model);
             gb_cartridge_scene_4_model_init(model);
             model->gameboy_rom_option_selected_text = app->gameboy_rom_option_selected_text;
             model->gameboy_rom_option_selected_text = app->gameboy_rom_option_selected_text;
 
 
-            
-            
-            // furi_record_close("dialogs");
-
-
-            // Stream* file_stream = file_stream_alloc(app->storage);
-            // FuriString* path = furi_string_alloc();
-            // furi_string_set(path, MALVEKE_APP_FOLDER);
-            // if(file_stream_open(file_stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
-
-            // }
-
-
-
-            // gb_cartridge_scene_4_model_init(model);
-            // model->cart_dump_rom_filename  = app->cart_dump_rom_filename;
-            // model->cart_dump_rom_extension = app->cart_dump_rom_extension;
-            // char *filename = strrchr(sequential_file_resolve_path(app->storage, MALVEKE_APP_FOLDER, app->cart_dump_rom_filename, app->cart_dump_rom_extension), '/');
-            // filename++;
-            // char *filename = sequential_file_resolve_path(app->storage, MALVEKE_APP_FOLDER, app->cart_dump_rom_filename, app->cart_dump_rom_extension);
-            // model->cart_dump_rom_filename_sequential =  filename;
-
-             // Register callbacks to receive data
-            // uart_set_handle_rx_data_cb(app->uart, gameboy_rom_write_handle_rx_data_cb); // setup callback for general log rx thread
-            // uart_set_handle_rx_data_cb(app->lp_uart, dump_rom_handle_rx_data_cb); // setup callback for general log rx thread
-
-
         },
         },
         false);
         false);
 }
 }

+ 345 - 0
views/gb_cartridge_scene_5.c

@@ -0,0 +1,345 @@
+#include "../gb_cartridge_app.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include <input/input.h>
+#include <gui/elements.h>
+#include <dolphin/dolphin.h>
+#include <gui/elements.h>
+#include <notification/notification_messages.h>
+#include <dialogs/dialogs.h>
+#include <gui/modules/dialog_ex.h>
+#include <toolbox/stream/file_stream.h>
+#include "../helpers/gb_cartridge_speaker.h"
+#include "../helpers/sequential_file.h"
+#include <stdio.h>  // Para sprintf
+#include <string.h> // Para strlen
+
+struct GBCartridgeScene5
+{
+    View *view;
+    GBCartridgeScene5Callback callback;
+    void *context;
+    GBCartridge *app;
+};
+
+typedef struct
+{
+    char *event_type;
+    int progress;
+    int total_ram;
+    int transfered;
+    int ramBanks;
+    int elapsed_time;
+    int start_time;
+
+    char *cart_dump_ram_filename_sequential;
+    bool rx_active;
+
+} GameBoyCartridgeRAMWriteModel;
+
+
+static bool select_ram_file(GBCartridge *app, File *file)
+{
+    bool result = false;
+    FuriString *file_path = furi_string_alloc();
+    furi_string_set(file_path, MALVEKE_APP_FOLDER);
+    DialogsFileBrowserOptions browser_options;
+    dialog_file_browser_set_basic_options(&browser_options, "sav", NULL);
+    browser_options.base_path = MALVEKE_APP_FOLDER;
+    browser_options.skip_assets = true;
+
+    // Input events and views are managed by file_browser
+    bool res = dialog_file_browser_show(app->dialogs, file_path, file_path, &browser_options);
+    // UNUSED(res);
+    // FURI_LOG_I(TAG, "File selected: %s", furi_string_get_cstr(file_path));
+    if (res)
+    {   
+        if(!storage_file_open(file, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING))
+        // if (!file_stream_open(stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING))
+        {
+            // FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(file_path));
+            // file_stream_close(stream);
+        }
+        else
+        {
+            // FURI_LOG_D(TAG, "Open file \"%s\"", furi_string_get_cstr(file_path));
+            result = true;
+        }
+    }
+    furi_string_free(file_path);
+    return result;
+}
+
+static int32_t cartridge_writting_worker_thread(void* thread_context) {
+    GBCartridge* app = thread_context;
+    UNUSED(app);
+    uint8_t buffer[BUFFER_SIZE];
+    File* file = storage_file_alloc(app->storage);
+
+    if (select_ram_file(app, file))
+    {
+        const char gbcartridge_start_command[] = "gbcartridge -w -o -s\n";
+        uart_tx((uint8_t *)gbcartridge_start_command, strlen(gbcartridge_start_command));
+        furi_delay_ms(100);
+        
+        uint16_t ret = 0;
+        do {
+            ret = storage_file_read(file, buffer, sizeof(buffer) - 1);
+            lp_uart_tx((uint8_t *)buffer, sizeof(buffer));
+            // uart_tx((uint8_t *)buffer, sizeof(buffer))
+
+            with_view_model(
+                app->gb_cartridge_scene_5->view,
+                GameBoyCartridgeRAMWriteModel * model,
+                {
+                    model->total_ram = storage_file_size(file);
+                    model->transfered += ret;
+                },
+                true);
+            
+        } while(ret > 0);
+
+        const char gbcartridge_end_command[] = "gbcartridge -w -o -e\n";
+        uart_tx((uint8_t *)gbcartridge_end_command, strlen(gbcartridge_end_command));
+        storage_file_free(file);
+    }
+    return 0;
+}
+void gb_cartridge_scene_5_set_callback(
+    GBCartridgeScene5 *instance,
+    GBCartridgeScene5Callback callback,
+    void *context)
+{
+    furi_assert(instance);
+    furi_assert(callback);
+    instance->callback = callback;
+    instance->context = context;
+}
+static void drawProgressBar(Canvas *canvas, int progress)
+{
+    for (int x = 0; x < 64 - 14 - UI_PADDING - UI_PADDING - UI_PADDING - UI_PADDING; x += 5)
+    {
+        for (int row = 0; row < 20; row += 5)
+        {
+            if (progress > 0)
+            {
+                canvas_draw_box(canvas, 14 /*ARROW*/ + UI_PADDING + 2 + x + 4, /*45*/ 26 + row, 4, 4);
+                progress--;
+            }
+            else
+            {
+                canvas_draw_frame(canvas, 14 /*ARROW*/ + UI_PADDING + 2 + x + 4, /*45*/ 26 + row, 4, 4);
+            }
+        }
+    }
+}
+
+void gb_cartridge_scene_5_draw(Canvas *canvas, GameBoyCartridgeRAMWriteModel *model)
+{
+    // Clear the screen.
+    canvas_set_color(canvas, ColorBlack);
+
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontKeyboard);
+    canvas_draw_frame(canvas, 0, 24, (128 / 2), 25);
+
+    canvas_set_bitmap_mode(canvas, 1);
+    canvas_set_font(canvas, FontPrimary);
+    char progressText[42];
+    int progress = 0;
+    if (model->total_ram > 0 && model->transfered > 0)
+    {
+        progress = model->transfered * 100 / model->total_ram;
+    }
+    snprintf(progressText, sizeof(progressText), "%d%% Write RAM...", progress);
+    canvas_draw_str_aligned(canvas, 128 / 2, 0, AlignCenter, AlignTop, progressText);
+    canvas_set_font(canvas, FontSecondary);
+
+    // char *filename = strrchr(model->cart_dump_ram_filename_sequential, '/');
+    // filename++;
+    // canvas_draw_str_aligned(canvas, 128/2, 12, AlignCenter, AlignTop, filename);
+
+    char total_ram_str[20];
+    snprintf(total_ram_str, sizeof(total_ram_str), "of %.2lf MiB", (double)(model->total_ram / 1024.0 / 1024.0));
+
+    char transfered_ram_str[20];
+    snprintf(transfered_ram_str, sizeof(transfered_ram_str), "%.2lf MiB", (double)(model->transfered / 1024.0 / 1024.0));
+
+    // Calcula la Tasa de Transferencia en KiB/s
+    char transfer_rate_str[20];
+    if (model->transfered > 0 && model->elapsed_time > 0)
+    {
+        double transfer_rate_kibps = (double)model->transfered / ((double)model->elapsed_time) / (double)1024.0;
+        snprintf(transfer_rate_str, sizeof(transfer_rate_str), "%.2lf KiB/s", transfer_rate_kibps);
+    }
+    else
+    {
+        snprintf(transfer_rate_str, sizeof(transfer_rate_str), "0 KiB/s");
+    }
+
+    canvas_draw_str_aligned(canvas, (128 / 2) + UI_PADDING, 22 + 2, AlignLeft, AlignTop, transfered_ram_str);
+    canvas_draw_str_aligned(canvas, (128 / 2) + UI_PADDING, 40 + 2, AlignLeft, AlignTop, total_ram_str);
+    canvas_draw_str_aligned(canvas, (128 / 2) + UI_PADDING, 48 + 2, AlignLeft, AlignTop, transfer_rate_str);
+
+    if (model->rx_active)
+    {
+        canvas_draw_icon_ex(canvas, UI_PADDING, 28, &I_ArrowUpFilled_14x15, IconRotation180);
+    }
+    else
+    {
+        canvas_draw_icon_ex(canvas, UI_PADDING, 28, &I_ArrowUpEmpty_14x15, IconRotation180);
+    }
+
+    char totalText[42];
+    snprintf(totalText, sizeof(totalText), "%d", model->total_ram);
+    // canvas_draw_str(canvas, 69, 48, totalText);
+
+    drawProgressBar(canvas, (progress * UI_PROGRESS_ROWS * UI_PROGRESS_COLS) / 100); // Pinta las primeras 10 cajas de negro
+    // free(totalText);
+
+    elements_button_center(canvas, "Write");
+}
+
+static void gb_cartridge_scene_5_model_init(GameBoyCartridgeRAMWriteModel *const model)
+{
+    model->progress = 0;
+    model->total_ram = 0;
+    model->transfered = 0;
+    model->ramBanks = 0;
+    model->elapsed_time = 0;
+    model->start_time = 0;
+}
+bool gb_cartridge_scene_5_input(InputEvent *event, void *context)
+{
+    furi_assert(context);
+    GBCartridgeScene5 *instance = context;
+
+    if (event->type == InputTypeRelease)
+    {
+        switch (event->key)
+        {
+        case InputKeyBack:
+            with_view_model(
+                instance->view,
+                GameBoyCartridgeRAMWriteModel * model,
+                {
+                    UNUSED(model);
+                    GBCartridge *app = (GBCartridge *)instance->context;
+                    // Unregister rx callback
+                    uart_set_handle_rx_data_cb(app->uart, NULL);
+                    uart_set_handle_rx_data_cb(app->lp_uart, NULL);
+                    instance->callback(GBCartridgeCustomEventScene5Back, instance->context);
+                },
+                true);
+            break;
+        case InputKeyUp:
+        case InputKeyDown:
+        case InputKeyLeft:
+        case InputKeyRight:
+            break;
+        case InputKeyOk:
+            with_view_model(
+                instance->view,
+                GameBoyCartridgeRAMWriteModel * model,
+                {
+                    GBCartridge *app = ((GBCartridge *)instance->context);
+                    app->thread = furi_thread_alloc_ex("CartridgeWriterWorker", 2048, cartridge_writting_worker_thread, app);
+                    furi_thread_start(app->thread);
+
+                    UNUSED(model);
+                    
+                    // UNUSED(app);
+                    // uint8_t buffer[BUFFER_SIZE];
+                    // File* file = storage_file_alloc(app->storage);
+
+                    // if (select_ram_file(app, file))
+                    // {
+                    //     model->total_ram = storage_file_size(file);
+                    //     const char gbcartridge_start_command[] = "gbcartridge -w -o -s\n";
+                    //     uart_tx((uint8_t *)gbcartridge_start_command, strlen(gbcartridge_start_command));
+                    //     furi_delay_ms(500);
+                        
+                    //     uint16_t ret = 0;
+                    //     do {
+                    //         ret = storage_file_read(file, buffer, sizeof(buffer) - 1);
+                    //         lp_uart_tx((uint8_t *)buffer, sizeof(buffer));
+                    //         model->transfered += ret;
+                    //     } while(ret > 0);
+                    //     const char gbcartridge_end_command[] = "gbcartridge -w -o -e\n";
+                    //     uart_tx((uint8_t *)gbcartridge_end_command, strlen(gbcartridge_end_command));
+                    //     storage_file_free(file);
+                    // }
+                },
+                true);
+            break;
+        case InputKeyMAX:
+            break;
+        }
+    }
+
+    return true;
+}
+
+void gb_cartridge_scene_5_exit(void *context)
+{
+    furi_assert(context);
+    GBCartridge *app = context;
+    gb_cartridge_stop_all_sound(app);
+}
+
+void gb_cartridge_scene_5_enter(void *context)
+{
+    furi_assert(context);
+    GBCartridgeScene5 *instance = context;
+    GBCartridge *app = (GBCartridge *)instance->context;
+
+    UNUSED(app);
+    with_view_model(
+        app->gb_cartridge_scene_5->view,
+        GameBoyCartridgeRAMWriteModel * model,
+        {
+            UNUSED(model);
+            gb_cartridge_scene_5_model_init(model);
+        },
+        false);
+}
+
+GBCartridgeScene5 *gb_cartridge_scene_5_alloc()
+{
+    GBCartridgeScene5 *instance = malloc(sizeof(GBCartridgeScene5));
+    instance->view = view_alloc();
+    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(GameBoyCartridgeRAMWriteModel));
+
+    view_set_context(instance->view, instance);
+    view_set_draw_callback(instance->view, (ViewDrawCallback)gb_cartridge_scene_5_draw);
+    view_set_input_callback(instance->view, gb_cartridge_scene_5_input);
+    view_set_enter_callback(instance->view, gb_cartridge_scene_5_enter);
+    view_set_exit_callback(instance->view, gb_cartridge_scene_5_exit);
+
+    with_view_model(
+        instance->view,
+        GameBoyCartridgeRAMWriteModel * model,
+        {
+            gb_cartridge_scene_5_model_init(model);
+        },
+        true);
+
+    return instance;
+}
+
+void gb_cartridge_scene_5_free(GBCartridgeScene5 *instance)
+{
+    GBCartridge* app = instance->context;
+    furi_assert(instance);
+    furi_thread_free(app->thread);
+    view_free(instance->view);
+    free(instance);
+}
+
+View *gb_cartridge_scene_5_get_view(GBCartridgeScene5 *instance)
+{
+    furi_assert(instance);
+
+    return instance->view;
+}

+ 19 - 0
views/gb_cartridge_scene_5.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../helpers/gb_cartridge_custom_event.h"
+
+typedef struct GBCartridgeScene5 GBCartridgeScene5;
+
+typedef void (*GBCartridgeScene5Callback)(GBCartridgeCustomEvent event, void* context);
+
+void gb_cartridge_scene_5_set_callback(
+    GBCartridgeScene5* instance,
+    GBCartridgeScene5Callback callback,
+    void * context);
+
+GBCartridgeScene5* gb_cartridge_scene_5_alloc();
+
+void gb_cartridge_scene_5_free(GBCartridgeScene5* gb_cartridge_static);
+
+View* gb_cartridge_scene_5_get_view(GBCartridgeScene5* boilerpate_static);