Merge branch 'dev' into release-candidate
This commit is contained in:
		
						commit
						625eb0a4a5
					
				
							
								
								
									
										52
									
								
								RoadMap.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								RoadMap.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | # RoadMap | ||||||
|  | 
 | ||||||
|  | # Where we are (0.x.x branch) | ||||||
|  | 
 | ||||||
|  | Our goal for 0.x.x branch is to build stable usable apps and API. | ||||||
|  | First public release that we support in this branch is 0.43.1. Your device most likely came with this version. | ||||||
|  | You can develop applications but keep in mind that API is not final yet. | ||||||
|  | 
 | ||||||
|  | ## What's already implemented | ||||||
|  | 
 | ||||||
|  | **Applications** | ||||||
|  | 
 | ||||||
|  | - SubGhz: all most common protocols, reading RAW for everything else | ||||||
|  | - 125kHz RFID: all most common protocols | ||||||
|  | - NFC: reading/emulating Mifare Ultralight, reading MiFare Classic and DESFire, basic EMV, basic NFC-B,F,V | ||||||
|  | - Infrared: all most common RC protocols, RAW format for everything else | ||||||
|  | - GPIO: UART bridge, basic GPIO controls | ||||||
|  | - iButton: DS1990, Cyfral, Metacom | ||||||
|  | - Bad USB: Full USB Rubber Ducky support, some extras for windows alt codes | ||||||
|  | - U2F: Full U2F specification support | ||||||
|  | 
 | ||||||
|  | **Extras** | ||||||
|  | 
 | ||||||
|  | - BLE Keyboard | ||||||
|  | - Snake game | ||||||
|  | 
 | ||||||
|  | **System and HAL** | ||||||
|  | 
 | ||||||
|  | - Furi Core | ||||||
|  | - Furi HAL  | ||||||
|  | 
 | ||||||
|  | # Where we're going (Version 1) | ||||||
|  | 
 | ||||||
|  | Main goal for 1.0.0 is to provide first stable version for both Users and Developers. | ||||||
|  | 
 | ||||||
|  | ## What we're planning to implement in 1.0.0 | ||||||
|  | 
 | ||||||
|  | - Updating firmware from SD (work in progress, almost done) | ||||||
|  | - Loading applications from SD (tested as PoC, work scheduled for Q2) | ||||||
|  | - More protocols (gathering feedback) | ||||||
|  | - User documentation (work in progress) | ||||||
|  | - FuriCore: get rid of CMSIS API, replace hard real time timers, improve stability and performance (work in progress) | ||||||
|  | - FuriHal: deep sleep mode, stable API, examples, documentation (work in progress) | ||||||
|  | - Application improvements (a ton of things that we want to add and improve that are too numerous to list here) | ||||||
|  | 
 | ||||||
|  | ## When will it happen and where I can see the progress? | ||||||
|  | 
 | ||||||
|  | Release 1.0.0 will most likely happen around the end of Q3 | ||||||
|  | 
 | ||||||
|  | Development progress can be tracked in our public Miro board: | ||||||
|  | 
 | ||||||
|  | https://miro.com/app/board/uXjVO_3D6xU=/?moveToWidget=3458764522498020058&cot=14 | ||||||
| @ -39,6 +39,8 @@ bool archive_app_read_dir(void* context, const char* path) { | |||||||
|     furi_assert(path); |     furi_assert(path); | ||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
| 
 | 
 | ||||||
|  |     archive_file_array_rm_all(browser); | ||||||
|  | 
 | ||||||
|     ArchiveAppTypeEnum app = archive_get_app_type(path); |     ArchiveAppTypeEnum app = archive_get_app_type(path); | ||||||
| 
 | 
 | ||||||
|     if(app == ArchiveAppTypeU2f) { |     if(app == ArchiveAppTypeU2f) { | ||||||
|  | |||||||
| @ -3,25 +3,31 @@ | |||||||
| #include "archive_browser.h" | #include "archive_browser.h" | ||||||
| #include <math.h> | #include <math.h> | ||||||
| 
 | 
 | ||||||
|  | bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) { | ||||||
|  |     size_t array_size = files_array_size(model->files); | ||||||
|  | 
 | ||||||
|  |     if((idx >= model->array_offset + array_size) || (idx < model->array_offset)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void archive_update_offset(ArchiveBrowserView* browser) { | void archive_update_offset(ArchiveBrowserView* browser) { | ||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             size_t array_size = files_array_size(model->files); |             uint16_t bounds = model->item_cnt > 3 ? 2 : model->item_cnt; | ||||||
|             uint16_t bounds = array_size > 3 ? 2 : array_size; |  | ||||||
| 
 | 
 | ||||||
|             if(array_size > 3 && model->idx >= array_size - 1) { |             if(model->item_cnt > 3 && model->item_idx >= model->item_cnt - 1) { | ||||||
|                 model->list_offset = model->idx - 3; |                 model->list_offset = model->item_idx - 3; | ||||||
|             } else if( |             } else if(model->list_offset < model->item_idx - bounds) { | ||||||
|                 model->last_offset && model->last_offset != model->list_offset && |                 model->list_offset = CLAMP(model->item_idx - 2, model->item_cnt - bounds, 0); | ||||||
|                 model->tab_idx == model->last_tab) { |             } else if(model->list_offset > model->item_idx - bounds) { | ||||||
|                 model->list_offset = model->last_offset; |                 model->list_offset = CLAMP(model->item_idx - 1, model->item_cnt - bounds, 0); | ||||||
|                 model->last_offset = !model->last_offset; |  | ||||||
|             } else if(model->list_offset < model->idx - bounds) { |  | ||||||
|                 model->list_offset = CLAMP(model->idx - 2, array_size - bounds, 0); |  | ||||||
|             } else if(model->list_offset > model->idx - bounds) { |  | ||||||
|                 model->list_offset = CLAMP(model->idx - 1, array_size - bounds, 0); |  | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
| @ -32,8 +38,8 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) { | |||||||
| 
 | 
 | ||||||
|     archive_get_filenames(browser, string_get_cstr(browser->path)); |     archive_get_filenames(browser, string_get_cstr(browser->path)); | ||||||
| 
 | 
 | ||||||
|     if(!archive_file_array_size(browser) && !archive_get_depth(browser)) { |     if(!archive_file_get_array_size(browser) && !archive_get_depth(browser)) { | ||||||
|         archive_switch_tab(browser, DEFAULT_TAB_DIR); |         archive_switch_tab(browser, TAB_RIGHT); | ||||||
|     } else { |     } else { | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             browser->view, (ArchiveBrowserViewModel * model) { |             browser->view, (ArchiveBrowserViewModel * model) { | ||||||
| @ -41,7 +47,7 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) { | |||||||
|                 while(idx < files_array_size(model->files)) { |                 while(idx < files_array_size(model->files)) { | ||||||
|                     ArchiveFile_t* current = files_array_get(model->files, idx); |                     ArchiveFile_t* current = files_array_get(model->files, idx); | ||||||
|                     if(!string_search(current->name, target)) { |                     if(!string_search(current->name, target)) { | ||||||
|                         model->idx = idx; |                         model->item_idx = idx + model->array_offset; | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                     ++idx; |                     ++idx; | ||||||
| @ -53,7 +59,9 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t archive_file_array_size(ArchiveBrowserView* browser) { | size_t archive_file_get_array_size(ArchiveBrowserView* browser) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     uint16_t size = 0; |     uint16_t size = 0; | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
| @ -63,40 +71,60 @@ size_t archive_file_array_size(ArchiveBrowserView* browser) { | |||||||
|     return size; |     return size; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_file_array_rm_selected(ArchiveBrowserView* browser) { | void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             files_array_remove_v(model->files, model->idx, model->idx + 1); |             model->item_cnt = count; | ||||||
|             model->idx = CLAMP(model->idx, files_array_size(model->files) - 1, 0); |             return false; | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void archive_file_array_rm_selected(ArchiveBrowserView* browser) { | ||||||
|  |     furi_assert(browser); | ||||||
|  |     uint32_t items_cnt = 0; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|  |             files_array_remove_v( | ||||||
|  |                 model->files, | ||||||
|  |                 model->item_idx - model->array_offset, | ||||||
|  |                 model->item_idx - model->array_offset + 1); | ||||||
|  |             model->item_cnt--; | ||||||
|  |             model->item_idx = CLAMP(model->item_idx, model->item_cnt - 1, 0); | ||||||
|  |             items_cnt = model->item_cnt; | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|     if(!archive_file_array_size(browser) && !archive_get_depth(browser)) { |     if((items_cnt == 0) && (archive_get_depth(browser) == 0)) { | ||||||
|         archive_switch_tab(browser, DEFAULT_TAB_DIR); |         archive_switch_tab(browser, TAB_RIGHT); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     archive_update_offset(browser); |     archive_update_offset(browser); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_file_array_swap(ArchiveBrowserView* browser, int8_t d) { | void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             ArchiveFile_t temp; |             ArchiveFile_t temp; | ||||||
|             size_t array_size = files_array_size(model->files) - 1; |             size_t array_size = files_array_size(model->files) - 1; | ||||||
|             uint8_t swap_idx = CLAMP(model->idx + d, array_size, 0); |             uint8_t swap_idx = CLAMP(model->item_idx + dir, array_size, 0); | ||||||
| 
 | 
 | ||||||
|             if(model->idx == 0 && d < 0) { |             if(model->item_idx == 0 && dir < 0) { | ||||||
|                 ArchiveFile_t_init(&temp); |                 ArchiveFile_t_init(&temp); | ||||||
|                 files_array_pop_at(&temp, model->files, array_size); |                 files_array_pop_at(&temp, model->files, array_size); | ||||||
|                 files_array_push_at(model->files, model->idx, temp); |                 files_array_push_at(model->files, model->item_idx, temp); | ||||||
|                 ArchiveFile_t_clear(&temp); |                 ArchiveFile_t_clear(&temp); | ||||||
|             } else if(model->idx == array_size && d > 0) { |             } else if(model->item_idx == array_size && dir > 0) { | ||||||
|                 ArchiveFile_t_init(&temp); |                 ArchiveFile_t_init(&temp); | ||||||
|                 files_array_pop_at(&temp, model->files, model->last_idx); |                 files_array_pop_at(&temp, model->files, model->item_idx); | ||||||
|                 files_array_push_at(model->files, array_size, temp); |                 files_array_push_at(model->files, array_size, temp); | ||||||
|                 ArchiveFile_t_clear(&temp); |                 ArchiveFile_t_clear(&temp); | ||||||
|             } else { |             } else { | ||||||
|                 files_array_swap_at(model->files, model->idx, swap_idx); |                 files_array_swap_at(model->files, model->item_idx, swap_idx); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return false; |             return false; | ||||||
| @ -104,6 +132,8 @@ void archive_file_array_swap(ArchiveBrowserView* browser, int8_t d) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_file_array_rm_all(ArchiveBrowserView* browser) { | void archive_file_array_rm_all(ArchiveBrowserView* browser) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             files_array_reset(model->files); |             files_array_reset(model->files); | ||||||
| @ -111,23 +141,61 @@ void archive_file_array_rm_all(ArchiveBrowserView* browser) { | |||||||
|         }); |         }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|  |     int32_t offset_new = 0; | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|  |             if(model->item_cnt > FILE_LIST_BUF_LEN) { | ||||||
|  |                 if(dir < 0) { | ||||||
|  |                     offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 3; | ||||||
|  |                 } else if(dir == 0) { | ||||||
|  |                     offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 2; | ||||||
|  |                 } else { | ||||||
|  |                     offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1; | ||||||
|  |                 } | ||||||
|  |                 offset_new = CLAMP(offset_new, model->item_cnt - FILE_LIST_BUF_LEN, 0); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     bool res = archive_dir_read_items( | ||||||
|  |         browser, string_get_cstr(browser->path), offset_new, FILE_LIST_BUF_LEN); | ||||||
|  | 
 | ||||||
|  |     with_view_model( | ||||||
|  |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|  |             model->array_offset = offset_new; | ||||||
|  |             model->list_loading = false; | ||||||
|  |             return true; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) { | ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     ArchiveFile_t* selected; |     ArchiveFile_t* selected; | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             selected = files_array_size(model->files) ? files_array_get(model->files, model->idx) : |             selected = files_array_size(model->files) ? | ||||||
|                                                         NULL; |                            files_array_get(model->files, model->item_idx - model->array_offset) : | ||||||
|  |                            NULL; | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
|     return selected; |     return selected; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) { | ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     ArchiveFile_t* selected; |     ArchiveFile_t* selected; | ||||||
|     idx = CLAMP(idx, archive_file_array_size(browser), 0); |  | ||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|  |             idx = CLAMP(idx - model->array_offset, files_array_size(model->files), 0); | ||||||
|             selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL; |             selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL; | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| @ -135,6 +203,8 @@ ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { | ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     ArchiveTabEnum tab_id; |     ArchiveTabEnum tab_id; | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
| @ -145,10 +215,12 @@ ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint8_t archive_get_depth(ArchiveBrowserView* browser) { | uint8_t archive_get_depth(ArchiveBrowserView* browser) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     uint8_t depth; |     uint8_t depth; | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             depth = model->depth; |             depth = idx_last_array_size(model->idx_last); | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -165,6 +237,8 @@ const char* archive_get_name(ArchiveBrowserView* browser) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { | void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             model->tab_idx = tab; |             model->tab_idx = tab; | ||||||
| @ -172,6 +246,8 @@ void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { | |||||||
|         }); |         }); | ||||||
| } | } | ||||||
| void archive_set_last_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { | void archive_set_last_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             model->last_tab = model->tab_idx; |             model->last_tab = model->tab_idx; | ||||||
| @ -198,11 +274,12 @@ void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { | |||||||
| 
 | 
 | ||||||
|     ArchiveFile_t_init(&item); |     ArchiveFile_t_init(&item); | ||||||
|     string_init_set_str(item.name, name); |     string_init_set_str(item.name, name); | ||||||
|     set_file_type(&item, NULL, app_name + 1, true); |     archive_set_file_type(&item, NULL, app_name + 1, true); | ||||||
| 
 | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             files_array_push_back(model->files, item); |             files_array_push_back(model->files, item); | ||||||
|  |             model->item_cnt = files_array_size(model->files); | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
|     ArchiveFile_t_clear(&item); |     ArchiveFile_t_clear(&item); | ||||||
| @ -216,10 +293,11 @@ void archive_add_file_item(ArchiveBrowserView* browser, FileInfo* file_info, con | |||||||
| 
 | 
 | ||||||
|     ArchiveFile_t item; |     ArchiveFile_t item; | ||||||
| 
 | 
 | ||||||
|     if(filter_by_extension(file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { |     if(archive_filter_by_extension( | ||||||
|  |            file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { | ||||||
|         ArchiveFile_t_init(&item); |         ArchiveFile_t_init(&item); | ||||||
|         string_init_set_str(item.name, name); |         string_init_set_str(item.name, name); | ||||||
|         set_file_type(&item, file_info, archive_get_path(browser), false); |         archive_set_file_type(&item, file_info, archive_get_path(browser), false); | ||||||
| 
 | 
 | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             browser->view, (ArchiveBrowserViewModel * model) { |             browser->view, (ArchiveBrowserViewModel * model) { | ||||||
| @ -234,12 +312,17 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { | |||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             model->menu = show; |  | ||||||
|             model->menu_idx = 0; |  | ||||||
| 
 |  | ||||||
|             if(show) { |             if(show) { | ||||||
|                 ArchiveFile_t* selected = files_array_get(model->files, model->idx); |                 if(archive_is_item_in_array(model, model->item_idx)) { | ||||||
|                 selected->fav = archive_is_favorite("%s", string_get_cstr(selected->name)); |                     model->menu = true; | ||||||
|  |                     model->menu_idx = 0; | ||||||
|  |                     ArchiveFile_t* selected = | ||||||
|  |                         files_array_get(model->files, model->item_idx - model->array_offset); | ||||||
|  |                     selected->fav = archive_is_favorite("%s", string_get_cstr(selected->name)); | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 model->menu = false; | ||||||
|  |                 model->menu_idx = 0; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return true; |             return true; | ||||||
| @ -247,6 +330,8 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { | void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { | ||||||
|  |     furi_assert(browser); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             model->move_fav = active; |             model->move_fav = active; | ||||||
| @ -282,7 +367,8 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { | |||||||
|     } else if(strncmp(path, "/app:", 5) == 0) { |     } else if(strncmp(path, "/app:", 5) == 0) { | ||||||
|         if(archive_app_is_available(browser, path)) tab_empty = false; |         if(archive_app_is_available(browser, path)) tab_empty = false; | ||||||
|     } else { |     } else { | ||||||
|         if(archive_dir_not_empty(browser, archive_get_default_path(tab))) tab_empty = false; |         uint32_t files_cnt = archive_dir_count_items(browser, archive_get_default_path(tab)); | ||||||
|  |         if(files_cnt > 0) tab_empty = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if((tab_empty) && (tab != ArchiveTabBrowser)) { |     if((tab_empty) && (tab != ArchiveTabBrowser)) { | ||||||
| @ -291,8 +377,9 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { | |||||||
|         with_view_model( |         with_view_model( | ||||||
|             browser->view, (ArchiveBrowserViewModel * model) { |             browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|                 if(model->last_tab != model->tab_idx) { |                 if(model->last_tab != model->tab_idx) { | ||||||
|                     model->idx = 0; |                     model->item_idx = 0; | ||||||
|                     model->depth = 0; |                     model->array_offset = 0; | ||||||
|  |                     idx_last_array_reset(model->idx_last); | ||||||
|                 } |                 } | ||||||
|                 return false; |                 return false; | ||||||
|             }); |             }); | ||||||
| @ -305,11 +392,13 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { | |||||||
|     furi_assert(browser); |     furi_assert(browser); | ||||||
|     furi_assert(name); |     furi_assert(name); | ||||||
| 
 | 
 | ||||||
|  |     archive_dir_count_items(browser, string_get_cstr(name)); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             model->last_idx = model->idx; |             idx_last_array_push_back(model->idx_last, model->item_idx); | ||||||
|             model->idx = 0; |             model->array_offset = 0; | ||||||
|             model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0); |             model->item_idx = 0; | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -329,10 +418,11 @@ void archive_leave_dir(ArchiveBrowserView* browser) { | |||||||
|         string_left(browser->path, pos); |         string_left(browser->path, pos); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     archive_dir_count_items(browser, path); | ||||||
|  | 
 | ||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             model->depth = CLAMP(model->depth - 1, MAX_DEPTH, 0); |             idx_last_array_pop_back(&model->item_idx, model->idx_last); | ||||||
|             model->idx = model->last_idx; |  | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,8 @@ | |||||||
| 
 | 
 | ||||||
| #include "../archive_i.h" | #include "../archive_i.h" | ||||||
| 
 | 
 | ||||||
| #define DEFAULT_TAB_DIR InputKeyRight //default tab swith direction
 | #define TAB_RIGHT InputKeyRight //default tab swith direction
 | ||||||
|  | #define FILE_LIST_BUF_LEN 100 | ||||||
| 
 | 
 | ||||||
| static const char* tab_default_paths[] = { | static const char* tab_default_paths[] = { | ||||||
|     [ArchiveTabFavorites] = "/any/favorites", |     [ArchiveTabFavorites] = "/any/favorites", | ||||||
| @ -56,14 +57,18 @@ inline bool archive_is_known_app(ArchiveFileTypeEnum type) { | |||||||
|     return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); |     return (type != ArchiveFileTypeFolder && type != ArchiveFileTypeUnknown); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx); | ||||||
| void archive_update_offset(ArchiveBrowserView* browser); | void archive_update_offset(ArchiveBrowserView* browser); | ||||||
| void archive_update_focus(ArchiveBrowserView* browser, const char* target); | void archive_update_focus(ArchiveBrowserView* browser, const char* target); | ||||||
| 
 | 
 | ||||||
| size_t archive_file_array_size(ArchiveBrowserView* browser); | bool archive_file_array_load(ArchiveBrowserView* browser, int8_t dir); | ||||||
|  | size_t archive_file_get_array_size(ArchiveBrowserView* browser); | ||||||
| void archive_file_array_rm_selected(ArchiveBrowserView* browser); | void archive_file_array_rm_selected(ArchiveBrowserView* browser); | ||||||
| void archive_file_array_swap(ArchiveBrowserView* browser, int8_t d); | void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir); | ||||||
| void archive_file_array_rm_all(ArchiveBrowserView* browser); | void archive_file_array_rm_all(ArchiveBrowserView* browser); | ||||||
| 
 | 
 | ||||||
|  | void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count); | ||||||
|  | 
 | ||||||
| ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser); | ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser); | ||||||
| ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx); | ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx); | ||||||
| ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser); | ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser); | ||||||
|  | |||||||
| @ -84,6 +84,9 @@ bool archive_favorites_read(void* context) { | |||||||
|     string_init(buffer); |     string_init(buffer); | ||||||
| 
 | 
 | ||||||
|     bool need_refresh = false; |     bool need_refresh = false; | ||||||
|  |     uint16_t file_count = 0; | ||||||
|  | 
 | ||||||
|  |     archive_file_array_rm_all(browser); | ||||||
| 
 | 
 | ||||||
|     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); |     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); | ||||||
| 
 | 
 | ||||||
| @ -99,6 +102,7 @@ bool archive_favorites_read(void* context) { | |||||||
|             if(string_search(buffer, "/app:") == 0) { |             if(string_search(buffer, "/app:") == 0) { | ||||||
|                 if(archive_app_is_available(browser, string_get_cstr(buffer))) { |                 if(archive_app_is_available(browser, string_get_cstr(buffer))) { | ||||||
|                     archive_add_app_item(browser, string_get_cstr(buffer)); |                     archive_add_app_item(browser, string_get_cstr(buffer)); | ||||||
|  |                     file_count++; | ||||||
|                 } else { |                 } else { | ||||||
|                     need_refresh = true; |                     need_refresh = true; | ||||||
|                 } |                 } | ||||||
| @ -106,10 +110,12 @@ bool archive_favorites_read(void* context) { | |||||||
|                 bool file_exists = false; |                 bool file_exists = false; | ||||||
|                 file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); |                 file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists); | ||||||
| 
 | 
 | ||||||
|                 if(file_exists) |                 if(file_exists) { | ||||||
|                     archive_add_file_item(browser, &file_info, string_get_cstr(buffer)); |                     archive_add_file_item(browser, &file_info, string_get_cstr(buffer)); | ||||||
|                 else |                     file_count++; | ||||||
|  |                 } else { | ||||||
|                     need_refresh = true; |                     need_refresh = true; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             string_reset(buffer); |             string_reset(buffer); | ||||||
| @ -119,6 +125,8 @@ bool archive_favorites_read(void* context) { | |||||||
|     file_worker_close(file_worker); |     file_worker_close(file_worker); | ||||||
|     file_worker_free(file_worker); |     file_worker_free(file_worker); | ||||||
| 
 | 
 | ||||||
|  |     archive_set_item_count(browser, file_count); | ||||||
|  | 
 | ||||||
|     if(need_refresh) { |     if(need_refresh) { | ||||||
|         archive_favourites_rescan(); |         archive_favourites_rescan(); | ||||||
|     } |     } | ||||||
| @ -257,7 +265,7 @@ void archive_favorites_save(void* context) { | |||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
|     FileWorker* file_worker = file_worker_alloc(true); |     FileWorker* file_worker = file_worker_alloc(true); | ||||||
| 
 | 
 | ||||||
|     for(size_t i = 0; i < archive_file_array_size(browser); i++) { |     for(size_t i = 0; i < archive_file_get_array_size(browser); i++) { | ||||||
|         ArchiveFile_t* item = archive_get_file_at(browser, i); |         ArchiveFile_t* item = archive_get_file_at(browser, i); | ||||||
|         archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(item->name)); |         archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(item->name)); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| #define ASSETS_DIR "assets" | #define ASSETS_DIR "assets" | ||||||
| 
 | 
 | ||||||
| bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { | bool archive_filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { | ||||||
|     furi_assert(file_info); |     furi_assert(file_info); | ||||||
|     furi_assert(tab_ext); |     furi_assert(tab_ext); | ||||||
|     furi_assert(name); |     furi_assert(name); | ||||||
| @ -45,7 +45,7 @@ void archive_get_file_extension(char* name, char* ext) { | |||||||
|         strncpy(ext, dot, MAX_EXT_LEN); |         strncpy(ext, dot, MAX_EXT_LEN); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app) { | void archive_set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app) { | ||||||
|     furi_assert(file); |     furi_assert(file); | ||||||
| 
 | 
 | ||||||
|     file->is_app = is_app; |     file->is_app = is_app; | ||||||
| @ -83,21 +83,19 @@ bool archive_get_filenames(void* context, const char* path) { | |||||||
| 
 | 
 | ||||||
|     bool res; |     bool res; | ||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
|     archive_file_array_rm_all(browser); |  | ||||||
| 
 | 
 | ||||||
|     if(archive_get_tab(browser) == ArchiveTabFavorites) { |     if(archive_get_tab(browser) == ArchiveTabFavorites) { | ||||||
|         res = archive_favorites_read(browser); |         res = archive_favorites_read(browser); | ||||||
|     } else if(strncmp(path, "/app:", 5) == 0) { |     } else if(strncmp(path, "/app:", 5) == 0) { | ||||||
|         res = archive_app_read_dir(browser, path); |         res = archive_app_read_dir(browser, path); | ||||||
|     } else { |     } else { | ||||||
|         res = archive_read_dir(browser, path); |         res = archive_file_array_load(browser, 0); | ||||||
|     } |     } | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool archive_dir_not_empty(void* context, const char* path) { // can be simpler?
 | uint32_t archive_dir_count_items(void* context, const char* path) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 |  | ||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
| 
 | 
 | ||||||
|     FileInfo file_info; |     FileInfo file_info; | ||||||
| @ -108,23 +106,19 @@ bool archive_dir_not_empty(void* context, const char* path) { // can be simpler? | |||||||
|     if(!storage_dir_open(directory, path)) { |     if(!storage_dir_open(directory, path)) { | ||||||
|         storage_dir_close(directory); |         storage_dir_close(directory); | ||||||
|         storage_file_free(directory); |         storage_file_free(directory); | ||||||
|         return false; |         return 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool files_found = false; |     uint32_t files_found = 0; | ||||||
|     while(1) { |     while(1) { | ||||||
|         if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { |         if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         if(files_found) { |         if((storage_file_get_error(directory) == FSE_OK) && (name[0])) { | ||||||
|             break; |             if(archive_filter_by_extension( | ||||||
|         } else if((storage_file_get_error(directory) == FSE_OK) && (name[0])) { |  | ||||||
|             if(filter_by_extension( |  | ||||||
|                    &file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { |                    &file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { | ||||||
|                 files_found = true; |                 files_found++; | ||||||
|             } |             } | ||||||
|         } else { |  | ||||||
|             return false; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     storage_dir_close(directory); |     storage_dir_close(directory); | ||||||
| @ -132,10 +126,12 @@ bool archive_dir_not_empty(void* context, const char* path) { // can be simpler? | |||||||
| 
 | 
 | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
| 
 | 
 | ||||||
|  |     archive_set_item_count(browser, files_found); | ||||||
|  | 
 | ||||||
|     return files_found; |     return files_found; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool archive_read_dir(void* context, const char* path) { | uint32_t archive_dir_read_items(void* context, const char* path, uint32_t offset, uint32_t count) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 | 
 | ||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
| @ -145,7 +141,6 @@ bool archive_read_dir(void* context, const char* path) { | |||||||
|     char name[MAX_NAME_LEN]; |     char name[MAX_NAME_LEN]; | ||||||
|     snprintf(name, MAX_NAME_LEN, "%s/", path); |     snprintf(name, MAX_NAME_LEN, "%s/", path); | ||||||
|     size_t path_len = strlen(name); |     size_t path_len = strlen(name); | ||||||
|     size_t files_cnt = 0; |  | ||||||
| 
 | 
 | ||||||
|     if(!storage_dir_open(directory, path)) { |     if(!storage_dir_open(directory, path)) { | ||||||
|         storage_dir_close(directory); |         storage_dir_close(directory); | ||||||
| @ -153,28 +148,48 @@ bool archive_read_dir(void* context, const char* path) { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     while(1) { |     // Skip items before offset
 | ||||||
|  |     uint32_t items_cnt = 0; | ||||||
|  |     while(items_cnt < offset) { | ||||||
|  |         if(!storage_dir_read(directory, &file_info, &name[path_len], MAX_NAME_LEN)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if(storage_file_get_error(directory) == FSE_OK) { | ||||||
|  |             if(archive_filter_by_extension( | ||||||
|  |                    &file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { | ||||||
|  |                 items_cnt++; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if(items_cnt != offset) { | ||||||
|  |         storage_dir_close(directory); | ||||||
|  |         storage_file_free(directory); | ||||||
|  |         furi_record_close("storage"); | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     items_cnt = 0; | ||||||
|  |     archive_file_array_rm_all(browser); | ||||||
|  |     while(items_cnt < count) { | ||||||
|         if(!storage_dir_read(directory, &file_info, &name[path_len], MAX_NAME_LEN - path_len)) { |         if(!storage_dir_read(directory, &file_info, &name[path_len], MAX_NAME_LEN - path_len)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(files_cnt > MAX_FILES) { |         if(storage_file_get_error(directory) == FSE_OK) { | ||||||
|             break; |  | ||||||
|         } else if(storage_file_get_error(directory) == FSE_OK) { |  | ||||||
|             archive_add_file_item(browser, &file_info, name); |             archive_add_file_item(browser, &file_info, name); | ||||||
|             ++files_cnt; |             items_cnt++; | ||||||
|         } else { |         } else { | ||||||
|             storage_dir_close(directory); |             break; | ||||||
|             storage_file_free(directory); |  | ||||||
|             return false; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     storage_dir_close(directory); |     storage_dir_close(directory); | ||||||
|     storage_file_free(directory); |     storage_file_free(directory); | ||||||
| 
 |  | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
| 
 | 
 | ||||||
|     return true; |     return (items_cnt == count); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_file_append(const char* path, const char* format, ...) { | void archive_file_append(const char* path, const char* format, ...) { | ||||||
| @ -210,10 +225,20 @@ void archive_delete_file(void* context, const char* format, ...) { | |||||||
|     va_end(args); |     va_end(args); | ||||||
| 
 | 
 | ||||||
|     ArchiveBrowserView* browser = context; |     ArchiveBrowserView* browser = context; | ||||||
|     FileWorker* file_worker = file_worker_alloc(true); |     Storage* fs_api = furi_record_open("storage"); | ||||||
| 
 | 
 | ||||||
|     bool res = file_worker_remove(file_worker, string_get_cstr(filename)); |     FileInfo fileinfo; | ||||||
|     file_worker_free(file_worker); |     storage_common_stat(fs_api, string_get_cstr(filename), &fileinfo); | ||||||
|  | 
 | ||||||
|  |     bool res = false; | ||||||
|  | 
 | ||||||
|  |     if(fileinfo.flags & FSF_DIRECTORY) { | ||||||
|  |         res = storage_simply_remove_recursive(fs_api, string_get_cstr(filename)); | ||||||
|  |     } else { | ||||||
|  |         res = (storage_common_remove(fs_api, string_get_cstr(filename)) == FSE_OK); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_record_close("storage"); | ||||||
| 
 | 
 | ||||||
|     if(archive_is_favorite("%s", string_get_cstr(filename))) { |     if(archive_is_favorite("%s", string_get_cstr(filename))) { | ||||||
|         archive_favorites_delete("%s", string_get_cstr(filename)); |         archive_favorites_delete("%s", string_get_cstr(filename)); | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "file_worker.h" | #include "file_worker.h" | ||||||
| 
 | #include <m-array.h> | ||||||
| #define MAX_FILES 100 //temp
 |  | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     ArchiveFileTypeIButton, |     ArchiveFileTypeIButton, | ||||||
| @ -13,7 +12,7 @@ typedef enum { | |||||||
|     ArchiveFileTypeU2f, |     ArchiveFileTypeU2f, | ||||||
|     ArchiveFileTypeFolder, |     ArchiveFileTypeFolder, | ||||||
|     ArchiveFileTypeUnknown, |     ArchiveFileTypeUnknown, | ||||||
|     ArchiveFileTypesTotal, |     ArchiveFileTypeLoading, | ||||||
| } ArchiveFileTypeEnum; | } ArchiveFileTypeEnum; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -56,12 +55,12 @@ ARRAY_DEF( | |||||||
|      INIT_SET(API_6(ArchiveFile_t_init_set)), |      INIT_SET(API_6(ArchiveFile_t_init_set)), | ||||||
|      CLEAR(API_2(ArchiveFile_t_clear)))) |      CLEAR(API_2(ArchiveFile_t_clear)))) | ||||||
| 
 | 
 | ||||||
| bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name); | bool archive_filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name); | ||||||
| void set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app); | void archive_set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app); | ||||||
| void archive_trim_file_path(char* name, bool ext); | void archive_trim_file_path(char* name, bool ext); | ||||||
| void archive_get_file_extension(char* name, char* ext); | void archive_get_file_extension(char* name, char* ext); | ||||||
| bool archive_get_filenames(void* context, const char* path); | bool archive_get_filenames(void* context, const char* path); | ||||||
| bool archive_dir_not_empty(void* context, const char* path); | uint32_t archive_dir_count_items(void* context, const char* path); | ||||||
| bool archive_read_dir(void* context, const char* path); | uint32_t archive_dir_read_items(void* context, const char* path, uint32_t offset, uint32_t count); | ||||||
| void archive_file_append(const char* path, const char* format, ...); | void archive_file_append(const char* path, const char* format, ...); | ||||||
| void archive_delete_file(void* context, const char* format, ...); | void archive_delete_file(void* context, const char* format, ...); | ||||||
| @ -105,7 +105,9 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case ArchiveBrowserEventFileMenuDelete: |         case ArchiveBrowserEventFileMenuDelete: | ||||||
|             scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); |             if(archive_get_tab(browser) != ArchiveTabFavorites) { | ||||||
|  |                 scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); | ||||||
|  |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case ArchiveBrowserEventEnterDir: |         case ArchiveBrowserEventEnterDir: | ||||||
| @ -136,6 +138,14 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { | |||||||
|             archive_favorites_save(archive->browser); |             archive_favorites_save(archive->browser); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  |         case ArchiveBrowserEventLoadPrevItems: | ||||||
|  |             archive_file_array_load(archive->browser, -1); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
|  |         case ArchiveBrowserEventLoadNextItems: | ||||||
|  |             archive_file_array_load(archive->browser, 1); | ||||||
|  |             consumed = true; | ||||||
|  |             break; | ||||||
| 
 | 
 | ||||||
|         case ArchiveBrowserEventExit: |         case ArchiveBrowserEventExit: | ||||||
|             if(archive_get_depth(browser)) { |             if(archive_get_depth(browser)) { | ||||||
|  | |||||||
| @ -31,8 +31,8 @@ void archive_scene_rename_on_enter(void* context) { | |||||||
|         MAX_TEXT_INPUT_LEN, |         MAX_TEXT_INPUT_LEN, | ||||||
|         false); |         false); | ||||||
| 
 | 
 | ||||||
|     ValidatorIsFile* validator_is_file = |     ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( | ||||||
|         validator_is_file_alloc_init(archive_get_path(archive->browser), archive->file_extension); |         archive_get_path(archive->browser), archive->file_extension, NULL); | ||||||
|     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); |     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); |     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); | ||||||
|  | |||||||
| @ -24,6 +24,7 @@ static const Icon* ArchiveItemIcons[] = { | |||||||
|     [ArchiveFileTypeU2f] = &I_u2f_10px, |     [ArchiveFileTypeU2f] = &I_u2f_10px, | ||||||
|     [ArchiveFileTypeFolder] = &I_dir_10px, |     [ArchiveFileTypeFolder] = &I_dir_10px, | ||||||
|     [ArchiveFileTypeUnknown] = &I_unknown_10px, |     [ArchiveFileTypeUnknown] = &I_unknown_10px, | ||||||
|  |     [ArchiveFileTypeLoading] = &I_unknown_10px, // TODO loading icon
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void archive_browser_set_callback( | void archive_browser_set_callback( | ||||||
| @ -49,7 +50,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { | |||||||
|     string_init_set_str(menu[2], "Rename"); |     string_init_set_str(menu[2], "Rename"); | ||||||
|     string_init_set_str(menu[3], "Delete"); |     string_init_set_str(menu[3], "Delete"); | ||||||
| 
 | 
 | ||||||
|     ArchiveFile_t* selected = files_array_get(model->files, model->idx); |     ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); | ||||||
| 
 | 
 | ||||||
|     if(!archive_is_known_app(selected->type)) { |     if(!archive_is_known_app(selected->type)) { | ||||||
|         string_set_str(menu[0], "---"); |         string_set_str(menu[0], "---"); | ||||||
| @ -58,6 +59,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { | |||||||
|     } else { |     } else { | ||||||
|         if(model->tab_idx == ArchiveTabFavorites) { |         if(model->tab_idx == ArchiveTabFavorites) { | ||||||
|             string_set_str(menu[2], "Move"); |             string_set_str(menu[2], "Move"); | ||||||
|  |             string_set_str(menu[3], "---"); | ||||||
|         } else if(selected->is_app) { |         } else if(selected->is_app) { | ||||||
|             string_set_str(menu[2], "---"); |             string_set_str(menu[2], "---"); | ||||||
|         } |         } | ||||||
| @ -100,36 +102,44 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { | |||||||
|     furi_assert(model); |     furi_assert(model); | ||||||
| 
 | 
 | ||||||
|     size_t array_size = files_array_size(model->files); |     size_t array_size = files_array_size(model->files); | ||||||
|     bool scrollbar = array_size > 4; |     bool scrollbar = model->item_cnt > 4; | ||||||
| 
 | 
 | ||||||
|     for(size_t i = 0; i < MIN(array_size, MENU_ITEMS); ++i) { |     for(size_t i = 0; i < MIN(model->item_cnt, MENU_ITEMS); ++i) { | ||||||
|         string_t str_buff; |         string_t str_buff; | ||||||
|         char cstr_buff[MAX_NAME_LEN]; |         char cstr_buff[MAX_NAME_LEN]; | ||||||
|         size_t idx = CLAMP(i + model->list_offset, array_size, 0); |         size_t idx = CLAMP(i + model->list_offset, model->item_cnt, 0); | ||||||
|         uint8_t x_offset = (model->move_fav && model->idx == idx) ? MOVE_OFFSET : 0; |         uint8_t x_offset = (model->move_fav && model->item_idx == idx) ? MOVE_OFFSET : 0; | ||||||
| 
 | 
 | ||||||
|         ArchiveFile_t* file = files_array_get(model->files, CLAMP(idx, array_size - 1, 0)); |         ArchiveFileTypeEnum file_type = ArchiveFileTypeLoading; | ||||||
|  | 
 | ||||||
|  |         if(archive_is_item_in_array(model, idx)) { | ||||||
|  |             ArchiveFile_t* file = | ||||||
|  |                 files_array_get(model->files, CLAMP(idx - model->array_offset, array_size - 1, 0)); | ||||||
|  |             strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); | ||||||
|  |             archive_trim_file_path(cstr_buff, archive_is_known_app(file->type)); | ||||||
|  |             string_init_set_str(str_buff, cstr_buff); | ||||||
|  |             file_type = file->type; | ||||||
|  |         } else { | ||||||
|  |             string_init_set_str(str_buff, "---"); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); |  | ||||||
|         archive_trim_file_path(cstr_buff, archive_is_known_app(file->type)); |  | ||||||
|         string_init_set_str(str_buff, cstr_buff); |  | ||||||
|         elements_string_fit_width( |         elements_string_fit_width( | ||||||
|             canvas, str_buff, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); |             canvas, str_buff, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); | ||||||
| 
 | 
 | ||||||
|         if(model->idx == idx) { |         if(model->item_idx == idx) { | ||||||
|             archive_draw_frame(canvas, i, scrollbar, model->move_fav); |             archive_draw_frame(canvas, i, scrollbar, model->move_fav); | ||||||
|         } else { |         } else { | ||||||
|             canvas_set_color(canvas, ColorBlack); |             canvas_set_color(canvas, ColorBlack); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         canvas_draw_icon( |         canvas_draw_icon(canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]); | ||||||
|             canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file->type]); |  | ||||||
|         canvas_draw_str(canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); |         canvas_draw_str(canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); | ||||||
|  | 
 | ||||||
|         string_clear(str_buff); |         string_clear(str_buff); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(scrollbar) { |     if(scrollbar) { | ||||||
|         elements_scrollbar_pos(canvas, 126, 15, 49, model->idx, array_size); |         elements_scrollbar_pos(canvas, 126, 15, 49, model->item_idx, model->item_cnt); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(model->menu) { |     if(model->menu) { | ||||||
| @ -173,13 +183,13 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m | |||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void archive_view_render(Canvas* canvas, void* model) { | void archive_view_render(Canvas* canvas, void* mdl) { | ||||||
|     ArchiveBrowserViewModel* m = model; |     ArchiveBrowserViewModel* model = mdl; | ||||||
| 
 | 
 | ||||||
|     archive_render_status_bar(canvas, model); |     archive_render_status_bar(canvas, mdl); | ||||||
| 
 | 
 | ||||||
|     if(files_array_size(m->files)) { |     if(model->item_cnt > 0) { | ||||||
|         draw_list(canvas, m); |         draw_list(canvas, model); | ||||||
|     } else { |     } else { | ||||||
|         canvas_draw_str_aligned( |         canvas_draw_str_aligned( | ||||||
|             canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); |             canvas, GUI_DISPLAY_WIDTH / 2, 40, AlignCenter, AlignCenter, "Empty"); | ||||||
| @ -191,6 +201,26 @@ View* archive_browser_get_view(ArchiveBrowserView* browser) { | |||||||
|     return browser->view; |     return browser->view; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool is_file_list_load_required(ArchiveBrowserViewModel* model) { | ||||||
|  |     size_t array_size = files_array_size(model->files); | ||||||
|  | 
 | ||||||
|  |     if((model->list_loading) || (array_size >= model->item_cnt)) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if((model->array_offset > 0) && | ||||||
|  |        (model->item_idx < (model->array_offset + FILE_LIST_BUF_LEN / 4))) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(((model->array_offset + array_size) < model->item_cnt) && | ||||||
|  |        (model->item_idx > (model->array_offset + array_size - FILE_LIST_BUF_LEN / 4))) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool archive_view_input(InputEvent* event, void* context) { | bool archive_view_input(InputEvent* event, void* context) { | ||||||
|     furi_assert(event); |     furi_assert(event); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| @ -247,22 +277,28 @@ bool archive_view_input(InputEvent* event, void* context) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(event->key == InputKeyUp || event->key == InputKeyDown) { |         if((event->key == InputKeyUp || event->key == InputKeyDown) && | ||||||
|  |            (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||||
|             with_view_model( |             with_view_model( | ||||||
|                 browser->view, (ArchiveBrowserViewModel * model) { |                 browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|                     uint16_t num_elements = (uint16_t)files_array_size(model->files); |                     if(event->key == InputKeyUp) { | ||||||
|                     if((event->type == InputTypeShort || event->type == InputTypeRepeat)) { |                         model->item_idx = | ||||||
|                         if(event->key == InputKeyUp) { |                             ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; | ||||||
|                             model->idx = ((model->idx - 1) + num_elements) % num_elements; |                         if(is_file_list_load_required(model)) { | ||||||
|                             if(move_fav_mode) { |                             model->list_loading = true; | ||||||
|                                 browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); |                             browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context); | ||||||
|                             } |                         } | ||||||
|                         } else if(event->key == InputKeyDown) { |                         if(move_fav_mode) { | ||||||
|                             model->idx = (model->idx + 1) % num_elements; |                             browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); | ||||||
|                             if(move_fav_mode) { |                         } | ||||||
|                                 browser->callback( |                     } else if(event->key == InputKeyDown) { | ||||||
|                                     ArchiveBrowserEventFavMoveDown, browser->context); |                         model->item_idx = (model->item_idx + 1) % model->item_cnt; | ||||||
|                             } |                         if(is_file_list_load_required(model)) { | ||||||
|  |                             model->list_loading = true; | ||||||
|  |                             browser->callback(ArchiveBrowserEventLoadNextItems, browser->context); | ||||||
|  |                         } | ||||||
|  |                         if(move_fav_mode) { | ||||||
|  |                             browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
| @ -317,6 +353,7 @@ ArchiveBrowserView* browser_alloc() { | |||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             files_array_init(model->files); |             files_array_init(model->files); | ||||||
|  |             idx_last_array_init(model->idx_last); | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
| @ -329,6 +366,7 @@ void browser_free(ArchiveBrowserView* browser) { | |||||||
|     with_view_model( |     with_view_model( | ||||||
|         browser->view, (ArchiveBrowserViewModel * model) { |         browser->view, (ArchiveBrowserViewModel * model) { | ||||||
|             files_array_clear(model->files); |             files_array_clear(model->files); | ||||||
|  |             idx_last_array_clear(model->idx_last); | ||||||
|             return false; |             return false; | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -14,7 +14,6 @@ | |||||||
| #define MAX_EXT_LEN 6 | #define MAX_EXT_LEN 6 | ||||||
| #define FRAME_HEIGHT 12 | #define FRAME_HEIGHT 12 | ||||||
| #define MENU_ITEMS 4 | #define MENU_ITEMS 4 | ||||||
| #define MAX_DEPTH 32 |  | ||||||
| #define MOVE_OFFSET 5 | #define MOVE_OFFSET 5 | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
| @ -37,12 +36,18 @@ typedef enum { | |||||||
|     ArchiveBrowserEventFileMenuPin, |     ArchiveBrowserEventFileMenuPin, | ||||||
|     ArchiveBrowserEventFileMenuAction, |     ArchiveBrowserEventFileMenuAction, | ||||||
|     ArchiveBrowserEventFileMenuDelete, |     ArchiveBrowserEventFileMenuDelete, | ||||||
|  | 
 | ||||||
|     ArchiveBrowserEventEnterDir, |     ArchiveBrowserEventEnterDir, | ||||||
|  | 
 | ||||||
|     ArchiveBrowserEventFavMoveUp, |     ArchiveBrowserEventFavMoveUp, | ||||||
|     ArchiveBrowserEventFavMoveDown, |     ArchiveBrowserEventFavMoveDown, | ||||||
|     ArchiveBrowserEventEnterFavMove, |     ArchiveBrowserEventEnterFavMove, | ||||||
|     ArchiveBrowserEventExitFavMove, |     ArchiveBrowserEventExitFavMove, | ||||||
|     ArchiveBrowserEventSaveFavMove, |     ArchiveBrowserEventSaveFavMove, | ||||||
|  | 
 | ||||||
|  |     ArchiveBrowserEventLoadPrevItems, | ||||||
|  |     ArchiveBrowserEventLoadNextItems, | ||||||
|  | 
 | ||||||
|     ArchiveBrowserEventExit, |     ArchiveBrowserEventExit, | ||||||
| } ArchiveBrowserEvent; | } ArchiveBrowserEvent; | ||||||
| 
 | 
 | ||||||
| @ -71,21 +76,23 @@ struct ArchiveBrowserView { | |||||||
|     string_t path; |     string_t path; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | ARRAY_DEF(idx_last_array, int32_t) | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     ArchiveTabEnum tab_idx; |     ArchiveTabEnum tab_idx; | ||||||
|     ArchiveTabEnum last_tab; |     ArchiveTabEnum last_tab; | ||||||
|     files_array_t files; |     files_array_t files; | ||||||
|  |     idx_last_array_t idx_last; | ||||||
| 
 | 
 | ||||||
|     uint8_t menu_idx; |     uint8_t menu_idx; | ||||||
|     bool move_fav; |     bool move_fav; | ||||||
|     bool menu; |     bool menu; | ||||||
|  |     bool list_loading; | ||||||
| 
 | 
 | ||||||
|     uint16_t idx; |     uint32_t item_cnt; | ||||||
|     uint16_t last_idx; |     int32_t item_idx; | ||||||
|     uint16_t list_offset; |     int32_t array_offset; | ||||||
|     uint16_t last_offset; |     int32_t list_offset; | ||||||
|     uint8_t depth; |  | ||||||
| 
 |  | ||||||
| } ArchiveBrowserViewModel; | } ArchiveBrowserViewModel; | ||||||
| 
 | 
 | ||||||
| void archive_browser_set_callback( | void archive_browser_set_callback( | ||||||
|  | |||||||
| @ -94,10 +94,21 @@ void animation_manager_set_interact_callback( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void animation_manager_check_blocking_callback(const void* message, void* context) { | static void animation_manager_check_blocking_callback(const void* message, void* context) { | ||||||
|     furi_assert(context); |     const StorageEvent* storage_event = message; | ||||||
|     AnimationManager* animation_manager = context; | 
 | ||||||
|     if(animation_manager->check_blocking_callback) { |     switch(storage_event->type) { | ||||||
|         animation_manager->check_blocking_callback(animation_manager->context); |     case StorageEventTypeCardMount: | ||||||
|  |     case StorageEventTypeCardUnmount: | ||||||
|  |     case StorageEventTypeCardMountError: | ||||||
|  |         furi_assert(context); | ||||||
|  |         AnimationManager* animation_manager = context; | ||||||
|  |         if(animation_manager->check_blocking_callback) { | ||||||
|  |             animation_manager->check_blocking_callback(animation_manager->context); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,11 +5,19 @@ | |||||||
| struct ValidatorIsFile { | struct ValidatorIsFile { | ||||||
|     const char* app_path_folder; |     const char* app_path_folder; | ||||||
|     const char* app_extension; |     const char* app_extension; | ||||||
|  |     char* current_name; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool validator_is_file_callback(const char* text, string_t error, void* context) { | bool validator_is_file_callback(const char* text, string_t error, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
|     ValidatorIsFile* instance = context; |     ValidatorIsFile* instance = context; | ||||||
|  | 
 | ||||||
|  |     if(instance->current_name != NULL) { | ||||||
|  |         if(strcmp(instance->current_name, text) == 0) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     bool ret = true; |     bool ret = true; | ||||||
|     string_t path; |     string_t path; | ||||||
|     string_init_printf(path, "%s/%s%s", instance->app_path_folder, text, instance->app_extension); |     string_init_printf(path, "%s/%s%s", instance->app_path_folder, text, instance->app_extension); | ||||||
| @ -26,17 +34,21 @@ bool validator_is_file_callback(const char* text, string_t error, void* context) | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ValidatorIsFile* | ValidatorIsFile* validator_is_file_alloc_init( | ||||||
|     validator_is_file_alloc_init(const char* app_path_folder, const char* app_extension) { |     const char* app_path_folder, | ||||||
|  |     const char* app_extension, | ||||||
|  |     const char* current_name) { | ||||||
|     ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); |     ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); | ||||||
| 
 | 
 | ||||||
|     instance->app_path_folder = app_path_folder; |     instance->app_path_folder = app_path_folder; | ||||||
|     instance->app_extension = app_extension; |     instance->app_extension = app_extension; | ||||||
|  |     instance->current_name = strdup(current_name); | ||||||
| 
 | 
 | ||||||
|     return instance; |     return instance; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void validator_is_file_free(ValidatorIsFile* instance) { | void validator_is_file_free(ValidatorIsFile* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|  |     free(instance->current_name); | ||||||
|     free(instance); |     free(instance); | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,8 +8,10 @@ extern "C" { | |||||||
| #endif | #endif | ||||||
| typedef struct ValidatorIsFile ValidatorIsFile; | typedef struct ValidatorIsFile ValidatorIsFile; | ||||||
| 
 | 
 | ||||||
| ValidatorIsFile* | ValidatorIsFile* validator_is_file_alloc_init( | ||||||
|     validator_is_file_alloc_init(const char* app_path_folder, const char* app_extension); |     const char* app_path_folder, | ||||||
|  |     const char* app_extension, | ||||||
|  |     const char* current_name); | ||||||
| 
 | 
 | ||||||
| void validator_is_file_free(ValidatorIsFile* instance); | void validator_is_file_free(ValidatorIsFile* instance); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -63,6 +63,7 @@ void ibutton_cli_print_key_data(iButtonKey* key) { | |||||||
| #define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0) | #define EVENT_FLAG_IBUTTON_COMPLETE (1 << 0) | ||||||
| 
 | 
 | ||||||
| static void ibutton_cli_worker_read_cb(void* context) { | static void ibutton_cli_worker_read_cb(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     osEventFlagsId_t event = context; |     osEventFlagsId_t event = context; | ||||||
|     osEventFlagsSet(event, EVENT_FLAG_IBUTTON_COMPLETE); |     osEventFlagsSet(event, EVENT_FLAG_IBUTTON_COMPLETE); | ||||||
| } | } | ||||||
| @ -112,6 +113,7 @@ typedef struct { | |||||||
| } iButtonWriteContext; | } iButtonWriteContext; | ||||||
| 
 | 
 | ||||||
| static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) { | static void ibutton_cli_worker_write_cb(void* context, iButtonWorkerWriteResult result) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonWriteContext* write_context = (iButtonWriteContext*)context; |     iButtonWriteContext* write_context = (iButtonWriteContext*)context; | ||||||
|     write_context->result = result; |     write_context->result = result; | ||||||
|     osEventFlagsSet(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE); |     osEventFlagsSet(write_context->event, EVENT_FLAG_IBUTTON_COMPLETE); | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ typedef enum { | |||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
| static void submenu_callback(void* context, uint32_t index) { | static void submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| static void byte_input_callback(void* context) { | static void byte_input_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| 
 | 
 | ||||||
| static void widget_callback(GuiButtonType result, InputType type, void* context) { | static void widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| 
 | 
 | ||||||
| static void popup_callback(void* context) { | static void popup_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
|     event.type = iButtonEvent::Type::EventTypeBack; |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| static void emulate_callback(void* context, bool emulated) { | static void emulate_callback(void* context, bool emulated) { | ||||||
|  |     furi_assert(context); | ||||||
|     if(emulated) { |     if(emulated) { | ||||||
|         iButtonApp* app = static_cast<iButtonApp*>(context); |         iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|         iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerEmulated}; |         iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerEmulated}; | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| 
 | 
 | ||||||
| static void widget_callback(GuiButtonType result, InputType type, void* context) { | static void widget_callback(GuiButtonType result, InputType type, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| static void read_callback(void* context) { | static void read_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerRead}; |     iButtonEvent event = {.type = iButtonEvent::Type::EventTypeWorkerRead}; | ||||||
|     app->get_view_manager()->send_event(&event); |     app->get_view_manager()->send_event(&event); | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <one_wire/maxim_crc.h> | #include <one_wire/maxim_crc.h> | ||||||
| 
 | 
 | ||||||
| static void dialog_ex_callback(DialogExResult result, void* context) { | static void dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <one_wire/maxim_crc.h> | #include <one_wire/maxim_crc.h> | ||||||
| 
 | 
 | ||||||
| static void dialog_ex_callback(DialogExResult result, void* context) { | static void dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| static void dialog_ex_callback(DialogExResult result, void* context) { | static void dialog_ex_callback(DialogExResult result, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,11 +4,12 @@ | |||||||
| typedef enum { | typedef enum { | ||||||
|     SubmenuIndexWrite, |     SubmenuIndexWrite, | ||||||
|     SubmenuIndexEmulate, |     SubmenuIndexEmulate, | ||||||
|     SubmenuIndexNameAndSave, |     SubmenuIndexSave, | ||||||
|     SubmenuIndexReadNewKey, |     SubmenuIndexReadNewKey, | ||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
| static void submenu_callback(void* context, uint32_t index) { | static void submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
| @ -25,7 +26,7 @@ void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) { | |||||||
|     if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) { |     if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) { | ||||||
|         submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app); |         submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app); | ||||||
|     } |     } | ||||||
|     submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, submenu_callback, app); |     submenu_add_item(submenu, "Save", SubmenuIndexSave, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); |     submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app); | ||||||
|     submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, submenu_callback, app); |     submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, submenu_callback, app); | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
| @ -45,7 +46,7 @@ bool iButtonSceneReadedKeyMenu::on_event(iButtonApp* app, iButtonEvent* event) { | |||||||
|         case SubmenuIndexEmulate: |         case SubmenuIndexEmulate: | ||||||
|             app->switch_to_next_scene(iButtonApp::Scene::SceneEmulate); |             app->switch_to_next_scene(iButtonApp::Scene::SceneEmulate); | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexNameAndSave: |         case SubmenuIndexSave: | ||||||
|             app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName); |             app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName); | ||||||
|             break; |             break; | ||||||
|         case SubmenuIndexReadNewKey: |         case SubmenuIndexReadNewKey: | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <lib/toolbox/random_name.h> | #include <lib/toolbox/random_name.h> | ||||||
| 
 | 
 | ||||||
| static void text_input_callback(void* context) { | static void text_input_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
| @ -34,7 +35,7 @@ void iButtonSceneSaveName::on_enter(iButtonApp* app) { | |||||||
|         key_name_empty); |         key_name_empty); | ||||||
| 
 | 
 | ||||||
|     ValidatorIsFile* validator_is_file = |     ValidatorIsFile* validator_is_file = | ||||||
|         validator_is_file_alloc_init(app->app_folder, app->app_extension); |         validator_is_file_alloc_init(app->app_folder, app->app_extension, key_name); | ||||||
|     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); |     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput); |     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput); | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| 
 | 
 | ||||||
| static void popup_callback(void* context) { | static void popup_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
|     event.type = iButtonEvent::Type::EventTypeBack; |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ typedef enum { | |||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
| static void submenu_callback(void* context, uint32_t index) { | static void submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ typedef enum { | |||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
| static void submenu_callback(void* context, uint32_t index) { | static void submenu_callback(void* context, uint32_t index) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| 
 | 
 | ||||||
| static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) { | static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
|     event.type = iButtonEvent::Type::EventTypeWorkerWrite; |     event.type = iButtonEvent::Type::EventTypeWorkerWrite; | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ | |||||||
| #include "../ibutton_app.h" | #include "../ibutton_app.h" | ||||||
| 
 | 
 | ||||||
| static void popup_callback(void* context) { | static void popup_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|     iButtonApp* app = static_cast<iButtonApp*>(context); |     iButtonApp* app = static_cast<iButtonApp*>(context); | ||||||
|     iButtonEvent event; |     iButtonEvent event; | ||||||
|     event.type = iButtonEvent::Type::EventTypeBack; |     event.type = iButtonEvent::Type::EventTypeBack; | ||||||
|  | |||||||
| @ -21,8 +21,8 @@ void InfraredAppSceneEditRename::on_enter(InfraredApp* app) { | |||||||
|         enter_name_length = InfraredAppRemoteManager::max_remote_name_length; |         enter_name_length = InfraredAppRemoteManager::max_remote_name_length; | ||||||
|         text_input_set_header_text(text_input, "Name the remote"); |         text_input_set_header_text(text_input, "Name the remote"); | ||||||
| 
 | 
 | ||||||
|         ValidatorIsFile* validator_is_file = |         ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( | ||||||
|             validator_is_file_alloc_init(app->infrared_directory, app->infrared_extension); |             app->infrared_directory, app->infrared_extension, remote_name.c_str()); | ||||||
|         text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); |         text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ void InfraredAppSceneUniversal::on_enter(InfraredApp* app) { | |||||||
|     InfraredAppViewManager* view_manager = app->get_view_manager(); |     InfraredAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     Submenu* submenu = view_manager->get_submenu(); |     Submenu* submenu = view_manager->get_submenu(); | ||||||
| 
 | 
 | ||||||
|     submenu_add_item(submenu, "TV's", SubmenuIndexUniversalTV, submenu_callback, app); |     submenu_add_item(submenu, "TVs", SubmenuIndexUniversalTV, submenu_callback, app); | ||||||
|     submenu_set_selected_item(submenu, submenu_item_selected); |     submenu_set_selected_item(submenu, submenu_item_selected); | ||||||
|     submenu_item_selected = 0; |     submenu_item_selected = 0; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     SubmenuWrite, |     SubmenuWrite, | ||||||
|     SubmenuNameAndSave, |     SubmenuSave, | ||||||
|     SubmenuEmulate, |     SubmenuEmulate, | ||||||
| } SubmenuIndex; | } SubmenuIndex; | ||||||
| 
 | 
 | ||||||
| @ -10,7 +10,7 @@ void LfRfidAppSceneReadedMenu::on_enter(LfRfidApp* app, bool need_restore) { | |||||||
|     auto submenu = app->view_controller.get<SubmenuVM>(); |     auto submenu = app->view_controller.get<SubmenuVM>(); | ||||||
| 
 | 
 | ||||||
|     submenu->add_item("Write", SubmenuWrite, submenu_callback, app); |     submenu->add_item("Write", SubmenuWrite, submenu_callback, app); | ||||||
|     submenu->add_item("Name and Save", SubmenuNameAndSave, submenu_callback, app); |     submenu->add_item("Save", SubmenuSave, submenu_callback, app); | ||||||
|     submenu->add_item("Emulate", SubmenuEmulate, submenu_callback, app); |     submenu->add_item("Emulate", SubmenuEmulate, submenu_callback, app); | ||||||
| 
 | 
 | ||||||
|     if(need_restore) { |     if(need_restore) { | ||||||
| @ -29,7 +29,7 @@ bool LfRfidAppSceneReadedMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event) | |||||||
|         case SubmenuWrite: |         case SubmenuWrite: | ||||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); |             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); | ||||||
|             break; |             break; | ||||||
|         case SubmenuNameAndSave: |         case SubmenuSave: | ||||||
|             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); |             app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); | ||||||
|             break; |             break; | ||||||
|         case SubmenuEmulate: |         case SubmenuEmulate: | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) { | |||||||
|         key_name_empty); |         key_name_empty); | ||||||
| 
 | 
 | ||||||
|     ValidatorIsFile* validator_is_file = |     ValidatorIsFile* validator_is_file = | ||||||
|         validator_is_file_alloc_init(app->app_folder, app->app_extension); |         validator_is_file_alloc_init(app->app_folder, app->app_extension, key_name); | ||||||
|     text_input->set_validator(validator_is_file_callback, validator_is_file); |     text_input->set_validator(validator_is_file_callback, validator_is_file); | ||||||
| 
 | 
 | ||||||
|     app->view_controller.switch_to<TextInputVM>(); |     app->view_controller.switch_to<TextInputVM>(); | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ void nfc_scene_save_name_on_enter(void* context) { | |||||||
|         dev_name_empty); |         dev_name_empty); | ||||||
| 
 | 
 | ||||||
|     ValidatorIsFile* validator_is_file = |     ValidatorIsFile* validator_is_file = | ||||||
|         validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION); |         validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION, nfc->dev->dev_name); | ||||||
|     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); |     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); |     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); | ||||||
|  | |||||||
| @ -20,6 +20,14 @@ void power_cli_reboot2dfu(Cli* cli, string_t args) { | |||||||
|     power_reboot(PowerBootModeDfu); |     power_reboot(PowerBootModeDfu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void power_cli_info_callback(const char* key, const char* value, bool last, void* context) { | ||||||
|  |     printf("%-24s: %s\r\n", key, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_cli_info(Cli* cli, string_t args) { | ||||||
|  |     furi_hal_power_info_get(power_cli_info_callback, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void power_cli_debug(Cli* cli, string_t args) { | void power_cli_debug(Cli* cli, string_t args) { | ||||||
|     furi_hal_power_dump_state(); |     furi_hal_power_dump_state(); | ||||||
| } | } | ||||||
| @ -52,6 +60,7 @@ static void power_cli_command_print_usage() { | |||||||
|     printf("\toff\t - shutdown power\r\n"); |     printf("\toff\t - shutdown power\r\n"); | ||||||
|     printf("\treboot\t - reboot\r\n"); |     printf("\treboot\t - reboot\r\n"); | ||||||
|     printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); |     printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); | ||||||
|  |     printf("\tinfo\t - show power info\r\n"); | ||||||
|     printf("\tdebug\t - show debug information\r\n"); |     printf("\tdebug\t - show debug information\r\n"); | ||||||
|     printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); |     printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); | ||||||
|     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { |     if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { | ||||||
| @ -84,6 +93,11 @@ void power_cli(Cli* cli, string_t args, void* context) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(string_cmp_str(cmd, "info") == 0) { | ||||||
|  |             power_cli_info(cli, args); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         if(string_cmp_str(cmd, "debug") == 0) { |         if(string_cmp_str(cmd, "debug") == 0) { | ||||||
|             power_cli_debug(cli, args); |             power_cli_debug(cli, args); | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ | |||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <semphr.h> | #include <semphr.h> | ||||||
| 
 | 
 | ||||||
|  | #define TAG "RpcCli" | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     Cli* cli; |     Cli* cli; | ||||||
|     bool session_close_request; |     bool session_close_request; | ||||||
| @ -38,6 +40,9 @@ static void rpc_session_terminated_callback(void* context) { | |||||||
| void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { | void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { | ||||||
|     Rpc* rpc = context; |     Rpc* rpc = context; | ||||||
| 
 | 
 | ||||||
|  |     uint32_t mem_before = memmgr_get_free_heap(); | ||||||
|  |     FURI_LOG_D(TAG, "Free memory %d", mem_before); | ||||||
|  | 
 | ||||||
|     furi_hal_usb_lock(); |     furi_hal_usb_lock(); | ||||||
|     RpcSession* rpc_session = rpc_session_open(rpc); |     RpcSession* rpc_session = rpc_session_open(rpc); | ||||||
|     if(rpc_session == NULL) { |     if(rpc_session == NULL) { | ||||||
|  | |||||||
| @ -346,8 +346,19 @@ void rpc_system_gui_free(void* context) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(rpc_gui->is_streaming) { |     if(rpc_gui->is_streaming) { | ||||||
|  |         rpc_gui->is_streaming = false; | ||||||
|  |         // Remove GUI framebuffer callback
 | ||||||
|         gui_remove_framebuffer_callback( |         gui_remove_framebuffer_callback( | ||||||
|             rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context); |             rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context); | ||||||
|  |         // Stop and release worker thread
 | ||||||
|  |         osThreadFlagsSet( | ||||||
|  |             furi_thread_get_thread_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagExit); | ||||||
|  |         furi_thread_join(rpc_gui->transmit_thread); | ||||||
|  |         furi_thread_free(rpc_gui->transmit_thread); | ||||||
|  |         // Release frame
 | ||||||
|  |         pb_release(&PB_Main_msg, rpc_gui->transmit_frame); | ||||||
|  |         free(rpc_gui->transmit_frame); | ||||||
|  |         rpc_gui->transmit_frame = NULL; | ||||||
|     } |     } | ||||||
|     furi_record_close("gui"); |     furi_record_close("gui"); | ||||||
|     free(rpc_gui); |     free(rpc_gui); | ||||||
|  | |||||||
| @ -6,6 +6,11 @@ | |||||||
| 
 | 
 | ||||||
| #include "rpc_i.h" | #include "rpc_i.h" | ||||||
| 
 | 
 | ||||||
|  | typedef struct { | ||||||
|  |     RpcSession* session; | ||||||
|  |     PB_Main* response; | ||||||
|  | } RpcSystemContext; | ||||||
|  | 
 | ||||||
| static void rpc_system_system_ping_process(const PB_Main* request, void* context) { | static void rpc_system_system_ping_process(const PB_Main* request, void* context) { | ||||||
|     furi_assert(request); |     furi_assert(request); | ||||||
|     furi_assert(request->which_content == PB_Main_system_ping_request_tag); |     furi_assert(request->which_content == PB_Main_system_ping_request_tag); | ||||||
| @ -55,11 +60,6 @@ static void rpc_system_system_reboot_process(const PB_Main* request, void* conte | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| typedef struct { |  | ||||||
|     RpcSession* session; |  | ||||||
|     PB_Main* response; |  | ||||||
| } RpcSystemSystemDeviceInfoContext; |  | ||||||
| 
 |  | ||||||
| static void rpc_system_system_device_info_callback( | static void rpc_system_system_device_info_callback( | ||||||
|     const char* key, |     const char* key, | ||||||
|     const char* value, |     const char* value, | ||||||
| @ -67,7 +67,7 @@ static void rpc_system_system_device_info_callback( | |||||||
|     void* context) { |     void* context) { | ||||||
|     furi_assert(key); |     furi_assert(key); | ||||||
|     furi_assert(value); |     furi_assert(value); | ||||||
|     RpcSystemSystemDeviceInfoContext* ctx = context; |     RpcSystemContext* ctx = context; | ||||||
| 
 | 
 | ||||||
|     char* str_key = strdup(key); |     char* str_key = strdup(key); | ||||||
|     char* str_value = strdup(value); |     char* str_value = strdup(value); | ||||||
| @ -91,7 +91,7 @@ static void rpc_system_system_device_info_process(const PB_Main* request, void* | |||||||
|     response->which_content = PB_Main_system_device_info_response_tag; |     response->which_content = PB_Main_system_device_info_response_tag; | ||||||
|     response->command_status = PB_CommandStatus_OK; |     response->command_status = PB_CommandStatus_OK; | ||||||
| 
 | 
 | ||||||
|     RpcSystemSystemDeviceInfoContext device_info_context = { |     RpcSystemContext device_info_context = { | ||||||
|         .session = session, |         .session = session, | ||||||
|         .response = response, |         .response = response, | ||||||
|     }; |     }; | ||||||
| @ -202,6 +202,46 @@ static void rpc_system_system_protobuf_version_process(const PB_Main* request, v | |||||||
|     free(response); |     free(response); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void rpc_system_system_power_info_callback( | ||||||
|  |     const char* key, | ||||||
|  |     const char* value, | ||||||
|  |     bool last, | ||||||
|  |     void* context) { | ||||||
|  |     furi_assert(key); | ||||||
|  |     furi_assert(value); | ||||||
|  |     RpcSystemContext* ctx = context; | ||||||
|  | 
 | ||||||
|  |     char* str_key = strdup(key); | ||||||
|  |     char* str_value = strdup(value); | ||||||
|  | 
 | ||||||
|  |     ctx->response->has_next = !last; | ||||||
|  |     ctx->response->content.system_device_info_response.key = str_key; | ||||||
|  |     ctx->response->content.system_device_info_response.value = str_value; | ||||||
|  | 
 | ||||||
|  |     rpc_send_and_release(ctx->session, ctx->response); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void rpc_system_system_get_power_info_process(const PB_Main* request, void* context) { | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(request->which_content == PB_Main_system_power_info_request_tag); | ||||||
|  | 
 | ||||||
|  |     RpcSession* session = (RpcSession*)context; | ||||||
|  |     furi_assert(session); | ||||||
|  | 
 | ||||||
|  |     PB_Main* response = malloc(sizeof(PB_Main)); | ||||||
|  |     response->command_id = request->command_id; | ||||||
|  |     response->which_content = PB_Main_system_power_info_response_tag; | ||||||
|  |     response->command_status = PB_CommandStatus_OK; | ||||||
|  | 
 | ||||||
|  |     RpcSystemContext power_info_context = { | ||||||
|  |         .session = session, | ||||||
|  |         .response = response, | ||||||
|  |     }; | ||||||
|  |     furi_hal_power_info_get(rpc_system_system_power_info_callback, &power_info_context); | ||||||
|  | 
 | ||||||
|  |     free(response); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void* rpc_system_system_alloc(RpcSession* session) { | void* rpc_system_system_alloc(RpcSession* session) { | ||||||
|     RpcHandler rpc_handler = { |     RpcHandler rpc_handler = { | ||||||
|         .message_handler = NULL, |         .message_handler = NULL, | ||||||
| @ -233,5 +273,8 @@ void* rpc_system_system_alloc(RpcSession* session) { | |||||||
|     rpc_handler.message_handler = rpc_system_system_protobuf_version_process; |     rpc_handler.message_handler = rpc_system_system_protobuf_version_process; | ||||||
|     rpc_add_handler(session, PB_Main_system_protobuf_version_request_tag, &rpc_handler); |     rpc_add_handler(session, PB_Main_system_protobuf_version_request_tag, &rpc_handler); | ||||||
| 
 | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_system_system_get_power_info_process; | ||||||
|  |     rpc_add_handler(session, PB_Main_system_power_info_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|     return NULL; |     return NULL; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <furi.h> | #include <stdint.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
|  | |||||||
| @ -75,21 +75,21 @@ struct File { | |||||||
|  *      @return end of file flag |  *      @return end of file flag | ||||||
|  */ |  */ | ||||||
| typedef struct { | typedef struct { | ||||||
|     bool (*open)( |     bool (*const open)( | ||||||
|         void* context, |         void* context, | ||||||
|         File* file, |         File* file, | ||||||
|         const char* path, |         const char* path, | ||||||
|         FS_AccessMode access_mode, |         FS_AccessMode access_mode, | ||||||
|         FS_OpenMode open_mode); |         FS_OpenMode open_mode); | ||||||
|     bool (*close)(void* context, File* file); |     bool (*const close)(void* context, File* file); | ||||||
|     uint16_t (*read)(void* context, File* file, void* buff, uint16_t bytes_to_read); |     uint16_t (*read)(void* context, File* file, void* buff, uint16_t bytes_to_read); | ||||||
|     uint16_t (*write)(void* context, File* file, const void* buff, uint16_t bytes_to_write); |     uint16_t (*write)(void* context, File* file, const void* buff, uint16_t bytes_to_write); | ||||||
|     bool (*seek)(void* context, File* file, uint32_t offset, bool from_start); |     bool (*const seek)(void* context, File* file, uint32_t offset, bool from_start); | ||||||
|     uint64_t (*tell)(void* context, File* file); |     uint64_t (*tell)(void* context, File* file); | ||||||
|     bool (*truncate)(void* context, File* file); |     bool (*const truncate)(void* context, File* file); | ||||||
|     uint64_t (*size)(void* context, File* file); |     uint64_t (*size)(void* context, File* file); | ||||||
|     bool (*sync)(void* context, File* file); |     bool (*const sync)(void* context, File* file); | ||||||
|     bool (*eof)(void* context, File* file); |     bool (*const eof)(void* context, File* file); | ||||||
| } FS_File_Api; | } FS_File_Api; | ||||||
| 
 | 
 | ||||||
| /** Dir api structure
 | /** Dir api structure
 | ||||||
| @ -118,10 +118,15 @@ typedef struct { | |||||||
|  *      @return success flag |  *      @return success flag | ||||||
|  */ |  */ | ||||||
| typedef struct { | typedef struct { | ||||||
|     bool (*open)(void* context, File* file, const char* path); |     bool (*const open)(void* context, File* file, const char* path); | ||||||
|     bool (*close)(void* context, File* file); |     bool (*const close)(void* context, File* file); | ||||||
|     bool (*read)(void* context, File* file, FileInfo* fileinfo, char* name, uint16_t name_length); |     bool (*const read)( | ||||||
|     bool (*rewind)(void* context, File* file); |         void* context, | ||||||
|  |         File* file, | ||||||
|  |         FileInfo* fileinfo, | ||||||
|  |         char* name, | ||||||
|  |         uint16_t name_length); | ||||||
|  |     bool (*const rewind)(void* context, File* file); | ||||||
| } FS_Dir_Api; | } FS_Dir_Api; | ||||||
| 
 | 
 | ||||||
| /** Common api structure
 | /** Common api structure
 | ||||||
| @ -141,12 +146,6 @@ typedef struct { | |||||||
|  *      @param path path to file/directory |  *      @param path path to file/directory | ||||||
|  *      @return FS_Error error info |  *      @return FS_Error error info | ||||||
|  *  |  *  | ||||||
|  *  @var FS_Common_Api::rename |  | ||||||
|  *      @brief Rename file/directory, |  | ||||||
|  *          file/directory must not be opened |  | ||||||
|  *      @param path path to file/directory |  | ||||||
|  *      @return FS_Error error info |  | ||||||
|  *  |  | ||||||
|  *  @var FS_Common_Api::mkdir |  *  @var FS_Common_Api::mkdir | ||||||
|  *      @brief Create new directory |  *      @brief Create new directory | ||||||
|  *      @param path path to new directory |  *      @param path path to new directory | ||||||
| @ -160,31 +159,21 @@ typedef struct { | |||||||
|  *      @return FS_Error error info |  *      @return FS_Error error info | ||||||
|  */ |  */ | ||||||
| typedef struct { | typedef struct { | ||||||
|     FS_Error (*stat)(void* context, const char* path, FileInfo* fileinfo); |     FS_Error (*const stat)(void* context, const char* path, FileInfo* fileinfo); | ||||||
|     FS_Error (*remove)(void* context, const char* path); |     FS_Error (*const remove)(void* context, const char* path); | ||||||
|     FS_Error (*rename)(void* context, const char* old_path, const char* new_path); |     FS_Error (*const mkdir)(void* context, const char* path); | ||||||
|     FS_Error (*mkdir)(void* context, const char* path); |     FS_Error (*const fs_info)( | ||||||
|     FS_Error ( |         void* context, | ||||||
|         *fs_info)(void* context, const char* fs_path, uint64_t* total_space, uint64_t* free_space); |         const char* fs_path, | ||||||
|  |         uint64_t* total_space, | ||||||
|  |         uint64_t* free_space); | ||||||
| } FS_Common_Api; | } FS_Common_Api; | ||||||
| 
 | 
 | ||||||
| /** Errors api structure
 |  | ||||||
|  *  @var FS_Error_Api::get_desc |  | ||||||
|  *      @brief Get error description text |  | ||||||
|  *      @param error_id FS_Error error id (for fire/dir functions result can be obtained from File.error_id) |  | ||||||
|  *      @return pointer to description text |  | ||||||
|  */ |  | ||||||
| typedef struct { |  | ||||||
|     const char* (*get_desc)(void* context, FS_Error error_id); |  | ||||||
| } FS_Error_Api; |  | ||||||
| 
 |  | ||||||
| /** Full filesystem api structure */ | /** Full filesystem api structure */ | ||||||
| typedef struct { | typedef struct { | ||||||
|     FS_File_Api file; |     const FS_File_Api file; | ||||||
|     FS_Dir_Api dir; |     const FS_Dir_Api dir; | ||||||
|     FS_Common_Api common; |     const FS_Common_Api common; | ||||||
|     FS_Error_Api error; |  | ||||||
|     void* context; |  | ||||||
| } FS_Api; | } FS_Api; | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
| @ -11,6 +11,8 @@ | |||||||
| #define ICON_SD_MOUNTED &I_SDcardMounted_11x8 | #define ICON_SD_MOUNTED &I_SDcardMounted_11x8 | ||||||
| #define ICON_SD_ERROR &I_SDcardFail_11x8 | #define ICON_SD_ERROR &I_SDcardFail_11x8 | ||||||
| 
 | 
 | ||||||
|  | #define TAG "Storage" | ||||||
|  | 
 | ||||||
| static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) { | static void storage_app_sd_icon_draw_callback(Canvas* canvas, void* context) { | ||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| @ -63,15 +65,14 @@ void storage_tick(Storage* app) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(app->storage[ST_EXT].status != app->prev_ext_storage_status) { |  | ||||||
|         app->prev_ext_storage_status = app->storage[ST_EXT].status; |  | ||||||
|         furi_pubsub_publish(app->pubsub, &app->storage[ST_EXT].status); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // storage not enabled but was enabled (sd card unmount)
 |     // storage not enabled but was enabled (sd card unmount)
 | ||||||
|     if(app->storage[ST_EXT].status == StorageStatusNotReady && app->sd_gui.enabled == true) { |     if(app->storage[ST_EXT].status == StorageStatusNotReady && app->sd_gui.enabled == true) { | ||||||
|         app->sd_gui.enabled = false; |         app->sd_gui.enabled = false; | ||||||
|         view_port_enabled_set(app->sd_gui.view_port, false); |         view_port_enabled_set(app->sd_gui.view_port, false); | ||||||
|  | 
 | ||||||
|  |         FURI_LOG_I(TAG, "SD card unmount"); | ||||||
|  |         StorageEvent event = {.type = StorageEventTypeCardUnmount}; | ||||||
|  |         furi_pubsub_publish(app->pubsub, &event); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // storage enabled (or in error state) but was not enabled (sd card mount)
 |     // storage enabled (or in error state) but was not enabled (sd card mount)
 | ||||||
| @ -83,6 +84,16 @@ void storage_tick(Storage* app) { | |||||||
|        app->sd_gui.enabled == false) { |        app->sd_gui.enabled == false) { | ||||||
|         app->sd_gui.enabled = true; |         app->sd_gui.enabled = true; | ||||||
|         view_port_enabled_set(app->sd_gui.view_port, true); |         view_port_enabled_set(app->sd_gui.view_port, true); | ||||||
|  | 
 | ||||||
|  |         if(app->storage[ST_EXT].status == StorageStatusOK) { | ||||||
|  |             FURI_LOG_I(TAG, "SD card mount"); | ||||||
|  |             StorageEvent event = {.type = StorageEventTypeCardMount}; | ||||||
|  |             furi_pubsub_publish(app->pubsub, &event); | ||||||
|  |         } else { | ||||||
|  |             FURI_LOG_I(TAG, "SD card mount error"); | ||||||
|  |             StorageEvent event = {.type = StorageEventTypeCardMountError}; | ||||||
|  |             furi_pubsub_publish(app->pubsub, &event); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <furi.h> | #include <stdint.h> | ||||||
|  | #include <m-string.h> | ||||||
| #include "filesystem_api_defines.h" | #include "filesystem_api_defines.h" | ||||||
| #include "storage_sd_api.h" | #include "storage_sd_api.h" | ||||||
| 
 | 
 | ||||||
| @ -18,6 +19,24 @@ File* storage_file_alloc(Storage* storage); | |||||||
|  */ |  */ | ||||||
| void storage_file_free(File* file); | void storage_file_free(File* file); | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     StorageEventTypeCardMount, | ||||||
|  |     StorageEventTypeCardUnmount, | ||||||
|  |     StorageEventTypeCardMountError, | ||||||
|  |     StorageEventTypeFileClose, | ||||||
|  |     StorageEventTypeDirClose, | ||||||
|  | } StorageEventType; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     StorageEventType type; | ||||||
|  | } StorageEvent; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Get storage pubsub. | ||||||
|  |  * Storage will send StorageEvent messages. | ||||||
|  |  * @param storage  | ||||||
|  |  * @return FuriPubSub*  | ||||||
|  |  */ | ||||||
| FuriPubSub* storage_get_pubsub(Storage* storage); | FuriPubSub* storage_get_pubsub(Storage* storage); | ||||||
| 
 | 
 | ||||||
| /******************* File Functions *******************/ | /******************* File Functions *******************/ | ||||||
| @ -47,6 +66,12 @@ bool storage_file_close(File* file); | |||||||
|  */ |  */ | ||||||
| bool storage_file_is_open(File* file); | bool storage_file_is_open(File* file); | ||||||
| 
 | 
 | ||||||
|  | /** Tells if the file is a directory
 | ||||||
|  |  * @param file pointer to a file object | ||||||
|  |  * @return bool true if file is a directory | ||||||
|  |  */ | ||||||
|  | bool storage_file_is_dir(File* file); | ||||||
|  | 
 | ||||||
| /** Reads bytes from a file into a buffer
 | /** Reads bytes from a file into a buffer
 | ||||||
|  * @param file pointer to file object. |  * @param file pointer to file object. | ||||||
|  * @param buff pointer to a buffer, for reading |  * @param buff pointer to a buffer, for reading | ||||||
| @ -272,13 +297,15 @@ bool storage_simply_mkdir(Storage* storage, const char* path); | |||||||
|  * @param filename  |  * @param filename  | ||||||
|  * @param fileextension  |  * @param fileextension  | ||||||
|  * @param nextfilename return name |  * @param nextfilename return name | ||||||
|  |  * @param max_len  max len name | ||||||
|  */ |  */ | ||||||
| void storage_get_next_filename( | void storage_get_next_filename( | ||||||
|     Storage* storage, |     Storage* storage, | ||||||
|     const char* dirname, |     const char* dirname, | ||||||
|     const char* filename, |     const char* filename, | ||||||
|     const char* fileextension, |     const char* fileextension, | ||||||
|     string_t nextfilename); |     string_t nextfilename, | ||||||
|  |     uint8_t max_len); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| #include "storage.h" | #include "storage.h" | ||||||
| #include "storage_i.h" | #include "storage_i.h" | ||||||
| #include "storage_message.h" | #include "storage_message.h" | ||||||
|  | #include <toolbox/stream/file_stream.h> | ||||||
| 
 | 
 | ||||||
| #define MAX_NAME_LENGTH 256 | #define MAX_NAME_LENGTH 256 | ||||||
| 
 | 
 | ||||||
| @ -46,12 +47,16 @@ | |||||||
| #define S_RETURN_ERROR (return_data.error_value); | #define S_RETURN_ERROR (return_data.error_value); | ||||||
| #define S_RETURN_CSTRING (return_data.cstring_value); | #define S_RETURN_CSTRING (return_data.cstring_value); | ||||||
| 
 | 
 | ||||||
| #define FILE_OPENED 1 | #define FILE_OPENED_FILE 1 | ||||||
|  | #define FILE_OPENED_DIR 2 | ||||||
| #define FILE_CLOSED 0 | #define FILE_CLOSED 0 | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     StorageEventFlagFileClose = (1 << 0), | ||||||
|  | } StorageEventFlag; | ||||||
| /****************** FILE ******************/ | /****************** FILE ******************/ | ||||||
| 
 | 
 | ||||||
| bool storage_file_open( | static bool storage_file_open_internal( | ||||||
|     File* file, |     File* file, | ||||||
|     const char* path, |     const char* path, | ||||||
|     FS_AccessMode access_mode, |     FS_AccessMode access_mode, | ||||||
| @ -67,7 +72,7 @@ bool storage_file_open( | |||||||
|             .open_mode = open_mode, |             .open_mode = open_mode, | ||||||
|         }}; |         }}; | ||||||
| 
 | 
 | ||||||
|     file->file_id = FILE_OPENED; |     file->file_id = FILE_OPENED_FILE; | ||||||
| 
 | 
 | ||||||
|     S_API_MESSAGE(StorageCommandFileOpen); |     S_API_MESSAGE(StorageCommandFileOpen); | ||||||
|     S_API_EPILOGUE; |     S_API_EPILOGUE; | ||||||
| @ -75,6 +80,42 @@ bool storage_file_open( | |||||||
|     return S_RETURN_BOOL; |     return S_RETURN_BOOL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void storage_file_close_callback(const void* message, void* context) { | ||||||
|  |     const StorageEvent* storage_event = message; | ||||||
|  | 
 | ||||||
|  |     if(storage_event->type == StorageEventTypeFileClose || | ||||||
|  |        storage_event->type == StorageEventTypeDirClose) { | ||||||
|  |         furi_assert(context); | ||||||
|  |         osEventFlagsId_t event = context; | ||||||
|  |         osEventFlagsSet(event, StorageEventFlagFileClose); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool storage_file_open( | ||||||
|  |     File* file, | ||||||
|  |     const char* path, | ||||||
|  |     FS_AccessMode access_mode, | ||||||
|  |     FS_OpenMode open_mode) { | ||||||
|  |     bool result; | ||||||
|  |     osEventFlagsId_t event = osEventFlagsNew(NULL); | ||||||
|  |     FuriPubSubSubscription* subscription = furi_pubsub_subscribe( | ||||||
|  |         storage_get_pubsub(file->storage), storage_file_close_callback, event); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         result = storage_file_open_internal(file, path, access_mode, open_mode); | ||||||
|  | 
 | ||||||
|  |         if(!result && file->error_id == FSE_ALREADY_OPEN) { | ||||||
|  |             osEventFlagsWait(event, StorageEventFlagFileClose, osFlagsWaitAny, osWaitForever); | ||||||
|  |         } else { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } while(true); | ||||||
|  | 
 | ||||||
|  |     furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); | ||||||
|  |     osEventFlagsDelete(event); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool storage_file_close(File* file) { | bool storage_file_close(File* file) { | ||||||
|     S_FILE_API_PROLOGUE; |     S_FILE_API_PROLOGUE; | ||||||
|     S_API_PROLOGUE; |     S_API_PROLOGUE; | ||||||
| @ -183,7 +224,7 @@ bool storage_file_eof(File* file) { | |||||||
| 
 | 
 | ||||||
| /****************** DIR ******************/ | /****************** DIR ******************/ | ||||||
| 
 | 
 | ||||||
| bool storage_dir_open(File* file, const char* path) { | static bool storage_dir_open_internal(File* file, const char* path) { | ||||||
|     S_FILE_API_PROLOGUE; |     S_FILE_API_PROLOGUE; | ||||||
|     S_API_PROLOGUE; |     S_API_PROLOGUE; | ||||||
| 
 | 
 | ||||||
| @ -193,13 +234,34 @@ bool storage_dir_open(File* file, const char* path) { | |||||||
|             .path = path, |             .path = path, | ||||||
|         }}; |         }}; | ||||||
| 
 | 
 | ||||||
|     file->file_id = FILE_OPENED; |     file->file_id = FILE_OPENED_DIR; | ||||||
| 
 | 
 | ||||||
|     S_API_MESSAGE(StorageCommandDirOpen); |     S_API_MESSAGE(StorageCommandDirOpen); | ||||||
|     S_API_EPILOGUE; |     S_API_EPILOGUE; | ||||||
|     return S_RETURN_BOOL; |     return S_RETURN_BOOL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool storage_dir_open(File* file, const char* path) { | ||||||
|  |     bool result; | ||||||
|  |     osEventFlagsId_t event = osEventFlagsNew(NULL); | ||||||
|  |     FuriPubSubSubscription* subscription = furi_pubsub_subscribe( | ||||||
|  |         storage_get_pubsub(file->storage), storage_file_close_callback, event); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         result = storage_dir_open_internal(file, path); | ||||||
|  | 
 | ||||||
|  |         if(!result && file->error_id == FSE_ALREADY_OPEN) { | ||||||
|  |             osEventFlagsWait(event, StorageEventFlagFileClose, osFlagsWaitAny, osWaitForever); | ||||||
|  |         } else { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } while(true); | ||||||
|  | 
 | ||||||
|  |     furi_pubsub_unsubscribe(storage_get_pubsub(file->storage), subscription); | ||||||
|  |     osEventFlagsDelete(event); | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool storage_dir_close(File* file) { | bool storage_dir_close(File* file) { | ||||||
|     S_FILE_API_PROLOGUE; |     S_FILE_API_PROLOGUE; | ||||||
|     S_API_PROLOGUE; |     S_API_PROLOGUE; | ||||||
| @ -259,31 +321,44 @@ FS_Error storage_common_remove(Storage* storage, const char* path) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { | FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { | ||||||
|     S_API_PROLOGUE; |     FS_Error error = storage_common_copy(storage, old_path, new_path); | ||||||
|  |     if(error == FSE_OK) { | ||||||
|  |         error = storage_common_remove(storage, old_path); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     SAData data = { |     return error; | ||||||
|         .cpaths = { |  | ||||||
|             .old = old_path, |  | ||||||
|             .new = new_path, |  | ||||||
|         }}; |  | ||||||
| 
 |  | ||||||
|     S_API_MESSAGE(StorageCommandCommonRename); |  | ||||||
|     S_API_EPILOGUE; |  | ||||||
|     return S_RETURN_ERROR; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) { | FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) { | ||||||
|     S_API_PROLOGUE; |     FS_Error error; | ||||||
| 
 | 
 | ||||||
|     SAData data = { |     FileInfo fileinfo; | ||||||
|         .cpaths = { |     error = storage_common_stat(storage, old_path, &fileinfo); | ||||||
|             .old = old_path, |  | ||||||
|             .new = new_path, |  | ||||||
|         }}; |  | ||||||
| 
 | 
 | ||||||
|     S_API_MESSAGE(StorageCommandCommonCopy); |     if(error == FSE_OK) { | ||||||
|     S_API_EPILOGUE; |         if(fileinfo.flags & FSF_DIRECTORY) { | ||||||
|     return S_RETURN_ERROR; |             error = storage_common_mkdir(storage, new_path); | ||||||
|  |         } else { | ||||||
|  |             Stream* stream_from = file_stream_alloc(storage); | ||||||
|  |             Stream* stream_to = file_stream_alloc(storage); | ||||||
|  | 
 | ||||||
|  |             do { | ||||||
|  |                 if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break; | ||||||
|  |                 if(!file_stream_open(stream_to, new_path, FSAM_WRITE, FSOM_CREATE_NEW)) break; | ||||||
|  |                 stream_copy_full(stream_from, stream_to); | ||||||
|  |             } while(false); | ||||||
|  | 
 | ||||||
|  |             error = file_stream_get_error(stream_from); | ||||||
|  |             if(error == FSE_OK) { | ||||||
|  |                 error = file_stream_get_error(stream_to); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             stream_free(stream_from); | ||||||
|  |             stream_free(stream_to); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return error; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FS_Error storage_common_mkdir(Storage* storage, const char* path) { | FS_Error storage_common_mkdir(Storage* storage, const char* path) { | ||||||
| @ -383,9 +458,17 @@ bool storage_file_is_open(File* file) { | |||||||
|     return (file->file_id != FILE_CLOSED); |     return (file->file_id != FILE_CLOSED); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool storage_file_is_dir(File* file) { | ||||||
|  |     return (file->file_id == FILE_OPENED_DIR); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void storage_file_free(File* file) { | void storage_file_free(File* file) { | ||||||
|     if(storage_file_is_open(file)) { |     if(storage_file_is_open(file)) { | ||||||
|         storage_file_close(file); |         if(storage_file_is_dir(file)) { | ||||||
|  |             storage_dir_close(file); | ||||||
|  |         } else { | ||||||
|  |             storage_file_close(file); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     free(file); |     free(file); | ||||||
| @ -473,7 +556,8 @@ void storage_get_next_filename( | |||||||
|     const char* dirname, |     const char* dirname, | ||||||
|     const char* filename, |     const char* filename, | ||||||
|     const char* fileextension, |     const char* fileextension, | ||||||
|     string_t nextfilename) { |     string_t nextfilename, | ||||||
|  |     uint8_t max_len) { | ||||||
|     string_t temp_str; |     string_t temp_str; | ||||||
|     uint16_t num = 0; |     uint16_t num = 0; | ||||||
| 
 | 
 | ||||||
| @ -483,8 +567,7 @@ void storage_get_next_filename( | |||||||
|         num++; |         num++; | ||||||
|         string_printf(temp_str, "%s/%s%d%s", dirname, filename, num, fileextension); |         string_printf(temp_str, "%s/%s%d%s", dirname, filename, num, fileextension); | ||||||
|     } |     } | ||||||
| 
 |     if(num && (max_len > strlen(filename))) { | ||||||
|     if(num) { |  | ||||||
|         string_printf(nextfilename, "%s%d", filename, num); |         string_printf(nextfilename, "%s%d", filename, num); | ||||||
|     } else { |     } else { | ||||||
|         string_printf(nextfilename, "%s", filename); |         string_printf(nextfilename, "%s", filename); | ||||||
|  | |||||||
| @ -103,25 +103,7 @@ bool storage_has_file(const File* file, StorageData* storage_data) { | |||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| StorageType storage_get_type_by_path(const char* path) { | bool storage_path_already_open(string_t path, StorageFileList_t array) { | ||||||
|     StorageType type = ST_ERROR; |  | ||||||
| 
 |  | ||||||
|     const char* ext_path = "/ext"; |  | ||||||
|     const char* int_path = "/int"; |  | ||||||
|     const char* any_path = "/any"; |  | ||||||
| 
 |  | ||||||
|     if(strlen(path) >= strlen(ext_path) && memcmp(path, ext_path, strlen(ext_path)) == 0) { |  | ||||||
|         type = ST_EXT; |  | ||||||
|     } else if(strlen(path) >= strlen(int_path) && memcmp(path, int_path, strlen(int_path)) == 0) { |  | ||||||
|         type = ST_INT; |  | ||||||
|     } else if(strlen(path) >= strlen(any_path) && memcmp(path, any_path, strlen(any_path)) == 0) { |  | ||||||
|         type = ST_ANY; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return type; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool storage_path_already_open(const char* path, StorageFileList_t array) { |  | ||||||
|     bool open = false; |     bool open = false; | ||||||
| 
 | 
 | ||||||
|     StorageFileList_it_t it; |     StorageFileList_it_t it; | ||||||
| @ -178,11 +160,7 @@ void* storage_get_storage_file_data(const File* file, StorageData* storage) { | |||||||
|     return founded_file->file_data; |     return founded_file->file_data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void storage_push_storage_file( | void storage_push_storage_file(File* file, string_t path, StorageType type, StorageData* storage) { | ||||||
|     File* file, |  | ||||||
|     const char* path, |  | ||||||
|     StorageType type, |  | ||||||
|     StorageData* storage) { |  | ||||||
|     StorageFile* storage_file = StorageFileList_push_new(storage->files); |     StorageFile* storage_file = StorageFileList_push_new(storage->files); | ||||||
|     furi_check(storage_file != NULL); |     furi_check(storage_file != NULL); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ | |||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include "filesystem_api_internal.h" | #include "filesystem_api_internal.h" | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <m-array.h> |  | ||||||
| #include <m-list.h> | #include <m-list.h> | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -54,7 +53,7 @@ LIST_DEF( | |||||||
|      CLEAR(API_2(storage_file_clear)))) |      CLEAR(API_2(storage_file_clear)))) | ||||||
| 
 | 
 | ||||||
| struct StorageData { | struct StorageData { | ||||||
|     FS_Api fs_api; |     const FS_Api* fs_api; | ||||||
|     StorageApi api; |     StorageApi api; | ||||||
|     void* data; |     void* data; | ||||||
|     osMutexId_t mutex; |     osMutexId_t mutex; | ||||||
| @ -63,17 +62,12 @@ struct StorageData { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool storage_has_file(const File* file, StorageData* storage_data); | bool storage_has_file(const File* file, StorageData* storage_data); | ||||||
| StorageType storage_get_type_by_path(const char* path); | bool storage_path_already_open(string_t path, StorageFileList_t files); | ||||||
| bool storage_path_already_open(const char* path, StorageFileList_t files); |  | ||||||
| 
 | 
 | ||||||
| void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage); | void storage_set_storage_file_data(const File* file, void* file_data, StorageData* storage); | ||||||
| void* storage_get_storage_file_data(const File* file, StorageData* storage); | void* storage_get_storage_file_data(const File* file, StorageData* storage); | ||||||
| 
 | 
 | ||||||
| void storage_push_storage_file( | void storage_push_storage_file(File* file, string_t path, StorageType type, StorageData* storage); | ||||||
|     File* file, |  | ||||||
|     const char* path, |  | ||||||
|     StorageType type, |  | ||||||
|     StorageData* storage); |  | ||||||
| bool storage_pop_storage_file(File* file, StorageData* storage); | bool storage_pop_storage_file(File* file, StorageData* storage); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
| @ -19,7 +19,6 @@ typedef struct { | |||||||
| struct Storage { | struct Storage { | ||||||
|     osMessageQueueId_t message_queue; |     osMessageQueueId_t message_queue; | ||||||
|     StorageData storage[STORAGE_COUNT]; |     StorageData storage[STORAGE_COUNT]; | ||||||
|     StorageStatus prev_ext_storage_status; |  | ||||||
|     StorageSDGui sd_gui; |     StorageSDGui sd_gui; | ||||||
|     FuriPubSub* pubsub; |     FuriPubSub* pubsub; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -47,11 +47,6 @@ typedef struct { | |||||||
|     FileInfo* fileinfo; |     FileInfo* fileinfo; | ||||||
| } SADataCStat; | } SADataCStat; | ||||||
| 
 | 
 | ||||||
| typedef struct { |  | ||||||
|     const char* old; |  | ||||||
|     const char* new; |  | ||||||
| } SADataCPaths; |  | ||||||
| 
 |  | ||||||
| typedef struct { | typedef struct { | ||||||
|     const char* fs_path; |     const char* fs_path; | ||||||
|     uint64_t* total_space; |     uint64_t* total_space; | ||||||
| @ -84,7 +79,6 @@ typedef union { | |||||||
|     SADataDRead dread; |     SADataDRead dread; | ||||||
| 
 | 
 | ||||||
|     SADataCStat cstat; |     SADataCStat cstat; | ||||||
|     SADataCPaths cpaths; |  | ||||||
|     SADataCFSInfo cfsinfo; |     SADataCFSInfo cfsinfo; | ||||||
| 
 | 
 | ||||||
|     SADataError error; |     SADataError error; | ||||||
| @ -120,8 +114,6 @@ typedef enum { | |||||||
|     StorageCommandDirRewind, |     StorageCommandDirRewind, | ||||||
|     StorageCommandCommonStat, |     StorageCommandCommonStat, | ||||||
|     StorageCommandCommonRemove, |     StorageCommandCommonRemove, | ||||||
|     StorageCommandCommonRename, |  | ||||||
|     StorageCommandCommonCopy, |  | ||||||
|     StorageCommandCommonMkDir, |     StorageCommandCommonMkDir, | ||||||
|     StorageCommandCommonFSInfo, |     StorageCommandCommonFSInfo, | ||||||
|     StorageCommandSDFormat, |     StorageCommandSDFormat, | ||||||
|  | |||||||
| @ -1,8 +1,11 @@ | |||||||
| #include "storage_processing.h" | #include "storage_processing.h" | ||||||
|  | #include <m-list.h> | ||||||
|  | #include <m-dict.h> | ||||||
|  | #include <m-string.h> | ||||||
| 
 | 
 | ||||||
| #define FS_CALL(_storage, _fn)   \ | #define FS_CALL(_storage, _fn)   \ | ||||||
|     storage_data_lock(_storage); \ |     storage_data_lock(_storage); \ | ||||||
|     ret = _storage->fs_api._fn;  \ |     ret = _storage->fs_api->_fn; \ | ||||||
|     storage_data_unlock(_storage); |     storage_data_unlock(_storage); | ||||||
| 
 | 
 | ||||||
| #define ST_CALL(_storage, _fn)   \ | #define ST_CALL(_storage, _fn)   \ | ||||||
| @ -11,18 +14,8 @@ | |||||||
|     storage_data_unlock(_storage); |     storage_data_unlock(_storage); | ||||||
| 
 | 
 | ||||||
| static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) { | static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) { | ||||||
|     StorageData* storage; |     furi_check(type == ST_EXT || type == ST_INT); | ||||||
| 
 |     StorageData* storage = &app->storage[type]; | ||||||
|     if(type == ST_ANY) { |  | ||||||
|         type = ST_INT; |  | ||||||
|         StorageData* ext_storage = &app->storage[ST_EXT]; |  | ||||||
| 
 |  | ||||||
|         if(storage_data_status(ext_storage) == StorageStatusOK) { |  | ||||||
|             type = ST_EXT; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     storage = &app->storage[type]; |  | ||||||
| 
 |  | ||||||
|     return storage; |     return storage; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -42,10 +35,55 @@ static StorageData* get_storage_by_file(File* file, StorageData* storages) { | |||||||
|     return storage_data; |     return storage_data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const char* remove_vfs(const char* path) { | static const char* remove_vfs(const char* path) { | ||||||
|     return path + MIN(4, strlen(path)); |     return path + MIN(4, strlen(path)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static const char* ext_path = "/ext"; | ||||||
|  | static const char* int_path = "/int"; | ||||||
|  | static const char* any_path = "/any"; | ||||||
|  | 
 | ||||||
|  | static StorageType storage_get_type_by_path(Storage* app, const char* path) { | ||||||
|  |     StorageType type = ST_ERROR; | ||||||
|  |     if(strlen(path) >= strlen(ext_path) && memcmp(path, ext_path, strlen(ext_path)) == 0) { | ||||||
|  |         type = ST_EXT; | ||||||
|  |     } else if(strlen(path) >= strlen(int_path) && memcmp(path, int_path, strlen(int_path)) == 0) { | ||||||
|  |         type = ST_INT; | ||||||
|  |     } else if(strlen(path) >= strlen(any_path) && memcmp(path, any_path, strlen(any_path)) == 0) { | ||||||
|  |         type = ST_ANY; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(type == ST_ANY) { | ||||||
|  |         type = ST_INT; | ||||||
|  |         if(storage_data_status(&app->storage[ST_EXT]) == StorageStatusOK) { | ||||||
|  |             type = ST_EXT; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void storage_path_change_to_real_storage(string_t path, StorageType real_storage) { | ||||||
|  |     if(memcmp(string_get_cstr(path), any_path, strlen(any_path)) == 0) { | ||||||
|  |         switch(real_storage) { | ||||||
|  |         case ST_EXT: | ||||||
|  |             string_set_char(path, 0, ext_path[0]); | ||||||
|  |             string_set_char(path, 1, ext_path[1]); | ||||||
|  |             string_set_char(path, 2, ext_path[2]); | ||||||
|  |             string_set_char(path, 3, ext_path[3]); | ||||||
|  |             break; | ||||||
|  |         case ST_INT: | ||||||
|  |             string_set_char(path, 0, int_path[0]); | ||||||
|  |             string_set_char(path, 1, int_path[1]); | ||||||
|  |             string_set_char(path, 2, int_path[2]); | ||||||
|  |             string_set_char(path, 3, int_path[3]); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /******************* File Functions *******************/ | /******************* File Functions *******************/ | ||||||
| 
 | 
 | ||||||
| bool storage_process_file_open( | bool storage_process_file_open( | ||||||
| @ -55,7 +93,7 @@ bool storage_process_file_open( | |||||||
|     FS_AccessMode access_mode, |     FS_AccessMode access_mode, | ||||||
|     FS_OpenMode open_mode) { |     FS_OpenMode open_mode) { | ||||||
|     bool ret = false; |     bool ret = false; | ||||||
|     StorageType type = storage_get_type_by_path(path); |     StorageType type = storage_get_type_by_path(app, path); | ||||||
|     StorageData* storage; |     StorageData* storage; | ||||||
|     file->error_id = FSE_OK; |     file->error_id = FSE_OK; | ||||||
| 
 | 
 | ||||||
| @ -63,12 +101,18 @@ bool storage_process_file_open( | |||||||
|         file->error_id = FSE_INVALID_NAME; |         file->error_id = FSE_INVALID_NAME; | ||||||
|     } else { |     } else { | ||||||
|         storage = storage_get_storage_by_type(app, type); |         storage = storage_get_storage_by_type(app, type); | ||||||
|         if(storage_path_already_open(path, storage->files)) { |         string_t real_path; | ||||||
|  |         string_init_set(real_path, path); | ||||||
|  |         storage_path_change_to_real_storage(real_path, type); | ||||||
|  | 
 | ||||||
|  |         if(storage_path_already_open(real_path, storage->files)) { | ||||||
|             file->error_id = FSE_ALREADY_OPEN; |             file->error_id = FSE_ALREADY_OPEN; | ||||||
|         } else { |         } else { | ||||||
|             storage_push_storage_file(file, path, type, storage); |             storage_push_storage_file(file, real_path, type, storage); | ||||||
|             FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode)); |             FS_CALL(storage, file.open(storage, file, remove_vfs(path), access_mode, open_mode)); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         string_clear(real_path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| @ -83,6 +127,9 @@ bool storage_process_file_close(Storage* app, File* file) { | |||||||
|     } else { |     } else { | ||||||
|         FS_CALL(storage, file.close(storage, file)); |         FS_CALL(storage, file.close(storage, file)); | ||||||
|         storage_pop_storage_file(file, storage); |         storage_pop_storage_file(file, storage); | ||||||
|  | 
 | ||||||
|  |         StorageEvent event = {.type = StorageEventTypeFileClose}; | ||||||
|  |         furi_pubsub_publish(app->pubsub, &event); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| @ -205,7 +252,7 @@ static bool storage_process_file_eof(Storage* app, File* file) { | |||||||
| 
 | 
 | ||||||
| bool storage_process_dir_open(Storage* app, File* file, const char* path) { | bool storage_process_dir_open(Storage* app, File* file, const char* path) { | ||||||
|     bool ret = false; |     bool ret = false; | ||||||
|     StorageType type = storage_get_type_by_path(path); |     StorageType type = storage_get_type_by_path(app, path); | ||||||
|     StorageData* storage; |     StorageData* storage; | ||||||
|     file->error_id = FSE_OK; |     file->error_id = FSE_OK; | ||||||
| 
 | 
 | ||||||
| @ -213,12 +260,17 @@ bool storage_process_dir_open(Storage* app, File* file, const char* path) { | |||||||
|         file->error_id = FSE_INVALID_NAME; |         file->error_id = FSE_INVALID_NAME; | ||||||
|     } else { |     } else { | ||||||
|         storage = storage_get_storage_by_type(app, type); |         storage = storage_get_storage_by_type(app, type); | ||||||
|         if(storage_path_already_open(path, storage->files)) { |         string_t real_path; | ||||||
|  |         string_init_set(real_path, path); | ||||||
|  |         storage_path_change_to_real_storage(real_path, type); | ||||||
|  | 
 | ||||||
|  |         if(storage_path_already_open(real_path, storage->files)) { | ||||||
|             file->error_id = FSE_ALREADY_OPEN; |             file->error_id = FSE_ALREADY_OPEN; | ||||||
|         } else { |         } else { | ||||||
|             storage_push_storage_file(file, path, type, storage); |             storage_push_storage_file(file, real_path, type, storage); | ||||||
|             FS_CALL(storage, dir.open(storage, file, remove_vfs(path))); |             FS_CALL(storage, dir.open(storage, file, remove_vfs(path))); | ||||||
|         } |         } | ||||||
|  |         string_clear(real_path); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| @ -233,6 +285,9 @@ bool storage_process_dir_close(Storage* app, File* file) { | |||||||
|     } else { |     } else { | ||||||
|         FS_CALL(storage, dir.close(storage, file)); |         FS_CALL(storage, dir.close(storage, file)); | ||||||
|         storage_pop_storage_file(file, storage); |         storage_pop_storage_file(file, storage); | ||||||
|  | 
 | ||||||
|  |         StorageEvent event = {.type = StorageEventTypeDirClose}; | ||||||
|  |         furi_pubsub_publish(app->pubsub, &event); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| @ -273,7 +328,7 @@ bool storage_process_dir_rewind(Storage* app, File* file) { | |||||||
| 
 | 
 | ||||||
| static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) { | static FS_Error storage_process_common_stat(Storage* app, const char* path, FileInfo* fileinfo) { | ||||||
|     FS_Error ret = FSE_OK; |     FS_Error ret = FSE_OK; | ||||||
|     StorageType type = storage_get_type_by_path(path); |     StorageType type = storage_get_type_by_path(app, path); | ||||||
| 
 | 
 | ||||||
|     if(storage_type_is_not_valid(type)) { |     if(storage_type_is_not_valid(type)) { | ||||||
|         ret = FSE_INVALID_NAME; |         ret = FSE_INVALID_NAME; | ||||||
| @ -287,7 +342,11 @@ static FS_Error storage_process_common_stat(Storage* app, const char* path, File | |||||||
| 
 | 
 | ||||||
| static FS_Error storage_process_common_remove(Storage* app, const char* path) { | static FS_Error storage_process_common_remove(Storage* app, const char* path) { | ||||||
|     FS_Error ret = FSE_OK; |     FS_Error ret = FSE_OK; | ||||||
|     StorageType type = storage_get_type_by_path(path); |     StorageType type = storage_get_type_by_path(app, path); | ||||||
|  | 
 | ||||||
|  |     string_t real_path; | ||||||
|  |     string_init_set(real_path, path); | ||||||
|  |     storage_path_change_to_real_storage(real_path, type); | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         if(storage_type_is_not_valid(type)) { |         if(storage_type_is_not_valid(type)) { | ||||||
| @ -296,7 +355,7 @@ static FS_Error storage_process_common_remove(Storage* app, const char* path) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         StorageData* storage = storage_get_storage_by_type(app, type); |         StorageData* storage = storage_get_storage_by_type(app, type); | ||||||
|         if(storage_path_already_open(path, storage->files)) { |         if(storage_path_already_open(real_path, storage->files)) { | ||||||
|             ret = FSE_ALREADY_OPEN; |             ret = FSE_ALREADY_OPEN; | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| @ -304,12 +363,14 @@ static FS_Error storage_process_common_remove(Storage* app, const char* path) { | |||||||
|         FS_CALL(storage, common.remove(storage, remove_vfs(path))); |         FS_CALL(storage, common.remove(storage, remove_vfs(path))); | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|  |     string_clear(real_path); | ||||||
|  | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { | static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { | ||||||
|     FS_Error ret = FSE_OK; |     FS_Error ret = FSE_OK; | ||||||
|     StorageType type = storage_get_type_by_path(path); |     StorageType type = storage_get_type_by_path(app, path); | ||||||
| 
 | 
 | ||||||
|     if(storage_type_is_not_valid(type)) { |     if(storage_type_is_not_valid(type)) { | ||||||
|         ret = FSE_INVALID_NAME; |         ret = FSE_INVALID_NAME; | ||||||
| @ -321,86 +382,13 @@ static FS_Error storage_process_common_mkdir(Storage* app, const char* path) { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static FS_Error storage_process_common_copy(Storage* app, const char* old, const char* new) { |  | ||||||
|     FS_Error ret = FSE_INTERNAL; |  | ||||||
|     File file_old; |  | ||||||
|     File file_new; |  | ||||||
| 
 |  | ||||||
|     FileInfo fileinfo; |  | ||||||
|     ret = storage_process_common_stat(app, old, &fileinfo); |  | ||||||
| 
 |  | ||||||
|     if(ret == FSE_OK) { |  | ||||||
|         if(fileinfo.flags & FSF_DIRECTORY) { |  | ||||||
|             ret = storage_process_common_mkdir(app, new); |  | ||||||
|         } else { |  | ||||||
|             do { |  | ||||||
|                 if(!storage_process_file_open(app, &file_old, old, FSAM_READ, FSOM_OPEN_EXISTING)) { |  | ||||||
|                     ret = storage_file_get_error(&file_old); |  | ||||||
|                     storage_process_file_close(app, &file_old); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if(!storage_process_file_open(app, &file_new, new, FSAM_WRITE, FSOM_CREATE_NEW)) { |  | ||||||
|                     ret = storage_file_get_error(&file_new); |  | ||||||
|                     storage_process_file_close(app, &file_new); |  | ||||||
|                     storage_process_file_close(app, &file_old); |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 const uint16_t buffer_size = 64; |  | ||||||
|                 uint8_t* buffer = malloc(buffer_size); |  | ||||||
|                 uint16_t readed_size = 0; |  | ||||||
|                 uint16_t writed_size = 0; |  | ||||||
| 
 |  | ||||||
|                 while(true) { |  | ||||||
|                     readed_size = storage_process_file_read(app, &file_old, buffer, buffer_size); |  | ||||||
|                     ret = storage_file_get_error(&file_old); |  | ||||||
|                     if(readed_size == 0) break; |  | ||||||
| 
 |  | ||||||
|                     writed_size = storage_process_file_write(app, &file_new, buffer, readed_size); |  | ||||||
|                     ret = storage_file_get_error(&file_new); |  | ||||||
|                     if(writed_size < readed_size) break; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 free(buffer); |  | ||||||
|                 storage_process_file_close(app, &file_old); |  | ||||||
|                 storage_process_file_close(app, &file_new); |  | ||||||
|             } while(false); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static FS_Error storage_process_common_rename(Storage* app, const char* old, const char* new) { |  | ||||||
|     FS_Error ret = FSE_INTERNAL; |  | ||||||
|     StorageType type_old = storage_get_type_by_path(old); |  | ||||||
|     StorageType type_new = storage_get_type_by_path(new); |  | ||||||
| 
 |  | ||||||
|     if(storage_type_is_not_valid(type_old) || storage_type_is_not_valid(type_new)) { |  | ||||||
|         ret = FSE_INVALID_NAME; |  | ||||||
|     } else { |  | ||||||
|         if(type_old != type_new) { |  | ||||||
|             ret = storage_process_common_copy(app, old, new); |  | ||||||
|             if(ret == FSE_OK) { |  | ||||||
|                 ret = storage_process_common_remove(app, old); |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             StorageData* storage = storage_get_storage_by_type(app, type_old); |  | ||||||
|             FS_CALL(storage, common.rename(storage, remove_vfs(old), remove_vfs(new))); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static FS_Error storage_process_common_fs_info( | static FS_Error storage_process_common_fs_info( | ||||||
|     Storage* app, |     Storage* app, | ||||||
|     const char* fs_path, |     const char* fs_path, | ||||||
|     uint64_t* total_space, |     uint64_t* total_space, | ||||||
|     uint64_t* free_space) { |     uint64_t* free_space) { | ||||||
|     FS_Error ret = FSE_OK; |     FS_Error ret = FSE_OK; | ||||||
|     StorageType type = storage_get_type_by_path(fs_path); |     StorageType type = storage_get_type_by_path(app, fs_path); | ||||||
| 
 | 
 | ||||||
|     if(storage_type_is_not_valid(type)) { |     if(storage_type_is_not_valid(type)) { | ||||||
|         ret = FSE_INVALID_NAME; |         ret = FSE_INVALID_NAME; | ||||||
| @ -472,8 +460,7 @@ static FS_Error storage_process_sd_status(Storage* app) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /****************** API calls processing ******************/ | /****************** API calls processing ******************/ | ||||||
| 
 | void storage_process_message_internal(Storage* app, StorageMessage* message) { | ||||||
| void storage_process_message(Storage* app, StorageMessage* message) { |  | ||||||
|     switch(message->command) { |     switch(message->command) { | ||||||
|     case StorageCommandFileOpen: |     case StorageCommandFileOpen: | ||||||
|         message->return_data->bool_value = storage_process_file_open( |         message->return_data->bool_value = storage_process_file_open( | ||||||
| @ -556,14 +543,6 @@ void storage_process_message(Storage* app, StorageMessage* message) { | |||||||
|         message->return_data->error_value = |         message->return_data->error_value = | ||||||
|             storage_process_common_remove(app, message->data->path.path); |             storage_process_common_remove(app, message->data->path.path); | ||||||
|         break; |         break; | ||||||
|     case StorageCommandCommonRename: |  | ||||||
|         message->return_data->error_value = storage_process_common_rename( |  | ||||||
|             app, message->data->cpaths.old, message->data->cpaths.new); |  | ||||||
|         break; |  | ||||||
|     case StorageCommandCommonCopy: |  | ||||||
|         message->return_data->error_value = |  | ||||||
|             storage_process_common_copy(app, message->data->cpaths.old, message->data->cpaths.new); |  | ||||||
|         break; |  | ||||||
|     case StorageCommandCommonMkDir: |     case StorageCommandCommonMkDir: | ||||||
|         message->return_data->error_value = |         message->return_data->error_value = | ||||||
|             storage_process_common_mkdir(app, message->data->path.path); |             storage_process_common_mkdir(app, message->data->path.path); | ||||||
| @ -592,3 +571,7 @@ void storage_process_message(Storage* app, StorageMessage* message) { | |||||||
| 
 | 
 | ||||||
|     osSemaphoreRelease(message->semaphore); |     osSemaphoreRelease(message->semaphore); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void storage_process_message(Storage* app, StorageMessage* message) { | ||||||
|  |     storage_process_message_internal(app, message); | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,8 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include "filesystem_api_defines.h" | #include "filesystem_api_defines.h" | ||||||
| #include <fatfs.h> |  | ||||||
| #include "storage_glue.h" |  | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| @ -11,10 +9,11 @@ extern "C" { | |||||||
| #define SD_LABEL_LENGTH 34 | #define SD_LABEL_LENGTH 34 | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     FST_FAT12 = FS_FAT12, |     FST_UNKNOWN, | ||||||
|     FST_FAT16 = FS_FAT16, |     FST_FAT12, | ||||||
|     FST_FAT32 = FS_FAT32, |     FST_FAT16, | ||||||
|     FST_EXFAT = FS_EXFAT, |     FST_FAT32, | ||||||
|  |     FST_EXFAT, | ||||||
| } SDFsType; | } SDFsType; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  | |||||||
| @ -164,7 +164,24 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { | |||||||
|         sector_size = fs->ssize; |         sector_size = fs->ssize; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|         sd_info->fs_type = fs->fs_type; |         switch(fs->fs_type) { | ||||||
|  |         case FS_FAT12: | ||||||
|  |             sd_info->fs_type = FST_FAT12; | ||||||
|  |             break; | ||||||
|  |         case FS_FAT16: | ||||||
|  |             sd_info->fs_type = FST_FAT16; | ||||||
|  |             break; | ||||||
|  |         case FS_FAT32: | ||||||
|  |             sd_info->fs_type = FST_FAT32; | ||||||
|  |             break; | ||||||
|  |         case FS_EXFAT: | ||||||
|  |             sd_info->fs_type = FST_EXFAT; | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             sd_info->fs_type = FST_UNKNOWN; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         sd_info->kb_total = total_sectors / 1024 * sector_size; |         sd_info->kb_total = total_sectors / 1024 * sector_size; | ||||||
|         sd_info->kb_free = free_sectors / 1024 * sector_size; |         sd_info->kb_free = free_sectors / 1024 * sector_size; | ||||||
| @ -466,11 +483,6 @@ static FS_Error storage_ext_common_remove(void* ctx, const char* path) { | |||||||
|     return storage_ext_parse_error(result); |     return storage_ext_parse_error(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static FS_Error storage_ext_common_rename(void* ctx, const char* old_path, const char* new_path) { |  | ||||||
|     SDError result = f_rename(old_path, new_path); |  | ||||||
|     return storage_ext_parse_error(result); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static FS_Error storage_ext_common_mkdir(void* ctx, const char* path) { | static FS_Error storage_ext_common_mkdir(void* ctx, const char* path) { | ||||||
|     SDError result = f_mkdir(path); |     SDError result = f_mkdir(path); | ||||||
|     return storage_ext_parse_error(result); |     return storage_ext_parse_error(result); | ||||||
| @ -510,6 +522,35 @@ static FS_Error storage_ext_common_fs_info( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /******************* Init Storage *******************/ | /******************* Init Storage *******************/ | ||||||
|  | static const FS_Api fs_api = { | ||||||
|  |     .file = | ||||||
|  |         { | ||||||
|  |             .open = storage_ext_file_open, | ||||||
|  |             .close = storage_ext_file_close, | ||||||
|  |             .read = storage_ext_file_read, | ||||||
|  |             .write = storage_ext_file_write, | ||||||
|  |             .seek = storage_ext_file_seek, | ||||||
|  |             .tell = storage_ext_file_tell, | ||||||
|  |             .truncate = storage_ext_file_truncate, | ||||||
|  |             .size = storage_ext_file_size, | ||||||
|  |             .sync = storage_ext_file_sync, | ||||||
|  |             .eof = storage_ext_file_eof, | ||||||
|  |         }, | ||||||
|  |     .dir = | ||||||
|  |         { | ||||||
|  |             .open = storage_ext_dir_open, | ||||||
|  |             .close = storage_ext_dir_close, | ||||||
|  |             .read = storage_ext_dir_read, | ||||||
|  |             .rewind = storage_ext_dir_rewind, | ||||||
|  |         }, | ||||||
|  |     .common = | ||||||
|  |         { | ||||||
|  |             .stat = storage_ext_common_stat, | ||||||
|  |             .mkdir = storage_ext_common_mkdir, | ||||||
|  |             .remove = storage_ext_common_remove, | ||||||
|  |             .fs_info = storage_ext_common_fs_info, | ||||||
|  |         }, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| void storage_ext_init(StorageData* storage) { | void storage_ext_init(StorageData* storage) { | ||||||
|     SDData* sd_data = malloc(sizeof(SDData)); |     SDData* sd_data = malloc(sizeof(SDData)); | ||||||
| @ -519,27 +560,7 @@ void storage_ext_init(StorageData* storage) { | |||||||
| 
 | 
 | ||||||
|     storage->data = sd_data; |     storage->data = sd_data; | ||||||
|     storage->api.tick = storage_ext_tick; |     storage->api.tick = storage_ext_tick; | ||||||
|     storage->fs_api.file.open = storage_ext_file_open; |     storage->fs_api = &fs_api; | ||||||
|     storage->fs_api.file.close = storage_ext_file_close; |  | ||||||
|     storage->fs_api.file.read = storage_ext_file_read; |  | ||||||
|     storage->fs_api.file.write = storage_ext_file_write; |  | ||||||
|     storage->fs_api.file.seek = storage_ext_file_seek; |  | ||||||
|     storage->fs_api.file.tell = storage_ext_file_tell; |  | ||||||
|     storage->fs_api.file.truncate = storage_ext_file_truncate; |  | ||||||
|     storage->fs_api.file.size = storage_ext_file_size; |  | ||||||
|     storage->fs_api.file.sync = storage_ext_file_sync; |  | ||||||
|     storage->fs_api.file.eof = storage_ext_file_eof; |  | ||||||
| 
 |  | ||||||
|     storage->fs_api.dir.open = storage_ext_dir_open; |  | ||||||
|     storage->fs_api.dir.close = storage_ext_dir_close; |  | ||||||
|     storage->fs_api.dir.read = storage_ext_dir_read; |  | ||||||
|     storage->fs_api.dir.rewind = storage_ext_dir_rewind; |  | ||||||
| 
 |  | ||||||
|     storage->fs_api.common.stat = storage_ext_common_stat; |  | ||||||
|     storage->fs_api.common.mkdir = storage_ext_common_mkdir; |  | ||||||
|     storage->fs_api.common.rename = storage_ext_common_rename; |  | ||||||
|     storage->fs_api.common.remove = storage_ext_common_remove; |  | ||||||
|     storage->fs_api.common.fs_info = storage_ext_common_fs_info; |  | ||||||
| 
 | 
 | ||||||
|     hal_sd_detect_init(); |     hal_sd_detect_init(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -636,13 +636,6 @@ static FS_Error storage_int_common_remove(void* ctx, const char* path) { | |||||||
|     return storage_int_parse_error(result); |     return storage_int_parse_error(result); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static FS_Error storage_int_common_rename(void* ctx, const char* old_path, const char* new_path) { |  | ||||||
|     StorageData* storage = ctx; |  | ||||||
|     lfs_t* lfs = lfs_get_from_storage(storage); |  | ||||||
|     int result = lfs_rename(lfs, old_path, new_path); |  | ||||||
|     return storage_int_parse_error(result); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static FS_Error storage_int_common_mkdir(void* ctx, const char* path) { | static FS_Error storage_int_common_mkdir(void* ctx, const char* path) { | ||||||
|     StorageData* storage = ctx; |     StorageData* storage = ctx; | ||||||
|     lfs_t* lfs = lfs_get_from_storage(storage); |     lfs_t* lfs = lfs_get_from_storage(storage); | ||||||
| @ -671,6 +664,35 @@ static FS_Error storage_int_common_fs_info( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /******************* Init Storage *******************/ | /******************* Init Storage *******************/ | ||||||
|  | static const FS_Api fs_api = { | ||||||
|  |     .file = | ||||||
|  |         { | ||||||
|  |             .open = storage_int_file_open, | ||||||
|  |             .close = storage_int_file_close, | ||||||
|  |             .read = storage_int_file_read, | ||||||
|  |             .write = storage_int_file_write, | ||||||
|  |             .seek = storage_int_file_seek, | ||||||
|  |             .tell = storage_int_file_tell, | ||||||
|  |             .truncate = storage_int_file_truncate, | ||||||
|  |             .size = storage_int_file_size, | ||||||
|  |             .sync = storage_int_file_sync, | ||||||
|  |             .eof = storage_int_file_eof, | ||||||
|  |         }, | ||||||
|  |     .dir = | ||||||
|  |         { | ||||||
|  |             .open = storage_int_dir_open, | ||||||
|  |             .close = storage_int_dir_close, | ||||||
|  |             .read = storage_int_dir_read, | ||||||
|  |             .rewind = storage_int_dir_rewind, | ||||||
|  |         }, | ||||||
|  |     .common = | ||||||
|  |         { | ||||||
|  |             .stat = storage_int_common_stat, | ||||||
|  |             .mkdir = storage_int_common_mkdir, | ||||||
|  |             .remove = storage_int_common_remove, | ||||||
|  |             .fs_info = storage_int_common_fs_info, | ||||||
|  |         }, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| void storage_int_init(StorageData* storage) { | void storage_int_init(StorageData* storage) { | ||||||
|     FURI_LOG_I(TAG, "Starting"); |     FURI_LOG_I(TAG, "Starting"); | ||||||
| @ -689,25 +711,5 @@ void storage_int_init(StorageData* storage) { | |||||||
| 
 | 
 | ||||||
|     storage->data = lfs_data; |     storage->data = lfs_data; | ||||||
|     storage->api.tick = NULL; |     storage->api.tick = NULL; | ||||||
|     storage->fs_api.file.open = storage_int_file_open; |     storage->fs_api = &fs_api; | ||||||
|     storage->fs_api.file.close = storage_int_file_close; |  | ||||||
|     storage->fs_api.file.read = storage_int_file_read; |  | ||||||
|     storage->fs_api.file.write = storage_int_file_write; |  | ||||||
|     storage->fs_api.file.seek = storage_int_file_seek; |  | ||||||
|     storage->fs_api.file.tell = storage_int_file_tell; |  | ||||||
|     storage->fs_api.file.truncate = storage_int_file_truncate; |  | ||||||
|     storage->fs_api.file.size = storage_int_file_size; |  | ||||||
|     storage->fs_api.file.sync = storage_int_file_sync; |  | ||||||
|     storage->fs_api.file.eof = storage_int_file_eof; |  | ||||||
| 
 |  | ||||||
|     storage->fs_api.dir.open = storage_int_dir_open; |  | ||||||
|     storage->fs_api.dir.close = storage_int_dir_close; |  | ||||||
|     storage->fs_api.dir.read = storage_int_dir_read; |  | ||||||
|     storage->fs_api.dir.rewind = storage_int_dir_rewind; |  | ||||||
| 
 |  | ||||||
|     storage->fs_api.common.stat = storage_int_common_stat; |  | ||||||
|     storage->fs_api.common.mkdir = storage_int_common_mkdir; |  | ||||||
|     storage->fs_api.common.rename = storage_int_common_rename; |  | ||||||
|     storage->fs_api.common.remove = storage_int_common_remove; |  | ||||||
|     storage->fs_api.common.fs_info = storage_int_common_fs_info; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,9 +23,9 @@ typedef enum { | |||||||
|     SubGhzCustomEventSceneExit, |     SubGhzCustomEventSceneExit, | ||||||
|     SubGhzCustomEventSceneStay, |     SubGhzCustomEventSceneStay, | ||||||
| 
 | 
 | ||||||
|     SubGhzCustomEventViewReceverOK, |     SubGhzCustomEventViewReceiverOK, | ||||||
|     SubGhzCustomEventViewReceverConfig, |     SubGhzCustomEventViewReceiverConfig, | ||||||
|     SubGhzCustomEventViewReceverBack, |     SubGhzCustomEventViewReceiverBack, | ||||||
| 
 | 
 | ||||||
|     SubGhzCustomEventViewReadRAWBack, |     SubGhzCustomEventViewReadRAWBack, | ||||||
|     SubGhzCustomEventViewReadRAWIDLE, |     SubGhzCustomEventViewReadRAWIDLE, | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) { | |||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubGhzCustomEventSceneDelete) { |         if(event.event == SubGhzCustomEventSceneDelete) { | ||||||
|             strcpy(subghz->file_name_tmp, subghz->file_name); |             strncpy(subghz->file_name_tmp, subghz->file_name, SUBGHZ_MAX_LEN_NAME); | ||||||
|             if(subghz_delete_file(subghz)) { |             if(subghz_delete_file(subghz)) { | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); | ||||||
|             } else { |             } else { | ||||||
|  | |||||||
| @ -56,7 +56,7 @@ bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) { | |||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         if(event.event == SubGhzCustomEventSceneDeleteRAW) { |         if(event.event == SubGhzCustomEventSceneDeleteRAW) { | ||||||
|             strcpy(subghz->file_name_tmp, subghz->file_name); |             strncpy(subghz->file_name_tmp, subghz->file_name, SUBGHZ_MAX_LEN_NAME); | ||||||
|             if(subghz_delete_file(subghz)) { |             if(subghz_delete_file(subghz)) { | ||||||
|                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess); | ||||||
|             } else { |             } else { | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ bool subghz_scene_read_raw_update_filename(SubGhz* subghz) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         path_extract_filename_no_ext(string_get_cstr(temp_str), temp_str); |         path_extract_filename_no_ext(string_get_cstr(temp_str), temp_str); | ||||||
|         strcpy(subghz->file_name, string_get_cstr(temp_str)); |         strncpy(subghz->file_name, string_get_cstr(temp_str), SUBGHZ_MAX_LEN_NAME); | ||||||
| 
 | 
 | ||||||
|         ret = true; |         ret = true; | ||||||
|     } while(false); |     } while(false); | ||||||
|  | |||||||
| @ -111,7 +111,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|         switch(event.event) { |         switch(event.event) { | ||||||
|         case SubGhzCustomEventViewReceverBack: |         case SubGhzCustomEventViewReceiverBack: | ||||||
| 
 | 
 | ||||||
|             // Stop CC1101 Rx
 |             // Stop CC1101 Rx
 | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
| @ -134,13 +134,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { | |||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|             break; |             break; | ||||||
|         case SubGhzCustomEventViewReceverOK: |         case SubGhzCustomEventViewReceiverOK: | ||||||
|             subghz->txrx->idx_menu_chosen = |             subghz->txrx->idx_menu_chosen = | ||||||
|                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); |                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); |             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); | ||||||
|             return true; |             return true; | ||||||
|             break; |             break; | ||||||
|         case SubGhzCustomEventViewReceverConfig: |         case SubGhzCustomEventViewReceiverConfig: | ||||||
|             subghz->state_notifications = SubGhzNotificationStateIDLE; |             subghz->state_notifications = SubGhzNotificationStateIDLE; | ||||||
|             subghz->txrx->idx_menu_chosen = |             subghz->txrx->idx_menu_chosen = | ||||||
|                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); |                 subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); | ||||||
|  | |||||||
| @ -22,10 +22,10 @@ void subghz_scene_save_name_on_enter(void* context) { | |||||||
|         //highlighting the entire filename by default
 |         //highlighting the entire filename by default
 | ||||||
|         dev_name_empty = true; |         dev_name_empty = true; | ||||||
|     } else { |     } else { | ||||||
|         strcpy(subghz->file_name_tmp, subghz->file_name); |         strncpy(subghz->file_name_tmp, subghz->file_name, SUBGHZ_MAX_LEN_NAME); | ||||||
|         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != |         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != | ||||||
|            SubGhzCustomEventManagerNoSet) { |            SubGhzCustomEventManagerNoSet) { | ||||||
|             subghz_get_next_name_file(subghz); |             subghz_get_next_name_file(subghz, SUBGHZ_MAX_LEN_NAME); | ||||||
|             if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == |             if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) == | ||||||
|                SubGhzCustomEventManagerSetRAW) { |                SubGhzCustomEventManagerSetRAW) { | ||||||
|                 dev_name_empty = true; |                 dev_name_empty = true; | ||||||
| @ -39,11 +39,13 @@ void subghz_scene_save_name_on_enter(void* context) { | |||||||
|         subghz_scene_save_name_text_input_callback, |         subghz_scene_save_name_text_input_callback, | ||||||
|         subghz, |         subghz, | ||||||
|         subghz->file_name, |         subghz->file_name, | ||||||
|         22, //Max len name
 |         SUBGHZ_MAX_LEN_NAME + 1, // buffer size
 | ||||||
|         dev_name_empty); |         dev_name_empty); | ||||||
| 
 | 
 | ||||||
|     ValidatorIsFile* validator_is_file = |     ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( | ||||||
|         validator_is_file_alloc_init(SUBGHZ_APP_FOLDER, SUBGHZ_APP_EXTENSION); |         SUBGHZ_APP_FOLDER, | ||||||
|  |         SUBGHZ_APP_EXTENSION, | ||||||
|  |         (dev_name_empty) ? (NULL) : (subghz->file_name_tmp)); | ||||||
|     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); |     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||||
| 
 | 
 | ||||||
|     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); |     view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); | ||||||
| @ -52,7 +54,7 @@ void subghz_scene_save_name_on_enter(void* context) { | |||||||
| bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { | ||||||
|     SubGhz* subghz = context; |     SubGhz* subghz = context; | ||||||
|     if(event.type == SceneManagerEventTypeBack) { |     if(event.type == SceneManagerEventTypeBack) { | ||||||
|         strcpy(subghz->file_name, subghz->file_name_tmp); |         strncpy(subghz->file_name, subghz->file_name_tmp, SUBGHZ_MAX_LEN_NAME); | ||||||
|         scene_manager_previous_scene(subghz->scene_manager); |         scene_manager_previous_scene(subghz->scene_manager); | ||||||
|         return true; |         return true; | ||||||
|     } else if(event.type == SceneManagerEventTypeCustom) { |     } else if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  | |||||||
| @ -296,6 +296,10 @@ void subghz_free(SubGhz* subghz) { | |||||||
|     furi_record_close("notification"); |     furi_record_close("notification"); | ||||||
|     subghz->notifications = NULL; |     subghz->notifications = NULL; | ||||||
| 
 | 
 | ||||||
|  |     // About birds
 | ||||||
|  |     furi_assert(subghz->file_name[SUBGHZ_MAX_LEN_NAME] == 0); | ||||||
|  |     furi_assert(subghz->file_name_tmp[SUBGHZ_MAX_LEN_NAME] == 0); | ||||||
|  | 
 | ||||||
|     // The rest
 |     // The rest
 | ||||||
|     free(subghz); |     free(subghz); | ||||||
| } | } | ||||||
| @ -309,20 +313,26 @@ int32_t subghz_app(void* p) { | |||||||
|     subghz_environment_load_keystore( |     subghz_environment_load_keystore( | ||||||
|         subghz->txrx->environment, "/ext/subghz/assets/keeloq_mfcodes_user"); |         subghz->txrx->environment, "/ext/subghz/assets/keeloq_mfcodes_user"); | ||||||
|     // Check argument and run corresponding scene
 |     // Check argument and run corresponding scene
 | ||||||
|     if(p && subghz_key_load(subghz, p)) { |     if(p) { | ||||||
|         string_t filename; |         if(subghz_key_load(subghz, p)) { | ||||||
|         string_init(filename); |             string_t filename; | ||||||
|  |             string_init(filename); | ||||||
| 
 | 
 | ||||||
|         path_extract_filename_no_ext(p, filename); |             path_extract_filename_no_ext(p, filename); | ||||||
|         strcpy(subghz->file_name, string_get_cstr(filename)); |             strncpy(subghz->file_name, string_get_cstr(filename), SUBGHZ_MAX_LEN_NAME); | ||||||
|         string_clear(filename); |             string_clear(filename); | ||||||
|         if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { |             if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { | ||||||
|             //Load Raw TX
 |                 //Load Raw TX
 | ||||||
|             subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; |                 subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad; | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); | ||||||
|  |             } else { | ||||||
|  |                 //Load transmitter TX
 | ||||||
|  |                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); | ||||||
|  |             } | ||||||
|         } else { |         } else { | ||||||
|             //Load transmitter TX
 |             //exit app
 | ||||||
|             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter); |             scene_manager_stop(subghz->scene_manager); | ||||||
|  |             view_dispatcher_stop(subghz->view_dispatcher); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         if(load_database) { |         if(load_database) { | ||||||
|  | |||||||
							
								
								
									
										74
									
								
								applications/subghz/subghz_i.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										74
									
								
								applications/subghz/subghz_i.c
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @ -197,6 +197,22 @@ void subghz_tx_stop(SubGhz* subghz) { | |||||||
|     notification_message(subghz->notifications, &sequence_reset_red); |     notification_message(subghz->notifications, &sequence_reset_red); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void subghz_dialog_message_show_only_rx(SubGhz* subghz) { | ||||||
|  |     DialogsApp* dialogs = subghz->dialogs; | ||||||
|  |     DialogMessage* message = dialog_message_alloc(); | ||||||
|  |     dialog_message_set_text( | ||||||
|  |         message, | ||||||
|  |         "This frequency can\nonly be used for RX\nin your region", | ||||||
|  |         38, | ||||||
|  |         23, | ||||||
|  |         AlignCenter, | ||||||
|  |         AlignCenter); | ||||||
|  |     dialog_message_set_icon(message, &I_DolphinFirstStart7_61x51, 67, 12); | ||||||
|  |     dialog_message_set_buttons(message, "Back", NULL, NULL); | ||||||
|  |     dialog_message_show(dialogs, message); | ||||||
|  |     dialog_message_free(message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool subghz_key_load(SubGhz* subghz, const char* file_path) { | bool subghz_key_load(SubGhz* subghz, const char* file_path) { | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
|     furi_assert(file_path); |     furi_assert(file_path); | ||||||
| @ -205,10 +221,11 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | |||||||
|     FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); |     FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); | ||||||
|     Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); |     Stream* fff_data_stream = flipper_format_get_raw_stream(subghz->txrx->fff_data); | ||||||
| 
 | 
 | ||||||
|  |     uint8_t err = 1; | ||||||
|     bool loaded = false; |     bool loaded = false; | ||||||
|     string_t temp_str; |     string_t temp_str; | ||||||
|     string_init(temp_str); |     string_init(temp_str); | ||||||
|     uint32_t version; |     uint32_t temp_data32; | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         stream_clean(fff_data_stream); |         stream_clean(fff_data_stream); | ||||||
| @ -217,25 +234,36 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(!flipper_format_read_header(fff_data_file, temp_str, &version)) { |         if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { | ||||||
|             FURI_LOG_E(TAG, "Missing or incorrect header"); |             FURI_LOG_E(TAG, "Missing or incorrect header"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(((!strcmp(string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || |         if(((!strcmp(string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) || | ||||||
|             (!strcmp(string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && |             (!strcmp(string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) && | ||||||
|            version == SUBGHZ_KEY_FILE_VERSION) { |            temp_data32 == SUBGHZ_KEY_FILE_VERSION) { | ||||||
|         } else { |         } else { | ||||||
|             FURI_LOG_E(TAG, "Type or version mismatch"); |             FURI_LOG_E(TAG, "Type or version mismatch"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(!flipper_format_read_uint32( |         if(!flipper_format_read_uint32(fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) { | ||||||
|                fff_data_file, "Frequency", (uint32_t*)&subghz->txrx->frequency, 1)) { |  | ||||||
|             FURI_LOG_E(TAG, "Missing Frequency"); |             FURI_LOG_E(TAG, "Missing Frequency"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(!furi_hal_subghz_is_frequency_valid(temp_data32)) { | ||||||
|  |             FURI_LOG_E(TAG, "Frequency not supported"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!furi_hal_subghz_is_tx_allowed(temp_data32)) { | ||||||
|  |             FURI_LOG_E(TAG, "This frequency can only be used for RX in your region"); | ||||||
|  |             err = 2; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         subghz->txrx->frequency = temp_data32; | ||||||
|  | 
 | ||||||
|         if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { |         if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) { | ||||||
|             FURI_LOG_E(TAG, "Missing Preset"); |             FURI_LOG_E(TAG, "Missing Preset"); | ||||||
|             break; |             break; | ||||||
| @ -267,24 +295,38 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { | |||||||
|         if(subghz->txrx->decoder_result) { |         if(subghz->txrx->decoder_result) { | ||||||
|             subghz_protocol_decoder_base_deserialize( |             subghz_protocol_decoder_base_deserialize( | ||||||
|                 subghz->txrx->decoder_result, subghz->txrx->fff_data); |                 subghz->txrx->decoder_result, subghz->txrx->fff_data); | ||||||
|  |         } else { | ||||||
|  |             FURI_LOG_E(TAG, "Protocol not found"); | ||||||
|  |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         loaded = true; |         loaded = true; | ||||||
|     } while(0); |     } while(0); | ||||||
| 
 | 
 | ||||||
|     if(!loaded) { |     if(!loaded) { | ||||||
|         dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); |         switch(err) { | ||||||
|  |         case 1: | ||||||
|  |             dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case 2: | ||||||
|  |             subghz_dialog_message_show_only_rx(subghz); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         default: | ||||||
|  |             furi_crash(NULL); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     string_clear(temp_str); |     string_clear(temp_str); | ||||||
|     //string_clear(path);
 |  | ||||||
|     flipper_format_free(fff_data_file); |     flipper_format_free(fff_data_file); | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
| 
 | 
 | ||||||
|     return loaded; |     return loaded; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool subghz_get_next_name_file(SubGhz* subghz) { | bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len) { | ||||||
|     furi_assert(subghz); |     furi_assert(subghz); | ||||||
| 
 | 
 | ||||||
|     Storage* storage = furi_record_open("storage"); |     Storage* storage = furi_record_open("storage"); | ||||||
| @ -295,9 +337,9 @@ bool subghz_get_next_name_file(SubGhz* subghz) { | |||||||
|     if(strcmp(subghz->file_name, "")) { |     if(strcmp(subghz->file_name, "")) { | ||||||
|         //get the name of the next free file
 |         //get the name of the next free file
 | ||||||
|         storage_get_next_filename( |         storage_get_next_filename( | ||||||
|             storage, SUBGHZ_RAW_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION, temp_str); |             storage, SUBGHZ_RAW_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION, temp_str, max_len); | ||||||
| 
 | 
 | ||||||
|         strcpy(subghz->file_name, string_get_cstr(temp_str)); |         strncpy(subghz->file_name, string_get_cstr(temp_str), SUBGHZ_MAX_LEN_NAME); | ||||||
|         res = true; |         res = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -393,12 +435,14 @@ bool subghz_rename_file(SubGhz* subghz) { | |||||||
|     string_init_printf( |     string_init_printf( | ||||||
|         new_path, "%s/%s%s", SUBGHZ_APP_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION); |         new_path, "%s/%s%s", SUBGHZ_APP_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION); | ||||||
| 
 | 
 | ||||||
|     FS_Error fs_result = |     if(string_cmp(old_path, new_path) != 0) { | ||||||
|         storage_common_rename(storage, string_get_cstr(old_path), string_get_cstr(new_path)); |         FS_Error fs_result = | ||||||
|  |             storage_common_rename(storage, string_get_cstr(old_path), string_get_cstr(new_path)); | ||||||
| 
 | 
 | ||||||
|     if(fs_result != FSE_OK) { |         if(fs_result != FSE_OK) { | ||||||
|         dialog_message_show_storage_error(subghz->dialogs, "Cannot rename\n file/directory"); |             dialog_message_show_storage_error(subghz->dialogs, "Cannot rename\n file/directory"); | ||||||
|         ret = false; |             ret = false; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     string_clear(old_path); |     string_clear(old_path); | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <gui/modules/variable_item_list.h> | #include <gui/modules/variable_item_list.h> | ||||||
| 
 | 
 | ||||||
| #define SUBGHZ_TEXT_STORE_SIZE 40 | #define SUBGHZ_MAX_LEN_NAME 21 | ||||||
| 
 | 
 | ||||||
| extern const char* const subghz_frequencies_text[]; | extern const char* const subghz_frequencies_text[]; | ||||||
| extern const uint32_t subghz_frequencies[]; | extern const uint32_t subghz_frequencies[]; | ||||||
| @ -115,8 +115,8 @@ struct SubGhz { | |||||||
|     TextInput* text_input; |     TextInput* text_input; | ||||||
|     Widget* widget; |     Widget* widget; | ||||||
|     DialogsApp* dialogs; |     DialogsApp* dialogs; | ||||||
|     char file_name[SUBGHZ_TEXT_STORE_SIZE + 1]; |     char file_name[SUBGHZ_MAX_LEN_NAME + 1]; | ||||||
|     char file_name_tmp[SUBGHZ_TEXT_STORE_SIZE + 1]; |     char file_name_tmp[SUBGHZ_MAX_LEN_NAME + 1]; | ||||||
|     SubGhzNotificationState state_notifications; |     SubGhzNotificationState state_notifications; | ||||||
| 
 | 
 | ||||||
|     SubGhzViewReceiver* subghz_receiver; |     SubGhzViewReceiver* subghz_receiver; | ||||||
| @ -155,8 +155,9 @@ void subghz_rx_end(SubGhz* subghz); | |||||||
| void subghz_sleep(SubGhz* subghz); | void subghz_sleep(SubGhz* subghz); | ||||||
| bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | ||||||
| void subghz_tx_stop(SubGhz* subghz); | void subghz_tx_stop(SubGhz* subghz); | ||||||
|  | void subghz_dialog_message_show_only_rx(SubGhz* subghz); | ||||||
| bool subghz_key_load(SubGhz* subghz, const char* file_path); | bool subghz_key_load(SubGhz* subghz, const char* file_path); | ||||||
| bool subghz_get_next_name_file(SubGhz* subghz); | bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); | ||||||
| bool subghz_save_protocol_to_file( | bool subghz_save_protocol_to_file( | ||||||
|     SubGhz* subghz, |     SubGhz* subghz, | ||||||
|     FlipperFormat* flipper_format, |     FlipperFormat* flipper_format, | ||||||
|  | |||||||
| @ -181,7 +181,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | |||||||
|     SubGhzViewReceiver* subghz_receiver = context; |     SubGhzViewReceiver* subghz_receiver = context; | ||||||
| 
 | 
 | ||||||
|     if(event->key == InputKeyBack && event->type == InputTypeShort) { |     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||||
|         subghz_receiver->callback(SubGhzCustomEventViewReceverBack, subghz_receiver->context); |         subghz_receiver->callback(SubGhzCustomEventViewReceiverBack, subghz_receiver->context); | ||||||
|     } else if( |     } else if( | ||||||
|         event->key == InputKeyUp && |         event->key == InputKeyUp && | ||||||
|         (event->type == InputTypeShort || event->type == InputTypeRepeat)) { |         (event->type == InputTypeShort || event->type == InputTypeRepeat)) { | ||||||
| @ -199,13 +199,13 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { | |||||||
|                 return true; |                 return true; | ||||||
|             }); |             }); | ||||||
|     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { |     } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { | ||||||
|         subghz_receiver->callback(SubGhzCustomEventViewReceverConfig, subghz_receiver->context); |         subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context); | ||||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { |     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||||
|         with_view_model( |         with_view_model( | ||||||
|             subghz_receiver->view, (SubGhzViewReceiverModel * model) { |             subghz_receiver->view, (SubGhzViewReceiverModel * model) { | ||||||
|                 if(model->history_item != 0) { |                 if(model->history_item != 0) { | ||||||
|                     subghz_receiver->callback( |                     subghz_receiver->callback( | ||||||
|                         SubGhzCustomEventViewReceverOK, subghz_receiver->context); |                         SubGhzCustomEventViewReceiverOK, subghz_receiver->context); | ||||||
|                 } |                 } | ||||||
|                 return false; |                 return false; | ||||||
|             }); |             }); | ||||||
|  | |||||||
							
								
								
									
										171
									
								
								applications/tests/storage/storage_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								applications/tests/storage/storage_test.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,171 @@ | |||||||
|  | #include "../minunit.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal_delay.h> | ||||||
|  | #include <storage/storage.h> | ||||||
|  | 
 | ||||||
|  | #define STORAGE_LOCKED_FILE "/ext/locked_file.test" | ||||||
|  | #define STORAGE_LOCKED_DIR "/int" | ||||||
|  | 
 | ||||||
|  | static void storage_file_open_lock_setup() { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     File* file = storage_file_alloc(storage); | ||||||
|  |     storage_simply_remove(storage, STORAGE_LOCKED_FILE); | ||||||
|  |     mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_WRITE, FSOM_CREATE_NEW)); | ||||||
|  |     mu_check(storage_file_write(file, "0123", 4) == 4); | ||||||
|  |     mu_check(storage_file_close(file)); | ||||||
|  |     storage_file_free(file); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void storage_file_open_lock_teardown() { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     mu_check(storage_simply_remove(storage, STORAGE_LOCKED_FILE)); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int32_t storage_file_locker(void* ctx) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     osSemaphoreId_t semaphore = ctx; | ||||||
|  |     File* file = storage_file_alloc(storage); | ||||||
|  |     furi_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); | ||||||
|  |     osSemaphoreRelease(semaphore); | ||||||
|  |     furi_hal_delay_ms(1000); | ||||||
|  | 
 | ||||||
|  |     furi_check(storage_file_close(file)); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     storage_file_free(file); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(storage_file_open_lock) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     bool result = false; | ||||||
|  |     osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); | ||||||
|  |     File* file = storage_file_alloc(storage); | ||||||
|  | 
 | ||||||
|  |     // file_locker thread start
 | ||||||
|  |     FuriThread* locker_thread = furi_thread_alloc(); | ||||||
|  |     furi_thread_set_name(locker_thread, "StorageFileLocker"); | ||||||
|  |     furi_thread_set_stack_size(locker_thread, 2048); | ||||||
|  |     furi_thread_set_context(locker_thread, semaphore); | ||||||
|  |     furi_thread_set_callback(locker_thread, storage_file_locker); | ||||||
|  |     mu_check(furi_thread_start(locker_thread)); | ||||||
|  | 
 | ||||||
|  |     // wait for file lock
 | ||||||
|  |     osSemaphoreAcquire(semaphore, osWaitForever); | ||||||
|  |     osSemaphoreDelete(semaphore); | ||||||
|  | 
 | ||||||
|  |     result = storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING); | ||||||
|  |     storage_file_close(file); | ||||||
|  | 
 | ||||||
|  |     // file_locker thread stop
 | ||||||
|  |     mu_check(furi_thread_join(locker_thread) == osOK); | ||||||
|  |     furi_thread_free(locker_thread); | ||||||
|  | 
 | ||||||
|  |     // clean data
 | ||||||
|  |     storage_file_free(file); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | 
 | ||||||
|  |     mu_assert(result, "cannot open locked file"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(storage_file_open_close) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     File* file; | ||||||
|  | 
 | ||||||
|  |     file = storage_file_alloc(storage); | ||||||
|  |     mu_check(storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); | ||||||
|  |     storage_file_close(file); | ||||||
|  |     storage_file_free(file); | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < 10; i++) { | ||||||
|  |         file = storage_file_alloc(storage); | ||||||
|  |         mu_check( | ||||||
|  |             storage_file_open(file, STORAGE_LOCKED_FILE, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)); | ||||||
|  |         storage_file_free(file); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST_SUITE(storage_file) { | ||||||
|  |     storage_file_open_lock_setup(); | ||||||
|  |     MU_RUN_TEST(storage_file_open_close); | ||||||
|  |     MU_RUN_TEST(storage_file_open_lock); | ||||||
|  |     storage_file_open_lock_teardown(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(storage_dir_open_close) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     File* file; | ||||||
|  | 
 | ||||||
|  |     file = storage_file_alloc(storage); | ||||||
|  |     mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); | ||||||
|  |     storage_dir_close(file); | ||||||
|  |     storage_file_free(file); | ||||||
|  | 
 | ||||||
|  |     for(size_t i = 0; i < 10; i++) { | ||||||
|  |         file = storage_file_alloc(storage); | ||||||
|  |         mu_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); | ||||||
|  |         storage_file_free(file); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int32_t storage_dir_locker(void* ctx) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     osSemaphoreId_t semaphore = ctx; | ||||||
|  |     File* file = storage_file_alloc(storage); | ||||||
|  |     furi_check(storage_dir_open(file, STORAGE_LOCKED_DIR)); | ||||||
|  |     osSemaphoreRelease(semaphore); | ||||||
|  |     furi_hal_delay_ms(1000); | ||||||
|  | 
 | ||||||
|  |     furi_check(storage_dir_close(file)); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  |     storage_file_free(file); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(storage_dir_open_lock) { | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     bool result = false; | ||||||
|  |     osSemaphoreId_t semaphore = osSemaphoreNew(1, 0, NULL); | ||||||
|  |     File* file = storage_file_alloc(storage); | ||||||
|  | 
 | ||||||
|  |     // file_locker thread start
 | ||||||
|  |     FuriThread* locker_thread = furi_thread_alloc(); | ||||||
|  |     furi_thread_set_name(locker_thread, "StorageDirLocker"); | ||||||
|  |     furi_thread_set_stack_size(locker_thread, 2048); | ||||||
|  |     furi_thread_set_context(locker_thread, semaphore); | ||||||
|  |     furi_thread_set_callback(locker_thread, storage_dir_locker); | ||||||
|  |     mu_check(furi_thread_start(locker_thread)); | ||||||
|  | 
 | ||||||
|  |     // wait for dir lock
 | ||||||
|  |     osSemaphoreAcquire(semaphore, osWaitForever); | ||||||
|  |     osSemaphoreDelete(semaphore); | ||||||
|  | 
 | ||||||
|  |     result = storage_dir_open(file, STORAGE_LOCKED_DIR); | ||||||
|  |     storage_dir_close(file); | ||||||
|  | 
 | ||||||
|  |     // file_locker thread stop
 | ||||||
|  |     mu_check(furi_thread_join(locker_thread) == osOK); | ||||||
|  |     furi_thread_free(locker_thread); | ||||||
|  | 
 | ||||||
|  |     // clean data
 | ||||||
|  |     storage_file_free(file); | ||||||
|  |     furi_record_close("storage"); | ||||||
|  | 
 | ||||||
|  |     mu_assert(result, "cannot open locked dir"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST_SUITE(storage_dir) { | ||||||
|  |     MU_RUN_TEST(storage_dir_open_close); | ||||||
|  |     MU_RUN_TEST(storage_dir_open_lock); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int run_minunit_test_storage() { | ||||||
|  |     MU_RUN_SUITE(storage_file); | ||||||
|  |     MU_RUN_SUITE(storage_dir); | ||||||
|  |     return MU_EXIT_CODE; | ||||||
|  | } | ||||||
| @ -16,6 +16,7 @@ int run_minunit_test_rpc(); | |||||||
| int run_minunit_test_flipper_format(); | int run_minunit_test_flipper_format(); | ||||||
| int run_minunit_test_flipper_format_string(); | int run_minunit_test_flipper_format_string(); | ||||||
| int run_minunit_test_stream(); | int run_minunit_test_stream(); | ||||||
|  | int run_minunit_test_storage(); | ||||||
| 
 | 
 | ||||||
| void minunit_print_progress(void) { | void minunit_print_progress(void) { | ||||||
|     static char progress[] = {'\\', '|', '/', '-'}; |     static char progress[] = {'\\', '|', '/', '-'}; | ||||||
| @ -53,11 +54,12 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) { | |||||||
|         uint32_t cycle_counter = DWT->CYCCNT; |         uint32_t cycle_counter = DWT->CYCCNT; | ||||||
| 
 | 
 | ||||||
|         test_result |= run_minunit(); |         test_result |= run_minunit(); | ||||||
|         test_result |= run_minunit_test_infrared_decoder_encoder(); |         test_result |= run_minunit_test_storage(); | ||||||
|         test_result |= run_minunit_test_rpc(); |  | ||||||
|         test_result |= run_minunit_test_stream(); |         test_result |= run_minunit_test_stream(); | ||||||
|         test_result |= run_minunit_test_flipper_format(); |         test_result |= run_minunit_test_flipper_format(); | ||||||
|         test_result |= run_minunit_test_flipper_format_string(); |         test_result |= run_minunit_test_flipper_format_string(); | ||||||
|  |         test_result |= run_minunit_test_infrared_decoder_encoder(); | ||||||
|  |         test_result |= run_minunit_test_rpc(); | ||||||
|         cycle_counter = (DWT->CYCCNT - cycle_counter); |         cycle_counter = (DWT->CYCCNT - cycle_counter); | ||||||
| 
 | 
 | ||||||
|         FURI_LOG_I(TAG, "Consumed: %0.2fs", (float)cycle_counter / (SystemCoreClock)); |         FURI_LOG_I(TAG, "Consumed: %0.2fs", (float)cycle_counter / (SystemCoreClock)); | ||||||
|  | |||||||
| @ -98,6 +98,11 @@ typedef struct _PB_Main { | |||||||
|         PB_System_PlayAudiovisualAlertRequest system_play_audiovisual_alert_request; |         PB_System_PlayAudiovisualAlertRequest system_play_audiovisual_alert_request; | ||||||
|         PB_System_ProtobufVersionRequest system_protobuf_version_request; |         PB_System_ProtobufVersionRequest system_protobuf_version_request; | ||||||
|         PB_System_ProtobufVersionResponse system_protobuf_version_response; |         PB_System_ProtobufVersionResponse system_protobuf_version_response; | ||||||
|  |         PB_System_UpdateRequest system_update_request; | ||||||
|  |         PB_Storage_BackupCreateRequest storage_backup_create_request; | ||||||
|  |         PB_Storage_BackupRestoreRequest storage_backup_restore_request; | ||||||
|  |         PB_System_PowerInfoRequest system_power_info_request; | ||||||
|  |         PB_System_PowerInfoResponse system_power_info_response; | ||||||
|     } content;  |     } content;  | ||||||
| } PB_Main; | } PB_Main; | ||||||
| 
 | 
 | ||||||
| @ -161,6 +166,11 @@ extern "C" { | |||||||
| #define PB_Main_system_play_audiovisual_alert_request_tag 38 | #define PB_Main_system_play_audiovisual_alert_request_tag 38 | ||||||
| #define PB_Main_system_protobuf_version_request_tag 39 | #define PB_Main_system_protobuf_version_request_tag 39 | ||||||
| #define PB_Main_system_protobuf_version_response_tag 40 | #define PB_Main_system_protobuf_version_response_tag 40 | ||||||
|  | #define PB_Main_system_update_request_tag        41 | ||||||
|  | #define PB_Main_storage_backup_create_request_tag 42 | ||||||
|  | #define PB_Main_storage_backup_restore_request_tag 43 | ||||||
|  | #define PB_Main_system_power_info_request_tag    44 | ||||||
|  | #define PB_Main_system_power_info_response_tag   45 | ||||||
| 
 | 
 | ||||||
| /* Struct field encoding specification for nanopb */ | /* Struct field encoding specification for nanopb */ | ||||||
| #define PB_Empty_FIELDLIST(X, a) \ | #define PB_Empty_FIELDLIST(X, a) \ | ||||||
| @ -213,7 +223,12 @@ X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_get_datetime_response,content | |||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_set_datetime_request,content.system_set_datetime_request),  37) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_set_datetime_request,content.system_set_datetime_request),  37) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_play_audiovisual_alert_request,content.system_play_audiovisual_alert_request),  38) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_play_audiovisual_alert_request,content.system_play_audiovisual_alert_request),  38) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_protobuf_version_request,content.system_protobuf_version_request),  39) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_protobuf_version_request,content.system_protobuf_version_request),  39) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_protobuf_version_response,content.system_protobuf_version_response),  40) | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_protobuf_version_response,content.system_protobuf_version_response),  40) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_update_request,content.system_update_request),  41) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_backup_create_request,content.storage_backup_create_request),  42) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_backup_restore_request,content.storage_backup_restore_request),  43) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_power_info_request,content.system_power_info_request),  44) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_power_info_response,content.system_power_info_response),  45) | ||||||
| #define PB_Main_CALLBACK NULL | #define PB_Main_CALLBACK NULL | ||||||
| #define PB_Main_DEFAULT NULL | #define PB_Main_DEFAULT NULL | ||||||
| #define PB_Main_content_empty_MSGTYPE PB_Empty | #define PB_Main_content_empty_MSGTYPE PB_Empty | ||||||
| @ -253,6 +268,11 @@ X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_protobuf_version_response,con | |||||||
| #define PB_Main_content_system_play_audiovisual_alert_request_MSGTYPE PB_System_PlayAudiovisualAlertRequest | #define PB_Main_content_system_play_audiovisual_alert_request_MSGTYPE PB_System_PlayAudiovisualAlertRequest | ||||||
| #define PB_Main_content_system_protobuf_version_request_MSGTYPE PB_System_ProtobufVersionRequest | #define PB_Main_content_system_protobuf_version_request_MSGTYPE PB_System_ProtobufVersionRequest | ||||||
| #define PB_Main_content_system_protobuf_version_response_MSGTYPE PB_System_ProtobufVersionResponse | #define PB_Main_content_system_protobuf_version_response_MSGTYPE PB_System_ProtobufVersionResponse | ||||||
|  | #define PB_Main_content_system_update_request_MSGTYPE PB_System_UpdateRequest | ||||||
|  | #define PB_Main_content_storage_backup_create_request_MSGTYPE PB_Storage_BackupCreateRequest | ||||||
|  | #define PB_Main_content_storage_backup_restore_request_MSGTYPE PB_Storage_BackupRestoreRequest | ||||||
|  | #define PB_Main_content_system_power_info_request_MSGTYPE PB_System_PowerInfoRequest | ||||||
|  | #define PB_Main_content_system_power_info_response_MSGTYPE PB_System_PowerInfoResponse | ||||||
| 
 | 
 | ||||||
| extern const pb_msgdesc_t PB_Empty_msg; | extern const pb_msgdesc_t PB_Empty_msg; | ||||||
| extern const pb_msgdesc_t PB_StopSession_msg; | extern const pb_msgdesc_t PB_StopSession_msg; | ||||||
| @ -266,9 +286,9 @@ extern const pb_msgdesc_t PB_Main_msg; | |||||||
| /* Maximum encoded size of messages (where known) */ | /* Maximum encoded size of messages (where known) */ | ||||||
| #define PB_Empty_size                            0 | #define PB_Empty_size                            0 | ||||||
| #define PB_StopSession_size                      0 | #define PB_StopSession_size                      0 | ||||||
| #if defined(PB_System_PingRequest_size) && defined(PB_System_PingResponse_size) && defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_StartRequest_size) && defined(PB_Gui_ScreenFrame_size) && defined(PB_Storage_StatRequest_size) && defined(PB_Storage_StatResponse_size) && defined(PB_Gui_StartVirtualDisplayRequest_size) && defined(PB_Storage_InfoRequest_size) && defined(PB_Storage_RenameRequest_size) && defined(PB_System_DeviceInfoResponse_size) | #if defined(PB_System_PingRequest_size) && defined(PB_System_PingResponse_size) && defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_StartRequest_size) && defined(PB_Gui_ScreenFrame_size) && defined(PB_Storage_StatRequest_size) && defined(PB_Storage_StatResponse_size) && defined(PB_Gui_StartVirtualDisplayRequest_size) && defined(PB_Storage_InfoRequest_size) && defined(PB_Storage_RenameRequest_size) && defined(PB_System_DeviceInfoResponse_size) && defined(PB_System_UpdateRequest_size) && defined(PB_Storage_BackupCreateRequest_size) && defined(PB_Storage_BackupRestoreRequest_size) && defined(PB_System_PowerInfoResponse_size) | ||||||
| #define PB_Main_size                             (10 + sizeof(union PB_Main_content_size_union)) | #define PB_Main_size                             (10 + sizeof(union PB_Main_content_size_union)) | ||||||
| union PB_Main_content_size_union {char f5[(6 + PB_System_PingRequest_size)]; char f6[(6 + PB_System_PingResponse_size)]; char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_StartRequest_size)]; char f22[(7 + PB_Gui_ScreenFrame_size)]; char f24[(7 + PB_Storage_StatRequest_size)]; char f25[(7 + PB_Storage_StatResponse_size)]; char f26[(7 + PB_Gui_StartVirtualDisplayRequest_size)]; char f28[(7 + PB_Storage_InfoRequest_size)]; char f30[(7 + PB_Storage_RenameRequest_size)]; char f33[(7 + PB_System_DeviceInfoResponse_size)]; char f0[36];}; | union PB_Main_content_size_union {char f5[(6 + PB_System_PingRequest_size)]; char f6[(6 + PB_System_PingResponse_size)]; char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_StartRequest_size)]; char f22[(7 + PB_Gui_ScreenFrame_size)]; char f24[(7 + PB_Storage_StatRequest_size)]; char f25[(7 + PB_Storage_StatResponse_size)]; char f26[(7 + PB_Gui_StartVirtualDisplayRequest_size)]; char f28[(7 + PB_Storage_InfoRequest_size)]; char f30[(7 + PB_Storage_RenameRequest_size)]; char f33[(7 + PB_System_DeviceInfoResponse_size)]; char f41[(7 + PB_System_UpdateRequest_size)]; char f42[(7 + PB_Storage_BackupCreateRequest_size)]; char f43[(7 + PB_Storage_BackupRestoreRequest_size)]; char f45[(7 + PB_System_PowerInfoResponse_size)]; char f0[36];}; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #define PROTOBUF_MAJOR_VERSION 0 | #define PROTOBUF_MAJOR_VERSION 0 | ||||||
| #define PROTOBUF_MINOR_VERSION 3 | #define PROTOBUF_MINOR_VERSION 5 | ||||||
|  | |||||||
| @ -51,5 +51,11 @@ PB_BIND(PB_Storage_Md5sumResponse, PB_Storage_Md5sumResponse, AUTO) | |||||||
| PB_BIND(PB_Storage_RenameRequest, PB_Storage_RenameRequest, AUTO) | PB_BIND(PB_Storage_RenameRequest, PB_Storage_RenameRequest, AUTO) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | PB_BIND(PB_Storage_BackupCreateRequest, PB_Storage_BackupCreateRequest, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PB_BIND(PB_Storage_BackupRestoreRequest, PB_Storage_BackupRestoreRequest, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -16,6 +16,14 @@ typedef enum _PB_Storage_File_FileType { | |||||||
| } PB_Storage_File_FileType; | } PB_Storage_File_FileType; | ||||||
| 
 | 
 | ||||||
| /* Struct definitions */ | /* Struct definitions */ | ||||||
|  | typedef struct _PB_Storage_BackupCreateRequest {  | ||||||
|  |     char *archive_path;  | ||||||
|  | } PB_Storage_BackupCreateRequest; | ||||||
|  | 
 | ||||||
|  | typedef struct _PB_Storage_BackupRestoreRequest {  | ||||||
|  |     char *archive_path;  | ||||||
|  | } PB_Storage_BackupRestoreRequest; | ||||||
|  | 
 | ||||||
| typedef struct _PB_Storage_InfoRequest {  | typedef struct _PB_Storage_InfoRequest {  | ||||||
|     char *path;  |     char *path;  | ||||||
| } PB_Storage_InfoRequest; | } PB_Storage_InfoRequest; | ||||||
| @ -114,6 +122,8 @@ extern "C" { | |||||||
| #define PB_Storage_Md5sumRequest_init_default    {NULL} | #define PB_Storage_Md5sumRequest_init_default    {NULL} | ||||||
| #define PB_Storage_Md5sumResponse_init_default   {""} | #define PB_Storage_Md5sumResponse_init_default   {""} | ||||||
| #define PB_Storage_RenameRequest_init_default    {NULL, NULL} | #define PB_Storage_RenameRequest_init_default    {NULL, NULL} | ||||||
|  | #define PB_Storage_BackupCreateRequest_init_default {NULL} | ||||||
|  | #define PB_Storage_BackupRestoreRequest_init_default {NULL} | ||||||
| #define PB_Storage_File_init_zero                {_PB_Storage_File_FileType_MIN, NULL, 0, NULL} | #define PB_Storage_File_init_zero                {_PB_Storage_File_FileType_MIN, NULL, 0, NULL} | ||||||
| #define PB_Storage_InfoRequest_init_zero         {NULL} | #define PB_Storage_InfoRequest_init_zero         {NULL} | ||||||
| #define PB_Storage_InfoResponse_init_zero        {0, 0} | #define PB_Storage_InfoResponse_init_zero        {0, 0} | ||||||
| @ -129,8 +139,12 @@ extern "C" { | |||||||
| #define PB_Storage_Md5sumRequest_init_zero       {NULL} | #define PB_Storage_Md5sumRequest_init_zero       {NULL} | ||||||
| #define PB_Storage_Md5sumResponse_init_zero      {""} | #define PB_Storage_Md5sumResponse_init_zero      {""} | ||||||
| #define PB_Storage_RenameRequest_init_zero       {NULL, NULL} | #define PB_Storage_RenameRequest_init_zero       {NULL, NULL} | ||||||
|  | #define PB_Storage_BackupCreateRequest_init_zero {NULL} | ||||||
|  | #define PB_Storage_BackupRestoreRequest_init_zero {NULL} | ||||||
| 
 | 
 | ||||||
| /* Field tags (for use in manual encoding/decoding) */ | /* Field tags (for use in manual encoding/decoding) */ | ||||||
|  | #define PB_Storage_BackupCreateRequest_archive_path_tag 1 | ||||||
|  | #define PB_Storage_BackupRestoreRequest_archive_path_tag 1 | ||||||
| #define PB_Storage_InfoRequest_path_tag          1 | #define PB_Storage_InfoRequest_path_tag          1 | ||||||
| #define PB_Storage_ListRequest_path_tag          1 | #define PB_Storage_ListRequest_path_tag          1 | ||||||
| #define PB_Storage_Md5sumRequest_path_tag        1 | #define PB_Storage_Md5sumRequest_path_tag        1 | ||||||
| @ -241,6 +255,16 @@ X(a, POINTER,  SINGULAR, STRING,   new_path,          2) | |||||||
| #define PB_Storage_RenameRequest_CALLBACK NULL | #define PB_Storage_RenameRequest_CALLBACK NULL | ||||||
| #define PB_Storage_RenameRequest_DEFAULT NULL | #define PB_Storage_RenameRequest_DEFAULT NULL | ||||||
| 
 | 
 | ||||||
|  | #define PB_Storage_BackupCreateRequest_FIELDLIST(X, a) \ | ||||||
|  | X(a, POINTER,  SINGULAR, STRING,   archive_path,      1) | ||||||
|  | #define PB_Storage_BackupCreateRequest_CALLBACK NULL | ||||||
|  | #define PB_Storage_BackupCreateRequest_DEFAULT NULL | ||||||
|  | 
 | ||||||
|  | #define PB_Storage_BackupRestoreRequest_FIELDLIST(X, a) \ | ||||||
|  | X(a, POINTER,  SINGULAR, STRING,   archive_path,      1) | ||||||
|  | #define PB_Storage_BackupRestoreRequest_CALLBACK NULL | ||||||
|  | #define PB_Storage_BackupRestoreRequest_DEFAULT NULL | ||||||
|  | 
 | ||||||
| extern const pb_msgdesc_t PB_Storage_File_msg; | extern const pb_msgdesc_t PB_Storage_File_msg; | ||||||
| extern const pb_msgdesc_t PB_Storage_InfoRequest_msg; | extern const pb_msgdesc_t PB_Storage_InfoRequest_msg; | ||||||
| extern const pb_msgdesc_t PB_Storage_InfoResponse_msg; | extern const pb_msgdesc_t PB_Storage_InfoResponse_msg; | ||||||
| @ -256,6 +280,8 @@ extern const pb_msgdesc_t PB_Storage_MkdirRequest_msg; | |||||||
| extern const pb_msgdesc_t PB_Storage_Md5sumRequest_msg; | extern const pb_msgdesc_t PB_Storage_Md5sumRequest_msg; | ||||||
| extern const pb_msgdesc_t PB_Storage_Md5sumResponse_msg; | extern const pb_msgdesc_t PB_Storage_Md5sumResponse_msg; | ||||||
| extern const pb_msgdesc_t PB_Storage_RenameRequest_msg; | extern const pb_msgdesc_t PB_Storage_RenameRequest_msg; | ||||||
|  | extern const pb_msgdesc_t PB_Storage_BackupCreateRequest_msg; | ||||||
|  | extern const pb_msgdesc_t PB_Storage_BackupRestoreRequest_msg; | ||||||
| 
 | 
 | ||||||
| /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ | ||||||
| #define PB_Storage_File_fields &PB_Storage_File_msg | #define PB_Storage_File_fields &PB_Storage_File_msg | ||||||
| @ -273,6 +299,8 @@ extern const pb_msgdesc_t PB_Storage_RenameRequest_msg; | |||||||
| #define PB_Storage_Md5sumRequest_fields &PB_Storage_Md5sumRequest_msg | #define PB_Storage_Md5sumRequest_fields &PB_Storage_Md5sumRequest_msg | ||||||
| #define PB_Storage_Md5sumResponse_fields &PB_Storage_Md5sumResponse_msg | #define PB_Storage_Md5sumResponse_fields &PB_Storage_Md5sumResponse_msg | ||||||
| #define PB_Storage_RenameRequest_fields &PB_Storage_RenameRequest_msg | #define PB_Storage_RenameRequest_fields &PB_Storage_RenameRequest_msg | ||||||
|  | #define PB_Storage_BackupCreateRequest_fields &PB_Storage_BackupCreateRequest_msg | ||||||
|  | #define PB_Storage_BackupRestoreRequest_fields &PB_Storage_BackupRestoreRequest_msg | ||||||
| 
 | 
 | ||||||
| /* Maximum encoded size of messages (where known) */ | /* Maximum encoded size of messages (where known) */ | ||||||
| /* PB_Storage_File_size depends on runtime parameters */ | /* PB_Storage_File_size depends on runtime parameters */ | ||||||
| @ -288,6 +316,8 @@ extern const pb_msgdesc_t PB_Storage_RenameRequest_msg; | |||||||
| /* PB_Storage_MkdirRequest_size depends on runtime parameters */ | /* PB_Storage_MkdirRequest_size depends on runtime parameters */ | ||||||
| /* PB_Storage_Md5sumRequest_size depends on runtime parameters */ | /* PB_Storage_Md5sumRequest_size depends on runtime parameters */ | ||||||
| /* PB_Storage_RenameRequest_size depends on runtime parameters */ | /* PB_Storage_RenameRequest_size depends on runtime parameters */ | ||||||
|  | /* PB_Storage_BackupCreateRequest_size depends on runtime parameters */ | ||||||
|  | /* PB_Storage_BackupRestoreRequest_size depends on runtime parameters */ | ||||||
| #define PB_Storage_InfoResponse_size             22 | #define PB_Storage_InfoResponse_size             22 | ||||||
| #define PB_Storage_Md5sumResponse_size           34 | #define PB_Storage_Md5sumResponse_size           34 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -45,5 +45,14 @@ PB_BIND(PB_System_ProtobufVersionRequest, PB_System_ProtobufVersionRequest, AUTO | |||||||
| PB_BIND(PB_System_ProtobufVersionResponse, PB_System_ProtobufVersionResponse, AUTO) | PB_BIND(PB_System_ProtobufVersionResponse, PB_System_ProtobufVersionResponse, AUTO) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | PB_BIND(PB_System_UpdateRequest, PB_System_UpdateRequest, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PB_BIND(PB_System_PowerInfoRequest, PB_System_PowerInfoRequest, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PB_BIND(PB_System_PowerInfoResponse, PB_System_PowerInfoResponse, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -45,10 +45,23 @@ typedef struct _PB_System_PlayAudiovisualAlertRequest { | |||||||
|     char dummy_field; |     char dummy_field; | ||||||
| } PB_System_PlayAudiovisualAlertRequest; | } PB_System_PlayAudiovisualAlertRequest; | ||||||
| 
 | 
 | ||||||
|  | typedef struct _PB_System_PowerInfoRequest {  | ||||||
|  |     char dummy_field; | ||||||
|  | } PB_System_PowerInfoRequest; | ||||||
|  | 
 | ||||||
|  | typedef struct _PB_System_PowerInfoResponse {  | ||||||
|  |     char *key;  | ||||||
|  |     char *value;  | ||||||
|  | } PB_System_PowerInfoResponse; | ||||||
|  | 
 | ||||||
| typedef struct _PB_System_ProtobufVersionRequest {  | typedef struct _PB_System_ProtobufVersionRequest {  | ||||||
|     char dummy_field; |     char dummy_field; | ||||||
| } PB_System_ProtobufVersionRequest; | } PB_System_ProtobufVersionRequest; | ||||||
| 
 | 
 | ||||||
|  | typedef struct _PB_System_UpdateRequest {  | ||||||
|  |     char *update_folder;  | ||||||
|  | } PB_System_UpdateRequest; | ||||||
|  | 
 | ||||||
| typedef struct _PB_System_DateTime {  | typedef struct _PB_System_DateTime {  | ||||||
|     /* Time */ |     /* Time */ | ||||||
|     uint8_t hour; /* *< Hour in 24H format: 0-23 */ |     uint8_t hour; /* *< Hour in 24H format: 0-23 */ | ||||||
| @ -105,6 +118,9 @@ extern "C" { | |||||||
| #define PB_System_PlayAudiovisualAlertRequest_init_default {0} | #define PB_System_PlayAudiovisualAlertRequest_init_default {0} | ||||||
| #define PB_System_ProtobufVersionRequest_init_default {0} | #define PB_System_ProtobufVersionRequest_init_default {0} | ||||||
| #define PB_System_ProtobufVersionResponse_init_default {0, 0} | #define PB_System_ProtobufVersionResponse_init_default {0, 0} | ||||||
|  | #define PB_System_UpdateRequest_init_default     {NULL} | ||||||
|  | #define PB_System_PowerInfoRequest_init_default  {0} | ||||||
|  | #define PB_System_PowerInfoResponse_init_default {NULL, NULL} | ||||||
| #define PB_System_PingRequest_init_zero          {NULL} | #define PB_System_PingRequest_init_zero          {NULL} | ||||||
| #define PB_System_PingResponse_init_zero         {NULL} | #define PB_System_PingResponse_init_zero         {NULL} | ||||||
| #define PB_System_RebootRequest_init_zero        {_PB_System_RebootRequest_RebootMode_MIN} | #define PB_System_RebootRequest_init_zero        {_PB_System_RebootRequest_RebootMode_MIN} | ||||||
| @ -118,12 +134,18 @@ extern "C" { | |||||||
| #define PB_System_PlayAudiovisualAlertRequest_init_zero {0} | #define PB_System_PlayAudiovisualAlertRequest_init_zero {0} | ||||||
| #define PB_System_ProtobufVersionRequest_init_zero {0} | #define PB_System_ProtobufVersionRequest_init_zero {0} | ||||||
| #define PB_System_ProtobufVersionResponse_init_zero {0, 0} | #define PB_System_ProtobufVersionResponse_init_zero {0, 0} | ||||||
|  | #define PB_System_UpdateRequest_init_zero        {NULL} | ||||||
|  | #define PB_System_PowerInfoRequest_init_zero     {0} | ||||||
|  | #define PB_System_PowerInfoResponse_init_zero    {NULL, NULL} | ||||||
| 
 | 
 | ||||||
| /* Field tags (for use in manual encoding/decoding) */ | /* Field tags (for use in manual encoding/decoding) */ | ||||||
| #define PB_System_DeviceInfoResponse_key_tag     1 | #define PB_System_DeviceInfoResponse_key_tag     1 | ||||||
| #define PB_System_DeviceInfoResponse_value_tag   2 | #define PB_System_DeviceInfoResponse_value_tag   2 | ||||||
| #define PB_System_PingRequest_data_tag           1 | #define PB_System_PingRequest_data_tag           1 | ||||||
| #define PB_System_PingResponse_data_tag          1 | #define PB_System_PingResponse_data_tag          1 | ||||||
|  | #define PB_System_PowerInfoResponse_key_tag      1 | ||||||
|  | #define PB_System_PowerInfoResponse_value_tag    2 | ||||||
|  | #define PB_System_UpdateRequest_update_folder_tag 1 | ||||||
| #define PB_System_DateTime_hour_tag              1 | #define PB_System_DateTime_hour_tag              1 | ||||||
| #define PB_System_DateTime_minute_tag            2 | #define PB_System_DateTime_minute_tag            2 | ||||||
| #define PB_System_DateTime_second_tag            3 | #define PB_System_DateTime_second_tag            3 | ||||||
| @ -213,6 +235,22 @@ X(a, STATIC,   SINGULAR, UINT32,   minor,             2) | |||||||
| #define PB_System_ProtobufVersionResponse_CALLBACK NULL | #define PB_System_ProtobufVersionResponse_CALLBACK NULL | ||||||
| #define PB_System_ProtobufVersionResponse_DEFAULT NULL | #define PB_System_ProtobufVersionResponse_DEFAULT NULL | ||||||
| 
 | 
 | ||||||
|  | #define PB_System_UpdateRequest_FIELDLIST(X, a) \ | ||||||
|  | X(a, POINTER,  SINGULAR, STRING,   update_folder,     1) | ||||||
|  | #define PB_System_UpdateRequest_CALLBACK NULL | ||||||
|  | #define PB_System_UpdateRequest_DEFAULT NULL | ||||||
|  | 
 | ||||||
|  | #define PB_System_PowerInfoRequest_FIELDLIST(X, a) \ | ||||||
|  | 
 | ||||||
|  | #define PB_System_PowerInfoRequest_CALLBACK NULL | ||||||
|  | #define PB_System_PowerInfoRequest_DEFAULT NULL | ||||||
|  | 
 | ||||||
|  | #define PB_System_PowerInfoResponse_FIELDLIST(X, a) \ | ||||||
|  | X(a, POINTER,  SINGULAR, STRING,   key,               1) \ | ||||||
|  | X(a, POINTER,  SINGULAR, STRING,   value,             2) | ||||||
|  | #define PB_System_PowerInfoResponse_CALLBACK NULL | ||||||
|  | #define PB_System_PowerInfoResponse_DEFAULT NULL | ||||||
|  | 
 | ||||||
| extern const pb_msgdesc_t PB_System_PingRequest_msg; | extern const pb_msgdesc_t PB_System_PingRequest_msg; | ||||||
| extern const pb_msgdesc_t PB_System_PingResponse_msg; | extern const pb_msgdesc_t PB_System_PingResponse_msg; | ||||||
| extern const pb_msgdesc_t PB_System_RebootRequest_msg; | extern const pb_msgdesc_t PB_System_RebootRequest_msg; | ||||||
| @ -226,6 +264,9 @@ extern const pb_msgdesc_t PB_System_DateTime_msg; | |||||||
| extern const pb_msgdesc_t PB_System_PlayAudiovisualAlertRequest_msg; | extern const pb_msgdesc_t PB_System_PlayAudiovisualAlertRequest_msg; | ||||||
| extern const pb_msgdesc_t PB_System_ProtobufVersionRequest_msg; | extern const pb_msgdesc_t PB_System_ProtobufVersionRequest_msg; | ||||||
| extern const pb_msgdesc_t PB_System_ProtobufVersionResponse_msg; | extern const pb_msgdesc_t PB_System_ProtobufVersionResponse_msg; | ||||||
|  | extern const pb_msgdesc_t PB_System_UpdateRequest_msg; | ||||||
|  | extern const pb_msgdesc_t PB_System_PowerInfoRequest_msg; | ||||||
|  | extern const pb_msgdesc_t PB_System_PowerInfoResponse_msg; | ||||||
| 
 | 
 | ||||||
| /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ | ||||||
| #define PB_System_PingRequest_fields &PB_System_PingRequest_msg | #define PB_System_PingRequest_fields &PB_System_PingRequest_msg | ||||||
| @ -241,17 +282,23 @@ extern const pb_msgdesc_t PB_System_ProtobufVersionResponse_msg; | |||||||
| #define PB_System_PlayAudiovisualAlertRequest_fields &PB_System_PlayAudiovisualAlertRequest_msg | #define PB_System_PlayAudiovisualAlertRequest_fields &PB_System_PlayAudiovisualAlertRequest_msg | ||||||
| #define PB_System_ProtobufVersionRequest_fields &PB_System_ProtobufVersionRequest_msg | #define PB_System_ProtobufVersionRequest_fields &PB_System_ProtobufVersionRequest_msg | ||||||
| #define PB_System_ProtobufVersionResponse_fields &PB_System_ProtobufVersionResponse_msg | #define PB_System_ProtobufVersionResponse_fields &PB_System_ProtobufVersionResponse_msg | ||||||
|  | #define PB_System_UpdateRequest_fields &PB_System_UpdateRequest_msg | ||||||
|  | #define PB_System_PowerInfoRequest_fields &PB_System_PowerInfoRequest_msg | ||||||
|  | #define PB_System_PowerInfoResponse_fields &PB_System_PowerInfoResponse_msg | ||||||
| 
 | 
 | ||||||
| /* Maximum encoded size of messages (where known) */ | /* Maximum encoded size of messages (where known) */ | ||||||
| /* PB_System_PingRequest_size depends on runtime parameters */ | /* PB_System_PingRequest_size depends on runtime parameters */ | ||||||
| /* PB_System_PingResponse_size depends on runtime parameters */ | /* PB_System_PingResponse_size depends on runtime parameters */ | ||||||
| /* PB_System_DeviceInfoResponse_size depends on runtime parameters */ | /* PB_System_DeviceInfoResponse_size depends on runtime parameters */ | ||||||
|  | /* PB_System_UpdateRequest_size depends on runtime parameters */ | ||||||
|  | /* PB_System_PowerInfoResponse_size depends on runtime parameters */ | ||||||
| #define PB_System_DateTime_size                  22 | #define PB_System_DateTime_size                  22 | ||||||
| #define PB_System_DeviceInfoRequest_size         0 | #define PB_System_DeviceInfoRequest_size         0 | ||||||
| #define PB_System_FactoryResetRequest_size       0 | #define PB_System_FactoryResetRequest_size       0 | ||||||
| #define PB_System_GetDateTimeRequest_size        0 | #define PB_System_GetDateTimeRequest_size        0 | ||||||
| #define PB_System_GetDateTimeResponse_size       24 | #define PB_System_GetDateTimeResponse_size       24 | ||||||
| #define PB_System_PlayAudiovisualAlertRequest_size 0 | #define PB_System_PlayAudiovisualAlertRequest_size 0 | ||||||
|  | #define PB_System_PowerInfoRequest_size          0 | ||||||
| #define PB_System_ProtobufVersionRequest_size    0 | #define PB_System_ProtobufVersionRequest_size    0 | ||||||
| #define PB_System_ProtobufVersionResponse_size   12 | #define PB_System_ProtobufVersionResponse_size   12 | ||||||
| #define PB_System_RebootRequest_size             2 | #define PB_System_RebootRequest_size             2 | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| Subproject commit cd11b029ac21462ea8a7615126d0a29e087c2908 | Subproject commit 0403ae1ba7a4501274da54b3aa6274f76fdd090c | ||||||
| @ -2,6 +2,7 @@ | |||||||
| #include <furi_hal_bt.h> | #include <furi_hal_bt.h> | ||||||
| #include <furi_hal_random.h> | #include <furi_hal_random.h> | ||||||
| #include <stm32wbxx_ll_cortex.h> | #include <stm32wbxx_ll_cortex.h> | ||||||
|  | #include <stm32wbxx_ll_bus.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <shci.h> | #include <shci.h> | ||||||
| 
 | 
 | ||||||
| @ -14,13 +15,16 @@ | |||||||
| #define CRYPTO_TIMEOUT (1000) | #define CRYPTO_TIMEOUT (1000) | ||||||
| 
 | 
 | ||||||
| #define CRYPTO_MODE_ENCRYPT 0U | #define CRYPTO_MODE_ENCRYPT 0U | ||||||
|  | #define CRYPTO_MODE_INIT (AES_CR_MODE_0) | ||||||
| #define CRYPTO_MODE_DECRYPT (AES_CR_MODE_1) | #define CRYPTO_MODE_DECRYPT (AES_CR_MODE_1) | ||||||
| #define CRYPTO_MODE_DECRYPT_INIT (AES_CR_MODE_0 | AES_CR_MODE_1) | #define CRYPTO_MODE_DECRYPT_INIT (AES_CR_MODE_0 | AES_CR_MODE_1) | ||||||
|  | 
 | ||||||
| #define CRYPTO_DATATYPE_32B 0U | #define CRYPTO_DATATYPE_32B 0U | ||||||
| #define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) | #define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) | ||||||
| #define CRYPTO_AES_CBC (AES_CR_CHMOD_0) | #define CRYPTO_AES_CBC (AES_CR_CHMOD_0) | ||||||
| 
 | 
 | ||||||
| static osMutexId_t furi_hal_crypto_mutex = NULL; | static osMutexId_t furi_hal_crypto_mutex = NULL; | ||||||
|  | static bool furi_hal_crypto_mode_init_done = false; | ||||||
| 
 | 
 | ||||||
| static const uint8_t enclave_signature_iv[ENCLAVE_FACTORY_KEY_SLOTS][16] = { | static const uint8_t enclave_signature_iv[ENCLAVE_FACTORY_KEY_SLOTS][16] = { | ||||||
|     {0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a}, |     {0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a}, | ||||||
| @ -176,16 +180,8 @@ bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) { | |||||||
|     return (shci_state == SHCI_Success); |     return (shci_state == SHCI_Success); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void crypto_enable() { |  | ||||||
|     SET_BIT(AES1->CR, AES_CR_EN); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void crypto_disable() { |  | ||||||
|     CLEAR_BIT(AES1->CR, AES_CR_EN); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void crypto_key_init(uint32_t* key, uint32_t* iv) { | static void crypto_key_init(uint32_t* key, uint32_t* iv) { | ||||||
|     crypto_disable(); |     CLEAR_BIT(AES1->CR, AES_CR_EN); | ||||||
|     MODIFY_REG( |     MODIFY_REG( | ||||||
|         AES1->CR, |         AES1->CR, | ||||||
|         AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, |         AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, | ||||||
| @ -249,12 +245,13 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     furi_hal_crypto_mode_init_done = false; | ||||||
|     crypto_key_init(NULL, (uint32_t*)iv); |     crypto_key_init(NULL, (uint32_t*)iv); | ||||||
| 
 | 
 | ||||||
|     if(SHCI_C2_FUS_LoadUsrKey(slot) == SHCI_Success) { |     if(SHCI_C2_FUS_LoadUsrKey(slot) == SHCI_Success) { | ||||||
|         return true; |         return true; | ||||||
|     } else { |     } else { | ||||||
|         crypto_disable(); |         CLEAR_BIT(AES1->CR, AES_CR_EN); | ||||||
|         furi_check(osMutexRelease(furi_hal_crypto_mutex) == osOK); |         furi_check(osMutexRelease(furi_hal_crypto_mutex) == osOK); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| @ -265,9 +262,16 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) { | |||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     crypto_disable(); |     CLEAR_BIT(AES1->CR, AES_CR_EN); | ||||||
| 
 | 
 | ||||||
|     SHCI_CmdStatus_t shci_state = SHCI_C2_FUS_UnloadUsrKey(slot); |     SHCI_CmdStatus_t shci_state = SHCI_C2_FUS_UnloadUsrKey(slot); | ||||||
|  |     furi_assert(shci_state == SHCI_Success); | ||||||
|  | 
 | ||||||
|  |     FURI_CRITICAL_ENTER(); | ||||||
|  |     LL_AHB2_GRP1_ForceReset(LL_AHB2_GRP1_PERIPH_AES1); | ||||||
|  |     LL_AHB2_GRP1_ReleaseReset(LL_AHB2_GRP1_PERIPH_AES1); | ||||||
|  |     FURI_CRITICAL_EXIT(); | ||||||
|  | 
 | ||||||
|     furi_check(osMutexRelease(furi_hal_crypto_mutex) == osOK); |     furi_check(osMutexRelease(furi_hal_crypto_mutex) == osOK); | ||||||
|     return (shci_state == SHCI_Success); |     return (shci_state == SHCI_Success); | ||||||
| } | } | ||||||
| @ -275,7 +279,7 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) { | |||||||
| bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { | bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { | ||||||
|     bool state = false; |     bool state = false; | ||||||
| 
 | 
 | ||||||
|     crypto_enable(); |     SET_BIT(AES1->CR, AES_CR_EN); | ||||||
| 
 | 
 | ||||||
|     MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); |     MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); | ||||||
| 
 | 
 | ||||||
| @ -290,7 +294,7 @@ bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     crypto_disable(); |     CLEAR_BIT(AES1->CR, AES_CR_EN); | ||||||
| 
 | 
 | ||||||
|     return state; |     return state; | ||||||
| } | } | ||||||
| @ -298,9 +302,28 @@ bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) | |||||||
| bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) { | bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) { | ||||||
|     bool state = false; |     bool state = false; | ||||||
| 
 | 
 | ||||||
|     MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT_INIT); |     if(!furi_hal_crypto_mode_init_done) { | ||||||
|  |         MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_INIT); | ||||||
| 
 | 
 | ||||||
|     crypto_enable(); |         SET_BIT(AES1->CR, AES_CR_EN); | ||||||
|  | 
 | ||||||
|  |         uint32_t countdown = CRYPTO_TIMEOUT; | ||||||
|  |         while(!READ_BIT(AES1->SR, AES_SR_CCF)) { | ||||||
|  |             if(LL_SYSTICK_IsActiveCounterFlag()) { | ||||||
|  |                 countdown--; | ||||||
|  |             } | ||||||
|  |             if(countdown == 0) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         SET_BIT(AES1->CR, AES_CR_CCFC); | ||||||
|  | 
 | ||||||
|  |         furi_hal_crypto_mode_init_done = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT); | ||||||
|  |     SET_BIT(AES1->CR, AES_CR_EN); | ||||||
| 
 | 
 | ||||||
|     for(size_t i = 0; i < size; i += CRYPTO_BLK_LEN) { |     for(size_t i = 0; i < size; i += CRYPTO_BLK_LEN) { | ||||||
|         size_t blk_len = size - i; |         size_t blk_len = size - i; | ||||||
| @ -313,7 +336,7 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     crypto_disable(); |     CLEAR_BIT(AES1->CR, AES_CR_EN); | ||||||
| 
 | 
 | ||||||
|     return state; |     return state; | ||||||
| } | } | ||||||
|  | |||||||
| @ -239,6 +239,13 @@ uint32_t furi_hal_power_get_battery_full_capacity() { | |||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | uint32_t furi_hal_power_get_battery_design_capacity() { | ||||||
|  |     furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); | ||||||
|  |     uint32_t ret = bq27220_get_design_capacity(&furi_hal_i2c_handle_power); | ||||||
|  |     furi_hal_i2c_release(&furi_hal_i2c_handle_power); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) { | float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) { | ||||||
|     float ret = 0.0f; |     float ret = 0.0f; | ||||||
| 
 | 
 | ||||||
| @ -399,3 +406,58 @@ void furi_hal_power_suppress_charge_exit() { | |||||||
|         furi_hal_i2c_release(&furi_hal_i2c_handle_power); |         furi_hal_i2c_release(&furi_hal_i2c_handle_power); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_power_info_get(FuriHalPowerInfoCallback out, void* context) { | ||||||
|  |     furi_assert(out); | ||||||
|  | 
 | ||||||
|  |     string_t value; | ||||||
|  |     string_init(value); | ||||||
|  | 
 | ||||||
|  |     // Power Info version
 | ||||||
|  |     out("power_info_major", "1", false, context); | ||||||
|  |     out("power_info_minor", "0", false, context); | ||||||
|  | 
 | ||||||
|  |     uint8_t charge = furi_hal_power_get_pct(); | ||||||
|  | 
 | ||||||
|  |     string_printf(value, "%u", charge); | ||||||
|  |     out("charge_level", string_get_cstr(value), false, context); | ||||||
|  | 
 | ||||||
|  |     if(furi_hal_power_is_charging()) { | ||||||
|  |         if(charge < 100) { | ||||||
|  |             string_printf(value, "charging"); | ||||||
|  |         } else { | ||||||
|  |             string_printf(value, "charged"); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         string_printf(value, "discharging"); | ||||||
|  |     } | ||||||
|  |     out("charge_state", string_get_cstr(value), false, context); | ||||||
|  | 
 | ||||||
|  |     uint16_t voltage = | ||||||
|  |         (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f); | ||||||
|  |     string_printf(value, "%u", voltage); | ||||||
|  |     out("battery_voltage", string_get_cstr(value), false, context); | ||||||
|  | 
 | ||||||
|  |     int16_t current = | ||||||
|  |         (int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f); | ||||||
|  |     string_printf(value, "%d", current); | ||||||
|  |     out("battery_current", string_get_cstr(value), false, context); | ||||||
|  | 
 | ||||||
|  |     int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); | ||||||
|  |     string_printf(value, "%d", temperature); | ||||||
|  |     out("gauge_temp", string_get_cstr(value), false, context); | ||||||
|  | 
 | ||||||
|  |     string_printf(value, "%u", furi_hal_power_get_bat_health_pct()); | ||||||
|  |     out("battery_health", string_get_cstr(value), false, context); | ||||||
|  | 
 | ||||||
|  |     string_printf(value, "%u", furi_hal_power_get_battery_remaining_capacity()); | ||||||
|  |     out("capacity_remain", string_get_cstr(value), false, context); | ||||||
|  | 
 | ||||||
|  |     string_printf(value, "%u", furi_hal_power_get_battery_full_capacity()); | ||||||
|  |     out("capacity_full", string_get_cstr(value), false, context); | ||||||
|  | 
 | ||||||
|  |     string_printf(value, "%u", furi_hal_power_get_battery_design_capacity()); | ||||||
|  |     out("capacity_design", string_get_cstr(value), true, context); | ||||||
|  | 
 | ||||||
|  |     string_clear(value); | ||||||
|  | } | ||||||
|  | |||||||
| @ -25,21 +25,32 @@ void furi_hal_speaker_start(float frequency, float volume) { | |||||||
|     if(volume > 1) volume = 1; |     if(volume > 1) volume = 1; | ||||||
|     volume = volume * volume * volume; |     volume = volume * volume * volume; | ||||||
| 
 | 
 | ||||||
|  |     uint32_t autoreload = (SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER / frequency) - 1; | ||||||
|  |     if(autoreload < 2) { | ||||||
|  |         autoreload = 2; | ||||||
|  |     } else if(autoreload > UINT16_MAX) { | ||||||
|  |         autoreload = UINT16_MAX; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     LL_TIM_InitTypeDef TIM_InitStruct = {0}; |     LL_TIM_InitTypeDef TIM_InitStruct = {0}; | ||||||
|     TIM_InitStruct.Prescaler = FURI_HAL_SPEAKER_PRESCALER - 1; |     TIM_InitStruct.Prescaler = FURI_HAL_SPEAKER_PRESCALER - 1; | ||||||
|     TIM_InitStruct.Autoreload = ((SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER) / frequency) - 1; |     TIM_InitStruct.Autoreload = autoreload; | ||||||
|     LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); |     LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); | ||||||
| 
 | 
 | ||||||
| #ifdef FURI_HAL_SPEAKER_NEW_VOLUME | #ifdef FURI_HAL_SPEAKER_NEW_VOLUME | ||||||
|     uint16_t compare_value = volume * FURI_HAL_SPEAKER_MAX_VOLUME; |     uint32_t compare_value = volume * FURI_HAL_SPEAKER_MAX_VOLUME; | ||||||
|     uint16_t clip_value = volume * TIM_InitStruct.Autoreload / 2; |     uint32_t clip_value = volume * TIM_InitStruct.Autoreload / 2; | ||||||
|     if(compare_value > clip_value) { |     if(compare_value > clip_value) { | ||||||
|         compare_value = clip_value; |         compare_value = clip_value; | ||||||
|     } |     } | ||||||
| #else | #else | ||||||
|     uint16_t compare_value = volume * TIM_InitStruct.Autoreload / 2; |     uint32_t compare_value = volume * autoreload / 2; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  |     if(compare_value == 0) { | ||||||
|  |         compare_value = 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; |     LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; | ||||||
|     TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; |     TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; | ||||||
|     TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; |     TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; | ||||||
| @ -51,6 +62,6 @@ void furi_hal_speaker_start(float frequency, float volume) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_speaker_stop() { | void furi_hal_speaker_stop() { | ||||||
|     LL_TIM_CC_DisableChannel(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL); |     LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); | ||||||
|     LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); |     LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); | ||||||
| } | } | ||||||
|  | |||||||
| @ -113,6 +113,12 @@ uint32_t furi_hal_power_get_battery_remaining_capacity(); | |||||||
|  */ |  */ | ||||||
| uint32_t furi_hal_power_get_battery_full_capacity(); | uint32_t furi_hal_power_get_battery_full_capacity(); | ||||||
| 
 | 
 | ||||||
|  | /** Get battery capacity in mAh from battery profile
 | ||||||
|  |  * | ||||||
|  |  * @return     capacity in mAh | ||||||
|  |  */ | ||||||
|  | uint32_t furi_hal_power_get_battery_design_capacity(); | ||||||
|  | 
 | ||||||
| /** Get battery voltage in V
 | /** Get battery voltage in V
 | ||||||
|  * |  * | ||||||
|  * @param      ic    FuriHalPowerIc to get measurment |  * @param      ic    FuriHalPowerIc to get measurment | ||||||
| @ -171,6 +177,23 @@ void furi_hal_power_suppress_charge_enter(); | |||||||
|  */ |  */ | ||||||
| void furi_hal_power_suppress_charge_exit(); | void furi_hal_power_suppress_charge_exit(); | ||||||
| 
 | 
 | ||||||
|  | /** Callback type called by furi_hal_power_info_get every time another key-value pair of information is ready
 | ||||||
|  |  * | ||||||
|  |  * @param      key[in]      power information type identifier | ||||||
|  |  * @param      value[in]    power information value | ||||||
|  |  * @param      last[in]     whether the passed key-value pair is the last one | ||||||
|  |  * @param      context[in]  to pass to callback | ||||||
|  |  */ | ||||||
|  | typedef void ( | ||||||
|  |     *FuriHalPowerInfoCallback)(const char* key, const char* value, bool last, void* context); | ||||||
|  | 
 | ||||||
|  | /** Get power information
 | ||||||
|  |  * | ||||||
|  |  * @param[in]  callback     callback to provide with new data | ||||||
|  |  * @param[in]  context      context to pass to callback | ||||||
|  |  */ | ||||||
|  | void furi_hal_power_info_get(FuriHalPowerInfoCallback callback, void* context); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -187,6 +187,8 @@ bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { | |||||||
|     string_t filetype; |     string_t filetype; | ||||||
|     string_init(filetype); |     string_init(filetype); | ||||||
| 
 | 
 | ||||||
|  |     FURI_LOG_I(TAG, "Loading keystore %s", file_name); | ||||||
|  | 
 | ||||||
|     Storage* storage = furi_record_open("storage"); |     Storage* storage = furi_record_open("storage"); | ||||||
| 
 | 
 | ||||||
|     FlipperFormat* flipper_format = flipper_format_file_alloc(storage); |     FlipperFormat* flipper_format = flipper_format_file_alloc(storage); | ||||||
| @ -224,7 +226,6 @@ bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { | |||||||
|             FURI_LOG_E(TAG, "Unknown encryption"); |             FURI_LOG_E(TAG, "Unknown encryption"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         FURI_LOG_I(TAG, "Loading keystore %s", file_name); |  | ||||||
|     } while(0); |     } while(0); | ||||||
|     flipper_format_free(flipper_format); |     flipper_format_free(flipper_format); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -61,6 +61,13 @@ bool file_stream_close(Stream* _stream) { | |||||||
|     return storage_file_close(stream->file); |     return storage_file_close(stream->file); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | FS_Error file_stream_get_error(Stream* _stream) { | ||||||
|  |     furi_assert(_stream); | ||||||
|  |     FileStream* stream = (FileStream*)_stream; | ||||||
|  |     furi_check(stream->stream_base.vtable == &file_stream_vtable); | ||||||
|  |     return storage_file_get_error(stream->file); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void file_stream_free(FileStream* stream) { | static void file_stream_free(FileStream* stream) { | ||||||
|     storage_file_free(stream->file); |     storage_file_free(stream->file); | ||||||
|     free(stream); |     free(stream); | ||||||
| @ -169,7 +176,7 @@ static bool file_stream_delete_and_insert( | |||||||
|     string_t scratch_name; |     string_t scratch_name; | ||||||
|     string_t tmp_name; |     string_t tmp_name; | ||||||
|     string_init(tmp_name); |     string_init(tmp_name); | ||||||
|     storage_get_next_filename(_stream->storage, "/any", ".scratch", ".pad", tmp_name); |     storage_get_next_filename(_stream->storage, "/any", ".scratch", ".pad", tmp_name, 255); | ||||||
|     string_init_printf(scratch_name, "/any/%s.pad", string_get_cstr(tmp_name)); |     string_init_printf(scratch_name, "/any/%s.pad", string_get_cstr(tmp_name)); | ||||||
|     string_clear(tmp_name); |     string_clear(tmp_name); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -35,6 +35,13 @@ bool file_stream_open( | |||||||
|  */ |  */ | ||||||
| bool file_stream_close(Stream* stream); | bool file_stream_close(Stream* stream); | ||||||
| 
 | 
 | ||||||
|  | /** 
 | ||||||
|  |  * Retrieves the error id from the file object | ||||||
|  |  * @param stream pointer to stream object. | ||||||
|  |  * @return FS_Error error id | ||||||
|  |  */ | ||||||
|  | FS_Error file_stream_get_error(Stream* stream); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov