|
@@ -0,0 +1,505 @@
|
|
|
|
|
+#include "weebo_i.h"
|
|
|
|
|
+
|
|
|
|
|
+#define TAG "weebo"
|
|
|
|
|
+
|
|
|
|
|
+#define WEEBO_KEY_RETAIL_FILENAME "key_retail"
|
|
|
|
|
+#define FIGURE_ID_LIST APP_ASSETS_PATH("figure_ids.nfc")
|
|
|
|
|
+#define UNPACKED_FIGURE_ID 0x1dc
|
|
|
|
|
+#define NFC_APP_EXTENSION ".nfc"
|
|
|
|
|
+#define NFC_APP_PATH_PREFIX "/ext/nfc"
|
|
|
|
|
+
|
|
|
|
|
+static const char* nfc_resources_header = "Flipper NFC resources";
|
|
|
|
|
+static const uint32_t nfc_resources_file_version = 1;
|
|
|
|
|
+
|
|
|
|
|
+bool weebo_load_key_retail(Weebo* weebo) {
|
|
|
|
|
+ FuriString* path = furi_string_alloc();
|
|
|
|
|
+ bool parsed = false;
|
|
|
|
|
+ uint8_t buffer[160];
|
|
|
|
|
+ memset(buffer, 0, sizeof(buffer));
|
|
|
|
|
+ Storage* storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
+ Stream* stream = file_stream_alloc(storage);
|
|
|
|
|
+
|
|
|
|
|
+ do {
|
|
|
|
|
+ furi_string_printf(
|
|
|
|
|
+ path, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, WEEBO_KEY_RETAIL_FILENAME, ".bin");
|
|
|
|
|
+
|
|
|
|
|
+ bool opened =
|
|
|
|
|
+ file_stream_open(stream, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING);
|
|
|
|
|
+ if(!opened) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to open file");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ size_t bytes_read = stream_read(stream, buffer, sizeof(buffer));
|
|
|
|
|
+ if(bytes_read != sizeof(buffer)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Insufficient data");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ memcpy(&weebo->keys, buffer, bytes_read);
|
|
|
|
|
+
|
|
|
|
|
+ // TODO: compare SHA1
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ } while(false);
|
|
|
|
|
+
|
|
|
|
|
+ file_stream_close(stream);
|
|
|
|
|
+ furi_record_close(RECORD_STORAGE);
|
|
|
|
|
+ furi_string_free(path);
|
|
|
|
|
+
|
|
|
|
|
+ return parsed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool weebo_load_figure(Weebo* weebo, FuriString* path, bool show_dialog) {
|
|
|
|
|
+ bool parsed = false;
|
|
|
|
|
+ FuriString* reason = furi_string_alloc_set("Couldn't load file");
|
|
|
|
|
+ uint8_t buffer[NTAG215_SIZE];
|
|
|
|
|
+ memset(buffer, 0, sizeof(buffer));
|
|
|
|
|
+
|
|
|
|
|
+ if(weebo->loading_cb) {
|
|
|
|
|
+ weebo->loading_cb(weebo->loading_cb_ctx, true);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ do {
|
|
|
|
|
+ NfcDevice* nfc_device = weebo->nfc_device;
|
|
|
|
|
+ if(!nfc_device_load(nfc_device, furi_string_get_cstr(path))) break;
|
|
|
|
|
+
|
|
|
|
|
+ NfcProtocol protocol = nfc_device_get_protocol(nfc_device);
|
|
|
|
|
+ if(protocol != NfcProtocolMfUltralight) {
|
|
|
|
|
+ furi_string_printf(reason, "Not Ultralight protocol");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const MfUltralightData* data = nfc_device_get_data(nfc_device, NfcProtocolMfUltralight);
|
|
|
|
|
+ if(data->type != MfUltralightTypeNTAG215) {
|
|
|
|
|
+ furi_string_printf(reason, "Not NTAG215");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(!mf_ultralight_is_all_data_read(data)) {
|
|
|
|
|
+ furi_string_printf(reason, "Incomplete data");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint8_t* uid = data->iso14443_3a_data->uid;
|
|
|
|
|
+ uint8_t pwd[4];
|
|
|
|
|
+ weebo_calculate_pwd(uid, pwd);
|
|
|
|
|
+
|
|
|
|
|
+ if(memcmp(data->page[133].data, pwd, sizeof(pwd)) != 0) {
|
|
|
|
|
+ furi_string_printf(reason, "Wrong password");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for(size_t i = 0; i < 135; i++) {
|
|
|
|
|
+ memcpy(
|
|
|
|
|
+ buffer + i * MF_ULTRALIGHT_PAGE_SIZE, data->page[i].data, MF_ULTRALIGHT_PAGE_SIZE);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(!nfc3d_amiibo_unpack(&weebo->keys, buffer, weebo->figure)) {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to unpack");
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ } while(false);
|
|
|
|
|
+
|
|
|
|
|
+ if(weebo->loading_cb) {
|
|
|
|
|
+ weebo->loading_cb(weebo->loading_cb_ctx, false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if((!parsed) && (show_dialog)) {
|
|
|
|
|
+ dialog_message_show_storage_error(weebo->dialogs, furi_string_get_cstr(reason));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(reason);
|
|
|
|
|
+ return parsed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static bool
|
|
|
|
|
+ weebo_search_data(Storage* storage, const char* file_name, FuriString* key, FuriString* data) {
|
|
|
|
|
+ bool parsed = false;
|
|
|
|
|
+ FlipperFormat* file = flipper_format_file_alloc(storage);
|
|
|
|
|
+ FuriString* temp_str;
|
|
|
|
|
+ temp_str = furi_string_alloc();
|
|
|
|
|
+
|
|
|
|
|
+ do {
|
|
|
|
|
+ // Open file
|
|
|
|
|
+ if(!flipper_format_file_open_existing(file, file_name)) break;
|
|
|
|
|
+ // Read file header and version
|
|
|
|
|
+ uint32_t version = 0;
|
|
|
|
|
+ if(!flipper_format_read_header(file, temp_str, &version)) break;
|
|
|
|
|
+ if(furi_string_cmp_str(temp_str, nfc_resources_header) ||
|
|
|
|
|
+ (version != nfc_resources_file_version))
|
|
|
|
|
+ break;
|
|
|
|
|
+ if(!flipper_format_read_string(file, furi_string_get_cstr(key), data)) break;
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ } while(false);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(temp_str);
|
|
|
|
|
+ flipper_format_free(file);
|
|
|
|
|
+ return parsed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+uint16_t weebo_get_figure_id(Weebo* weebo) {
|
|
|
|
|
+ uint16_t id = 0;
|
|
|
|
|
+ id |= weebo->figure[UNPACKED_FIGURE_ID + 0] << 8;
|
|
|
|
|
+ id |= weebo->figure[UNPACKED_FIGURE_ID + 1] << 0;
|
|
|
|
|
+ FURI_LOG_D(TAG, "id = %04x", id);
|
|
|
|
|
+ return id;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool weebo_get_figure_form(Weebo* weebo, FuriString* name) {
|
|
|
|
|
+ bool parsed = false;
|
|
|
|
|
+ uint8_t form = weebo->figure[UNPACKED_FIGURE_ID + 3];
|
|
|
|
|
+ FURI_LOG_D(TAG, "form = %02x", form);
|
|
|
|
|
+ switch(form) {
|
|
|
|
|
+ case 0x00:
|
|
|
|
|
+ furi_string_set_str(name, "Figure");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x01:
|
|
|
|
|
+ furi_string_set_str(name, "Card");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x02:
|
|
|
|
|
+ furi_string_set_str(name, "Yarn");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return parsed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool weebo_get_figure_series(Weebo* weebo, FuriString* name) {
|
|
|
|
|
+ bool parsed = false;
|
|
|
|
|
+
|
|
|
|
|
+ uint8_t series_id = weebo->figure[UNPACKED_FIGURE_ID + 6];
|
|
|
|
|
+ switch(series_id) {
|
|
|
|
|
+ case 0x00:
|
|
|
|
|
+ furi_string_set_str(name, "Smash Bros");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x01:
|
|
|
|
|
+ furi_string_set_str(name, "Mario Bros");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x02:
|
|
|
|
|
+ furi_string_set_str(name, "Chibi Robo");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x03:
|
|
|
|
|
+ furi_string_set_str(name, "Yarn Yoshi");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x04:
|
|
|
|
|
+ furi_string_set_str(name, "Splatoon");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x05:
|
|
|
|
|
+ furi_string_set_str(name, "Animal Crossing");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x06:
|
|
|
|
|
+ furi_string_set_str(name, "8-bit Mario");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x07:
|
|
|
|
|
+ furi_string_set_str(name, "Skylanders");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x09:
|
|
|
|
|
+ furi_string_set_str(name, "Legend of Zelda");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x0A:
|
|
|
|
|
+ furi_string_set_str(name, "Shovel Knight");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x0C:
|
|
|
|
|
+ furi_string_set_str(name, "Kirby");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x0D:
|
|
|
|
|
+ furi_string_set_str(name, "Pokken");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 0x0F:
|
|
|
|
|
+ furi_string_set_str(name, "Monster Hunter");
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ return parsed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool weebo_get_figure_name(Weebo* weebo, FuriString* name) {
|
|
|
|
|
+ bool parsed = false;
|
|
|
|
|
+
|
|
|
|
|
+ uint16_t id = weebo_get_figure_id(weebo);
|
|
|
|
|
+
|
|
|
|
|
+ FuriString* key = furi_string_alloc_printf("%04x", id);
|
|
|
|
|
+ if(weebo_search_data(weebo->storage, FIGURE_ID_LIST, key, name)) {
|
|
|
|
|
+ parsed = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ furi_string_free(key);
|
|
|
|
|
+ return parsed;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void weebo_set_loading_callback(Weebo* weebo, WeeboLoadingCallback callback, void* context) {
|
|
|
|
|
+ furi_assert(weebo);
|
|
|
|
|
+
|
|
|
|
|
+ weebo->loading_cb = callback;
|
|
|
|
|
+ weebo->loading_cb_ctx = context;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool weebo_file_select(Weebo* weebo) {
|
|
|
|
|
+ furi_assert(weebo);
|
|
|
|
|
+ bool res = false;
|
|
|
|
|
+
|
|
|
|
|
+ FuriString* weebo_app_folder;
|
|
|
|
|
+
|
|
|
|
|
+ if(storage_dir_exists(weebo->storage, "/ext/nfc/SmashAmiibo")) {
|
|
|
|
|
+ weebo_app_folder = furi_string_alloc_set("/ext/nfc/SmashAmiibo");
|
|
|
|
|
+ } else if(storage_dir_exists(weebo->storage, "/ext/nfc/Amiibo")) {
|
|
|
|
|
+ weebo_app_folder = furi_string_alloc_set("/ext/nfc/Amiibo");
|
|
|
|
|
+ } else if(storage_dir_exists(weebo->storage, "/ext/nfc/Amiibos")) {
|
|
|
|
|
+ weebo_app_folder = furi_string_alloc_set("/ext/nfc/Amiibos");
|
|
|
|
|
+ } else if(storage_dir_exists(weebo->storage, "/ext/nfc/amiibo")) {
|
|
|
|
|
+ weebo_app_folder = furi_string_alloc_set("/ext/nfc/amiibo");
|
|
|
|
|
+ } else if(storage_dir_exists(weebo->storage, "/ext/nfc/amiibos")) {
|
|
|
|
|
+ weebo_app_folder = furi_string_alloc_set("/ext/nfc/amiibos");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ weebo_app_folder = furi_string_alloc_set(NFC_APP_PATH_PREFIX);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ DialogsFileBrowserOptions browser_options;
|
|
|
|
|
+ dialog_file_browser_set_basic_options(&browser_options, NFC_APP_EXTENSION, &I_Nfc_10px);
|
|
|
|
|
+ browser_options.base_path = NFC_APP_PATH_PREFIX;
|
|
|
|
|
+
|
|
|
|
|
+ res = dialog_file_browser_show(
|
|
|
|
|
+ weebo->dialogs, weebo->load_path, weebo_app_folder, &browser_options);
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(weebo_app_folder);
|
|
|
|
|
+ if(res) {
|
|
|
|
|
+ FuriString* filename;
|
|
|
|
|
+ filename = furi_string_alloc();
|
|
|
|
|
+ path_extract_filename(weebo->load_path, filename, true);
|
|
|
|
|
+ strncpy(weebo->file_name, furi_string_get_cstr(filename), WEEBO_FILE_NAME_MAX_LENGTH);
|
|
|
|
|
+ res = weebo_load_figure(weebo, weebo->load_path, true);
|
|
|
|
|
+ furi_string_free(filename);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return res;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool weebo_custom_event_callback(void* context, uint32_t event) {
|
|
|
|
|
+ furi_assert(context);
|
|
|
|
|
+ Weebo* weebo = context;
|
|
|
|
|
+ return scene_manager_handle_custom_event(weebo->scene_manager, event);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bool weebo_back_event_callback(void* context) {
|
|
|
|
|
+ furi_assert(context);
|
|
|
|
|
+ Weebo* weebo = context;
|
|
|
|
|
+ return scene_manager_handle_back_event(weebo->scene_manager);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void weebo_tick_event_callback(void* context) {
|
|
|
|
|
+ furi_assert(context);
|
|
|
|
|
+ Weebo* weebo = context;
|
|
|
|
|
+ scene_manager_handle_tick_event(weebo->scene_manager);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Weebo* weebo_alloc() {
|
|
|
|
|
+ Weebo* weebo = malloc(sizeof(Weebo));
|
|
|
|
|
+
|
|
|
|
|
+ weebo->view_dispatcher = view_dispatcher_alloc();
|
|
|
|
|
+ weebo->scene_manager = scene_manager_alloc(&weebo_scene_handlers, weebo);
|
|
|
|
|
+ view_dispatcher_set_event_callback_context(weebo->view_dispatcher, weebo);
|
|
|
|
|
+ view_dispatcher_set_custom_event_callback(weebo->view_dispatcher, weebo_custom_event_callback);
|
|
|
|
|
+ view_dispatcher_set_navigation_event_callback(
|
|
|
|
|
+ weebo->view_dispatcher, weebo_back_event_callback);
|
|
|
|
|
+ view_dispatcher_set_tick_event_callback(
|
|
|
|
|
+ weebo->view_dispatcher, weebo_tick_event_callback, 100);
|
|
|
|
|
+
|
|
|
|
|
+ weebo->nfc = nfc_alloc();
|
|
|
|
|
+
|
|
|
|
|
+ // Nfc device
|
|
|
|
|
+ weebo->nfc_device = nfc_device_alloc();
|
|
|
|
|
+ nfc_device_set_loading_callback(weebo->nfc_device, weebo_show_loading_popup, weebo);
|
|
|
|
|
+
|
|
|
|
|
+ // Open GUI record
|
|
|
|
|
+ weebo->gui = furi_record_open(RECORD_GUI);
|
|
|
|
|
+ view_dispatcher_attach_to_gui(
|
|
|
|
|
+ weebo->view_dispatcher, weebo->gui, ViewDispatcherTypeFullscreen);
|
|
|
|
|
+
|
|
|
|
|
+ // Open Notification record
|
|
|
|
|
+ weebo->notifications = furi_record_open(RECORD_NOTIFICATION);
|
|
|
|
|
+
|
|
|
|
|
+ // Submenu
|
|
|
|
|
+ weebo->submenu = submenu_alloc();
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ weebo->view_dispatcher, WeeboViewMenu, submenu_get_view(weebo->submenu));
|
|
|
|
|
+
|
|
|
|
|
+ // Popup
|
|
|
|
|
+ weebo->popup = popup_alloc();
|
|
|
|
|
+ view_dispatcher_add_view(weebo->view_dispatcher, WeeboViewPopup, popup_get_view(weebo->popup));
|
|
|
|
|
+
|
|
|
|
|
+ // Loading
|
|
|
|
|
+ weebo->loading = loading_alloc();
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ weebo->view_dispatcher, WeeboViewLoading, loading_get_view(weebo->loading));
|
|
|
|
|
+
|
|
|
|
|
+ // Text Input
|
|
|
|
|
+ weebo->text_input = text_input_alloc();
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ weebo->view_dispatcher, WeeboViewTextInput, text_input_get_view(weebo->text_input));
|
|
|
|
|
+
|
|
|
|
|
+ // Number Input
|
|
|
|
|
+ weebo->number_input = number_input_alloc();
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ weebo->view_dispatcher, WeeboViewNumberInput, number_input_get_view(weebo->number_input));
|
|
|
|
|
+
|
|
|
|
|
+ // TextBox
|
|
|
|
|
+ weebo->text_box = text_box_alloc();
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ weebo->view_dispatcher, WeeboViewTextBox, text_box_get_view(weebo->text_box));
|
|
|
|
|
+ weebo->text_box_store = furi_string_alloc();
|
|
|
|
|
+
|
|
|
|
|
+ // Custom Widget
|
|
|
|
|
+ weebo->widget = widget_alloc();
|
|
|
|
|
+ view_dispatcher_add_view(
|
|
|
|
|
+ weebo->view_dispatcher, WeeboViewWidget, widget_get_view(weebo->widget));
|
|
|
|
|
+
|
|
|
|
|
+ weebo->storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
+ weebo->dialogs = furi_record_open(RECORD_DIALOGS);
|
|
|
|
|
+ weebo->load_path = furi_string_alloc();
|
|
|
|
|
+
|
|
|
|
|
+ weebo->keys_loaded = false;
|
|
|
|
|
+
|
|
|
|
|
+ return weebo;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void weebo_free(Weebo* weebo) {
|
|
|
|
|
+ furi_assert(weebo);
|
|
|
|
|
+
|
|
|
|
|
+ nfc_free(weebo->nfc);
|
|
|
|
|
+
|
|
|
|
|
+ // Nfc device
|
|
|
|
|
+ nfc_device_free(weebo->nfc_device);
|
|
|
|
|
+
|
|
|
|
|
+ // Submenu
|
|
|
|
|
+ view_dispatcher_remove_view(weebo->view_dispatcher, WeeboViewMenu);
|
|
|
|
|
+ submenu_free(weebo->submenu);
|
|
|
|
|
+
|
|
|
|
|
+ // Popup
|
|
|
|
|
+ view_dispatcher_remove_view(weebo->view_dispatcher, WeeboViewPopup);
|
|
|
|
|
+ popup_free(weebo->popup);
|
|
|
|
|
+
|
|
|
|
|
+ // Loading
|
|
|
|
|
+ view_dispatcher_remove_view(weebo->view_dispatcher, WeeboViewLoading);
|
|
|
|
|
+ loading_free(weebo->loading);
|
|
|
|
|
+
|
|
|
|
|
+ // TextInput
|
|
|
|
|
+ view_dispatcher_remove_view(weebo->view_dispatcher, WeeboViewTextInput);
|
|
|
|
|
+ text_input_free(weebo->text_input);
|
|
|
|
|
+
|
|
|
|
|
+ // NumberInput
|
|
|
|
|
+ view_dispatcher_remove_view(weebo->view_dispatcher, WeeboViewNumberInput);
|
|
|
|
|
+ number_input_free(weebo->number_input);
|
|
|
|
|
+
|
|
|
|
|
+ // TextBox
|
|
|
|
|
+ view_dispatcher_remove_view(weebo->view_dispatcher, WeeboViewTextBox);
|
|
|
|
|
+ text_box_free(weebo->text_box);
|
|
|
|
|
+ furi_string_free(weebo->text_box_store);
|
|
|
|
|
+
|
|
|
|
|
+ // Custom Widget
|
|
|
|
|
+ view_dispatcher_remove_view(weebo->view_dispatcher, WeeboViewWidget);
|
|
|
|
|
+ widget_free(weebo->widget);
|
|
|
|
|
+
|
|
|
|
|
+ // View Dispatcher
|
|
|
|
|
+ view_dispatcher_free(weebo->view_dispatcher);
|
|
|
|
|
+
|
|
|
|
|
+ // Scene Manager
|
|
|
|
|
+ scene_manager_free(weebo->scene_manager);
|
|
|
|
|
+
|
|
|
|
|
+ // GUI
|
|
|
|
|
+ furi_record_close(RECORD_GUI);
|
|
|
|
|
+ weebo->gui = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ // Notifications
|
|
|
|
|
+ furi_record_close(RECORD_NOTIFICATION);
|
|
|
|
|
+ weebo->notifications = NULL;
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(weebo->load_path);
|
|
|
|
|
+ furi_record_close(RECORD_STORAGE);
|
|
|
|
|
+ furi_record_close(RECORD_DIALOGS);
|
|
|
|
|
+
|
|
|
|
|
+ free(weebo);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void weebo_text_store_set(Weebo* weebo, const char* text, ...) {
|
|
|
|
|
+ va_list args;
|
|
|
|
|
+ va_start(args, text);
|
|
|
|
|
+
|
|
|
|
|
+ vsnprintf(weebo->text_store, sizeof(weebo->text_store), text, args);
|
|
|
|
|
+
|
|
|
|
|
+ va_end(args);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void weebo_text_store_clear(Weebo* weebo) {
|
|
|
|
|
+ memset(weebo->text_store, 0, sizeof(weebo->text_store));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static const NotificationSequence weebo_sequence_blink_start_blue = {
|
|
|
|
|
+ &message_blink_start_10,
|
|
|
|
|
+ &message_blink_set_color_blue,
|
|
|
|
|
+ &message_do_not_reset,
|
|
|
|
|
+ NULL,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+static const NotificationSequence weebo_sequence_blink_stop = {
|
|
|
|
|
+ &message_blink_stop,
|
|
|
|
|
+ NULL,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+void weebo_blink_start(Weebo* weebo) {
|
|
|
|
|
+ notification_message(weebo->notifications, &weebo_sequence_blink_start_blue);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void weebo_blink_stop(Weebo* weebo) {
|
|
|
|
|
+ notification_message(weebo->notifications, &weebo_sequence_blink_stop);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void weebo_show_loading_popup(void* context, bool show) {
|
|
|
|
|
+ Weebo* weebo = context;
|
|
|
|
|
+
|
|
|
|
|
+ if(show) {
|
|
|
|
|
+ // Raise timer priority so that animations can play
|
|
|
|
|
+ furi_timer_set_thread_priority(FuriTimerThreadPriorityElevated);
|
|
|
|
|
+ view_dispatcher_switch_to_view(weebo->view_dispatcher, WeeboViewLoading);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Restore default timer priority
|
|
|
|
|
+ furi_timer_set_thread_priority(FuriTimerThreadPriorityNormal);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+int32_t weebo_app(void* p) {
|
|
|
|
|
+ UNUSED(p);
|
|
|
|
|
+ Weebo* weebo = weebo_alloc();
|
|
|
|
|
+
|
|
|
|
|
+ weebo->keys_loaded = weebo_load_key_retail(weebo);
|
|
|
|
|
+ if(weebo->keys_loaded) {
|
|
|
|
|
+ scene_manager_next_scene(weebo->scene_manager, WeeboSceneMainMenu);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ scene_manager_next_scene(weebo->scene_manager, WeeboSceneKeysMissing);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ view_dispatcher_run(weebo->view_dispatcher);
|
|
|
|
|
+
|
|
|
|
|
+ weebo_free(weebo);
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|