@@ -12,6 +12,12 @@ static void protopirate_scene_receiver_info_widget_callback(
1212 InputType type ,
1313 void * context );
1414
15+ static void protopirate_scene_receiver_info_text_input_callback (void * context ) {
16+ ProtoPirateApp * app = context ;
17+ view_dispatcher_send_custom_event (
18+ app -> view_dispatcher , ProtoPirateCustomEventReceiverInfoSaveConfirm );
19+ }
20+
1521static void psa_bf_done_cb_receiver_info (void * context ) {
1622 ProtoPirateApp * app = context ;
1723 view_dispatcher_send_custom_event (
@@ -416,27 +422,98 @@ bool protopirate_scene_receiver_info_on_event(void* context, SceneManagerEvent e
416422 FlipperFormat * ff =
417423 protopirate_history_get_raw_data (app -> txrx -> history , app -> txrx -> idx_menu_chosen );
418424 if (ff ) {
425+ // Read protocol name for default filename
419426 FuriString * protocol = furi_string_alloc ();
420427 flipper_format_rewind (ff );
421428 if (!flipper_format_read_string (ff , "Protocol" , protocol )) {
422429 furi_string_set_str (protocol , "Unknown" );
423430 }
424- FuriString * saved_path = furi_string_alloc ();
425- if (protopirate_storage_save_capture (
426- ff , furi_string_get_cstr (protocol ), saved_path )) {
431+
432+ // Clean protocol name for filename
433+ furi_string_replace_all (protocol , "/" , "_" );
434+ furi_string_replace_all (protocol , " " , "_" );
435+
436+ // Get the next auto-generated filename (just the name part)
437+ FuriString * auto_path = furi_string_alloc ();
438+ if (protopirate_storage_get_next_filename (
439+ furi_string_get_cstr (protocol ), auto_path )) {
440+ // Extract just the filename without folder and extension
441+ const char * full = furi_string_get_cstr (auto_path );
442+ const char * slash = strrchr (full , '/' );
443+ const char * name_start = slash ? slash + 1 : full ;
444+
445+ // Copy without extension
446+ size_t name_len = strlen (name_start );
447+ const char * dot = strrchr (name_start , '.' );
448+ if (dot ) name_len = dot - name_start ;
449+ if (name_len >= sizeof (app -> save_filename ))
450+ name_len = sizeof (app -> save_filename ) - 1 ;
451+
452+ memcpy (app -> save_filename , name_start , name_len );
453+ app -> save_filename [name_len ] = '\0' ;
454+ } else {
455+ snprintf (app -> save_filename , sizeof (app -> save_filename ), "capture" );
456+ }
457+ furi_string_free (auto_path );
458+
459+ // Store context for when text input confirms
460+ if (app -> save_protocol ) furi_string_free (app -> save_protocol );
461+ app -> save_protocol = protocol ; // transfer ownership
462+ app -> save_history_idx = app -> txrx -> idx_menu_chosen ;
463+ app -> save_from_saved_info = false;
464+
465+ // Configure and show text input
466+ text_input_reset (app -> text_input );
467+ text_input_set_header_text (app -> text_input , "Save filename:" );
468+ text_input_set_result_callback (
469+ app -> text_input ,
470+ protopirate_scene_receiver_info_text_input_callback ,
471+ app ,
472+ app -> save_filename ,
473+ sizeof (app -> save_filename ),
474+ false); // don't clear default text
475+
476+ view_dispatcher_switch_to_view (app -> view_dispatcher , ProtoPirateViewTextInput );
477+ }
478+ consumed = true;
479+ }
480+
481+ if (event .event == ProtoPirateCustomEventReceiverInfoSaveConfirm ) {
482+ // User confirmed the filename in text input
483+ FlipperFormat * ff =
484+ protopirate_history_get_raw_data (app -> txrx -> history , app -> save_history_idx );
485+ if (ff ) {
486+ // Build full path: folder/filename.psf
487+ FuriString * save_path = furi_string_alloc_printf (
488+ "%s/%s%s" ,
489+ PROTOPIRATE_APP_FOLDER ,
490+ app -> save_filename ,
491+ PROTOPIRATE_APP_EXTENSION );
492+
493+ if (protopirate_storage_save_capture_to_path (
494+ ff , furi_string_get_cstr (save_path ))) {
427495 notification_message (app -> notifications , & sequence_success );
428- FURI_LOG_I (TAG , "Saved to: %s" , furi_string_get_cstr (saved_path ));
496+ FURI_LOG_I (TAG , "Saved to: %s" , furi_string_get_cstr (save_path ));
429497 } else {
430498 notification_message (app -> notifications , & sequence_error );
431499 FURI_LOG_E (TAG , "Save failed" );
432500 }
433- furi_string_free (protocol );
434- furi_string_free (saved_path );
501+ furi_string_free (save_path );
435502 }
503+
504+ // Clean up save protocol string
505+ if (app -> save_protocol ) {
506+ furi_string_free (app -> save_protocol );
507+ app -> save_protocol = NULL ;
508+ }
509+
510+ // Return to the receiver info widget
511+ view_dispatcher_switch_to_view (app -> view_dispatcher , ProtoPirateViewWidget );
436512 consumed = true;
437513 }
514+
438515#ifdef ENABLE_EMULATE_FEATURE
439- else if (event .event == ProtoPirateCustomEventReceiverInfoEmulate && !is_emu_off ) {
516+ if (event .event == ProtoPirateCustomEventReceiverInfoEmulate && !is_emu_off ) {
440517 FlipperFormat * ff =
441518 protopirate_history_get_raw_data (app -> txrx -> history , app -> txrx -> idx_menu_chosen );
442519 if (ff ) {
@@ -464,4 +541,4 @@ void protopirate_scene_receiver_info_on_exit(void* context) {
464541 furi_check (context );
465542 ProtoPirateApp * app = context ;
466543 widget_reset (app -> widget );
467- }
544+ }
0 commit comments