mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
* Subghz: brought back manual region bypass * MNTM: change frequency check this allows to add "extended" frequencies to your custom added frequencies * MNTM: reboot when changing extended frequencies * Fix regio typo * Show bypass before extend * Consistency with OFW and UL api for region, version, otp * Fix API symbols * Restrict only on TX * Oops * Improve bypass/extend UI --------- Co-authored-by: Willy-JL <49810075+Willy-JL@users.noreply.github.com>
399 lines
15 KiB
C
399 lines
15 KiB
C
#include "momentum_app.h"
|
|
|
|
static bool momentum_app_custom_event_callback(void* context, uint32_t event) {
|
|
furi_assert(context);
|
|
MomentumApp* app = context;
|
|
return scene_manager_handle_custom_event(app->scene_manager, event);
|
|
}
|
|
|
|
void callback_reboot(void* context) {
|
|
UNUSED(context);
|
|
power_reboot(PowerBootModeNormal);
|
|
}
|
|
|
|
bool momentum_app_apply(MomentumApp* app) {
|
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
if(app->save_mainmenu_apps) {
|
|
Stream* stream = file_stream_alloc(storage);
|
|
if(file_stream_open(stream, MAINMENU_APPS_PATH, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) {
|
|
stream_write_format(stream, "MenuAppList Version %u\n", 1);
|
|
CharList_it_t it;
|
|
CharList_it(it, app->mainmenu_app_exes);
|
|
for(size_t i = 0; i < CharList_size(app->mainmenu_app_exes); i++) {
|
|
stream_write_format(stream, "%s\n", *CharList_get(app->mainmenu_app_exes, i));
|
|
}
|
|
}
|
|
file_stream_close(stream);
|
|
stream_free(stream);
|
|
}
|
|
|
|
if(app->save_subghz_freqs) {
|
|
FlipperFormat* file = flipper_format_file_alloc(storage);
|
|
do {
|
|
FrequencyList_it_t it;
|
|
if(!flipper_format_file_open_always(file, EXT_PATH("subghz/assets/setting_user")))
|
|
break;
|
|
|
|
if(!flipper_format_write_header_cstr(
|
|
file, SUBGHZ_SETTING_FILE_TYPE, SUBGHZ_SETTING_FILE_VERSION))
|
|
break;
|
|
|
|
while(flipper_format_delete_key(file, "Add_standard_frequencies"))
|
|
;
|
|
flipper_format_write_bool(
|
|
file, "Add_standard_frequencies", &app->subghz_use_defaults, 1);
|
|
|
|
if(!flipper_format_rewind(file)) break;
|
|
while(flipper_format_delete_key(file, "Frequency"))
|
|
;
|
|
FrequencyList_it(it, app->subghz_static_freqs);
|
|
for(size_t i = 0; i < FrequencyList_size(app->subghz_static_freqs); i++) {
|
|
flipper_format_write_uint32(
|
|
file, "Frequency", FrequencyList_get(app->subghz_static_freqs, i), 1);
|
|
}
|
|
|
|
if(!flipper_format_rewind(file)) break;
|
|
while(flipper_format_delete_key(file, "Hopper_frequency"))
|
|
;
|
|
for(size_t i = 0; i < FrequencyList_size(app->subghz_hopper_freqs); i++) {
|
|
flipper_format_write_uint32(
|
|
file, "Hopper_frequency", FrequencyList_get(app->subghz_hopper_freqs, i), 1);
|
|
}
|
|
} while(false);
|
|
flipper_format_free(file);
|
|
}
|
|
|
|
if(app->save_subghz) {
|
|
FlipperFormat* file = flipper_format_file_alloc(storage);
|
|
do {
|
|
if(!flipper_format_file_open_always(file, "/ext/subghz/assets/extend_range.txt"))
|
|
break;
|
|
if(!flipper_format_write_header_cstr(file, "Flipper SubGhz Setting File", 1)) break;
|
|
if(!flipper_format_write_comment_cstr(
|
|
file, "Whether to allow extended ranges that can break your flipper"))
|
|
break;
|
|
if(!flipper_format_write_bool(
|
|
file, "use_ext_range_at_own_risk", &app->subghz_extend, 1))
|
|
break;
|
|
if(!flipper_format_write_bool(file, "ignore_default_tx_region", &app->subghz_bypass, 1))
|
|
break;
|
|
} while(0);
|
|
flipper_format_free(file);
|
|
}
|
|
|
|
if(app->save_name) {
|
|
if(strcmp(app->device_name, "") == 0) {
|
|
storage_simply_remove(storage, NAMESPOOF_PATH);
|
|
} else {
|
|
FlipperFormat* file = flipper_format_file_alloc(storage);
|
|
|
|
do {
|
|
if(!flipper_format_file_open_always(file, NAMESPOOF_PATH)) break;
|
|
if(!flipper_format_write_header_cstr(file, NAMESPOOF_HEADER, NAMESPOOF_VERSION))
|
|
break;
|
|
if(!flipper_format_write_string_cstr(file, "Name", app->device_name)) break;
|
|
} while(0);
|
|
|
|
flipper_format_free(file);
|
|
}
|
|
}
|
|
|
|
if(app->save_level || app->save_angry) {
|
|
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
|
|
if(app->save_level) {
|
|
int32_t xp = app->dolphin_level > 1 ? DOLPHIN_LEVELS[app->dolphin_level - 2] : 0;
|
|
dolphin->state->data.icounter = xp + 1;
|
|
}
|
|
if(app->save_angry) {
|
|
dolphin->state->data.butthurt = app->dolphin_angry;
|
|
}
|
|
dolphin->state->dirty = true;
|
|
dolphin_state_save(dolphin->state);
|
|
furi_record_close(RECORD_DOLPHIN);
|
|
}
|
|
|
|
if(app->save_backlight) {
|
|
rgb_backlight_save_settings();
|
|
}
|
|
|
|
if(app->save_settings) {
|
|
momentum_settings_save();
|
|
}
|
|
|
|
if(app->show_slideshow) {
|
|
callback_reboot(NULL);
|
|
} else if(app->require_reboot) {
|
|
popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter);
|
|
popup_set_text(app->popup, "Applying changes...", 64, 40, AlignCenter, AlignCenter);
|
|
popup_set_callback(app->popup, callback_reboot);
|
|
popup_set_context(app->popup, app);
|
|
popup_set_timeout(app->popup, 1000);
|
|
popup_enable_timeout(app->popup);
|
|
view_dispatcher_switch_to_view(app->view_dispatcher, MomentumAppViewPopup);
|
|
return true;
|
|
} else if(app->apply_pack) {
|
|
asset_packs_free();
|
|
popup_set_header(app->popup, "Reloading...", 64, 26, AlignCenter, AlignCenter);
|
|
popup_set_text(app->popup, "Applying asset pack...", 64, 40, AlignCenter, AlignCenter);
|
|
popup_set_callback(app->popup, NULL);
|
|
popup_set_context(app->popup, NULL);
|
|
popup_set_timeout(app->popup, 0);
|
|
popup_disable_timeout(app->popup);
|
|
view_dispatcher_switch_to_view(app->view_dispatcher, MomentumAppViewPopup);
|
|
asset_packs_init();
|
|
}
|
|
|
|
furi_record_close(RECORD_STORAGE);
|
|
return false;
|
|
}
|
|
|
|
static bool momentum_app_back_event_callback(void* context) {
|
|
furi_assert(context);
|
|
MomentumApp* app = context;
|
|
|
|
if(!scene_manager_has_previous_scene(app->scene_manager, MomentumAppSceneStart)) {
|
|
if(momentum_app_apply(app)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return scene_manager_handle_back_event(app->scene_manager);
|
|
}
|
|
|
|
MomentumApp* momentum_app_alloc() {
|
|
MomentumApp* app = malloc(sizeof(MomentumApp));
|
|
app->gui = furi_record_open(RECORD_GUI);
|
|
app->dialogs = furi_record_open(RECORD_DIALOGS);
|
|
app->expansion = furi_record_open(RECORD_EXPANSION);
|
|
app->notification = furi_record_open(RECORD_NOTIFICATION);
|
|
|
|
// View Dispatcher and Scene Manager
|
|
app->view_dispatcher = view_dispatcher_alloc();
|
|
app->scene_manager = scene_manager_alloc(&momentum_app_scene_handlers, app);
|
|
view_dispatcher_enable_queue(app->view_dispatcher);
|
|
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
|
|
|
view_dispatcher_set_custom_event_callback(
|
|
app->view_dispatcher, momentum_app_custom_event_callback);
|
|
view_dispatcher_set_navigation_event_callback(
|
|
app->view_dispatcher, momentum_app_back_event_callback);
|
|
|
|
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
|
|
|
// Gui Modules
|
|
app->var_item_list = variable_item_list_alloc();
|
|
view_dispatcher_add_view(
|
|
app->view_dispatcher,
|
|
MomentumAppViewVarItemList,
|
|
variable_item_list_get_view(app->var_item_list));
|
|
|
|
app->submenu = submenu_alloc();
|
|
view_dispatcher_add_view(
|
|
app->view_dispatcher, MomentumAppViewSubmenu, submenu_get_view(app->submenu));
|
|
|
|
app->text_input = text_input_alloc();
|
|
view_dispatcher_add_view(
|
|
app->view_dispatcher, MomentumAppViewTextInput, text_input_get_view(app->text_input));
|
|
|
|
app->byte_input = byte_input_alloc();
|
|
view_dispatcher_add_view(
|
|
app->view_dispatcher, MomentumAppViewByteInput, byte_input_get_view(app->byte_input));
|
|
|
|
app->popup = popup_alloc();
|
|
view_dispatcher_add_view(
|
|
app->view_dispatcher, MomentumAppViewPopup, popup_get_view(app->popup));
|
|
|
|
app->dialog_ex = dialog_ex_alloc();
|
|
view_dispatcher_add_view(
|
|
app->view_dispatcher, MomentumAppViewDialogEx, dialog_ex_get_view(app->dialog_ex));
|
|
|
|
// Settings init
|
|
|
|
app->asset_pack_index = 0;
|
|
CharList_init(app->asset_pack_names);
|
|
Storage* storage = furi_record_open(RECORD_STORAGE);
|
|
File* folder = storage_file_alloc(storage);
|
|
FileInfo info;
|
|
char* name = malloc(ASSET_PACKS_NAME_LEN);
|
|
if(storage_dir_open(folder, ASSET_PACKS_PATH)) {
|
|
while(storage_dir_read(folder, &info, name, ASSET_PACKS_NAME_LEN)) {
|
|
if(info.flags & FSF_DIRECTORY) {
|
|
char* copy = strdup(name);
|
|
size_t idx = 0;
|
|
for(; idx < CharList_size(app->asset_pack_names); idx++) {
|
|
char* comp = *CharList_get(app->asset_pack_names, idx);
|
|
if(strcasecmp(copy, comp) < 0) {
|
|
break;
|
|
}
|
|
}
|
|
CharList_push_at(app->asset_pack_names, idx, copy);
|
|
if(app->asset_pack_index != 0) {
|
|
if(idx < app->asset_pack_index) app->asset_pack_index++;
|
|
} else {
|
|
if(strcmp(copy, momentum_settings.asset_pack) == 0)
|
|
app->asset_pack_index = idx + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
free(name);
|
|
storage_file_free(folder);
|
|
|
|
CharList_init(app->mainmenu_app_labels);
|
|
CharList_init(app->mainmenu_app_exes);
|
|
Stream* stream = file_stream_alloc(storage);
|
|
FuriString* line = furi_string_alloc();
|
|
uint32_t version;
|
|
if(file_stream_open(stream, MAINMENU_APPS_PATH, FSAM_READ, FSOM_OPEN_EXISTING) &&
|
|
stream_read_line(stream, line) &&
|
|
sscanf(furi_string_get_cstr(line), "MenuAppList Version %lu", &version) == 1 &&
|
|
version <= 1) {
|
|
while(stream_read_line(stream, line)) {
|
|
furi_string_replace_all(line, "\r", "");
|
|
furi_string_replace_all(line, "\n", "");
|
|
if(version == 0) {
|
|
if(!furi_string_cmp(line, "RFID")) {
|
|
furi_string_set(line, "125 kHz RFID");
|
|
} else if(!furi_string_cmp(line, "SubGHz")) {
|
|
furi_string_set(line, "Sub-GHz");
|
|
}
|
|
}
|
|
CharList_push_back(app->mainmenu_app_exes, strdup(furi_string_get_cstr(line)));
|
|
flipper_application_load_name_and_icon(line, storage, NULL, line);
|
|
if(!furi_string_cmp(line, "Momentum")) {
|
|
furi_string_set(line, "MNTM");
|
|
} else if(!furi_string_cmp(line, "125 kHz RFID")) {
|
|
furi_string_set(line, "RFID");
|
|
} else if(!furi_string_cmp(line, "Sub-GHz")) {
|
|
furi_string_set(line, "SubGHz");
|
|
} else if(furi_string_start_with_str(line, "[")) {
|
|
size_t trim = furi_string_search_str(line, "] ", 1);
|
|
if(trim != FURI_STRING_FAILURE) {
|
|
furi_string_right(line, trim + 2);
|
|
}
|
|
}
|
|
CharList_push_back(app->mainmenu_app_labels, strdup(furi_string_get_cstr(line)));
|
|
}
|
|
}
|
|
furi_string_free(line);
|
|
file_stream_close(stream);
|
|
stream_free(stream);
|
|
|
|
FlipperFormat* file = flipper_format_file_alloc(storage);
|
|
FrequencyList_init(app->subghz_static_freqs);
|
|
FrequencyList_init(app->subghz_hopper_freqs);
|
|
app->subghz_use_defaults = true;
|
|
do {
|
|
uint32_t temp;
|
|
if(!flipper_format_file_open_existing(file, EXT_PATH("subghz/assets/setting_user"))) break;
|
|
|
|
flipper_format_read_bool(file, "Add_standard_frequencies", &app->subghz_use_defaults, 1);
|
|
|
|
if(!flipper_format_rewind(file)) break;
|
|
while(flipper_format_read_uint32(file, "Frequency", &temp, 1)) {
|
|
if(furi_hal_subghz_is_frequency_valid(temp)) {
|
|
FrequencyList_push_back(app->subghz_static_freqs, temp);
|
|
}
|
|
}
|
|
|
|
if(!flipper_format_rewind(file)) break;
|
|
while(flipper_format_read_uint32(file, "Hopper_frequency", &temp, 1)) {
|
|
if(furi_hal_subghz_is_frequency_valid(temp)) {
|
|
FrequencyList_push_back(app->subghz_hopper_freqs, temp);
|
|
}
|
|
}
|
|
} while(false);
|
|
flipper_format_free(file);
|
|
|
|
file = flipper_format_file_alloc(storage);
|
|
if(flipper_format_file_open_existing(file, "/ext/subghz/assets/extend_range.txt")) {
|
|
flipper_format_read_bool(file, "use_ext_range_at_own_risk", &app->subghz_extend, 1);
|
|
flipper_format_read_bool(file, "ignore_default_tx_region", &app->subghz_bypass, 1);
|
|
}
|
|
flipper_format_free(file);
|
|
furi_record_close(RECORD_STORAGE);
|
|
|
|
strlcpy(app->device_name, furi_hal_version_get_name_ptr(), FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
|
|
|
|
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
|
|
DolphinStats stats = dolphin_stats(dolphin);
|
|
app->dolphin_level = stats.level;
|
|
app->dolphin_angry = stats.butthurt;
|
|
furi_record_close(RECORD_DOLPHIN);
|
|
|
|
app->version_tag = furi_string_alloc_printf("%s ", version_get_version(NULL));
|
|
if(furi_string_start_with(app->version_tag, "mntm-dev")) {
|
|
furi_string_set(app->version_tag, "MNTM-DEV ");
|
|
const char* sha = version_get_githash(NULL);
|
|
for(size_t i = 0; i < strlen(sha); ++i) {
|
|
furi_string_push_back(app->version_tag, toupper(sha[i]));
|
|
}
|
|
} else {
|
|
furi_string_replace(app->version_tag, "mntm", "MNTM");
|
|
furi_string_cat(app->version_tag, version_get_builddate(NULL));
|
|
}
|
|
|
|
return app;
|
|
}
|
|
|
|
void momentum_app_free(MomentumApp* app) {
|
|
furi_assert(app);
|
|
|
|
// Gui modules
|
|
view_dispatcher_remove_view(app->view_dispatcher, MomentumAppViewVarItemList);
|
|
variable_item_list_free(app->var_item_list);
|
|
view_dispatcher_remove_view(app->view_dispatcher, MomentumAppViewSubmenu);
|
|
submenu_free(app->submenu);
|
|
view_dispatcher_remove_view(app->view_dispatcher, MomentumAppViewTextInput);
|
|
text_input_free(app->text_input);
|
|
view_dispatcher_remove_view(app->view_dispatcher, MomentumAppViewByteInput);
|
|
byte_input_free(app->byte_input);
|
|
view_dispatcher_remove_view(app->view_dispatcher, MomentumAppViewPopup);
|
|
popup_free(app->popup);
|
|
view_dispatcher_remove_view(app->view_dispatcher, MomentumAppViewDialogEx);
|
|
dialog_ex_free(app->dialog_ex);
|
|
|
|
// View Dispatcher and Scene Manager
|
|
view_dispatcher_free(app->view_dispatcher);
|
|
scene_manager_free(app->scene_manager);
|
|
|
|
// Settings deinit
|
|
|
|
CharList_it_t it;
|
|
for(CharList_it(it, app->asset_pack_names); !CharList_end_p(it); CharList_next(it)) {
|
|
free(*CharList_cref(it));
|
|
}
|
|
CharList_clear(app->asset_pack_names);
|
|
|
|
for(CharList_it(it, app->mainmenu_app_labels); !CharList_end_p(it); CharList_next(it)) {
|
|
free(*CharList_cref(it));
|
|
}
|
|
CharList_clear(app->mainmenu_app_labels);
|
|
for(CharList_it(it, app->mainmenu_app_exes); !CharList_end_p(it); CharList_next(it)) {
|
|
free(*CharList_cref(it));
|
|
}
|
|
CharList_clear(app->mainmenu_app_exes);
|
|
|
|
FrequencyList_clear(app->subghz_static_freqs);
|
|
FrequencyList_clear(app->subghz_hopper_freqs);
|
|
|
|
furi_string_free(app->version_tag);
|
|
|
|
// Records
|
|
furi_record_close(RECORD_NOTIFICATION);
|
|
furi_record_close(RECORD_EXPANSION);
|
|
furi_record_close(RECORD_DIALOGS);
|
|
furi_record_close(RECORD_GUI);
|
|
free(app);
|
|
}
|
|
|
|
extern int32_t momentum_app(void* p) {
|
|
UNUSED(p);
|
|
MomentumApp* app = momentum_app_alloc();
|
|
scene_manager_next_scene(app->scene_manager, MomentumAppSceneStart);
|
|
view_dispatcher_run(app->view_dispatcher);
|
|
momentum_app_free(app);
|
|
return 0;
|
|
}
|