From eed92312b4f8328903894a2b6a910ebd4b632120 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Wed, 29 Mar 2023 18:25:20 +1100 Subject: [PATCH 001/370] wip: NFC: FeliCa lite authentication --- firmware/targets/f7/api_symbols.csv | 3 +- lib/nfc/protocols/felica.c | 219 +++++++++++++++++++++++++++- lib/nfc/protocols/felica_util.c | 14 ++ lib/nfc/protocols/felica_util.h | 1 + 4 files changed, 232 insertions(+), 5 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d68c2f471..d9468636e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,20.1,, +Version,+,20.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -829,6 +829,7 @@ Function,-,felica_get_service_name,FuriString*,FelicaService* Function,-,felica_get_system_name,FuriString*,FelicaSystem* Function,-,felica_lite_can_read_without_mac,_Bool,"uint8_t*, uint8_t" Function,-,felica_lite_dump_data,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" +Function,-,felica_lite_is_issued,_Bool,FelicaLiteInfo* Function,-,felica_lite_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, _Bool, const uint8_t*, uint8_t" Function,-,felica_lite_prepare_unencrypted_write,uint8_t,"uint8_t*, const FelicaReader*, const uint8_t*, uint8_t, const uint8_t*" Function,-,felica_parse_unencrypted_read,uint16_t,"uint8_t*, uint8_t, FelicaReader*, uint8_t*, uint16_t" diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index 827187564..c5823a221 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -1,4 +1,5 @@ #include +#include #include #include "felica.h" #include "nfc_util.h" @@ -151,6 +152,215 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { return FelicaICType2K; } +static void felica_lite_diversify_key(uint8_t* id_block, uint8_t* master_key, uint8_t* card_key) { + uint8_t ZERO[8] = {0}; + uint8_t L[8]; + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set3key_enc(&ctx, master_key); + mbedtls_des3_crypt_ecb(&ctx, ZERO, L); + mbedtls_des3_free(&ctx); + + uint8_t K1[8]; + for(int i = 0; i < 8; i++) { + K1[i] = L[i] << 1; + if(i < 7) { + K1[i] |= (L[i + 1] >> 7); + } + } + + if((L[0] ^ 0x80) == 0) { + K1[7] ^= 0x1B; + } + + uint8_t M1[8]; + uint8_t M2[8]; + memcpy(M1, id_block, 8); + memcpy(M2, id_block + 8, 8); + for(int i = 0; i < 8; i++) { + M2[i] ^= K1[i]; + } + + uint8_t C1[8]; + mbedtls_des3_init(&ctx); + mbedtls_des3_set3key_enc(&ctx, master_key); + mbedtls_des3_crypt_ecb(&ctx, M1, C1); + for(int i = 0; i < 8; i++) { + C1[i] ^= M2[i]; + } + + mbedtls_des3_crypt_ecb(&ctx, C1, card_key); // T + + M1[0] ^= 0x80; // M'1 + mbedtls_des3_crypt_ecb(&ctx, M1, C1); // C'1 + for(int i = 0; i < 8; i++) { + C1[i] ^= M2[i]; + } + + mbedtls_des3_crypt_ecb(&ctx, C1, card_key + 8); // T' + mbedtls_des3_free(&ctx); +} + +static void felica_lite_generate_session_key( + uint8_t* random_challenge, + uint8_t* card_key, + uint8_t* session_key) { + uint8_t RC1[8]; + uint8_t RC2[8]; + uint8_t CK[16]; + + for(int i = 0; i < 8; i++) { + RC1[i] = random_challenge[7 - i]; + RC2[i] = random_challenge[i]; + CK[i] = card_key[7 - i]; + CK[i + 8] = card_key[15 - i]; + } + + mbedtls_des3_context ctx; + + uint8_t SK1[8]; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_enc(&ctx, CK); + mbedtls_des3_crypt_ecb(&ctx, RC1, SK1); + + uint8_t SK2[8]; + for(int i = 0; i < 8; i++) { + RC2[i] ^= SK1[i]; + } + mbedtls_des3_crypt_ecb(&ctx, RC2, SK2); + mbedtls_des3_free(&ctx); + + for(int i = 0; i < 8; i++) { + session_key[i] = SK1[7 - i]; + session_key[i + 8] = SK2[7 - i]; + } +} + +static void felica_lite_calculate_mac( + uint8_t* random_challenge, + uint8_t* session_key, + uint8_t* block_data, + size_t block_count, + uint8_t* MAC) { + uint8_t SK[16]; + + for(int i = 0; i < 8; i++) { + MAC[i] = random_challenge[7 - i]; + SK[i] = session_key[7 - i]; + SK[i + 8] = session_key[15 - i]; + } + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set3key_enc(&ctx, SK); + + for(size_t block_num = 0; block_num < block_count; block_num++) { + for(int i = 0; i < 8; i++) { + MAC[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; + } + + uint8_t intermediate[8]; + mbedtls_des3_crypt_ecb(&ctx, MAC, intermediate); + for(int i = 0; i < 8; i++) { + intermediate[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 15 - i]; + } + + mbedtls_des3_crypt_ecb(&ctx, intermediate, MAC); + } + + mbedtls_des3_free(&ctx); +} + +static void felica_lite_calculate_mac_a( + uint8_t* random_challenge, + uint8_t* session_key, + uint8_t* iv, + uint8_t* block_data, + size_t block_count, + uint8_t* MAC_A) { + uint8_t SK[16]; + uint8_t intermediate_a[8]; + uint8_t intermediate_b[8]; + + for(int i = 0; i < 8; i++) { + intermediate_a[i] = iv[7 - 1] ^ random_challenge[7 - i]; + SK[i] = session_key[7 - i]; + SK[i + 8] = session_key[15 - i]; + } + + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set3key_enc(&ctx, SK); + mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); + + for(size_t block_num = 0; block_num < block_count; block_num++) { + for(int i = 0; i < 8; i++) { + intermediate_b[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; + } + + mbedtls_des3_crypt_ecb(&ctx, intermediate_b, intermediate_a); + for(int i = 0; i < 8; i++) { + intermediate_a[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; + } + + mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); + } + + for(int i = 0; i < 8; i++) { + MAC_A[i] = intermediate_b[7 - 1]; + } +} + +static void felica_lite_calculate_mac_a_for_write( + uint8_t* random_challenge, + uint8_t* session_key, + uint32_t write_count, + uint8_t block_number, + uint8_t* block_data, + uint8_t* MAC_A) { + uint8_t iv[8]; + nfc_util_num2bytes(write_count, 3, iv); + iv[3] = 0x00; + iv[4] = block_number; + iv[5] = 0x00; + iv[6] = 0x91; + iv[7] = 0x00; + + uint8_t SK[16]; + for(int i = 0; i < 8; i++) { + SK[i] = session_key[i + 8]; + SK[i + 8] = session_key[i]; + } + + felica_lite_calculate_mac_a(random_challenge, SK, iv, block_data, 1, MAC_A); +} + +static void felica_lite_calculate_mac_a_for_read( + uint8_t* random_challenge, + uint8_t* session_key, + uint8_t* block_list, + uint8_t block_list_count, + uint8_t* block_data, + uint8_t block_count, + uint8_t* MAC_A) { + uint8_t iv[8] = {0}; + + uint8_t block_list_to_write = MIN(block_list_count, 4); + for(int i = 0; i < block_list_to_write; i++) { + iv[i * 2] = block_list[i]; + } + if(block_list_to_write < 4) { + iv[6] = 0xFF; + iv[7] = 0xFF; + } + if(block_list_to_write < 3) { + iv[4] = 0xFF; + iv[5] = 0xFF; + } + + felica_lite_calculate_mac_a(random_challenge, session_key, iv, block_data, block_count, MAC_A); +} + /** Parse common FeliCa response headers. * * This parses and validates the most commonly occurring response header types. @@ -165,7 +375,7 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { * @param always_succeed When set to true, skip status flags (sf1 and sf2) parsing. * @return The number of bytes parsed, or 0 when response is invalid or status flags are set. */ -static uint8_t felica_consume_header( +static uint8_t felica_consume_unencrypted_header( uint8_t* buf, uint8_t len, FelicaReader* reader, @@ -261,7 +471,8 @@ uint16_t felica_parse_unencrypted_read( FelicaReader* reader, uint8_t* out, uint16_t out_len) { - uint8_t consumed = felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); + uint8_t consumed = + felica_consume_unencrypted_header(buf, len, reader, FELICA_UNENCRYPTED_READ_RES, false); if(!consumed) { return 0; } @@ -345,7 +556,7 @@ uint8_t felica_lite_prepare_unencrypted_write( bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader) { uint8_t consumed = - felica_consume_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); + felica_consume_unencrypted_header(buf, len, reader, FELICA_UNENCRYPTED_WRITE_RES, false); if(!consumed) { return false; } @@ -364,7 +575,7 @@ bool felica_parse_request_system_code( FelicaReader* reader, FelicaSystemArray_t* systems) { uint8_t consumed = - felica_consume_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); + felica_consume_unencrypted_header(buf, len, reader, FELICA_REQUEST_SYSTEM_CODE_RES, true); if(consumed == 0) { return false; } diff --git a/lib/nfc/protocols/felica_util.c b/lib/nfc/protocols/felica_util.c index 481ea5ed7..47f66fcc5 100644 --- a/lib/nfc/protocols/felica_util.c +++ b/lib/nfc/protocols/felica_util.c @@ -11,6 +11,20 @@ uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t uni return TIME_CONSTANT_US * scale * (base_cost_factor + unit_cost_factor * units); } +bool felica_lite_is_issued(FelicaLiteInfo* lite_info) { + // System blocks aren't writable? + if(lite_info->memory_config[2] == 0x00) { + return false; + } + + // MC is not writable? + if(lite_info->memory_config[1] & 0x80) { + return false; + } + + return true; +} + FuriString* felica_get_system_name(FelicaSystem* system) { uint16_t code = system->code; diff --git a/lib/nfc/protocols/felica_util.h b/lib/nfc/protocols/felica_util.h index e53d66805..de7ff884a 100644 --- a/lib/nfc/protocols/felica_util.h +++ b/lib/nfc/protocols/felica_util.h @@ -1,5 +1,6 @@ #include "./felica.h" uint_least32_t felica_estimate_timing_us(uint_least8_t timing, uint_least8_t units); +bool felica_lite_is_issued(FelicaLiteInfo* lite_info); FuriString* felica_get_system_name(FelicaSystem* system); FuriString* felica_get_service_name(FelicaService* service); \ No newline at end of file From f36beec01bac80a7ccf84a957ef4a3328ecc9135 Mon Sep 17 00:00:00 2001 From: nullableVoidPtr <30564701+nullableVoidPtr@users.noreply.github.com> Date: Sat, 29 Apr 2023 08:24:45 +0800 Subject: [PATCH 002/370] WIP --- .../main/nfc/scenes/nfc_scene_felica_menu.c | 9 +- lib/STM32CubeWB | 2 +- lib/nfc/nfc_device.c | 234 ++++++++++++++++-- lib/nfc/protocols/felica.c | 1 + lib/nfc/protocols/felica.h | 3 +- 5 files changed, 223 insertions(+), 26 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_felica_menu.c b/applications/main/nfc/scenes/nfc_scene_felica_menu.c index b989047d6..7aaba30db 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_menu.c @@ -4,7 +4,9 @@ enum SubmenuIndex { /* SubmenuIndexUnlock, + */ SubmenuIndexSave, + /* SubmenuIndexEmulate, */ SubmenuIndexInfo, @@ -24,8 +26,10 @@ void nfc_scene_felica_menu_on_enter(void* context) { /* submenu_add_item( submenu, "Unlock", SubmenuIndexUnlock, nfc_scene_felica_menu_submenu_callback, nfc); + */ submenu_add_item( submenu, "Save", SubmenuIndexSave, nfc_scene_felica_menu_submenu_callback, nfc); + /* submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_felica_menu_submenu_callback, nfc); */ @@ -43,13 +47,13 @@ bool nfc_scene_felica_menu_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - /* if(event.event == SubmenuIndexSave) { nfc->dev->format = NfcDeviceSaveFormatFelica; // Clear device name nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; + /* } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { @@ -61,9 +65,8 @@ bool nfc_scene_felica_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexUnlock) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockMenu); consumed = true; - } else */ - if(event.event == SubmenuIndexInfo) { + } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaInfoSelect); consumed = true; } diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB index 06b8133fa..a9e29b431 160000 --- a/lib/STM32CubeWB +++ b/lib/STM32CubeWB @@ -1 +1 @@ -Subproject commit 06b8133fa295474507b55b1a5695d4b8bf804ed6 +Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 6a2bcfcc0..09d9008d6 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -20,6 +20,7 @@ static const uint32_t nfc_keys_file_version = 1; // Protocols format versions static const uint32_t nfc_mifare_classic_data_format_version = 2; static const uint32_t nfc_mifare_ultralight_data_format_version = 1; +static const uint32_t nfc_felica_data_format_version = 1; NfcDevice* nfc_device_alloc() { NfcDevice* nfc_dev = malloc(sizeof(NfcDevice)); @@ -58,6 +59,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare Classic"); } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { furi_string_set(format_string, "Mifare DESFire"); + } else if(dev->format == NfcDeviceSaveFormatFelica) { + furi_string_set(format_string, "FeliCa"); } else { furi_string_set(format_string, "Unknown"); } @@ -781,6 +784,192 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* return saved; } +static bool nfc_device_save_felica_lite(FlipperFormat* file, FelicaLiteInfo* info) { + bool saved = false; + FuriString* key = furi_string_alloc(); + + do { + flipper_format_write_comment_cstr(file, "Lite(-S) System"); + flipper_format_write_hex(file, "Data Format Code", info->data_format_code, sizeof(uint16_t)); + flipper_format_write_hex(file, "ID Arbitrary Value", info->ID_value, 6); + flipper_format_write_hex(file, "Memory Config", info->memory_config, FELICA_BLOCK_SIZE); + + for(uint8_t block_num = 0; block_num < 14; block_num++) { + FuriString* spad_str = furi_string_alloc(); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->S_PAD[block_num] != NULL) { + furi_string_cat_printf(spad_str, "%02X ", info->S_PAD[block_num][i]); + } else { + furi_string_cat_printf(spad_str, "?? "); + } + } + + furi_string_printf(key, "S_PAD%d", block_num); + flipper_format_write_string(file, furi_string_get_cstr(key), spad_str); + } + + FuriString* reg_str = furi_string_alloc(); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(reg_str, "%02X ", info->REG[i]); + } else { + furi_string_cat_printf(reg_str, "?? "); + } + } + flipper_format_write_string(file, "REG", reg_str); + + flipper_format_write_hex(file, "Card Key Version", &info->card_key_version, sizeof(uint16_t)); + FuriString* ck1_str = furi_string_alloc(); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(ck1_str, "%02X ", info->card_key_1[i]); + } else { + furi_string_cat_printf(ck1_str, "?? "); + } + } + flipper_format_write_string(file, "Card Key 1", ck1_str); + + FuriString* ck2_str = furi_string_alloc(); + for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { + if(info->REG != NULL) { + furi_string_cat_printf(ck2_str, "%02X ", info->card_key_2[i]); + } else { + furi_string_cat_printf(ck2_str, "?? "); + } + } + flipper_format_write_string(file, "Card Key 2", ck2_str); + + flipper_format_write_hex(file, "Fixed Challenge MAC Response", info->MAC, 8); + + flipper_format_write_bool(file, "Is Lite-S", &info->is_lite_s, 1); + if (info->is_lite_s) { + flipper_format_write_hex(file, "Fixed Challenge MAC-A Response", info->MAC_A, 8); + flipper_format_write_uint32(file, "Write Count", &info->write_count, 1); + } + + } while(false); + + return saved; +} + +static bool nfc_device_save_felica_area(FlipperFormat* file, FelicaArea* area) { + bool saved = false; + FuriString* prefix = furi_string_alloc_printf("Area %d", area->number); + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s Can Create Subareas", prefix); + flipper_format_write_bool(file, furi_string_get_cstr(key), &area->can_create_subareas, 1); + furi_string_printf(key, "%s End Service Code", prefix); + flipper_format_write_hex(file, furi_string_get_cstr(key), (uint8_t*)&area->end_service_code, sizeof(uint16_t)); + + bool node_saved = true; + for + M_EACH(node, area->nodes, FelicaNodeArray_t) { + if (nfc_device_save_felica_node(file, node)) { + node_saved = false; + break; + } + } + + if (!node_saved) break; + saved = true; + } while(false); + + return saved; +} + +static bool nfc_device_save_felica_service(FlipperFormat* file, FelicaService* service) { + bool saved = false; + FuriString* prefix = furi_string_alloc_printf("Service %d", service->number); + FuriString* key = furi_string_alloc(); + + do { + furi_string_printf(key, "%s Is Extended Overlap", prefix); + flipper_format_write_bool(file, furi_string_get_cstr(key), &service->is_extended_overlap, 1); + if (service->is_extended_overlap) { + furi_string_printf(key, "%s Overlap Target", prefix); + flipper_format_write_hex(file, furi_string_get_cstr(key), (uint8_t*)&service->overlap_target, sizeof(uint16_t)); + + furi_string_printf(key, "%s Block Start", prefix); + const uint32_t block_start = service->block_start; + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_start, 1); + + furi_string_printf(key, "%s Block Count", prefix); + const uint32_t block_count = service->block_count; + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1); + + uint32_t i = 0; + for + M_EACH(block, service->blocks, FelicaBlockArray_t) { + furi_string_printf(key, "%s Block %d", prefix, i); + flipper_format_write_hex(file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE); + } + } else { + furi_string_printf(key, "%s Block Count", prefix); + uint32_t block_count = FelicaBlockArray_size(service->blocks); + flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1); + uint32_t i = 0; + for + M_EACH(block, service->blocks, FelicaBlockArray_t) { + furi_string_printf(key, "%s Block %d", prefix, i); + flipper_format_write_hex(file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE); + } + } + + saved = true; + } while(false); + +} + +static bool nfc_device_save_felica_node(FlipperFormat* file, FelicaNode* node) { + bool saved = false; + FuriString* key = furi_string_alloc(); + + do { + if(node->type == FelicaNodeTypeArea) { + if(!nfc_device_save_felica_node(file, node->area)) { + saved = false; + break; + } + } else if(node->type == FelicaNodeTypeService) { + if(!nfc_device_save_felica_service(file, node->area)) { + saved = false; + break; + } + } + + saved = true; + } while(false); + + return saved; +} + +static bool nfc_device_save_felica_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + FelicaData* data = &dev->dev_data.felica_data; + // Save FeliCa specific data + do { + if(!flipper_format_write_comment_cstr(file, "FeliCa specific data")) break; + if(!flipper_format_write_uint32( + file, "Data format version", &nfc_felica_data_format_version, 1)) + break; + + for + M_EACH(system, data->systems, FelicaSystemArray_t) { + flipper_format_write_hex(file, "System", &system->number, sizeof(uint8_t)); + flipper_format_write_hex(file, "Code", (uint8_t*)&system->code, sizeof(uint16_t)); + if (system->code == LITE_SYSTEM_CODE) { + nfc_device_save_felica_lite(file, &system->lite_info); + } else { + nfc_device_save_felica_node(file, &system->root); + } + } + } while(false); + + return saved; +} + static void nfc_device_load_mifare_classic_block( FuriString* block_str, MfClassicData* data, @@ -1069,30 +1258,35 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; // Write nfc device type if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic")) + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic, FeliCa")) break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) - break; - if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]}; - if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break; - // Save more data if necessary - if(dev->format == NfcDeviceSaveFormatMifareUl) { - if(!nfc_device_save_mifare_ul_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { - if(!nfc_device_save_mifare_df_data(file, dev)) break; - } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { - // Save data - if(!nfc_device_save_mifare_classic_data(file, dev)) break; - // Save keys cache - if(!nfc_device_save_mifare_classic_keys(dev)) break; + if(data->type == FuriHalNfcTypeA) { + // Write UID, ATQA, SAK + if(!flipper_format_write_comment_cstr( + file, "UID, ATQA and SAK are common for all A formats")) + break; + if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->a_data.atqa[1], data->a_data.atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->a_data.sak, 1)) break; + // Save more data if necessary + if(dev->format == NfcDeviceSaveFormatMifareUl) { + if(!nfc_device_save_mifare_ul_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { + if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { + // Save data + if(!nfc_device_save_mifare_classic_data(file, dev)) break; + // Save keys cache + if(!nfc_device_save_mifare_classic_keys(dev)) break; + } + saved = true; + } else if(data->type == FuriHalNfcTypeF) { + if(!nfc_device_save_felica_data(file, dev)) break; } - saved = true; } while(0); if(!saved) { //-V547 diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index c5823a221..bf9da65f5 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -799,6 +799,7 @@ bool felica_lite_dump_data( } } if(data->type == FelicaICTypeLiteS) { + lite_info->is_lite_s = true; const uint8_t fixed_s_blocks[] = { CARD_KEY_LITE_BLOCK, MAC_A_LITE_BLOCK, diff --git a/lib/nfc/protocols/felica.h b/lib/nfc/protocols/felica.h index f8e504102..876596bb5 100644 --- a/lib/nfc/protocols/felica.h +++ b/lib/nfc/protocols/felica.h @@ -206,6 +206,7 @@ typedef struct { uint16_t card_key_version; uint8_t memory_config[FELICA_BLOCK_SIZE]; + bool is_lite_s; // Lite-S only uint8_t MAC_A[8]; uint32_t write_count; @@ -294,8 +295,6 @@ uint8_t felica_lite_prepare_unencrypted_write( const uint8_t* block_data); bool felica_parse_unencrypted_write(uint8_t* buf, uint8_t len, FelicaReader* reader); -bool felica_lite_can_read_without_mac(uint8_t* mc_r_restr, uint8_t block_number); - void felica_define_normal_block(FelicaService* service, uint16_t number, uint8_t* data); void felica_push_normal_block(FelicaService* service, uint8_t* data); From 263c94d1673f151a6dfb04c1e5a1d528469a3c44 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 3 Jun 2023 15:03:07 +0300 Subject: [PATCH 003/370] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e4a49fb2..d93dd25ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ### New changes * If you have copied any apps manually into `apps` folder - remove `apps` folder or that specific apps you copied on your microSD before installing this release to avoid issues due to OFW API version update! If you using regular builds or extra pack builds (e) without your manually added apps, all included apps will be installed automatically, no extra actions needed! ----- +* Only in release 052 -> **Multiple Extra pack apps was fixed!** -> TAMA P1, Flizzer Tracker, Video Player, Music Tracker +### Previous changes * SubGHz Remote: Fixed BinRAW support, + many other fixes (by @gid9798 | PR #492) * SubGHz: Fix KL: Stilmatic support + add manually support * SubGHz: Keeloq mfname refactoring (by @gid9798 | PR #479) From 2008247e658b8f4a60fd29c6593d0fa23e416d81 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Sat, 3 Jun 2023 22:31:24 +1000 Subject: [PATCH 004/370] Remove delay from emulation loop. This improves compatibility when the reader is Android. --- lib/nfc/nfc_worker.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index f2d07e4c2..2819ea7ad 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -179,7 +179,6 @@ void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { } } } - furi_delay_ms(10); } nfcv_emu_deinit(nfcv_data); @@ -207,7 +206,6 @@ void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) { nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); } } - furi_delay_ms(10); } nfcv_emu_deinit(nfcv_data); From a0e39ddb67c0d2376f8a726ca1d5acbc6740b202 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:36:20 +0300 Subject: [PATCH 005/370] more checks just in case?? --- lib/digital_signal/digital_signal.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 6ccfcf280..9dd13886d 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -243,11 +243,15 @@ static void digital_signal_stop_timer() { LL_TIM_DisableUpdateEvent(TIM2); LL_TIM_DisableDMAReq_UPDATE(TIM2); - furi_hal_bus_disable(FuriHalBusTIM2); + if(furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_disable(FuriHalBusTIM2); + } } static void digital_signal_setup_timer() { - furi_hal_bus_enable(FuriHalBusTIM2); + if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_enable(FuriHalBusTIM2); + } LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); From 5a2fbf4af3e65369a6beab12af86971e9e3dc49e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:36:19 +0300 Subject: [PATCH 006/370] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d93dd25ba..d92b032a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * If you have copied any apps manually into `apps` folder - remove `apps` folder or that specific apps you copied on your microSD before installing this release to avoid issues due to OFW API version update! If you using regular builds or extra pack builds (e) without your manually added apps, all included apps will be installed automatically, no extra actions needed! ----- * Only in release 052 -> **Multiple Extra pack apps was fixed!** -> TAMA P1, Flizzer Tracker, Video Player, Music Tracker +* NFC V: Remove delay from emulation loop. This improves compatibility when the reader is Android. ### Previous changes * SubGHz Remote: Fixed BinRAW support, + many other fixes (by @gid9798 | PR #492) * SubGHz: Fix KL: Stilmatic support + add manually support From 75ee4efa31b3cb38b55b031be1da0c42ff557890 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:46:55 +0300 Subject: [PATCH 007/370] Fix ibtn fuzzer file loading (temp) --- CHANGELOG.md | 1 + .../scene/ibtnfuzzer_scene_load_file.c | 193 ++++++++++++++---- 2 files changed, 154 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d92b032a4..f81527b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ----- * Only in release 052 -> **Multiple Extra pack apps was fixed!** -> TAMA P1, Flizzer Tracker, Video Player, Music Tracker * NFC V: Remove delay from emulation loop. This improves compatibility when the reader is Android. +* Plugins: iButton Fuzzer -> Fix v2 key files load (all new saved files) ### Previous changes * SubGHz Remote: Fixed BinRAW support, + many other fixes (by @gid9798 | PR #492) * SubGHz: Fix KL: Stilmatic support + add manually support diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c index 47d5122ab..92f79a424 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c @@ -10,6 +10,7 @@ bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); FuriString* temp_str; temp_str = furi_string_alloc(); + bool key_v2 = false; do { if(!flipper_format_file_open_existing(fff_data_file, file_path)) { FURI_LOG_E(TAG, "Error open file %s", file_path); @@ -30,10 +31,43 @@ bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { // Key type if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key type"); - break; + FURI_LOG_E(TAG, "Missing or incorrect Key type, checking for typ2.."); + + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Failed to rewind file"); + break; + } + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + furi_string_reset(context->notification_msg); + furi_string_set( + context->notification_msg, "Missing or incorrect Protocol or Key type"); + break; + } + FURI_LOG_I(TAG, "Key type V2: %s", furi_string_get_cstr(temp_str)); + key_v2 = true; + + if(context->proto == DS1990) { + if(strcmp(furi_string_get_cstr(temp_str), "DS1990") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } else if(context->proto == Cyfral) { + if(strcmp(furi_string_get_cstr(temp_str), "Cyfral") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } else { + if(strcmp(furi_string_get_cstr(temp_str), "Metakom") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } } else { FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); @@ -60,46 +94,125 @@ bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { } } } - - // Data - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(context->proto == DS1990) { - if(furi_string_size(context->data_str) != 23) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == Cyfral) { - if(furi_string_size(context->data_str) != 5) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } + if(!key_v2) { + // Data + if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Data"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Missing or incorrect Key"); + break; } else { - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); + + if(context->proto == DS1990) { + if(furi_string_size(context->data_str) != 23) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } else if(context->proto == Cyfral) { + if(furi_string_size(context->data_str) != 5) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } else { + if(furi_string_size(context->data_str) != 11) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } + + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); } } + } else { + // Data + if(context->proto == DS1990) { + if(!flipper_format_read_string(fff_data_file, "Rom Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Rom Data"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Missing or incorrect Rom Data"); + break; + } else { + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + if(furi_string_size(context->data_str) != 23) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } + } + } else if(context->proto == Cyfral) { + if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Data"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Missing or incorrect Data"); + break; + } else { + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); + + if(furi_string_size(context->data_str) != 5) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } + } + } else { + if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Data"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Missing or incorrect Data"); + break; + } else { + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); + + if(furi_string_size(context->data_str) != 11) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } + } } } From 6c6a4816836efb4b591900fbb366c96b802114d9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:59:49 +0100 Subject: [PATCH 008/370] Fix switching nsfw mode --- lib/xtreme/assets.c | 1 + lib/xtreme/settings.c | 1 - lib/xtreme/xtreme.h | 3 ++- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/xtreme/assets.c b/lib/xtreme/assets.c index e291d6074..59df1206d 100644 --- a/lib/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -105,6 +105,7 @@ void XTREME_ASSETS_LOAD() { if(!furi_hal_is_normal_boot()) return; const char* pack = XTREME_SETTINGS()->asset_pack; + XTREME_SETTINGS()->is_nsfw = !strncmp(pack, "NSFW", strlen("NSFW")); if(pack[0] == '\0') return; Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index f87e30f5f..fb2a48d1b 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -43,7 +43,6 @@ void XTREME_SETTINGS_LOAD() { FuriString* string = furi_string_alloc(); if(flipper_format_read_string(file, "asset_pack", string)) { strlcpy(x->asset_pack, furi_string_get_cstr(string), XTREME_ASSETS_PACK_NAME_LEN); - x->is_nsfw = strncmp(x->asset_pack, "NSFW", strlen("NSFW")) == 0; } furi_string_free(string); uint32_t u; diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index a827730f8..7a30adf8b 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -13,8 +13,9 @@ extern "C" { #define XTREME_ASSETS_PACK_NAME_LEN 32 typedef struct { - char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; bool is_nsfw; // TODO: replace with packs text support + + char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; uint32_t anim_speed; int32_t cycle_anims; bool unlock_anims; From e50c8f496bae17574f042640b9575d136fa7f175 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:00:04 +0100 Subject: [PATCH 009/370] Fix weird format --- lib/xtreme/settings.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index fb2a48d1b..ecf1c905b 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -118,9 +118,7 @@ void XTREME_SETTINGS_LOAD() { } flipper_format_rewind(file); if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { - { - x->dark_mode = b; - } + x->dark_mode = b; } flipper_format_rewind(file); if(flipper_format_read_uint32(file, "favorite_timeout", &u, 1)) { From eda2ab4e5dda65384799825d328ae797a54da63b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:11:59 +0100 Subject: [PATCH 010/370] Fix battery icon cast (#286) --- lib/xtreme/settings.c | 2 +- lib/xtreme/xtreme.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index ecf1c905b..f3e3cd6c2 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -169,7 +169,7 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "lockscreen_date", &x->lockscreen_date, 1); flipper_format_write_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); flipper_format_write_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); - flipper_format_write_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); + flipper_format_write_uint32(file, "battery_icon", &x->battery_icon, 1); flipper_format_write_bool(file, "status_icons", &x->status_icons, 1); flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 7a30adf8b..ea6076354 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -28,7 +28,7 @@ typedef struct { bool lockscreen_date; bool lockscreen_statusbar; bool lockscreen_prompt; - BatteryIcon battery_icon; + uint32_t battery_icon; bool status_icons; bool bar_borders; bool bar_background; From 6d433edc3887d007b20c9dc6ee05dfcc1b459b1f Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 10:48:46 +0200 Subject: [PATCH 011/370] Add furi_hal_version_uid_default (Fix TOTP) --- applications/external/totp/services/crypto/crypto.c | 2 +- firmware/targets/f7/api_symbols.csv | 3 ++- firmware/targets/f7/furi_hal/furi_hal_version.c | 4 ++++ firmware/targets/furi_hal_include/furi_hal_version.h | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 03d9c9d51..d01fe5c11 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -90,7 +90,7 @@ CryptoSeedIVResult max_i = uid_size; } - const uint8_t* uid = furi_hal_version_uid(); + const uint8_t* uid = furi_hal_version_uid_default(); for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index c033cf3ea..5128fdace 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.2,, +Version,+,28.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1481,6 +1481,7 @@ Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, Function,-,furi_hal_version_init,void, Function,-,furi_hal_version_set_name,void,const char* Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_default,const uint8_t*, Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index d0857f288..0e5f428ba 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -314,6 +314,10 @@ size_t furi_hal_version_uid_size() { return 64 / 8; } +const uint8_t* furi_hal_version_uid_default() { + return (const uint8_t*)UID64_BASE; +} + const uint8_t* furi_hal_version_uid() { if(version_get_custom_name(NULL) != NULL) { return (const uint8_t*)&(*((uint32_t*)version_get_custom_name(NULL))); diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index f5e5ca49a..4a3f4c170 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -204,6 +204,8 @@ size_t furi_hal_version_uid_size(); */ const uint8_t* furi_hal_version_uid(); +const uint8_t* furi_hal_version_uid_default(); + #ifdef __cplusplus } #endif From b7f91fe5a5bad433481f9a48540e61e78fc1dfbe Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 10:48:51 +0200 Subject: [PATCH 012/370] Add furi_hal_version_uid_default (Fix TOTP) --- applications/external/totp/services/crypto/crypto.c | 2 +- firmware/targets/f7/api_symbols.csv | 1 + firmware/targets/f7/furi_hal/furi_hal_version.c | 4 ++++ firmware/targets/furi_hal_include/furi_hal_version.h | 2 ++ lib/STM32CubeWB | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) create mode 160000 lib/STM32CubeWB diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 03d9c9d51..d01fe5c11 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -90,7 +90,7 @@ CryptoSeedIVResult max_i = uid_size; } - const uint8_t* uid = furi_hal_version_uid(); + const uint8_t* uid = furi_hal_version_uid_default(); for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 863cbcb6f..a8a32069b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1531,6 +1531,7 @@ Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, Function,-,furi_hal_version_init,void, Function,-,furi_hal_version_set_name,void,const char* Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_default,const uint8_t*, Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index 814ec3926..66580945e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -318,6 +318,10 @@ size_t furi_hal_version_uid_size() { return 64 / 8; } +const uint8_t* furi_hal_version_uid_default() { + return (const uint8_t*)UID64_BASE; +} + const uint8_t* furi_hal_version_uid() { if(version_get_custom_name(NULL) != NULL) { return (const uint8_t*)&(*((uint32_t*)version_get_custom_name(NULL))); diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index df97a988a..351a4da10 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -204,6 +204,8 @@ size_t furi_hal_version_uid_size(); */ const uint8_t* furi_hal_version_uid(); +const uint8_t* furi_hal_version_uid_default(); + #ifdef __cplusplus } #endif diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB new file mode 160000 index 000000000..a9e29b431 --- /dev/null +++ b/lib/STM32CubeWB @@ -0,0 +1 @@ +Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 From a12f563d1c44b9bd6b71035bc50f6a4b8cfea4bf Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 10:56:53 +0200 Subject: [PATCH 013/370] revert in app, dumb idea, needs some more work. --- applications/external/totp/services/crypto/crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index d01fe5c11..03d9c9d51 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -90,7 +90,7 @@ CryptoSeedIVResult max_i = uid_size; } - const uint8_t* uid = furi_hal_version_uid_default(); + const uint8_t* uid = furi_hal_version_uid(); for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } From d339adc8abe1f9cd5e2c223782ec83815c71c9e3 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 11:08:47 +0200 Subject: [PATCH 014/370] where tf did this came from, lol? --- lib/STM32CubeWB | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lib/STM32CubeWB diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB deleted file mode 160000 index a9e29b431..000000000 --- a/lib/STM32CubeWB +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 From 0f8ca0a56347571963ade5af580e843eff946543 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 4 Jun 2023 12:52:24 +0300 Subject: [PATCH 015/370] Apply required TOTP changes --- applications/external/totp/services/crypto/crypto.c | 5 ++++- firmware/targets/f7/api_symbols.csv | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 03d9c9d51..448529100 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -89,8 +89,11 @@ CryptoSeedIVResult } else { max_i = uid_size; } - +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_UL_XFW + const uint8_t* uid = furi_hal_version_uid_default(); +#else const uint8_t* uid = furi_hal_version_uid(); +#endif for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 5128fdace..58f439b3a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.3,, +Version,+,28.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, From d7b5eac90b13eb1dc66181c7ea5b35af06096297 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 4 Jun 2023 11:57:00 +0200 Subject: [PATCH 016/370] Add ko-fi --- ReadMe.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 82cbfd7a0..c2cdaf59e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -222,12 +222,13 @@ $ ./fbt resources icons dolphin_ext ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, just specify amount and hit send -- **[Patreon](https://patreon.com/CynthiaLabs)** -- **[Paypal](https://paypal.me/RdX2020)** +- **[Patreon](https://patreon.com/CynthiaLabs)** ❤️ Account needed, subscription with perks across my entire org. +- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)** Account needed, one-time +- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` **Thanks for all your support <3** ---- -

"What we do for ourselves dies with us. What we do for others and the world remains and is immortal.” ― Albert Pine

\ No newline at end of file +

"What we do for ourselves dies with us. What we do for others and the world remains and is immortal.” ― Albert Pine

From ec2c6467caf5712961674a9800cb0b0e9e2f524d Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 11:58:06 +0200 Subject: [PATCH 017/370] Also add ko-fi to release script --- .github/workflow_data/release.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 0e716526a..88daf5d9b 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -21,9 +21,10 @@ ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, just specify amount and hit send -- **[Patreon](https://patreon.com/CynthiaLabs)** -- **[Paypal](https://paypal.me/RdX2020)** +- **[Patreon](https://patreon.com/CynthiaLabs)** ❤️ Account needed, subscription with perks across my entire org. +- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)** Account needed, one-time +- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` **Thanks for all your support <3** From f5b7ce5a0865d7134d9c0a383a7bd7a04b84be30 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 12:00:06 +0200 Subject: [PATCH 018/370] consistency is power --- .github/workflow_data/release.md | 4 ++-- ReadMe.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 88daf5d9b..77316bae7 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -21,9 +21,9 @@ ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Patreon](https://patreon.com/CynthiaLabs)** ❤️ Account needed, subscription with perks across my entire org. +- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. - **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time -- **[Paypal](https://paypal.me/RdX2020)** Account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time - **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` diff --git a/ReadMe.md b/ReadMe.md index c2cdaf59e..e99e1b9dd 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -222,9 +222,9 @@ $ ./fbt resources icons dolphin_ext ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Patreon](https://patreon.com/CynthiaLabs)** ❤️ Account needed, subscription with perks across my entire org. +- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. - **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time -- **[Paypal](https://paypal.me/RdX2020)** Account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time - **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` From 2e47c5a2acbcf5e4cfd614fe209043760e90e9a3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:27:59 +0300 Subject: [PATCH 019/370] lets try patch --- .ci_files/rgb.patch | 556 ++++++++++++++++++++++++++++++++++++++++++++ .drone.yml | 96 +++++++- 2 files changed, 648 insertions(+), 4 deletions(-) create mode 100644 .ci_files/rgb.patch diff --git a/.ci_files/rgb.patch b/.ci_files/rgb.patch new file mode 100644 index 000000000..804034bab --- /dev/null +++ b/.ci_files/rgb.patch @@ -0,0 +1,556 @@ +diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c +index f91a73f32..b559a79ad 100644 +--- a/applications/services/notification/notification_app.c ++++ b/applications/services/notification/notification_app.c +@@ -6,6 +6,7 @@ + #include "notification.h" + #include "notification_messages.h" + #include "notification_app.h" ++#include "applications/settings/notification_settings/rgb_backlight.h" + + #define TAG "NotificationSrv" + +@@ -564,6 +565,7 @@ int32_t notification_srv(void* p) { + break; + case SaveSettingsMessage: + notification_save_settings(app); ++ rgb_backlight_save_settings(); + break; + } + +diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c +index f5d7a82ca..930c0bd1f 100644 +--- a/applications/settings/notification_settings/notification_settings_app.c ++++ b/applications/settings/notification_settings/notification_settings_app.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + + #define MAX_NOTIFICATION_SETTINGS 4 + +@@ -73,7 +74,6 @@ const bool vibro_value[VIBRO_COUNT] = {false, true}; + static void backlight_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); +- + variable_item_set_current_value_text(item, backlight_text[index]); + app->notification->settings.display_brightness = backlight_value[index]; + notification_message(app->notification, &sequence_display_backlight_on); +@@ -125,6 +125,14 @@ static void vibro_changed(VariableItem* item) { + notification_message(app->notification, &sequence_single_vibro); + } + ++static void color_changed(VariableItem* item) { ++ NotificationAppSettings* app = variable_item_get_context(item); ++ uint8_t index = variable_item_get_current_value_index(item); ++ rgb_backlight_set_color(index); ++ variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); ++ notification_message(app->notification, &sequence_display_backlight_on); ++} ++ + static uint32_t notification_app_settings_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +@@ -143,7 +151,13 @@ static NotificationAppSettings* alloc_settings() { + uint8_t value_index; + + item = variable_item_list_add( +- app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); ++ app->variable_item_list, "LCD Color", rgb_backlight_get_color_count(), color_changed, app); ++ value_index = rgb_backlight_get_settings()->display_color_index; ++ variable_item_set_current_value_index(item, value_index); ++ variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); ++ ++ item = variable_item_list_add( ++ app->variable_item_list, "LCD Brightness", BACKLIGHT_COUNT, backlight_changed, app); + value_index = value_index_float( + app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); + variable_item_set_current_value_index(item, value_index); +@@ -215,6 +229,7 @@ int32_t notification_settings_app(void* p) { + NotificationAppSettings* app = alloc_settings(); + view_dispatcher_run(app->view_dispatcher); + notification_message_save_settings(app->notification); ++ + free_settings(app); + return 0; + } +diff --git a/applications/settings/notification_settings/rgb_backlight.c b/applications/settings/notification_settings/rgb_backlight.c +new file mode 100644 +index 000000000..269b544ae +--- /dev/null ++++ b/applications/settings/notification_settings/rgb_backlight.c +@@ -0,0 +1,171 @@ ++/* ++ RGB backlight FlipperZero driver ++ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "rgb_backlight.h" ++#include ++#include ++ ++#define RGB_BACKLIGHT_SETTINGS_VERSION 5 ++#define RGB_BACKLIGHT_SETTINGS_FILE_NAME ".rgb_backlight.settings" ++#define RGB_BACKLIGHT_SETTINGS_PATH EXT_PATH(RGB_BACKLIGHT_SETTINGS_FILE_NAME) ++ ++#define COLOR_COUNT (sizeof(colors) / sizeof(RGBBacklightColor)) ++ ++#define TAG "RGB Backlight" ++ ++static RGBBacklightSettings rgb_settings = { ++ .version = RGB_BACKLIGHT_SETTINGS_VERSION, ++ .display_color_index = 0, ++ .settings_is_loaded = false}; ++ ++static const RGBBacklightColor colors[] = { ++ {"Orange", 255, 79, 0}, ++ {"Yellow", 255, 170, 0}, ++ {"Spring", 167, 255, 0}, ++ {"Lime", 0, 255, 0}, ++ {"Aqua", 0, 255, 127}, ++ {"Cyan", 0, 210, 210}, ++ {"Azure", 0, 127, 255}, ++ {"Blue", 0, 0, 255}, ++ {"Purple", 127, 0, 255}, ++ {"Magenta", 210, 0, 210}, ++ {"Pink", 255, 0, 127}, ++ {"Red", 255, 0, 0}, ++ {"White", 140, 140, 140}, ++}; ++ ++uint8_t rgb_backlight_get_color_count(void) { ++ return COLOR_COUNT; ++} ++ ++const char* rgb_backlight_get_color_text(uint8_t index) { ++ return colors[index].name; ++} ++ ++void rgb_backlight_load_settings(void) { ++ //Не загружать данные из внутренней памяти при загрузке в режиме DFU ++ FuriHalRtcBootMode bm = furi_hal_rtc_get_boot_mode(); ++ if(bm == FuriHalRtcBootModeDfu) { ++ rgb_settings.settings_is_loaded = true; ++ return; ++ } ++ ++ RGBBacklightSettings settings; ++ File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); ++ const size_t settings_size = sizeof(RGBBacklightSettings); ++ ++ FURI_LOG_I(TAG, "loading settings from \"%s\"", RGB_BACKLIGHT_SETTINGS_PATH); ++ bool fs_result = ++ storage_file_open(file, RGB_BACKLIGHT_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING); ++ ++ if(fs_result) { ++ uint16_t bytes_count = storage_file_read(file, &settings, settings_size); ++ ++ if(bytes_count != settings_size) { ++ fs_result = false; ++ } ++ } ++ ++ if(fs_result) { ++ FURI_LOG_I(TAG, "load success"); ++ if(settings.version != RGB_BACKLIGHT_SETTINGS_VERSION) { ++ FURI_LOG_E( ++ TAG, ++ "version(%d != %d) mismatch", ++ settings.version, ++ RGB_BACKLIGHT_SETTINGS_VERSION); ++ } else { ++ memcpy(&rgb_settings, &settings, settings_size); ++ } ++ } else { ++ FURI_LOG_E(TAG, "load failed, %s", storage_file_get_error_desc(file)); ++ } ++ ++ storage_file_close(file); ++ storage_file_free(file); ++ furi_record_close(RECORD_STORAGE); ++ rgb_settings.settings_is_loaded = true; ++}; ++ ++void rgb_backlight_save_settings(void) { ++ RGBBacklightSettings settings; ++ File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); ++ const size_t settings_size = sizeof(RGBBacklightSettings); ++ ++ FURI_LOG_I(TAG, "saving settings to \"%s\"", RGB_BACKLIGHT_SETTINGS_PATH); ++ ++ memcpy(&settings, &rgb_settings, settings_size); ++ ++ bool fs_result = ++ storage_file_open(file, RGB_BACKLIGHT_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS); ++ ++ if(fs_result) { ++ uint16_t bytes_count = storage_file_write(file, &settings, settings_size); ++ ++ if(bytes_count != settings_size) { ++ fs_result = false; ++ } ++ } ++ ++ if(fs_result) { ++ FURI_LOG_I(TAG, "save success"); ++ } else { ++ FURI_LOG_E(TAG, "save failed, %s", storage_file_get_error_desc(file)); ++ } ++ ++ storage_file_close(file); ++ storage_file_free(file); ++ furi_record_close(RECORD_STORAGE); ++}; ++ ++RGBBacklightSettings* rgb_backlight_get_settings(void) { ++ if(!rgb_settings.settings_is_loaded) { ++ rgb_backlight_load_settings(); ++ } ++ return &rgb_settings; ++} ++ ++void rgb_backlight_set_color(uint8_t color_index) { ++ if(color_index > (rgb_backlight_get_color_count() - 1)) color_index = 0; ++ rgb_settings.display_color_index = color_index; ++} ++ ++void rgb_backlight_update(uint8_t brightness) { ++ if(!rgb_settings.settings_is_loaded) { ++ rgb_backlight_load_settings(); ++ } ++ ++ static uint8_t last_color_index = 255; ++ static uint8_t last_brightness = 123; ++ ++ if(last_brightness == brightness && last_color_index == rgb_settings.display_color_index) ++ return; ++ ++ last_brightness = brightness; ++ last_color_index = rgb_settings.display_color_index; ++ ++ for(uint8_t i = 0; i < SK6805_get_led_count(); i++) { ++ uint8_t r = colors[rgb_settings.display_color_index].red * (brightness / 255.0f); ++ uint8_t g = colors[rgb_settings.display_color_index].green * (brightness / 255.0f); ++ uint8_t b = colors[rgb_settings.display_color_index].blue * (brightness / 255.0f); ++ ++ SK6805_set_led_color(i, r, g, b); ++ } ++ ++ SK6805_update(); ++} +diff --git a/applications/settings/notification_settings/rgb_backlight.h b/applications/settings/notification_settings/rgb_backlight.h +new file mode 100644 +index 000000000..b63d223e6 +--- /dev/null ++++ b/applications/settings/notification_settings/rgb_backlight.h +@@ -0,0 +1,79 @@ ++/* ++ RGB backlight FlipperZero driver ++ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include "SK6805.h" ++ ++typedef struct { ++ char* name; ++ uint8_t red; ++ uint8_t green; ++ uint8_t blue; ++} RGBBacklightColor; ++ ++typedef struct { ++ uint8_t version; ++ uint8_t display_color_index; ++ bool settings_is_loaded; ++} RGBBacklightSettings; ++ ++/** ++ * @brief Получить текущие настройки RGB-подсветки ++ * ++ * @return Указатель на структуру настроек ++ */ ++RGBBacklightSettings* rgb_backlight_get_settings(void); ++ ++/** ++ * @brief Загрузить настройки подсветки с SD-карты ++ */ ++void rgb_backlight_load_settings(void); ++ ++/** ++ * @brief Сохранить текущие настройки RGB-подсветки ++ */ ++void rgb_backlight_save_settings(void); ++ ++/** ++ * @brief Применить текущие настройки RGB-подсветки ++ * ++ * @param brightness Яркость свечения (0-255) ++ */ ++void rgb_backlight_update(uint8_t brightness); ++ ++/** ++ * @brief Установить цвет RGB-подсветки ++ * ++ * @param color_index Индекс цвета (0 - rgb_backlight_get_color_count()) ++ */ ++void rgb_backlight_set_color(uint8_t color_index); ++ ++/** ++ * @brief Получить количество доступных цветов ++ * ++ * @return Число доступных вариантов цвета ++ */ ++uint8_t rgb_backlight_get_color_count(void); ++ ++/** ++ * @brief Получить текстовое название цвета ++ * ++ * @param index Индекс из доступных вариантов цвета ++ * @return Указатель на строку с названием цвета ++ */ ++const char* rgb_backlight_get_color_text(uint8_t index); +\ No newline at end of file +diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c +index 83e1603b7..cad5b86cb 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_light.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_light.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + + #define LED_CURRENT_RED 50 + #define LED_CURRENT_GREEN 50 +@@ -31,22 +32,21 @@ void furi_hal_light_init() { + } + + void furi_hal_light_set(Light light, uint8_t value) { +- furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); +- if(light & LightRed) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); +- } +- if(light & LightGreen) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); +- } +- if(light & LightBlue) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); +- } + if(light & LightBacklight) { +- uint8_t prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); +- lp5562_execute_ramp( +- &furi_hal_i2c_handle_power, LP5562Engine1, LP5562ChannelWhite, prev, value, 100); ++ rgb_backlight_update(value); ++ } else { ++ furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); ++ if(light & LightRed) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); ++ } ++ if(light & LightGreen) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); ++ } ++ if(light & LightBlue) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); ++ } ++ furi_hal_i2c_release(&furi_hal_i2c_handle_power); + } +- furi_hal_i2c_release(&furi_hal_i2c_handle_power); + } + + void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period) { +diff --git a/lib/drivers/SK6805.c b/lib/drivers/SK6805.c +new file mode 100644 +index 000000000..572e1df97 +--- /dev/null ++++ b/lib/drivers/SK6805.c +@@ -0,0 +1,101 @@ ++/* ++ SK6805 FlipperZero driver ++ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "SK6805.h" ++#include ++ ++/* Настройки */ ++#define SK6805_LED_COUNT 3 //Количество светодиодов на плате подсветки ++#define SK6805_LED_PIN &led_pin //Порт подключения светодиодов ++ ++#ifdef FURI_DEBUG ++#define DEBUG_PIN &gpio_ext_pa7 ++#define DEBUG_INIT() \ ++ furi_hal_gpio_init(DEBUG_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh) ++#define DEBUG_SET_HIGH() furi_hal_gpio_write(DEBUG_PIN, true) ++#define DEBUG_SET_LOW() furi_hal_gpio_write(DEBUG_PIN, false) ++#else ++#define DEBUG_INIT() ++#define DEBUG_SET_HIGH() ++#define DEBUG_SET_LOW() ++#endif ++ ++static const GpioPin led_pin = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; ++static uint8_t led_buffer[SK6805_LED_COUNT][3]; ++ ++void SK6805_init(void) { ++ DEBUG_INIT(); ++ furi_hal_gpio_write(SK6805_LED_PIN, false); ++ furi_hal_gpio_init(SK6805_LED_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); ++} ++ ++uint8_t SK6805_get_led_count(void) { ++ return (const uint8_t)SK6805_LED_COUNT; ++} ++void SK6805_set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b) { ++ furi_check(led_index < SK6805_LED_COUNT); ++ ++ led_buffer[led_index][0] = g; ++ led_buffer[led_index][1] = r; ++ led_buffer[led_index][2] = b; ++} ++ ++void SK6805_update(void) { ++ SK6805_init(); ++ furi_kernel_lock(); ++ uint32_t end; ++ /* Последовательная отправка цветов светодиодов */ ++ for(uint8_t lednumber = 0; lednumber < SK6805_LED_COUNT; lednumber++) { ++ //Последовательная отправка цветов светодиода ++ for(uint8_t color = 0; color < 3; color++) { ++ //Последовательная отправка битов цвета ++ uint8_t i = 0b10000000; ++ while(i != 0) { ++ if(led_buffer[lednumber][color] & (i)) { ++ furi_hal_gpio_write(SK6805_LED_PIN, true); ++ DEBUG_SET_HIGH(); ++ end = DWT->CYCCNT + 30; ++ //T1H 600 us (615 us) ++ while(DWT->CYCCNT < end) { ++ } ++ furi_hal_gpio_write(SK6805_LED_PIN, false); ++ DEBUG_SET_LOW(); ++ end = DWT->CYCCNT + 26; ++ //T1L 600 us (587 us) ++ while(DWT->CYCCNT < end) { ++ } ++ } else { ++ furi_hal_gpio_write(SK6805_LED_PIN, true); ++ DEBUG_SET_HIGH(); ++ end = DWT->CYCCNT + 11; ++ //T0H 300 ns (312 ns) ++ while(DWT->CYCCNT < end) { ++ } ++ furi_hal_gpio_write(SK6805_LED_PIN, false); ++ DEBUG_SET_LOW(); ++ end = DWT->CYCCNT + 43; ++ //T0L 900 ns (890 ns) ++ while(DWT->CYCCNT < end) { ++ } ++ } ++ i >>= 1; ++ } ++ } ++ } ++ furi_kernel_unlock(); ++} +diff --git a/lib/drivers/SK6805.h b/lib/drivers/SK6805.h +new file mode 100644 +index 000000000..7c58956fa +--- /dev/null ++++ b/lib/drivers/SK6805.h +@@ -0,0 +1,51 @@ ++/* ++ SK6805 FlipperZero driver ++ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef SK6805_H_ ++#define SK6805_H_ ++ ++#include ++ ++/** ++ * @brief Инициализация линии управления подсветкой ++ */ ++void SK6805_init(void); ++ ++/** ++ * @brief Получить количество светодиодов в подсветке ++ * ++ * @return Количество светодиодов ++ */ ++uint8_t SK6805_get_led_count(void); ++ ++/** ++ * @brief Установить цвет свечения светодиода ++ * ++ * @param led_index номер светодиода (от 0 до SK6805_get_led_count()) ++ * @param r значение красного (0-255) ++ * @param g значение зелёного (0-255) ++ * @param b значение синего (0-255) ++ */ ++void SK6805_set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b); ++ ++/** ++ * @brief Обновление состояния подсветки дисплея ++ */ ++void SK6805_update(void); ++ ++#endif /* SK6805_H_ */ +\ No newline at end of file + +\ No newline at end of file diff --git a/.drone.yml b/.drone.yml index a03063528..c82921c9d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -31,7 +31,9 @@ steps: - echo '' >> CHANGELOG.md - echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md - echo '' >> CHANGELOG.md - - echo '### [Version with extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md + - echo '### [Version with RGB patch - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md + - echo '' >> CHANGELOG.md + - echo '## [Version with Extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md environment: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link @@ -56,10 +58,30 @@ steps: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link + - name: "Build with RGB patch" + image: hfdj/fztools + pull: never + commands: + - git apply .ci_files/rgb.patch + - export DIST_SUFFIX=${DRONE_TAG}r + - export WORKFLOW_BRANCH_OR_TAG=release-cfw + - export FORCE_NO_DIRTY=yes + - rm -f build/f7-firmware-C/toolbox/version.* + - ./fbt COMPACT=1 DEBUG=0 updater_package + - mkdir artifacts-rgb-patch + - mv dist/f7-C/* artifacts-rgb-patch/ + - ls -laS artifacts-rgb-patch + - ls -laS artifacts-rgb-patch/f7-update-${DRONE_TAG}r + environment: + FBT_TOOLS_CUSTOM_LINK: + from_secret: fbt_link + - name: "Build with ofw anims" image: hfdj/fztools pull: never commands: + - git clean -df + - git checkout -- . - rm -f assets/dolphin/external/manifest.txt - cp .ci_files/anims_ofw.txt assets/dolphin/external/manifest.txt - rm -rf assets/resources/apps/ @@ -80,16 +102,20 @@ steps: image: kramos/alpine-zip commands: - cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.tgz . + - cp artifacts-rgb-patch/flipper-z-f7-update-${DRONE_TAG}r.tgz . - cp artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.tgz . - cp artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz . - zip -r artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.zip artifacts-extra-apps/f7-update-${DRONE_TAG}e + - zip -r artifacts-rgb-patch/flipper-z-f7-update-${DRONE_TAG}r.zip artifacts-rgb-patch/f7-update-${DRONE_TAG}r - zip -r artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.zip artifacts-ofw-anims/f7-update-${DRONE_TAG}n - zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG} - tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts - rm -rf artifacts-extra-apps/f7-update-${DRONE_TAG} + - rm -rf artifacts-rgb-patch/f7-update-${DRONE_TAG} - rm -rf artifacts-ofw-anims/f7-update-${DRONE_TAG} - rm -rf artifacts-default/f7-update-${DRONE_TAG} - ls -laS artifacts-extra-apps + - ls -laS artifacts-rgb-patch - ls -laS artifacts-ofw-anims - ls -laS artifacts-default - mv artifacts-default/ ${DRONE_TAG} @@ -146,6 +172,21 @@ steps: from_secret: dep_target_extra source: flipper-z-f7-update-${DRONE_TAG}e.tgz + - name: "Upload rgb patch version to updates srv" + image: appleboy/drone-scp:linux-amd64 + settings: + host: + from_secret: dep_host + username: + from_secret: dep_user + password: + from_secret: dep_passwd + port: + from_secret: dep_port + target: + from_secret: dep_target_extra + source: flipper-z-f7-update-${DRONE_TAG}r.tgz + - name: "Do Github release" image: ddplugins/github-release pull: never @@ -160,6 +201,7 @@ steps: - ${DRONE_TAG}/*.zip - artifacts-ofw-anims/*.tgz - artifacts-extra-apps/*.tgz + - artifacts-rgb-patch/*.tgz title: ${DRONE_TAG} note: CHANGELOG.md checksum: @@ -210,7 +252,13 @@ steps: [-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n) - [-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}e.tgz&channel=release-cfw&version=${DRONE_TAG}e)" + [-Version with RGB patch - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz&channel=release-cfw&version=${DRONE_TAG}r) + + + [-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz) + + + [-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}e.tgz&channel=release-cfw&version=${DRONE_TAG}e)" document: - ${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz @@ -223,7 +271,7 @@ steps: commands: - wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - chmod +x ./discord.sh - - ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' + - ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' - name: "Send extra pack build to telegram" image: appleboy/drone-telegram @@ -297,10 +345,29 @@ steps: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link + - name: "Build dev with rgb patch" + image: hfdj/fztools + pull: never + commands: + - git apply .ci_files/rgb.patch + - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}r + - export WORKFLOW_BRANCH_OR_TAG=dev-cfw + - export FORCE_NO_DIRTY=yes + - rm -f build/f7-firmware-C/toolbox/version.* + - ./fbt COMPACT=1 DEBUG=0 updater_package + - mkdir artifacts-rgb-patch + - mv dist/f7-C/* artifacts-rgb-patch/ + - ls -laS artifacts-rgb-patch + - ls -laS artifacts-rgb-patch/f7-update-${DRONE_BUILD_NUMBER}r + environment: + FBT_TOOLS_CUSTOM_LINK: + from_secret: fbt_link + - name: "Bundle self-update packages" image: kramos/alpine-zip commands: - cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz . + - cp artifacts-rgb-patch/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz . - cp artifacts-default/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz . - rm -rf artifacts-default/f7-update-${DRONE_BUILD_NUMBER} - ls -laS artifacts-default @@ -358,6 +425,21 @@ steps: from_secret: dep_target_extra source: flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz + - name: "Upload rgb patch version to updates srv" + image: appleboy/drone-scp:linux-amd64 + settings: + host: + from_secret: dep_host + username: + from_secret: dep_user + password: + from_secret: dep_passwd + port: + from_secret: dep_port + target: + from_secret: dep_target_extra + source: flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz + - name: "Trigger update server reindex" image: hfdj/fztools pull: never @@ -392,6 +474,12 @@ steps: [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}) + [-Version with RGB patch - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}r) + + + [-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz) + + [-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)" - name: "Send build to telegram" @@ -427,7 +515,7 @@ steps: commands: - wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - chmod +x ./discord.sh - - ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' + - ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with RGB patch - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' trigger: branch: From b16357494dd28e62e158032b622cc04e74bd295a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:31:39 +0300 Subject: [PATCH 020/370] fix branch --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index c82921c9d..bb7d4fcd9 100644 --- a/.drone.yml +++ b/.drone.yml @@ -64,7 +64,7 @@ steps: commands: - git apply .ci_files/rgb.patch - export DIST_SUFFIX=${DRONE_TAG}r - - export WORKFLOW_BRANCH_OR_TAG=release-cfw + - export WORKFLOW_BRANCH_OR_TAG=release-cfw-rgb - export FORCE_NO_DIRTY=yes - rm -f build/f7-firmware-C/toolbox/version.* - ./fbt COMPACT=1 DEBUG=0 updater_package @@ -351,7 +351,7 @@ steps: commands: - git apply .ci_files/rgb.patch - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}r - - export WORKFLOW_BRANCH_OR_TAG=dev-cfw + - export WORKFLOW_BRANCH_OR_TAG=dev-cfw-rgb - export FORCE_NO_DIRTY=yes - rm -f build/f7-firmware-C/toolbox/version.* - ./fbt COMPACT=1 DEBUG=0 updater_package From 73d4fc6d2fa0f65fc050e525157688e6abb8de25 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:35:43 +0300 Subject: [PATCH 021/370] Fix E --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index bb7d4fcd9..f7c6d07de 100644 --- a/.drone.yml +++ b/.drone.yml @@ -480,7 +480,7 @@ steps: [-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz) - [-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)" + [-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)" - name: "Send build to telegram" image: appleboy/drone-telegram From 14fbc3648edbc9b4bcafb26bf990234b01b72541 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 16:36:17 +0200 Subject: [PATCH 022/370] move support up --- .github/workflow_data/release.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 77316bae7..f54095323 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -1,3 +1,14 @@ +## ❤️ Support +If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! + +- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. +- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time +- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time +- **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` + +**Thanks for all your support <3** + ## ⬇️ Download >### [🖥️ Web Updater (chrome)](https://flipper-xtre.me/update) [recommended] @@ -17,14 +28,3 @@ **If you have any of the above issues, please re-download and re-install!** --> - -## ❤️ Support -If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! - -- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. -- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time -- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time -- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time -- **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` - -**Thanks for all your support <3** From b5aa4afd9f53e31c557b31719a19adc390900860 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:40:52 +0100 Subject: [PATCH 023/370] Move download up and fix hotfix --- .github/workflow_data/hotfix.py | 2 +- .github/workflow_data/release.md | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflow_data/hotfix.py b/.github/workflow_data/hotfix.py index b35cf7a07..5d09d9f9c 100644 --- a/.github/workflow_data/hotfix.py +++ b/.github/workflow_data/hotfix.py @@ -82,6 +82,6 @@ if __name__ == "__main__": print(f"{req.url = }\n{req.status_code = }\n{req.content = }") sys.exit(1) - changelog = body.split("## 🚀 Changelog", 1)[1].rsplit("## ❤️ Support", 1)[0] + changelog = body.split("## 🚀 Changelog", 1)[1] with open(os.environ["ARTIFACT_TGZ"].removesuffix(".tgz") + ".md", "w") as f: f.write(changelog.strip() + "\n\n") diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index f54095323..7128cb454 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -1,3 +1,12 @@ +## ⬇️ Download +>### [🖥️ Web Updater (chrome)](https://flipper-xtre.me/update) [recommended] + +>### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_TGZ}) + +>### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_ZIP}) + +**Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions or encounter issues!** + ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! @@ -9,15 +18,6 @@ If you like what you're seeing, **please consider donating to us**. We won't eve **Thanks for all your support <3** -## ⬇️ Download ->### [🖥️ Web Updater (chrome)](https://flipper-xtre.me/update) [recommended] - ->### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_TGZ}) - ->### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_ZIP}) - -**Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions or encounter issues!** - ## 🚀 Changelog {CHANGELOG} From 945bc46837ed39707ca4ffb5c7a37bee759758d8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:09:37 +0100 Subject: [PATCH 024/370] Archive fix unpinning last item --- .../main/archive/helpers/archive_favorites.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index c7b058235..799b29034 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -112,7 +112,9 @@ static bool archive_favourites_rescan() { furi_string_free(buffer); storage_file_close(file); - storage_common_move(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(storage, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -215,7 +217,9 @@ bool archive_favorites_delete(const char* format, ...) { furi_string_free(filename); storage_file_close(file); - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -298,7 +302,9 @@ bool archive_favorites_rename(const char* src, const char* dst) { furi_string_free(path); storage_file_close(file); - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -324,7 +330,9 @@ void archive_favorites_save(void* context) { archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", furi_string_get_cstr(item->path)); } - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); From f2ce2ad95c8eb4540758c3140b7997af36e95b53 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:25:35 +0100 Subject: [PATCH 025/370] Archive fix internal and external browsers --- .../main/archive/helpers/archive_browser.c | 22 ++++++++----------- .../main/archive/helpers/archive_browser.h | 16 +++++++------- .../main/archive/scenes/archive_scene_info.c | 5 ----- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 15c9d97f4..57219e05b 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -449,7 +449,8 @@ void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { } static bool archive_is_dir_exists(FuriString* path) { - if(furi_string_equal(path, STORAGE_ANY_PATH_PREFIX)) { + if(furi_string_equal(path, STORAGE_INT_PATH_PREFIX) || + furi_string_equal(path, STORAGE_EXT_PATH_PREFIX)) { return true; } bool state = false; @@ -475,13 +476,6 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { } else { tab = (tab + 1) % ArchiveTabTotal; } - if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; - } - } browser->is_root = true; archive_set_tab(browser, tab); @@ -502,18 +496,20 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { } else { tab = archive_get_tab(browser); if(archive_is_dir_exists(browser->path)) { - bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; + bool is_browser = !strcmp(archive_get_tab_ext(tab), "*"); + bool skip_assets = is_browser; // Hide dot files everywhere except Browser if in debug mode - bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? - !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) : - true; + bool hide_dot_files = !is_browser ? true : + tab == ArchiveTabInternal ? + false : + !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); archive_file_browser_set_path( browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files); tab_empty = false; // Empty check will be performed later } } - if((tab_empty) && (tab != ArchiveTabBrowser)) { + if((tab_empty) && (tab != ArchiveTabBrowser) && (tab != ArchiveTabInternal)) { archive_switch_tab(browser, key); } else { with_view_model( diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index ec3ef00ce..dfc572a2b 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -9,16 +9,16 @@ static const char* tab_default_paths[] = { [ArchiveTabFavorites] = "/app:favorites", - [ArchiveTabIButton] = ANY_PATH("ibutton"), - [ArchiveTabNFC] = ANY_PATH("nfc"), - [ArchiveTabSubGhz] = ANY_PATH("subghz"), - [ArchiveTabLFRFID] = ANY_PATH("lfrfid"), - [ArchiveTabInfrared] = ANY_PATH("infrared"), - [ArchiveTabBadKb] = ANY_PATH("badkb"), + [ArchiveTabIButton] = EXT_PATH("ibutton"), + [ArchiveTabNFC] = EXT_PATH("nfc"), + [ArchiveTabSubGhz] = EXT_PATH("subghz"), + [ArchiveTabLFRFID] = EXT_PATH("lfrfid"), + [ArchiveTabInfrared] = EXT_PATH("infrared"), + [ArchiveTabBadKb] = EXT_PATH("badkb"), [ArchiveTabU2f] = "/app:u2f", - [ArchiveTabApplications] = ANY_PATH("apps"), + [ArchiveTabApplications] = EXT_PATH("apps"), [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, - [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, + [ArchiveTabBrowser] = STORAGE_EXT_PATH_PREFIX, }; static const char* known_ext[] = { diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 07b5aeadb..6a470ffaf 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -38,11 +38,6 @@ void archive_scene_info_on_enter(void* context) { // Directory path path_extract_dirname(furi_string_get_cstr(current->path), dirname); - if(strcmp(furi_string_get_cstr(dirname), "/any") == 0) { - furi_string_replace(dirname, STORAGE_ANY_PATH_PREFIX, "/"); - } else { - furi_string_replace(dirname, STORAGE_ANY_PATH_PREFIX, ""); - } // File size FileInfo fileinfo; From 5cc5115a186aef193fba8f3c87bb790c84e016c8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:27:33 +0100 Subject: [PATCH 026/370] Archive fix bad file sizes --- applications/main/archive/scenes/archive_scene_info.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 6a470ffaf..6f8d40a56 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -41,8 +41,13 @@ void archive_scene_info_on_enter(void* context) { // File size FileInfo fileinfo; - storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo); - if(fileinfo.size <= 1024) { + if(storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo) != FSE_OK) { + snprintf( + file_info_message, + sizeof(file_info_message), + "Size: \e#N/A\e#\n%s", + furi_string_get_cstr(dirname)); + } else if(fileinfo.size <= 1024) { furi_string_printf(str_size, "%lld", fileinfo.size); snprintf( file_info_message, From 2b2708e635fae366f99d3958ff0b71199dda7c68 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:28:41 +0100 Subject: [PATCH 027/370] Archive fix favorites, u2f and app tabs --- .../main/archive/helpers/archive_apps.c | 13 +++---- .../main/archive/helpers/archive_browser.c | 8 ++--- .../main/archive/views/archive_browser_view.c | 34 ++++++++++--------- .../main/archive/views/archive_browser_view.h | 1 + 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index c8ad67625..b7037e6b7 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -1,6 +1,7 @@ #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include static const char* known_apps[] = { [ArchiveAppTypeU2f] = "u2f", @@ -28,13 +29,9 @@ bool archive_app_is_available(void* context, const char* path) { ArchiveAppTypeEnum app = archive_get_app_type(path); if(app == ArchiveAppTypeU2f) { - bool file_exists = false; Storage* storage = furi_record_open(RECORD_STORAGE); - - if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) { - file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f")); - } - + bool file_exists = storage_file_exists(storage, U2F_KEY_FILE) && + storage_file_exists(storage, U2F_CNT_FILE); furi_record_close(RECORD_STORAGE); return file_exists; } else { @@ -69,8 +66,8 @@ void archive_app_delete_file(void* context, const char* path) { if(app == ArchiveAppTypeU2f) { Storage* fs_api = furi_record_open(RECORD_STORAGE); - res = (storage_common_remove(fs_api, ANY_PATH("u2f/key.u2f")) == FSE_OK); - res |= (storage_common_remove(fs_api, ANY_PATH("u2f/cnt.u2f")) == FSE_OK); + res = (storage_common_remove(fs_api, U2F_KEY_FILE) == FSE_OK); + res |= (storage_common_remove(fs_api, U2F_CNT_FILE) == FSE_OK); furi_record_close(RECORD_STORAGE); if(archive_is_favorite("/app:u2f/U2F Token")) { diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 57219e05b..b0781d843 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -482,11 +482,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { furi_string_set(browser->path, archive_get_default_path(tab)); bool tab_empty = true; + bool is_app_tab = furi_string_start_with_str(browser->path, "/app:"); if(tab == ArchiveTabFavorites) { - if(archive_favorites_count(browser) > 0) { - tab_empty = false; - } - } else if(furi_string_start_with_str(browser->path, "/app:")) { + tab_empty = false; + } else if(is_app_tab) { char* app_name = strchr(furi_string_get_cstr(browser->path), ':'); if(app_name != NULL) { if(archive_app_is_available(browser, furi_string_get_cstr(browser->path))) { @@ -518,6 +517,7 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { { model->item_idx = 0; model->array_offset = 0; + model->is_app_tab = is_app_tab; }, false); archive_get_items(browser, furi_string_get_cstr(browser->path)); diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index d4a52ba9e..33e1eace6 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -64,25 +64,27 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { FuriString* item_new_dir = furi_string_alloc_set("New Dir"); FuriString* item_rename = furi_string_alloc_set("Rename"); FuriString* item_delete = furi_string_alloc_set("Delete"); - if(model->clipboard != NULL) { + if(!model->is_app_tab) { + if(model->clipboard != NULL) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_paste, + ArchiveBrowserEventFileMenuPaste); + } else if(selected) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_cut, + ArchiveBrowserEventFileMenuCut); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_copy, + ArchiveBrowserEventFileMenuCopy); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_paste, - ArchiveBrowserEventFileMenuPaste); - } else if(selected) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_cut, - ArchiveBrowserEventFileMenuCut); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_copy, - ArchiveBrowserEventFileMenuCopy); + item_new_dir, + ArchiveBrowserEventFileMenuNewDir); } - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_new_dir, - ArchiveBrowserEventFileMenuNewDir); if(selected) { if(!selected->is_app) { archive_menu_add_item( diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 1b4c0f525..ed18df387 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -101,6 +101,7 @@ typedef struct { bool clipboard_copy; menu_array_t context_menu; + bool is_app_tab; bool move_fav; bool list_loading; bool folder_loading; From cfa75bb7125a2817b852cc9e717ab615a2694182 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:31:34 +0100 Subject: [PATCH 028/370] Archive favorites loop cleanup --- .../main/archive/helpers/archive_favorites.c | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index 799b29034..7e3229283 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -59,10 +59,7 @@ uint16_t archive_favorites_count(void* context) { uint16_t lines = 0; if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; // Skip empty lines } @@ -87,10 +84,7 @@ static bool archive_favourites_rescan() { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -141,10 +135,7 @@ bool archive_favorites_read(void* context) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -199,10 +190,7 @@ bool archive_favorites_delete(const char* format, ...) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -243,10 +231,7 @@ bool archive_is_favorite(const char* format, ...) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -283,10 +268,7 @@ bool archive_favorites_rename(const char* src, const char* dst) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } From b63fe76a71805a217d0d66f624bc3c1e982dc9bc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 18:54:39 +0100 Subject: [PATCH 029/370] Properly hide favs tab when no favs are set --- applications/main/archive/helpers/archive_browser.c | 4 +++- applications/main/archive/helpers/archive_favorites.c | 4 +--- applications/main/archive/helpers/archive_favorites.h | 2 +- applications/main/archive/scenes/archive_scene_browser.c | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index b0781d843..43ab8c950 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -484,7 +484,9 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { bool tab_empty = true; bool is_app_tab = furi_string_start_with_str(browser->path, "/app:"); if(tab == ArchiveTabFavorites) { - tab_empty = false; + if(archive_favorites_count() > 0) { + tab_empty = false; + } } else if(is_app_tab) { char* app_name = strchr(furi_string_get_cstr(browser->path), ':'); if(app_name != NULL) { diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index 7e3229283..7efeeb4d1 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -46,9 +46,7 @@ static bool archive_favorites_read_line(File* file, FuriString* str_result) { return result; } -uint16_t archive_favorites_count(void* context) { - furi_assert(context); - +uint16_t archive_favorites_count() { Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); diff --git a/applications/main/archive/helpers/archive_favorites.h b/applications/main/archive/helpers/archive_favorites.h index e45af92e7..dfeb18e76 100644 --- a/applications/main/archive/helpers/archive_favorites.h +++ b/applications/main/archive/helpers/archive_favorites.h @@ -6,7 +6,7 @@ #define ARCHIVE_FAV_PATH CFG_PATH("favorites.txt") #define ARCHIVE_FAV_TEMP_PATH CFG_PATH("favorites.tmp") -uint16_t archive_favorites_count(void* context); +uint16_t archive_favorites_count(); bool archive_favorites_read(void* context); bool archive_favorites_delete(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); bool archive_is_favorite(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index bf9079be4..0854c2d3f 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -77,6 +77,9 @@ void archive_scene_browser_on_enter(void* context) { browser->is_root = true; archive_browser_set_callback(browser, archive_scene_browser_callback, archive); + if(archive_get_tab(browser) == ArchiveTabFavorites && archive_favorites_count() < 1) { + archive_switch_tab(browser, TAB_LEFT); + } archive_update_focus(browser, archive->text_store); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); From 82ad081a481356d705fdb0316b7d1e2fbaced19f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 22:01:14 +0100 Subject: [PATCH 030/370] Fix hiding assets in archive --- applications/main/archive/helpers/archive_browser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 43ab8c950..aaef1c9e5 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -498,7 +498,7 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { tab = archive_get_tab(browser); if(archive_is_dir_exists(browser->path)) { bool is_browser = !strcmp(archive_get_tab_ext(tab), "*"); - bool skip_assets = is_browser; + bool skip_assets = !is_browser; // Hide dot files everywhere except Browser if in debug mode bool hide_dot_files = !is_browser ? true : tab == ArchiveTabInternal ? From b89f93e9a3452bdfb3a6df16f7e8b875279f171a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 22:57:31 +0100 Subject: [PATCH 031/370] Archive fix actions menu retardedness --- .../main/archive/helpers/archive_browser.h | 2 +- .../main/archive/scenes/archive_scene_info.c | 3 +- .../main/archive/views/archive_browser_view.c | 88 ++++--------------- 3 files changed, 22 insertions(+), 71 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index dfc572a2b..c2d4c59f8 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -62,7 +62,7 @@ static inline const char* archive_get_default_path(ArchiveTabEnum tab) { } inline bool archive_is_known_app(ArchiveFileTypeEnum type) { - return (type != ArchiveFileTypeUnknown); + return (type < ArchiveFileTypeUnknown); } bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx); diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 6f8d40a56..7af7d0db5 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -41,7 +41,8 @@ void archive_scene_info_on_enter(void* context) { // File size FileInfo fileinfo; - if(storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo) != FSE_OK) { + if(storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo) != FSE_OK || + file_info_is_dir(&fileinfo)) { snprintf( file_info_message, sizeof(file_info_message), diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 33e1eace6..6495ad76f 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -108,97 +108,47 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { FuriString* item_pin = furi_string_alloc_set("Pin"); FuriString* item_info = furi_string_alloc_set("Info"); FuriString* item_show = furi_string_alloc_set("Show"); + FuriString* item_move = furi_string_alloc_set("Move"); if(selected->fav || favorites) { furi_string_set(item_pin, "Unpin"); } - if(favorites) { - //FURI_LOG_D(TAG, "ArchiveTabFavorites"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); + if(archive_is_known_app(selected->type)) { + if(selected->type != ArchiveFileTypeFolder) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_run, + ArchiveBrowserEventFileMenuRun); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), item_pin, ArchiveBrowserEventFileMenuPin); - if(selected->type <= ArchiveFileTypeBadKb) { + } + if(!selected->is_app) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_info, + ArchiveBrowserEventFileMenuInfo); + if(selected->type != ArchiveFileTypeFolder) { archive_menu_add_item( menu_array_push_raw(model->context_menu), item_show, ArchiveBrowserEventFileMenuShow); } - furi_string_set(item_info, "Move"); + } + if(favorites) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_info, + item_move, ArchiveBrowserEventFileMenuRename); - } else { - if(selected->type == ArchiveFileTypeFolder) { - //FURI_LOG_D(TAG, "Directory type"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - } else if(!archive_is_known_app(selected->type)) { - //FURI_LOG_D(TAG, "Unknown type"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->is_text_file) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - } else if(selected->is_app) { - //FURI_LOG_D(TAG, "3 types"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->type <= ArchiveFileTypeBadKb) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - } else { - //FURI_LOG_D(TAG, "All menu"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->type <= ArchiveFileTypeBadKb) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - } } furi_string_free(item_run); furi_string_free(item_pin); furi_string_free(item_info); furi_string_free(item_show); + furi_string_free(item_move); } } /*else { FURI_LOG_D(TAG, "menu_array_size already set: %d", menu_array_size(model->context_menu)); From 90f06037bf1111736337c451880323ad325ed5e5 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:30:24 +0100 Subject: [PATCH 032/370] Fix xtreme app code naming consistency --- .../xtreme_app_scene_interface_common.c | 5 ++-- .../xtreme_app_scene_interface_graphics.c | 1 + .../xtreme_app_scene_interface_lockscreen.c | 28 +++++++++++-------- .../xtreme_app_scene_interface_mainmenu.c | 2 +- .../scenes/xtreme_app_scene_protocols.c | 12 +++++--- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c index 54b0241d9..b0f5a1d4d 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c @@ -4,6 +4,7 @@ enum VarItemListIndex { VarItemListIndexSortDirsFirst, VarItemListIndexDarkMode, VarItemListIndexLeftHanded, + VarItemListIndexFavoriteTimeout, }; void xtreme_app_scene_interface_common_var_item_list_callback(void* context, uint32_t index) { @@ -27,7 +28,7 @@ static void xtreme_app_scene_interface_common_dark_mode_changed(VariableItem* it app->save_settings = true; } -static void xtreme_app_scene_interface_common_left_handed_changed(VariableItem* item) { +static void xtreme_app_scene_interface_common_hand_orient_changed(VariableItem* item) { bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); if(value) { @@ -71,7 +72,7 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { var_item_list, "Left Handed", 2, - xtreme_app_scene_interface_common_left_handed_changed, + xtreme_app_scene_interface_common_hand_orient_changed, app); bool value = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient); variable_item_set_current_value_index(item, value); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c index 7b0d54b39..5b016815f 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c @@ -5,6 +5,7 @@ enum VarItemListIndex { VarItemListIndexAnimSpeed, VarItemListIndexCycleAnims, VarItemListIndexUnlockAnims, + VarItemListIndexFallbackAnim, }; void xtreme_app_scene_interface_graphics_var_item_list_callback(void* context, uint32_t index) { diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c index 5ae21cb3f..632401a7c 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c @@ -1,8 +1,13 @@ #include "../xtreme_app.h" enum VarItemListIndex { - VarItemListIndexShowClock, + VarItemListIndexLockOnBoot, + VarItemListIndexFormatOn10BadPins, + VarItemListIndexShowTime, + VarItemListIndexShowSeconds, VarItemListIndexShowDate, + VarItemListIndexShowStatusbar, + VarItemListIndexUnlockPrompt, }; void xtreme_app_scene_interface_lockscreen_var_item_list_callback(void* context, uint32_t index) { @@ -26,7 +31,7 @@ static void xtreme_app_scene_interface_lockscreen_bad_pins_format_changed(Variab app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_time_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -34,7 +39,7 @@ static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_seconds_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_seconds_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -42,7 +47,7 @@ static void xtreme_app_scene_interface_lockscreen_show_seconds_changed(VariableI app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_date_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_date_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -50,7 +55,8 @@ static void xtreme_app_scene_interface_lockscreen_show_date_changed(VariableItem app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_statusbar_changed(VariableItem* item) { +static void + xtreme_app_scene_interface_lockscreen_lockscreen_statusbar_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -58,7 +64,7 @@ static void xtreme_app_scene_interface_lockscreen_show_statusbar_changed(Variabl app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_unlock_prompt_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_prompt_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -94,7 +100,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Time", 2, - xtreme_app_scene_interface_lockscreen_show_time_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_time_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_time); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_time ? "ON" : "OFF"); @@ -103,7 +109,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Seconds", 2, - xtreme_app_scene_interface_lockscreen_show_seconds_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_seconds_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_seconds); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_seconds ? "ON" : "OFF"); @@ -112,7 +118,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Date", 2, - xtreme_app_scene_interface_lockscreen_show_date_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_date_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_date); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_date ? "ON" : "OFF"); @@ -121,7 +127,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Statusbar", 2, - xtreme_app_scene_interface_lockscreen_show_statusbar_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_statusbar_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_statusbar); variable_item_set_current_value_text( @@ -131,7 +137,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Unlock Prompt", 2, - xtreme_app_scene_interface_lockscreen_unlock_prompt_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_prompt_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_prompt); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_prompt ? "ON" : "OFF"); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c index 8d4a6185e..4a5b5779d 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c @@ -1,7 +1,7 @@ #include "../xtreme_app.h" enum VarItemListIndex { - VarItemListIndexWiiMenu, + VarItemListIndexMenuStyle, VarItemListIndexApp, VarItemListIndexRemoveApp, VarItemListIndexAddApp, diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index 28b97618e..b1322b0da 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -12,7 +12,7 @@ void xtreme_app_scene_protocols_var_item_list_callback(void* context, uint32_t i view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_bad_bt_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "BT" : "USB"); @@ -20,7 +20,7 @@ static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) { app->save_settings = true; } -static void xtreme_app_scene_protocols_badbt_remember_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_bad_bt_remember_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -42,12 +42,16 @@ void xtreme_app_scene_protocols_on_enter(void* context) { VariableItem* item; item = variable_item_list_add( - var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_badkb_mode_changed, app); + var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_bad_bt_changed, app); variable_item_set_current_value_index(item, xtreme_settings->bad_bt); variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB"); item = variable_item_list_add( - var_item_list, "BadBT Remember", 2, xtreme_app_scene_protocols_badbt_remember_changed, app); + var_item_list, + "BadBT Remember", + 2, + xtreme_app_scene_protocols_bad_bt_remember_changed, + app); variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); From f292bbe4f63c95383326a8394f09a67eb33d18e8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:33:38 +0100 Subject: [PATCH 033/370] More precision for frequency editor + change text --- .../scenes/xtreme_app_scene_protocols_frequencies_add.c | 4 ++-- applications/main/xtreme_app/xtreme_app.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c index 88e421147..a9efa8b54 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c @@ -9,7 +9,7 @@ static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* XtremeApp* app = context; char* end; - uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 10000; + uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 1000; if(*end || !furi_hal_subghz_is_frequency_valid(value)) { view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultError); return; @@ -29,7 +29,7 @@ void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) { XtremeApp* app = context; TextInput* text_input = app->text_input; - text_input_set_header_text(text_input, "Format: 12356"); + text_input_set_header_text(text_input, "Ex: 123456 for 123.456 MHz"); strlcpy(app->subghz_freq_buffer, "", XTREME_SUBGHZ_FREQ_BUFFER_SIZE); diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index d02c5e9f0..ce50facdd 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -25,7 +25,7 @@ #include #include -#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 6 +#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 7 ARRAY_DEF(CharList, char*) From 5c0b07c7b452fb49d2e8a94710d04307ac3aedb4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:34:50 +0100 Subject: [PATCH 034/370] Retry when failing to re-invoke first slideshow --- .../xtreme_app/scenes/xtreme_app_scene_start.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c index fabd7ce6c..7cb1153df 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c @@ -48,12 +48,15 @@ bool xtreme_app_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc); break; case VarItemListIndexVersion: { - if(storage_common_copy( - furi_record_open(RECORD_STORAGE), - EXT_PATH("dolphin/xfwfirstboot.bin"), - EXT_PATH(".slideshow"))) { - app->show_slideshow = true; - xtreme_app_apply(app); + for(int i = 0; i < 10; i++) { + if(storage_common_copy( + furi_record_open(RECORD_STORAGE), + EXT_PATH("dolphin/xfwfirstboot.bin"), + EXT_PATH(".slideshow"))) { + app->show_slideshow = true; + xtreme_app_apply(app); + break; + } } break; } From 2c1834f1f3ca9d4b703e53ecab654fd84aa0563d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:58:05 +0100 Subject: [PATCH 035/370] Add submenu indicator to xtreme app --- .../scenes/xtreme_app_scene_interface.c | 20 ++++++++++++++----- .../scenes/xtreme_app_scene_protocols.c | 3 ++- .../xtreme_app_scene_protocols_frequencies.c | 6 ++++-- .../scenes/xtreme_app_scene_start.c | 13 +++++++++--- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c index a033e4746..5e04fc4b8 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c @@ -16,12 +16,22 @@ void xtreme_app_scene_interface_var_item_list_callback(void* context, uint32_t i void xtreme_app_scene_interface_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; - variable_item_list_add(var_item_list, "Graphics", 0, NULL, app); - variable_item_list_add(var_item_list, "Mainmenu", 0, NULL, app); - variable_item_list_add(var_item_list, "Lockscreen", 0, NULL, app); - variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); - variable_item_list_add(var_item_list, "Common", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Graphics", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Mainmenu", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Lockscreen", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Common", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_interface_var_item_list_callback, app); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index b1322b0da..497d3f1ec 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -55,7 +55,8 @@ void xtreme_app_scene_protocols_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); - variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); item = variable_item_list_add( var_item_list, "SubGHz Extend", 2, xtreme_app_scene_protocols_subghz_extend_changed, app); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c index 6b92d1eb0..668fe8327 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c @@ -33,9 +33,11 @@ void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { variable_item_set_current_value_index(item, app->subghz_use_defaults); variable_item_set_current_value_text(item, app->subghz_use_defaults ? "ON" : "OFF"); - variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_protocols_frequencies_var_item_list_callback, app); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c index 7cb1153df..1f1c1de70 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c @@ -15,10 +15,17 @@ void xtreme_app_scene_start_var_item_list_callback(void* context, uint32_t index void xtreme_app_scene_start_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add(var_item_list, "Interface", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Protocols", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Misc", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - variable_item_list_add(var_item_list, "Interface", 0, NULL, app); - variable_item_list_add(var_item_list, "Protocols", 0, NULL, app); - variable_item_list_add(var_item_list, "Misc", 0, NULL, app); variable_item_list_add(var_item_list, furi_string_get_cstr(app->version_tag), 0, NULL, app); variable_item_list_set_enter_callback( From 14a0fd0a314c7814460c26f557de4450f7b3bc1a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:58:46 +0100 Subject: [PATCH 036/370] Move screen options to own section --- .../scenes/xtreme_app_scene_config.h | 1 + .../xtreme_app_scene_interface_common.c | 35 ----- .../xtreme_app/scenes/xtreme_app_scene_misc.c | 59 +------- .../scenes/xtreme_app_scene_misc_screen.c | 130 ++++++++++++++++++ 4 files changed, 138 insertions(+), 87 deletions(-) create mode 100644 applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h index dfe115522..23459cd15 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -12,4 +12,5 @@ ADD_SCENE(xtreme_app, protocols_frequencies_static, ProtocolsFrequenciesStatic) ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper) ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd) ADD_SCENE(xtreme_app, misc, Misc) +ADD_SCENE(xtreme_app, misc_screen, MiscScreen) ADD_SCENE(xtreme_app, misc_rename, MiscRename) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c index b0f5a1d4d..3efa4fffa 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c @@ -2,8 +2,6 @@ enum VarItemListIndex { VarItemListIndexSortDirsFirst, - VarItemListIndexDarkMode, - VarItemListIndexLeftHanded, VarItemListIndexFavoriteTimeout, }; @@ -20,24 +18,6 @@ static void xtreme_app_scene_interface_common_sort_dirs_first_changed(VariableIt app->save_settings = true; } -static void xtreme_app_scene_interface_common_dark_mode_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->dark_mode = value; - app->save_settings = true; -} - -static void xtreme_app_scene_interface_common_hand_orient_changed(VariableItem* item) { - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - if(value) { - furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient); - } else { - furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); - } -} - static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint32_t value = variable_item_get_current_value_index(item); @@ -63,21 +43,6 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first); variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); - item = variable_item_list_add( - var_item_list, "Dark Mode", 2, xtreme_app_scene_interface_common_dark_mode_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->dark_mode); - variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, - "Left Handed", - 2, - xtreme_app_scene_interface_common_hand_orient_changed, - app); - bool value = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient); - variable_item_set_current_value_index(item, value); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - item = variable_item_list_add( var_item_list, "Favorite Timeout", diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c index 84bb37886..62f9af2a5 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c @@ -1,13 +1,12 @@ #include "../xtreme_app.h" enum VarItemListIndex { + VarItemListIndexScreen, VarItemListIndexChangeDeviceName, VarItemListIndexDolphinLevel, VarItemListIndexDolphinAngry, VarItemListIndexButthurtTimer, VarItemListIndexChargeCap, - VarItemListIndexRgbBacklight, - VarItemListIndexLcdColor, }; void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) { @@ -57,15 +56,6 @@ static void xtreme_app_scene_misc_charge_cap_changed(VariableItem* item) { app->save_settings = true; } -static void xtreme_app_scene_misc_lcd_color_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); - rgb_backlight_set_color(index); - app->save_backlight = true; - notification_message(app->notification, &sequence_display_backlight_on); -} - void xtreme_app_scene_misc_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); @@ -73,6 +63,9 @@ void xtreme_app_scene_misc_on_enter(void* context) { VariableItem* item; uint8_t value_index; + item = variable_item_list_add(var_item_list, "Screen", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app); char level_str[4]; @@ -120,20 +113,6 @@ void xtreme_app_scene_misc_on_enter(void* context) { variable_item_set_current_value_index(item, value_index - 1); variable_item_set_current_value_text(item, cap_str); - item = variable_item_list_add(var_item_list, "RGB Backlight", 1, NULL, app); - variable_item_set_current_value_text(item, xtreme_settings->rgb_backlight ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, - "LCD Color", - rgb_backlight_get_color_count(), - xtreme_app_scene_misc_lcd_color_changed, - app); - value_index = rgb_backlight_get_settings()->display_color_index; - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); - variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); - variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_misc_var_item_list_callback, app); @@ -151,36 +130,12 @@ bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMisc, event.event); consumed = true; switch(event.event) { + case VarItemListIndexScreen: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); + break; case VarItemListIndexChangeDeviceName: scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename); break; - case VarItemListIndexRgbBacklight: { - bool change = XTREME_SETTINGS()->rgb_backlight; - if(!change) { - DialogMessage* msg = dialog_message_alloc(); - dialog_message_set_header(msg, "RGB Backlight", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(msg, "No", NULL, "Yes"); - dialog_message_set_text( - msg, - "This option requires installing\na hardware modification!\nIs it installed?", - 64, - 32, - AlignCenter, - AlignCenter); - if(dialog_message_show(app->dialogs, msg) == DialogMessageButtonRight) { - change = true; - } - dialog_message_free(msg); - } - if(change) { - XTREME_SETTINGS()->rgb_backlight = !XTREME_SETTINGS()->rgb_backlight; - app->save_settings = true; - notification_message(app->notification, &sequence_display_backlight_on); - scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc); - } - break; - } default: break; } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c new file mode 100644 index 000000000..b86326aae --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c @@ -0,0 +1,130 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexDarkMode, + VarItemListIndexLeftHanded, + VarItemListIndexRgbBacklight, + VarItemListIndexLcdColor, +}; + +void xtreme_app_scene_misc_screen_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_misc_screen_dark_mode_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->dark_mode = value; + app->save_settings = true; +} + +static void xtreme_app_scene_misc_screen_hand_orient_changed(VariableItem* item) { + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + if(value) { + furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); + } +} + +static void xtreme_app_scene_misc_screen_lcd_color_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); + rgb_backlight_set_color(index); + app->save_backlight = true; + notification_message(app->notification, &sequence_display_backlight_on); +} + +void xtreme_app_scene_misc_screen_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + var_item_list, "Dark Mode", 2, xtreme_app_scene_misc_screen_dark_mode_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->dark_mode); + variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "Left Handed", 2, xtreme_app_scene_misc_screen_hand_orient_changed, app); + bool value = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient); + variable_item_set_current_value_index(item, value); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + + item = variable_item_list_add(var_item_list, "RGB Backlight", 1, NULL, app); + variable_item_set_current_value_text(item, xtreme_settings->rgb_backlight ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, + "LCD Color", + rgb_backlight_get_color_count(), + xtreme_app_scene_misc_screen_lcd_color_changed, + app); + value_index = rgb_backlight_get_settings()->display_color_index; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_misc_screen_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMiscScreen)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_misc_screen_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscScreen, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexRgbBacklight: { + bool change = XTREME_SETTINGS()->rgb_backlight; + if(!change) { + DialogMessage* msg = dialog_message_alloc(); + dialog_message_set_header(msg, "RGB Backlight", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(msg, "No", NULL, "Yes"); + dialog_message_set_text( + msg, + "This option requires installing\na hardware modification!\nIs it installed?", + 64, + 32, + AlignCenter, + AlignCenter); + if(dialog_message_show(app->dialogs, msg) == DialogMessageButtonRight) { + change = true; + } + dialog_message_free(msg); + } + if(change) { + XTREME_SETTINGS()->rgb_backlight = !XTREME_SETTINGS()->rgb_backlight; + app->save_settings = true; + notification_message(app->notification, &sequence_display_backlight_on); + scene_manager_previous_scene(app->scene_manager); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); + } + break; + } + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_screen_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} From 022e686e4a804e8a1a5f73b18b24b2304010462a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:06:24 +0100 Subject: [PATCH 037/370] Move dolphin options to own submenu --- .../scenes/xtreme_app_scene_config.h | 1 + .../xtreme_app/scenes/xtreme_app_scene_misc.c | 74 ++---------- .../scenes/xtreme_app_scene_misc_dolphin.c | 114 ++++++++++++++++++ 3 files changed, 122 insertions(+), 67 deletions(-) create mode 100644 applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h index 23459cd15..15c25f665 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -13,4 +13,5 @@ ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper) ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd) ADD_SCENE(xtreme_app, misc, Misc) ADD_SCENE(xtreme_app, misc_screen, MiscScreen) +ADD_SCENE(xtreme_app, misc_dolphin, MiscDolphin) ADD_SCENE(xtreme_app, misc_rename, MiscRename) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c index 62f9af2a5..1e5e61fc0 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c @@ -2,10 +2,8 @@ enum VarItemListIndex { VarItemListIndexScreen, + VarItemListIndexDolphin, VarItemListIndexChangeDeviceName, - VarItemListIndexDolphinLevel, - VarItemListIndexDolphinAngry, - VarItemListIndexButthurtTimer, VarItemListIndexChargeCap, }; @@ -14,37 +12,6 @@ void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_misc_dolphin_level_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->dolphin_level = variable_item_get_current_value_index(item) + 1; - char level_str[4]; - snprintf(level_str, 4, "%li", app->dolphin_level); - variable_item_set_current_value_text(item, level_str); - app->save_level = true; -} - -static void xtreme_app_scene_misc_dolphin_angry_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->dolphin_angry = variable_item_get_current_value_index(item); - char angry_str[4]; - snprintf(angry_str, 4, "%li", app->dolphin_angry); - variable_item_set_current_value_text(item, angry_str); - app->save_angry = true; -} - -const char* const butthurt_timer_names[] = - {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"}; -const uint32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] = - {0, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800}; -static void xtreme_app_scene_misc_butthurt_timer_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, butthurt_timer_names[index]); - XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index]; - app->save_settings = true; - app->require_reboot = true; -} - #define CHARGE_CAP_INTV 5 static void xtreme_app_scene_misc_charge_cap_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); @@ -66,41 +33,11 @@ void xtreme_app_scene_misc_on_enter(void* context) { item = variable_item_list_add(var_item_list, "Screen", 0, NULL, app); variable_item_set_current_value_text(item, ">"); + item = variable_item_list_add(var_item_list, "Dolphin", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app); - char level_str[4]; - snprintf(level_str, 4, "%li", app->dolphin_level); - item = variable_item_list_add( - var_item_list, - "Dolphin Level", - DOLPHIN_LEVEL_COUNT + 1, - xtreme_app_scene_misc_dolphin_level_changed, - app); - variable_item_set_current_value_index(item, app->dolphin_level - 1); - variable_item_set_current_value_text(item, level_str); - - char angry_str[4]; - snprintf(angry_str, 4, "%li", app->dolphin_angry); - item = variable_item_list_add( - var_item_list, - "Dolphin Angry", - BUTTHURT_MAX + 1, - xtreme_app_scene_misc_dolphin_angry_changed, - app); - variable_item_set_current_value_index(item, app->dolphin_angry); - variable_item_set_current_value_text(item, angry_str); - - item = variable_item_list_add( - var_item_list, - "Butthurt Timer", - COUNT_OF(butthurt_timer_names), - xtreme_app_scene_misc_butthurt_timer_changed, - app); - value_index = value_index_uint32( - xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_values)); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, butthurt_timer_names[value_index]); - char cap_str[6]; value_index = xtreme_settings->charge_cap / CHARGE_CAP_INTV; snprintf(cap_str, 6, "%lu%%", (uint32_t)value_index * CHARGE_CAP_INTV); @@ -133,6 +70,9 @@ bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) { case VarItemListIndexScreen: scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); break; + case VarItemListIndexDolphin: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscDolphin); + break; case VarItemListIndexChangeDeviceName: scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename); break; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c new file mode 100644 index 000000000..e6425d20b --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c @@ -0,0 +1,114 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexDolphinLevel, + VarItemListIndexDolphinAngry, + VarItemListIndexButthurtTimer, +}; + +void xtreme_app_scene_misc_dolphin_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_misc_dolphin_dolphin_level_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->dolphin_level = variable_item_get_current_value_index(item) + 1; + char level_str[4]; + snprintf(level_str, 4, "%li", app->dolphin_level); + variable_item_set_current_value_text(item, level_str); + app->save_level = true; +} + +static void xtreme_app_scene_misc_dolphin_dolphin_angry_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->dolphin_angry = variable_item_get_current_value_index(item); + char angry_str[4]; + snprintf(angry_str, 4, "%li", app->dolphin_angry); + variable_item_set_current_value_text(item, angry_str); + app->save_angry = true; +} + +const char* const butthurt_timer_names[] = + {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"}; +const uint32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] = + {0, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800}; +static void xtreme_app_scene_misc_dolphin_butthurt_timer_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, butthurt_timer_names[index]); + XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index]; + app->save_settings = true; + app->require_reboot = true; +} + +void xtreme_app_scene_misc_dolphin_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + char level_str[4]; + snprintf(level_str, 4, "%li", app->dolphin_level); + item = variable_item_list_add( + var_item_list, + "Dolphin Level", + DOLPHIN_LEVEL_COUNT + 1, + xtreme_app_scene_misc_dolphin_dolphin_level_changed, + app); + variable_item_set_current_value_index(item, app->dolphin_level - 1); + variable_item_set_current_value_text(item, level_str); + + char angry_str[4]; + snprintf(angry_str, 4, "%li", app->dolphin_angry); + item = variable_item_list_add( + var_item_list, + "Dolphin Angry", + BUTTHURT_MAX + 1, + xtreme_app_scene_misc_dolphin_dolphin_angry_changed, + app); + variable_item_set_current_value_index(item, app->dolphin_angry); + variable_item_set_current_value_text(item, angry_str); + + item = variable_item_list_add( + var_item_list, + "Butthurt Timer", + COUNT_OF(butthurt_timer_names), + xtreme_app_scene_misc_dolphin_butthurt_timer_changed, + app); + value_index = value_index_uint32( + xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_values)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, butthurt_timer_names[value_index]); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_misc_dolphin_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMiscDolphin)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_misc_dolphin_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscDolphin, event.event); + consumed = true; + switch(event.event) { + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_dolphin_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} From fff830b4633ca7a02e23f65c0031b66cb4026999 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:10:47 +0100 Subject: [PATCH 038/370] Rename frequencies menus to freqs to avoid scroll --- .../scenes/xtreme_app_scene_config.h | 8 ++--- .../scenes/xtreme_app_scene_protocols.c | 8 ++--- ...s.c => xtreme_app_scene_protocols_freqs.c} | 36 +++++++++---------- ...=> xtreme_app_scene_protocols_freqs_add.c} | 14 ++++---- ...xtreme_app_scene_protocols_freqs_hopper.c} | 29 +++++++-------- ...xtreme_app_scene_protocols_freqs_static.c} | 29 +++++++-------- applications/main/xtreme_app/xtreme_app.c | 2 +- applications/main/xtreme_app/xtreme_app.h | 2 +- 8 files changed, 61 insertions(+), 67 deletions(-) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_protocols_frequencies.c => xtreme_app_scene_protocols_freqs.c} (59%) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_protocols_frequencies_add.c => xtreme_app_scene_protocols_freqs_add.c} (82%) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_protocols_frequencies_hopper.c => xtreme_app_scene_protocols_freqs_hopper.c} (74%) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_protocols_frequencies_static.c => xtreme_app_scene_protocols_freqs_static.c} (74%) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h index 15c25f665..8e283e9eb 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -7,10 +7,10 @@ ADD_SCENE(xtreme_app, interface_lockscreen, InterfaceLockscreen) ADD_SCENE(xtreme_app, interface_statusbar, InterfaceStatusbar) ADD_SCENE(xtreme_app, interface_common, InterfaceCommon) ADD_SCENE(xtreme_app, protocols, Protocols) -ADD_SCENE(xtreme_app, protocols_frequencies, ProtocolsFrequencies) -ADD_SCENE(xtreme_app, protocols_frequencies_static, ProtocolsFrequenciesStatic) -ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper) -ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd) +ADD_SCENE(xtreme_app, protocols_freqs, ProtocolsFreqs) +ADD_SCENE(xtreme_app, protocols_freqs_static, ProtocolsFreqsStatic) +ADD_SCENE(xtreme_app, protocols_freqs_hopper, ProtocolsFreqsHopper) +ADD_SCENE(xtreme_app, protocols_freqs_add, ProtocolsFreqsAdd) ADD_SCENE(xtreme_app, misc, Misc) ADD_SCENE(xtreme_app, misc_screen, MiscScreen) ADD_SCENE(xtreme_app, misc_dolphin, MiscDolphin) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index 497d3f1ec..67c80b14e 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -3,7 +3,7 @@ enum VarItemListIndex { VarItemListIndexBadkbMode, VarItemListIndexBadbtRemember, - VarItemListIndexSubghzFrequencies, + VarItemListIndexSubghzFreqs, VarItemListIndexSubghzExtend, }; @@ -55,7 +55,7 @@ void xtreme_app_scene_protocols_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); - item = variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "SubGHz Freqs", 0, NULL, app); variable_item_set_current_value_text(item, ">"); item = variable_item_list_add( @@ -80,8 +80,8 @@ bool xtreme_app_scene_protocols_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocols, event.event); consumed = true; switch(event.event) { - case VarItemListIndexSubghzFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequencies); + case VarItemListIndexSubghzFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqs); break; default: break; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c similarity index 59% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c index 668fe8327..f8c779cc5 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c @@ -2,24 +2,24 @@ enum VarItemListIndex { VarItemListIndexUseDefaults, - VarItemListIndexStaticFrequencies, - VarItemListIndexHopperFrequencies, + VarItemListIndexStaticFreqs, + VarItemListIndexHopperFreqs, }; -void xtreme_app_scene_protocols_frequencies_var_item_list_callback(void* context, uint32_t index) { +void xtreme_app_scene_protocols_freqs_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_use_defaults_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_use_defaults_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); app->subghz_use_defaults = value; - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; } -void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -28,41 +28,41 @@ void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { var_item_list, "Use Defaults", 2, - xtreme_app_scene_protocols_frequencies_use_defaults_changed, + xtreme_app_scene_protocols_freqs_use_defaults_changed, app); variable_item_set_current_value_index(item, app->subghz_use_defaults); variable_item_set_current_value_text(item, app->subghz_use_defaults ? "ON" : "OFF"); - item = variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Static Freqs", 0, NULL, app); variable_item_set_current_value_text(item, ">"); - item = variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Hopper Freqs", 0, NULL, app); variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequencies)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqs)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequencies, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqs, event.event); consumed = true; switch(event.event) { - case VarItemListIndexStaticFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic); + case VarItemListIndexStaticFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic); break; - case VarItemListIndexHopperFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper); + case VarItemListIndexHopperFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper); break; default: break; @@ -72,7 +72,7 @@ bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManager return consumed; } -void xtreme_app_scene_protocols_frequencies_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c similarity index 82% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c index a9efa8b54..314a69e1e 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c @@ -5,7 +5,7 @@ enum TextInputResult { TextInputResultError, }; -static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* context) { +static void xtreme_app_scene_protocols_freqs_add_text_input_callback(void* context) { XtremeApp* app = context; char* end; @@ -15,17 +15,17 @@ static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* return; } bool is_hopper = - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); if(is_hopper) { FrequencyList_push_back(app->subghz_hopper_freqs, value); } else { FrequencyList_push_back(app->subghz_static_freqs, value); } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); } -void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_add_on_enter(void* context) { XtremeApp* app = context; TextInput* text_input = app->text_input; @@ -35,7 +35,7 @@ void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) { text_input_set_result_callback( text_input, - xtreme_app_scene_protocols_frequencies_add_text_input_callback, + xtreme_app_scene_protocols_freqs_add_text_input_callback, app, app->subghz_freq_buffer, XTREME_SUBGHZ_FREQ_BUFFER_SIZE, @@ -49,7 +49,7 @@ void callback_return(void* context) { scene_manager_previous_scene(app->scene_manager); } -bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_add_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; @@ -77,7 +77,7 @@ bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneMan return consumed; } -void xtreme_app_scene_protocols_frequencies_add_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_add_on_exit(void* context) { XtremeApp* app = context; text_input_reset(app->text_input); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c similarity index 74% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c index b9e387252..13fdc0ac2 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c @@ -6,14 +6,12 @@ enum VarItemListIndex { VarItemListIndexAddHopperFreq, }; -void xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback( - void* context, - uint32_t index) { +void xtreme_app_scene_protocols_freqs_hopper_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_hopper_frequency_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); app->subghz_hopper_index = variable_item_get_current_value_index(item); uint32_t value = *FrequencyList_get(app->subghz_hopper_freqs, app->subghz_hopper_index); @@ -22,7 +20,7 @@ static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(Vari variable_item_set_current_value_text(item, text); } -void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_hopper_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -31,7 +29,7 @@ void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { var_item_list, "Hopper Freq", FrequencyList_size(app->subghz_hopper_freqs), - xtreme_app_scene_protocols_frequencies_hopper_frequency_changed, + xtreme_app_scene_protocols_freqs_hopper_frequency_changed, app); app->subghz_hopper_index = 0; variable_item_set_current_value_index(item, app->subghz_hopper_index); @@ -49,23 +47,22 @@ void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { variable_item_list_add(var_item_list, "Add Hopper Freq", 0, NULL, app); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_hopper_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_hopper_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqsHopper, event.event); consumed = true; switch(event.event) { case VarItemListIndexRemoveHopperFreq: @@ -81,14 +78,14 @@ bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, Scene FrequencyList_next(it); } } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper); break; case VarItemListIndexAddHopperFreq: scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, true); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + app->scene_manager, XtremeAppSceneProtocolsFreqsAdd, true); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); break; default: break; @@ -98,7 +95,7 @@ bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, Scene return consumed; } -void xtreme_app_scene_protocols_frequencies_hopper_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_hopper_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c similarity index 74% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c index b50c1f896..2f139b2b2 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c @@ -6,14 +6,12 @@ enum VarItemListIndex { VarItemListIndexAddStaticFreq, }; -void xtreme_app_scene_protocols_frequencies_static_var_item_list_callback( - void* context, - uint32_t index) { +void xtreme_app_scene_protocols_freqs_static_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_static_frequency_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); app->subghz_static_index = variable_item_get_current_value_index(item); uint32_t value = *FrequencyList_get(app->subghz_static_freqs, app->subghz_static_index); @@ -22,7 +20,7 @@ static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(Vari variable_item_set_current_value_text(item, text); } -void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_static_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -31,7 +29,7 @@ void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { var_item_list, "Static Freq", FrequencyList_size(app->subghz_static_freqs), - xtreme_app_scene_protocols_frequencies_static_frequency_changed, + xtreme_app_scene_protocols_freqs_static_frequency_changed, app); app->subghz_static_index = 0; variable_item_set_current_value_index(item, app->subghz_static_index); @@ -49,23 +47,22 @@ void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { variable_item_list_add(var_item_list, "Add Static Freq", 0, NULL, app); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_static_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_static_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_static_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqsStatic, event.event); consumed = true; switch(event.event) { case VarItemListIndexRemoveStaticFreq: @@ -81,14 +78,14 @@ bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, Scene FrequencyList_next(it); } } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic); break; case VarItemListIndexAddStaticFreq: scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, false); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + app->scene_manager, XtremeAppSceneProtocolsFreqsAdd, false); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); break; default: break; @@ -98,7 +95,7 @@ bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, Scene return consumed; } -void xtreme_app_scene_protocols_frequencies_static_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_static_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index dedd9fcde..3d5760923 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -27,7 +27,7 @@ bool xtreme_app_apply(XtremeApp* app) { stream_free(stream); } - if(app->save_subghz_frequencies) { + if(app->save_subghz_freqs) { FlipperFormat* file = flipper_format_file_alloc(storage); do { FrequencyList_it_t it; diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index ce50facdd..321ad167c 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -57,7 +57,7 @@ typedef struct { FuriString* version_tag; bool save_mainmenu_apps; - bool save_subghz_frequencies; + bool save_subghz_freqs; bool save_subghz; bool save_name; bool save_level; From 4111ff4e045dc4ba349d859e4751c94c7ce91cf1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:17:28 +0100 Subject: [PATCH 039/370] Rename common section to file browser --- .../scenes/xtreme_app_scene_config.h | 2 +- .../scenes/xtreme_app_scene_interface.c | 8 +++---- ... xtreme_app_scene_interface_filebrowser.c} | 22 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_interface_common.c => xtreme_app_scene_interface_filebrowser.c} (72%) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h index 8e283e9eb..77ba43991 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -5,7 +5,7 @@ ADD_SCENE(xtreme_app, interface_mainmenu, InterfaceMainmenu) ADD_SCENE(xtreme_app, interface_mainmenu_add, InterfaceMainmenuAdd) ADD_SCENE(xtreme_app, interface_lockscreen, InterfaceLockscreen) ADD_SCENE(xtreme_app, interface_statusbar, InterfaceStatusbar) -ADD_SCENE(xtreme_app, interface_common, InterfaceCommon) +ADD_SCENE(xtreme_app, interface_filebrowser, InterfaceFilebrowser) ADD_SCENE(xtreme_app, protocols, Protocols) ADD_SCENE(xtreme_app, protocols_freqs, ProtocolsFreqs) ADD_SCENE(xtreme_app, protocols_freqs_static, ProtocolsFreqsStatic) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c index 5e04fc4b8..e6b62d034 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c @@ -5,7 +5,7 @@ enum VarItemListIndex { VarItemListIndexMainmenu, VarItemListIndexLockscreen, VarItemListIndexStatusbar, - VarItemListIndexCommon, + VarItemListIndexFileBrowser, }; void xtreme_app_scene_interface_var_item_list_callback(void* context, uint32_t index) { @@ -30,7 +30,7 @@ void xtreme_app_scene_interface_on_enter(void* context) { item = variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); variable_item_set_current_value_text(item, ">"); - item = variable_item_list_add(var_item_list, "Common", 0, NULL, app); + item = variable_item_list_add(var_item_list, "File Browser", 0, NULL, app); variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( @@ -62,8 +62,8 @@ bool xtreme_app_scene_interface_on_event(void* context, SceneManagerEvent event) case VarItemListIndexStatusbar: scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceStatusbar); break; - case VarItemListIndexCommon: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceCommon); + case VarItemListIndexFileBrowser: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceFilebrowser); break; default: break; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c similarity index 72% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c index 3efa4fffa..2df77cdf3 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c @@ -5,12 +5,12 @@ enum VarItemListIndex { VarItemListIndexFavoriteTimeout, }; -void xtreme_app_scene_interface_common_var_item_list_callback(void* context, uint32_t index) { +void xtreme_app_scene_interface_filebrowser_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_interface_common_sort_dirs_first_changed(VariableItem* item) { +static void xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -18,7 +18,7 @@ static void xtreme_app_scene_interface_common_sort_dirs_first_changed(VariableIt app->save_settings = true; } -static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableItem* item) { +static void xtreme_app_scene_interface_filebrowser_favorite_timeout_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint32_t value = variable_item_get_current_value_index(item); char text[6]; @@ -28,7 +28,7 @@ static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableI app->save_settings = true; } -void xtreme_app_scene_interface_common_on_enter(void* context) { +void xtreme_app_scene_interface_filebrowser_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); VariableItemList* var_item_list = app->var_item_list; @@ -38,7 +38,7 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { var_item_list, "Sort Dirs First", 2, - xtreme_app_scene_interface_common_sort_dirs_first_changed, + xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed, app); variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first); variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); @@ -47,7 +47,7 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { var_item_list, "Favorite Timeout", 61, - xtreme_app_scene_interface_common_favorite_timeout_changed, + xtreme_app_scene_interface_filebrowser_favorite_timeout_changed, app); variable_item_set_current_value_index(item, xtreme_settings->favorite_timeout); char text[4]; @@ -55,22 +55,22 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { variable_item_set_current_value_text(item, xtreme_settings->favorite_timeout ? text : "OFF"); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_interface_common_var_item_list_callback, app); + var_item_list, xtreme_app_scene_interface_filebrowser_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneInterfaceCommon)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneInterfaceFilebrowser)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_interface_common_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_interface_filebrowser_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneInterfaceCommon, event.event); + app->scene_manager, XtremeAppSceneInterfaceFilebrowser, event.event); consumed = true; switch(event.event) { default: @@ -81,7 +81,7 @@ bool xtreme_app_scene_interface_common_on_event(void* context, SceneManagerEvent return consumed; } -void xtreme_app_scene_interface_common_on_exit(void* context) { +void xtreme_app_scene_interface_filebrowser_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } From c4624ea209f2065644b44273a58474446c721798 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:21:42 +0100 Subject: [PATCH 040/370] Sort settings file and struct --- lib/xtreme/settings.c | 24 ++++++++++++------------ lib/xtreme/xtreme.h | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index f3e3cd6c2..d69800154 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -24,13 +24,13 @@ XtremeSettings xtreme_settings = { .bar_borders = true, // ON .bar_background = false, // OFF .sort_dirs_first = true, // ON - .dark_mode = false, // OFF .favorite_timeout = 0, // OFF .bad_bt = false, // USB .bad_bt_remember = false, // OFF + .dark_mode = false, // OFF + .rgb_backlight = false, // OFF .butthurt_timer = 21600, // 6 H .charge_cap = 100, // 100% - .rgb_backlight = false, // OFF }; void XTREME_SETTINGS_LOAD() { @@ -117,10 +117,6 @@ void XTREME_SETTINGS_LOAD() { x->sort_dirs_first = b; } flipper_format_rewind(file); - if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { - x->dark_mode = b; - } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "favorite_timeout", &u, 1)) { x->favorite_timeout = CLAMP(u, 60U, 0U); } @@ -133,6 +129,14 @@ void XTREME_SETTINGS_LOAD() { x->bad_bt_remember = b; } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { + x->dark_mode = b; + } + flipper_format_rewind(file); + if(flipper_format_read_bool(file, "rgb_backlight", &b, 1)) { + x->rgb_backlight = b; + } + flipper_format_rewind(file); if(flipper_format_read_uint32(file, "butthurt_timer", &u, 1)) { x->butthurt_timer = CLAMP(u, 172800U, 0U); } @@ -140,10 +144,6 @@ void XTREME_SETTINGS_LOAD() { if(flipper_format_read_uint32(file, "charge_cap", &u, 1)) { x->charge_cap = CLAMP(u, 100U, 5U); } - flipper_format_rewind(file); - if(flipper_format_read_bool(file, "rgb_backlight", &b, 1)) { - x->rgb_backlight = b; - } } flipper_format_free(file); furi_record_close(RECORD_STORAGE); @@ -174,13 +174,13 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); - flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); flipper_format_write_uint32(file, "favorite_timeout", &x->favorite_timeout, 1); flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); + flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); flipper_format_write_uint32(file, "butthurt_timer", &x->butthurt_timer, 1); flipper_format_write_uint32(file, "charge_cap", &x->charge_cap, 1); - flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index ea6076354..4a52cd7cf 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -33,13 +33,13 @@ typedef struct { bool bar_borders; bool bar_background; bool sort_dirs_first; - bool dark_mode; uint32_t favorite_timeout; bool bad_bt; bool bad_bt_remember; + bool dark_mode; + bool rgb_backlight; uint32_t butthurt_timer; uint32_t charge_cap; - bool rgb_backlight; } XtremeSettings; void XTREME_SETTINGS_SAVE(); From 1e299c1b74e95ca87f08c251a0f8736354a4f481 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:39:52 +0100 Subject: [PATCH 041/370] Add hidden files and internal tab settings --- .../main/archive/helpers/archive_browser.c | 16 +++++---- .../xtreme_app_scene_interface_filebrowser.c | 36 +++++++++++++++++++ lib/xtreme/settings.c | 14 +++++++- lib/xtreme/xtreme.h | 2 ++ 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index aaef1c9e5..5c6f486d1 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -471,10 +471,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; + for(int i = 0; i < 2; i++) { + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; + } + if(tab == ArchiveTabInternal && !XTREME_SETTINGS()->show_internal_tab) continue; + break; } browser->is_root = true; @@ -503,14 +507,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { bool hide_dot_files = !is_browser ? true : tab == ArchiveTabInternal ? false : - !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); + !XTREME_SETTINGS()->show_hidden_files; archive_file_browser_set_path( browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files); tab_empty = false; // Empty check will be performed later } } - if((tab_empty) && (tab != ArchiveTabBrowser) && (tab != ArchiveTabInternal)) { + if(tab_empty && tab != ArchiveTabBrowser && tab != ArchiveTabInternal) { archive_switch_tab(browser, key); } else { with_view_model( diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c index 2df77cdf3..ed1951626 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c @@ -2,6 +2,8 @@ enum VarItemListIndex { VarItemListIndexSortDirsFirst, + VarItemListIndexShowHiddenFiles, + VarItemListIndexShowInternalTab, VarItemListIndexFavoriteTimeout, }; @@ -18,6 +20,22 @@ static void xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed(Varia app->save_settings = true; } +static void xtreme_app_scene_interface_filebrowser_show_hidden_files_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->show_hidden_files = value; + app->save_settings = true; +} + +static void xtreme_app_scene_interface_filebrowser_show_internal_tab_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->show_internal_tab = value; + app->save_settings = true; +} + static void xtreme_app_scene_interface_filebrowser_favorite_timeout_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint32_t value = variable_item_get_current_value_index(item); @@ -43,6 +61,24 @@ void xtreme_app_scene_interface_filebrowser_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first); variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, + "Show Hidden Files", + 2, + xtreme_app_scene_interface_filebrowser_show_hidden_files_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->show_hidden_files); + variable_item_set_current_value_text(item, xtreme_settings->show_hidden_files ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, + "Show Internal Tab", + 2, + xtreme_app_scene_interface_filebrowser_show_internal_tab_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->show_internal_tab); + variable_item_set_current_value_text(item, xtreme_settings->show_internal_tab ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Favorite Timeout", diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index d69800154..023cbcbc9 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -6,7 +6,7 @@ #define TAG "XtremeSettings" XtremeSettings xtreme_settings = { - .asset_pack = "", + .asset_pack = "", // SFW .anim_speed = 100, // 100% .cycle_anims = 0, // Meta.txt .unlock_anims = false, // OFF @@ -24,6 +24,8 @@ XtremeSettings xtreme_settings = { .bar_borders = true, // ON .bar_background = false, // OFF .sort_dirs_first = true, // ON + .show_hidden_files = false, // OFF + .show_internal_tab = false, // OFF .favorite_timeout = 0, // OFF .bad_bt = false, // USB .bad_bt_remember = false, // OFF @@ -117,6 +119,14 @@ void XTREME_SETTINGS_LOAD() { x->sort_dirs_first = b; } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "show_hidden_files", &b, 1)) { + x->show_hidden_files = b; + } + flipper_format_rewind(file); + if(flipper_format_read_bool(file, "show_internal_tab", &b, 1)) { + x->show_internal_tab = b; + } + flipper_format_rewind(file); if(flipper_format_read_uint32(file, "favorite_timeout", &u, 1)) { x->favorite_timeout = CLAMP(u, 60U, 0U); } @@ -174,6 +184,8 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); + flipper_format_write_bool(file, "show_hidden_files", &x->show_hidden_files, 1); + flipper_format_write_bool(file, "show_internal_tab", &x->show_internal_tab, 1); flipper_format_write_uint32(file, "favorite_timeout", &x->favorite_timeout, 1); flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 4a52cd7cf..89a126201 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -33,6 +33,8 @@ typedef struct { bool bar_borders; bool bar_background; bool sort_dirs_first; + bool show_hidden_files; + bool show_internal_tab; uint32_t favorite_timeout; bool bad_bt; bool bad_bt_remember; From f01b68e1e0ff17b58a948a888211af770ce93674 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 01:08:55 +0100 Subject: [PATCH 042/370] Allow desktop clock with no battery icon --- .../xtreme_app_scene_interface_statusbar.c | 2 + applications/services/gui/gui.c | 120 ++++++++---------- .../services/power/power_service/power.c | 7 + .../services/power/power_service/power.h | 7 + firmware/targets/f7/api_symbols.csv | 1 + 5 files changed, 73 insertions(+), 64 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c index 1a3eb36b1..5d58472d8 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c @@ -20,6 +20,8 @@ static void xtreme_app_scene_interface_statusbar_battery_icon_changed(VariableIt variable_item_set_current_value_text(item, battery_icon_names[index]); XTREME_SETTINGS()->battery_icon = index; app->save_settings = true; + power_set_battery_icon_enabled(furi_record_open(RECORD_POWER), index != BatteryIconOff); + furi_record_close(RECORD_POWER); } static void xtreme_app_scene_interface_statusbar_status_icons_changed(VariableItem* item) { diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 500d746f8..d6445d38e 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -100,75 +100,67 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { } canvas_set_bitmap_mode(gui->canvas, 0); - uint8_t x; - // Right side - if(xtreme_settings->battery_icon != BatteryIconOff) { - x = GUI_DISPLAY_WIDTH - 1; - ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); - while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { - ViewPort* view_port = *ViewPortArray_ref(it); - if(view_port_is_enabled(view_port)) { - width = view_port_get_width(view_port); - if(!width) width = 8; - // Recalculate next position - right_used += (width + 2); - x -= (width + 2); - // Prepare work area background - canvas_frame_set( - gui->canvas, - x - 1, - GUI_STATUS_BAR_Y + 1, - width + 2, - GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - // Hide battery background - if(xtreme_settings->bar_borders) { - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box( - gui->canvas, - -1, - 0, - canvas_width(gui->canvas) + 1, - canvas_height(gui->canvas)); - } - canvas_set_color(gui->canvas, ColorBlack); - // ViewPort draw - canvas_frame_set( - gui->canvas, - x - xtreme_settings->bar_borders, - GUI_STATUS_BAR_Y + 2, - width, - GUI_STATUS_BAR_WORKAREA_HEIGHT); - view_port_draw(view_port, gui->canvas); - } - ViewPortArray_next(it); - } - // Draw frame around icons on the right - if(right_used) { + uint8_t x = GUI_DISPLAY_WIDTH - 1; + ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); + while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + width = view_port_get_width(view_port); + if(!width) width = 8; + // Recalculate next position + right_used += (width + 2); + x -= (width + 2); + // Prepare work area background canvas_frame_set( gui->canvas, - GUI_DISPLAY_WIDTH - 4 - right_used, - GUI_STATUS_BAR_Y, - right_used + 4, - GUI_STATUS_BAR_HEIGHT); - // Disable battery border + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + // Hide battery background if(xtreme_settings->bar_borders) { - canvas_set_color(gui->canvas, ColorBlack); - canvas_draw_rframe( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); - canvas_draw_line( - gui->canvas, - canvas_width(gui->canvas) - 2, - 1, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - canvas_draw_line( - gui->canvas, - 1, - canvas_height(gui->canvas) - 2, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); } + canvas_set_color(gui->canvas, ColorBlack); + // ViewPort draw + canvas_frame_set( + gui->canvas, + x - xtreme_settings->bar_borders, + GUI_STATUS_BAR_Y + 2, + width, + GUI_STATUS_BAR_WORKAREA_HEIGHT); + view_port_draw(view_port, gui->canvas); + } + ViewPortArray_next(it); + } + // Draw frame around icons on the right + if(right_used) { + canvas_frame_set( + gui->canvas, + GUI_DISPLAY_WIDTH - 4 - right_used, + GUI_STATUS_BAR_Y, + right_used + 4, + GUI_STATUS_BAR_HEIGHT); + // Disable battery border + if(xtreme_settings->bar_borders) { + canvas_set_color(gui->canvas, ColorBlack); + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); } } diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 0810c84d6..51fcb3895 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -7,6 +7,12 @@ #define POWER_OFF_TIMEOUT 90 #define TAG "Power" +void power_set_battery_icon_enabled(Power* power, bool is_enabled) { + furi_assert(power); + + view_port_enabled_set(power->battery_view_port, is_enabled); +} + void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); Power* power = context; @@ -356,6 +362,7 @@ Power* power_alloc() { // Battery view port power->battery_view_port = power_battery_view_port_alloc(power); + power_set_battery_icon_enabled(power, XTREME_SETTINGS()->battery_icon != BatteryIconOff); power->show_low_bat_level_message = true; //Auto shutdown timer diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 8a9d5947d..a2dc34f90 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -120,6 +120,13 @@ void power_enable_low_battery_level_notification(Power* power, bool enable); */ void power_trigger_ui_update(Power* power); +/** Enable or disable battery icon + * + * @param power Power instance + * @param is_enabled Show battery or not + */ +void power_set_battery_icon_enabled(Power* power, bool is_enabled); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a8a32069b..5a8de3d23 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2276,6 +2276,7 @@ Function,+,power_get_settings_events_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode +Function,+,power_set_battery_icon_enabled,void,"Power*, _Bool" Function,-,power_trigger_ui_update,void,Power* Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" From 72ad22bb91e0277394ddf13e8ead026776224455 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Mon, 5 Jun 2023 14:25:43 +0400 Subject: [PATCH 043/370] [DEVOPS-18]: Add map file parser, mariadb inserter (#2732) --- .github/workflows/build.yml | 42 +++--- scripts/map_mariadb_insert.py | 139 +++++++++++++++++++ scripts/map_parser.py | 251 ++++++++++++++++++++++++++++++++++ 3 files changed, 414 insertions(+), 18 deletions(-) create mode 100755 scripts/map_mariadb_insert.py create mode 100755 scripts/map_parser.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca603a64a..e8b76a02c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,25 +100,31 @@ jobs: cp build/f7-firmware-*/firmware.elf map_analyser_files/firmware.elf cp ${{ github.event_path }} map_analyser_files/event.json - - name: 'Upload map analyser files to storage' + - name: 'Analyse map file' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: prewk/s3-cp-action@v2 - with: - aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" - aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" - aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" - source: "./map_analyser_files/" - dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" - flags: "--recursive --acl public-read" - - - name: 'Trigger map file reporter' - if: ${{ !github.event.pull_request.head.repo.fork }} - uses: peter-evans/repository-dispatch@v2 - with: - repository: flipperdevices/flipper-map-reporter - token: ${{ secrets.REPOSITORY_DISPATCH_TOKEN }} - event-type: map-file-analyse - client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' + run: | + source scripts/toolchain/fbtenv.sh + get_size() + { + SECTION="$1"; + arm-none-eabi-size \ + -A map_analyser_files/firmware.elf \ + | grep "^$SECTION" | awk '{print $2}' + } + export BSS_SIZE="$(get_size ".bss")" + export TEXT_SIZE="$(get_size ".text")" + export RODATA_SIZE="$(get_size ".rodata")" + export DATA_SIZE="$(get_size ".data")" + export FREE_FLASH_SIZE="$(get_size ".free_flash")" + python3 -m pip install mariadb==1.1.6 cxxfilt==0.3.0 + python3 scripts/map_parser.py map_analyser_files/firmware.elf.map map_analyser_files/firmware.elf.map.all + python3 scripts/map_mariadb_insert.py \ + ${{ secrets.AMAP_MARIADB_USER }} \ + ${{ secrets.AMAP_MARIADB_PASSWORD }} \ + ${{ secrets.AMAP_MARIADB_HOST }} \ + ${{ secrets.AMAP_MARIADB_PORT }} \ + ${{ secrets.AMAP_MARIADB_DATABASE }} \ + map_analyser_files/firmware.elf.map.all - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} diff --git a/scripts/map_mariadb_insert.py b/scripts/map_mariadb_insert.py new file mode 100755 index 000000000..a4c9ed5c7 --- /dev/null +++ b/scripts/map_mariadb_insert.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +# Requiremets: +# mariadb==1.1.6 + +from datetime import datetime +import argparse +import mariadb +import sys +import os + + +def parseArgs(): + parser = argparse.ArgumentParser() + parser.add_argument("db_user", help="MariaDB user") + parser.add_argument("db_pass", help="MariaDB password") + parser.add_argument("db_host", help="MariaDB hostname") + parser.add_argument("db_port", type=int, help="MariaDB port") + parser.add_argument("db_name", help="MariaDB database") + parser.add_argument("report_file", help="Report file(.map.all)") + args = parser.parse_args() + return args + + +def mariadbConnect(args): + try: + conn = mariadb.connect( + user=args.db_user, + password=args.db_pass, + host=args.db_host, + port=args.db_port, + database=args.db_name, + ) + except mariadb.Error as e: + print(f"Error connecting to MariaDB: {e}") + sys.exit(1) + return conn + + +def parseEnv(): + outArr = [] + outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + outArr.append(os.getenv("COMMIT_HASH", default=None)) + outArr.append(os.getenv("COMMIT_MSG", default=None)) + outArr.append(os.getenv("BRANCH_NAME", default=None)) + outArr.append(os.getenv("BSS_SIZE", default=None)) + outArr.append(os.getenv("TEXT_SIZE", default=None)) + outArr.append(os.getenv("RODATA_SIZE", default=None)) + outArr.append(os.getenv("DATA_SIZE", default=None)) + outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) + outArr.append(os.getenv("PULL_ID", default=None)) + outArr.append(os.getenv("PULL_NAME", default=None)) + return outArr + + +def createTables(cur, conn): + headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `datetime` datetime NOT NULL, \ + `commit` varchar(40) NOT NULL, \ + `commit_msg` text NOT NULL, \ + `branch_name` text NOT NULL, \ + `bss_size` int(10) unsigned NOT NULL, \ + `text_size` int(10) unsigned NOT NULL, \ + `rodata_size` int(10) unsigned NOT NULL, \ + `data_size` int(10) unsigned NOT NULL, \ + `free_flash_size` int(10) unsigned NOT NULL, \ + `pullrequest_id` int(10) unsigned DEFAULT NULL, \ + `pullrequest_name` text DEFAULT NULL, \ + PRIMARY KEY (`id`), \ + KEY `header_id_index` (`id`) )" + dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ + `header_id` int(10) unsigned NOT NULL, \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `section` text NOT NULL, \ + `address` text NOT NULL, \ + `size` int(10) unsigned NOT NULL, \ + `name` text NOT NULL, \ + `lib` text NOT NULL, \ + `obj_name` text NOT NULL, \ + PRIMARY KEY (`id`), \ + KEY `data_id_index` (`id`), \ + KEY `data_header_id_index` (`header_id`), \ + CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" + cur.execute(headerTable) + cur.execute(dataTable) + conn.commit() + + +def insertHeader(data, cur, conn): + query = "INSERT INTO `header` ( \ + datetime, commit, commit_msg, branch_name, bss_size, text_size, \ + rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + cur.execute(query, data) + conn.commit() + return cur.lastrowid + + +def parseFile(fileObj, headerID): + arr = [] + fileLines = fileObj.readlines() + for line in fileLines: + lineArr = [] + tempLineArr = line.split("\t") + lineArr.append(headerID) + lineArr.append(tempLineArr[0]) # section + lineArr.append(int(tempLineArr[2], 16)) # address hex + lineArr.append(int(tempLineArr[3])) # size + lineArr.append(tempLineArr[4]) # name + lineArr.append(tempLineArr[5]) # lib + lineArr.append(tempLineArr[6]) # obj_name + arr.append(tuple(lineArr)) + return arr + + +def insertData(data, cur, conn): + query = "INSERT INTO `data` ( \ + header_id, section, address, size, \ + name, lib, obj_name) \ + VALUES (?, ?, ?, ?, ? ,?, ?)" + cur.executemany(query, data) + conn.commit() + + +def main(): + args = parseArgs() + dbConn = mariadbConnect(args) + reportFile = open(args.report_file) + dbCurs = dbConn.cursor() + createTables(dbCurs, dbConn) + headerID = insertHeader(parseEnv(), dbCurs, dbConn) + insertData(parseFile(reportFile, headerID), dbCurs, dbConn) + reportFile.close() + dbCurs.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/map_parser.py b/scripts/map_parser.py new file mode 100755 index 000000000..c0c34e3d1 --- /dev/null +++ b/scripts/map_parser.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 + +# Requiremets: +# cxxfilt==0.3.0 + +import sys +import re +import os +from typing import TextIO +from cxxfilt import demangle + + +class Objectfile: + def __init__(self, section: str, offset: int, size: int, comment: str): + self.section = section.strip() + self.offset = offset + self.size = size + self.path = (None, None) + self.basepath = None + + if comment: + self.path = re.match(r"^(.+?)(?:\(([^\)]+)\))?$", comment).groups() + self.basepath = os.path.basename(self.path[0]) + + self.children = [] + + def __repr__(self) -> str: + return f"" + + +def update_children_size(children: list[list], subsection_size: int) -> list: + # set subsection size to an only child + if len(children) == 1: + children[0][1] = subsection_size + return children + + rest_size = subsection_size + + for index in range(1, len(children)): + if rest_size > 0: + # current size = current address - previous child address + child_size = children[index][0] - children[index - 1][0] + rest_size -= child_size + children[index - 1][1] = child_size + + # if there is rest size, set it to the last child element + if rest_size > 0: + children[-1][1] = rest_size + + return children + + +def parse_sections(file_name: str) -> list: + """ + Quick&Dirty parsing for GNU ld’s linker map output, needs LANG=C, because + some messages are localized. + """ + + sections = [] + with open(file_name, "r") as file: + # skip until memory map is found + found = False + + while True: + line = file.readline() + if not line: + break + if line.strip() == "Memory Configuration": + found = True + break + + if not found: + raise Exception(f"Memory configuration is not found in the{input_file}") + + # long section names result in a linebreak afterwards + sectionre = re.compile( + "(?P
.+?|.{14,}\n)[ ]+0x(?P[0-9a-f]+)[ ]+0x(?P[0-9a-f]+)(?:[ ]+(?P.+))?\n+", + re.I, + ) + subsectionre = re.compile( + "[ ]{16}0x(?P[0-9a-f]+)[ ]+(?P.+)\n+", re.I + ) + s = file.read() + pos = 0 + + while True: + m = sectionre.match(s, pos) + if not m: + # skip that line + try: + nextpos = s.index("\n", pos) + 1 + pos = nextpos + continue + except ValueError: + break + + pos = m.end() + section = m.group("section") + v = m.group("offset") + offset = int(v, 16) if v is not None else None + v = m.group("size") + size = int(v, 16) if v is not None else None + comment = m.group("comment") + + if section != "*default*" and size > 0: + of = Objectfile(section, offset, size, comment) + + if section.startswith(" "): + children = [] + sections[-1].children.append(of) + + while True: + m = subsectionre.match(s, pos) + if not m: + break + pos = m.end() + offset, function = m.groups() + offset = int(offset, 16) + if sections and sections[-1].children: + children.append([offset, 0, function]) + + if children: + children = update_children_size( + children=children, subsection_size=of.size + ) + + sections[-1].children[-1].children.extend(children) + + else: + sections.append(of) + + return sections + + +def get_subsection_name(section_name: str, subsection: Objectfile) -> str: + subsection_split_names = subsection.section.split(".") + if subsection.section.startswith("."): + subsection_split_names = subsection_split_names[1:] + + return ( + f".{subsection_split_names[1]}" + if len(subsection_split_names) > 2 + else section_name + ) + + +def write_subsection( + section_name: str, + subsection_name: str, + address: str, + size: int, + demangled_name: str, + module_name: str, + file_name: str, + mangled_name: str, + write_file_object: TextIO, +) -> None: + write_file_object.write( + f"{section_name}\t" + f"{subsection_name}\t" + f"{address}\t" + f"{size}\t" + f"{demangled_name}\t" + f"{module_name}\t" + f"{file_name}\t" + f"{mangled_name}\n" + ) + + +def save_subsection( + section_name: str, subsection: Objectfile, write_file_object: TextIO +) -> None: + subsection_name = get_subsection_name(section_name, subsection) + module_name = subsection.path[0] + file_name = subsection.path[1] + + if not file_name: + file_name, module_name = module_name, "" + + if not subsection.children: + address = f"{subsection.offset:x}" + size = subsection.size + mangled_name = ( + "" + if subsection.section == section_name + else subsection.section.split(".")[-1] + ) + demangled_name = demangle(mangled_name) if mangled_name else mangled_name + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + return + + for subsection_child in subsection.children: + address = f"{subsection_child[0]:x}" + size = subsection_child[1] + mangled_name = subsection_child[2] + demangled_name = demangle(mangled_name) + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + + +def save_section(section: Objectfile, write_file_object: TextIO) -> None: + section_name = section.section + for subsection in section.children: + save_subsection( + section_name=section_name, + subsection=subsection, + write_file_object=write_file_object, + ) + + +def save_parsed_data(parsed_data: list[Objectfile], output_file_name: str) -> None: + with open(output_file_name, "w") as write_file_object: + for section in parsed_data: + if section.children: + save_section(section=section, write_file_object=write_file_object) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + raise Exception(f"Usage: {sys.argv[0]} ") + + input_file = sys.argv[1] + output_file = sys.argv[2] + + parsed_sections = parse_sections(input_file) + + if parsed_sections is None: + raise Exception(f"Memory configuration is not {input_file}") + + save_parsed_data(parsed_sections, output_file) From ab86f58643be1a9d478c220c95447958cf72c194 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 2 Jun 2023 16:42:58 +0300 Subject: [PATCH 044/370] Fuzzers App: gui start --- .../external/pacs_fuzzer/application.fam | 19 ++++ applications/external/pacs_fuzzer/fuzzer.c | 78 +++++++++++++++ applications/external/pacs_fuzzer/fuzzer_i.h | 21 ++++ .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 8 ++ .../pacs_fuzzer/helpers/fuzzer_types.h | 8 ++ .../external/pacs_fuzzer/rfid_10px.png | Bin 0 -> 2389 bytes .../pacs_fuzzer/scenes/fuzzer_scene.c | 30 ++++++ .../pacs_fuzzer/scenes/fuzzer_scene.h | 29 ++++++ .../pacs_fuzzer/scenes/fuzzer_scene_config.h | 1 + .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 41 ++++++++ .../external/pacs_fuzzer/views/main_menu.c | 94 ++++++++++++++++++ .../external/pacs_fuzzer/views/main_menu.h | 26 +++++ 12 files changed, 355 insertions(+) create mode 100644 applications/external/pacs_fuzzer/application.fam create mode 100644 applications/external/pacs_fuzzer/fuzzer.c create mode 100644 applications/external/pacs_fuzzer/fuzzer_i.h create mode 100644 applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h create mode 100644 applications/external/pacs_fuzzer/helpers/fuzzer_types.h create mode 100644 applications/external/pacs_fuzzer/rfid_10px.png create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene.c create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene.h create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c create mode 100644 applications/external/pacs_fuzzer/views/main_menu.c create mode 100644 applications/external/pacs_fuzzer/views/main_menu.h diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam new file mode 100644 index 000000000..5e0aafd99 --- /dev/null +++ b/applications/external/pacs_fuzzer/application.fam @@ -0,0 +1,19 @@ +App( + appid="pacs_fuzzer", + name="Fuzzer Gui", + apptype=FlipperAppType.EXTERNAL, + entry_point="fuzzer_start", + requires=[ + "gui", + "storage", + "dialogs", + "input", + "notification", + ], + stack_size=2 * 1024, + order=15, + fap_icon="rfid_10px.png", + fap_category="Debug", + # fap_icon_assets="images", + # fap_icon_assets_symbol="fuzzer", +) diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c new file mode 100644 index 000000000..ac0400ae5 --- /dev/null +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -0,0 +1,78 @@ +#include "fuzzer_i.h" +#include "helpers/fuzzer_types.h" + +static bool fuzzer_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + PacsFuzzerApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool fuzzer_app_back_event_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void fuzzer_app_tick_event_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +PacsFuzzerApp* fuzzer_app_alloc() { + PacsFuzzerApp* app = malloc(sizeof(PacsFuzzerApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + + // Main view + app->main_view = fuzzer_view_main_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FuzzerViewIDMain, fuzzer_view_main_get_view(app->main_view)); + + app->scene_manager = scene_manager_alloc(&fuzzer_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, fuzzer_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, fuzzer_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, fuzzer_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene(app->scene_manager, FuzzerSceneMain); + + return app; +} + +void fuzzer_app_free(PacsFuzzerApp* app) { + furi_assert(app); + + // Remote view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDMain); + fuzzer_view_main_free(app->main_view); + + scene_manager_free(app->scene_manager); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t fuzzer_start(void* p) { + UNUSED(p); + PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); + + view_dispatcher_run(fuzzer_app->view_dispatcher); + + fuzzer_app_free(fuzzer_app); + return 0; +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h new file mode 100644 index 000000000..f1ed6e738 --- /dev/null +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include + +#include "scenes/fuzzer_scene.h" +#include "views/main_menu.h" + +#include "helpers/fuzzer_types.h" + +#include + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + FuzzerViewMain* main_view; + +} PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h new file mode 100644 index 000000000..2ef8fce56 --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -0,0 +1,8 @@ +#pragma once + +typedef enum { + + // FuzzerCustomEvent + FuzzerCustomEventViewMainOk = 100, + FuzzerCustomEventViewMainBack, +} FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h new file mode 100644 index 000000000..7b6f5a680 --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +typedef enum { + FuzzerViewIDMain, +} FuzzerViewID; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/rfid_10px.png b/applications/external/pacs_fuzzer/rfid_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..8097f477552333f695733baf98f72e52ae840206 GIT binary patch literal 2389 zcmcIleQXnD7{6^!#>U44<0gnZixCj6_tCqycdm@C?Y2%j%BZlR1g>}Qb?4f<$dYAbBp?=L>s9H44zi+G=z@ z$+GSR2r_^Bu5APL?}u;SJTN7D;oJYQXJbzy83Gd~}8wyVLJ?VRDO5w9Zzw8@g?;6Z|IeLEIrsbQiG#zUHZ2RGh zXD*aUd-8i4_PpADs0}HdxBi3jq2Z#Q^P&Dr^r?|WO%j;%cM#(P)0|Mc8; z{t8g`*A^{V)i?clE}f@2H&CjT}F< z?2jues!qd7PS)z04FoBfX?^mLy)Tp_$Rt&cG?`7IrJSH9?7UT9dorQHXauRON@~2& z3QRN#VzT0~4fhY&P+9cYRxu$Wr1?OLT-T+86?9@-1c8#I)9z+Pr-LUJp%g)pI7#A^ z8>2{$U^#~a&AepaJ-|V!`|Vrt9lHFc42XX!YK-a5tz}b zn0yjbjJa6^KQIJc)=XJdPz#Zds%@sn2DzpWkAYyf`V1Rfhy zjlu{PBk2f5aSnoGTnh;YM-b`I5OjjboBR#IOoSjf8osX&Rz+FroJeRW#03?@@6WtU}<<5`k-GmIOPTuus$(zJDPkQf=2B!Xdi7eP5vyx@MnDzsUZu=b~oE2;v- z$W@bzGC*KNw}3fBr-TVK?Z%=6L1S(pkqisLNim1EOqXHr@bS^87Ap}ViVmI>S)RcJ zh9WV*(*TPy(I`d}G$F90;3Qy6q1W>I)VQjLR1sDe;)?<&sd|Ek{*e=W4B(m)v)l~P z;VJ5514`GK>5mm)eP$Jx(Uj>pUa-9Gu?d#Q0Om>GmdB{x#CWFnceDTqI*$11FhiBh z4qgY|7_9WanhU=fd4q2spSnP~C5On1n8Y&u^S%9C@(-&evej?~Ro2+Su!z(GxDJJ~1-%>yfuE?__2!EG(Q-w?lmFd+!g0U&StM zdu5hu`a91J8fzAOad#fF`0WbM%BH;qRp;*CJ^0}3t1p(6l^kTQe)IdHQfq1l0}cMe I)$5-48`f?Vpa1{> literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene.c new file mode 100644 index 000000000..0fe0f558d --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene.c @@ -0,0 +1,30 @@ +#include "fuzzer_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const fuzzer_scene_on_enter_handlers[])(void*) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const fuzzer_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const fuzzer_scene_on_exit_handlers[])(void* context) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers fuzzer_scene_handlers = { + .on_enter_handlers = fuzzer_scene_on_enter_handlers, + .on_event_handlers = fuzzer_scene_on_event_handlers, + .on_exit_handlers = fuzzer_scene_on_exit_handlers, + .scene_num = FuzzerSceneNum, +}; diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene.h b/applications/external/pacs_fuzzer/scenes/fuzzer_scene.h new file mode 100644 index 000000000..280654510 --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FuzzerScene##id, +typedef enum { +#include "fuzzer_scene_config.h" + FuzzerSceneNum, +} FuzzerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers fuzzer_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h new file mode 100644 index 000000000..7923cd83e --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h @@ -0,0 +1 @@ +ADD_SCENE(fuzzer, main, Main) \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c new file mode 100644 index 000000000..994867ed3 --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -0,0 +1,41 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_main_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_main_set_callback(app->main_view, fuzzer_scene_main_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); +} + +bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewMainBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + + return consumed; +} + +void fuzzer_scene_main_on_exit(void* context) { + // furi_assert(context); + // PacsFuzzerApp* app = context; + UNUSED(context); +} diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c new file mode 100644 index 000000000..7db7d26f3 --- /dev/null +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -0,0 +1,94 @@ +#include "main_menu.h" +#include "../fuzzer_i.h" + +#include +#include + +struct FuzzerViewMain { + View* view; + FuzzerViewMainCallback callback; + void* context; +}; + +typedef struct { + uint8_t proto_index; + uint8_t menu_index; +} FuzzerViewMainModel; + +void fuzzer_view_main_set_callback( + FuzzerViewMain* fuzzer_view_main, + FuzzerViewMainCallback callback, + void* context) { + furi_assert(fuzzer_view_main); + + fuzzer_view_main->callback = callback; + fuzzer_view_main->context = context; +} + +void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { + UNUSED(canvas); + UNUSED(model); +} + +bool fuzzer_view_main_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewMain* fuzzer_view_main = context; + + if(event->key == InputKeyBack && + (event->type == InputTypeLong || event->type == InputTypeShort)) { + fuzzer_view_main->callback(FuzzerCustomEventViewMainBack, fuzzer_view_main->context); + return true; + } + + return true; +} + +void fuzzer_view_main_enter(void* context) { + furi_assert(context); +} + +void fuzzer_view_main_exit(void* context) { + furi_assert(context); +} + +FuzzerViewMain* fuzzer_view_main_alloc() { + FuzzerViewMain* fuzzer_view_main = malloc(sizeof(FuzzerViewMain)); + + // View allocation and configuration + fuzzer_view_main->view = view_alloc(); + view_allocate_model(fuzzer_view_main->view, ViewModelTypeLocking, sizeof(FuzzerViewMainModel)); + view_set_context(fuzzer_view_main->view, fuzzer_view_main); + view_set_draw_callback(fuzzer_view_main->view, (ViewDrawCallback)fuzzer_view_main_draw); + view_set_input_callback(fuzzer_view_main->view, fuzzer_view_main_input); + view_set_enter_callback(fuzzer_view_main->view, fuzzer_view_main_enter); + view_set_exit_callback(fuzzer_view_main->view, fuzzer_view_main_exit); + + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + model->proto_index = 0; + model->menu_index = 0; + }, + true); + return fuzzer_view_main; +} + +void fuzzer_view_main_free(FuzzerViewMain* fuzzer_view_main) { + furi_assert(fuzzer_view_main); + + // with_view_model( + // fuzzer_view_main->view, + // FuzzerViewMainModel * model, + // { + + // }, + // true); + view_free(fuzzer_view_main->view); + free(fuzzer_view_main); +} + +View* fuzzer_view_main_get_view(FuzzerViewMain* fuzzer_view_main) { + furi_assert(fuzzer_view_main); + return fuzzer_view_main->view; +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.h b/applications/external/pacs_fuzzer/views/main_menu.h new file mode 100644 index 000000000..17631807f --- /dev/null +++ b/applications/external/pacs_fuzzer/views/main_menu.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "../helpers/fuzzer_custom_event.h" + +typedef enum { + FuzzerViewMainStateIdle, + FuzzerViewMainStateLoading, + FuzzerViewMainStateSending, + FuzzerViewMainStateOFF, +} FuzzerViewMainState; + +typedef struct FuzzerViewMain FuzzerViewMain; + +typedef void (*FuzzerViewMainCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_main_set_callback( + FuzzerViewMain* fuzzer_view_main, + FuzzerViewMainCallback callback, + void* context); + +FuzzerViewMain* fuzzer_view_main_alloc(); + +void fuzzer_view_main_free(FuzzerViewMain* fuzzer_view_main); + +View* fuzzer_view_main_get_view(FuzzerViewMain* fuzzer_view_main); \ No newline at end of file From 321f2d8d504e8362543f36e5c0337bd000b89eac Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 2 Jun 2023 17:48:21 +0300 Subject: [PATCH 045/370] Fuzzers app: main menu v0 --- .../pacs_fuzzer/helpers/fuzzer_types.h | 9 ++ .../external/pacs_fuzzer/helpers/protocol.c | 92 +++++++++++++++++++ .../external/pacs_fuzzer/helpers/protocol.h | 28 ++++++ .../external/pacs_fuzzer/views/main_menu.c | 83 ++++++++++++++++- 4 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 applications/external/pacs_fuzzer/helpers/protocol.c create mode 100644 applications/external/pacs_fuzzer/helpers/protocol.h diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index 7b6f5a680..cfc9274d1 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -3,6 +3,15 @@ #include #include +// TODO replace it +typedef enum { + FuzzerMainMenuIndexDefaultValues = 0, + FuzzerMainMenuIndexLoadFile, + FuzzerMainMenuIndexLoadFileCustomUids, + + FuzzerMainMenuIndexMax, +} FuzzerMainMenuIndex; + typedef enum { FuzzerViewIDMain, } FuzzerViewID; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.c b/applications/external/pacs_fuzzer/helpers/protocol.c new file mode 100644 index 000000000..072749179 --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/protocol.c @@ -0,0 +1,92 @@ +#include "protocol.h" + +#define DS1990_DATA_SIZE (8) +#define Metakom_DATA_SIZE (4) +#define Cyfral_DATA_SIZE (2) + +const uint8_t uid_list_ds1990[][DS1990_DATA_SIZE] = { + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал + {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах + {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% + {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) + {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF + {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 + {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni +}; + +const uint8_t uid_list_metakom[][Metakom_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // ?? + {0x34, 0x00, 0x29, 0x3d}, // ?? + {0x04, 0xdf, 0x00, 0x00}, // ?? + {0xCA, 0xCA, 0xCA, 0xCA}, // ?? +}; + +const uint8_t uid_list_cyfral[][Cyfral_DATA_SIZE] = { + {0x00, 0x00}, // Null bytes + {0xFF, 0xFF}, // Only FF + {0x11, 0x11}, // Only 11 + {0x22, 0x22}, // Only 22 + {0x33, 0x33}, // Only 33 + {0x44, 0x44}, // Only 44 + {0x55, 0x55}, // Only 55 + {0x66, 0x66}, // Only 66 + {0x77, 0x77}, // Only 77 + {0x88, 0x88}, // Only 88 + {0x99, 0x99}, // Only 99 + {0x12, 0x34}, // Incremental UID + {0x56, 0x34}, // Decremental UID + {0xCA, 0xCA}, // ?? + {0x8E, 0xC9}, // Elevator code + {0x6A, 0x50}, // VERY fresh code from smartkey +}; + +const FuzzerProtocol fuzzer_proto_items[] = { + [DS1990] = + { + .name = "DS1990", + .data_size = DS1990_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_ds1990, + .len = sizeof(uid_list_ds1990) / DS1990_DATA_SIZE}, + }, + [Metakom] = + { + .name = "Metakom", + .data_size = Metakom_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_metakom, + .len = sizeof(uid_list_metakom) / Metakom_DATA_SIZE}, + }, + [Cyfral] = + { + .name = "Cyfral", + .data_size = Cyfral_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_cyfral, + .len = sizeof(uid_list_cyfral) / Cyfral_DATA_SIZE}, + }, +}; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.h b/applications/external/pacs_fuzzer/helpers/protocol.h new file mode 100644 index 000000000..c43e7f128 --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/protocol.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +typedef enum { + DS1990, + Metakom, + Cyfral, + // Reserved + FuzzerProtoMax, +} FuzzerProtos; + +struct ProtoDict { + const uint8_t* val; + const uint8_t len; +}; + +typedef struct ProtoDict ProtoDict; + +struct FuzzerProtocol { + const char* name; + const uint8_t data_size; + const ProtoDict dict; +}; + +typedef struct FuzzerProtocol FuzzerProtocol; + +extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 7db7d26f3..2d55cc143 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -4,12 +4,21 @@ #include #include +#include "../helpers/protocol.h" + +const char* main_menu_items[FuzzerMainMenuIndexMax] = { + [FuzzerMainMenuIndexDefaultValues] = "Default Values", + [FuzzerMainMenuIndexLoadFile] = "Load File", + [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", +}; + struct FuzzerViewMain { View* view; FuzzerViewMainCallback callback; void* context; }; +// TODO Furi string for procol name typedef struct { uint8_t proto_index; uint8_t menu_index; @@ -26,8 +35,30 @@ void fuzzer_view_main_set_callback( } void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { - UNUSED(canvas); - UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(model->menu_index > 0) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 24, AlignCenter, AlignTop, main_menu_items[model->menu_index - 1]); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 64, 36, AlignCenter, AlignTop, main_menu_items[model->menu_index]); + + if(model->menu_index < FuzzerMainMenuIndexMax) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 48, AlignCenter, AlignTop, main_menu_items[model->menu_index + 1]); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); + canvas_draw_str_aligned( + canvas, 64, 4, AlignCenter, AlignTop, fuzzer_proto_items[model->proto_index].name); + canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); } bool fuzzer_view_main_input(InputEvent* event, void* context) { @@ -38,6 +69,54 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { (event->type == InputTypeLong || event->type == InputTypeShort)) { fuzzer_view_main->callback(FuzzerCustomEventViewMainBack, fuzzer_view_main->context); return true; + } else if(event->key == InputKeyDown && event->type == InputTypeShort) { + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + if(model->menu_index < (FuzzerMainMenuIndexMax - 1)) { + model->menu_index++; + } + }, + true); + return true; + } else if(event->key == InputKeyUp && event->type == InputTypeShort) { + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + if(model->menu_index != 0) { + model->menu_index--; + } + }, + true); + return true; + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + if(model->proto_index != 0) { + model->proto_index--; + } else { + model->proto_index = (FuzzerProtoMax - 1); + } + }, + true); + return true; + } else if(event->key == InputKeyRight && event->type == InputTypeShort) { + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + if(model->proto_index == (FuzzerProtoMax - 1)) { + model->proto_index = 0; + } else { + model->proto_index++; + } + }, + true); + return true; } return true; From e31a0c4d6d7d144b67e64e621e5bd5c70e3210e9 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 2 Jun 2023 20:33:28 +0300 Subject: [PATCH 046/370] Fuzzers App: attack gui --- applications/external/pacs_fuzzer/fuzzer.c | 12 + applications/external/pacs_fuzzer/fuzzer_i.h | 6 +- .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 7 +- .../pacs_fuzzer/helpers/fuzzer_types.h | 13 +- .../external/pacs_fuzzer/helpers/gui_const.c | 7 + .../external/pacs_fuzzer/helpers/gui_const.h | 12 + .../external/pacs_fuzzer/helpers/protocol.h | 4 + .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 53 +++++ .../pacs_fuzzer/scenes/fuzzer_scene_config.h | 3 +- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 6 + .../external/pacs_fuzzer/views/attack.c | 212 ++++++++++++++++++ .../external/pacs_fuzzer/views/attack.h | 27 +++ .../external/pacs_fuzzer/views/main_menu.c | 98 ++++---- .../external/pacs_fuzzer/views/main_menu.h | 15 +- 14 files changed, 416 insertions(+), 59 deletions(-) create mode 100644 applications/external/pacs_fuzzer/helpers/gui_const.c create mode 100644 applications/external/pacs_fuzzer/helpers/gui_const.h create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c create mode 100644 applications/external/pacs_fuzzer/views/attack.c create mode 100644 applications/external/pacs_fuzzer/views/attack.h diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index ac0400ae5..42c7d1e47 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -22,6 +22,9 @@ static void fuzzer_app_tick_event_callback(void* context) { PacsFuzzerApp* fuzzer_app_alloc() { PacsFuzzerApp* app = malloc(sizeof(PacsFuzzerApp)); + app->fuzzer_state.menu_index = 0; + app->fuzzer_state.proto_index = 0; + // GUI app->gui = furi_record_open(RECORD_GUI); @@ -33,6 +36,11 @@ PacsFuzzerApp* fuzzer_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, FuzzerViewIDMain, fuzzer_view_main_get_view(app->main_view)); + // Attack view + app->attack_view = fuzzer_view_attack_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FuzzerViewIDAttack, fuzzer_view_attack_get_view(app->attack_view)); + app->scene_manager = scene_manager_alloc(&fuzzer_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); @@ -58,6 +66,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) { view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDMain); fuzzer_view_main_free(app->main_view); + // Attack view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDAttack); + fuzzer_view_attack_free(app->attack_view); + scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index f1ed6e738..8f84a558d 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -7,6 +7,7 @@ #include "scenes/fuzzer_scene.h" #include "views/main_menu.h" +#include "views/attack.h" #include "helpers/fuzzer_types.h" @@ -16,6 +17,9 @@ typedef struct { Gui* gui; ViewDispatcher* view_dispatcher; SceneManager* scene_manager; - FuzzerViewMain* main_view; + FuzzerViewMain* main_view; + FuzzerViewAttack* attack_view; + + FuzzerState fuzzer_state; } PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h index 2ef8fce56..189d72ef4 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -3,6 +3,9 @@ typedef enum { // FuzzerCustomEvent - FuzzerCustomEventViewMainOk = 100, - FuzzerCustomEventViewMainBack, + FuzzerCustomEventViewMainBack = 100, + FuzzerCustomEventViewMainOk, + + FuzzerCustomEventViewAttackBack, + FuzzerCustomEventViewAttackOk, } FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index cfc9274d1..55c64954c 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -3,15 +3,12 @@ #include #include -// TODO replace it -typedef enum { - FuzzerMainMenuIndexDefaultValues = 0, - FuzzerMainMenuIndexLoadFile, - FuzzerMainMenuIndexLoadFileCustomUids, - - FuzzerMainMenuIndexMax, -} FuzzerMainMenuIndex; +typedef struct { + uint8_t menu_index; + uint8_t proto_index; +} FuzzerState; typedef enum { FuzzerViewIDMain, + FuzzerViewIDAttack, } FuzzerViewID; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/gui_const.c b/applications/external/pacs_fuzzer/helpers/gui_const.c new file mode 100644 index 000000000..79e31ad1b --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/gui_const.c @@ -0,0 +1,7 @@ +#include "gui_const.h" + +const char* fuzzer_attack_names[FuzzerMainMenuIndexMax] = { + [FuzzerMainMenuIndexDefaultValues] = "Default Values", + [FuzzerMainMenuIndexLoadFile] = "Load File", + [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", +}; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/gui_const.h b/applications/external/pacs_fuzzer/helpers/gui_const.h new file mode 100644 index 000000000..837322c2a --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/gui_const.h @@ -0,0 +1,12 @@ +#pragma once + +// TODO replace it +typedef enum { + FuzzerMainMenuIndexDefaultValues = 0, + FuzzerMainMenuIndexLoadFile, + FuzzerMainMenuIndexLoadFileCustomUids, + + FuzzerMainMenuIndexMax, +} FuzzerMainMenuIndex; + +extern const char* fuzzer_attack_names[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.h b/applications/external/pacs_fuzzer/helpers/protocol.h index c43e7f128..aedeedd8d 100644 --- a/applications/external/pacs_fuzzer/helpers/protocol.h +++ b/applications/external/pacs_fuzzer/helpers/protocol.h @@ -2,6 +2,10 @@ #include +#define FUZZ_TIME_DELAY_MIN (4) +#define FUZZ_TIME_DELAY_DEFAULT (8) +#define FUZZ_TIME_DELAY_MAX (80) + typedef enum { DS1990, Metakom, diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c new file mode 100644 index 000000000..71d38650c --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -0,0 +1,53 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +#include "../helpers/protocol.h" +#include "../helpers/gui_const.h" + +void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_attack_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_attack_set_callback(app->attack_view, fuzzer_scene_attack_callback, app); + + FuzzerProtocol proto = fuzzer_proto_items[app->fuzzer_state.proto_index]; + + fuzzer_view_attack_reset_data( + app->attack_view, + fuzzer_attack_names[app->fuzzer_state.menu_index], + proto.name, + proto.data_size); + fuzzer_view_attack_set_uid(app->attack_view, &proto.dict.val[0], false); + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDAttack); +} + +bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewAttackBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + + return consumed; +} + +void fuzzer_scene_attack_on_exit(void* context) { + // furi_assert(context); + // PacsFuzzerApp* app = context; + UNUSED(context); +} diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h index 7923cd83e..bccdbd9a5 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h @@ -1 +1,2 @@ -ADD_SCENE(fuzzer, main, Main) \ No newline at end of file +ADD_SCENE(fuzzer, main, Main) +ADD_SCENE(fuzzer, attack, Attack) \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 994867ed3..812e063ab 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -13,6 +13,8 @@ void fuzzer_scene_main_on_enter(void* context) { fuzzer_view_main_set_callback(app->main_view, fuzzer_scene_main_callback, app); + fuzzer_view_main_update_data(app->main_view, app->fuzzer_state); + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); } @@ -28,6 +30,10 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(app->view_dispatcher); } consumed = true; + } else if(event.event == FuzzerCustomEventViewMainOk) { + fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + consumed = true; } } diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c new file mode 100644 index 000000000..a83740f3d --- /dev/null +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -0,0 +1,212 @@ +#include "attack.h" +#include "../fuzzer_i.h" + +#include +#include + +#include "../helpers/protocol.h" + +#define ATTACK_SCENE_MAX_UID_LENGTH 25 + +struct FuzzerViewAttack { + View* view; + FuzzerViewAttackCallback callback; + void* context; +}; + +typedef struct { + uint8_t time_delay; + const char* attack_name; + const char* protocol_name; + bool attack_enabled; + char* uid; + uint8_t uid_size; +} FuzzerViewAttackModel; + +void fuzzer_view_attack_reset_data( + FuzzerViewAttack* view, + const char* attack_name, + const char* protocol_name, + uint8_t uid_size) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { + model->attack_name = attack_name; + model->protocol_name = protocol_name; + model->attack_enabled = false; + model->uid_size = uid_size; + }, + true); +} + +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool attack) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { + snprintf( + model->uid, + model->uid_size * 3, + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", + uid[0], + uid[1], + uid[2], + uid[3], + uid[4], + uid[5], + uid[6], + uid[7]); + model->attack_enabled = attack; + }, + true); +} + +void fuzzer_view_attack_set_callback( + FuzzerViewAttack* view_attack, + FuzzerViewAttackCallback callback, + void* context) { + furi_assert(view_attack); + + view_attack->callback = callback; + view_attack->context = context; +} + +void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { + char time_delay[16]; + snprintf(time_delay, sizeof(time_delay), "Time delay: %d", model->time_delay); + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, time_delay); + canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); + + canvas_set_font(canvas, FontPrimary); + if(128 < canvas_string_width(canvas, model->uid)) { + canvas_set_font(canvas, FontSecondary); + } + canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, model->uid); + + if(model->attack_enabled) { + elements_button_center(canvas, "Stop"); + } else { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "TD -"); + elements_button_right(canvas, "+ TD"); + } +} + +bool fuzzer_view_attack_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewAttack* view_attack = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view_attack->callback(FuzzerCustomEventViewAttackOk, view_attack->context); + return true; + } else if(event->key == InputKeyLeft) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + if(!model->attack_enabled) { + if(event->type == InputTypeShort) { + if(model->time_delay > FUZZ_TIME_DELAY_MIN) { + model->time_delay--; + } + } else if(event->type == InputTypeLong) { + if((model->time_delay - 10) >= FUZZ_TIME_DELAY_MIN) { + model->time_delay -= 10; + } else { + model->time_delay = FUZZ_TIME_DELAY_MIN; + } + } + } + }, + true); + return true; + } else if(event->key == InputKeyRight) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + if(!model->attack_enabled) { + if(event->type == InputTypeShort) { + if(model->time_delay < FUZZ_TIME_DELAY_MAX) { + model->time_delay++; + } + } else if(event->type == InputTypeLong) { + model->time_delay += 10; + if(model->time_delay > FUZZ_TIME_DELAY_MAX) { + model->time_delay = FUZZ_TIME_DELAY_MAX; + } + } + } + }, + true); + return true; + } + + return true; +} + +void fuzzer_view_attack_enter(void* context) { + furi_assert(context); +} + +void fuzzer_view_attack_exit(void* context) { + furi_assert(context); +} + +FuzzerViewAttack* fuzzer_view_attack_alloc() { + FuzzerViewAttack* view_attack = malloc(sizeof(FuzzerViewAttack)); + + // View allocation and configuration + view_attack->view = view_alloc(); + view_allocate_model(view_attack->view, ViewModelTypeLocking, sizeof(FuzzerViewAttackModel)); + view_set_context(view_attack->view, view_attack); + view_set_draw_callback(view_attack->view, (ViewDrawCallback)fuzzer_view_attack_draw); + view_set_input_callback(view_attack->view, fuzzer_view_attack_input); + view_set_enter_callback(view_attack->view, fuzzer_view_attack_enter); + view_set_exit_callback(view_attack->view, fuzzer_view_attack_exit); + + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + model->time_delay = FUZZ_TIME_DELAY_MIN; + model->uid = malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); + model->attack_enabled = false; + + strcpy(model->uid, "Not_set"); + model->attack_name = "Not_set"; + model->protocol_name = "Not_set"; + }, + true); + return view_attack; +} + +void fuzzer_view_attack_free(FuzzerViewAttack* view_attack) { + furi_assert(view_attack); + + with_view_model( + view_attack->view, FuzzerViewAttackModel * model, { free(model->uid); }, true); + view_free(view_attack->view); + free(view_attack); +} + +View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack) { + furi_assert(view_attack); + return view_attack->view; +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h new file mode 100644 index 000000000..082b7ba36 --- /dev/null +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "../helpers/fuzzer_custom_event.h" + +typedef struct FuzzerViewAttack FuzzerViewAttack; + +typedef void (*FuzzerViewAttackCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_attack_set_callback( + FuzzerViewAttack* view_attack, + FuzzerViewAttackCallback callback, + void* context); + +FuzzerViewAttack* fuzzer_view_attack_alloc(); + +void fuzzer_view_attack_free(FuzzerViewAttack* view_attack); + +View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack); + +void fuzzer_view_attack_reset_data( + FuzzerViewAttack* view, + const char* attack_name, + const char* protocol_name, + uint8_t uid_size); + +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool attack); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 2d55cc143..c037fdf44 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -2,15 +2,10 @@ #include "../fuzzer_i.h" #include -#include +// #include #include "../helpers/protocol.h" - -const char* main_menu_items[FuzzerMainMenuIndexMax] = { - [FuzzerMainMenuIndexDefaultValues] = "Default Values", - [FuzzerMainMenuIndexLoadFile] = "Load File", - [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", -}; +#include "../helpers/gui_const.h" struct FuzzerViewMain { View* view; @@ -24,14 +19,38 @@ typedef struct { uint8_t menu_index; } FuzzerViewMainModel; +void fuzzer_view_main_update_data(FuzzerViewMain* view, FuzzerState state) { + furi_assert(view); + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + model->proto_index = state.proto_index; + model->menu_index = state.menu_index; + }, + true); +} + +void fuzzer_view_main_get_state(FuzzerViewMain* view, FuzzerState* state) { + furi_assert(view); + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + state->proto_index = model->proto_index; + state->menu_index = model->menu_index; + }, + true); +} + void fuzzer_view_main_set_callback( - FuzzerViewMain* fuzzer_view_main, + FuzzerViewMain* view, FuzzerViewMainCallback callback, void* context) { - furi_assert(fuzzer_view_main); + furi_assert(view); - fuzzer_view_main->callback = callback; - fuzzer_view_main->context = context; + view->callback = callback; + view->context = context; } void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { @@ -41,17 +60,17 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { if(model->menu_index > 0) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 64, 24, AlignCenter, AlignTop, main_menu_items[model->menu_index - 1]); + canvas, 64, 24, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index - 1]); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned( - canvas, 64, 36, AlignCenter, AlignTop, main_menu_items[model->menu_index]); + canvas, 64, 36, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index]); if(model->menu_index < FuzzerMainMenuIndexMax) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 64, 48, AlignCenter, AlignTop, main_menu_items[model->menu_index + 1]); + canvas, 64, 48, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index + 1]); } canvas_set_font(canvas, FontPrimary); @@ -63,15 +82,18 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { bool fuzzer_view_main_input(InputEvent* event, void* context) { furi_assert(context); - FuzzerViewMain* fuzzer_view_main = context; + FuzzerViewMain* view = context; if(event->key == InputKeyBack && (event->type == InputTypeLong || event->type == InputTypeShort)) { - fuzzer_view_main->callback(FuzzerCustomEventViewMainBack, fuzzer_view_main->context); + view->callback(FuzzerCustomEventViewMainBack, view->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view->callback(FuzzerCustomEventViewMainOk, view->context); return true; } else if(event->key == InputKeyDown && event->type == InputTypeShort) { with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { if(model->menu_index < (FuzzerMainMenuIndexMax - 1)) { @@ -82,7 +104,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { return true; } else if(event->key == InputKeyUp && event->type == InputTypeShort) { with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { if(model->menu_index != 0) { @@ -93,7 +115,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { return true; } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { if(model->proto_index != 0) { @@ -106,7 +128,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { return true; } else if(event->key == InputKeyRight && event->type == InputTypeShort) { with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { if(model->proto_index == (FuzzerProtoMax - 1)) { @@ -131,43 +153,43 @@ void fuzzer_view_main_exit(void* context) { } FuzzerViewMain* fuzzer_view_main_alloc() { - FuzzerViewMain* fuzzer_view_main = malloc(sizeof(FuzzerViewMain)); + FuzzerViewMain* view = malloc(sizeof(FuzzerViewMain)); // View allocation and configuration - fuzzer_view_main->view = view_alloc(); - view_allocate_model(fuzzer_view_main->view, ViewModelTypeLocking, sizeof(FuzzerViewMainModel)); - view_set_context(fuzzer_view_main->view, fuzzer_view_main); - view_set_draw_callback(fuzzer_view_main->view, (ViewDrawCallback)fuzzer_view_main_draw); - view_set_input_callback(fuzzer_view_main->view, fuzzer_view_main_input); - view_set_enter_callback(fuzzer_view_main->view, fuzzer_view_main_enter); - view_set_exit_callback(fuzzer_view_main->view, fuzzer_view_main_exit); + view->view = view_alloc(); + view_allocate_model(view->view, ViewModelTypeLocking, sizeof(FuzzerViewMainModel)); + view_set_context(view->view, view); + view_set_draw_callback(view->view, (ViewDrawCallback)fuzzer_view_main_draw); + view_set_input_callback(view->view, fuzzer_view_main_input); + view_set_enter_callback(view->view, fuzzer_view_main_enter); + view_set_exit_callback(view->view, fuzzer_view_main_exit); with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { model->proto_index = 0; model->menu_index = 0; }, true); - return fuzzer_view_main; + return view; } -void fuzzer_view_main_free(FuzzerViewMain* fuzzer_view_main) { - furi_assert(fuzzer_view_main); +void fuzzer_view_main_free(FuzzerViewMain* view) { + furi_assert(view); // with_view_model( - // fuzzer_view_main->view, + // view->view, // FuzzerViewMainModel * model, // { // }, // true); - view_free(fuzzer_view_main->view); - free(fuzzer_view_main); + view_free(view->view); + free(view); } -View* fuzzer_view_main_get_view(FuzzerViewMain* fuzzer_view_main) { - furi_assert(fuzzer_view_main); - return fuzzer_view_main->view; +View* fuzzer_view_main_get_view(FuzzerViewMain* view) { + furi_assert(view); + return view->view; } \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.h b/applications/external/pacs_fuzzer/views/main_menu.h index 17631807f..262d54405 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.h +++ b/applications/external/pacs_fuzzer/views/main_menu.h @@ -2,13 +2,7 @@ #include #include "../helpers/fuzzer_custom_event.h" - -typedef enum { - FuzzerViewMainStateIdle, - FuzzerViewMainStateLoading, - FuzzerViewMainStateSending, - FuzzerViewMainStateOFF, -} FuzzerViewMainState; +#include "../helpers/fuzzer_types.h" typedef struct FuzzerViewMain FuzzerViewMain; @@ -21,6 +15,9 @@ void fuzzer_view_main_set_callback( FuzzerViewMain* fuzzer_view_main_alloc(); -void fuzzer_view_main_free(FuzzerViewMain* fuzzer_view_main); +void fuzzer_view_main_free(FuzzerViewMain* view); -View* fuzzer_view_main_get_view(FuzzerViewMain* fuzzer_view_main); \ No newline at end of file +View* fuzzer_view_main_get_view(FuzzerViewMain* view); + +void fuzzer_view_main_update_data(FuzzerViewMain* view, FuzzerState state); +void fuzzer_view_main_get_state(FuzzerViewMain* view, FuzzerState* state); \ No newline at end of file From 70edcf3f6a1797cef169dfe524a3e744be309109 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 3 Jun 2023 00:32:32 +0300 Subject: [PATCH 047/370] Fuzzer App: worker --- applications/external/pacs_fuzzer/fuzzer.c | 4 + applications/external/pacs_fuzzer/fuzzer_i.h | 3 + .../pacs_fuzzer/helpers/fake_worker.c | 183 ++++++++++++++++++ .../pacs_fuzzer/helpers/fake_worker.h | 40 ++++ .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 2 + .../external/pacs_fuzzer/helpers/protocol.h | 2 + .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 73 ++++++- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 13 ++ .../external/pacs_fuzzer/views/attack.c | 21 +- .../external/pacs_fuzzer/views/attack.h | 6 +- 10 files changed, 336 insertions(+), 11 deletions(-) create mode 100644 applications/external/pacs_fuzzer/helpers/fake_worker.c create mode 100644 applications/external/pacs_fuzzer/helpers/fake_worker.h diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index 42c7d1e47..5a6a4c9b6 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -25,6 +25,8 @@ PacsFuzzerApp* fuzzer_app_alloc() { app->fuzzer_state.menu_index = 0; app->fuzzer_state.proto_index = 0; + app->worker = fuzzer_worker_alloc(); + // GUI app->gui = furi_record_open(RECORD_GUI); @@ -76,6 +78,8 @@ void fuzzer_app_free(PacsFuzzerApp* app) { // Close records furi_record_close(RECORD_GUI); + fuzzer_worker_free(app->worker); + free(app); } diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 8f84a558d..bd4833c5b 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -10,6 +10,7 @@ #include "views/attack.h" #include "helpers/fuzzer_types.h" +#include "helpers/fake_worker.h" #include @@ -22,4 +23,6 @@ typedef struct { FuzzerViewAttack* attack_view; FuzzerState fuzzer_state; + + FuzzerWorker* worker; } PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fake_worker.c b/applications/external/pacs_fuzzer/helpers/fake_worker.c new file mode 100644 index 000000000..15e3e035a --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/fake_worker.c @@ -0,0 +1,183 @@ +#include "fake_worker.h" + +#include +#include + +#include +#include + +#include + +struct FuzzerWorker { + iButtonWorker* proto_worker; + iButtonProtocolId protocol_id; + iButtonProtocols* protocols_items; + iButtonKey* key; + + const FuzzerProtocol* protocol; + FuzzerWorkerAttackType attack_type; + uint8_t timeer_delay; + + uint8_t payload[MAX_PAYLOAD_SIZE]; + Stream* uids_stream; + uint16_t index; + + bool treead_running; + FuriTimer* timer; + + FuzzerWorkerUidChagedCallback tick_callback; + void* tick_context; + + FuzzerWorkerEndCallback end_callback; + void* end_context; +}; + +static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { + furi_assert(worker); + furi_assert(worker->protocol); + bool res = false; + + const FuzzerProtocol* protocol = worker->protocol; + + if(next) { + worker->index++; + } + + switch(worker->attack_type) { + case FuzzerWorkerAttackTypeDefaultDict: + if(worker->index < protocol->dict.len) { + memcpy( + worker->payload, + &protocol->dict.val[worker->index * protocol->data_size], + protocol->data_size); + res = true; + } + break; + + default: + break; + } + + return res; +} + +static void fuzzer_worker_on_tick_callback(void* context) { + furi_assert(context); + + FuzzerWorker* worker = context; + + if(!fuzzer_worker_load_key(worker, true)) { + fuzzer_worker_stop(worker); + if(worker->end_callback) { + worker->end_callback(worker->end_context); + } + } else { + if(worker->tick_callback) { + worker->tick_callback(worker->tick_context); + } + } + + // TODO load ibutton key +} + +void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { + furi_assert(worker); + furi_assert(worker->protocol); + + memcpy(key, worker->payload, worker->protocol->data_size); +} + +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { + furi_assert(worker); + + worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; + worker->protocol = &fuzzer_proto_items[protocol_index]; + worker->index = 0; + + return fuzzer_worker_load_key(worker, false); +} + +FuzzerWorker* fuzzer_worker_alloc() { + FuzzerWorker* worker = malloc(sizeof(FuzzerWorker)); + + worker->protocols_items = ibutton_protocols_alloc(); + worker->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(worker->protocols_items)); + + worker->proto_worker = ibutton_worker_alloc(worker->protocols_items); + + worker->index = 0; + worker->treead_running = false; + + memset(worker->payload, 0x00, sizeof(worker->payload)); + + worker->timeer_delay = FUZZ_TIME_DELAY_DEFAULT; + + worker->timer = + furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypePeriodic, worker); + + return worker; +} + +void fuzzer_worker_free(FuzzerWorker* worker) { + furi_assert(worker); + + fuzzer_worker_stop(worker); + + furi_timer_free(worker->timer); + + ibutton_worker_free(worker->proto_worker); + + ibutton_key_free(worker->key); + ibutton_protocols_free(worker->protocols_items); + // TODO delete + UNUSED(fuzzer_worker_on_tick_callback); + free(worker); +} + +void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { + furi_assert(worker); + + worker->timeer_delay = timer_dellay; + + furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); + + // TODO start timer + // worker->treead_running = true; + // ibutton_worker_start_thread(worker->proto_worker); + + // TODO load ibutton key + + // ibutton_worker_emulate_start(worker->proto_worker, worker->key); +} + +void fuzzer_worker_stop(FuzzerWorker* worker) { + furi_assert(worker); + + furi_timer_stop(worker->timer); + + if(worker->treead_running) { + ibutton_worker_stop(worker->proto_worker); + ibutton_worker_stop_thread(worker->proto_worker); + worker->treead_running = false; + } + + // TODO stop timer, anything else +} + +void fuzzer_worker_set_uid_chaged_callback( + FuzzerWorker* worker, + FuzzerWorkerUidChagedCallback callback, + void* context) { + furi_assert(worker); + worker->tick_callback = callback; + worker->tick_context = context; +} + +void fuzzer_worker_set_end_callback( + FuzzerWorker* worker, + FuzzerWorkerEndCallback callback, + void* context) { + furi_assert(worker); + worker->end_callback = callback; + worker->end_context = context; +} diff --git a/applications/external/pacs_fuzzer/helpers/fake_worker.h b/applications/external/pacs_fuzzer/helpers/fake_worker.h new file mode 100644 index 000000000..00e3cb23f --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/fake_worker.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "protocol.h" + +typedef enum { + FuzzerWorkerAttackTypeDefaultDict = 0, + FuzzerWorkerAttackTypeLoadFile, + FuzzerWorkerAttackTypeLoadFileCustomUids, + + FuzzerWorkerAttackTypeMax, +} FuzzerWorkerAttackType; + +typedef void (*FuzzerWorkerUidChagedCallback)(void* context); +typedef void (*FuzzerWorkerEndCallback)(void* context); + +typedef struct FuzzerWorker FuzzerWorker; + +FuzzerWorker* fuzzer_worker_alloc(); + +void fuzzer_worker_free(FuzzerWorker* worker); + +void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); + +void fuzzer_worker_stop(FuzzerWorker* worker); + +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index); + +void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key); + +void fuzzer_worker_set_uid_chaged_callback( + FuzzerWorker* worker, + FuzzerWorkerUidChagedCallback callback, + void* context); + +void fuzzer_worker_set_end_callback( + FuzzerWorker* worker, + FuzzerWorkerEndCallback callback, + void* context); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h index 189d72ef4..890d961db 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -8,4 +8,6 @@ typedef enum { FuzzerCustomEventViewAttackBack, FuzzerCustomEventViewAttackOk, + FuzzerCustomEventViewAttackTick, + FuzzerCustomEventViewAttackEnd, } FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.h b/applications/external/pacs_fuzzer/helpers/protocol.h index aedeedd8d..c0dd5dd15 100644 --- a/applications/external/pacs_fuzzer/helpers/protocol.h +++ b/applications/external/pacs_fuzzer/helpers/protocol.h @@ -2,6 +2,8 @@ #include +#define MAX_PAYLOAD_SIZE 8 + #define FUZZ_TIME_DELAY_MIN (4) #define FUZZ_TIME_DELAY_DEFAULT (8) #define FUZZ_TIME_DELAY_MAX (80) diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index 71d38650c..f80cc9b29 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -1,9 +1,22 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -#include "../helpers/protocol.h" #include "../helpers/gui_const.h" +// TODO simlify callbacks and attack state + +void fuzzer_scene_attack_worker_tick_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick); +} + +void fuzzer_scene_attack_worker_end_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackEnd); +} + void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -23,7 +36,20 @@ void fuzzer_scene_attack_on_enter(void* context) { fuzzer_attack_names[app->fuzzer_state.menu_index], proto.name, proto.data_size); - fuzzer_view_attack_set_uid(app->attack_view, &proto.dict.val[0], false); + + fuzzer_worker_set_uid_chaged_callback( + app->worker, fuzzer_scene_attack_worker_tick_callback, app); + + fuzzer_worker_set_end_callback(app->worker, fuzzer_scene_attack_worker_end_callback, app); + + uint8_t temp_uid[MAX_PAYLOAD_SIZE]; + + fuzzer_worker_get_current_key(app->worker, temp_uid); + + fuzzer_view_attack_set_attack(app->attack_view, false); + fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); + + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDAttack); } @@ -35,11 +61,40 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == FuzzerCustomEventViewAttackBack) { - if(!scene_manager_previous_scene(app->scene_manager)) { - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); + if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack)) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + } else { + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); + fuzzer_view_attack_set_attack(app->attack_view, false); + fuzzer_worker_stop(app->worker); } consumed = true; + } else if(event.event == FuzzerCustomEventViewAttackOk) { + if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack)) { + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, true); + fuzzer_view_attack_set_attack(app->attack_view, true); + fuzzer_worker_start( + app->worker, fuzzer_view_attack_get_time_delay(app->attack_view)); + } else { + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); + fuzzer_view_attack_set_attack(app->attack_view, false); + fuzzer_worker_stop(app->worker); + } + consumed = true; + } else if(event.event == FuzzerCustomEventViewAttackTick) { + uint8_t temp_uid[MAX_PAYLOAD_SIZE]; + + fuzzer_worker_get_current_key(app->worker, temp_uid); + + fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); + consumed = true; + } else if(event.event == FuzzerCustomEventViewAttackEnd) { + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); + fuzzer_view_attack_set_attack(app->attack_view, false); + consumed = true; } } @@ -47,7 +102,9 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { } void fuzzer_scene_attack_on_exit(void* context) { - // furi_assert(context); - // PacsFuzzerApp* app = context; - UNUSED(context); + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_worker_set_uid_chaged_callback(app->worker, NULL, NULL); + fuzzer_worker_set_end_callback(app->worker, NULL, NULL); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 812e063ab..00ecdc543 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -1,6 +1,9 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" +#include "../helpers/protocol.h" +#include "../helpers/gui_const.h" + void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -32,6 +35,16 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == FuzzerCustomEventViewMainOk) { fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); + + switch(app->fuzzer_state.menu_index) { + case FuzzerMainMenuIndexDefaultValues: + fuzzer_worker_attack_dict(app->worker, app->fuzzer_state.proto_index); + break; + + default: + break; + } + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); consumed = true; } diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index a83740f3d..57dd4dd4b 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -42,7 +42,7 @@ void fuzzer_view_attack_reset_data( true); } -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool attack) { +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid) { furi_assert(view); with_view_model( @@ -61,11 +61,17 @@ void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool uid[5], uid[6], uid[7]); - model->attack_enabled = attack; }, true); } +void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack) { + furi_assert(view); + + with_view_model( + view->view, FuzzerViewAttackModel * model, { model->attack_enabled = attack; }, true); +} + void fuzzer_view_attack_set_callback( FuzzerViewAttack* view_attack, FuzzerViewAttackCallback callback, @@ -96,6 +102,7 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { } canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, model->uid); + canvas_set_font(canvas, FontSecondary); if(model->attack_enabled) { elements_button_center(canvas, "Stop"); } else { @@ -209,4 +216,14 @@ void fuzzer_view_attack_free(FuzzerViewAttack* view_attack) { View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack) { furi_assert(view_attack); return view_attack->view; +} + +uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view) { + furi_assert(view); + uint8_t time_delay; + + with_view_model( + view->view, FuzzerViewAttackModel * model, { time_delay = model->time_delay; }, false); + + return time_delay; } \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index 082b7ba36..c8204eb18 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -24,4 +24,8 @@ void fuzzer_view_attack_reset_data( const char* protocol_name, uint8_t uid_size); -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool attack); \ No newline at end of file +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid); + +void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack); + +uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); \ No newline at end of file From d3a260e4417d96a77c750f0f815f4255d39ace24 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 3 Jun 2023 14:24:27 +0300 Subject: [PATCH 048/370] Fuzzer App: worker add RFID --- .../external/pacs_fuzzer/application.fam | 38 +++- applications/external/pacs_fuzzer/fuzzer_i.h | 2 +- .../external/pacs_fuzzer/helpers/protocol.c | 92 -------- .../external/pacs_fuzzer/icons/125_10px.png | Bin 0 -> 308 bytes .../external/pacs_fuzzer/icons/ibutt_10px.png | Bin 0 -> 304 bytes .../pacs_fuzzer/{ => icons}/rfid_10px.png | Bin .../{helpers => lib/worker}/fake_worker.c | 106 +++++++-- .../{helpers => lib/worker}/fake_worker.h | 0 .../pacs_fuzzer/lib/worker/protocol.c | 214 ++++++++++++++++++ .../{helpers => lib/worker}/protocol.h | 23 ++ .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 2 +- .../external/pacs_fuzzer/views/attack.c | 2 +- .../external/pacs_fuzzer/views/main_menu.c | 2 +- 13 files changed, 365 insertions(+), 116 deletions(-) delete mode 100644 applications/external/pacs_fuzzer/helpers/protocol.c create mode 100644 applications/external/pacs_fuzzer/icons/125_10px.png create mode 100644 applications/external/pacs_fuzzer/icons/ibutt_10px.png rename applications/external/pacs_fuzzer/{ => icons}/rfid_10px.png (100%) rename applications/external/pacs_fuzzer/{helpers => lib/worker}/fake_worker.c (59%) rename applications/external/pacs_fuzzer/{helpers => lib/worker}/fake_worker.h (100%) create mode 100644 applications/external/pacs_fuzzer/lib/worker/protocol.c rename applications/external/pacs_fuzzer/{helpers => lib/worker}/protocol.h (65%) diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam index 5e0aafd99..6b6b2ab36 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/pacs_fuzzer/application.fam @@ -12,8 +12,40 @@ App( ], stack_size=2 * 1024, order=15, - fap_icon="rfid_10px.png", + fap_icon="icons/rfid_10px.png", fap_category="Debug", - # fap_icon_assets="images", - # fap_icon_assets_symbol="fuzzer", + fap_private_libs=[ + Lib( + name="worker", + cdefines=["IBUTTON_PROTOCOL"], + ), + ], + fap_icon_assets="icons", + fap_icon_assets_symbol="fuzzer", +) + +App( + appid="pacs_rfid_fuzzer", + name="Fuzzer Gui rfid", + apptype=FlipperAppType.EXTERNAL, + entry_point="fuzzer_start", + requires=[ + "gui", + "storage", + "dialogs", + "input", + "notification", + ], + stack_size=2 * 1024, + order=15, + fap_icon="icons/125_10px.png", + fap_category="Debug", + fap_private_libs=[ + Lib( + name="worker", + cdefines=["RFID_125_PROTOCOL"], + ), + ], + fap_icon_assets="icons", + fap_icon_assets_symbol="fuzzer", ) diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index bd4833c5b..bc31a137c 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -10,7 +10,7 @@ #include "views/attack.h" #include "helpers/fuzzer_types.h" -#include "helpers/fake_worker.h" +#include "lib/worker/fake_worker.h" #include diff --git a/applications/external/pacs_fuzzer/helpers/protocol.c b/applications/external/pacs_fuzzer/helpers/protocol.c deleted file mode 100644 index 072749179..000000000 --- a/applications/external/pacs_fuzzer/helpers/protocol.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "protocol.h" - -#define DS1990_DATA_SIZE (8) -#define Metakom_DATA_SIZE (4) -#define Cyfral_DATA_SIZE (2) - -const uint8_t uid_list_ds1990[][DS1990_DATA_SIZE] = { - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал - {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах - {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% - {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% - {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) - {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF - {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 - {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni -}; - -const uint8_t uid_list_metakom[][Metakom_DATA_SIZE] = { - {0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d}, // ?? - {0x34, 0x00, 0x29, 0x3d}, // ?? - {0x04, 0xdf, 0x00, 0x00}, // ?? - {0xCA, 0xCA, 0xCA, 0xCA}, // ?? -}; - -const uint8_t uid_list_cyfral[][Cyfral_DATA_SIZE] = { - {0x00, 0x00}, // Null bytes - {0xFF, 0xFF}, // Only FF - {0x11, 0x11}, // Only 11 - {0x22, 0x22}, // Only 22 - {0x33, 0x33}, // Only 33 - {0x44, 0x44}, // Only 44 - {0x55, 0x55}, // Only 55 - {0x66, 0x66}, // Only 66 - {0x77, 0x77}, // Only 77 - {0x88, 0x88}, // Only 88 - {0x99, 0x99}, // Only 99 - {0x12, 0x34}, // Incremental UID - {0x56, 0x34}, // Decremental UID - {0xCA, 0xCA}, // ?? - {0x8E, 0xC9}, // Elevator code - {0x6A, 0x50}, // VERY fresh code from smartkey -}; - -const FuzzerProtocol fuzzer_proto_items[] = { - [DS1990] = - { - .name = "DS1990", - .data_size = DS1990_DATA_SIZE, - .dict = - {.val = (const uint8_t*)&uid_list_ds1990, - .len = sizeof(uid_list_ds1990) / DS1990_DATA_SIZE}, - }, - [Metakom] = - { - .name = "Metakom", - .data_size = Metakom_DATA_SIZE, - .dict = - {.val = (const uint8_t*)&uid_list_metakom, - .len = sizeof(uid_list_metakom) / Metakom_DATA_SIZE}, - }, - [Cyfral] = - { - .name = "Cyfral", - .data_size = Cyfral_DATA_SIZE, - .dict = - {.val = (const uint8_t*)&uid_list_cyfral, - .len = sizeof(uid_list_cyfral) / Cyfral_DATA_SIZE}, - }, -}; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/icons/125_10px.png b/applications/external/pacs_fuzzer/icons/125_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..ce01284a2c1f3eb413f581b84f1fb3f3a2a7223b GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bWZjP>yH&963)5S4_<9hOs!iI<>&pI=m5)bW_|craZ$KesPZ!4!j_b)kjvx52z42i= z^Wk##wtdVzTiGS{99;2>!TC2M!yZeXz}?LkD}l;YOI#yLQW8s2t&)pUffR$0fsvuE zfvK*cNr<75m9c@9v4ysQft7*5`ikN&C>nC}Q!>*kp&E>VdO{3LtqcsU49p-Jly36? Qy~)7f>FVdQ&MBb@0C$~I0{{R3 literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/rfid_10px.png b/applications/external/pacs_fuzzer/icons/rfid_10px.png similarity index 100% rename from applications/external/pacs_fuzzer/rfid_10px.png rename to applications/external/pacs_fuzzer/icons/rfid_10px.png diff --git a/applications/external/pacs_fuzzer/helpers/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c similarity index 59% rename from applications/external/pacs_fuzzer/helpers/fake_worker.c rename to applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 15e3e035a..6da2becbc 100644 --- a/applications/external/pacs_fuzzer/helpers/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -3,16 +3,36 @@ #include #include +#if defined(RFID_125_PROTOCOL) + +#else + +#endif + +#if defined(RFID_125_PROTOCOL) + +#include +#include + +#else + #include #include +#endif #include struct FuzzerWorker { +#if defined(RFID_125_PROTOCOL) + LFRFIDWorker* proto_worker; + ProtocolId protocol_id; + ProtocolDict* protocols_items; +#else iButtonWorker* proto_worker; - iButtonProtocolId protocol_id; + iButtonProtocolId protocol_id; // TODO iButtonProtocols* protocols_items; iButtonKey* key; +#endif const FuzzerProtocol* protocol; FuzzerWorkerAttackType attack_type; @@ -57,7 +77,18 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { default: break; } +#if defined(RFID_125_PROTOCOL) + protocol_dict_set_data( + worker->protocols_items, worker->protocol_id, worker->payload, MAX_PAYLOAD_SIZE); +#else + ibutton_key_set_protocol_id(worker->key, worker->protocol_id); + iButtonEditableData data; + ibutton_protocols_get_editable_data(worker->protocols_items, worker->key, &data); + // TODO check data.size logic + data.size = MAX_PAYLOAD_SIZE; + memcpy(data.ptr, worker->payload, MAX_PAYLOAD_SIZE); // data.size); +#endif return res; } @@ -66,18 +97,31 @@ static void fuzzer_worker_on_tick_callback(void* context) { FuzzerWorker* worker = context; + if(worker->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(worker->proto_worker); +#else + ibutton_worker_stop(worker->proto_worker); +#endif + } + if(!fuzzer_worker_load_key(worker, true)) { fuzzer_worker_stop(worker); if(worker->end_callback) { worker->end_callback(worker->end_context); } } else { + if(worker->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); +#else + ibutton_worker_emulate_start(worker->proto_worker, worker->key); +#endif + } if(worker->tick_callback) { worker->tick_callback(worker->tick_context); } } - - // TODO load ibutton key } void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { @@ -90,8 +134,17 @@ void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { furi_assert(worker); - worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; worker->protocol = &fuzzer_proto_items[protocol_index]; + // TODO iButtonProtocolIdInvalid check + +#if defined(RFID_125_PROTOCOL) + worker->protocol_id = + protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); +#else + worker->protocol_id = + ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); +#endif + worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; worker->index = 0; return fuzzer_worker_load_key(worker, false); @@ -100,11 +153,17 @@ bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index FuzzerWorker* fuzzer_worker_alloc() { FuzzerWorker* worker = malloc(sizeof(FuzzerWorker)); +#if defined(RFID_125_PROTOCOL) + worker->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + + worker->proto_worker = lfrfid_worker_alloc(worker->protocols_items); +#else worker->protocols_items = ibutton_protocols_alloc(); worker->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(worker->protocols_items)); worker->proto_worker = ibutton_worker_alloc(worker->protocols_items); - +#endif + worker->attack_type = FuzzerWorkerAttackTypeMax; worker->index = 0; worker->treead_running = false; @@ -125,29 +184,37 @@ void fuzzer_worker_free(FuzzerWorker* worker) { furi_timer_free(worker->timer); +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_free(worker->proto_worker); + + protocol_dict_free(worker->protocols_items); +#else ibutton_worker_free(worker->proto_worker); ibutton_key_free(worker->key); ibutton_protocols_free(worker->protocols_items); - // TODO delete - UNUSED(fuzzer_worker_on_tick_callback); +#endif + free(worker); } void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { furi_assert(worker); - worker->timeer_delay = timer_dellay; + if(worker->attack_type < FuzzerWorkerAttackTypeMax) { + worker->timeer_delay = timer_dellay; - furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); + furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); - // TODO start timer - // worker->treead_running = true; - // ibutton_worker_start_thread(worker->proto_worker); - - // TODO load ibutton key - - // ibutton_worker_emulate_start(worker->proto_worker, worker->key); + worker->treead_running = true; +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_start_thread(worker->proto_worker); + lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); +#else + ibutton_worker_start_thread(worker->proto_worker); + ibutton_worker_emulate_start(worker->proto_worker, worker->key); +#endif + } } void fuzzer_worker_stop(FuzzerWorker* worker) { @@ -156,12 +223,17 @@ void fuzzer_worker_stop(FuzzerWorker* worker) { furi_timer_stop(worker->timer); if(worker->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(worker->proto_worker); + lfrfid_worker_stop_thread(worker->proto_worker); +#else ibutton_worker_stop(worker->proto_worker); ibutton_worker_stop_thread(worker->proto_worker); +#endif worker->treead_running = false; } - // TODO stop timer, anything else + // TODO anything else } void fuzzer_worker_set_uid_chaged_callback( diff --git a/applications/external/pacs_fuzzer/helpers/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h similarity index 100% rename from applications/external/pacs_fuzzer/helpers/fake_worker.h rename to applications/external/pacs_fuzzer/lib/worker/fake_worker.h diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c new file mode 100644 index 000000000..b27524d06 --- /dev/null +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -0,0 +1,214 @@ +#include "protocol.h" + +// ####################### +// ## Ibutton Protocols ## +// ####################### +#define DS1990_DATA_SIZE (8) +#define Metakom_DATA_SIZE (4) +#define Cyfral_DATA_SIZE (2) + +const uint8_t uid_list_ds1990[][DS1990_DATA_SIZE] = { + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал + {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах + {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% + {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) + {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF + {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 + {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni +}; + +const uint8_t uid_list_metakom[][Metakom_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // ?? + {0x34, 0x00, 0x29, 0x3d}, // ?? + {0x04, 0xdf, 0x00, 0x00}, // ?? + {0xCA, 0xCA, 0xCA, 0xCA}, // ?? +}; + +const uint8_t uid_list_cyfral[][Cyfral_DATA_SIZE] = { + {0x00, 0x00}, // Null bytes + {0xFF, 0xFF}, // Only FF + {0x11, 0x11}, // Only 11 + {0x22, 0x22}, // Only 22 + {0x33, 0x33}, // Only 33 + {0x44, 0x44}, // Only 44 + {0x55, 0x55}, // Only 55 + {0x66, 0x66}, // Only 66 + {0x77, 0x77}, // Only 77 + {0x88, 0x88}, // Only 88 + {0x99, 0x99}, // Only 99 + {0x12, 0x34}, // Incremental UID + {0x56, 0x34}, // Decremental UID + {0xCA, 0xCA}, // ?? + {0x8E, 0xC9}, // Elevator code + {0x6A, 0x50}, // VERY fresh code from smartkey +}; + +// ########################### +// ## Rfid_125khz Protocols ## +// ########################### +#define EM4100_DATA_SIZE (5) +#define HIDProx_DATA_SIZE (6) +#define PAC_DATA_SIZE (4) +#define H10301_DATA_SIZE (3) + +const uint8_t uid_list_em4100[][EM4100_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha + {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha + {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha + {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_hid[][HIDProx_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, // Incremental UID + {0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID + {0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_pac[][PAC_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // From arha + {0x34, 0x00, 0x29, 0x3d}, // From arha + {0x04, 0xdf, 0x00, 0x00}, // From arha + {0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_h10301[][H10301_DATA_SIZE] = { + {0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56}, // Incremental UID + {0x56, 0x34, 0x12}, // Decremental UID + {0xCA, 0xCA, 0xCA}, // From arha +}; + +#if defined(RFID_125_PROTOCOL) +const FuzzerProtocol fuzzer_proto_items[] = { + [EM4100] = + { + .name = "EM4100", + .data_size = EM4100_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_em4100, + .len = sizeof(uid_list_em4100) / EM4100_DATA_SIZE}, + }, + [HIDProx] = + { + .name = "HIDProx", + .data_size = HIDProx_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_hid, + .len = sizeof(uid_list_hid) / HIDProx_DATA_SIZE}, + }, + [PAC] = + { + .name = "PAC/Stanley", + .data_size = PAC_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_pac, + .len = sizeof(uid_list_pac) / PAC_DATA_SIZE}, + }, + [H10301] = + { + .name = "H10301", + .data_size = H10301_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_h10301, + .len = sizeof(uid_list_h10301) / H10301_DATA_SIZE}, + }, +}; +#else +const FuzzerProtocol fuzzer_proto_items[] = { + [DS1990] = + { + .name = "DS1990", + .data_size = DS1990_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_ds1990, + .len = sizeof(uid_list_ds1990) / DS1990_DATA_SIZE}, + }, + [Metakom] = + { + .name = "Metakom", + .data_size = Metakom_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_metakom, + .len = sizeof(uid_list_metakom) / Metakom_DATA_SIZE}, + }, + [Cyfral] = + { + .name = "Cyfral", + .data_size = Cyfral_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_cyfral, + .len = sizeof(uid_list_cyfral) / Cyfral_DATA_SIZE}, + }, +}; +#endif \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h similarity index 65% rename from applications/external/pacs_fuzzer/helpers/protocol.h rename to applications/external/pacs_fuzzer/lib/worker/protocol.h index c0dd5dd15..c6d7c88ba 100644 --- a/applications/external/pacs_fuzzer/helpers/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -2,16 +2,39 @@ #include +// #define RFID_125_PROTOCOL + +#if defined(RFID_125_PROTOCOL) + +#define MAX_PAYLOAD_SIZE 6 + +#define FUZZ_TIME_DELAY_MIN (5) +#define FUZZ_TIME_DELAY_DEFAULT (10) +#define FUZZ_TIME_DELAY_MAX (70) + +#else + #define MAX_PAYLOAD_SIZE 8 #define FUZZ_TIME_DELAY_MIN (4) #define FUZZ_TIME_DELAY_DEFAULT (8) #define FUZZ_TIME_DELAY_MAX (80) +#endif + typedef enum { + +#if defined(RFID_125_PROTOCOL) + EM4100, + HIDProx, + PAC, + H10301, +#else DS1990, Metakom, Cyfral, +#endif + // Reserved FuzzerProtoMax, } FuzzerProtos; diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 00ecdc543..1caf0b0ed 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -1,7 +1,7 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -#include "../helpers/protocol.h" +#include "../lib/worker/protocol.h" #include "../helpers/gui_const.h" void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 57dd4dd4b..9e589985d 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -4,7 +4,7 @@ #include #include -#include "../helpers/protocol.h" +#include "../lib/worker/protocol.h" #define ATTACK_SCENE_MAX_UID_LENGTH 25 diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index c037fdf44..13ed005f1 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -4,7 +4,7 @@ #include // #include -#include "../helpers/protocol.h" +#include "../lib/worker/protocol.h" #include "../helpers/gui_const.h" struct FuzzerViewMain { From 2b677c83e3832e3bfb7d9c9df2bf0fa178f82406 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 3 Jun 2023 18:47:53 +0300 Subject: [PATCH 049/370] Fuzzers App: load custom dict --- .../external/pacs_fuzzer/application.fam | 4 +- applications/external/pacs_fuzzer/fuzzer.c | 38 +++++- applications/external/pacs_fuzzer/fuzzer_i.h | 19 +++ .../pacs_fuzzer/lib/worker/fake_worker.c | 114 ++++++++++++++++-- .../pacs_fuzzer/lib/worker/fake_worker.h | 9 +- .../pacs_fuzzer/lib/worker/protocol.h | 18 --- .../pacs_fuzzer/lib/worker/protocol_i.h | 31 +++++ .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 21 ++-- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 43 ++++++- 9 files changed, 253 insertions(+), 44 deletions(-) create mode 100644 applications/external/pacs_fuzzer/lib/worker/protocol_i.h diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam index 6b6b2ab36..8e67af6d4 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/pacs_fuzzer/application.fam @@ -2,7 +2,7 @@ App( appid="pacs_fuzzer", name="Fuzzer Gui", apptype=FlipperAppType.EXTERNAL, - entry_point="fuzzer_start", + entry_point="fuzzer_start_ibtn", requires=[ "gui", "storage", @@ -28,7 +28,7 @@ App( appid="pacs_rfid_fuzzer", name="Fuzzer Gui rfid", apptype=FlipperAppType.EXTERNAL, - entry_point="fuzzer_start", + entry_point="fuzzer_start_rfid", requires=[ "gui", "storage", diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index 5a6a4c9b6..b6b66fb18 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -27,9 +27,14 @@ PacsFuzzerApp* fuzzer_app_alloc() { app->worker = fuzzer_worker_alloc(); + app->file_path = furi_string_alloc(); + // GUI app->gui = furi_record_open(RECORD_GUI); + // Dialog + app->dialogs = furi_record_open(RECORD_DIALOGS); + // View Dispatcher app->view_dispatcher = view_dispatcher_alloc(); @@ -75,18 +80,49 @@ void fuzzer_app_free(PacsFuzzerApp* app) { scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); + // Dialog + furi_record_close(RECORD_DIALOGS); + // Close records furi_record_close(RECORD_GUI); + furi_string_free(app->file_path); + fuzzer_worker_free(app->worker); free(app); } -int32_t fuzzer_start(void* p) { +int32_t fuzzer_start_ibtn(void* p) { UNUSED(p); PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); + FuzzerConsts app_const = { + .custom_dict_folder = "/ext/ibtnfuzzer", + .custom_dict_extension = ".txt", + .key_extension = ".ibtn", + .path_key_folder = "/ext/ibutton", + }; + fuzzer_app->fuzzer_const = &app_const; + + view_dispatcher_run(fuzzer_app->view_dispatcher); + + fuzzer_app_free(fuzzer_app); + return 0; +} + +int32_t fuzzer_start_rfid(void* p) { + UNUSED(p); + PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); + + FuzzerConsts app_const = { + .custom_dict_folder = "/ext/rfidfuzzer", + .custom_dict_extension = ".txt", + .key_extension = ".rfid", + .path_key_folder = "/ext/lfrfid", + }; + fuzzer_app->fuzzer_const = &app_const; + view_dispatcher_run(fuzzer_app->view_dispatcher); fuzzer_app_free(fuzzer_app); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index bc31a137c..b56becd8f 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -1,9 +1,13 @@ #pragma once +#include +#include + #include #include #include #include +#include #include "scenes/fuzzer_scene.h" #include "views/main_menu.h" @@ -13,16 +17,31 @@ #include "lib/worker/fake_worker.h" #include +#include "fuzzer_icons.h" + +#define FUZZ_TIME_DELAY_MIN (5) +#define FUZZ_TIME_DELAY_MAX (80) + +typedef struct { + const char* custom_dict_extension; + const char* custom_dict_folder; + const char* key_extension; + const char* path_key_folder; +} FuzzerConsts; typedef struct { Gui* gui; ViewDispatcher* view_dispatcher; SceneManager* scene_manager; + DialogsApp* dialogs; FuzzerViewMain* main_view; FuzzerViewAttack* attack_view; + FuriString* file_path; + FuzzerState fuzzer_state; + FuzzerConsts* fuzzer_const; FuzzerWorker* worker; } PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 6da2becbc..ff74f29e1 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -1,25 +1,28 @@ #include "fake_worker.h" -#include #include -#if defined(RFID_125_PROTOCOL) +#include +#include +#include -#else - -#endif +#define TAG "Fuzzer worker" +#define FUZZ_TIME_DELAY_DEFAULT (10) #if defined(RFID_125_PROTOCOL) +#define MAX_PAYLOAD_SIZE 6 #include #include #else +#define MAX_PAYLOAD_SIZE 8 #include #include #endif + #include struct FuzzerWorker { @@ -74,6 +77,42 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { } break; + case FuzzerWorkerAttackTypeLoadFileCustomUids: { + uint8_t str_len = protocol->data_size * 2 + 1; + FuriString* data_str = furi_string_alloc(); + while(true) { + furi_string_reset(data_str); + if(!stream_read_line(worker->uids_stream, data_str)) { + stream_rewind(worker->uids_stream); + // TODO Check empty file & close stream and storage + break; + } else if(furi_string_get_char(data_str, 0) == '#') { + // Skip comment string + continue; + } else if(furi_string_size(data_str) != str_len) { + // Ignore strin with bad length + FURI_LOG_W(TAG, "Bad string length"); + continue; + } else { + FURI_LOG_D(TAG, "Uid candidate: \"%s\"", furi_string_get_cstr(data_str)); + bool parse_ok = true; + for(uint8_t i = 0; i < protocol->data_size; i++) { + if(!hex_char_to_uint8( + furi_string_get_cstr(data_str)[i * 2], + furi_string_get_cstr(data_str)[i * 2 + 1], + &worker->payload[i])) { + parse_ok = false; + break; + } + } + res = parse_ok; + } + break; + } + } + + break; + default: break; } @@ -134,20 +173,71 @@ void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { furi_assert(worker); + bool res = false; + worker->protocol = &fuzzer_proto_items[protocol_index]; - // TODO iButtonProtocolIdInvalid check #if defined(RFID_125_PROTOCOL) worker->protocol_id = protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); #else + // TODO iButtonProtocolIdInvalid check worker->protocol_id = ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); #endif worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; worker->index = 0; - return fuzzer_worker_load_key(worker, false); + if(!fuzzer_worker_load_key(worker, false)) { + worker->attack_type = FuzzerWorkerAttackTypeMax; + } else { + res = true; + } + + return res; +} + +bool fuzzer_worker_attack_file_dict( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + FuriString* file_path) { + furi_assert(worker); + furi_assert(file_path); + + bool res = false; + + worker->protocol = &fuzzer_proto_items[protocol_index]; + +#if defined(RFID_125_PROTOCOL) + worker->protocol_id = + protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); +#else + // TODO iButtonProtocolIdInvalid check + worker->protocol_id = + ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); +#endif + + Storage* storage = furi_record_open(RECORD_STORAGE); + worker->uids_stream = buffered_file_stream_alloc(storage); + + if(!buffered_file_stream_open( + worker->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(worker->uids_stream); + return res; + } + + worker->attack_type = FuzzerWorkerAttackTypeLoadFileCustomUids; + worker->index = 0; + + if(!fuzzer_worker_load_key(worker, false)) { + worker->attack_type = FuzzerWorkerAttackTypeMax; + buffered_file_stream_close(worker->uids_stream); + furi_record_close(RECORD_STORAGE); + } else { + res = true; + } + + return res; } FuzzerWorker* fuzzer_worker_alloc() { @@ -198,7 +288,7 @@ void fuzzer_worker_free(FuzzerWorker* worker) { free(worker); } -void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { +bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { furi_assert(worker); if(worker->attack_type < FuzzerWorkerAttackTypeMax) { @@ -214,7 +304,9 @@ void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { ibutton_worker_start_thread(worker->proto_worker); ibutton_worker_emulate_start(worker->proto_worker, worker->key); #endif + return true; } + return false; } void fuzzer_worker_stop(FuzzerWorker* worker) { @@ -233,6 +325,12 @@ void fuzzer_worker_stop(FuzzerWorker* worker) { worker->treead_running = false; } + if(worker->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) { + buffered_file_stream_close(worker->uids_stream); + furi_record_close(RECORD_STORAGE); + worker->attack_type = FuzzerWorkerAttackTypeMax; + } + // TODO anything else } diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 00e3cb23f..2628ac649 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "protocol.h" @@ -21,12 +21,17 @@ FuzzerWorker* fuzzer_worker_alloc(); void fuzzer_worker_free(FuzzerWorker* worker); -void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); +bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); void fuzzer_worker_stop(FuzzerWorker* worker); bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index); +bool fuzzer_worker_attack_file_dict( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + FuriString* file_path); + void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key); void fuzzer_worker_set_uid_chaged_callback( diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index c6d7c88ba..a69d3db04 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -4,24 +4,6 @@ // #define RFID_125_PROTOCOL -#if defined(RFID_125_PROTOCOL) - -#define MAX_PAYLOAD_SIZE 6 - -#define FUZZ_TIME_DELAY_MIN (5) -#define FUZZ_TIME_DELAY_DEFAULT (10) -#define FUZZ_TIME_DELAY_MAX (70) - -#else - -#define MAX_PAYLOAD_SIZE 8 - -#define FUZZ_TIME_DELAY_MIN (4) -#define FUZZ_TIME_DELAY_DEFAULT (8) -#define FUZZ_TIME_DELAY_MAX (80) - -#endif - typedef enum { #if defined(RFID_125_PROTOCOL) diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h new file mode 100644 index 000000000..e0ab76cb3 --- /dev/null +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -0,0 +1,31 @@ +#pragma once + +#include "protocol.h" + +#if defined(RFID_125_PROTOCOL) + +#define MAX_PAYLOAD_SIZE 6 + +#define FUZZ_TIME_DELAY_MIN (5) +#define FUZZ_TIME_DELAY_DEFAULT (10) +#define FUZZ_TIME_DELAY_MAX (70) + +#define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" +#define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/rfidfuzzer" +#define FUZZER_APP_KEY_EXTENSION ".rfid" +#define FUZZER_APP_PATH_KEY_FOLDER "/ext/lfrfid" + +#else + +#define MAX_PAYLOAD_SIZE 8 + +#define FUZZ_TIME_DELAY_MIN (4) +#define FUZZ_TIME_DELAY_DEFAULT (8) +#define FUZZ_TIME_DELAY_MAX (80) + +#define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" +#define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/ibtnfuzzer" +#define FUZZER_APP_KEY_EXTENSION ".ibtn" +#define FUZZER_APP_PATH_KEY_FOLDER "/ext/ibutton" + +#endif diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index f80cc9b29..c18e2cec9 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -31,21 +31,20 @@ void fuzzer_scene_attack_on_enter(void* context) { FuzzerProtocol proto = fuzzer_proto_items[app->fuzzer_state.proto_index]; - fuzzer_view_attack_reset_data( - app->attack_view, - fuzzer_attack_names[app->fuzzer_state.menu_index], - proto.name, - proto.data_size); - fuzzer_worker_set_uid_chaged_callback( app->worker, fuzzer_scene_attack_worker_tick_callback, app); fuzzer_worker_set_end_callback(app->worker, fuzzer_scene_attack_worker_end_callback, app); - uint8_t temp_uid[MAX_PAYLOAD_SIZE]; + uint8_t temp_uid[proto.data_size]; fuzzer_worker_get_current_key(app->worker, temp_uid); + fuzzer_view_attack_reset_data( + app->attack_view, + fuzzer_attack_names[app->fuzzer_state.menu_index], + proto.name, + proto.data_size); fuzzer_view_attack_set_attack(app->attack_view, false); fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); @@ -73,11 +72,11 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackOk) { - if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack)) { + if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) && + fuzzer_worker_start( + app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, true); fuzzer_view_attack_set_attack(app->attack_view, true); - fuzzer_worker_start( - app->worker, fuzzer_view_attack_get_time_delay(app->attack_view)); } else { scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); fuzzer_view_attack_set_attack(app->attack_view, false); @@ -85,7 +84,7 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackTick) { - uint8_t temp_uid[MAX_PAYLOAD_SIZE]; + uint8_t temp_uid[fuzzer_proto_items[app->fuzzer_state.proto_index].data_size]; fuzzer_worker_get_current_key(app->worker, temp_uid); diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 1caf0b0ed..a42701adf 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -10,6 +10,26 @@ void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, event); } +static bool fuzzer_scene_main_load_custom_dict(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + FuzzerConsts* consts = app->fuzzer_const; + + furi_string_set_str(app->file_path, consts->custom_dict_folder); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, consts->custom_dict_extension, &I_rfid_10px); + browser_options.base_path = consts->custom_dict_folder; + browser_options.hide_ext = false; + + bool res = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + return res; +} + void fuzzer_scene_main_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -36,16 +56,35 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { } else if(event.event == FuzzerCustomEventViewMainOk) { fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); + // TODO error logic + bool loading_ok = false; + switch(app->fuzzer_state.menu_index) { case FuzzerMainMenuIndexDefaultValues: - fuzzer_worker_attack_dict(app->worker, app->fuzzer_state.proto_index); + + loading_ok = fuzzer_worker_attack_dict(app->worker, app->fuzzer_state.proto_index); + + if(!loading_ok) { + // error + } + break; + + case FuzzerMainMenuIndexLoadFileCustomUids: + if(!fuzzer_scene_main_load_custom_dict(app)) { + break; + } else { + loading_ok = fuzzer_worker_attack_file_dict( + app->worker, app->fuzzer_state.proto_index, app->file_path); + } break; default: break; } - scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + if(loading_ok) { + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + } consumed = true; } } From 5b4bb66848e28a325b97872339beb986d80c4d39 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:03:20 +0300 Subject: [PATCH 050/370] Fuzzer App: Field editor view --- applications/external/pacs_fuzzer/fuzzer.c | 11 + applications/external/pacs_fuzzer/fuzzer_i.h | 2 + .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 3 + .../pacs_fuzzer/helpers/fuzzer_types.h | 1 + .../pacs_fuzzer/scenes/fuzzer_scene_config.h | 3 +- .../scenes/fuzzer_scene_field_editor.c | 42 +++ .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 6 + .../external/pacs_fuzzer/views/attack.c | 2 - .../external/pacs_fuzzer/views/field_editor.c | 251 ++++++++++++++++++ .../external/pacs_fuzzer/views/field_editor.h | 19 ++ .../external/pacs_fuzzer/views/main_menu.c | 2 - 11 files changed, 337 insertions(+), 5 deletions(-) create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c create mode 100644 applications/external/pacs_fuzzer/views/field_editor.c create mode 100644 applications/external/pacs_fuzzer/views/field_editor.h diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index b6b66fb18..6b609040c 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -48,6 +48,13 @@ PacsFuzzerApp* fuzzer_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, FuzzerViewIDAttack, fuzzer_view_attack_get_view(app->attack_view)); + // FieldEditor view + app->field_editor_view = fuzzer_view_field_editor_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FuzzerViewIDFieldEditor, + fuzzer_view_field_editor_get_view(app->field_editor_view)); + app->scene_manager = scene_manager_alloc(&fuzzer_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); @@ -77,6 +84,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) { view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDAttack); fuzzer_view_attack_free(app->attack_view); + // FieldEditor view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDFieldEditor); + fuzzer_view_field_editor_free(app->field_editor_view); + scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index b56becd8f..59ec2df33 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -12,6 +12,7 @@ #include "scenes/fuzzer_scene.h" #include "views/main_menu.h" #include "views/attack.h" +#include "views/field_editor.h" #include "helpers/fuzzer_types.h" #include "lib/worker/fake_worker.h" @@ -37,6 +38,7 @@ typedef struct { DialogsApp* dialogs; FuzzerViewMain* main_view; FuzzerViewAttack* attack_view; + FuzzerViewFieldEditor* field_editor_view; FuriString* file_path; diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h index 890d961db..930029d3c 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -10,4 +10,7 @@ typedef enum { FuzzerCustomEventViewAttackOk, FuzzerCustomEventViewAttackTick, FuzzerCustomEventViewAttackEnd, + + FuzzerCustomEventViewFieldEditorBack, + FuzzerCustomEventViewFieldEditorOk, } FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index 55c64954c..7e390f875 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -11,4 +11,5 @@ typedef struct { typedef enum { FuzzerViewIDMain, FuzzerViewIDAttack, + FuzzerViewIDFieldEditor, } FuzzerViewID; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h index bccdbd9a5..711ebe1c4 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h @@ -1,2 +1,3 @@ ADD_SCENE(fuzzer, main, Main) -ADD_SCENE(fuzzer, attack, Attack) \ No newline at end of file +ADD_SCENE(fuzzer, attack, Attack) +ADD_SCENE(fuzzer, field_editor, FieldEditor) \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c new file mode 100644 index 000000000..197bd82c4 --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -0,0 +1,42 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +void fuzzer_scene_field_editor_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_field_editor_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_field_editor_set_callback( + app->field_editor_view, fuzzer_scene_field_editor_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); +} + +bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewFieldEditorBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + + return consumed; +} + +void fuzzer_scene_field_editor_on_exit(void* context) { + // furi_assert(context); + // PacsFuzzerApp* app = context; + UNUSED(context); +} diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index a42701adf..cb5e97c52 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -69,6 +69,12 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { } break; + case FuzzerMainMenuIndexLoadFile: + // TODO Delete + scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); + + break; + case FuzzerMainMenuIndexLoadFileCustomUids: if(!fuzzer_scene_main_load_custom_dict(app)) { break; diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 9e589985d..910d69c0c 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -4,8 +4,6 @@ #include #include -#include "../lib/worker/protocol.h" - #define ATTACK_SCENE_MAX_UID_LENGTH 25 struct FuzzerViewAttack { diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c new file mode 100644 index 000000000..daf8e9d24 --- /dev/null +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -0,0 +1,251 @@ +#include "field_editor.h" +#include "../fuzzer_i.h" + +#include +#include +#include + +#define UID_STR_LENGTH 25 +#define EDITOR_STRING_Y 50 + +struct FuzzerViewFieldEditor { + View* view; + FuzzerViewFieldEditorCallback callback; + void* context; +}; + +// TODO model +typedef struct { + uint8_t* uid; + uint8_t uid_size; + uint8_t index; + FuriString* uid_str; + bool lo; +} FuzzerViewFieldEditorModel; + +void fuzzer_view_field_editor_set_callback( + FuzzerViewFieldEditor* view_edit, + FuzzerViewFieldEditorCallback callback, + void* context) { + furi_assert(view_edit); + + view_edit->callback = callback; + view_edit->context = context; +} + +void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Left and right: select byte"); + canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "Up and down: adjust byte"); + + char msg_index[18]; + canvas_set_font(canvas, FontPrimary); + snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); + + // ####### Editor ####### + FuriString* temp_s = model->uid_str; + canvas_set_font(canvas, FontSecondary); + + furi_string_reset(temp_s); + for(int i = -3; i != 0; i++) { + if(0 <= (model->index + i)) { + furi_string_cat_printf(temp_s, "%2X ", model->uid[model->index + i]); + } + } + canvas_draw_str_aligned( + canvas, 52, EDITOR_STRING_Y, AlignRight, AlignBottom, furi_string_get_cstr(temp_s)); + + furi_string_reset(temp_s); + for(int i = 1; i != 4; i++) { + if((model->index + i) < model->uid_size) { + furi_string_cat_printf(temp_s, " %2X", model->uid[model->index + i]); + } + } + canvas_draw_str_aligned( + canvas, 77, EDITOR_STRING_Y, AlignLeft, AlignBottom, furi_string_get_cstr(temp_s)); + + canvas_set_font(canvas, FontPrimary); + + furi_string_reset(temp_s); + furi_string_cat_printf(temp_s, "<%02X>", model->uid[model->index]); + canvas_draw_str_aligned( + canvas, 64, EDITOR_STRING_Y, AlignCenter, AlignBottom, furi_string_get_cstr(temp_s)); + + uint16_t w = canvas_string_width(canvas, furi_string_get_cstr(temp_s)); + w -= 11; // '<' & '>' + w /= 2; + + if(model->lo) { + canvas_draw_line(canvas, 64 + 1, EDITOR_STRING_Y + 2, 64 + w, EDITOR_STRING_Y + 2); + } else { + canvas_draw_line(canvas, 64 - w, EDITOR_STRING_Y + 2, 64 - 1, EDITOR_STRING_Y + 2); + } + // ####### Editor ####### +} + +bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewFieldEditor* view_edit = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + view_edit->callback(FuzzerCustomEventViewFieldEditorBack, view_edit->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view_edit->callback(FuzzerCustomEventViewFieldEditorOk, view_edit->context); + return true; + } else if(event->key == InputKeyLeft) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(model->index > 0 || model->lo) { + if(!model->lo) { + model->index--; + } + model->lo = !model->lo; + } + } else if(event->type == InputTypeLong) { + model->index = 0; + model->lo = false; + } + }, + true); + return true; + } else if(event->key == InputKeyRight) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(model->index < (model->uid_size - 1) || !model->lo) { + if(model->lo) { + model->index++; + } + model->lo = !model->lo; + } + } else if(event->type == InputTypeLong) { + model->index = model->uid_size - 1; + model->lo = true; + } + }, + true); + return true; + } else if(event->key == InputKeyUp) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(model->lo) { + model->uid[model->index] = (model->uid[model->index] & 0xF0) | + ((model->uid[model->index] + 1) & 0x0F); + } else { + model->uid[model->index] = ((model->uid[model->index] + 0x10) & 0xF0) | + (model->uid[model->index] & 0x0F); + } + } + }, + true); + return true; + } else if(event->key == InputKeyDown) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(model->lo) { + model->uid[model->index] = (model->uid[model->index] & 0xF0) | + ((model->uid[model->index] - 1) & 0x0F); + } else { + model->uid[model->index] = ((model->uid[model->index] - 0x10) & 0xF0) | + (model->uid[model->index] & 0x0F); + } + } + }, + true); + return true; + } + + return true; +} + +void fuzzer_view_field_editor_enter(void* context) { + furi_assert(context); + // TODO delete only for debug + // FuzzerViewFieldEditor* view_edit = context; + // uint8_t temp[8] = { + // 0x12, + // 0x34, + // 0x56, + // 0x78, + // 0x90, + // 0xAB, + // 0xCD, + // 0xEF, + // }; + // with_view_model( + // view_edit->view, + // FuzzerViewFieldEditorModel * model, + // { + // memcpy(model->uid, &temp, 8); + + // // memset(model->uid, 0xCC, 8); + // model->index = 0; + // model->uid_size = 8; + // }, + // true); +} + +void fuzzer_view_field_editor_exit(void* context) { + furi_assert(context); +} + +FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc() { + FuzzerViewFieldEditor* view_edit = malloc(sizeof(FuzzerViewFieldEditor)); + + // View allocation and configuration + view_edit->view = view_alloc(); + view_allocate_model(view_edit->view, ViewModelTypeLocking, sizeof(FuzzerViewFieldEditorModel)); + view_set_context(view_edit->view, view_edit); + view_set_draw_callback(view_edit->view, (ViewDrawCallback)fuzzer_view_field_editor_draw); + view_set_input_callback(view_edit->view, fuzzer_view_field_editor_input); + view_set_enter_callback(view_edit->view, fuzzer_view_field_editor_enter); + view_set_exit_callback(view_edit->view, fuzzer_view_field_editor_exit); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + model->uid_str = furi_string_alloc(); + + model->uid = malloc(8); + }, + true); + + return view_edit; +} + +void fuzzer_view_field_editor_free(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + furi_string_free(model->uid_str); + free(model->uid); + }, + true); + view_free(view_edit->view); + free(view_edit); +} + +View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + return view_edit->view; +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h new file mode 100644 index 000000000..df3aba724 --- /dev/null +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/fuzzer_custom_event.h" + +typedef struct FuzzerViewFieldEditor FuzzerViewFieldEditor; + +typedef void (*FuzzerViewFieldEditorCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_field_editor_set_callback( + FuzzerViewFieldEditor* view_attack, + FuzzerViewFieldEditorCallback callback, + void* context); + +FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc(); + +void fuzzer_view_field_editor_free(FuzzerViewFieldEditor* view_attack); + +View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 13ed005f1..d12c3e380 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -2,9 +2,7 @@ #include "../fuzzer_i.h" #include -// #include -#include "../lib/worker/protocol.h" #include "../helpers/gui_const.h" struct FuzzerViewMain { From 3bd08ab31cb56ce30aaf6d6953dc3c6a12419072 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sun, 4 Jun 2023 20:21:51 +0300 Subject: [PATCH 051/370] Fuzzer App: Load key file --- .../pacs_fuzzer/lib/worker/fake_worker.c | 119 +++++++++++++++--- .../pacs_fuzzer/lib/worker/fake_worker.h | 11 ++ .../scenes/fuzzer_scene_field_editor.c | 22 ++++ .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 36 +++++- applications/external/pacs_fuzzer/todo.md | 32 +++++ .../external/pacs_fuzzer/views/field_editor.c | 42 ++++++- .../external/pacs_fuzzer/views/field_editor.h | 11 +- 7 files changed, 247 insertions(+), 26 deletions(-) create mode 100644 applications/external/pacs_fuzzer/todo.md diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index ff74f29e1..b44790ebc 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -12,6 +12,7 @@ #if defined(RFID_125_PROTOCOL) #define MAX_PAYLOAD_SIZE 6 +#include #include #include @@ -44,6 +45,7 @@ struct FuzzerWorker { uint8_t payload[MAX_PAYLOAD_SIZE]; Stream* uids_stream; uint16_t index; + uint8_t chusen_byte; bool treead_running; FuriTimer* timer; @@ -62,12 +64,11 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { const FuzzerProtocol* protocol = worker->protocol; - if(next) { - worker->index++; - } - switch(worker->attack_type) { case FuzzerWorkerAttackTypeDefaultDict: + if(next) { + worker->index++; + } if(worker->index < protocol->dict.len) { memcpy( worker->payload, @@ -78,6 +79,9 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { break; case FuzzerWorkerAttackTypeLoadFileCustomUids: { + if(next) { + worker->index++; + } uint8_t str_len = protocol->data_size * 2 + 1; FuriString* data_str = furi_string_alloc(); while(true) { @@ -113,6 +117,14 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { break; + case FuzzerWorkerAttackTypeLoadFile: + if(worker->payload[worker->index] != 0xFF) { + worker->payload[worker->index]++; + res = true; + } + + break; + default: break; } @@ -170,11 +182,7 @@ void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { memcpy(key, worker->payload, worker->protocol->data_size); } -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { - furi_assert(worker); - - bool res = false; - +static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtos protocol_index) { worker->protocol = &fuzzer_proto_items[protocol_index]; #if defined(RFID_125_PROTOCOL) @@ -185,6 +193,14 @@ bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index worker->protocol_id = ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); #endif +} + +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { + furi_assert(worker); + + bool res = false; + fuzzer_worker_set_protocol(worker, protocol_index); + worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; worker->index = 0; @@ -205,17 +221,7 @@ bool fuzzer_worker_attack_file_dict( furi_assert(file_path); bool res = false; - - worker->protocol = &fuzzer_proto_items[protocol_index]; - -#if defined(RFID_125_PROTOCOL) - worker->protocol_id = - protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); -#else - // TODO iButtonProtocolIdInvalid check - worker->protocol_id = - ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); -#endif + fuzzer_worker_set_protocol(worker, protocol_index); Storage* storage = furi_record_open(RECORD_STORAGE); worker->uids_stream = buffered_file_stream_alloc(storage); @@ -240,6 +246,79 @@ bool fuzzer_worker_attack_file_dict( return res; } +bool fuzzer_worker_attack_bf_byte( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + const uint8_t* uid, + uint8_t chusen) { + furi_assert(worker); + + bool res = false; + fuzzer_worker_set_protocol(worker, protocol_index); + + worker->attack_type = FuzzerWorkerAttackTypeLoadFile; + worker->index = chusen; + + memcpy(worker->payload, uid, worker->protocol->data_size); + + res = true; + + return res; +} + +// TODO make it protocol independent +bool fuzzer_worker_load_key_from_file( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + const char* filename) { + furi_assert(worker); + + bool res = false; + fuzzer_worker_set_protocol(worker, protocol_index); + +#if defined(RFID_125_PROTOCOL) + ProtocolId loaded_proto_id = lfrfid_dict_file_load(worker->protocols_items, filename); + if(loaded_proto_id == PROTOCOL_NO) { + // Err Cant load file + FURI_LOG_W(TAG, "Cant load file"); + } else if(worker->protocol_id != loaded_proto_id) { // Err wrong protocol + FURI_LOG_W(TAG, "Wrong protocol"); + FURI_LOG_W( + TAG, + "Selected: %s Loaded: %s", + worker->protocol->name, + protocol_dict_get_name(worker->protocols_items, loaded_proto_id)); + } else { + protocol_dict_get_data( + worker->protocols_items, worker->protocol_id, worker->payload, MAX_PAYLOAD_SIZE); + res = true; + } +#else + if(!ibutton_protocols_load(worker->protocols_items, worker->key, filename)) { + // Err Cant load file + FURI_LOG_W(TAG, "Cant load file"); + } else { + if(worker->protocol_id != ibutton_key_get_protocol_id(worker->key)) { + // Err wrong protocol + FURI_LOG_W(TAG, "Wrong protocol"); + FURI_LOG_W( + TAG, + "Selected: %s Loaded: %s", + worker->protocol->name, + ibutton_protocols_get_name( + worker->protocols_items, ibutton_key_get_protocol_id(worker->key))); + } else { + iButtonEditableData data; + ibutton_protocols_get_editable_data(worker->protocols_items, worker->key, &data); + memcpy(worker->payload, data.ptr, data.size); + res = true; + } + } +#endif + + return res; +} + FuzzerWorker* fuzzer_worker_alloc() { FuzzerWorker* worker = malloc(sizeof(FuzzerWorker)); diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 2628ac649..b1eea19fb 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -27,6 +27,12 @@ void fuzzer_worker_stop(FuzzerWorker* worker); bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index); +bool fuzzer_worker_attack_bf_byte( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + const uint8_t* uid, + uint8_t chusen); + bool fuzzer_worker_attack_file_dict( FuzzerWorker* worker, FuzzerProtos protocol_index, @@ -34,6 +40,11 @@ bool fuzzer_worker_attack_file_dict( void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key); +bool fuzzer_worker_load_key_from_file( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + const char* filename); + void fuzzer_worker_set_uid_chaged_callback( FuzzerWorker* worker, FuzzerWorkerUidChagedCallback callback, diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index 197bd82c4..52e19e9e0 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -1,6 +1,8 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" +#define UID_MAX_SIZE 8 // TODO + void fuzzer_scene_field_editor_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -14,6 +16,17 @@ void fuzzer_scene_field_editor_on_enter(void* context) { fuzzer_view_field_editor_set_callback( app->field_editor_view, fuzzer_scene_field_editor_callback, app); + uint8_t uid[UID_MAX_SIZE]; + + uint8_t* uid_p = &uid[0]; + + fuzzer_worker_get_current_key(app->worker, uid_p); + + fuzzer_view_field_editor_reset_data( + app->field_editor_view, + uid_p, + fuzzer_proto_items[app->fuzzer_state.proto_index].data_size); // TODO + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); } @@ -29,6 +42,15 @@ bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) view_dispatcher_stop(app->view_dispatcher); } consumed = true; + } else if(event.event == FuzzerCustomEventViewFieldEditorOk) { + // TODO + if(fuzzer_worker_attack_bf_byte( + app->worker, + app->fuzzer_state.proto_index, + fuzzer_view_field_editor_get_uid(app->field_editor_view), + fuzzer_view_field_editor_get_index(app->field_editor_view))) { + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + } } } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index cb5e97c52..3f03835fa 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -30,6 +30,26 @@ static bool fuzzer_scene_main_load_custom_dict(void* context) { return res; } +static bool fuzzer_scene_main_load_key(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + FuzzerConsts* consts = app->fuzzer_const; + + furi_string_set_str(app->file_path, consts->path_key_folder); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, consts->key_extension, &I_rfid_10px); // TODO icon + browser_options.base_path = consts->path_key_folder; + browser_options.hide_ext = true; + + bool res = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + return res; +} + void fuzzer_scene_main_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -70,9 +90,19 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { break; case FuzzerMainMenuIndexLoadFile: - // TODO Delete - scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); - + if(!fuzzer_scene_main_load_key(app)) { + break; + } else { + if(fuzzer_worker_load_key_from_file( + app->worker, + app->fuzzer_state.proto_index, + furi_string_get_cstr(app->file_path))) { + scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); + FURI_LOG_I("Scene", "Load ok"); + } else { + FURI_LOG_W("Scene", "Load err"); + } + } break; case FuzzerMainMenuIndexLoadFileCustomUids: diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md new file mode 100644 index 000000000..c96e897e9 --- /dev/null +++ b/applications/external/pacs_fuzzer/todo.md @@ -0,0 +1,32 @@ +## Working Improvement + +#### Quality of life + +- [ ] Make the "Load File" independent of the current protocol +- [ ] Add pause + - [ ] Switching UIDs if possible +- [ ] Led and sound Notification +- [ ] Error Notification + - [ ] Custom UIDs dict loading + - [ ] Key file loading + - [ ] Anything else + +#### App functionality + +- [ ] Add `BFCustomerID` attack +- [ ] Save key logic + +## Code Improvement + +- [ ] GUI + - [ ] Rewrite `gui_const` logic + - [ ] Separate protocol name from `fuzzer_proto_items` + - [ ] Icon in dialog + - [ ] Description and buttons in `field_editor` view + - [ ] Protocol carousel in `main_menu` +- [ ] UID + - [ ] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` + - [ ] `UID_MAX_SIZE` +- [ ] Add pause + - [ ] Fix `Custom dict` attack when ended +- [ ] this can be simplified `fuzzer_proto_items` diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index daf8e9d24..f073baf99 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -8,6 +8,8 @@ #define UID_STR_LENGTH 25 #define EDITOR_STRING_Y 50 +#define UID_MAX_SIZE 8 // TODO + struct FuzzerViewFieldEditor { View* view; FuzzerViewFieldEditorCallback callback; @@ -18,8 +20,10 @@ struct FuzzerViewFieldEditor { typedef struct { uint8_t* uid; uint8_t uid_size; - uint8_t index; + FuriString* uid_str; + + uint8_t index; bool lo; } FuzzerViewFieldEditorModel; @@ -33,6 +37,40 @@ void fuzzer_view_field_editor_set_callback( view_edit->context = context; } +void fuzzer_view_field_editor_reset_data( + FuzzerViewFieldEditor* view_edit, + uint8_t* uid, + uint8_t uid_size) { + furi_assert(view_edit); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + memcpy(model->uid, uid, uid_size); + model->index = 0; + model->lo = false; + model->uid_size = uid_size; + }, + true); +} + +const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + uint8_t* uid; + with_view_model( + view_edit->view, FuzzerViewFieldEditorModel * model, { uid = model->uid; }, true); + return uid; +} + +uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + uint8_t index; + with_view_model( + view_edit->view, FuzzerViewFieldEditorModel * model, { index = model->index; }, true); + return index; +} + void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); @@ -223,7 +261,7 @@ FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc() { { model->uid_str = furi_string_alloc(); - model->uid = malloc(8); + model->uid = malloc(UID_MAX_SIZE); }, true); diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h index df3aba724..26a8a8ac9 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.h +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -16,4 +16,13 @@ FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc(); void fuzzer_view_field_editor_free(FuzzerViewFieldEditor* view_attack); -View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); \ No newline at end of file +View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); + +void fuzzer_view_field_editor_reset_data( + FuzzerViewFieldEditor* view_edit, + uint8_t* uid, + uint8_t uid_size); + +const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit); + +uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit); \ No newline at end of file From b95620cdd0fe8332e9f499551fdd9c6eceef4975 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:10:51 +0300 Subject: [PATCH 052/370] Fuzzer App: Some Improvement --- applications/external/pacs_fuzzer/fuzzer.c | 2 + applications/external/pacs_fuzzer/fuzzer_i.h | 1 + .../external/pacs_fuzzer/helpers/gui_const.c | 7 -- .../external/pacs_fuzzer/helpers/gui_const.h | 12 --- .../pacs_fuzzer/lib/worker/fake_worker.c | 20 ++--- .../pacs_fuzzer/lib/worker/fake_worker.h | 10 +-- .../pacs_fuzzer/lib/worker/protocol.c | 73 +++++++++++++++---- .../pacs_fuzzer/lib/worker/protocol.h | 32 ++++---- .../pacs_fuzzer/lib/worker/protocol_i.h | 64 ++++++++++------ .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 35 ++++----- .../scenes/fuzzer_scene_field_editor.c | 14 +--- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 3 +- applications/external/pacs_fuzzer/todo.md | 12 +-- .../external/pacs_fuzzer/views/attack.c | 37 ++++++---- .../external/pacs_fuzzer/views/attack.h | 6 +- .../external/pacs_fuzzer/views/field_editor.c | 12 +-- .../external/pacs_fuzzer/views/field_editor.h | 5 +- .../external/pacs_fuzzer/views/main_menu.c | 32 +++++--- 18 files changed, 218 insertions(+), 159 deletions(-) delete mode 100644 applications/external/pacs_fuzzer/helpers/gui_const.c delete mode 100644 applications/external/pacs_fuzzer/helpers/gui_const.h diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index 6b609040c..0a9aa3f7d 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -113,6 +113,7 @@ int32_t fuzzer_start_ibtn(void* p) { .custom_dict_extension = ".txt", .key_extension = ".ibtn", .path_key_folder = "/ext/ibutton", + .key_icon = &I_ibutt_10px, }; fuzzer_app->fuzzer_const = &app_const; @@ -131,6 +132,7 @@ int32_t fuzzer_start_rfid(void* p) { .custom_dict_extension = ".txt", .key_extension = ".rfid", .path_key_folder = "/ext/lfrfid", + .key_icon = &I_125_10px, }; fuzzer_app->fuzzer_const = &app_const; diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 59ec2df33..2f24ec431 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -28,6 +28,7 @@ typedef struct { const char* custom_dict_folder; const char* key_extension; const char* path_key_folder; + const Icon* key_icon; } FuzzerConsts; typedef struct { diff --git a/applications/external/pacs_fuzzer/helpers/gui_const.c b/applications/external/pacs_fuzzer/helpers/gui_const.c deleted file mode 100644 index 79e31ad1b..000000000 --- a/applications/external/pacs_fuzzer/helpers/gui_const.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "gui_const.h" - -const char* fuzzer_attack_names[FuzzerMainMenuIndexMax] = { - [FuzzerMainMenuIndexDefaultValues] = "Default Values", - [FuzzerMainMenuIndexLoadFile] = "Load File", - [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", -}; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/gui_const.h b/applications/external/pacs_fuzzer/helpers/gui_const.h deleted file mode 100644 index 837322c2a..000000000 --- a/applications/external/pacs_fuzzer/helpers/gui_const.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -// TODO replace it -typedef enum { - FuzzerMainMenuIndexDefaultValues = 0, - FuzzerMainMenuIndexLoadFile, - FuzzerMainMenuIndexLoadFileCustomUids, - - FuzzerMainMenuIndexMax, -} FuzzerMainMenuIndex; - -extern const char* fuzzer_attack_names[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index b44790ebc..f4a442bc0 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -1,4 +1,5 @@ #include "fake_worker.h" +#include "protocol_i.h" #include @@ -11,14 +12,12 @@ #if defined(RFID_125_PROTOCOL) -#define MAX_PAYLOAD_SIZE 6 #include #include #include #else -#define MAX_PAYLOAD_SIZE 8 #include #include @@ -175,14 +174,17 @@ static void fuzzer_worker_on_tick_callback(void* context) { } } -void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { +void fuzzer_worker_get_current_key(FuzzerWorker* worker, FuzzerPayload* output_key) { furi_assert(worker); + furi_assert(output_key); furi_assert(worker->protocol); - memcpy(key, worker->payload, worker->protocol->data_size); + output_key->data_size = worker->protocol->data_size; + output_key->data = malloc(sizeof(output_key->data_size)); + memcpy(output_key->data, worker->payload, worker->protocol->data_size); } -static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtos protocol_index) { +static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtocolsID protocol_index) { worker->protocol = &fuzzer_proto_items[protocol_index]; #if defined(RFID_125_PROTOCOL) @@ -195,7 +197,7 @@ static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtos protoc #endif } -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index) { furi_assert(worker); bool res = false; @@ -215,7 +217,7 @@ bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index bool fuzzer_worker_attack_file_dict( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, FuriString* file_path) { furi_assert(worker); furi_assert(file_path); @@ -248,7 +250,7 @@ bool fuzzer_worker_attack_file_dict( bool fuzzer_worker_attack_bf_byte( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, const uint8_t* uid, uint8_t chusen) { furi_assert(worker); @@ -269,7 +271,7 @@ bool fuzzer_worker_attack_bf_byte( // TODO make it protocol independent bool fuzzer_worker_load_key_from_file( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, const char* filename) { furi_assert(worker); diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index b1eea19fb..fe680f36b 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -25,24 +25,24 @@ bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); void fuzzer_worker_stop(FuzzerWorker* worker); -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index); +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index); bool fuzzer_worker_attack_bf_byte( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, const uint8_t* uid, uint8_t chusen); bool fuzzer_worker_attack_file_dict( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, FuriString* file_path); -void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key); +void fuzzer_worker_get_current_key(FuzzerWorker* worker, FuzzerPayload* output_key); bool fuzzer_worker_load_key_from_file( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, const char* filename); void fuzzer_worker_set_uid_chaged_callback( diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index b27524d06..b9f9e6bf6 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -1,4 +1,5 @@ -#include "protocol.h" +#include "protocol_i.h" +#include "furi.h" // ####################### // ## Ibutton Protocols ## @@ -156,32 +157,40 @@ const FuzzerProtocol fuzzer_proto_items[] = { .name = "EM4100", .data_size = EM4100_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_em4100, - .len = sizeof(uid_list_em4100) / EM4100_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_em4100, + .len = COUNT_OF(uid_list_em4100), + }, }, [HIDProx] = { .name = "HIDProx", .data_size = HIDProx_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_hid, - .len = sizeof(uid_list_hid) / HIDProx_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_hid, + .len = COUNT_OF(uid_list_hid), + }, }, [PAC] = { .name = "PAC/Stanley", .data_size = PAC_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_pac, - .len = sizeof(uid_list_pac) / PAC_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_pac, + .len = COUNT_OF(uid_list_pac), + }, }, [H10301] = { .name = "H10301", .data_size = H10301_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_h10301, - .len = sizeof(uid_list_h10301) / H10301_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_h10301, + .len = COUNT_OF(uid_list_h10301), + }, }, }; #else @@ -191,24 +200,56 @@ const FuzzerProtocol fuzzer_proto_items[] = { .name = "DS1990", .data_size = DS1990_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_ds1990, - .len = sizeof(uid_list_ds1990) / DS1990_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_ds1990, + .len = COUNT_OF(uid_list_ds1990), + }, }, [Metakom] = { .name = "Metakom", .data_size = Metakom_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_metakom, - .len = sizeof(uid_list_metakom) / Metakom_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_metakom, + .len = COUNT_OF(uid_list_metakom), + }, }, [Cyfral] = { .name = "Cyfral", .data_size = Cyfral_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_cyfral, - .len = sizeof(uid_list_cyfral) / Cyfral_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_cyfral, + .len = COUNT_OF(uid_list_cyfral), + }, }, }; -#endif \ No newline at end of file +#endif + +const char* fuzzer_attack_names[] = { + [FuzzerMainMenuIndexDefaultValues] = "Default Values", + [FuzzerMainMenuIndexLoadFile] = "Load File", + [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", +}; + +const char* fuzzer_proto_get_name(FuzzerProtocolsID index) { + return fuzzer_proto_items[index].name; +} + +uint8_t fuzzer_proto_get_count_of_protocols() { + return COUNT_OF(fuzzer_proto_items); +} + +uint8_t fuzzer_proto_get_max_data_size() { + return MAX_PAYLOAD_SIZE; +} + +const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index) { + return fuzzer_attack_names[index]; +} + +uint8_t fuzzer_proto_get_count_of_menu_items() { + return COUNT_OF(fuzzer_attack_names); +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index a69d3db04..0de1223ff 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -4,8 +4,9 @@ // #define RFID_125_PROTOCOL -typedef enum { +typedef struct FuzzerPayload FuzzerPayload; +typedef enum { #if defined(RFID_125_PROTOCOL) EM4100, HIDProx, @@ -16,24 +17,25 @@ typedef enum { Metakom, Cyfral, #endif +} FuzzerProtocolsID; - // Reserved - FuzzerProtoMax, -} FuzzerProtos; +typedef enum { + FuzzerMainMenuIndexDefaultValues = 0, + FuzzerMainMenuIndexLoadFile, + FuzzerMainMenuIndexLoadFileCustomUids, +} FuzzerMainMenuIndex; -struct ProtoDict { - const uint8_t* val; - const uint8_t len; +struct FuzzerPayload { + uint8_t* data; + uint8_t data_size; }; -typedef struct ProtoDict ProtoDict; +uint8_t fuzzer_proto_get_max_data_size(); -struct FuzzerProtocol { - const char* name; - const uint8_t data_size; - const ProtoDict dict; -}; +const char* fuzzer_proto_get_name(FuzzerProtocolsID index); -typedef struct FuzzerProtocol FuzzerProtocol; +uint8_t fuzzer_proto_get_count_of_protocols(); -extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file +const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index); + +uint8_t fuzzer_proto_get_count_of_menu_items(); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h index e0ab76cb3..841784f16 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -3,29 +3,45 @@ #include "protocol.h" #if defined(RFID_125_PROTOCOL) - -#define MAX_PAYLOAD_SIZE 6 - -#define FUZZ_TIME_DELAY_MIN (5) -#define FUZZ_TIME_DELAY_DEFAULT (10) -#define FUZZ_TIME_DELAY_MAX (70) - -#define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" -#define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/rfidfuzzer" -#define FUZZER_APP_KEY_EXTENSION ".rfid" -#define FUZZER_APP_PATH_KEY_FOLDER "/ext/lfrfid" - +#define MAX_PAYLOAD_SIZE (6) #else - -#define MAX_PAYLOAD_SIZE 8 - -#define FUZZ_TIME_DELAY_MIN (4) -#define FUZZ_TIME_DELAY_DEFAULT (8) -#define FUZZ_TIME_DELAY_MAX (80) - -#define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" -#define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/ibtnfuzzer" -#define FUZZER_APP_KEY_EXTENSION ".ibtn" -#define FUZZER_APP_PATH_KEY_FOLDER "/ext/ibutton" - +#define MAX_PAYLOAD_SIZE (8) #endif + +typedef struct ProtoDict ProtoDict; +typedef struct FuzzerProtocol FuzzerProtocol; + +struct ProtoDict { + const uint8_t* val; + const uint8_t len; // TODO +}; + +struct FuzzerProtocol { + const char* name; + const uint8_t data_size; + const ProtoDict dict; +}; + +// #define MAX_PAYLOAD_SIZE 6 + +// #define FUZZ_TIME_DELAY_MIN (5) +// #define FUZZ_TIME_DELAY_DEFAULT (10) +// #define FUZZ_TIME_DELAY_MAX (70) + +// #define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" +// #define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/rfidfuzzer" +// #define FUZZER_APP_KEY_EXTENSION ".rfid" +// #define FUZZER_APP_PATH_KEY_FOLDER "/ext/lfrfid" + +// #define MAX_PAYLOAD_SIZE 8 + +// #define FUZZ_TIME_DELAY_MIN (4) +// #define FUZZ_TIME_DELAY_DEFAULT (8) +// #define FUZZ_TIME_DELAY_MAX (80) + +// #define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" +// #define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/ibtnfuzzer" +// #define FUZZER_APP_KEY_EXTENSION ".ibtn" +// #define FUZZER_APP_PATH_KEY_FOLDER "/ext/ibutton" + +extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index c18e2cec9..ac3962f32 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -1,8 +1,6 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -#include "../helpers/gui_const.h" - // TODO simlify callbacks and attack state void fuzzer_scene_attack_worker_tick_callback(void* context) { @@ -23,30 +21,37 @@ void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, event); } +static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { + furi_assert(app); + furi_assert(app->worker); + furi_assert(app->attack_view); + + FuzzerPayload uid; + fuzzer_worker_get_current_key(app->worker, &uid); + + fuzzer_view_attack_set_uid(app->attack_view, uid); + + free(uid.data); +} + void fuzzer_scene_attack_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; fuzzer_view_attack_set_callback(app->attack_view, fuzzer_scene_attack_callback, app); - FuzzerProtocol proto = fuzzer_proto_items[app->fuzzer_state.proto_index]; - fuzzer_worker_set_uid_chaged_callback( app->worker, fuzzer_scene_attack_worker_tick_callback, app); fuzzer_worker_set_end_callback(app->worker, fuzzer_scene_attack_worker_end_callback, app); - uint8_t temp_uid[proto.data_size]; - - fuzzer_worker_get_current_key(app->worker, temp_uid); - fuzzer_view_attack_reset_data( app->attack_view, - fuzzer_attack_names[app->fuzzer_state.menu_index], - proto.name, - proto.data_size); + fuzzer_proto_get_menu_label(app->fuzzer_state.menu_index), + fuzzer_proto_get_name(app->fuzzer_state.proto_index)); fuzzer_view_attack_set_attack(app->attack_view, false); - fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); + + fuzzer_scene_attack_update_uid(app); scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); @@ -84,11 +89,7 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackTick) { - uint8_t temp_uid[fuzzer_proto_items[app->fuzzer_state.proto_index].data_size]; - - fuzzer_worker_get_current_key(app->worker, temp_uid); - - fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); + fuzzer_scene_attack_update_uid(app); consumed = true; } else if(event.event == FuzzerCustomEventViewAttackEnd) { scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index 52e19e9e0..f8adae0df 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -1,8 +1,6 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -#define UID_MAX_SIZE 8 // TODO - void fuzzer_scene_field_editor_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -16,16 +14,12 @@ void fuzzer_scene_field_editor_on_enter(void* context) { fuzzer_view_field_editor_set_callback( app->field_editor_view, fuzzer_scene_field_editor_callback, app); - uint8_t uid[UID_MAX_SIZE]; + FuzzerPayload uid; + fuzzer_worker_get_current_key(app->worker, &uid); - uint8_t* uid_p = &uid[0]; + fuzzer_view_field_editor_reset_data(app->field_editor_view, uid); - fuzzer_worker_get_current_key(app->worker, uid_p); - - fuzzer_view_field_editor_reset_data( - app->field_editor_view, - uid_p, - fuzzer_proto_items[app->fuzzer_state.proto_index].data_size); // TODO + free(uid.data); view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 3f03835fa..e27bc0776 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -2,7 +2,6 @@ #include "../helpers/fuzzer_custom_event.h" #include "../lib/worker/protocol.h" -#include "../helpers/gui_const.h" void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); @@ -40,7 +39,7 @@ static bool fuzzer_scene_main_load_key(void* context) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( - &browser_options, consts->key_extension, &I_rfid_10px); // TODO icon + &browser_options, consts->key_extension, consts->key_icon); browser_options.base_path = consts->path_key_folder; browser_options.hide_ext = true; diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index c96e897e9..75e708c06 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -19,14 +19,14 @@ ## Code Improvement - [ ] GUI - - [ ] Rewrite `gui_const` logic + - [x] Rewrite `gui_const` logic - [ ] Separate protocol name from `fuzzer_proto_items` - - [ ] Icon in dialog + - [x] Icon in dialog - [ ] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` -- [ ] UID - - [ ] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - - [ ] `UID_MAX_SIZE` +- [x] UID + - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` + - [x] `UID_MAX_SIZE` - [ ] Add pause - [ ] Fix `Custom dict` attack when ended -- [ ] this can be simplified `fuzzer_proto_items` +- [x] this can be simplified `fuzzer_proto_items` diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 910d69c0c..6ef306f07 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -5,6 +5,7 @@ #include #define ATTACK_SCENE_MAX_UID_LENGTH 25 +#define UID_MAX_DISPLAYED_LEN (8U) struct FuzzerViewAttack { View* view; @@ -18,14 +19,12 @@ typedef struct { const char* protocol_name; bool attack_enabled; char* uid; - uint8_t uid_size; } FuzzerViewAttackModel; void fuzzer_view_attack_reset_data( FuzzerViewAttack* view, const char* attack_name, - const char* protocol_name, - uint8_t uid_size) { + const char* protocol_name) { furi_assert(view); with_view_model( @@ -35,32 +34,38 @@ void fuzzer_view_attack_reset_data( model->attack_name = attack_name; model->protocol_name = protocol_name; model->attack_enabled = false; - model->uid_size = uid_size; + strcpy(model->uid, "Not_set"); }, true); } -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid) { +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid) { furi_assert(view); + // TODO fix it + uint8_t* data = malloc(uid.data_size); + memcpy(data, uid.data, uid.data_size); + with_view_model( view->view, FuzzerViewAttackModel * model, { snprintf( model->uid, - model->uid_size * 3, + uid.data_size * 3, "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - uid[0], - uid[1], - uid[2], - uid[3], - uid[4], - uid[5], - uid[6], - uid[7]); + data[0], + data[1], + data[2], + data[3], + data[4], + data[5], + data[6], + data[7]); }, true); + + free(data); } void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack) { @@ -175,6 +180,10 @@ void fuzzer_view_attack_exit(void* context) { } FuzzerViewAttack* fuzzer_view_attack_alloc() { + if(fuzzer_proto_get_max_data_size() > UID_MAX_DISPLAYED_LEN) { + furi_crash("Maximum of displayed bytes exceeded"); + } + FuzzerViewAttack* view_attack = malloc(sizeof(FuzzerViewAttack)); // View allocation and configuration diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index c8204eb18..e1aa4edae 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -2,6 +2,7 @@ #include #include "../helpers/fuzzer_custom_event.h" +#include "../lib/worker/protocol.h" typedef struct FuzzerViewAttack FuzzerViewAttack; @@ -21,10 +22,9 @@ View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack); void fuzzer_view_attack_reset_data( FuzzerViewAttack* view, const char* attack_name, - const char* protocol_name, - uint8_t uid_size); + const char* protocol_name); -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid); +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid); void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack); diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index f073baf99..53e15e152 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -8,8 +8,6 @@ #define UID_STR_LENGTH 25 #define EDITOR_STRING_Y 50 -#define UID_MAX_SIZE 8 // TODO - struct FuzzerViewFieldEditor { View* view; FuzzerViewFieldEditorCallback callback; @@ -39,18 +37,17 @@ void fuzzer_view_field_editor_set_callback( void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - uint8_t* uid, - uint8_t uid_size) { + const FuzzerPayload new_uid) { furi_assert(view_edit); with_view_model( view_edit->view, FuzzerViewFieldEditorModel * model, { - memcpy(model->uid, uid, uid_size); + memcpy(model->uid, new_uid.data, new_uid.data_size); model->index = 0; model->lo = false; - model->uid_size = uid_size; + model->uid_size = new_uid.data_size; }, true); } @@ -260,8 +257,7 @@ FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc() { FuzzerViewFieldEditorModel * model, { model->uid_str = furi_string_alloc(); - - model->uid = malloc(UID_MAX_SIZE); + model->uid = malloc(fuzzer_proto_get_max_data_size()); }, true); diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h index 26a8a8ac9..f76b5d336 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.h +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -2,6 +2,7 @@ #include #include "../helpers/fuzzer_custom_event.h" +#include "../lib/worker/protocol.h" typedef struct FuzzerViewFieldEditor FuzzerViewFieldEditor; @@ -20,9 +21,9 @@ View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - uint8_t* uid, - uint8_t uid_size); + const FuzzerPayload new_uid); +// TODO const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit); uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index d12c3e380..8194275ec 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -3,7 +3,7 @@ #include -#include "../helpers/gui_const.h" +#include "../lib/worker/protocol.h" struct FuzzerViewMain { View* view; @@ -15,6 +15,8 @@ struct FuzzerViewMain { typedef struct { uint8_t proto_index; uint8_t menu_index; + uint8_t proto_max; + uint8_t menu_max; } FuzzerViewMainModel; void fuzzer_view_main_update_data(FuzzerViewMain* view, FuzzerState state) { @@ -58,23 +60,33 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { if(model->menu_index > 0) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 64, 24, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index - 1]); + canvas, + 64, + 24, + AlignCenter, + AlignTop, + fuzzer_proto_get_menu_label(model->menu_index - 1)); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned( - canvas, 64, 36, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index]); + canvas, 64, 36, AlignCenter, AlignTop, fuzzer_proto_get_menu_label(model->menu_index)); - if(model->menu_index < FuzzerMainMenuIndexMax) { + if(model->menu_index < model->menu_max) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 64, 48, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index + 1]); + canvas, + 64, + 48, + AlignCenter, + AlignTop, + fuzzer_proto_get_menu_label(model->menu_index + 1)); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); canvas_draw_str_aligned( - canvas, 64, 4, AlignCenter, AlignTop, fuzzer_proto_items[model->proto_index].name); + canvas, 64, 4, AlignCenter, AlignTop, fuzzer_proto_get_name(model->proto_index)); canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); } @@ -94,7 +106,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { view->view, FuzzerViewMainModel * model, { - if(model->menu_index < (FuzzerMainMenuIndexMax - 1)) { + if(model->menu_index < (model->menu_max - 1)) { model->menu_index++; } }, @@ -119,7 +131,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { if(model->proto_index != 0) { model->proto_index--; } else { - model->proto_index = (FuzzerProtoMax - 1); + model->proto_index = (model->proto_max - 1); } }, true); @@ -129,7 +141,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { view->view, FuzzerViewMainModel * model, { - if(model->proto_index == (FuzzerProtoMax - 1)) { + if(model->proto_index == (model->proto_max - 1)) { model->proto_index = 0; } else { model->proto_index++; @@ -167,7 +179,9 @@ FuzzerViewMain* fuzzer_view_main_alloc() { FuzzerViewMainModel * model, { model->proto_index = 0; + model->proto_max = fuzzer_proto_get_count_of_protocols(); model->menu_index = 0; + model->menu_max = fuzzer_proto_get_count_of_menu_items(); }, true); return view; From 6eed74c71628f4a1e98fda43f58b3d4bb1077293 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:35:15 +0300 Subject: [PATCH 053/370] Fuzzer App: prtocol carusel prototype --- .../pacs_fuzzer/helpers/fuzzer_types.h | 1 - applications/external/pacs_fuzzer/todo.md | 1 + .../external/pacs_fuzzer/views/main_menu.c | 35 +++++++++++++++++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index 7e390f875..a50b89c61 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -1,7 +1,6 @@ #pragma once #include -#include typedef struct { uint8_t menu_index; diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 75e708c06..1b56ec189 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -24,6 +24,7 @@ - [x] Icon in dialog - [ ] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` + - [x] prototype - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] `UID_MAX_SIZE` diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 8194275ec..8c4c23603 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -5,6 +5,9 @@ #include "../lib/worker/protocol.h" +#define PROTOCOL_NAME_Y 12 +// #define PROTOCOL_CAROUSEL + struct FuzzerViewMain { View* view; FuzzerViewMainCallback callback; @@ -84,10 +87,36 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { } canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); + canvas_draw_str_aligned(canvas, 27, PROTOCOL_NAME_Y, AlignCenter, AlignBottom, "<"); canvas_draw_str_aligned( - canvas, 64, 4, AlignCenter, AlignTop, fuzzer_proto_get_name(model->proto_index)); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); + canvas, + 64, + PROTOCOL_NAME_Y, + AlignCenter, + AlignBottom, + fuzzer_proto_get_name(model->proto_index)); + canvas_draw_str_aligned(canvas, 101, PROTOCOL_NAME_Y, AlignCenter, AlignBottom, ">"); + +#ifdef PROTOCOL_CAROUSEL + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 20, + PROTOCOL_NAME_Y, + AlignRight, + AlignBottom, + (model->proto_index > 0) ? fuzzer_proto_get_name(model->proto_index - 1) : + fuzzer_proto_get_name((model->proto_max - 1))); + canvas_draw_str_aligned( + canvas, + 108, + PROTOCOL_NAME_Y, + AlignLeft, + AlignBottom, + (model->proto_index < (model->proto_max - 1)) ? + fuzzer_proto_get_name(model->proto_index + 1) : + fuzzer_proto_get_name(0)); +#endif } bool fuzzer_view_main_input(InputEvent* event, void* context) { From d3eb43ce3537643f78840b248e4f186d0cd160bb Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:49:30 +0300 Subject: [PATCH 054/370] Fuzzer App: Attack state --- .../pacs_fuzzer/helpers/fuzzer_types.h | 8 +++ .../pacs_fuzzer/lib/worker/fake_worker.c | 35 +++++++++-- .../pacs_fuzzer/lib/worker/fake_worker.h | 2 + .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 53 ++++++++++------ applications/external/pacs_fuzzer/todo.md | 6 +- .../external/pacs_fuzzer/views/attack.c | 63 ++++++++++++++++--- .../external/pacs_fuzzer/views/attack.h | 11 +++- 7 files changed, 143 insertions(+), 35 deletions(-) diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index a50b89c61..259fc2b52 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -7,6 +7,14 @@ typedef struct { uint8_t proto_index; } FuzzerState; +typedef enum { + FuzzerAttackStateOff = 0, + FuzzerAttackStateIdle, + FuzzerAttackStateRunning, + FuzzerAttackStateEnd, + +} FuzzerAttackState; + typedef enum { FuzzerViewIDMain, FuzzerViewIDAttack, diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index f4a442bc0..97f632085 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -156,7 +156,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { } if(!fuzzer_worker_load_key(worker, true)) { - fuzzer_worker_stop(worker); + fuzzer_worker_pause(worker); // XXX if(worker->end_callback) { worker->end_callback(worker->end_context); } @@ -377,12 +377,23 @@ bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); - worker->treead_running = true; + if(!worker->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_start_thread(worker->proto_worker); + lfrfid_worker_start_thread(worker->proto_worker); +#else + ibutton_worker_start_thread(worker->proto_worker); +#endif + FURI_LOG_D(TAG, "Worker Starting"); + worker->treead_running = true; + } else { + FURI_LOG_D(TAG, "Worker UnPaused"); + } + +#if defined(RFID_125_PROTOCOL) + // lfrfid_worker_start_thread(worker->proto_worker); lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); #else - ibutton_worker_start_thread(worker->proto_worker); + // ibutton_worker_start_thread(worker->proto_worker); ibutton_worker_emulate_start(worker->proto_worker, worker->key); #endif return true; @@ -390,6 +401,21 @@ bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { return false; } +void fuzzer_worker_pause(FuzzerWorker* worker) { + furi_assert(worker); + + furi_timer_stop(worker->timer); + + if(worker->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(worker->proto_worker); +#else + ibutton_worker_stop(worker->proto_worker); +#endif + FURI_LOG_D(TAG, "Worker Paused"); + } +} + void fuzzer_worker_stop(FuzzerWorker* worker) { furi_assert(worker); @@ -403,6 +429,7 @@ void fuzzer_worker_stop(FuzzerWorker* worker) { ibutton_worker_stop(worker->proto_worker); ibutton_worker_stop_thread(worker->proto_worker); #endif + FURI_LOG_D(TAG, "Worker Stopping"); worker->treead_running = false; } diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index fe680f36b..2f8733393 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -25,6 +25,8 @@ bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); void fuzzer_worker_stop(FuzzerWorker* worker); +void fuzzer_worker_pause(FuzzerWorker* worker); + bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index); bool fuzzer_worker_attack_bf_byte( diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index ac3962f32..61fa84261 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -49,11 +49,10 @@ void fuzzer_scene_attack_on_enter(void* context) { app->attack_view, fuzzer_proto_get_menu_label(app->fuzzer_state.menu_index), fuzzer_proto_get_name(app->fuzzer_state.proto_index)); - fuzzer_view_attack_set_attack(app->attack_view, false); fuzzer_scene_attack_update_uid(app); - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDAttack); } @@ -65,35 +64,53 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == FuzzerCustomEventViewAttackBack) { - if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack)) { + if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateRunning) { + // Pause if attack running + fuzzer_worker_pause(app->worker); + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); + fuzzer_view_attack_pause(app->attack_view); + } else { + // Exit + fuzzer_worker_stop(app->worker); + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateOff); + fuzzer_view_attack_stop(app->attack_view); if(!scene_manager_previous_scene(app->scene_manager)) { scene_manager_stop(app->scene_manager); view_dispatcher_stop(app->view_dispatcher); } - } else { - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); - fuzzer_view_attack_set_attack(app->attack_view, false); - fuzzer_worker_stop(app->worker); } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackOk) { - if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) && - fuzzer_worker_start( - app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, true); - fuzzer_view_attack_set_attack(app->attack_view, true); - } else { - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); - fuzzer_view_attack_set_attack(app->attack_view, false); - fuzzer_worker_stop(app->worker); + if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateIdle) { + // Start or Continue Attack + if(fuzzer_worker_start( + app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateRunning); + fuzzer_view_attack_start(app->attack_view); + } else { + // Error? + } + } else if( + scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateRunning) { + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); + fuzzer_view_attack_pause(app->attack_view); + fuzzer_worker_pause(app->worker); // XXX } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackTick) { fuzzer_scene_attack_update_uid(app); consumed = true; } else if(event.event == FuzzerCustomEventViewAttackEnd) { - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); - fuzzer_view_attack_set_attack(app->attack_view, false); + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateEnd); + fuzzer_view_attack_end(app->attack_view); consumed = true; } } diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 1b56ec189..dd6f4fc69 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -3,7 +3,7 @@ #### Quality of life - [ ] Make the "Load File" independent of the current protocol -- [ ] Add pause +- [x] Add pause - [ ] Switching UIDs if possible - [ ] Led and sound Notification - [ ] Error Notification @@ -28,6 +28,6 @@ - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] `UID_MAX_SIZE` -- [ ] Add pause - - [ ] Fix `Custom dict` attack when ended +- [x] Add pause + - [x] Fix `Custom dict` attack when ended - [x] this can be simplified `fuzzer_proto_items` diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 6ef306f07..ad99f0132 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -17,7 +17,7 @@ typedef struct { uint8_t time_delay; const char* attack_name; const char* protocol_name; - bool attack_enabled; + FuzzerAttackState attack_state; char* uid; } FuzzerViewAttackModel; @@ -33,7 +33,7 @@ void fuzzer_view_attack_reset_data( { model->attack_name = attack_name; model->protocol_name = protocol_name; - model->attack_enabled = false; + model->attack_state = FuzzerAttackStateIdle; strcpy(model->uid, "Not_set"); }, true); @@ -68,11 +68,44 @@ void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid) free(data); } -void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack) { +void fuzzer_view_attack_start(FuzzerViewAttack* view) { furi_assert(view); with_view_model( - view->view, FuzzerViewAttackModel * model, { model->attack_enabled = attack; }, true); + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateRunning; }, + true); +} + +void fuzzer_view_attack_stop(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateOff; }, + true); +} + +void fuzzer_view_attack_pause(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateIdle; }, + true); +} + +void fuzzer_view_attack_end(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateEnd; }, + true); } void fuzzer_view_attack_set_callback( @@ -106,12 +139,15 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, model->uid); canvas_set_font(canvas, FontSecondary); - if(model->attack_enabled) { + if(model->attack_state == FuzzerAttackStateRunning) { elements_button_center(canvas, "Stop"); - } else { + } else if(model->attack_state == FuzzerAttackStateIdle) { elements_button_center(canvas, "Start"); elements_button_left(canvas, "TD -"); elements_button_right(canvas, "+ TD"); + } else if(model->attack_state == FuzzerAttackStateEnd) { + // elements_button_center(canvas, "Restart"); // Reset + elements_button_left(canvas, "Exit"); } } @@ -130,7 +166,8 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { view_attack->view, FuzzerViewAttackModel * model, { - if(!model->attack_enabled) { + if(model->attack_state == FuzzerAttackStateIdle) { + // TimeDelay if(event->type == InputTypeShort) { if(model->time_delay > FUZZ_TIME_DELAY_MIN) { model->time_delay--; @@ -142,6 +179,11 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { model->time_delay = FUZZ_TIME_DELAY_MIN; } } + } else if( + (model->attack_state == FuzzerAttackStateEnd) && + (event->type == InputTypeShort)) { + // Exit if Ended + view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context); } }, true); @@ -151,7 +193,8 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { view_attack->view, FuzzerViewAttackModel * model, { - if(!model->attack_enabled) { + if(model->attack_state == FuzzerAttackStateIdle) { + // TimeDelay if(event->type == InputTypeShort) { if(model->time_delay < FUZZ_TIME_DELAY_MAX) { model->time_delay++; @@ -162,6 +205,8 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { model->time_delay = FUZZ_TIME_DELAY_MAX; } } + } else { + // Nothing } }, true); @@ -201,7 +246,7 @@ FuzzerViewAttack* fuzzer_view_attack_alloc() { { model->time_delay = FUZZ_TIME_DELAY_MIN; model->uid = malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); - model->attack_enabled = false; + model->attack_state = FuzzerAttackStateOff; strcpy(model->uid, "Not_set"); model->attack_name = "Not_set"; diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index e1aa4edae..41fd857bf 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -1,7 +1,10 @@ #pragma once #include + #include "../helpers/fuzzer_custom_event.h" +#include "../helpers/fuzzer_types.h" + #include "../lib/worker/protocol.h" typedef struct FuzzerViewAttack FuzzerViewAttack; @@ -26,6 +29,12 @@ void fuzzer_view_attack_reset_data( void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid); -void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack); +void fuzzer_view_attack_start(FuzzerViewAttack* view); + +void fuzzer_view_attack_stop(FuzzerViewAttack* view); + +void fuzzer_view_attack_pause(FuzzerViewAttack* view); + +void fuzzer_view_attack_end(FuzzerViewAttack* view); uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); \ No newline at end of file From 6a9f396663092a90241399148a102bdaef57739f Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 20:24:38 +0300 Subject: [PATCH 055/370] Fuzzer App: Some description --- .../pacs_fuzzer/lib/worker/fake_worker.c | 293 +++++++++--------- .../pacs_fuzzer/lib/worker/fake_worker.h | 111 ++++++- .../pacs_fuzzer/lib/worker/protocol.h | 22 ++ .../scenes/fuzzer_scene_field_editor.c | 2 +- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 5 +- 5 files changed, 268 insertions(+), 165 deletions(-) diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 97f632085..9d3d89cdf 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -56,22 +56,22 @@ struct FuzzerWorker { void* end_context; }; -static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { - furi_assert(worker); - furi_assert(worker->protocol); +static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) { + furi_assert(instance); + furi_assert(instance->protocol); bool res = false; - const FuzzerProtocol* protocol = worker->protocol; + const FuzzerProtocol* protocol = instance->protocol; - switch(worker->attack_type) { + switch(instance->attack_type) { case FuzzerWorkerAttackTypeDefaultDict: if(next) { - worker->index++; + instance->index++; } - if(worker->index < protocol->dict.len) { + if(instance->index < protocol->dict.len) { memcpy( - worker->payload, - &protocol->dict.val[worker->index * protocol->data_size], + instance->payload, + &protocol->dict.val[instance->index * protocol->data_size], protocol->data_size); res = true; } @@ -79,14 +79,14 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { case FuzzerWorkerAttackTypeLoadFileCustomUids: { if(next) { - worker->index++; + instance->index++; } uint8_t str_len = protocol->data_size * 2 + 1; FuriString* data_str = furi_string_alloc(); while(true) { furi_string_reset(data_str); - if(!stream_read_line(worker->uids_stream, data_str)) { - stream_rewind(worker->uids_stream); + if(!stream_read_line(instance->uids_stream, data_str)) { + stream_rewind(instance->uids_stream); // TODO Check empty file & close stream and storage break; } else if(furi_string_get_char(data_str, 0) == '#') { @@ -103,7 +103,7 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { if(!hex_char_to_uint8( furi_string_get_cstr(data_str)[i * 2], furi_string_get_cstr(data_str)[i * 2 + 1], - &worker->payload[i])) { + &instance->payload[i])) { parse_ok = false; break; } @@ -117,8 +117,8 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { break; case FuzzerWorkerAttackTypeLoadFile: - if(worker->payload[worker->index] != 0xFF) { - worker->payload[worker->index]++; + if(instance->payload[instance->index] != 0xFF) { + instance->payload[instance->index]++; res = true; } @@ -129,15 +129,15 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { } #if defined(RFID_125_PROTOCOL) protocol_dict_set_data( - worker->protocols_items, worker->protocol_id, worker->payload, MAX_PAYLOAD_SIZE); + instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE); #else - ibutton_key_set_protocol_id(worker->key, worker->protocol_id); + ibutton_key_set_protocol_id(instance->key, instance->protocol_id); iButtonEditableData data; - ibutton_protocols_get_editable_data(worker->protocols_items, worker->key, &data); + ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data); // TODO check data.size logic data.size = MAX_PAYLOAD_SIZE; - memcpy(data.ptr, worker->payload, MAX_PAYLOAD_SIZE); // data.size); + memcpy(data.ptr, instance->payload, MAX_PAYLOAD_SIZE); // data.size); #endif return res; } @@ -145,69 +145,69 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { static void fuzzer_worker_on_tick_callback(void* context) { furi_assert(context); - FuzzerWorker* worker = context; + FuzzerWorker* instance = context; - if(worker->treead_running) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_stop(worker->proto_worker); + lfrfid_worker_stop(instance->proto_worker); #else - ibutton_worker_stop(worker->proto_worker); + ibutton_worker_stop(instance->proto_worker); #endif } - if(!fuzzer_worker_load_key(worker, true)) { - fuzzer_worker_pause(worker); // XXX - if(worker->end_callback) { - worker->end_callback(worker->end_context); + if(!fuzzer_worker_load_key(instance, true)) { + fuzzer_worker_pause(instance); // XXX + if(instance->end_callback) { + instance->end_callback(instance->end_context); } } else { - if(worker->treead_running) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); + lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); #else - ibutton_worker_emulate_start(worker->proto_worker, worker->key); + ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif } - if(worker->tick_callback) { - worker->tick_callback(worker->tick_context); + if(instance->tick_callback) { + instance->tick_callback(instance->tick_context); } } } -void fuzzer_worker_get_current_key(FuzzerWorker* worker, FuzzerPayload* output_key) { - furi_assert(worker); +void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key) { + furi_assert(instance); furi_assert(output_key); - furi_assert(worker->protocol); + furi_assert(instance->protocol); - output_key->data_size = worker->protocol->data_size; + output_key->data_size = instance->protocol->data_size; output_key->data = malloc(sizeof(output_key->data_size)); - memcpy(output_key->data, worker->payload, worker->protocol->data_size); + memcpy(output_key->data, instance->payload, instance->protocol->data_size); } -static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtocolsID protocol_index) { - worker->protocol = &fuzzer_proto_items[protocol_index]; +static void fuzzer_worker_set_protocol(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) { + instance->protocol = &fuzzer_proto_items[protocol_index]; #if defined(RFID_125_PROTOCOL) - worker->protocol_id = - protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); + instance->protocol_id = + protocol_dict_get_protocol_by_name(instance->protocols_items, instance->protocol->name); #else // TODO iButtonProtocolIdInvalid check - worker->protocol_id = - ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); + instance->protocol_id = + ibutton_protocols_get_id_by_name(instance->protocols_items, instance->protocol->name); #endif } -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index) { - furi_assert(worker); +bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) { + furi_assert(instance); bool res = false; - fuzzer_worker_set_protocol(worker, protocol_index); + fuzzer_worker_set_protocol(instance, protocol_index); - worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; - worker->index = 0; + instance->attack_type = FuzzerWorkerAttackTypeDefaultDict; + instance->index = 0; - if(!fuzzer_worker_load_key(worker, false)) { - worker->attack_type = FuzzerWorkerAttackTypeMax; + if(!fuzzer_worker_load_key(instance, false)) { + instance->attack_type = FuzzerWorkerAttackTypeMax; } else { res = true; } @@ -215,31 +215,31 @@ bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_ return res; } -bool fuzzer_worker_attack_file_dict( - FuzzerWorker* worker, +bool fuzzer_worker_init_attack_file_dict( + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, FuriString* file_path) { - furi_assert(worker); + furi_assert(instance); furi_assert(file_path); bool res = false; - fuzzer_worker_set_protocol(worker, protocol_index); + fuzzer_worker_set_protocol(instance, protocol_index); Storage* storage = furi_record_open(RECORD_STORAGE); - worker->uids_stream = buffered_file_stream_alloc(storage); + instance->uids_stream = buffered_file_stream_alloc(storage); if(!buffered_file_stream_open( - worker->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - buffered_file_stream_close(worker->uids_stream); + instance->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(instance->uids_stream); return res; } - worker->attack_type = FuzzerWorkerAttackTypeLoadFileCustomUids; - worker->index = 0; + instance->attack_type = FuzzerWorkerAttackTypeLoadFileCustomUids; + instance->index = 0; - if(!fuzzer_worker_load_key(worker, false)) { - worker->attack_type = FuzzerWorkerAttackTypeMax; - buffered_file_stream_close(worker->uids_stream); + if(!fuzzer_worker_load_key(instance, false)) { + instance->attack_type = FuzzerWorkerAttackTypeMax; + buffered_file_stream_close(instance->uids_stream); furi_record_close(RECORD_STORAGE); } else { res = true; @@ -248,20 +248,20 @@ bool fuzzer_worker_attack_file_dict( return res; } -bool fuzzer_worker_attack_bf_byte( - FuzzerWorker* worker, +bool fuzzer_worker_init_attack_bf_byte( + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, const uint8_t* uid, uint8_t chusen) { - furi_assert(worker); + furi_assert(instance); bool res = false; - fuzzer_worker_set_protocol(worker, protocol_index); + fuzzer_worker_set_protocol(instance, protocol_index); - worker->attack_type = FuzzerWorkerAttackTypeLoadFile; - worker->index = chusen; + instance->attack_type = FuzzerWorkerAttackTypeLoadFile; + instance->index = chusen; - memcpy(worker->payload, uid, worker->protocol->data_size); + memcpy(instance->payload, uid, instance->protocol->data_size); res = true; @@ -270,49 +270,49 @@ bool fuzzer_worker_attack_bf_byte( // TODO make it protocol independent bool fuzzer_worker_load_key_from_file( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, const char* filename) { - furi_assert(worker); + furi_assert(instance); bool res = false; - fuzzer_worker_set_protocol(worker, protocol_index); + fuzzer_worker_set_protocol(instance, protocol_index); #if defined(RFID_125_PROTOCOL) - ProtocolId loaded_proto_id = lfrfid_dict_file_load(worker->protocols_items, filename); + ProtocolId loaded_proto_id = lfrfid_dict_file_load(instance->protocols_items, filename); if(loaded_proto_id == PROTOCOL_NO) { // Err Cant load file FURI_LOG_W(TAG, "Cant load file"); - } else if(worker->protocol_id != loaded_proto_id) { // Err wrong protocol + } else if(instance->protocol_id != loaded_proto_id) { // Err wrong protocol FURI_LOG_W(TAG, "Wrong protocol"); FURI_LOG_W( TAG, "Selected: %s Loaded: %s", - worker->protocol->name, - protocol_dict_get_name(worker->protocols_items, loaded_proto_id)); + instance->protocol->name, + protocol_dict_get_name(instance->protocols_items, loaded_proto_id)); } else { protocol_dict_get_data( - worker->protocols_items, worker->protocol_id, worker->payload, MAX_PAYLOAD_SIZE); + instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE); res = true; } #else - if(!ibutton_protocols_load(worker->protocols_items, worker->key, filename)) { + if(!ibutton_protocols_load(instance->protocols_items, instance->key, filename)) { // Err Cant load file FURI_LOG_W(TAG, "Cant load file"); } else { - if(worker->protocol_id != ibutton_key_get_protocol_id(worker->key)) { + if(instance->protocol_id != ibutton_key_get_protocol_id(instance->key)) { // Err wrong protocol FURI_LOG_W(TAG, "Wrong protocol"); FURI_LOG_W( TAG, "Selected: %s Loaded: %s", - worker->protocol->name, + instance->protocol->name, ibutton_protocols_get_name( - worker->protocols_items, ibutton_key_get_protocol_id(worker->key))); + instance->protocols_items, ibutton_key_get_protocol_id(instance->key))); } else { iButtonEditableData data; - ibutton_protocols_get_editable_data(worker->protocols_items, worker->key, &data); - memcpy(worker->payload, data.ptr, data.size); + ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data); + memcpy(instance->payload, data.ptr, data.size); res = true; } } @@ -322,140 +322,141 @@ bool fuzzer_worker_load_key_from_file( } FuzzerWorker* fuzzer_worker_alloc() { - FuzzerWorker* worker = malloc(sizeof(FuzzerWorker)); + FuzzerWorker* instance = malloc(sizeof(FuzzerWorker)); #if defined(RFID_125_PROTOCOL) - worker->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + instance->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); - worker->proto_worker = lfrfid_worker_alloc(worker->protocols_items); + instance->proto_worker = lfrfid_worker_alloc(instance->protocols_items); #else - worker->protocols_items = ibutton_protocols_alloc(); - worker->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(worker->protocols_items)); + instance->protocols_items = ibutton_protocols_alloc(); + instance->key = + ibutton_key_alloc(ibutton_protocols_get_max_data_size(instance->protocols_items)); - worker->proto_worker = ibutton_worker_alloc(worker->protocols_items); + instance->proto_worker = ibutton_worker_alloc(instance->protocols_items); #endif - worker->attack_type = FuzzerWorkerAttackTypeMax; - worker->index = 0; - worker->treead_running = false; + instance->attack_type = FuzzerWorkerAttackTypeMax; + instance->index = 0; + instance->treead_running = false; - memset(worker->payload, 0x00, sizeof(worker->payload)); + memset(instance->payload, 0x00, sizeof(instance->payload)); - worker->timeer_delay = FUZZ_TIME_DELAY_DEFAULT; + instance->timeer_delay = FUZZ_TIME_DELAY_DEFAULT; - worker->timer = - furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypePeriodic, worker); + instance->timer = + furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypePeriodic, instance); - return worker; + return instance; } -void fuzzer_worker_free(FuzzerWorker* worker) { - furi_assert(worker); +void fuzzer_worker_free(FuzzerWorker* instance) { + furi_assert(instance); - fuzzer_worker_stop(worker); + fuzzer_worker_stop(instance); - furi_timer_free(worker->timer); + furi_timer_free(instance->timer); #if defined(RFID_125_PROTOCOL) - lfrfid_worker_free(worker->proto_worker); + lfrfid_worker_free(instance->proto_worker); - protocol_dict_free(worker->protocols_items); + protocol_dict_free(instance->protocols_items); #else - ibutton_worker_free(worker->proto_worker); + ibutton_worker_free(instance->proto_worker); - ibutton_key_free(worker->key); - ibutton_protocols_free(worker->protocols_items); + ibutton_key_free(instance->key); + ibutton_protocols_free(instance->protocols_items); #endif - free(worker); + free(instance); } -bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { - furi_assert(worker); +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { + furi_assert(instance); - if(worker->attack_type < FuzzerWorkerAttackTypeMax) { - worker->timeer_delay = timer_dellay; + if(instance->attack_type < FuzzerWorkerAttackTypeMax) { + instance->timeer_delay = timer_dellay; - furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(timer_dellay * 100)); - if(!worker->treead_running) { + if(!instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_start_thread(worker->proto_worker); + lfrfid_worker_start_thread(instance->proto_worker); #else - ibutton_worker_start_thread(worker->proto_worker); + ibutton_worker_start_thread(instance->proto_worker); #endif FURI_LOG_D(TAG, "Worker Starting"); - worker->treead_running = true; + instance->treead_running = true; } else { FURI_LOG_D(TAG, "Worker UnPaused"); } #if defined(RFID_125_PROTOCOL) - // lfrfid_worker_start_thread(worker->proto_worker); - lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); + // lfrfid_worker_start_thread(instance->proto_worker); + lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); #else - // ibutton_worker_start_thread(worker->proto_worker); - ibutton_worker_emulate_start(worker->proto_worker, worker->key); + // ibutton_worker_start_thread(instance->proto_worker); + ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif return true; } return false; } -void fuzzer_worker_pause(FuzzerWorker* worker) { - furi_assert(worker); +void fuzzer_worker_pause(FuzzerWorker* instance) { + furi_assert(instance); - furi_timer_stop(worker->timer); + furi_timer_stop(instance->timer); - if(worker->treead_running) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_stop(worker->proto_worker); + lfrfid_worker_stop(instance->proto_worker); #else - ibutton_worker_stop(worker->proto_worker); + ibutton_worker_stop(instance->proto_worker); #endif FURI_LOG_D(TAG, "Worker Paused"); } } -void fuzzer_worker_stop(FuzzerWorker* worker) { - furi_assert(worker); +void fuzzer_worker_stop(FuzzerWorker* instance) { + furi_assert(instance); - furi_timer_stop(worker->timer); + furi_timer_stop(instance->timer); - if(worker->treead_running) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_stop(worker->proto_worker); - lfrfid_worker_stop_thread(worker->proto_worker); + lfrfid_worker_stop(instance->proto_worker); + lfrfid_worker_stop_thread(instance->proto_worker); #else - ibutton_worker_stop(worker->proto_worker); - ibutton_worker_stop_thread(worker->proto_worker); + ibutton_worker_stop(instance->proto_worker); + ibutton_worker_stop_thread(instance->proto_worker); #endif FURI_LOG_D(TAG, "Worker Stopping"); - worker->treead_running = false; + instance->treead_running = false; } - if(worker->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) { - buffered_file_stream_close(worker->uids_stream); + if(instance->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) { + buffered_file_stream_close(instance->uids_stream); furi_record_close(RECORD_STORAGE); - worker->attack_type = FuzzerWorkerAttackTypeMax; + instance->attack_type = FuzzerWorkerAttackTypeMax; } // TODO anything else } void fuzzer_worker_set_uid_chaged_callback( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerWorkerUidChagedCallback callback, void* context) { - furi_assert(worker); - worker->tick_callback = callback; - worker->tick_context = context; + furi_assert(instance); + instance->tick_callback = callback; + instance->tick_context = context; } void fuzzer_worker_set_end_callback( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerWorkerEndCallback callback, void* context) { - furi_assert(worker); - worker->end_callback = callback; - worker->end_context = context; + furi_assert(instance); + instance->end_callback = callback; + instance->end_context = context; } diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 2f8733393..04635169b 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -17,42 +17,121 @@ typedef void (*FuzzerWorkerEndCallback)(void* context); typedef struct FuzzerWorker FuzzerWorker; +/** + * Allocate FuzzerWorker + * + * @return FuzzerWorker* pointer to FuzzerWorker + */ FuzzerWorker* fuzzer_worker_alloc(); -void fuzzer_worker_free(FuzzerWorker* worker); +/** + * Free FuzzerWorker + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_free(FuzzerWorker* instance); -bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); +/** + * Start or continue emulation + * + * @param instance Pointer to a FuzzerWorker + * @param timer_dellay Emulation time of one UID in tenths of a second + * @return bool True if emulation has started + */ +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay); -void fuzzer_worker_stop(FuzzerWorker* worker); +/** + * Stop emulation and deinit worker + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_stop(FuzzerWorker* instance); -void fuzzer_worker_pause(FuzzerWorker* worker); +/** + * Suspend emulation + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_pause(FuzzerWorker* instance); -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index); +/** + * Init attack by default dictionary + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index); -bool fuzzer_worker_attack_bf_byte( - FuzzerWorker* worker, +/** + * Init attack by custom dictionary + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param file_path file path to the dictionary + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_file_dict( + FuzzerWorker* instance, + FuzzerProtocolsID protocol_index, + FuriString* file_path); + +/** + * Init attack brute force one of byte + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param uid UID for brute force + * @param chosen index of chusen byte + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_bf_byte( + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, const uint8_t* uid, uint8_t chusen); -bool fuzzer_worker_attack_file_dict( - FuzzerWorker* worker, - FuzzerProtocolsID protocol_index, - FuriString* file_path); - -void fuzzer_worker_get_current_key(FuzzerWorker* worker, FuzzerPayload* output_key); +/** + * Get current UID + * + * @param instance Pointer to a FuzzerWorker + * @param output_key Pointer to a FuzzerWorker, memory for data will be allocated + */ +void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key); +/** + * Load UID from Flipper Format Key file + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param filename file path to the key file + * @return bool True if loading is successful + */ bool fuzzer_worker_load_key_from_file( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, const char* filename); +/** + * Set callback for uid changed + * + * @param instance Pointer to a FuzzerWorker + * @param callback Callback for uid changed + * @param context Context for callback + */ void fuzzer_worker_set_uid_chaged_callback( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerWorkerUidChagedCallback callback, void* context); +/** + * Set callback for end of emulation + * + * @param instance Pointer to a FuzzerWorker + * @param callback Callback for end of emulation + * @param context Context for callback + */ void fuzzer_worker_set_end_callback( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerWorkerEndCallback callback, void* context); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index 0de1223ff..a2893123e 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -30,12 +30,34 @@ struct FuzzerPayload { uint8_t data_size; }; +/** + * Get maximum length of UID among all supported protocols + * @return Maximum length of UID + */ uint8_t fuzzer_proto_get_max_data_size(); +/** + * Get protocol name based on its index + * @param index protocol index + * @return pointer to a string containing the name + */ const char* fuzzer_proto_get_name(FuzzerProtocolsID index); +/** + * Get number of protocols + * @return number of protocols + */ uint8_t fuzzer_proto_get_count_of_protocols(); +/** + * Get menu label based on its index + * @param index menu index + * @return pointer to a string containing the menu label + */ const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index); +/** + * Get number of menu items + * @return number of menu items + */ uint8_t fuzzer_proto_get_count_of_menu_items(); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index f8adae0df..637eff2d7 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -38,7 +38,7 @@ bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == FuzzerCustomEventViewFieldEditorOk) { // TODO - if(fuzzer_worker_attack_bf_byte( + if(fuzzer_worker_init_attack_bf_byte( app->worker, app->fuzzer_state.proto_index, fuzzer_view_field_editor_get_uid(app->field_editor_view), diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index e27bc0776..5ad37654a 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -81,7 +81,8 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { switch(app->fuzzer_state.menu_index) { case FuzzerMainMenuIndexDefaultValues: - loading_ok = fuzzer_worker_attack_dict(app->worker, app->fuzzer_state.proto_index); + loading_ok = + fuzzer_worker_init_attack_dict(app->worker, app->fuzzer_state.proto_index); if(!loading_ok) { // error @@ -108,7 +109,7 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { if(!fuzzer_scene_main_load_custom_dict(app)) { break; } else { - loading_ok = fuzzer_worker_attack_file_dict( + loading_ok = fuzzer_worker_init_attack_file_dict( app->worker, app->fuzzer_state.proto_index, app->file_path); } break; From b346487e760e8d55505a91e117e801344237fd39 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 21:19:32 +0300 Subject: [PATCH 056/370] Fuzzer App: BFCustomerID attack, some fix --- .../pacs_fuzzer/lib/worker/protocol.c | 28 +++++++++++++------ .../pacs_fuzzer/lib/worker/protocol.h | 18 ++++++++---- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 22 ++++++++++++--- applications/external/pacs_fuzzer/todo.md | 2 +- .../external/pacs_fuzzer/views/main_menu.c | 2 +- 5 files changed, 53 insertions(+), 19 deletions(-) diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index b9f9e6bf6..c295289ae 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -228,10 +228,18 @@ const FuzzerProtocol fuzzer_proto_items[] = { }; #endif -const char* fuzzer_attack_names[] = { - [FuzzerMainMenuIndexDefaultValues] = "Default Values", - [FuzzerMainMenuIndexLoadFile] = "Load File", - [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", +typedef struct { + const char* menu_label; + FuzzerAttackId attack_id; +} FuzzerMenuItems; + +const FuzzerMenuItems fuzzer_menu_items[] = { + {"Default Values", FuzzerAttackIdDefaultValues}, +#ifdef RFID_125_PROTOCOL + {"BF Customer ID", FuzzerAttackIdBFCustomerID}, +#endif + {"Load File", FuzzerAttackIdLoadFile}, + {"Load UIDs from file", FuzzerAttackIdLoadFileCustomUids}, }; const char* fuzzer_proto_get_name(FuzzerProtocolsID index) { @@ -246,10 +254,14 @@ uint8_t fuzzer_proto_get_max_data_size() { return MAX_PAYLOAD_SIZE; } -const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index) { - return fuzzer_attack_names[index]; +const char* fuzzer_proto_get_menu_label(uint8_t index) { + return fuzzer_menu_items[index].menu_label; +} + +FuzzerAttackId fuzzer_proto_get_attack_id_by_index(uint8_t index) { + return fuzzer_menu_items[index].attack_id; } uint8_t fuzzer_proto_get_count_of_menu_items() { - return COUNT_OF(fuzzer_attack_names); -} \ No newline at end of file + return COUNT_OF(fuzzer_menu_items); +} diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index a2893123e..4c2c70e0c 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -20,10 +20,11 @@ typedef enum { } FuzzerProtocolsID; typedef enum { - FuzzerMainMenuIndexDefaultValues = 0, - FuzzerMainMenuIndexLoadFile, - FuzzerMainMenuIndexLoadFileCustomUids, -} FuzzerMainMenuIndex; + FuzzerAttackIdDefaultValues = 0, + FuzzerAttackIdLoadFile, + FuzzerAttackIdLoadFileCustomUids, + FuzzerAttackIdBFCustomerID, +} FuzzerAttackId; struct FuzzerPayload { uint8_t* data; @@ -54,7 +55,14 @@ uint8_t fuzzer_proto_get_count_of_protocols(); * @param index menu index * @return pointer to a string containing the menu label */ -const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index); +const char* fuzzer_proto_get_menu_label(uint8_t index); + +/** + * Get FuzzerAttackId based on its index + * @param index menu index + * @return FuzzerAttackId + */ +FuzzerAttackId fuzzer_proto_get_attack_id_by_index(uint8_t index); /** * Get number of menu items diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 5ad37654a..699037430 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -77,9 +77,11 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { // TODO error logic bool loading_ok = false; + uint8_t d_size = fuzzer_proto_get_max_data_size(); + uint8_t* uid; - switch(app->fuzzer_state.menu_index) { - case FuzzerMainMenuIndexDefaultValues: + switch(fuzzer_proto_get_attack_id_by_index(app->fuzzer_state.menu_index)) { + case FuzzerAttackIdDefaultValues: loading_ok = fuzzer_worker_init_attack_dict(app->worker, app->fuzzer_state.proto_index); @@ -88,8 +90,20 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { // error } break; + case FuzzerAttackIdBFCustomerID: + uid = malloc(d_size); + memset(uid, 0x00, d_size); - case FuzzerMainMenuIndexLoadFile: + loading_ok = fuzzer_worker_init_attack_bf_byte( + app->worker, app->fuzzer_state.proto_index, uid, 0); + + free(uid); + if(!loading_ok) { + // error + } + break; + + case FuzzerAttackIdLoadFile: if(!fuzzer_scene_main_load_key(app)) { break; } else { @@ -105,7 +119,7 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { } break; - case FuzzerMainMenuIndexLoadFileCustomUids: + case FuzzerAttackIdLoadFileCustomUids: if(!fuzzer_scene_main_load_custom_dict(app)) { break; } else { diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index dd6f4fc69..7afd3fad8 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -13,7 +13,7 @@ #### App functionality -- [ ] Add `BFCustomerID` attack +- [x] Add `BFCustomerID` attack - [ ] Save key logic ## Code Improvement diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 8c4c23603..49b0a0d9c 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -75,7 +75,7 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { canvas_draw_str_aligned( canvas, 64, 36, AlignCenter, AlignTop, fuzzer_proto_get_menu_label(model->menu_index)); - if(model->menu_index < model->menu_max) { + if(model->menu_index < (model->menu_max - 1)) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( canvas, From 237d2ba1a01ff1ea371d92bdaf0df7527c1ac845 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:14:23 +0300 Subject: [PATCH 057/370] Fuzzer App: field_editor view V2 --- .../pacs_fuzzer/icons/ButtonLeft_4x7.png | Bin 0 -> 1415 bytes .../pacs_fuzzer/icons/ButtonRight_4x7.png | Bin 0 -> 1839 bytes .../external/pacs_fuzzer/icons/Ok_btn_9x9.png | Bin 0 -> 3605 bytes .../pacs_fuzzer/icons/Pin_arrow_up_7x9.png | Bin 0 -> 3603 bytes .../pacs_fuzzer/icons/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes applications/external/pacs_fuzzer/todo.md | 2 +- .../external/pacs_fuzzer/views/attack.c | 42 +++---- .../external/pacs_fuzzer/views/field_editor.c | 108 ++++++++++++------ .../external/pacs_fuzzer/views/main_menu.c | 1 - 9 files changed, 94 insertions(+), 59 deletions(-) create mode 100644 applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png create mode 100644 applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png create mode 100644 applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png create mode 100644 applications/external/pacs_fuzzer/icons/Pin_arrow_up_7x9.png create mode 100644 applications/external/pacs_fuzzer/icons/Pin_back_arrow_10x8.png diff --git a/applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png b/applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4655d43247083aa705620e9836ac415b42ca46 GIT binary patch literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png b/applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1c74c1c0038ea55172f19ac875003fc80c2d06 GIT binary patch literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png b/applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..9a1539da2049f12f7b25f96b11a9c40cd8227302 GIT binary patch literal 3605 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 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 7afd3fad8..9070e6363 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -22,7 +22,7 @@ - [x] Rewrite `gui_const` logic - [ ] Separate protocol name from `fuzzer_proto_items` - [x] Icon in dialog - - [ ] Description and buttons in `field_editor` view + - [x] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` - [x] prototype - [x] UID diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index ad99f0132..13e2325fd 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -18,7 +18,7 @@ typedef struct { const char* attack_name; const char* protocol_name; FuzzerAttackState attack_state; - char* uid; + FuriString* uid_str; } FuzzerViewAttackModel; void fuzzer_view_attack_reset_data( @@ -34,38 +34,25 @@ void fuzzer_view_attack_reset_data( model->attack_name = attack_name; model->protocol_name = protocol_name; model->attack_state = FuzzerAttackStateIdle; - strcpy(model->uid, "Not_set"); + furi_string_set_str(model->uid_str, "Not_set"); }, true); } void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid) { furi_assert(view); - - // TODO fix it - uint8_t* data = malloc(uid.data_size); - memcpy(data, uid.data, uid.data_size); + furi_assert(uid.data); with_view_model( view->view, FuzzerViewAttackModel * model, { - snprintf( - model->uid, - uid.data_size * 3, - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - data[0], - data[1], - data[2], - data[3], - data[4], - data[5], - data[6], - data[7]); + furi_string_printf(model->uid_str, "%02X", uid.data[0]); + for(uint8_t i = 1; i < uid.data_size; i++) { + furi_string_cat_printf(model->uid_str, ":%02X", uid.data[i]); + } }, true); - - free(data); } void fuzzer_view_attack_start(FuzzerViewAttack* view) { @@ -133,10 +120,11 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); canvas_set_font(canvas, FontPrimary); - if(128 < canvas_string_width(canvas, model->uid)) { + if(128 < canvas_string_width(canvas, furi_string_get_cstr(model->uid_str))) { canvas_set_font(canvas, FontSecondary); } - canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, model->uid); + canvas_draw_str_aligned( + canvas, 64, 38, AlignCenter, AlignTop, furi_string_get_cstr(model->uid_str)); canvas_set_font(canvas, FontSecondary); if(model->attack_state == FuzzerAttackStateRunning) { @@ -245,10 +233,11 @@ FuzzerViewAttack* fuzzer_view_attack_alloc() { FuzzerViewAttackModel * model, { model->time_delay = FUZZ_TIME_DELAY_MIN; - model->uid = malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); + model->uid_str = furi_string_alloc_set_str("Not_set"); + // malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); model->attack_state = FuzzerAttackStateOff; - strcpy(model->uid, "Not_set"); + // strcpy(model->uid_str, "Not_set"); model->attack_name = "Not_set"; model->protocol_name = "Not_set"; }, @@ -260,7 +249,10 @@ void fuzzer_view_attack_free(FuzzerViewAttack* view_attack) { furi_assert(view_attack); with_view_model( - view_attack->view, FuzzerViewAttackModel * model, { free(model->uid); }, true); + view_attack->view, + FuzzerViewAttackModel * model, + { furi_string_free(model->uid_str); }, + true); view_free(view_attack->view); free(view_attack); } diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index 53e15e152..07a19ae0e 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -5,8 +5,21 @@ #include #include +#define FIELD_EDITOR_V2 + +#define GUI_DISPLAY_WIDTH 128 +#define GUI_DISPLAY_HEIGHT 64 + +#define GUI_DISPLAY_HORIZONTAL_CENTER 64 +#define GUI_DISPLAY_VERTICAL_CENTER 32 + #define UID_STR_LENGTH 25 + +#ifdef FIELD_EDITOR_V2 +#define EDITOR_STRING_Y 38 +#else #define EDITOR_STRING_Y 50 +#endif struct FuzzerViewFieldEditor { View* view; @@ -14,7 +27,6 @@ struct FuzzerViewFieldEditor { void* context; }; -// TODO model typedef struct { uint8_t* uid; uint8_t uid_size; @@ -72,15 +84,55 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); +#ifdef FIELD_EDITOR_V2 + canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Left and right: select byte"); - canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "Up and down: adjust byte"); + + canvas_draw_icon(canvas, 2, 4, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 8, 4, &I_ButtonRight_4x7); + + canvas_draw_icon_ex(canvas, 62, 3, &I_Pin_arrow_up_7x9, IconRotation180); + canvas_draw_icon(canvas, 69, 3, &I_Pin_arrow_up_7x9); + + canvas_draw_str(canvas, 14, 10, "select byte"); + canvas_draw_str(canvas, 79, 10, "adjust byte"); char msg_index[18]; canvas_set_font(canvas, FontPrimary); snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_HORIZONTAL_CENTER, 24, AlignCenter, AlignBottom, msg_index); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 4, 52, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 85, 52, &I_Ok_btn_9x9); + + canvas_draw_str(canvas, 16, 60, "Back"); + canvas_draw_str(canvas, 96, 60, "Attack"); +#else + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + 5, + AlignCenter, + AlignTop, + "Left and right: select byte"); + canvas_draw_str_aligned( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + 15, + AlignCenter, + AlignTop, + "Up and down: adjust byte"); + + char msg_index[18]; + canvas_set_font(canvas, FontPrimary); + snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_HORIZONTAL_CENTER, 28, AlignCenter, AlignTop, msg_index); +#endif // ####### Editor ####### FuriString* temp_s = model->uid_str; canvas_set_font(canvas, FontSecondary); @@ -88,7 +140,7 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m furi_string_reset(temp_s); for(int i = -3; i != 0; i++) { if(0 <= (model->index + i)) { - furi_string_cat_printf(temp_s, "%2X ", model->uid[model->index + i]); + furi_string_cat_printf(temp_s, "%02X ", model->uid[model->index + i]); } } canvas_draw_str_aligned( @@ -97,7 +149,7 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m furi_string_reset(temp_s); for(int i = 1; i != 4; i++) { if((model->index + i) < model->uid_size) { - furi_string_cat_printf(temp_s, " %2X", model->uid[model->index + i]); + furi_string_cat_printf(temp_s, " %02X", model->uid[model->index + i]); } } canvas_draw_str_aligned( @@ -108,16 +160,31 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m furi_string_reset(temp_s); furi_string_cat_printf(temp_s, "<%02X>", model->uid[model->index]); canvas_draw_str_aligned( - canvas, 64, EDITOR_STRING_Y, AlignCenter, AlignBottom, furi_string_get_cstr(temp_s)); + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + EDITOR_STRING_Y, + AlignCenter, + AlignBottom, + furi_string_get_cstr(temp_s)); uint16_t w = canvas_string_width(canvas, furi_string_get_cstr(temp_s)); w -= 11; // '<' & '>' w /= 2; if(model->lo) { - canvas_draw_line(canvas, 64 + 1, EDITOR_STRING_Y + 2, 64 + w, EDITOR_STRING_Y + 2); + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER + 1, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER + w, + EDITOR_STRING_Y + 2); } else { - canvas_draw_line(canvas, 64 - w, EDITOR_STRING_Y + 2, 64 - 1, EDITOR_STRING_Y + 2); + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER - w, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER - 1, + EDITOR_STRING_Y + 2); } // ####### Editor ####### } @@ -211,29 +278,6 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { void fuzzer_view_field_editor_enter(void* context) { furi_assert(context); - // TODO delete only for debug - // FuzzerViewFieldEditor* view_edit = context; - // uint8_t temp[8] = { - // 0x12, - // 0x34, - // 0x56, - // 0x78, - // 0x90, - // 0xAB, - // 0xCD, - // 0xEF, - // }; - // with_view_model( - // view_edit->view, - // FuzzerViewFieldEditorModel * model, - // { - // memcpy(model->uid, &temp, 8); - - // // memset(model->uid, 0xCC, 8); - // model->index = 0; - // model->uid_size = 8; - // }, - // true); } void fuzzer_view_field_editor_exit(void* context) { diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 49b0a0d9c..14422145b 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -14,7 +14,6 @@ struct FuzzerViewMain { void* context; }; -// TODO Furi string for procol name typedef struct { uint8_t proto_index; uint8_t menu_index; From a0638588428301fb6e6f4b07b942210d35750cb0 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 13:03:36 +0300 Subject: [PATCH 058/370] Fuzzer App: notifications --- applications/external/pacs_fuzzer/fuzzer.c | 15 +++ applications/external/pacs_fuzzer/fuzzer_i.h | 7 +- .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 3 +- .../pacs_fuzzer/helpers/fuzzer_types.h | 2 + .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 107 ++++++++++++------ .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 33 ++++++ applications/external/pacs_fuzzer/todo.md | 16 ++- 7 files changed, 140 insertions(+), 43 deletions(-) diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index 0a9aa3f7d..c80c18130 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -35,9 +35,16 @@ PacsFuzzerApp* fuzzer_app_alloc() { // Dialog app->dialogs = furi_record_open(RECORD_DIALOGS); + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + // View Dispatcher app->view_dispatcher = view_dispatcher_alloc(); + // Popup + app->popup = popup_alloc(); + view_dispatcher_add_view(app->view_dispatcher, FuzzerViewIDPopup, popup_get_view(app->popup)); + // Main view app->main_view = fuzzer_view_main_alloc(); view_dispatcher_add_view( @@ -88,6 +95,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) { view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDFieldEditor); fuzzer_view_field_editor_free(app->field_editor_view); + // Popup + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDPopup); + popup_free(app->popup); + scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); @@ -97,6 +108,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) { // Close records furi_record_close(RECORD_GUI); + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + furi_string_free(app->file_path); fuzzer_worker_free(app->worker); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 2f24ec431..1dad1608a 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -3,11 +3,13 @@ #include #include -#include #include #include #include +#include + #include +#include #include "scenes/fuzzer_scene.h" #include "views/main_menu.h" @@ -33,9 +35,12 @@ typedef struct { typedef struct { Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; SceneManager* scene_manager; + Popup* popup; DialogsApp* dialogs; FuzzerViewMain* main_view; FuzzerViewAttack* attack_view; diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h index 930029d3c..321187722 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -5,10 +5,11 @@ typedef enum { // FuzzerCustomEvent FuzzerCustomEventViewMainBack = 100, FuzzerCustomEventViewMainOk, + FuzzerCustomEventViewMainPopupErr, FuzzerCustomEventViewAttackBack, FuzzerCustomEventViewAttackOk, - FuzzerCustomEventViewAttackTick, + // FuzzerCustomEventViewAttackTick, // now not use FuzzerCustomEventViewAttackEnd, FuzzerCustomEventViewFieldEditorBack, diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index 259fc2b52..e4661ed7b 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -16,6 +16,8 @@ typedef enum { } FuzzerAttackState; typedef enum { + FuzzerViewIDPopup, + FuzzerViewIDMain, FuzzerViewIDAttack, FuzzerViewIDFieldEditor, diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index 61fa84261..a0bd0e2d3 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -3,23 +3,15 @@ // TODO simlify callbacks and attack state -void fuzzer_scene_attack_worker_tick_callback(void* context) { - furi_assert(context); - PacsFuzzerApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick); -} - -void fuzzer_scene_attack_worker_end_callback(void* context) { - furi_assert(context); - PacsFuzzerApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackEnd); -} - -void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { - furi_assert(context); - PacsFuzzerApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} +const NotificationSequence sequence_one_green_50_on_blink_blue = { + &message_red_255, + &message_delay_50, + &message_red_0, + &message_blink_start_10, + &message_blink_set_color_blue, + &message_do_not_reset, + NULL, +}; static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { furi_assert(app); @@ -34,6 +26,56 @@ static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { free(uid.data); } +static void fuzzer_scene_attack_set_state(PacsFuzzerApp* app, FuzzerAttackState state) { + furi_assert(app); + + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, state); + switch(state) { + case FuzzerAttackStateIdle: + notification_message(app->notifications, &sequence_blink_stop); + fuzzer_view_attack_pause(app->attack_view); + break; + + case FuzzerAttackStateRunning: + notification_message(app->notifications, &sequence_blink_start_blue); + fuzzer_view_attack_start(app->attack_view); + break; + + case FuzzerAttackStateEnd: + notification_message(app->notifications, &sequence_blink_stop); + notification_message(app->notifications, &sequence_single_vibro); + fuzzer_view_attack_end(app->attack_view); + break; + + case FuzzerAttackStateOff: + notification_message(app->notifications, &sequence_blink_stop); + fuzzer_view_attack_stop(app->attack_view); + break; + } +} + +void fuzzer_scene_attack_worker_tick_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + notification_message(app->notifications, &sequence_one_green_50_on_blink_blue); + fuzzer_scene_attack_update_uid(app); + + // view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick); +} + +void fuzzer_scene_attack_worker_end_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackEnd); +} + +void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + void fuzzer_scene_attack_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -68,15 +110,13 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { FuzzerAttackStateRunning) { // Pause if attack running fuzzer_worker_pause(app->worker); - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); - fuzzer_view_attack_pause(app->attack_view); + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle); } else { // Exit fuzzer_worker_stop(app->worker); - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateOff); - fuzzer_view_attack_stop(app->attack_view); + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateOff); if(!scene_manager_previous_scene(app->scene_manager)) { scene_manager_stop(app->scene_manager); view_dispatcher_stop(app->view_dispatcher); @@ -89,28 +129,23 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { // Start or Continue Attack if(fuzzer_worker_start( app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateRunning); - fuzzer_view_attack_start(app->attack_view); + fuzzer_scene_attack_set_state(app, FuzzerAttackStateRunning); } else { // Error? } } else if( scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == FuzzerAttackStateRunning) { - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); - fuzzer_view_attack_pause(app->attack_view); + // Pause if attack running fuzzer_worker_pause(app->worker); // XXX + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle); } consumed = true; - } else if(event.event == FuzzerCustomEventViewAttackTick) { - fuzzer_scene_attack_update_uid(app); - consumed = true; + // } else if(event.event == FuzzerCustomEventViewAttackTick) { + // consumed = true; } else if(event.event == FuzzerCustomEventViewAttackEnd) { - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateEnd); - fuzzer_view_attack_end(app->attack_view); + fuzzer_scene_attack_set_state(app, FuzzerAttackStateEnd); consumed = true; } } @@ -122,6 +157,8 @@ void fuzzer_scene_attack_on_exit(void* context) { furi_assert(context); PacsFuzzerApp* app = context; + // fuzzer_worker_stop(); // XXX + fuzzer_worker_set_uid_chaged_callback(app->worker, NULL, NULL); fuzzer_worker_set_end_callback(app->worker, NULL, NULL); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 699037430..cfa43ad87 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -9,6 +9,12 @@ void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, event); } +void fuzzer_scene_main_error_popup_callback(void* context) { + PacsFuzzerApp* app = context; + notification_message(app->notifications, &sequence_reset_rgb); + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewMainPopupErr); +} + static bool fuzzer_scene_main_load_custom_dict(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -49,6 +55,15 @@ static bool fuzzer_scene_main_load_key(void* context) { return res; } +static void fuzzer_scene_main_show_error(void* context, const char* erre_str) { + furi_assert(context); + PacsFuzzerApp* app = context; + popup_set_header(app->popup, erre_str, 64, 20, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_set_red_255); + notification_message(app->notifications, &sequence_double_vibro); + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDPopup); +} + void fuzzer_scene_main_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -57,6 +72,14 @@ void fuzzer_scene_main_on_enter(void* context) { fuzzer_view_main_update_data(app->main_view, app->fuzzer_state); + // Setup view + Popup* popup = app->popup; + // popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_timeout(popup, 2500); + popup_set_context(popup, app); + popup_set_callback(popup, fuzzer_scene_main_error_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); } @@ -72,6 +95,9 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(app->view_dispatcher); } consumed = true; + } else if(event.event == FuzzerCustomEventViewMainPopupErr) { + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); + consumed = true; } else if(event.event == FuzzerCustomEventViewMainOk) { fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); @@ -88,9 +114,11 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { if(!loading_ok) { // error + fuzzer_scene_main_show_error(app, "Default dictionary\nis empty"); } break; case FuzzerAttackIdBFCustomerID: + // TODO uid = malloc(d_size); memset(uid, 0x00, d_size); @@ -114,6 +142,7 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); FURI_LOG_I("Scene", "Load ok"); } else { + fuzzer_scene_main_show_error(app, "Unsupported protocol\nor broken file"); FURI_LOG_W("Scene", "Load err"); } } @@ -125,6 +154,10 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { } else { loading_ok = fuzzer_worker_init_attack_file_dict( app->worker, app->fuzzer_state.proto_index, app->file_path); + if(!loading_ok) { + fuzzer_scene_main_show_error(app, "Incorrect key format\nor length"); + // error + } } break; diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 9070e6363..98450035c 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -5,10 +5,13 @@ - [ ] Make the "Load File" independent of the current protocol - [x] Add pause - [ ] Switching UIDs if possible -- [ ] Led and sound Notification -- [ ] Error Notification - - [ ] Custom UIDs dict loading - - [ ] Key file loading +- [x] Led and sound Notification + - [x] Led + - [x] Vibro + - [ ] Sound? +- [x] Error Notification + - [x] Custom UIDs dict loading + - [x] Key file loading - [ ] Anything else #### App functionality @@ -20,7 +23,6 @@ - [ ] GUI - [x] Rewrite `gui_const` logic - - [ ] Separate protocol name from `fuzzer_proto_items` - [x] Icon in dialog - [x] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` @@ -30,4 +32,6 @@ - [x] `UID_MAX_SIZE` - [x] Add pause - [x] Fix `Custom dict` attack when ended -- [x] this can be simplified `fuzzer_proto_items` +- [ ] Worker + - [ ] Use `prtocol_id` instead of protocol name + - [x] this can be simplified `fuzzer_proto_items` \ No newline at end of file From d2b0aa8513e9df980d6d1d94261663e5838605c9 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 15:13:50 +0300 Subject: [PATCH 059/370] Fuzzer App: fix time_delay --- applications/external/pacs_fuzzer/fuzzer_i.h | 1 - .../pacs_fuzzer/lib/worker/fake_worker.c | 65 ++++++++++++------- .../pacs_fuzzer/lib/worker/protocol.c | 4 ++ .../pacs_fuzzer/lib/worker/protocol.h | 6 ++ .../pacs_fuzzer/lib/worker/protocol_i.h | 4 ++ .../external/pacs_fuzzer/views/attack.c | 10 +-- 6 files changed, 61 insertions(+), 29 deletions(-) diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 1dad1608a..63bf85d24 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -22,7 +22,6 @@ #include #include "fuzzer_icons.h" -#define FUZZ_TIME_DELAY_MIN (5) #define FUZZ_TIME_DELAY_MAX (80) typedef struct { diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 9d3d89cdf..896088308 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -8,7 +8,6 @@ #include #define TAG "Fuzzer worker" -#define FUZZ_TIME_DELAY_DEFAULT (10) #if defined(RFID_125_PROTOCOL) @@ -39,7 +38,8 @@ struct FuzzerWorker { const FuzzerProtocol* protocol; FuzzerWorkerAttackType attack_type; - uint8_t timeer_delay; + uint8_t timer_idle_delay; + uint8_t timer_emu_delay; uint8_t payload[MAX_PAYLOAD_SIZE]; Stream* uids_stream; @@ -47,6 +47,7 @@ struct FuzzerWorker { uint8_t chusen_byte; bool treead_running; + bool in_emu_phase; FuriTimer* timer; FuzzerWorkerUidChagedCallback tick_callback; @@ -147,29 +148,35 @@ static void fuzzer_worker_on_tick_callback(void* context) { FuzzerWorker* instance = context; - if(instance->treead_running) { -#if defined(RFID_125_PROTOCOL) - lfrfid_worker_stop(instance->proto_worker); -#else - ibutton_worker_stop(instance->proto_worker); -#endif - } - - if(!fuzzer_worker_load_key(instance, true)) { - fuzzer_worker_pause(instance); // XXX - if(instance->end_callback) { - instance->end_callback(instance->end_context); - } - } else { + if(instance->in_emu_phase) { if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); + lfrfid_worker_stop(instance->proto_worker); #else - ibutton_worker_emulate_start(instance->proto_worker, instance->key); + ibutton_worker_stop(instance->proto_worker); #endif } - if(instance->tick_callback) { - instance->tick_callback(instance->tick_context); + instance->in_emu_phase = false; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_delay * 100)); + } else { + if(!fuzzer_worker_load_key(instance, true)) { + fuzzer_worker_pause(instance); // XXX + if(instance->end_callback) { + instance->end_callback(instance->end_context); + } + } else { + if(instance->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); +#else + ibutton_worker_emulate_start(instance->proto_worker, instance->key); +#endif + } + instance->in_emu_phase = true; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_delay * 100)); + if(instance->tick_callback) { + instance->tick_callback(instance->tick_context); + } } } } @@ -338,13 +345,15 @@ FuzzerWorker* fuzzer_worker_alloc() { instance->attack_type = FuzzerWorkerAttackTypeMax; instance->index = 0; instance->treead_running = false; + instance->in_emu_phase = false; memset(instance->payload, 0x00, sizeof(instance->payload)); - instance->timeer_delay = FUZZ_TIME_DELAY_DEFAULT; + instance->timer_idle_delay = PROTOCOL_MIN_IDLE_DELAY; + instance->timer_emu_delay = PROTOCOL_MIN_IDLE_DELAY; instance->timer = - furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypePeriodic, instance); + furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypeOnce, instance); return instance; } @@ -374,9 +383,15 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { furi_assert(instance); if(instance->attack_type < FuzzerWorkerAttackTypeMax) { - instance->timeer_delay = timer_dellay; + uint8_t temp = timer_dellay / 2; + instance->timer_emu_delay = temp; + instance->timer_idle_delay = temp + timer_dellay % 2; - furi_timer_start(instance->timer, furi_ms_to_ticks(timer_dellay * 100)); + FURI_LOG_D( + TAG, + "Emu_delay %u Idle_delay %u", + instance->timer_emu_delay, + instance->timer_idle_delay); if(!instance->treead_running) { #if defined(RFID_125_PROTOCOL) @@ -397,6 +412,8 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { // ibutton_worker_start_thread(instance->proto_worker); ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif + instance->in_emu_phase = true; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_delay * 100)); return true; } return false; diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index c295289ae..fb7651901 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -254,6 +254,10 @@ uint8_t fuzzer_proto_get_max_data_size() { return MAX_PAYLOAD_SIZE; } +uint8_t fuzzer_proto_get_min_delay() { + return PROTOCOL_TIME_DELAY_MIN; +} + const char* fuzzer_proto_get_menu_label(uint8_t index) { return fuzzer_menu_items[index].menu_label; } diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index 4c2c70e0c..62ce88d5c 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -37,6 +37,12 @@ struct FuzzerPayload { */ uint8_t fuzzer_proto_get_max_data_size(); +/** + * Get minimum time delay for protocols + * @return Minimum time delay + */ +uint8_t fuzzer_proto_get_min_delay(); + /** * Get protocol name based on its index * @param index protocol index diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h index 841784f16..793b3e043 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -4,8 +4,12 @@ #if defined(RFID_125_PROTOCOL) #define MAX_PAYLOAD_SIZE (6) +#define PROTOCOL_MIN_IDLE_DELAY (5) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_MIN_IDLE_DELAY + 4 #else #define MAX_PAYLOAD_SIZE (8) +#define PROTOCOL_MIN_IDLE_DELAY (2) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_MIN_IDLE_DELAY + 2 #endif typedef struct ProtoDict ProtoDict; diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 13e2325fd..1df6d5eb3 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -15,6 +15,7 @@ struct FuzzerViewAttack { typedef struct { uint8_t time_delay; + uint8_t time_delay_min; const char* attack_name; const char* protocol_name; FuzzerAttackState attack_state; @@ -157,14 +158,14 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { if(model->attack_state == FuzzerAttackStateIdle) { // TimeDelay if(event->type == InputTypeShort) { - if(model->time_delay > FUZZ_TIME_DELAY_MIN) { + if(model->time_delay > model->time_delay_min) { model->time_delay--; } } else if(event->type == InputTypeLong) { - if((model->time_delay - 10) >= FUZZ_TIME_DELAY_MIN) { + if((model->time_delay - 10) >= model->time_delay_min) { model->time_delay -= 10; } else { - model->time_delay = FUZZ_TIME_DELAY_MIN; + model->time_delay = model->time_delay_min; } } } else if( @@ -232,7 +233,8 @@ FuzzerViewAttack* fuzzer_view_attack_alloc() { view_attack->view, FuzzerViewAttackModel * model, { - model->time_delay = FUZZ_TIME_DELAY_MIN; + model->time_delay_min = fuzzer_proto_get_min_delay(); + model->time_delay = model->time_delay_min; model->uid_str = furi_string_alloc_set_str("Not_set"); // malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); model->attack_state = FuzzerAttackStateOff; From caab7c8e1092ca7f090ec828ce9857b8197c1950 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 16:55:29 +0300 Subject: [PATCH 060/370] Fuzzer App: Edit Manifests --- applications/external/pacs_fuzzer/application.fam | 14 ++++++-------- .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 2 +- applications/external/pacs_fuzzer/todo.md | 1 + 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam index 8e67af6d4..4d14e55fd 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/pacs_fuzzer/application.fam @@ -1,6 +1,6 @@ App( - appid="pacs_fuzzer", - name="Fuzzer Gui", + appid="pacs_fuzzer_ibtn", + name="iButton Fuzzer [B]", apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_ibtn", requires=[ @@ -10,8 +10,7 @@ App( "input", "notification", ], - stack_size=2 * 1024, - order=15, + stack_size=1 * 1024, fap_icon="icons/rfid_10px.png", fap_category="Debug", fap_private_libs=[ @@ -25,8 +24,8 @@ App( ) App( - appid="pacs_rfid_fuzzer", - name="Fuzzer Gui rfid", + appid="pacs_fuzzer_rfid", + name="RFID Fuzzer [B]", apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_rfid", requires=[ @@ -36,8 +35,7 @@ App( "input", "notification", ], - stack_size=2 * 1024, - order=15, + stack_size=1 * 1024, fap_icon="icons/125_10px.png", fap_category="Debug", fap_private_libs=[ diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index a0bd0e2d3..36734495b 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -137,7 +137,7 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == FuzzerAttackStateRunning) { // Pause if attack running - fuzzer_worker_pause(app->worker); // XXX + fuzzer_worker_pause(app->worker); fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle); } diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 98450035c..823e2f05a 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -27,6 +27,7 @@ - [x] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` - [x] prototype + - [ ] Add the ability to edit emulation time and downtime separately - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] `UID_MAX_SIZE` From 7c172c7c064a6afe36d71ae571df4d6e6fa4744d Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 19:23:39 +0300 Subject: [PATCH 061/370] Fuzzer App: revert stack_size --- applications/external/pacs_fuzzer/application.fam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam index 4d14e55fd..6098322ac 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/pacs_fuzzer/application.fam @@ -10,7 +10,7 @@ App( "input", "notification", ], - stack_size=1 * 1024, + stack_size=2 * 1024, fap_icon="icons/rfid_10px.png", fap_category="Debug", fap_private_libs=[ @@ -35,7 +35,7 @@ App( "input", "notification", ], - stack_size=1 * 1024, + stack_size=2 * 1024, fap_icon="icons/125_10px.png", fap_category="Debug", fap_private_libs=[ From 1e512b6add48bb8eef84962e8f71173041691ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 7 Jun 2023 02:15:50 +0900 Subject: [PATCH 062/370] [FL-3293] FuriHal: add system setting to device info, bump device info version (#2736) --- firmware/targets/f7/furi_hal/furi_hal_info.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index 47672c97a..a2c9232c0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -23,10 +24,10 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { // Device Info version if(sep == '.') { property_value_out(&property_context, NULL, 2, "format", "major", "3"); - property_value_out(&property_context, NULL, 2, "format", "minor", "1"); + property_value_out(&property_context, NULL, 2, "format", "minor", "2"); } else { property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); - property_value_out(&property_context, NULL, 3, "device", "info", "minor", "2"); + property_value_out(&property_context, NULL, 3, "device", "info", "minor", "3"); } // Model name @@ -297,6 +298,18 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { property_value_out(&property_context, NULL, 2, "radio", "alive", "false"); } + property_value_out( + &property_context, + "%u", + 2, + "system", + "debug", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)); + property_value_out( + &property_context, "%u", 3, "system", "heap", "track", furi_hal_rtc_get_heap_track_mode()); + property_value_out( + &property_context, "%u", 3, "system", "log", "level", furi_hal_rtc_get_log_level()); + property_value_out( &property_context, "%u", 3, "protobuf", "version", "major", PROTOBUF_MAJOR_VERSION); property_context.last = true; From 76c70bdf2c33437aa3d62205fb37d0a5fd66b7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 7 Jun 2023 02:46:01 +0900 Subject: [PATCH 063/370] [FL-3316] Settings: add contrast adjustment (#2737) Co-authored-by: hedger --- .../services/notification/notification.h | 2 + .../services/notification/notification_app.c | 55 ++++++++++++------- .../services/notification/notification_app.h | 3 +- .../notification/notification_messages.c | 9 +++ .../notification/notification_messages.h | 6 ++ .../notification_settings_app.c | 44 +++++++++++++++ firmware/targets/f18/api_symbols.csv | 5 +- firmware/targets/f7/api_symbols.csv | 5 +- lib/toolbox/value_index.c | 13 +++++ lib/toolbox/value_index.h | 13 +++++ lib/u8g2/u8g2_glue.c | 20 ++++++- lib/u8g2/u8g2_glue.h | 2 + 12 files changed, 152 insertions(+), 25 deletions(-) diff --git a/applications/services/notification/notification.h b/applications/services/notification/notification.h index b38620f0f..0e1c07e5d 100644 --- a/applications/services/notification/notification.h +++ b/applications/services/notification/notification.h @@ -75,6 +75,8 @@ typedef enum { NotificationMessageTypeForceDisplayBrightnessSetting, NotificationMessageTypeLedBrightnessSettingApply, + + NotificationMessageTypeLcdContrastUpdate, } NotificationMessageType; typedef struct { diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index f91a73f32..2f947fe8a 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include + #include "notification.h" #include "notification_messages.h" #include "notification_app.h" @@ -20,14 +23,14 @@ static const uint8_t reset_sound_mask = 1 << 4; static const uint8_t reset_display_mask = 1 << 5; static const uint8_t reset_blink_mask = 1 << 6; -void notification_vibro_on(bool force); -void notification_vibro_off(); -void notification_sound_on(float freq, float volume, bool force); -void notification_sound_off(); +static void notification_vibro_on(bool force); +static void notification_vibro_off(); +static void notification_sound_on(float freq, float volume, bool force); +static void notification_sound_off(); -uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); +static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); void notification_message_save_settings(NotificationApp* app) { NotificationAppMessage m = { @@ -39,7 +42,8 @@ void notification_message_save_settings(NotificationApp* app) { }; // internal layer -void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { +static void + notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -52,7 +56,13 @@ void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t } } -bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { +static void notification_apply_lcd_contrast(NotificationApp* app) { + Gui* gui = furi_record_open(RECORD_GUI); + u8x8_d_st756x_set_contrast(&gui->canvas->fb.u8x8, app->settings.contrast); + furi_record_close(RECORD_GUI); +} + +static bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { bool result = false; if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) || (app->led[2].index == LayerInternal)) { @@ -67,7 +77,7 @@ bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) } // notification layer -void notification_apply_notification_led_layer( +static void notification_apply_notification_led_layer( NotificationLedLayer* layer, const uint8_t layer_value) { furi_assert(layer); @@ -81,7 +91,7 @@ void notification_apply_notification_led_layer( furi_hal_light_set(layer->light, layer->value[LayerNotification]); } -void notification_reset_notification_led_layer(NotificationLedLayer* layer) { +static void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -94,7 +104,7 @@ void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_hal_light_set(layer->light, layer->value[LayerInternal]); } -void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { +static void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { if(reset_mask & reset_blink_mask) { furi_hal_light_blink_stop(); } @@ -130,28 +140,28 @@ uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8 return (value * app->settings.display_brightness); } -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { return (value * app->settings.led_brightness); } -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { return ( (float)(app->settings.display_off_delay_ms) / (1000.0f / furi_kernel_get_tick_frequency())); } // generics -void notification_vibro_on(bool force) { +static void notification_vibro_on(bool force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { furi_hal_vibro_on(true); } } -void notification_vibro_off() { +static void notification_vibro_off() { furi_hal_vibro_on(false); } -void notification_sound_on(float freq, float volume, bool force) { +static void notification_sound_on(float freq, float volume, bool force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { furi_hal_speaker_start(freq, volume); @@ -159,7 +169,7 @@ void notification_sound_on(float freq, float volume, bool force) { } } -void notification_sound_off() { +static void notification_sound_off() { if(furi_hal_speaker_is_mine()) { furi_hal_speaker_stop(); furi_hal_speaker_release(); @@ -174,7 +184,7 @@ static void notification_display_timer(void* ctx) { } // message processing -void notification_process_notification_message( +static void notification_process_notification_message( NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; @@ -333,6 +343,9 @@ void notification_process_notification_message( reset_mask |= reset_green_mask; reset_mask |= reset_blue_mask; break; + case NotificationMessageTypeLcdContrastUpdate: + notification_apply_lcd_contrast(app); + break; } notification_message_index++; notification_message = (*message->sequence)[notification_message_index]; @@ -361,7 +374,8 @@ void notification_process_notification_message( } } -void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { +static void + notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; const NotificationMessage* notification_message; notification_message = (*message->sequence)[notification_message_index]; @@ -548,6 +562,7 @@ int32_t notification_srv(void* p) { notification_apply_internal_led_layer(&app->led[0], 0x00); notification_apply_internal_led_layer(&app->led[1], 0x00); notification_apply_internal_led_layer(&app->led[2], 0x00); + notification_apply_lcd_contrast(app); furi_record_create(RECORD_NOTIFICATION, app); diff --git a/applications/services/notification/notification_app.h b/applications/services/notification/notification_app.h index 88194bfbd..cacc17ffb 100644 --- a/applications/services/notification/notification_app.h +++ b/applications/services/notification/notification_app.h @@ -32,7 +32,7 @@ typedef struct { Light light; } NotificationLedLayer; -#define NOTIFICATION_SETTINGS_VERSION 0x01 +#define NOTIFICATION_SETTINGS_VERSION 0x02 #define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) typedef struct { @@ -41,6 +41,7 @@ typedef struct { float led_brightness; float speaker_volume; uint32_t display_off_delay_ms; + int8_t contrast; bool vibro_on; } NotificationSettings; diff --git a/applications/services/notification/notification_messages.c b/applications/services/notification/notification_messages.c index 51f3533c3..28ec327c6 100644 --- a/applications/services/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -197,6 +197,10 @@ const NotificationMessage message_force_display_brightness_setting_1f = { .data.forced_settings.display_brightness = 1.0f, }; +const NotificationMessage message_lcd_contrast_update = { + .type = NotificationMessageTypeLcdContrastUpdate, +}; + /****************************** Message sequences ******************************/ // Reset @@ -566,3 +570,8 @@ const NotificationSequence sequence_audiovisual_alert = { &message_vibro_off, NULL, }; + +const NotificationSequence sequence_lcd_contrast_update = { + &message_lcd_contrast_update, + NULL, +}; diff --git a/applications/services/notification/notification_messages.h b/applications/services/notification/notification_messages.h index 100796917..d87cf74f4 100644 --- a/applications/services/notification/notification_messages.h +++ b/applications/services/notification/notification_messages.h @@ -63,6 +63,9 @@ extern const NotificationMessage message_force_vibro_setting_on; extern const NotificationMessage message_force_vibro_setting_off; extern const NotificationMessage message_force_display_brightness_setting_1f; +// LCD Messages +extern const NotificationMessage message_lcd_contrast_update; + /****************************** Message sequences ******************************/ // Reset @@ -138,6 +141,9 @@ extern const NotificationSequence sequence_success; extern const NotificationSequence sequence_error; extern const NotificationSequence sequence_audiovisual_alert; +// LCD +extern const NotificationSequence sequence_lcd_contrast_update; + #ifdef __cplusplus } #endif diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index 8efbc5e08..450aaee14 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -20,6 +20,34 @@ static const NotificationSequence sequence_note_c = { NULL, }; +#define CONTRAST_COUNT 11 +const char* const contrast_text[CONTRAST_COUNT] = { + "-5", + "-4", + "-3", + "-2", + "-1", + "0", + "+1", + "+2", + "+3", + "+4", + "+5", +}; +const int32_t contrast_value[CONTRAST_COUNT] = { + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, +}; + #define BACKLIGHT_COUNT 5 const char* const backlight_text[BACKLIGHT_COUNT] = { "0%", @@ -64,6 +92,15 @@ const char* const vibro_text[VIBRO_COUNT] = { }; const bool vibro_value[VIBRO_COUNT] = {false, true}; +static void contrast_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, contrast_text[index]); + app->notification->settings.contrast = contrast_value[index]; + notification_message(app->notification, &sequence_lcd_contrast_update); +} + static void backlight_changed(VariableItem* item) { NotificationAppSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -136,6 +173,13 @@ static NotificationAppSettings* alloc_settings() { VariableItem* item; uint8_t value_index; + item = variable_item_list_add( + app->variable_item_list, "LCD Contrast", CONTRAST_COUNT, contrast_changed, app); + value_index = + value_index_int32(app->notification->settings.contrast, contrast_value, CONTRAST_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, contrast_text[value_index]); + item = variable_item_list_add( app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); value_index = value_index_float( diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 3c075e0d1..f3c4e31d7 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.2,, +Version,+,28.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1999,6 +1999,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* @@ -2259,6 +2260,7 @@ Variable,+,message_force_vibro_setting_off,const NotificationMessage, Variable,+,message_force_vibro_setting_on,const NotificationMessage, Variable,+,message_green_0,const NotificationMessage, Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, Variable,+,message_note_a0,const NotificationMessage, Variable,+,message_note_a1,const NotificationMessage, Variable,+,message_note_a2,const NotificationMessage, @@ -2402,6 +2404,7 @@ Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, Variable,+,sequence_display_backlight_on,const NotificationSequence, Variable,+,sequence_double_vibro,const NotificationSequence, Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, Variable,+,sequence_not_charging,const NotificationSequence, Variable,+,sequence_reset_blue,const NotificationSequence, Variable,+,sequence_reset_display,const NotificationSequence, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a9ce0c26a..a8708ce8c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.2,, +Version,+,28.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2923,6 +2923,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* @@ -3192,6 +3193,7 @@ Variable,+,message_force_vibro_setting_off,const NotificationMessage, Variable,+,message_force_vibro_setting_on,const NotificationMessage, Variable,+,message_green_0,const NotificationMessage, Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, Variable,+,message_note_a0,const NotificationMessage, Variable,+,message_note_a1,const NotificationMessage, Variable,+,message_note_a2,const NotificationMessage, @@ -3335,6 +3337,7 @@ Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, Variable,+,sequence_display_backlight_on,const NotificationSequence, Variable,+,sequence_double_vibro,const NotificationSequence, Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, Variable,+,sequence_not_charging,const NotificationSequence, Variable,+,sequence_reset_blue,const NotificationSequence, Variable,+,sequence_reset_display,const NotificationSequence, diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c index e0745e434..5ec0fb962 100644 --- a/lib/toolbox/value_index.c +++ b/lib/toolbox/value_index.c @@ -1,5 +1,18 @@ #include "value_index.h" +uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) { + int64_t last_value = INT64_MIN; + uint8_t index = 0; + for(uint8_t i = 0; i < values_count; i++) { + if((value >= last_value) && (value <= values[i])) { + index = i; + break; + } + last_value = values[i]; + } + return index; +} + uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) { int64_t last_value = INT64_MIN; uint8_t index = 0; diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h index 9459292a7..5aa768e3d 100644 --- a/lib/toolbox/value_index.h +++ b/lib/toolbox/value_index.h @@ -7,6 +7,19 @@ extern "C" { #endif +/** Get the index of a int32_t array element which is closest to the given value. + * + * Returned index corresponds to the first element found. + * If no suitable elements were found, the function returns 0. + * + * @param value value to be searched. + * @param values pointer to the array to perform the search in. + * @param values_count array size. + * + * @return value's index. + */ +uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count); + /** Get the index of a uint32_t array element which is closest to the given value. * * Returned index corresponds to the first element found. diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index 17a702b50..0d4879bce 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -2,6 +2,9 @@ #include +#define CONTRAST_ERC 32 +#define CONTRAST_MGG 31 + uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { UNUSED(u8x8); UNUSED(arg_ptr); @@ -207,6 +210,19 @@ void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio u8x8_cad_EndTransfer(u8x8); } +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset) { + uint8_t contrast = (furi_hal_version_get_hw_display() == FuriHalVersionDisplayMgg) ? + CONTRAST_MGG : + CONTRAST_ERC; + contrast += contrast_offset; + contrast = contrast & 0b00111111; + + u8x8_cad_StartTransfer(u8x8); + u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_EV); + u8x8_cad_SendArg(u8x8, contrast); + u8x8_cad_EndTransfer(u8x8); +} + uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { /* call common procedure first and handle messages there */ if(u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) { @@ -225,7 +241,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 31, 0b110, false); + u8x8_d_st756x_init(u8x8, CONTRAST_MGG, 0b110, false); } else { /* ERC v1(ST7565) and v2(ST7567) * EV = 33 @@ -233,7 +249,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 32, 0b101, false); + u8x8_d_st756x_init(u8x8, CONTRAST_ERC, 0b101, false); } break; case U8X8_MSG_DISPLAY_SET_FLIP_MODE: diff --git a/lib/u8g2/u8g2_glue.h b/lib/u8g2/u8g2_glue.h index 91ba2980a..af236279e 100644 --- a/lib/u8g2/u8g2_glue.h +++ b/lib/u8g2/u8g2_glue.h @@ -14,3 +14,5 @@ void u8g2_Setup_st756x_flipper( u8x8_msg_cb gpio_and_delay_cb); void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias); + +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset); From b0555d96e989c6b86e67d557fafde060ebe9f134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 7 Jun 2023 02:52:49 +0900 Subject: [PATCH 064/370] [FL-3213] f7: add PB9 to debug pins (#2738) Co-authored-by: hedger --- firmware/targets/f7/furi_hal/furi_hal_resources.c | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 561cef08a..34b26b831 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -81,6 +81,7 @@ const GpioPinRecord gpio_pins[] = { /* Dangerous pins, may damage hardware */ {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, {.pin = &gpio_speaker, .name = "PB8", .debug = true}, + {.pin = &gpio_infrared_tx, .name = "PB9", .debug = true}, }; const size_t gpio_pins_count = sizeof(gpio_pins) / sizeof(GpioPinRecord); From 61394b8be4dc36c41ea860e08c4887e8a5c0d3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 7 Jun 2023 03:04:27 +0900 Subject: [PATCH 065/370] [FL-3352] Dolphin: new animation (#2735) Co-authored-by: hedger --- assets/dolphin/external/L2_Dj_128x64/frame_0.png | Bin 0 -> 1640 bytes assets/dolphin/external/L2_Dj_128x64/frame_1.png | Bin 0 -> 1687 bytes .../dolphin/external/L2_Dj_128x64/frame_10.png | Bin 0 -> 1630 bytes .../dolphin/external/L2_Dj_128x64/frame_11.png | Bin 0 -> 1660 bytes .../dolphin/external/L2_Dj_128x64/frame_12.png | Bin 0 -> 1637 bytes .../dolphin/external/L2_Dj_128x64/frame_13.png | Bin 0 -> 1654 bytes .../dolphin/external/L2_Dj_128x64/frame_14.png | Bin 0 -> 1667 bytes .../dolphin/external/L2_Dj_128x64/frame_15.png | Bin 0 -> 1344 bytes .../dolphin/external/L2_Dj_128x64/frame_16.png | Bin 0 -> 1251 bytes .../dolphin/external/L2_Dj_128x64/frame_17.png | Bin 0 -> 1292 bytes .../dolphin/external/L2_Dj_128x64/frame_18.png | Bin 0 -> 1498 bytes .../dolphin/external/L2_Dj_128x64/frame_19.png | Bin 0 -> 1530 bytes assets/dolphin/external/L2_Dj_128x64/frame_2.png | Bin 0 -> 1726 bytes .../dolphin/external/L2_Dj_128x64/frame_20.png | Bin 0 -> 1698 bytes .../dolphin/external/L2_Dj_128x64/frame_21.png | Bin 0 -> 1665 bytes .../dolphin/external/L2_Dj_128x64/frame_22.png | Bin 0 -> 1809 bytes .../dolphin/external/L2_Dj_128x64/frame_23.png | Bin 0 -> 1775 bytes .../dolphin/external/L2_Dj_128x64/frame_24.png | Bin 0 -> 1758 bytes .../dolphin/external/L2_Dj_128x64/frame_25.png | Bin 0 -> 1725 bytes .../dolphin/external/L2_Dj_128x64/frame_26.png | Bin 0 -> 1835 bytes .../dolphin/external/L2_Dj_128x64/frame_27.png | Bin 0 -> 1759 bytes .../dolphin/external/L2_Dj_128x64/frame_28.png | Bin 0 -> 1462 bytes .../dolphin/external/L2_Dj_128x64/frame_29.png | Bin 0 -> 1407 bytes assets/dolphin/external/L2_Dj_128x64/frame_3.png | Bin 0 -> 1777 bytes .../dolphin/external/L2_Dj_128x64/frame_30.png | Bin 0 -> 1408 bytes .../dolphin/external/L2_Dj_128x64/frame_31.png | Bin 0 -> 1404 bytes .../dolphin/external/L2_Dj_128x64/frame_32.png | Bin 0 -> 1327 bytes .../dolphin/external/L2_Dj_128x64/frame_33.png | Bin 0 -> 1306 bytes .../dolphin/external/L2_Dj_128x64/frame_34.png | Bin 0 -> 1341 bytes .../dolphin/external/L2_Dj_128x64/frame_35.png | Bin 0 -> 1255 bytes .../dolphin/external/L2_Dj_128x64/frame_36.png | Bin 0 -> 1059 bytes assets/dolphin/external/L2_Dj_128x64/frame_4.png | Bin 0 -> 1727 bytes assets/dolphin/external/L2_Dj_128x64/frame_5.png | Bin 0 -> 1641 bytes assets/dolphin/external/L2_Dj_128x64/frame_6.png | Bin 0 -> 1635 bytes assets/dolphin/external/L2_Dj_128x64/frame_7.png | Bin 0 -> 1588 bytes assets/dolphin/external/L2_Dj_128x64/frame_8.png | Bin 0 -> 1608 bytes assets/dolphin/external/L2_Dj_128x64/frame_9.png | Bin 0 -> 1610 bytes assets/dolphin/external/L2_Dj_128x64/meta.txt | 14 ++++++++++++++ assets/dolphin/external/manifest.txt | 7 +++++++ 39 files changed, 21 insertions(+) create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_0.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_1.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_10.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_11.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_12.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_13.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_14.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_15.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_16.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_17.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_18.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_19.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_2.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_20.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_21.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_22.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_23.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_24.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_25.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_26.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_27.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_28.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_29.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_3.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_30.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_31.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_32.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_33.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_34.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_35.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_36.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_4.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_5.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_6.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_7.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_8.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_9.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_0.png b/assets/dolphin/external/L2_Dj_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..95f72f901a5e03e63373957cbb188c9ae4673162 GIT binary patch literal 1640 zcmaJ=eNfY87!O~VNeuA$d_XjMR_@J904APtJopZ9>n2_@i7=U z7bDo5qK$XJUPju&uN5^?pmsbByVGQPN$hY<6bqD1?xCG>Ooqzsc5iND-YzkV`(MUu zy+r^u)WlR3N~EUfj2LD#=ya%2uU9Bh zW1=F3L=`@4y2B;X4uf5Kfw#kBWmel>Bf*-eP~sNUg5V^jLI@Q}A{L4z5lWGWsgxvv zlZ;h5foESS-*~G63rNH1Q==wv5?9fj0#Tuu0wI+Ij*tw6A_QqAX(dfqX^TQC2NLDp z&UvMid?wJI-tEiT03R=>i*taH62R;jKKImvxto-^G}{Q)(b3Vbd+KgLkUuD{F5A;P zl-J?7g@(qAj(+>#&CXml|0Zr_S82ZHZeDvy*Ju;jJ-hsezA0>E#l3uYha5U^Z>Hrj;ykkbVy( zL46TIbF*1n{J{pT?v6xo9<)U43Tx~)`z5sFVb6jaPj47mAKvhutpZ*b7P-a!N8;9@ z3ro9Gg5bIvmscG*Qq}j>>KVNk`=Zg*T_+_c-ONz+{$JhD?ojH!F6!FjbH_(KGvo4T zNzE+3jDH7L?fGc0wxi?flfyyRHyoX}sd35a<>mwOs(`D<6810nBs+(le86u<`ih#^ zy}u>s^$RmR4?>Tb!eblzdgovIYz}*P@xhY*>qgA)p<$=CdWoYxzNapsLOtW%ntDsQ z4(XcR_@lDhoOz~JcYDjDsQpb_D|=EW^|a>Zb?vTdi!SxwV{T1quXIbck(b-2s<>lA z=A4kxkjeloaz{{gXXn+Y0edb!OMh$%zf~Jw|8dQ2`@z*mMF^XA;pRVs=Vx#4^z56% zZH|gqDAqKcY&;qg@kMJ$LtJNE;i=`Vk5=CdHl3_0S3S)2d> literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_1.png b/assets/dolphin/external/L2_Dj_128x64/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..32e13541d83eb0571414851957a4b8a5647a8c03 GIT binary patch literal 1687 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 F(|@~CN2UM( literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_11.png b/assets/dolphin/external/L2_Dj_128x64/frame_11.png new file mode 100644 index 0000000000000000000000000000000000000000..eca4a1296ee3dedf9ebd3b010c7be8763eaaa44d GIT binary patch literal 1660 zcmaJ?eM}Q)7{5{wsDfm1laGv?g=~Cy?e%)Jca>^OTLfY&D~+Nvlk448D)z3l2egQz z&P`+lb&HF*5tVG6ZU%;lafqm>bCdXm&S7L5V{Aj6_<=^iIbm0z&OeNoyL;dFx##yh zzvnAgn{QeiC5jgT01%a%qc`)bkAE}~LjL_%#5NS?f3o1<(va*E4De$9i*@h7#onB$x(skEY^LQ)}@u&y;y8II!t8wdjN8j#z|aD)e9+(|(T zdfH98>`uZsFu3yd|`-ndX_A6&C63R}g zNCl2rQA`ODq!ouyxeSL08ID0Ti6W>>VO3Bt6}0mryh4W~a-|N<&eCSfWCp!f3mbGg zxdJw1$g?n59>nG{ZjN9`I_TTZ`<}+iUy4=fTr|P4E(^=93wA<&G0U;;V%7;Fs00O5 zj09ZYE@pshv5veQDF#%C<$5)DPfr$!W1Y1VI&SiC}zV51%cWKt6VJQ zDM|yK^IRtdMSOb(w$EmRKRlZ*n&C&v#m`RjuGTz$?q=jKu^9MuVq(I7eb27|5aP|% zYb~DMKO62ao8Vc74Sf^-H=EP3ttUd7empeZ(3|R&+hf0Q;(q`5)48`byTd&P(!KSar-7QgDb=rJ z&T`&d`M9wt0jL?BCvFSNU#0tZ>{H)9Anj>VXyYf=wq5Vog}8LpwTUB};}U-})+H7T zyCc2c9;bG~psDNE`jSGAgkRkqo!QyGaG<8BCY%ur<(!Py7+XdCH-GNfn*7VCuWO4S zW?$NYWptGfwe?u|!gQD4GPjN0FE>PQ@LxoifZAKo*1Jp5wymV%+~(7C3= zug)%Nm)_oJr5ZLj?B5mH6a|b_e7;%|e<5CDB7|^Q^al=e`ddFmJQ3vi1m(_9>yz(d z_m21ku{QINt;uwuD_q^`&-l;zS;6s_ZjuPu#^3Jhie;4 zyLPa-gK5c88&ma)<23R0+<~1tYBdc)&5FT<^wL4?>*-}9<3k^IS%z2iSJmAJsg5Z) z=-GC=_CRD(3>tlY{eqT(^ME0G`&$14Z_}mH{MDggW7cQ?+;Du^+dJH8x4jJ)Ua2{> z>~g(N{eyW!S9Mu|rl%ztC@&cbi+K=%9jh01KLkF$DySH{zaTx*GV;{|W z(mqGi4mm_>8_+bWlaD8 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_12.png b/assets/dolphin/external/L2_Dj_128x64/frame_12.png new file mode 100644 index 0000000000000000000000000000000000000000..5f92e47fdd7cdffb93e2cb52b64dd87858071c6b GIT binary patch literal 1637 zcmaJ>dr;GM98U}4iz153Ly=JC&`Fv!P1~fSyxO9@S=M1UVS3s$Noj#LAq}>8J|gGy zrW?~u-E?z$zHcWJ(Xs6We9hwyH@Tfo*ii8?<=n3w_Ddp2oF7Az&gJk~7)NuB}nY1YMnRv%f$ z6f-1UQ`N>~13=U|ucJgLvCTD5tWQOTbyOiAC!ql#J2%9UR0Sh|E~d=uH!AzSX;Fe+ z+NhkbwIMdn#CW`Omh#NJr9}>EX$3{l%G?|D~;}^&f<3u(8^i0oS1JryX=bTvALAFSW}{O2qN2UDfzlDU z3&ZsgNx29J)2IoER1-MFP#B7-buJx^&=EV&!|O~0s?nS9!h-xlwc29N&qpjKlSYSF zw3-4O(L}ITe?TDp6ch37m3$9l^)JL4OguvhEbn01%19^JJ*>b6JS+#Im)P=Yp6esi$rpIx}MbJ1zXtZj*4#!ZO zaw~`N^fT!jYE?-hg~3JG7;qFv^(3Q#^oUvm;W`Y3aEd@640q$Cj>O!gOQVD(ig37d zp6R5BNNUgU_UUX$ho{rU_@$BJrP)#7ubq|VF2+9BVUgB>fq{LUySf2D?y{Qm9igsY zx3_IRgN!ow4h-}jIOpQsRJJK?UC=?6Mvx9J;F@;a+2+2oO1 zzFu|8wr5WPm{rl>cBR_o?VslQ;wJ2+GX8Bz5(}ap-f{vhE4e9VYWf%j&fiQpdyJY` zOU$$%*xg^6bf)SndlFoK{Crc!jXgK~$GZ9)*^TqA3itBjoK&zWy7utr?zXq&2mdHJ zxxBe<)8);DE#u-x^glR%B{nZXFAt4KzbMW(T^ftZpO_NMM+YaZPEL9#-!QKd(N3|?l$+N920Vr zFP%*C{FK(bA_hDPFNyz4eOL>;GC6hA)+a3)7y5K@qDQ2c_;&$X6VUwqHlTK-%;Ou| zfQ)WaC?*2STDRRjQ@UF=ry+4_IhW8oL3Dfqsm|GNR+p#JT(`^_Q)>Mgd}BGEQg`r! zhDCXn)?hKFtqTFdw>`ZNj>NZL+dsWDkQU`!d}o1jBL$*qqDNK!5nhZ?zPej}$m- F{{s^?M>YTe literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_13.png b/assets/dolphin/external/L2_Dj_128x64/frame_13.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1017ce259d7b511bfb2d6c565cbe8350ca929f GIT binary patch literal 1654 zcmaJ?dr;GM98X(TD+i*^sccSTIXtJPP1CeZdK6j-NN*H-(8=R)Gfh$`&?cmT7Iu#6 zx%0&-6BVc898TSx;Hg99oDZhsd~ABCi0_-5!tmxD+t_?;Se62H{^6NR^85Y1`F_6N z&-Zm%ZO>W|8~t`P006Pp3?f@xgW_XW$i#P#V!cgV;)S$4A%}Gdev)SZ3(Yzi(CQ(J zm~4imtIE$aNdO>Q>2~A^dA3X=#d=g^SVtA`a3UH2l2QU3NtH4J=wyoAUX!x_+fz!= zO`DXtIvZ@`Qki0RMm5jmRA)J;>QV})l_|+!Qotx0@Gt@i20Ue6pD|!k4%;<~`>CoRG!Tarm{Q(;wl)TU|0hha1{k3FzQ5T#x)N_U_`5i5%GpJh*5(Y5fmJ~ zl%hwTb{VsY^ihA}$)qe61kR{d`~7~EU#nvIBDDs`@vsd9fkX|+SLGGR0Oa*0L@W@D zkK)~&;AXvG*dpmmSS)Z_YN}Qb zr|GowF<2YHTD?Aj^ioX3w_Ef*iq*dsYfR-CQeb%p%a%nt!CuS?tgo2mKm=8x;A|U7 zxxHb@d*LdL1Who!yON>PdDa6Cr`hO!1qGK*t4Ex$6GAZD0HFp9gPb^xLAX|@G3YTA z!6=t<6i>gDzL8cHC1My{gpCnHFvLJITF3xvv=F985eTDj7(y`@M(Rn_MLM-gwMbDN z?wpr8DIyZvGrWB<8{*-`bTM9Wq ztw&pOz2Czz1pefd5QH{lj9a@_|D7_VI224w&N(ydvp>vR>5|1K zPA)D6LQyN|o{oI!{$+p9>*{yNexKO+bO$#6cz(h~cv^Ew5882L_OS=V(n-Lf-uP9a zRbc!50(0nn-M!Ymi<&N-Q0&rVOg;Hi_d%D=UK^8d?5nsUZV&00oqu@dl6=44_kExD zKJR_sOUjLz^FGOa40A zApvPyvSe9;0XDePCc(98qBlk|m?0vJ6Hrjqy+*E`=d049h_kF15l4j9L&IWuFToFrtvb2zNtrL@h_v z2nvo}63(N?W>=et^f7i0Y}cQ9lj!B~r&l#A69$r5fy zYNKsxs}jd7D5io)s|ANpg&c=SIgUY;6-7|F(xSA%wy>QS;W4=i!L>wcda5>EF4qxS zEv!pRQz&6wf+7`z6=AI2)#^x1Qws?O#M!Xq?%Z%IiyVW>>^H#9CtWE2;BZHH%gY{ax9> zC)HKLoj@ynAyGVdW}pYi-+d>lv*q>s_e8*!d-?!-{^s#Mkx@3S4ghlgou8e}8w!e( zo|e7I+wsUVyr^6^#C3}9o~_>}zP;?9_57YcelI$Alo{-4NMmufIf zueUd?-I3BUNI+E6Kz>!b)aW;L$IX)5-hF!t0+qd*p?LS1!jfsT z?gkSAZId@$l73gx`nIv0f9xsCyL*^Th_Kfh9_(zX<2iyA9m3NwRBY6_*jWKz*%zXtfZu9Xi~;@8j}*ap0E4 r14aC?w7aOMg&+C!?=^uYKr=29SRN~Sr=Tku5B(POx=f-uHE-R2HCSU* literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_15.png b/assets/dolphin/external/L2_Dj_128x64/frame_15.png new file mode 100644 index 0000000000000000000000000000000000000000..9b796498c0c48c2c9125e98dbfea9c0c812345ef GIT binary patch literal 1344 zcmaJ>YfKzf6rKgPB1nVPmPk@M9co&mv-4zjX2KR0cA?vJaiwdam25kYyD))yI5V&d z{%}KUjP(aL#-t@ptfo~HZSg^DLSwYs z@0s(R^PO|gnb#6+trca}We9>)#P&qmq0GUjv9uVz7fMIsP(0yAJKgf-Jl&v>fno4Ks|yKzp5oW-Ol(vQ8D#^M^_8p1m2;$+-v20hxIAsg%;YD+3Zy^5e>4ZG3$tOyDo8(r(j?6gBn@wzqD6`oX%=0* zFc{HR(_(w1Wi=k$g|HsiwM2r*WHNya6EN*=f)WJ5^Fh-%w7{J~!@?YQmWe;VsrJIO%{ z5beM*`)viDNxHTGhV^@JAkPr85udd+cra35#8moqV7ReJ2!me&s-}txC-5@M2XRS} z1)OCl0hcI&$AQAqEXB#3N~#4v>*0ASNDJXeOG{I@g`%R7aF~oXH!~a=tz(*ak||(g zhT}?x0t&G;7<&~PeIQnBwt?iDcG5Ka3Z0PXFntSWt`@PAkGGP9+w4`#|5U23UWM4^GX_9#jE#~Z>3eC z2Bu*Z*oZvM(?JO^c#xzRoaa~?=M{m(Sw786oW!OjnZXE1k?=a_zD_ErV0(JYwb_81 zwdn!|94Q;l&SPbN`~>H2Lt9@`cUJ=oZ-7!G~sv4Z*RGdpLt(;QFk`(M1HN3K3S3x z?h*foSF4IX?k2lNh@xByx$Xb`^^t|eft<`8dnruc+pu@~*vV4j;ATZxtVWa@+h?~b zAKm-(&eCFaY&_K3@Yn9CrTJH8o$=WevIB~D-@hQ>q4&yD>PQtJ6~on$xmA#|9Td*{sg zzVn@P&v~ypxqrvDC$|9rc9cu`8d*E!$qXjQ``X|_g)C1*g{kO(XGJX(V30LE9aCio zHE<0hbMEK`JO)7Gq+OqirYaMf;W;ddeb~0+6Epy0>9&uIX&g~HZrE;${`1RoG-aD9 z`nAytU-5IeX_w{$d|-aEZp=>`(4^C^P-AV45I8tORNI+xL#>^n*LXFukIfuSt+_>$k#Zo)oQUViS>d8CqM{e4pC%?2NTY@5o$ATIMQRt9rDs^R}-Z0a4>=X}i7aOlkj200UJ zqz5MTdvKuGk*LvL4Qz5SXeRF&vkrEnay~_qKdfn+nxQ~dm(?VL3>`AEBtQlUP-U2{M}m zxqGNOQ7@2nb#?X3Pam(4-PLkFTW|mT$CYn}&QseH)w`?5=P&=b=aZ!$sL6>UOx}E? ze`{Z3VDQ4?q0HHi+qZJ!SMYAf&9*X!GiMjm)XTRaej`)8)PMSg+Ye_-nOl!u1k1Ru zZ-4*Z>EFQa{^vT4Q^$6_4{FQC<*#3o|8x3-qRjV+yf zhlH=c+dVen?|J(B1@P>hcfzX&hlV?U{WG}u`Ri{dmbV-{HVi&{Jo7>NE_-Fyjpr|2 zTL4D}sArCE_upE&xcB$dO&9!q{oi4KUt%P4Zs1<$BuIdp=XWj~{U~dZTtT@onO_<| Ga^gRXW|=zx literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_17.png b/assets/dolphin/external/L2_Dj_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..80863f0b6935ebcdc353deb69a6b5cf4d0c4d4ad GIT binary patch literal 1292 zcmaJ>Z)n_P7|*p`cWu3O-I%%|8ZwZ%HJ9W~E|~=*oE27&jfM8X;EgN@(wi zDFRHO&Nw4+p4zXX5s%dHq>@&r(Imqp;?XFbh{af*PK4M&fo7Z7lEZ|nc-Xltt%@l=YD+WXAJXoq~g(Bb#dwD4_l7W{^N3W<Y-MP88j#+;%e)*wV`r+Q!^6&d*!PZ}nesb;Ey{|`4O@H^E zwHM}3|8eWz{nxu@dbl(Br3>c^cWWQC9SPidt0z+1Om{ACD`0t~x literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_18.png b/assets/dolphin/external/L2_Dj_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..b4527bc83306f43a5ee138258cee037f073fc276 GIT binary patch literal 1498 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 BgtY(w literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_20.png b/assets/dolphin/external/L2_Dj_128x64/frame_20.png new file mode 100644 index 0000000000000000000000000000000000000000..f63904f29b2f44112bcec74fe738a623b2d9f1c8 GIT binary patch literal 1698 zcmaJ?dr;GM91joWAtK1;K-?K3r>LY&)6k}cN=r+z&LYE3!P|s1O&^0cr46(KL#|9- zn};{2r`s5cn~F}y&BIgY1|lfp&f()w<_XGl^9eqVbHYiN0(Ji3Etll?`+f8Me7~RX z>r#`Eo;EXhZZH4P$O!qoHCl07u%@S#pxQdIFA(RpyXv>l2gnYFjEWxI?eeOyVxm#ChUsYy+_Q2 zzzG#5PXbMn%2H>5a@t0LA`SwR2*Lw}A`Xe52&P4Kls+Cr5tPqGQ1*s-sF;U|Q4E~A zAl9Qzrx#}`lvDoLlLRs{j8)9#Ivfs;gU_LD1};w|5_xQ(D9mcW_6-(>aKaY*0|#Jld{u=Hj=H0et{Rv=I{RiTNol=L6e1M%EG7=5{Ui6(V28& zQXs;$7%qefQY(TnK2HP_JP{64B!*%o<-AG$O6ysoE zoSGm_7LR1Hr%IDSD=3?(h|(!-v>BX8v)J?o3i<@T0M#N|7{x_G7!%?+tQF~SSj11@ z2?aQY;-ns$!s}j3-(;(@5;hF0myH-laa2f9d{~I^_%JTOP#7mg2#n!+oDdM0p3w3k zE=!T?>73U($tz;p)3bdw8|>lLbWs*|q-^Z$Sax10Vdrjo#!5{pySm-(@B12V0Dy0U zN+HuY|LDjLvUDJU^W5&shfb(Mc97N&&EKt1yG}dtkoa=q$2;L=4PA!F!QPzKvZ8Hc zrBR_SpW&_dKk+HQveD;pf6U9hM8~c31N5$es%9uKE;mrxxX~JhxoUm-$0K8BC?WaH z)pfaJbr(-ss~u~ul_U3BOmT+&y~w&!V_^I6`_WRVaZ{jlJg8}-czauB{Gzz#x^P27 z-8va<4hU=7bTs*MS{WV`8VVDQp}l98`YS@nfG-OL&Byx+D}LQPXWHXE@ag7Rzl&am zjXvFe?^MsK`m>8Gm9x_;G*tV-871A4S+Z}}3dkFW3huX8G+p?L7tyOpdlXa~o}Z}s z=9&A#7fU{#QPocYj=_0PvxevOJdGbZV%_&}=gI3ML62R&)y0{;Ys@30f9EwGuwS}+ zMRos@uG*sU)`~L>qQuXs9DPxGg_4ZT_jGno4_4gL?1T*uNv>&o=k{Rr+GRuF`2GEoX$eX;jmbqa)9i>YV9q z(A{mL>RW$~Uca<&%sUtm1R#hl@4Frf3s6%T<53TGiW?9wj})A>}Aon4&N`}L>w58Jsjn05N)j;9{8cm aKN6?|A`4=UbUyd|HB_nTio+?nu73e(FMSmN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_21.png b/assets/dolphin/external/L2_Dj_128x64/frame_21.png new file mode 100644 index 0000000000000000000000000000000000000000..076448fa940b42f0740c62feeb1943e00616371f GIT binary patch literal 1665 zcmaJ?c~BE)6kotbz=O_+tyqsGqP55-dnMVd1PBBKAs8W5v>j!$*+2-%#?4{^s6(mX zF)gOmv6Sji4=T0}W(4VBhoYk3RmXx>@MzFt$I&S`)3H>ebOXfthtr+i{l4$*`@Q#j z*X%Z3_L3>#Gs6J@n39pM$r08H;YkV&5#9r#JG8saj+#EE!4y`F8~(u_y3OkiUJ#Q8D|y>LltXSy2Q%Z_zaCoEPRm|%|?=z zD+vQmC?JY9C?Om}ln{j|35cO_6i4I+xe+!7?7RxE(WD>*f@so`)oBQls!^-q)RYuV z4yVenWCF$l*bJM4r))G6@NE`+k7HB*7fYsa48^mYo@I*zouDgZdDc};o2?Golrq|4H$3hT(oNJDS^EdU7Kl%Y}U zU40#^dbtj8a`W?%LyH%`6}ly&*VFx1gE{omDr;oyu{FcK{O{^Fb-JF$&zg8K7dr4+ z?~w=CS(C?*)wptKuE=0Xn&zI_b}E1Bg&0r5*LNmPmX~}PUNL1y=Sb7>y<1o8Y4jdw zZwI{t+h-?EjY?Yk!SuPF)76JR{zTM0i402I&`O?c{Az0W(ETd!jF?BcS1P;S-@NUT zw|O-ud+50|P~8crXXo}~y|vc>Ji+6%sFxQQYDJe*KRO238~!=9sJwO0a&JHNr24_H zf9hdKeX(DBrn+rw{q7)DO`oOcc{H+PZDZq=?CH@WXlH---V0|tkIZM~(+^a(INuw( zT4OqFQk|cX*MerlNwcD^H)tO04-2liX_40fK_NG-Md{Os%+j6hzpSY8w60E!o94c) zQ;GI%=^Nl6$i7s(j zdVXBY@C|y|hNy>;k>!u(HFk)Epp~_O|TUWV$e-xag2+53mE$H?f-@US&5NnTHw%hKG{ubHTrC$+!GWa?jx>IuR=61uGuuCUn-P(;VNI@mp(>qsi0_UI(@nS&LHE==?~46>Y3P-=bBIav+ZV(4UU$iMgUUA8-f& literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_22.png b/assets/dolphin/external/L2_Dj_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..8651f12f8bd91d684d5943af135697e9e7d700d0 GIT binary patch literal 1809 zcmaJ?dr;GM9FMfpg1~U&alT`4t}1DhG_+|2ZJ`AVR-x9T&KGH#N)OxAHcDYbD|!NR zDpSun>vN~>oKBs}eBcH?PQ|I9PCZbZcy@=6gQ@rcL6#~w|M1Ku`Tc(1d_Ujs=li;p zWll;94;>f^06@5Ig2uqDUhYu|dEDDCT&3rh0j$=Jr~4z zbXlxQgC@1dA9qTEa#_}?lt}XP^Tqj6G2@yeK@|!`zy^lFoCfSJaImBYcDUnuEof*r z<+3?h8{+^27D+QRpG|@|wr|6*JN5c^h8^ynWN~Secu1!N6(bV6JrLU)Z8vM6|EKXm zZMU(&NlOg0o0;#TxSCkwdcj=&KJF->$gxpQcG-5HL;Q7>>%!G7DnqweumoMw5aPD2Apct5Z=_t5K^FZAyw% zhG-L{$pj+p#p)bxmUK{buWuXYy9bMW6st^e(Im^bj0`ikw-YjR8J2P9GENY~#W*O^ zla$R7kc21&&+GSfnTT)$&9ek5!rR@V0tO-&XW=6~~MkwSkE++`stgsNULYjce zWdx29lojg1Ti#3G+g9ZyTo}$?HcA2`FgZy}VL5_IVM2yuFhMC07$>X*DI;+!X_i6~ zj-n*cIq!8+uZU~U!1mp2aEEu(MLW2Wa&fcMYI2R_<}NsMqEXAO-QC?g>bEp+YX!u9OJ&N0#opWU_y|KR&zITY9Ip_V0eMJ9@lr5#9y+=*fn)byNzk7lC0BorAn|VQRv$u&DmcOnOTTQ7J>G zhekI#=Dnzt+{!u@RNbtPSw2{-EV#in0n@Srpi{@2x@pMC8^Ydx{vsjItU&pdSwt-PK6 zS#91uzkh93W838TKX{SGFAjy2XD+JPiNxPi?ZGqDUD3RhoxbRnlB(asvjpeDc{lUj zv5%xHy?x|)qk^-W_*<2p^{;2$-gaQ!6ID$GPfH)q{0bT(hl$$bfOqq}+0Tj_59@Bl zjU9S4v>`9m{LA*Tk~ZfPzb;+?y6=^15A7P(rRI$glsD#HWVeqmsb4sALi%jlynp4V za<~K7N`<9QE9(ZzJC96RKl(Ud6*}uwwpg@c)ssK#D^c&&(jToVK{cst%vzNZeD3QV zg(0gJjm|w`l%o{tsyvoy!0K literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_23.png b/assets/dolphin/external/L2_Dj_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..d2d8e7e5144697f5932a8e920d673c29de730372 GIT binary patch literal 1775 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_27.png b/assets/dolphin/external/L2_Dj_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..39ddf46ab3bd195b3e75567a3f91021720c9e1c8 GIT binary patch literal 1759 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_3.png b/assets/dolphin/external/L2_Dj_128x64/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..40d1314c95fb07190f995925e69f77df558f8cbf GIT binary patch literal 1777 zcmaJ?dsNeA6ptX!!4ySyz*i{vnx;+D7Scc`l(rV7h)@;h)TU`F9ki)QfP#v}Q*{jR zdFIq1zQHL+Jvtd4buwS`wZTzgQykzFA7ew9=wLFtRH*Y0k2y)c@4LCbdw=(JQk0TB zEzm#A9{_;BM71K7TkYHv=j+M6+kDq)xFwWTrn703k6 zxlG0afae;MHl0n^Bw++)=Ho6MzRhgq&;SrS)n>(sOp*ol z%6R>x(lsey0!5Rcln+A$3=6?%DW8B5Sgc13q;U#}z=%iyBis!M5lkq?5HUD#@i>pP z!HA_QR0ICFlZ=V0wEt3n9Z)(UTHIID)~Q+w`w!m zJS!{%gg+iqwJ|0#kB#5G5 zr9>o0VUZh~Xkl2~LXd9XCeHT&7I`NYOQ1=drD!chWw|>cC4*uqCWEqqh?p-1Cu(rQ zWN}F*xvJD3w1T8fIix{FQ)cj0nlaN`C>SN8C`1qIAq161L*i%@h4fMb3Q0v0VRRHK zMo_}Y8^9ahNMC=eauO~KtD6mmA}A7#lOiY@7K$J=N{m1#A%!6^YDDoUTx`VkBA$Sw zC~$So8=d49aqa2azMc*4@OrvP3pY|UH#>C0@?dW6yi=02N^W&Jo%?R>_!R&=IuaG} zTHEbE<_%hVAND=xbap>JwCSSv#`WJY*yHNSr8x~ruy?@aZU4qjty|%2&41}Ps)ZTS z&~SME%!2JJ+GanU&~aKNubvfj*2q3x(4}Lv!`8HnpS+}FOw~qo3sp?4yxSN6+UJI6 z72g=TXIoO>V`^=^v)M|&)ZeWeyY$Ph_Jp9F+a8cbS9pEB^t2=Ifm>A4AL0V{EP1mIw>30iOq)uLK>okRiy>lBR_MbYy7I(x7p2uiCOf!87(} zk5z|#q>DSg_b(_iyk=9Yj@Nvp&^OL;0t-6rr`t2MQ@y$I`QhH~oe^hz07hD{rf}=> z9+CXV+XsOP-|`fDw)Y&gb_S5;p(o|;L)Y>I~$)hhJ)JV4SL__$2QpOs=CTkLiU2}Zy^Ck6b=tZFpw6s>+jai0+s<# z^N0?}R1^f9de$R(`qlBW62E&tLqLxVj2K8B>;-$hl`ZNR@@)IWI8|WPqcW(p?quk`u<+*0)$>P* ziZ*rVhj^E4+BFC zJF#+Jeazt5`P9_F!lmPGhd8q@gs07T@cjKlF_)j!%eOkN?RIpXE*gHe_u|cez*Vto z|AtCQL*c}l9bcn2gFoJKY{Vd6AO4iJeTzH-fQAKrUcxWfcGpiRQJJhbB-gF{58GOv ANdN!< literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_30.png b/assets/dolphin/external/L2_Dj_128x64/frame_30.png new file mode 100644 index 0000000000000000000000000000000000000000..27c297e8d59c983d0a06ce9afafdc39064216039 GIT binary patch literal 1408 zcmaJ>YfKzf6rP0z)~)4H)TFJ3af+4#v-4zkX37ft;Mp#$+cgr4hIuVaV0YHpp}TC` zWUFb~{;1#&joKz9n$}lR5lz%I^00|sYcrp@Gh{}AEc8zbMHO( zeCK@U+;irLx1naed6OA}p!IdNZXcKv;H@;JgYPeQznpE;XP>c}<2Kk}Ak> zU65~;eKId~z4(n>4ngUM6n~T6*iKh9X;17Tj_~ z)ReHUs3ACMkr&iX-HrgZ%V7k=9?z=bP-G=pAZ=KT4`YM{$AZCRY)je^-6#J~V@>Ud zzbhd3k*Wl5o2sjkHZ=^Bw1tL?JiMrcl9Fx7 zTUrjI}Ii=#9XV0bG}2Y7)(FhCJYR?a<@loEmR zOfFY@18i2iOAdjS(m?M#)c2JN`YzME(_aPV#l^*wmrq=QAjAGTx5FR%@%PJrWWS7? zGR7|49sFy*wFU^`;UMVO|*cWakyoStc6*)cm!&`@H_nlwp zF*0}a)EyHKOdH1AzQu0*-9MH0ld-SA$%!u=fd-^IjZyKO4L24ZYPkN0@rcQlQp|;`e;wT z!GC)6s?uWge(8EM@qK>%lf7TuY&N5b(0pO#_Qxp0+f&D1r{rP@#gJW*+n|L}axNu4OzVvwe9JU;vjA4Jq zh#}s1JneAzVjwa1dMUhMYAG5>&wUl@9Nq!_S~Bo(?&b%_kdQPJ~{u%69q#* e7k@BOlQ2NG;zy>fj4vzsPpqqIaG!Rzy!0Pj5bA>f literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_31.png b/assets/dolphin/external/L2_Dj_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..f2aefbfae2f1ed51e8e87c69f47d42b6d57b6a7b GIT binary patch literal 1404 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 UNHl%vIsb2!jCaRAjU1c&4=x3@iU0rr literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_34.png b/assets/dolphin/external/L2_Dj_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..81f133ac5592569c6963b813e7eee54087a5195b GIT binary patch literal 1341 zcmaJ>YfKzf6dr^uO>wc>#DuoN>0qKnXXm-QGZVJmC+%vNlI_M4k(7DdWk;AtX9jn` zqy_t<`k(ow@g( zd%knNbM85FsIg(2ckPC?2!eQ%+v90C_rN#2rWAfJujxy{sme(-IlGLklNT+3L=-~; zXi^i~KpKcj*WPbI9fFh|R5MLZQ)-7G8=6ln==faCglGh*+v=L4+zuR60&S`u!e+<5 zz))2QVY~e)GG)d_+=< zkCu`Z(((Fbe{dJVa*ksP1d-3@eR;-bSZxHw^L)VuP2*4lx4U#lbaCCTDO!jFTeeiw zQ4Jj}SQI6r(+Oda?NS(;nM$n~*6rnF!L$*sXcCld| zWhfpODW1cD%+f3skOB&+6z!~rrM|($R-x}@Y~+DhA!Y&5F|3SXbQCM0F=sf2oij|7W_>LBbV`&}y&$PA+|p9eabT&h z0HxkCG;}e|g1QQYte*+c5-H&{#|LpX$Z@#DD;&-)_nFO2{78XwZa_kaP&;`s z^86 zSUq@pTkhoJga3XP9cY=_GmpMM7uzXC{=`}2Ir8(V-PEWZzWVV$rB99_?|#t3jSb&D zG1W6sO5ahlsJpK9YyZ!`hdtM;-btp$kBDg4~+HYXme{t=gri$nls2pT3LVj*o_N?A6hce5Fd%Q GzV;v5Zqt+i literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_35.png b/assets/dolphin/external/L2_Dj_128x64/frame_35.png new file mode 100644 index 0000000000000000000000000000000000000000..c828207d33ed6ec94a71c904597dd24b4805b3b3 GIT binary patch literal 1255 zcmaJ=U1%It6dt>whW_M5Y@;ZQ(+X8PGk1P=cV?2!-_E94%%;m`!T1t-XXftiG?|~y zOuCblYMP4L2dhGbRV)#;STGjEKd=xj6j5KaNeZtO$oFf;eg zne%<;JLjHrB0scm`<92d5CpM3JD?WuI*p&6));Tnc@?~m%q&H& zx&&h>YE9HgE>ET%50NqhXdM8KOv;Q7cpz%Lfl7Nx9`FJSczn|wuW+Kmi)7=Xa7522 zDFwB^5f6W*s7esH3d>fjRi-L1j#p+mS(YOXo~N+~?N8bP3~Afn)nrhSuY0B&n2t?G zj8JnXf)s_-uBBnQx!gLj?Kg^r%f^P#WjO}0mKEi;>g@*w^uLS^z5U{(i`WA4oe59J zXHweL1mpVMJ5c0^)hPQt6CVs5R~>!ALUxc?JlW9@lWEyg^ zL?d0~MJ}Nw3}7^QHo^-6P(`37`uq9-$7R&sUXV$rg#^gNg+2)gO>EZo18D208Qa9M z8`$_gu}a!QFmSx07njEsKIvB=dj zE9M3gN^v2t}wNUBbI7S+%zqE-u$Dv>gZA(l_taKlmb6d2eFRBM&489_+hz z;oRS%y1e+=hxU!5->~+!w`-g+9qTS^J^3%!Rd4_0{H@X1+h;c|-u(Kt>8>Yhm#FZN zPIPSkw4VL>^Sw*Z`U@RseQS$A5P8p$J+jU16A`K z2Y(-ZyuRG}QEjL#{A=d>(F2!fj?A69zV+hL^Eb*X?~Zl9v-{2p^RoC@aI~eJIOg8x U{4;a%BF>k{W`@*reFtX#193vGHUIzs literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_36.png b/assets/dolphin/external/L2_Dj_128x64/frame_36.png new file mode 100644 index 0000000000000000000000000000000000000000..fc923b40236dee851d9c3b1f60a64d0050b67350 GIT binary patch literal 1059 zcmaJ=&2G~`5O!4wRh2k$fYZvA_>ow9y>^nd)zBt!N+Z=JN+S_*Vr{P*)A|Q{OWdAN zC4@Nf25{mE55NI9@&pJZj@*#?2w|PXrH863+dDI!Z~o^^fA`__)my8QBwg=q+XHdG z6z}HBviSa4dFzPlChv~;UJ~#L&6w2k6PJNrMEBW%QUCb)ceXA`%df-Xh>x5d%S$4K zmO4rir2;KU>l;N%y#vO9%l5;#DW84*B7@Lx%8zRfa?%bPhubF^+dJ7EdM5{->B}1r zzng(Hff}|n!@>qw99ekG{Jv{)cg?c0N2#nL1#3a+G^0qFPSQg-NPJVmwRod^ZGh zzFjO9E0u}ed3UFHBKGcDe`nYg_jEe_@bmp|L4ls#8Wz9)ep^}++$orTinljQvOH(E Yze-?JTKaM0*{hQ8b$9KL?Wd>z0Hr%cDgXcg literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_4.png b/assets/dolphin/external/L2_Dj_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..d372ff643b40c4cf27bc810fb25a3ed239535b5d GIT binary patch literal 1727 zcmaJ?c~BE)6kiBRhRdT$MX|0y8B{jeY(lcTLx4aMi4uws6;RO4W&<_xrxL@AuyCU9;nM`l^5xU6V3nUzLjT25#P7;MKG0|tsFK)WH`XvOUk&Y)eK**nBM4mhYn zr%548t5Y5Cmd0AZwNjFRMp;RV z-c0LFM$loA(3mo435Q`j6o$d9QoS*3v<@eWNgK~jn0b6I%rh7qu?=cl=~(K28t>G$ zsx!?LFP5^JGAtxh6P@1(n91M!9XS*kHu%RDJyRGWU1lOP43v>p$|M}-3s3WwtkP(u2_s33_|`MNhq0pfV(|zIMbIXT+GI)}>4cb66K%4ln#>>~m4iCG2GgPtCJ5q- zMW_%#NgZbxuYD_hL#@h4m@v#EY;Y7o5ivmtATi7rK&VKFKq!g9kPy|OgoqI82#tWl zV<_?*o%2>Fjfj}`bZpYwXXb8nOq5#AtOElB`~N6w0|4h! zN|{t`Z#k9V9^C+Y6g^v|cS*1A&?cSPF?M`c%kkols=C1J<@3kDFJ4_`ClV*XJO8+! zox!f^_vyi+_Sly-^|^R=-`s(FaNBrO*MH{L9GK+s-Q$JN>Smw6rTXpA*nNQ;8eA2X z9w*z$n~!S!{wE~ffz64j7pBc;6$59x;t87FbTPD|9L4h&xE59jLtLzJo4L-T0dTU< z-<`VZ2eW{r2_bi@XY}TG^pqRQI@~Ebj>uyfq2m5tBmo(HY z__3&Mpdz+)c+p zEi2yNr+jBFH==i9Nqyr1<@Ur!KF8FpE8{#%XU_1cJ&<0os!v`}=~>dus{scZn=&U{ z-G6S>QO@NJe`?}a#Y#JW)$b02JK8(8*vgiEOAD?=`ezsHE4nxPNQ}QUVaB8l;+-=KlMOI!zDSddDuouQvmIZjsBsC!Fjr4Os1%)+;r1jmjZSE;h9VFzIpQee$VfD z-sjnnVVoB_ar#6607CWY+AMjkke}q>0Qub?ywxBt(ULA#$`&nB3G3tmjafADpx(yj z^I1G=E-gCCCjmgfIw2=l$~7!tIMJqHy*>)J%^{-!AZf1K!E(!a2{iHff?W;WKY9WJ z1+yAjlxRQ@s5xSGJs^&CuLAV3M1W32eN?f^J)(-Nm@o&l8F$N_Vi~z?j zNRH?<)3RUk^6&70eZw@b?6|CjMn zZ&yyKgI8wpF0t6j$!B7T@qy*~y*iNBQC7pe?-b;NVGFe)S8U_$l3uHZf~8TbmoX+p|2A%tfC~ktfB+N2n7MYV_-SK z?zO~uPiZu1E$mEO*I zp_6UZqZfQKqt4>>;xjM z^8PHOwjt_zU~pS+L{S8m^zfUe#+G2q@4#fs@xV*_1Uz{|?YxsM->)fCLNVHs=M&a@PPedfEmY;l0M93#=g}bGU*!Tk%Yz zPkH-Z==ck+4_@og4?2$*o4Xe&4sOhnxVNG!>Tb1EDx=nS3GrlSf`5CEc~4!{hr^kX zU^x9pdxWr&udCkP&l&a|>i7FAB~toiO5v_W?wT5O>-PFzvFL)vnEJACzn->bHMzSj zJMlhE*tOFsD_d=Rxv8Y=*ur$n-U-=@&)0Jgn;G`AM@#g1GnzEFTS_m*9y{e9IDJQE z@wu*B$H>_quhq7MZB4Rd85gd9FC=i+uDUq#>mTz&_I2%Eqe?%tEpE!Y2T!&>uoI&hrEhO+t@MJ8-10H(1?aH75N0`nbEpTRxhfH&ZWW7d2gq>8$F!Z2WfVx#EJR z1=R<-kakt;o3lyd{!Kf|Gp6pn_T}wl=5XSpye9pUwJh&n9kBgWwI{X%Xd4dJ?Hpg* QB=;Vm*BQ0VDS4~^1HhwTkpKVy literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_6.png b/assets/dolphin/external/L2_Dj_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1e84a11eeb499fcd47e7709e166c714229afe5 GIT binary patch literal 1635 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 a;?v;Bx|Au6L*e|`zuj&tqdJRfTmA#Jq%n&C literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_9.png b/assets/dolphin/external/L2_Dj_128x64/frame_9.png new file mode 100644 index 0000000000000000000000000000000000000000..05de5d5c698a86baccd74cfe33d1109ee566f793 GIT binary patch literal 1610 zcmaJ?eNfY87*FY-RYd$4;Mce;$Tm0ACTVDsj7keF*o^{*I5_7`lQe}4+JrQW!qls% zb4>51c<1~=xedSADd@J-`89URhTeJNoJ=VSH#ZzR*%XIt?mU(Pb^hU*OY*+&ljrw5 zzvt^xo0pRnGjY~L003ef3vKzz8c-gKR;|2$)qd<$mYK4>NM0nk9l8c<#6f1aibVx_%_lOD_08-QZBF&a^GRSbnyw?oD#SE^5Usx@1i>h-9*y>W2bU|qgz*Ik_81Qg14f;K0ULWN*L!)*nWgimj zA#hYhE;U1Aq>7w*V1^)ZAgM!O7C}(ZMCw=sLvRLjaqjsbhF}Igf+;tQVibx~7!HnK zkm6Btxv6|x=D0uQWQIy)S)}y(%F0Szr9mf1#d?$^$&d{UgB1q%nM#HWRYfs3fT-PY{$azh)(BY!(QKbvXr#x{j{h@b%@^M3B@+5?UVDl|7ko` z+gDH}a{7GECsatRQWJMl7_8*)*^WYr3L7d{;+4YCWj2AW@Niz)VKYO@7oCfDQLK?9 z7@RP{G|P}MZa_(xMo9wZSRBJqBV%+SuCSe_;W3k)w51!xRbht|!tr)~bp`2}2CCp$Lq?Of+YJO$cg$2_ufd1WO_?PPhr$NaJprF+h5SqCV6) zPjpgPq_k&f`*=2#!{h1Vyvj&P%ItLP`YBGCyQsYE0=u$~jEwBPzUKx2L}WQ^)&hUm zo$YT+XA!Oa>wkOX;<~QBn7V_(e%mts)!T`xh`H12)j!U;=6Bz#Oan$@fu38(fL6=y zNHIRq(5=b)VDjF(NkH}=L-&SmJLV1qs({niGM?YH=EceC#=$8`Q-hj7{gn3o&jo?E z(zd2eh*B{t|4ynq`_6-?nxLh{r!^MdY zC$;gLk^YF851H$lZ!L#-^{JECRQ0OaSb|!JnRBA?=;}?wvpR!l3lKP7f3iO!5`2)@ z_=UCtys5c+DeGLeba8gAs{MdZb@N$M@Qcm}vksJ!osZl6;O; zqZVUKS5pJ1I`6?&2&DzA+N}en>UA zv$^-BzOD#!)VsT4T$(-3&eWm3`4IgLP`WPP Date: Tue, 6 Jun 2023 21:11:23 +0300 Subject: [PATCH 066/370] [FL-2872] Remove unused resources (#2740) Co-authored-by: hedger --- .../scenes/nfc_scene_restore_original_confirm.c | 2 +- assets/icons/NFC/Reader_detect_43x40.png | Bin 3799 -> 0 bytes assets/icons/NFC/Restoring_38x32.png | Bin 3794 -> 0 bytes assets/icons/NFC/Tap_reader_36x38.png | Bin 3748 -> 0 bytes 4 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 assets/icons/NFC/Reader_detect_43x40.png delete mode 100644 assets/icons/NFC/Restoring_38x32.png delete mode 100644 assets/icons/NFC/Tap_reader_36x38.png diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c index 730dd41e8..16b0953f8 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); - dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32); + dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36); dialog_ex_set_text( dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); diff --git a/assets/icons/NFC/Reader_detect_43x40.png b/assets/icons/NFC/Reader_detect_43x40.png deleted file mode 100644 index d833a5277fcf067cce9ae363ffb2bccf31c6acb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3799 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 Date: Tue, 6 Jun 2023 20:18:24 +0200 Subject: [PATCH 067/370] Serial_CLI: Fixing serial cli logger error so it sounds more concise (#2721) Co-authored-by: hedger --- scripts/serial_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py index 2fa37d751..6dae68be6 100644 --- a/scripts/serial_cli.py +++ b/scripts/serial_cli.py @@ -9,7 +9,7 @@ from flipper.utils.cdc import resolve_port def main(): logger = logging.getLogger() if not (port := resolve_port(logger, "auto")): - logger.error("Is Flipper connected over USB and is it not in DFU mode?") + logger.error("Is Flipper connected via USB and not in DFU mode?") return 1 subprocess.call( [ From 3e1f209d64981fb806b8819be1806e61a1a6f1ac Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Tue, 6 Jun 2023 12:00:43 -0700 Subject: [PATCH 068/370] Furi: smaller critical enter and critical exit macro (#2716) * Furi: smaller critical enter and critical exit macro * api: bumped version --------- Co-authored-by: hedger Co-authored-by: hedger --- firmware/targets/f18/api_symbols.csv | 4 +++- firmware/targets/f7/api_symbols.csv | 4 +++- furi/core/common_defines.h | 31 +++++++++++----------------- furi/core/critical.c | 29 ++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 furi/core/critical.c diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index f3c4e31d7..1dfe4c7a2 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.3,, +Version,+,28.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -288,6 +288,8 @@ Function,+,__clear_cache,void,"void*, void*" Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" Function,+,__errno,int*, Function,+,__furi_crash,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a8708ce8c..2f6d8ffd8 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.3,, +Version,+,28.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -323,6 +323,8 @@ Function,+,__errno,int*, Function,-,__fpclassifyd,int,double Function,-,__fpclassifyf,int,float Function,+,__furi_crash,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index d7bfaf207..5bd218d35 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -31,29 +31,22 @@ extern "C" { #define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) #endif +typedef struct { + uint32_t isrm; + bool from_isr; + bool kernel_running; +} __FuriCriticalInfo; + +__FuriCriticalInfo __furi_critical_enter(void); + +void __furi_critical_exit(__FuriCriticalInfo info); + #ifndef FURI_CRITICAL_ENTER -#define FURI_CRITICAL_ENTER() \ - uint32_t __isrm = 0; \ - bool __from_isr = FURI_IS_ISR(); \ - bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ - if(__from_isr) { \ - __isrm = taskENTER_CRITICAL_FROM_ISR(); \ - } else if(__kernel_running) { \ - taskENTER_CRITICAL(); \ - } else { \ - __disable_irq(); \ - } +#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter(); #endif #ifndef FURI_CRITICAL_EXIT -#define FURI_CRITICAL_EXIT() \ - if(__from_isr) { \ - taskEXIT_CRITICAL_FROM_ISR(__isrm); \ - } else if(__kernel_running) { \ - taskEXIT_CRITICAL(); \ - } else { \ - __enable_irq(); \ - } +#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info); #endif #ifdef __cplusplus diff --git a/furi/core/critical.c b/furi/core/critical.c new file mode 100644 index 000000000..57fe2403b --- /dev/null +++ b/furi/core/critical.c @@ -0,0 +1,29 @@ +#include "common_defines.h" + +__FuriCriticalInfo __furi_critical_enter(void) { + __FuriCriticalInfo info; + + info.isrm = 0; + info.from_isr = FURI_IS_ISR(); + info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); + + if(info.from_isr) { + info.isrm = taskENTER_CRITICAL_FROM_ISR(); + } else if(info.kernel_running) { + taskENTER_CRITICAL(); + } else { + __disable_irq(); + } + + return info; +} + +void __furi_critical_exit(__FuriCriticalInfo info) { + if(info.from_isr) { + taskEXIT_CRITICAL_FROM_ISR(info.isrm); + } else if(info.kernel_running) { + taskEXIT_CRITICAL(); + } else { + __enable_irq(); + } +} \ No newline at end of file From dbd48a04d4eced78903f79ffd06499ced58de843 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue, 6 Jun 2023 23:13:41 +0400 Subject: [PATCH 069/370] [FL-3331] SubGhz: add subghz_protocol_registry external API (#2712) * [FL-3331] SubGhz: add subghz_protocol_registry external API * F18: fix API version --------- Co-authored-by: hedger --- firmware/targets/f18/api_symbols.csv | 2 +- firmware/targets/f7/api_symbols.csv | 8 +++++--- lib/subghz/SConscript | 1 + lib/subghz/environment.c | 7 ++++--- lib/subghz/environment.h | 7 +++++-- lib/subghz/protocols/protocol_items.h | 3 +-- lib/subghz/registry.h | 1 + lib/subghz/subghz_protocol_registry.h | 13 +++++++++++++ lib/subghz/types.h | 7 +++++-- 9 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 lib/subghz/subghz_protocol_registry.h diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 1dfe4c7a2..f551a09c1 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.4,, +Version,+,29.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2f6d8ffd8..b6eb8c765 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,28.4,, +Version,+,29.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -189,6 +189,7 @@ Header,+,lib/subghz/environment.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, +Header,+,lib/subghz/subghz_protocol_registry.h,, Header,+,lib/subghz/subghz_setting.h,, Header,+,lib/subghz/subghz_tx_rx_worker.h,, Header,+,lib/subghz/subghz_worker.h,, @@ -2662,12 +2663,12 @@ Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char* Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" -Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* +Function,+,subghz_environment_get_protocol_registry,const SubGhzProtocolRegistry*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" -Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" +Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, const SubGhzProtocolRegistry*" Function,-,subghz_keystore_alloc,SubGhzKeystore*, Function,-,subghz_keystore_free,void,SubGhzKeystore* Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* @@ -3361,6 +3362,7 @@ Variable,+,sequence_success,const NotificationSequence, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, Variable,-,suboptarg,char*, Variable,+,usb_cdc_dual,FuriHalUsbInterface, Variable,+,usb_cdc_single,FuriHalUsbInterface, diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 6d9c0cd06..3a0325b71 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -18,6 +18,7 @@ env.Append( File("blocks/generic.h"), File("blocks/math.h"), File("subghz_setting.h"), + File("subghz_protocol_registry.h"), ], ) diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 5ded243c4..3794dbad8 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -92,16 +92,17 @@ const char* void subghz_environment_set_protocol_registry( SubGhzEnvironment* instance, - void* protocol_registry_items) { + const SubGhzProtocolRegistry* protocol_registry_items) { furi_assert(instance); const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; instance->protocol_registry = protocol_registry; } -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { furi_assert(instance); furi_assert(instance->protocol_registry); - return (void*)instance->protocol_registry; + return instance->protocol_registry; } const char* diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index 7bd38ba2f..c15b8b211 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -1,6 +1,7 @@ #pragma once #include +#include "registry.h" #include "subghz_keystore.h" @@ -9,6 +10,7 @@ extern "C" { #endif typedef struct SubGhzEnvironment SubGhzEnvironment; +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; /** * Allocate SubGhzEnvironment. @@ -93,14 +95,15 @@ const char* */ void subghz_environment_set_protocol_registry( SubGhzEnvironment* instance, - void* protocol_registry_items); + const SubGhzProtocolRegistry* protocol_registry_items); /** * Get list of protocols to work. * @param instance Pointer to a SubGhzEnvironment instance * @return Pointer to a SubGhzProtocolRegistry */ -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); /** * Get list of protocols names. diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 4ca1c4679..f1a28ac9b 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -1,5 +1,6 @@ #pragma once #include "../registry.h" +#include "../subghz_protocol_registry.h" #include "princeton.h" #include "keeloq.h" @@ -43,5 +44,3 @@ #include "alutech_at_4n.h" #include "kinggates_stylo_4k.h" #include "bin_raw.h" - -extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h index 91027807e..8529c1097 100644 --- a/lib/subghz/registry.h +++ b/lib/subghz/registry.h @@ -9,6 +9,7 @@ extern "C" { typedef struct SubGhzEnvironment SubGhzEnvironment; typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzProtocol SubGhzProtocol; struct SubGhzProtocolRegistry { const SubGhzProtocol** items; diff --git a/lib/subghz/subghz_protocol_registry.h b/lib/subghz/subghz_protocol_registry.h new file mode 100644 index 000000000..6a27da992 --- /dev/null +++ b/lib/subghz/subghz_protocol_registry.h @@ -0,0 +1,13 @@ +#pragma once + +#include "registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzProtocolRegistry subghz_protocol_registry; + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 09eb07eea..719beff45 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -21,6 +21,9 @@ #define SUBGHZ_RAW_FILE_VERSION 1 #define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzEnvironment SubGhzEnvironment; + // Radio Preset typedef struct { FuriString* name; @@ -115,11 +118,11 @@ typedef enum { SubGhzProtocolFlag_BinRAW = (1 << 10), } SubGhzProtocolFlag; -typedef struct { +struct SubGhzProtocol { const char* name; SubGhzProtocolType type; SubGhzProtocolFlag flag; const SubGhzProtocolEncoder* encoder; const SubGhzProtocolDecoder* decoder; -} SubGhzProtocol; +}; From 6f6ead1726bae2de28b7aac8df3f97338de3be13 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Tue, 6 Jun 2023 23:33:04 +0400 Subject: [PATCH 070/370] [FL-3045] Fix core2 permisions (#2742) * Fix core2 permisions * Fix Python code style * scripts: copro: changed int literals * scripts: copro: shorter string line in code --------- Co-authored-by: hedger Co-authored-by: hedger --- scripts/flipper/assets/copro.py | 11 ++++++++++- scripts/flipper/assets/coprobin.py | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index f176e3b2e..25c072899 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -58,14 +58,23 @@ class Copro: def _getFileName(self, name): return posixpath.join(self.COPRO_TAR_DIR, name) + def _addFileReadPermission(self, tarinfo): + tarinfo.mode = 0o644 + return tarinfo + def addFile(self, array, filename, **kwargs): source_file = os.path.join(self.mcu_copro, filename) - self.output_tar.add(source_file, arcname=self._getFileName(filename)) + self.output_tar.add( + source_file, + arcname=self._getFileName(filename), + filter=self._addFileReadPermission, + ) array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs}) def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None): self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT) fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR) + fw_directory.mode = 0o755 fw_directory.type = tarfile.DIRTYPE self.output_tar.addfile(fw_directory) diff --git a/scripts/flipper/assets/coprobin.py b/scripts/flipper/assets/coprobin.py index 75bf76d76..84f52fbb3 100644 --- a/scripts/flipper/assets/coprobin.py +++ b/scripts/flipper/assets/coprobin.py @@ -46,7 +46,10 @@ class CoproFooterBase: _SIG_BIN_COMMON_SIZE = 2 * 4 def get_version(self): - return f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + return ( + f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, " + f"branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + ) def get_details(self): raise CoproException("Not implemented") From 28f4cd3d3ce172f9c60283120294de1a606d7165 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 22:43:44 +0300 Subject: [PATCH 071/370] Fuzzer App: Zero idle time --- .../pacs_fuzzer/lib/worker/fake_worker.c | 32 +-- .../pacs_fuzzer/lib/worker/fake_worker.h | 5 +- .../pacs_fuzzer/lib/worker/protocol.c | 8 +- .../pacs_fuzzer/lib/worker/protocol.h | 8 +- .../pacs_fuzzer/lib/worker/protocol_i.h | 10 +- .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 5 +- applications/external/pacs_fuzzer/todo.md | 3 +- .../external/pacs_fuzzer/views/attack.c | 217 ++++++++++++++++-- .../external/pacs_fuzzer/views/attack.h | 4 +- 9 files changed, 237 insertions(+), 55 deletions(-) diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 896088308..e48b1dd32 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -38,8 +38,8 @@ struct FuzzerWorker { const FuzzerProtocol* protocol; FuzzerWorkerAttackType attack_type; - uint8_t timer_idle_delay; - uint8_t timer_emu_delay; + uint8_t timer_idle_time; + uint8_t timer_emu_time; uint8_t payload[MAX_PAYLOAD_SIZE]; Stream* uids_stream; @@ -157,7 +157,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { #endif } instance->in_emu_phase = false; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_delay * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time * 100)); } else { if(!fuzzer_worker_load_key(instance, true)) { fuzzer_worker_pause(instance); // XXX @@ -173,7 +173,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { #endif } instance->in_emu_phase = true; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_delay * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time * 100)); if(instance->tick_callback) { instance->tick_callback(instance->tick_context); } @@ -349,8 +349,8 @@ FuzzerWorker* fuzzer_worker_alloc() { memset(instance->payload, 0x00, sizeof(instance->payload)); - instance->timer_idle_delay = PROTOCOL_MIN_IDLE_DELAY; - instance->timer_emu_delay = PROTOCOL_MIN_IDLE_DELAY; + instance->timer_idle_time = PROTOCOL_DEF_IDLE_TIME; + instance->timer_emu_time = PROTOCOL_DEF_EMU_TIME; instance->timer = furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypeOnce, instance); @@ -379,19 +379,21 @@ void fuzzer_worker_free(FuzzerWorker* instance) { free(instance); } -bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_time) { furi_assert(instance); if(instance->attack_type < FuzzerWorkerAttackTypeMax) { - uint8_t temp = timer_dellay / 2; - instance->timer_emu_delay = temp; - instance->timer_idle_delay = temp + timer_dellay % 2; + // if(emu_time == 0) { + // uint8_t temp = idle_time / 2; + // instance->timer_emu_time = temp; + // instance->timer_idle_time = temp + idle_time % 2; + // } else { + instance->timer_idle_time = idle_time; + instance->timer_emu_time = emu_time; + // } FURI_LOG_D( - TAG, - "Emu_delay %u Idle_delay %u", - instance->timer_emu_delay, - instance->timer_idle_delay); + TAG, "Emu_time %u Idle_time %u", instance->timer_emu_time, instance->timer_idle_time); if(!instance->treead_running) { #if defined(RFID_125_PROTOCOL) @@ -413,7 +415,7 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif instance->in_emu_phase = true; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_delay * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time * 100)); return true; } return false; diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 04635169b..6396525be 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -35,10 +35,11 @@ void fuzzer_worker_free(FuzzerWorker* instance); * Start or continue emulation * * @param instance Pointer to a FuzzerWorker - * @param timer_dellay Emulation time of one UID in tenths of a second + * @param idle_time Delay between emulations in tenths of a second + * @param emu_time Emulation time of one UID in tenths of a second * @return bool True if emulation has started */ -bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay); +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_time); /** * Stop emulation and deinit worker diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index fb7651901..f520037ac 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -254,8 +254,12 @@ uint8_t fuzzer_proto_get_max_data_size() { return MAX_PAYLOAD_SIZE; } -uint8_t fuzzer_proto_get_min_delay() { - return PROTOCOL_TIME_DELAY_MIN; +uint8_t fuzzer_proto_get_def_emu_time() { + return PROTOCOL_DEF_EMU_TIME; +} + +uint8_t fuzzer_proto_get_def_idle_time() { + return PROTOCOL_DEF_IDLE_TIME; } const char* fuzzer_proto_get_menu_label(uint8_t index) { diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index 62ce88d5c..68632b029 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -37,11 +37,9 @@ struct FuzzerPayload { */ uint8_t fuzzer_proto_get_max_data_size(); -/** - * Get minimum time delay for protocols - * @return Minimum time delay - */ -uint8_t fuzzer_proto_get_min_delay(); +// TODO add description +uint8_t fuzzer_proto_get_def_emu_time(); +uint8_t fuzzer_proto_get_def_idle_time(); /** * Get protocol name based on its index diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h index 793b3e043..074c50d9d 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -4,12 +4,14 @@ #if defined(RFID_125_PROTOCOL) #define MAX_PAYLOAD_SIZE (6) -#define PROTOCOL_MIN_IDLE_DELAY (5) -#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_MIN_IDLE_DELAY + 4 +#define PROTOCOL_DEF_IDLE_TIME (4) +#define PROTOCOL_DEF_EMU_TIME (5) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME #else #define MAX_PAYLOAD_SIZE (8) -#define PROTOCOL_MIN_IDLE_DELAY (2) -#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_MIN_IDLE_DELAY + 2 +#define PROTOCOL_DEF_IDLE_TIME (2) +#define PROTOCOL_DEF_EMU_TIME (2) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME #endif typedef struct ProtoDict ProtoDict; diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index 36734495b..836bbdef5 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -127,8 +127,11 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == FuzzerAttackStateIdle) { // Start or Continue Attack + // TODO emu_time if(fuzzer_worker_start( - app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { + app->worker, + fuzzer_view_attack_get_time_delay(app->attack_view), + fuzzer_view_attack_get_emu_time(app->attack_view))) { fuzzer_scene_attack_set_state(app, FuzzerAttackStateRunning); } else { // Error? diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 823e2f05a..d0bab30d6 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -27,7 +27,8 @@ - [x] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` - [x] prototype - - [ ] Add the ability to edit emulation time and downtime separately + - [x] Add the ability to edit emulation time and downtime separately + - [ ] Decide on the display - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] `UID_MAX_SIZE` diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 1df6d5eb3..a29e2d966 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -4,8 +4,13 @@ #include #include +#define ATACK_VIEW_V2 +// #define ATACK_VIEW_V2_1 +#define ATACK_VIEW_V2_2 + #define ATTACK_SCENE_MAX_UID_LENGTH 25 #define UID_MAX_DISPLAYED_LEN (8U) +#define LIFT_RIGHT_OFFSET (3) struct FuzzerViewAttack { View* view; @@ -14,8 +19,11 @@ struct FuzzerViewAttack { }; typedef struct { - uint8_t time_delay; - uint8_t time_delay_min; + uint8_t time_delay; // 1 = 100ms + uint8_t time_delay_min; // 1 = 100ms + uint8_t emu_time; // 1 = 100ms + uint8_t emu_time_min; // 1 = 100ms + bool td_emt_cursor; // false - time_delay, true - emu_time const char* attack_name; const char* protocol_name; FuzzerAttackState attack_state; @@ -107,8 +115,7 @@ void fuzzer_view_attack_set_callback( } void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { - char time_delay[16]; - snprintf(time_delay, sizeof(time_delay), "Time delay: %d", model->time_delay); + char temp_str[50]; canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); @@ -116,8 +123,101 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name); +#ifndef ATACK_VIEW_V2 + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, + sizeof(temp_str), + "Time delay: %d.%d", + model->time_delay / 10, + model->time_delay % 10); + canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, temp_str); +#elif defined(ATACK_VIEW_V2_1) + + canvas_set_font(canvas, FontSecondary); + if(!model->td_emt_cursor) { + snprintf( + temp_str, + sizeof(temp_str), + "Time delay: %d.%d EmT: %d.%d", + model->time_delay / 10, + model->time_delay % 10, + model->emu_time / 10, + model->emu_time % 10); + } else { + snprintf( + temp_str, + sizeof(temp_str), + "TD: %d.%d Emulation time: %d.%d", + model->time_delay / 10, + model->time_delay % 10, + model->emu_time / 10, + model->emu_time % 10); + } + canvas_draw_str_aligned(canvas, 64, 21, AlignCenter, AlignBottom, temp_str); + +#elif defined(ATACK_VIEW_V2_2) + + uint16_t crt; + canvas_set_font(canvas, FontPrimary); + + if(!model->td_emt_cursor) { + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, sizeof(temp_str), "Time delay:"); + canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str); + crt = canvas_string_width(canvas, temp_str); + + canvas_set_font(canvas, FontPrimary); + snprintf( + temp_str, sizeof(temp_str), "%d.%d", model->time_delay / 10, model->time_delay % 10); + canvas_draw_str_aligned( + canvas, crt + LIFT_RIGHT_OFFSET + 3, 21, AlignLeft, AlignBottom, temp_str); + + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, sizeof(temp_str), "EmT: %d.%d", model->emu_time / 10, model->emu_time % 10); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str); + + } else { + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, + sizeof(temp_str), + "TD: %d.%d", + model->time_delay / 10, + model->time_delay % 10); + + canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str); + + canvas_set_font(canvas, FontPrimary); + snprintf(temp_str, sizeof(temp_str), "%d.%d", model->emu_time / 10, model->emu_time % 10); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str); + crt = canvas_string_width(canvas, temp_str); + + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, sizeof(temp_str), "Emulation time:"); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET - crt - 3, 21, AlignRight, AlignBottom, temp_str); + } + +#else + + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, + sizeof(temp_str), + "Time delay: %d.%d Emu time: %d.%d", + model->time_delay / 10, + model->time_delay % 10, + model->emu_time / 10, + model->emu_time % 10); + canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, temp_str); + +#endif + canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, time_delay); canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); canvas_set_font(canvas, FontPrimary); @@ -131,9 +231,21 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { if(model->attack_state == FuzzerAttackStateRunning) { elements_button_center(canvas, "Stop"); } else if(model->attack_state == FuzzerAttackStateIdle) { +#ifndef ATACK_VIEW_V2 elements_button_center(canvas, "Start"); elements_button_left(canvas, "TD -"); elements_button_right(canvas, "+ TD"); +#else + if(model->td_emt_cursor) { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "EmT -"); + elements_button_right(canvas, "+ EmT"); + } else { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "TD -"); + elements_button_right(canvas, "+ TD"); + } +#endif } else if(model->attack_state == FuzzerAttackStateEnd) { // elements_button_center(canvas, "Restart"); // Reset elements_button_left(canvas, "Exit"); @@ -156,16 +268,31 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { FuzzerViewAttackModel * model, { if(model->attack_state == FuzzerAttackStateIdle) { - // TimeDelay - if(event->type == InputTypeShort) { - if(model->time_delay > model->time_delay_min) { - model->time_delay--; + if(!model->td_emt_cursor) { + // TimeDelay -- + if(event->type == InputTypeShort) { + if(model->time_delay > model->time_delay_min) { + model->time_delay--; + } + } else if(event->type == InputTypeLong) { + if((model->time_delay - 10) >= model->time_delay_min) { + model->time_delay -= 10; + } else { + model->time_delay = model->time_delay_min; + } } - } else if(event->type == InputTypeLong) { - if((model->time_delay - 10) >= model->time_delay_min) { - model->time_delay -= 10; - } else { - model->time_delay = model->time_delay_min; + } else { + // EmuTime -- + if(event->type == InputTypeShort) { + if(model->emu_time > model->emu_time_min) { + model->emu_time--; + } + } else if(event->type == InputTypeLong) { + if((model->emu_time - 10) >= model->emu_time_min) { + model->emu_time -= 10; + } else { + model->emu_time = model->emu_time_min; + } } } } else if( @@ -183,15 +310,29 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { FuzzerViewAttackModel * model, { if(model->attack_state == FuzzerAttackStateIdle) { - // TimeDelay - if(event->type == InputTypeShort) { - if(model->time_delay < FUZZ_TIME_DELAY_MAX) { - model->time_delay++; + if(!model->td_emt_cursor) { + // TimeDelay ++ + if(event->type == InputTypeShort) { + if(model->time_delay < FUZZ_TIME_DELAY_MAX) { + model->time_delay++; + } + } else if(event->type == InputTypeLong) { + model->time_delay += 10; + if(model->time_delay > FUZZ_TIME_DELAY_MAX) { + model->time_delay = FUZZ_TIME_DELAY_MAX; + } } - } else if(event->type == InputTypeLong) { - model->time_delay += 10; - if(model->time_delay > FUZZ_TIME_DELAY_MAX) { - model->time_delay = FUZZ_TIME_DELAY_MAX; + } else { + // EmuTime ++ + if(event->type == InputTypeShort) { + if(model->emu_time < FUZZ_TIME_DELAY_MAX) { + model->emu_time++; + } + } else if(event->type == InputTypeLong) { + model->emu_time += 10; + if(model->emu_time > FUZZ_TIME_DELAY_MAX) { + model->emu_time = FUZZ_TIME_DELAY_MAX; + } } } } else { @@ -200,6 +341,15 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { }, true); return true; + } else if( + (event->key == InputKeyUp || event->key == InputKeyDown) && + event->type == InputTypeShort) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { model->td_emt_cursor = !model->td_emt_cursor; }, + true); + return true; } return true; @@ -211,6 +361,9 @@ void fuzzer_view_attack_enter(void* context) { void fuzzer_view_attack_exit(void* context) { furi_assert(context); + FuzzerViewAttack* view_attack = context; + with_view_model( + view_attack->view, FuzzerViewAttackModel * model, { model->td_emt_cursor = false; }, true); } FuzzerViewAttack* fuzzer_view_attack_alloc() { @@ -233,11 +386,17 @@ FuzzerViewAttack* fuzzer_view_attack_alloc() { view_attack->view, FuzzerViewAttackModel * model, { - model->time_delay_min = fuzzer_proto_get_min_delay(); - model->time_delay = model->time_delay_min; + model->time_delay = fuzzer_proto_get_def_idle_time(); + model->time_delay_min = 0; // model->time_delay; + + model->emu_time = fuzzer_proto_get_def_emu_time(); + + model->emu_time_min = 2; // model->emu_time; + model->uid_str = furi_string_alloc_set_str("Not_set"); // malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); model->attack_state = FuzzerAttackStateOff; + model->td_emt_cursor = false; // strcpy(model->uid_str, "Not_set"); model->attack_name = "Not_set"; @@ -272,4 +431,14 @@ uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view) { view->view, FuzzerViewAttackModel * model, { time_delay = model->time_delay; }, false); return time_delay; +} + +uint8_t fuzzer_view_attack_get_emu_time(FuzzerViewAttack* view) { + furi_assert(view); + uint8_t emu_time; + + with_view_model( + view->view, FuzzerViewAttackModel * model, { emu_time = model->emu_time; }, false); + + return emu_time; } \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index 41fd857bf..9341ae7e2 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -37,4 +37,6 @@ void fuzzer_view_attack_pause(FuzzerViewAttack* view); void fuzzer_view_attack_end(FuzzerViewAttack* view); -uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); \ No newline at end of file +uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); + +uint8_t fuzzer_view_attack_get_emu_time(FuzzerViewAttack* view); \ No newline at end of file From d86eb870d4651e463241e465bf3198dd21c52579 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:28:26 +0300 Subject: [PATCH 072/370] pre-merge fix --- lib/subghz/SConscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 8fbec94ad..3a0325b71 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -7,11 +7,10 @@ env.Append( SDK_HEADERS=[ File("environment.h"), File("receiver.h"), + File("registry.h"), File("subghz_worker.h"), File("subghz_tx_rx_worker.h"), File("transmitter.h"), - File("registry.h"), - File("protocols/protocol_items.h"), File("protocols/raw.h"), File("blocks/const.h"), File("blocks/decoder.h"), @@ -19,6 +18,7 @@ env.Append( File("blocks/generic.h"), File("blocks/math.h"), File("subghz_setting.h"), + File("subghz_protocol_registry.h"), ], ) From ac15621e74c69bc390c46fc2b71549b57abaa35c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:41:14 +0300 Subject: [PATCH 073/370] fix API --- firmware/targets/f7/api_symbols.csv | 636 ---------------------------- 1 file changed, 636 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3f501e805..2950b419b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -186,7 +186,6 @@ Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, -Header,+,lib/subghz/protocols/protocol_items.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, @@ -548,8 +547,6 @@ Function,-,atoff,float,const char* Function,+,atoi,int,const char* Function,-,atol,long,const char* Function,-,atoll,long long,const char* -Function,-,atomo_decrypt,void,uint8_t* -Function,-,atomo_encrypt,void,uint8_t* Function,-,basename,char*,const char* Function,-,bcmp,int,"const void*, const void*, size_t" Function,-,bcopy,void,"const void*, void*, size_t" @@ -2733,7 +2730,6 @@ Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, u Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_reset_kl,void,SubGhzKeystore* Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" -Function,-,subghz_protocol_alutech_at_4n_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" @@ -2755,547 +2751,22 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" -Function,-,subghz_protocol_came_atomo_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_alutech_at_4n_free,void,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_alutech_at_4n_reset,void,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ansonic_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ansonic_free,void,void* -Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ansonic_reset,void,void* -Function,-,subghz_protocol_decoder_ansonic_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_base_deserialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" -Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bett_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_bett_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_bett_free,void,void* -Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_bett_reset,void,void* -Function,-,subghz_protocol_decoder_bett_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_bin_raw_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bin_raw_data_input_rssi,void,"SubGhzProtocolDecoderBinRAW*, float" -Function,-,subghz_protocol_decoder_bin_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_bin_raw_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_bin_raw_free,void,void* -Function,-,subghz_protocol_decoder_bin_raw_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_bin_raw_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_bin_raw_reset,void,void* -Function,-,subghz_protocol_decoder_bin_raw_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_atomo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_atomo_free,void,void* -Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* -Function,-,subghz_protocol_decoder_came_atomo_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_free,void,void* -Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_reset,void,void* -Function,-,subghz_protocol_decoder_came_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_twee_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_twee_free,void,void* -Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_twee_reset,void,void* -Function,-,subghz_protocol_decoder_came_twee_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_chamb_code_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_chamb_code_free,void,void* -Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* -Function,-,subghz_protocol_decoder_chamb_code_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_clemsa_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_clemsa_free,void,void* -Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_clemsa_reset,void,void* -Function,-,subghz_protocol_decoder_clemsa_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_doitrand_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_doitrand_free,void,void* -Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_doitrand_reset,void,void* -Function,-,subghz_protocol_decoder_doitrand_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_dooya_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_dooya_free,void,void* -Function,-,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_dooya_reset,void,void* -Function,-,subghz_protocol_decoder_dooya_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_faac_slh_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_faac_slh_free,void,void* -Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* -Function,-,subghz_protocol_decoder_faac_slh_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_gate_tx_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_gate_tx_free,void,void* -Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* -Function,-,subghz_protocol_decoder_gate_tx_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_free,void,void* -Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_th12x_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_th12x_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_hormann_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_hormann_free,void,void* -Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_hormann_reset,void,void* -Function,-,subghz_protocol_decoder_hormann_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ido_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ido_free,void,void* -Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ido_reset,void,void* -Function,-,subghz_protocol_decoder_ido_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_keeloq_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_keeloq_free,void,void* -Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_keeloq_reset,void,void* -Function,-,subghz_protocol_decoder_keeloq_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kia_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_kia_free,void,void* -Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_kia_reset,void,void* -Function,-,subghz_protocol_decoder_kia_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_linear_delta3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_linear_delta3_free,void,void* -Function,-,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_linear_delta3_reset,void,void* -Function,-,subghz_protocol_decoder_linear_delta3_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_linear_free,void,void* -Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_linear_reset,void,void* -Function,-,subghz_protocol_decoder_linear_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_magellan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_magellan_free,void,void* -Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_magellan_reset,void,void* -Function,-,subghz_protocol_decoder_magellan_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_marantec_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_marantec_free,void,void* -Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_marantec_reset,void,void* -Function,-,subghz_protocol_decoder_marantec_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_megacode_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_megacode_free,void,void* -Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_megacode_reset,void,void* -Function,-,subghz_protocol_decoder_megacode_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_radio_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_radio_free,void,void* -Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* -Function,-,subghz_protocol_decoder_nero_radio_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_sketch_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flo_free,void,void* -Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flo_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_power_smart_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_power_smart_free,void,void* -Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_power_smart_reset,void,void* -Function,-,subghz_protocol_decoder_power_smart_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_princeton_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_princeton_free,void,void* -Function,-,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_princeton_reset,void,void* -Function,-,subghz_protocol_decoder_princeton_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" Function,+,subghz_protocol_decoder_raw_free,void,void* Function,+,subghz_protocol_decoder_raw_get_string,void,"void*, FuriString*" Function,+,subghz_protocol_decoder_raw_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_scher_khan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_scher_khan_free,void,void* -Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v1_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_smc5326_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_smc5326_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_smc5326_free,void,void* -Function,-,subghz_protocol_decoder_smc5326_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_smc5326_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_smc5326_reset,void,void* -Function,-,subghz_protocol_decoder_smc5326_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_telis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_star_line_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_star_line_free,void,void* -Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_star_line_reset,void,void* -Function,-,subghz_protocol_decoder_star_line_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_encoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_alutech_at_4n_free,void,void* -Function,-,subghz_protocol_encoder_alutech_at_4n_stop,void,void* -Function,-,subghz_protocol_encoder_alutech_at_4n_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_ansonic_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_ansonic_free,void,void* -Function,-,subghz_protocol_encoder_ansonic_stop,void,void* -Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bett_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_bett_free,void,void* -Function,-,subghz_protocol_encoder_bett_stop,void,void* -Function,-,subghz_protocol_encoder_bett_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_bin_raw_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bin_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_bin_raw_free,void,void* -Function,-,subghz_protocol_encoder_bin_raw_stop,void,void* -Function,-,subghz_protocol_encoder_bin_raw_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_atomo_free,void,void* -Function,-,subghz_protocol_encoder_came_atomo_stop,void,void* -Function,-,subghz_protocol_encoder_came_atomo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_free,void,void* -Function,-,subghz_protocol_encoder_came_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_twee_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_twee_free,void,void* -Function,-,subghz_protocol_encoder_came_twee_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_chamb_code_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_chamb_code_free,void,void* -Function,-,subghz_protocol_encoder_chamb_code_stop,void,void* -Function,-,subghz_protocol_encoder_chamb_code_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_clemsa_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_clemsa_free,void,void* -Function,-,subghz_protocol_encoder_clemsa_stop,void,void* -Function,-,subghz_protocol_encoder_clemsa_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_doitrand_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_doitrand_free,void,void* -Function,-,subghz_protocol_encoder_doitrand_stop,void,void* -Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_dooya_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_dooya_free,void,void* -Function,-,subghz_protocol_encoder_dooya_stop,void,void* -Function,-,subghz_protocol_encoder_dooya_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_faac_slh_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_faac_slh_free,void,void* -Function,-,subghz_protocol_encoder_faac_slh_stop,void,void* -Function,-,subghz_protocol_encoder_faac_slh_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_gate_tx_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_gate_tx_free,void,void* -Function,-,subghz_protocol_encoder_gate_tx_stop,void,void* -Function,-,subghz_protocol_encoder_gate_tx_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_free,void,void* -Function,-,subghz_protocol_encoder_holtek_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_holtek_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_stop,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_hormann_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_hormann_free,void,void* -Function,-,subghz_protocol_encoder_hormann_stop,void,void* -Function,-,subghz_protocol_encoder_hormann_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_stop,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_keeloq_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_keeloq_free,void,void* -Function,-,subghz_protocol_encoder_keeloq_stop,void,void* -Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_free,void,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_stop,void,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_linear_delta3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_linear_delta3_free,void,void* -Function,-,subghz_protocol_encoder_linear_delta3_stop,void,void* -Function,-,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_linear_free,void,void* -Function,-,subghz_protocol_encoder_linear_stop,void,void* -Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_magellan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_magellan_free,void,void* -Function,-,subghz_protocol_encoder_magellan_stop,void,void* -Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_marantec_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_marantec_free,void,void* -Function,-,subghz_protocol_encoder_marantec_stop,void,void* -Function,-,subghz_protocol_encoder_marantec_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_megacode_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_megacode_free,void,void* -Function,-,subghz_protocol_encoder_megacode_stop,void,void* -Function,-,subghz_protocol_encoder_megacode_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_radio_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_radio_free,void,void* -Function,-,subghz_protocol_encoder_nero_radio_stop,void,void* -Function,-,subghz_protocol_encoder_nero_radio_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_sketch_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_stop,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flo_free,void,void* -Function,-,subghz_protocol_encoder_nice_flo_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_stop,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_power_smart_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_power_smart_free,void,void* -Function,-,subghz_protocol_encoder_power_smart_stop,void,void* -Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_princeton_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_princeton_free,void,void* -Function,-,subghz_protocol_encoder_princeton_stop,void,void* -Function,-,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_encoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v1_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_smc5326_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_smc5326_free,void,void* -Function,-,subghz_protocol_encoder_smc5326_stop,void,void* -Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_somfy_keytis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_somfy_keytis_free,void,void* -Function,-,subghz_protocol_encoder_somfy_keytis_stop,void,void* -Function,-,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_somfy_telis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_somfy_telis_free,void,void* -Function,-,subghz_protocol_encoder_somfy_telis_stop,void,void* -Function,-,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_star_line_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_star_line_free,void,void* -Function,-,subghz_protocol_encoder_star_line_stop,void,void* -Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* -Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*, _Bool" -Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* @@ -3305,11 +2776,6 @@ Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" -Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t -Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* @@ -3962,108 +3428,6 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, -Variable,-,subghz_protocol_alutech_at_4n,const SubGhzProtocol, -Variable,-,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, -Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_bett,const SubGhzProtocol, -Variable,-,subghz_protocol_bett_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_bett_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_bin_raw,const SubGhzProtocol, -Variable,-,subghz_protocol_bin_raw_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_bin_raw_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_atomo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_twee,const SubGhzProtocol, -Variable,-,subghz_protocol_came_twee_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_twee_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_chamb_code,const SubGhzProtocol, -Variable,-,subghz_protocol_chamb_code_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_chamb_code_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_clemsa,const SubGhzProtocol, -Variable,-,subghz_protocol_clemsa_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, -Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_dooya,const SubGhzProtocol, -Variable,-,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, -Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_gate_tx,const SubGhzProtocol, -Variable,-,subghz_protocol_gate_tx_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_gate_tx_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek_th12x,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_th12x_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_th12x_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_honeywell_wdb,const SubGhzProtocol, -Variable,-,subghz_protocol_honeywell_wdb_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_honeywell_wdb_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_hormann,const SubGhzProtocol, -Variable,-,subghz_protocol_hormann_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_hormann_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ido,const SubGhzProtocol, -Variable,-,subghz_protocol_ido_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ido_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_intertechno_v3,const SubGhzProtocol, -Variable,-,subghz_protocol_intertechno_v3_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_intertechno_v3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_keeloq,const SubGhzProtocol, -Variable,-,subghz_protocol_keeloq_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_kia,const SubGhzProtocol, -Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, -Variable,-,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear,const SubGhzProtocol, -Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_linear_delta3,const SubGhzProtocol, -Variable,-,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_magellan,const SubGhzProtocol, -Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_marantec,const SubGhzProtocol, -Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_megacode,const SubGhzProtocol, -Variable,-,subghz_protocol_megacode_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_megacode_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_radio,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_radio_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_radio_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_sketch,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_sketch_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_sketch_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flo,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flor_s,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flor_s_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flor_s_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_phoenix_v2,const SubGhzProtocol, -Variable,-,subghz_protocol_phoenix_v2_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_phoenix_v2_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_power_smart,const SubGhzProtocol, -Variable,-,subghz_protocol_power_smart_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_power_smart_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_princeton,const SubGhzProtocol, -Variable,-,subghz_protocol_princeton_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, From 58338ff51f6f9857f39ef07d5eb4495cdc02290d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:59:19 +0300 Subject: [PATCH 074/370] Fix RGB patch --- .ci_files/rgb.patch | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/.ci_files/rgb.patch b/.ci_files/rgb.patch index 804034bab..b5141abb9 100644 --- a/.ci_files/rgb.patch +++ b/.ci_files/rgb.patch @@ -1,8 +1,8 @@ diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c -index f91a73f32..b559a79ad 100644 +index 2f947fe..03c4c76 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c -@@ -6,6 +6,7 @@ +@@ -9,6 +9,7 @@ #include "notification.h" #include "notification_messages.h" #include "notification_app.h" @@ -10,7 +10,7 @@ index f91a73f32..b559a79ad 100644 #define TAG "NotificationSrv" -@@ -564,6 +565,7 @@ int32_t notification_srv(void* p) { +@@ -579,6 +580,7 @@ int32_t notification_srv(void* p) { break; case SaveSettingsMessage: notification_save_settings(app); @@ -19,7 +19,7 @@ index f91a73f32..b559a79ad 100644 } diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c -index f5d7a82ca..930c0bd1f 100644 +index 565d4f1..bae9299 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -3,6 +3,7 @@ @@ -30,15 +30,7 @@ index f5d7a82ca..930c0bd1f 100644 #define MAX_NOTIFICATION_SETTINGS 4 -@@ -73,7 +74,6 @@ const bool vibro_value[VIBRO_COUNT] = {false, true}; - static void backlight_changed(VariableItem* item) { - NotificationAppSettings* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); -- - variable_item_set_current_value_text(item, backlight_text[index]); - app->notification->settings.display_brightness = backlight_value[index]; - notification_message(app->notification, &sequence_display_backlight_on); -@@ -125,6 +125,14 @@ static void vibro_changed(VariableItem* item) { +@@ -162,6 +163,14 @@ static void vibro_changed(VariableItem* item) { notification_message(app->notification, &sequence_single_vibro); } @@ -53,8 +45,8 @@ index f5d7a82ca..930c0bd1f 100644 static uint32_t notification_app_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; -@@ -143,7 +151,13 @@ static NotificationAppSettings* alloc_settings() { - uint8_t value_index; +@@ -187,7 +196,13 @@ static NotificationAppSettings* alloc_settings() { + variable_item_set_current_value_text(item, contrast_text[value_index]); item = variable_item_list_add( - app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); @@ -68,17 +60,9 @@ index f5d7a82ca..930c0bd1f 100644 value_index = value_index_float( app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); variable_item_set_current_value_index(item, value_index); -@@ -215,6 +229,7 @@ int32_t notification_settings_app(void* p) { - NotificationAppSettings* app = alloc_settings(); - view_dispatcher_run(app->view_dispatcher); - notification_message_save_settings(app->notification); -+ - free_settings(app); - return 0; - } diff --git a/applications/settings/notification_settings/rgb_backlight.c b/applications/settings/notification_settings/rgb_backlight.c new file mode 100644 -index 000000000..269b544ae +index 0000000..269b544 --- /dev/null +++ b/applications/settings/notification_settings/rgb_backlight.c @@ -0,0 +1,171 @@ @@ -255,7 +239,7 @@ index 000000000..269b544ae +} diff --git a/applications/settings/notification_settings/rgb_backlight.h b/applications/settings/notification_settings/rgb_backlight.h new file mode 100644 -index 000000000..b63d223e6 +index 0000000..b63d223 --- /dev/null +++ b/applications/settings/notification_settings/rgb_backlight.h @@ -0,0 +1,79 @@ @@ -340,7 +324,7 @@ index 000000000..b63d223e6 +const char* rgb_backlight_get_color_text(uint8_t index); \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c -index 83e1603b7..cad5b86cb 100644 +index 83e1603..cad5b86 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -3,6 +3,7 @@ @@ -389,7 +373,7 @@ index 83e1603b7..cad5b86cb 100644 void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period) { diff --git a/lib/drivers/SK6805.c b/lib/drivers/SK6805.c new file mode 100644 -index 000000000..572e1df97 +index 0000000..572e1df --- /dev/null +++ b/lib/drivers/SK6805.c @@ -0,0 +1,101 @@ @@ -496,7 +480,7 @@ index 000000000..572e1df97 +} diff --git a/lib/drivers/SK6805.h b/lib/drivers/SK6805.h new file mode 100644 -index 000000000..7c58956fa +index 0000000..7c58956 --- /dev/null +++ b/lib/drivers/SK6805.h @@ -0,0 +1,51 @@ @@ -552,5 +536,3 @@ index 000000000..7c58956fa + +#endif /* SK6805_H_ */ \ No newline at end of file - -\ No newline at end of file From 754e640c8d2cdcb45e6420d549452251c6e171da Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 7 Jun 2023 06:14:33 +0400 Subject: [PATCH 075/370] [FL-3246] fbt, ufbt: added checks for appid in app manifests(#2720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- scripts/fbt/appmanifest.py | 9 ++++++++- scripts/ufbt/SConstruct | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index eb265cee8..820f5a8c5 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -1,7 +1,8 @@ import os +import re from dataclasses import dataclass, field from enum import Enum -from typing import Callable, List, Optional, Tuple, Union +from typing import Callable, ClassVar, List, Optional, Tuple, Union class FlipperManifestException(Exception): @@ -23,6 +24,8 @@ class FlipperAppType(Enum): @dataclass class FlipperApplication: + APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$") + @dataclass class ExternallyBuiltFile: path: str @@ -84,6 +87,10 @@ class FlipperApplication: def __post_init__(self): if self.apptype == FlipperAppType.PLUGIN: self.stack_size = 0 + if not self.APP_ID_REGEX.match(self.appid): + raise FlipperManifestException( + f"Invalid appid '{self.appid}'. Must match regex '{self.APP_ID_REGEX}'" + ) if isinstance(self.fap_version, str): try: self.fap_version = tuple(int(v) for v in self.fap_version.split(".")) diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index d72de380c..a1acd270a 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -75,7 +75,7 @@ from fbt.util import ( wrap_tempfile, path_as_posix, ) -from fbt.appmanifest import FlipperAppType +from fbt.appmanifest import FlipperAppType, FlipperApplication from fbt.sdk.cache import SdkCache # Base environment with all tools loaded from SDK @@ -410,6 +410,12 @@ dist_env.Alias("vscode_dist", vscode_dist) # Creating app from base template dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template") +if fbt_appid := dist_env.subst("$FBT_APPID"): + if not FlipperApplication.APP_ID_REGEX.match(fbt_appid): + raise UserError( + f"Invalid app id '{fbt_appid}'. App id must match {FlipperApplication.APP_ID_REGEX.pattern}" + ) + app_template_dir = project_template_dir.Dir("app_template") app_template_dist = [] for template_file in app_template_dir.glob("*"): From 6ce098064aed2282437a5df8208f63ec2e263da5 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Wed, 7 Jun 2023 11:51:15 +0300 Subject: [PATCH 076/370] Fuzzer App: use FuzzerPayload & smal fixes --- applications/external/pacs_fuzzer/fuzzer.c | 2 + applications/external/pacs_fuzzer/fuzzer_i.h | 1 + .../pacs_fuzzer/lib/worker/fake_worker.c | 42 ++++++++++--------- .../pacs_fuzzer/lib/worker/fake_worker.h | 6 +-- .../pacs_fuzzer/lib/worker/protocol.c | 16 +++++++ .../pacs_fuzzer/lib/worker/protocol.h | 14 +++++++ .../pacs_fuzzer/lib/worker/protocol_i.h | 12 +----- .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 13 ++---- .../scenes/fuzzer_scene_field_editor.c | 11 ++--- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 9 ++-- applications/external/pacs_fuzzer/todo.md | 4 ++ .../external/pacs_fuzzer/views/attack.c | 10 ++--- .../external/pacs_fuzzer/views/attack.h | 2 +- .../external/pacs_fuzzer/views/field_editor.c | 20 +++++---- .../external/pacs_fuzzer/views/field_editor.h | 5 +-- 15 files changed, 96 insertions(+), 71 deletions(-) diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index c80c18130..113291d0c 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -26,6 +26,7 @@ PacsFuzzerApp* fuzzer_app_alloc() { app->fuzzer_state.proto_index = 0; app->worker = fuzzer_worker_alloc(); + app->payload = fuzzer_payload_alloc(); app->file_path = furi_string_alloc(); @@ -114,6 +115,7 @@ void fuzzer_app_free(PacsFuzzerApp* app) { furi_string_free(app->file_path); + fuzzer_payload_free(app->payload); fuzzer_worker_free(app->worker); free(app); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 63bf85d24..5b58e59d8 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -51,4 +51,5 @@ typedef struct { FuzzerConsts* fuzzer_const; FuzzerWorker* worker; + FuzzerPayload* payload; } PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index e48b1dd32..07b0479b4 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -38,8 +38,8 @@ struct FuzzerWorker { const FuzzerProtocol* protocol; FuzzerWorkerAttackType attack_type; - uint8_t timer_idle_time; - uint8_t timer_emu_time; + uint16_t timer_idle_time_ms; + uint16_t timer_emu_time_ms; uint8_t payload[MAX_PAYLOAD_SIZE]; Stream* uids_stream; @@ -157,7 +157,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { #endif } instance->in_emu_phase = false; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time_ms)); } else { if(!fuzzer_worker_load_key(instance, true)) { fuzzer_worker_pause(instance); // XXX @@ -173,7 +173,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { #endif } instance->in_emu_phase = true; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms)); if(instance->tick_callback) { instance->tick_callback(instance->tick_context); } @@ -187,7 +187,6 @@ void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output furi_assert(instance->protocol); output_key->data_size = instance->protocol->data_size; - output_key->data = malloc(sizeof(output_key->data_size)); memcpy(output_key->data, instance->payload, instance->protocol->data_size); } @@ -258,7 +257,7 @@ bool fuzzer_worker_init_attack_file_dict( bool fuzzer_worker_init_attack_bf_byte( FuzzerWorker* instance, FuzzerProtocolsID protocol_index, - const uint8_t* uid, + const FuzzerPayload* new_uid, uint8_t chusen) { furi_assert(instance); @@ -268,7 +267,7 @@ bool fuzzer_worker_init_attack_bf_byte( instance->attack_type = FuzzerWorkerAttackTypeLoadFile; instance->index = chusen; - memcpy(instance->payload, uid, instance->protocol->data_size); + memcpy(instance->payload, new_uid->data, instance->protocol->data_size); res = true; @@ -349,8 +348,8 @@ FuzzerWorker* fuzzer_worker_alloc() { memset(instance->payload, 0x00, sizeof(instance->payload)); - instance->timer_idle_time = PROTOCOL_DEF_IDLE_TIME; - instance->timer_emu_time = PROTOCOL_DEF_EMU_TIME; + instance->timer_idle_time_ms = PROTOCOL_DEF_IDLE_TIME * 100; + instance->timer_emu_time_ms = PROTOCOL_DEF_EMU_TIME * 100; instance->timer = furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypeOnce, instance); @@ -383,17 +382,22 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_ furi_assert(instance); if(instance->attack_type < FuzzerWorkerAttackTypeMax) { - // if(emu_time == 0) { - // uint8_t temp = idle_time / 2; - // instance->timer_emu_time = temp; - // instance->timer_idle_time = temp + idle_time % 2; - // } else { - instance->timer_idle_time = idle_time; - instance->timer_emu_time = emu_time; - // } + if(idle_time == 0) { + instance->timer_idle_time_ms = 10; + } else { + instance->timer_idle_time_ms = idle_time * 100; + } + if(emu_time == 0) { + instance->timer_emu_time_ms = 10; + } else { + instance->timer_emu_time_ms = emu_time * 100; + } FURI_LOG_D( - TAG, "Emu_time %u Idle_time %u", instance->timer_emu_time, instance->timer_idle_time); + TAG, + "Emu_time %u ms Idle_time %u ms", + instance->timer_emu_time_ms, + instance->timer_idle_time_ms); if(!instance->treead_running) { #if defined(RFID_125_PROTOCOL) @@ -415,7 +419,7 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_ ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif instance->in_emu_phase = true; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms)); return true; } return false; diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 6396525be..8b934f300 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -82,21 +82,21 @@ bool fuzzer_worker_init_attack_file_dict( * * @param instance Pointer to a FuzzerWorker * @param protocol_index index of the selected protocol - * @param uid UID for brute force + * @param new_uid Pointer to a FuzzerPayload with UID for brute force * @param chosen index of chusen byte * @return bool True if initialization is successful */ bool fuzzer_worker_init_attack_bf_byte( FuzzerWorker* instance, FuzzerProtocolsID protocol_index, - const uint8_t* uid, + const FuzzerPayload* new_uid, uint8_t chusen); /** * Get current UID * * @param instance Pointer to a FuzzerWorker - * @param output_key Pointer to a FuzzerWorker, memory for data will be allocated + * @param output_key Pointer to a FuzzerPayload */ void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key); diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index f520037ac..a64fe8767 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -242,6 +242,22 @@ const FuzzerMenuItems fuzzer_menu_items[] = { {"Load UIDs from file", FuzzerAttackIdLoadFileCustomUids}, }; +FuzzerPayload* fuzzer_payload_alloc() { + FuzzerPayload* payload = malloc(sizeof(FuzzerPayload)); + payload->data = malloc(sizeof(payload->data[0]) * MAX_PAYLOAD_SIZE); + + return payload; +} + +void fuzzer_payload_free(FuzzerPayload* payload) { + furi_assert(payload); + + if(payload->data) { + free(payload->data); + } + free(payload); +} + const char* fuzzer_proto_get_name(FuzzerProtocolsID index) { return fuzzer_proto_items[index].name; } diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index 68632b029..9c5315d00 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -31,6 +31,20 @@ struct FuzzerPayload { uint8_t data_size; }; +/** + * Allocate FuzzerPayload + * + * @return FuzzerPayload* pointer to FuzzerPayload + */ +FuzzerPayload* fuzzer_payload_alloc(); + +/** + * Free FuzzerPayload + * + * @param instance Pointer to a FuzzerPayload + */ +void fuzzer_payload_free(FuzzerPayload*); + /** * Get maximum length of UID among all supported protocols * @return Maximum length of UID diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h index 074c50d9d..2f1c65fd7 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -19,7 +19,7 @@ typedef struct FuzzerProtocol FuzzerProtocol; struct ProtoDict { const uint8_t* val; - const uint8_t len; // TODO + const uint8_t len; }; struct FuzzerProtocol { @@ -34,20 +34,10 @@ struct FuzzerProtocol { // #define FUZZ_TIME_DELAY_DEFAULT (10) // #define FUZZ_TIME_DELAY_MAX (70) -// #define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" -// #define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/rfidfuzzer" -// #define FUZZER_APP_KEY_EXTENSION ".rfid" -// #define FUZZER_APP_PATH_KEY_FOLDER "/ext/lfrfid" - // #define MAX_PAYLOAD_SIZE 8 // #define FUZZ_TIME_DELAY_MIN (4) // #define FUZZ_TIME_DELAY_DEFAULT (8) // #define FUZZ_TIME_DELAY_MAX (80) -// #define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" -// #define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/ibtnfuzzer" -// #define FUZZER_APP_KEY_EXTENSION ".ibtn" -// #define FUZZER_APP_PATH_KEY_FOLDER "/ext/ibutton" - extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index 836bbdef5..6424e62b5 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -1,8 +1,6 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -// TODO simlify callbacks and attack state - const NotificationSequence sequence_one_green_50_on_blink_blue = { &message_red_255, &message_delay_50, @@ -18,12 +16,9 @@ static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { furi_assert(app->worker); furi_assert(app->attack_view); - FuzzerPayload uid; - fuzzer_worker_get_current_key(app->worker, &uid); + fuzzer_worker_get_current_key(app->worker, app->payload); - fuzzer_view_attack_set_uid(app->attack_view, uid); - - free(uid.data); + fuzzer_view_attack_set_uid(app->attack_view, app->payload); } static void fuzzer_scene_attack_set_state(PacsFuzzerApp* app, FuzzerAttackState state) { @@ -127,7 +122,6 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == FuzzerAttackStateIdle) { // Start or Continue Attack - // TODO emu_time if(fuzzer_worker_start( app->worker, fuzzer_view_attack_get_time_delay(app->attack_view), @@ -160,7 +154,8 @@ void fuzzer_scene_attack_on_exit(void* context) { furi_assert(context); PacsFuzzerApp* app = context; - // fuzzer_worker_stop(); // XXX + // XXX the scene has no descendants, and the return will be processed in on_event + // fuzzer_worker_stop(); fuzzer_worker_set_uid_chaged_callback(app->worker, NULL, NULL); fuzzer_worker_set_end_callback(app->worker, NULL, NULL); diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index 637eff2d7..4c45bd154 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -14,12 +14,9 @@ void fuzzer_scene_field_editor_on_enter(void* context) { fuzzer_view_field_editor_set_callback( app->field_editor_view, fuzzer_scene_field_editor_callback, app); - FuzzerPayload uid; - fuzzer_worker_get_current_key(app->worker, &uid); + fuzzer_worker_get_current_key(app->worker, app->payload); - fuzzer_view_field_editor_reset_data(app->field_editor_view, uid); - - free(uid.data); + fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload); view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); } @@ -37,11 +34,11 @@ bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) } consumed = true; } else if(event.event == FuzzerCustomEventViewFieldEditorOk) { - // TODO + fuzzer_view_field_editor_get_uid(app->field_editor_view, app->payload); if(fuzzer_worker_init_attack_bf_byte( app->worker, app->fuzzer_state.proto_index, - fuzzer_view_field_editor_get_uid(app->field_editor_view), + app->payload, fuzzer_view_field_editor_get_index(app->field_editor_view))) { scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index cfa43ad87..8ed7e09d4 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -103,8 +103,6 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { // TODO error logic bool loading_ok = false; - uint8_t d_size = fuzzer_proto_get_max_data_size(); - uint8_t* uid; switch(fuzzer_proto_get_attack_id_by_index(app->fuzzer_state.menu_index)) { case FuzzerAttackIdDefaultValues: @@ -119,13 +117,12 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { break; case FuzzerAttackIdBFCustomerID: // TODO - uid = malloc(d_size); - memset(uid, 0x00, d_size); + app->payload->data_size = fuzzer_proto_get_max_data_size(); + memset(app->payload->data, 0x00, app->payload->data_size); loading_ok = fuzzer_worker_init_attack_bf_byte( - app->worker, app->fuzzer_state.proto_index, uid, 0); + app->worker, app->fuzzer_state.proto_index, app->payload, 0); - free(uid); if(!loading_ok) { // error } diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index d0bab30d6..1cbd53c46 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -31,9 +31,13 @@ - [ ] Decide on the display - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` + - [x] Using `FuzzerPayload` to store the uid - [x] `UID_MAX_SIZE` - [x] Add pause - [x] Fix `Custom dict` attack when ended +- [ ] Pause V2 + - [ ] Save logic + - [ ] Switching UIDs if possible - [ ] Worker - [ ] Use `prtocol_id` instead of protocol name - [x] this can be simplified `fuzzer_proto_items` \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index a29e2d966..9787278a6 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -48,17 +48,17 @@ void fuzzer_view_attack_reset_data( true); } -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid) { +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid) { furi_assert(view); - furi_assert(uid.data); + furi_assert(uid->data); with_view_model( view->view, FuzzerViewAttackModel * model, { - furi_string_printf(model->uid_str, "%02X", uid.data[0]); - for(uint8_t i = 1; i < uid.data_size; i++) { - furi_string_cat_printf(model->uid_str, ":%02X", uid.data[i]); + furi_string_printf(model->uid_str, "%02X", uid->data[0]); + for(uint8_t i = 1; i < uid->data_size; i++) { + furi_string_cat_printf(model->uid_str, ":%02X", uid->data[i]); } }, true); diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index 9341ae7e2..66e96d7d6 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -27,7 +27,7 @@ void fuzzer_view_attack_reset_data( const char* attack_name, const char* protocol_name); -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid); +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid); void fuzzer_view_attack_start(FuzzerViewAttack* view); diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index 07a19ae0e..45b5f70a1 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -49,27 +49,33 @@ void fuzzer_view_field_editor_set_callback( void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - const FuzzerPayload new_uid) { + const FuzzerPayload* new_uid) { furi_assert(view_edit); + furi_assert(new_uid->data); with_view_model( view_edit->view, FuzzerViewFieldEditorModel * model, { - memcpy(model->uid, new_uid.data, new_uid.data_size); + memcpy(model->uid, new_uid->data, new_uid->data_size); model->index = 0; model->lo = false; - model->uid_size = new_uid.data_size; + model->uid_size = new_uid->data_size; }, true); } -const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit) { +void fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit, FuzzerPayload* output_uid) { furi_assert(view_edit); - uint8_t* uid; + furi_assert(output_uid); with_view_model( - view_edit->view, FuzzerViewFieldEditorModel * model, { uid = model->uid; }, true); - return uid; + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + output_uid->data_size = model->uid_size; + memcpy(output_uid->data, model->uid, model->uid_size); + }, + true); } uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit) { diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h index f76b5d336..72c5de5e5 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.h +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -21,9 +21,8 @@ View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - const FuzzerPayload new_uid); + const FuzzerPayload* new_uid); -// TODO -const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit); +void fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit, FuzzerPayload* output_uid); uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit); \ No newline at end of file From c763ae6d5cefcc332487478405ad64c8e8fcece0 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Wed, 7 Jun 2023 11:54:20 +0300 Subject: [PATCH 077/370] Fuzzer App: cleanup attack view --- applications/external/pacs_fuzzer/todo.md | 2 +- .../external/pacs_fuzzer/views/attack.c | 62 +------------------ 2 files changed, 2 insertions(+), 62 deletions(-) diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 1cbd53c46..136ec5e99 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -28,7 +28,7 @@ - [ ] Protocol carousel in `main_menu` - [x] prototype - [x] Add the ability to edit emulation time and downtime separately - - [ ] Decide on the display + - [x] Decide on the display - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] Using `FuzzerPayload` to store the uid diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 9787278a6..87aa9f659 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -4,10 +4,6 @@ #include #include -#define ATACK_VIEW_V2 -// #define ATACK_VIEW_V2_1 -#define ATACK_VIEW_V2_2 - #define ATTACK_SCENE_MAX_UID_LENGTH 25 #define UID_MAX_DISPLAYED_LEN (8U) #define LIFT_RIGHT_OFFSET (3) @@ -123,41 +119,6 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name); -#ifndef ATACK_VIEW_V2 - canvas_set_font(canvas, FontSecondary); - snprintf( - temp_str, - sizeof(temp_str), - "Time delay: %d.%d", - model->time_delay / 10, - model->time_delay % 10); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, temp_str); -#elif defined(ATACK_VIEW_V2_1) - - canvas_set_font(canvas, FontSecondary); - if(!model->td_emt_cursor) { - snprintf( - temp_str, - sizeof(temp_str), - "Time delay: %d.%d EmT: %d.%d", - model->time_delay / 10, - model->time_delay % 10, - model->emu_time / 10, - model->emu_time % 10); - } else { - snprintf( - temp_str, - sizeof(temp_str), - "TD: %d.%d Emulation time: %d.%d", - model->time_delay / 10, - model->time_delay % 10, - model->emu_time / 10, - model->emu_time % 10); - } - canvas_draw_str_aligned(canvas, 64, 21, AlignCenter, AlignBottom, temp_str); - -#elif defined(ATACK_VIEW_V2_2) - uint16_t crt; canvas_set_font(canvas, FontPrimary); @@ -178,7 +139,6 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { temp_str, sizeof(temp_str), "EmT: %d.%d", model->emu_time / 10, model->emu_time % 10); canvas_draw_str_aligned( canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str); - } else { canvas_set_font(canvas, FontSecondary); snprintf( @@ -202,21 +162,6 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas, 128 - LIFT_RIGHT_OFFSET - crt - 3, 21, AlignRight, AlignBottom, temp_str); } -#else - - canvas_set_font(canvas, FontSecondary); - snprintf( - temp_str, - sizeof(temp_str), - "Time delay: %d.%d Emu time: %d.%d", - model->time_delay / 10, - model->time_delay % 10, - model->emu_time / 10, - model->emu_time % 10); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, temp_str); - -#endif - canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); @@ -231,11 +176,6 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { if(model->attack_state == FuzzerAttackStateRunning) { elements_button_center(canvas, "Stop"); } else if(model->attack_state == FuzzerAttackStateIdle) { -#ifndef ATACK_VIEW_V2 - elements_button_center(canvas, "Start"); - elements_button_left(canvas, "TD -"); - elements_button_right(canvas, "+ TD"); -#else if(model->td_emt_cursor) { elements_button_center(canvas, "Start"); elements_button_left(canvas, "EmT -"); @@ -245,7 +185,7 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { elements_button_left(canvas, "TD -"); elements_button_right(canvas, "+ TD"); } -#endif + } else if(model->attack_state == FuzzerAttackStateEnd) { // elements_button_center(canvas, "Restart"); // Reset elements_button_left(canvas, "Exit"); From 82de8145b0b123954183e18b688b18b51744c485 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:44:33 +0300 Subject: [PATCH 078/370] Fuzzer App: improved BFCustomerID attack --- .../pacs_fuzzer/helpers/fuzzer_types.h | 6 ++ .../scenes/fuzzer_scene_field_editor.c | 13 +++- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 18 +++-- applications/external/pacs_fuzzer/todo.md | 1 + .../external/pacs_fuzzer/views/field_editor.c | 69 ++++++++++++------- .../external/pacs_fuzzer/views/field_editor.h | 3 +- 6 files changed, 81 insertions(+), 29 deletions(-) diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index e4661ed7b..bb608a5f1 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -15,6 +15,12 @@ typedef enum { } FuzzerAttackState; +typedef enum { + FuzzerFieldEditorStateEditingOn = 0, + FuzzerFieldEditorStateEditingOff, + +} FuzzerFieldEditorState; + typedef enum { FuzzerViewIDPopup, diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index 4c45bd154..ccea123dc 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -16,7 +16,18 @@ void fuzzer_scene_field_editor_on_enter(void* context) { fuzzer_worker_get_current_key(app->worker, app->payload); - fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload); + switch(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneFieldEditor)) { + case FuzzerFieldEditorStateEditingOn: + fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, true); + break; + + case FuzzerFieldEditorStateEditingOff: + fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, false); + break; + + default: + break; + } view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 8ed7e09d4..0d074a121 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -106,7 +106,6 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { switch(fuzzer_proto_get_attack_id_by_index(app->fuzzer_state.menu_index)) { case FuzzerAttackIdDefaultValues: - loading_ok = fuzzer_worker_init_attack_dict(app->worker, app->fuzzer_state.proto_index); @@ -115,15 +114,21 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { fuzzer_scene_main_show_error(app, "Default dictionary\nis empty"); } break; + case FuzzerAttackIdBFCustomerID: // TODO app->payload->data_size = fuzzer_proto_get_max_data_size(); memset(app->payload->data, 0x00, app->payload->data_size); - loading_ok = fuzzer_worker_init_attack_bf_byte( - app->worker, app->fuzzer_state.proto_index, app->payload, 0); + if(fuzzer_worker_init_attack_bf_byte( + app->worker, app->fuzzer_state.proto_index, app->payload, 0)) { + scene_manager_set_scene_state( + app->scene_manager, + FuzzerSceneFieldEditor, + FuzzerFieldEditorStateEditingOff); + scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); - if(!loading_ok) { + } else { // error } break; @@ -136,6 +141,10 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { app->worker, app->fuzzer_state.proto_index, furi_string_get_cstr(app->file_path))) { + scene_manager_set_scene_state( + app->scene_manager, + FuzzerSceneFieldEditor, + FuzzerFieldEditorStateEditingOn); scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); FURI_LOG_I("Scene", "Load ok"); } else { @@ -159,6 +168,7 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { break; default: + fuzzer_scene_main_show_error(app, "Unsuported attack"); break; } diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 136ec5e99..4e3b0e17d 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -17,6 +17,7 @@ #### App functionality - [x] Add `BFCustomerID` attack + - [x] Add the ability to select index - [ ] Save key logic ## Code Improvement diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index 45b5f70a1..bdce0a516 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -35,6 +35,7 @@ typedef struct { uint8_t index; bool lo; + bool allow_edit; } FuzzerViewFieldEditorModel; void fuzzer_view_field_editor_set_callback( @@ -49,7 +50,8 @@ void fuzzer_view_field_editor_set_callback( void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - const FuzzerPayload* new_uid) { + const FuzzerPayload* new_uid, + bool allow_edit) { furi_assert(view_edit); furi_assert(new_uid->data); @@ -61,6 +63,7 @@ void fuzzer_view_field_editor_reset_data( model->index = 0; model->lo = false; model->uid_size = new_uid->data_size; + model->allow_edit = allow_edit; }, true); } @@ -93,15 +96,20 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m #ifdef FIELD_EDITOR_V2 canvas_set_font(canvas, FontSecondary); + if(model->allow_edit) { + canvas_draw_icon(canvas, 2, 4, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 8, 4, &I_ButtonRight_4x7); - canvas_draw_icon(canvas, 2, 4, &I_ButtonLeft_4x7); - canvas_draw_icon(canvas, 8, 4, &I_ButtonRight_4x7); + canvas_draw_icon_ex(canvas, 62, 3, &I_Pin_arrow_up_7x9, IconRotation180); + canvas_draw_icon(canvas, 69, 3, &I_Pin_arrow_up_7x9); - canvas_draw_icon_ex(canvas, 62, 3, &I_Pin_arrow_up_7x9, IconRotation180); - canvas_draw_icon(canvas, 69, 3, &I_Pin_arrow_up_7x9); - - canvas_draw_str(canvas, 14, 10, "select byte"); - canvas_draw_str(canvas, 79, 10, "adjust byte"); + canvas_draw_str(canvas, 14, 10, "select byte"); + canvas_draw_str(canvas, 79, 10, "adjust byte"); + } else { + canvas_draw_icon(canvas, 35, 4, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 41, 4, &I_ButtonRight_4x7); + canvas_draw_str(canvas, 49, 10, "select byte"); + } char msg_index[18]; canvas_set_font(canvas, FontPrimary); @@ -177,20 +185,29 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m w -= 11; // '<' & '>' w /= 2; - if(model->lo) { - canvas_draw_line( - canvas, - GUI_DISPLAY_HORIZONTAL_CENTER + 1, - EDITOR_STRING_Y + 2, - GUI_DISPLAY_HORIZONTAL_CENTER + w, - EDITOR_STRING_Y + 2); + if(model->allow_edit) { + if(model->lo) { + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER + 1, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER + w, + EDITOR_STRING_Y + 2); + } else { + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER - w, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER - 1, + EDITOR_STRING_Y + 2); + } } else { - canvas_draw_line( - canvas, - GUI_DISPLAY_HORIZONTAL_CENTER - w, - EDITOR_STRING_Y + 2, - GUI_DISPLAY_HORIZONTAL_CENTER - 1, - EDITOR_STRING_Y + 2); + // canvas_draw_line( + // canvas, + // GUI_DISPLAY_HORIZONTAL_CENTER - w, + // EDITOR_STRING_Y + 2, + // GUI_DISPLAY_HORIZONTAL_CENTER + w, + // EDITOR_STRING_Y + 2); } // ####### Editor ####### } @@ -211,6 +228,9 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { FuzzerViewFieldEditorModel * model, { if(event->type == InputTypeShort) { + if(!model->allow_edit) { + model->lo = false; + } if(model->index > 0 || model->lo) { if(!model->lo) { model->index--; @@ -230,6 +250,9 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { FuzzerViewFieldEditorModel * model, { if(event->type == InputTypeShort) { + if(!model->allow_edit) { + model->lo = true; + } if(model->index < (model->uid_size - 1) || !model->lo) { if(model->lo) { model->index++; @@ -248,7 +271,7 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { view_edit->view, FuzzerViewFieldEditorModel * model, { - if(event->type == InputTypeShort) { + if(event->type == InputTypeShort && model->allow_edit) { if(model->lo) { model->uid[model->index] = (model->uid[model->index] & 0xF0) | ((model->uid[model->index] + 1) & 0x0F); @@ -265,7 +288,7 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { view_edit->view, FuzzerViewFieldEditorModel * model, { - if(event->type == InputTypeShort) { + if(event->type == InputTypeShort && model->allow_edit) { if(model->lo) { model->uid[model->index] = (model->uid[model->index] & 0xF0) | ((model->uid[model->index] - 1) & 0x0F); diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h index 72c5de5e5..d81538bf8 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.h +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -21,7 +21,8 @@ View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - const FuzzerPayload* new_uid); + const FuzzerPayload* new_uid, + bool allow_edit); void fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit, FuzzerPayload* output_uid); From 09fae620d9bd4fd6ce6bfb263df074739fd122f8 Mon Sep 17 00:00:00 2001 From: glebmashanov <65850300+glebmashanov@users.noreply.github.com> Date: Wed, 7 Jun 2023 16:30:26 +0300 Subject: [PATCH 079/370] Map parser licence description (#2739) * Add map parser licence description * Add map parser copyright text & licence note --------- Co-authored-by: hedger --- scripts/map_parser.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/scripts/map_parser.py b/scripts/map_parser.py index c0c34e3d1..1efc4fe82 100755 --- a/scripts/map_parser.py +++ b/scripts/map_parser.py @@ -3,6 +3,29 @@ # Requiremets: # cxxfilt==0.3.0 +# Most part of this code written by Lars-Dominik Braun https://github.com/PromyLOPh/linkermapviz +# and distributes under MIT licence + +# Copyright (c) 2017 Lars-Dominik Braun +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import sys import re import os From 921ab90e467af697d0de484a79e931606b049549 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 19:58:42 +0100 Subject: [PATCH 080/370] Lowercase appid's --- applications/debug/uart_echo/application.fam | 2 +- applications/external/airmouse/application.fam | 2 +- applications/external/avr_isp_programmer/avr_isp_app_i.h | 2 +- applications/external/bpmtapper/application.fam | 2 +- applications/external/bpmtapper/bpm.c | 2 +- applications/external/brainfuck/application.fam | 2 +- applications/external/brainfuck/brainfuck_i.h | 2 +- applications/external/caesarcipher/application.fam | 2 +- applications/external/calculator/application.fam | 2 +- applications/external/esp32cam_camera/application.fam | 2 +- .../external/esp32cam_marauder_companion/application.fam | 2 +- .../external/esp32cam_morseflasher/application.fam | 2 +- .../external/esp32cam_morseflasher/uart_text_input.c | 2 +- .../external/esp32cam_motion_detection/application.fam | 2 +- applications/external/esp32cam_nannycam/application.fam | 2 +- applications/external/esp32cam_qrcode/application.fam | 2 +- applications/external/game_of_life/application.fam | 2 +- applications/external/ifttt/application.fam | 2 +- applications/external/mandelbrot/application.fam | 2 +- applications/external/music_beeper/application.fam | 2 +- applications/external/music_beeper/music_beeper.c | 2 +- applications/external/nrf24scan/application.fam | 2 +- applications/external/ocarina/application.fam | 2 +- applications/external/orgasmotron/application.fam | 2 +- applications/external/paint/application.fam | 2 +- applications/external/sam/application.fam | 8 ++++---- applications/external/subghz_bruteforcer/application.fam | 2 +- applications/external/subghz_bruteforcer/subbrute_i.h | 4 ++-- applications/external/subghz_remote/application.fam | 2 +- applications/external/tama_p1/application.fam | 2 +- applications/external/tanksgame/application.fam | 2 +- applications/external/tanksgame/tanks_game.c | 2 +- applications/external/timelapse/application.fam | 2 +- applications/external/timelapse/zeitraffer.c | 2 +- applications/external/videopoker/application.fam | 2 +- applications/external/wifi_deauther/application.fam | 2 +- 36 files changed, 40 insertions(+), 40 deletions(-) diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index ecdc847cd..3e262a09d 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -1,5 +1,5 @@ App( - appid="UART_Echo", + appid="uart_echo", name="[GPIO] UART Echo", apptype=FlipperAppType.DEBUG, entry_point="uart_echo_app", diff --git a/applications/external/airmouse/application.fam b/applications/external/airmouse/application.fam index 7bdba948a..1164b66de 100644 --- a/applications/external/airmouse/application.fam +++ b/applications/external/airmouse/application.fam @@ -1,5 +1,5 @@ App( - appid="Air_Mouse", + appid="air_mouse", name="[BMI160] Air Mouse", apptype=FlipperAppType.EXTERNAL, entry_point="air_mouse_app", diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.h b/applications/external/avr_isp_programmer/avr_isp_app_i.h index 17c69f8f2..819e3f45f 100644 --- a/applications/external/avr_isp_programmer/avr_isp_app_i.h +++ b/applications/external/avr_isp_programmer/avr_isp_app_i.h @@ -41,4 +41,4 @@ typedef struct { AvrIspError error; } AvrIspApp; -bool avr_isp_load_from_file(AvrIspApp* app); \ No newline at end of file +bool avr_isp_load_from_file(AvrIspApp* app); diff --git a/applications/external/bpmtapper/application.fam b/applications/external/bpmtapper/application.fam index 93c4179c5..ec8ca5baf 100644 --- a/applications/external/bpmtapper/application.fam +++ b/applications/external/bpmtapper/application.fam @@ -1,5 +1,5 @@ App( - appid="BPM_Tapper", + appid="bpm_tapper", name="BPM Tapper", apptype=FlipperAppType.EXTERNAL, entry_point="bpm_tapper_app", diff --git a/applications/external/bpmtapper/bpm.c b/applications/external/bpmtapper/bpm.c index 5720ac4d2..397b1a3b5 100644 --- a/applications/external/bpmtapper/bpm.c +++ b/applications/external/bpmtapper/bpm.c @@ -4,7 +4,7 @@ #include #include #include -#include "BPM_Tapper_icons.h" +#include "bpm_tapper_icons.h" typedef enum { EventTypeTick, diff --git a/applications/external/brainfuck/application.fam b/applications/external/brainfuck/application.fam index 716c44a6a..b4cab7be2 100644 --- a/applications/external/brainfuck/application.fam +++ b/applications/external/brainfuck/application.fam @@ -1,5 +1,5 @@ App( - appid="Brainfuck", + appid="brainfuck", name="Brainfuck", apptype=FlipperAppType.EXTERNAL, entry_point="brainfuck_app", diff --git a/applications/external/brainfuck/brainfuck_i.h b/applications/external/brainfuck/brainfuck_i.h index 3940cecad..d3d27dcbd 100644 --- a/applications/external/brainfuck/brainfuck_i.h +++ b/applications/external/brainfuck/brainfuck_i.h @@ -29,7 +29,7 @@ typedef unsigned char byte; #include #include -#include +#include #include #include diff --git a/applications/external/caesarcipher/application.fam b/applications/external/caesarcipher/application.fam index 4f438d2b3..487494640 100644 --- a/applications/external/caesarcipher/application.fam +++ b/applications/external/caesarcipher/application.fam @@ -1,5 +1,5 @@ App( - appid="Caesar_Cipher", + appid="caesar_cipher", name="Caesar Cipher", apptype=FlipperAppType.EXTERNAL, entry_point="caesar_cipher_app", diff --git a/applications/external/calculator/application.fam b/applications/external/calculator/application.fam index ac78af7a5..195a62143 100644 --- a/applications/external/calculator/application.fam +++ b/applications/external/calculator/application.fam @@ -1,5 +1,5 @@ App( - appid="Calculator", + appid="calculator", name="Calculator", apptype=FlipperAppType.EXTERNAL, entry_point="calculator_app", diff --git a/applications/external/esp32cam_camera/application.fam b/applications/external/esp32cam_camera/application.fam index 459413817..a0e0ede4c 100644 --- a/applications/external/esp32cam_camera/application.fam +++ b/applications/external/esp32cam_camera/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Camera", + appid="mayhem_camera", name="[MAYHEM] Camera", apptype=FlipperAppType.EXTERNAL, entry_point="camera_app", diff --git a/applications/external/esp32cam_marauder_companion/application.fam b/applications/external/esp32cam_marauder_companion/application.fam index 430d64105..800cb92be 100644 --- a/applications/external/esp32cam_marauder_companion/application.fam +++ b/applications/external/esp32cam_marauder_companion/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Marauder", + appid="mayhem_marauder", name="[MAYHEM] Marauder", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_marauder_app", diff --git a/applications/external/esp32cam_morseflasher/application.fam b/applications/external/esp32cam_morseflasher/application.fam index c15207015..037e63c81 100644 --- a/applications/external/esp32cam_morseflasher/application.fam +++ b/applications/external/esp32cam_morseflasher/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_MorseFlash", + appid="mayhem_morseflash", name="[MAYHEM] Morse Flasher", apptype=FlipperAppType.EXTERNAL, entry_point="uart_terminal_app", diff --git a/applications/external/esp32cam_morseflasher/uart_text_input.c b/applications/external/esp32cam_morseflasher/uart_text_input.c index 4e2336254..5b2b1033d 100644 --- a/applications/external/esp32cam_morseflasher/uart_text_input.c +++ b/applications/external/esp32cam_morseflasher/uart_text_input.c @@ -1,6 +1,6 @@ #include "uart_text_input.h" #include -#include "MAYHEM_MorseFlash_icons.h" +#include "mayhem_morseflash_icons.h" #include struct UART_TextInput { diff --git a/applications/external/esp32cam_motion_detection/application.fam b/applications/external/esp32cam_motion_detection/application.fam index 9a9b4fcc9..57a5396c5 100644 --- a/applications/external/esp32cam_motion_detection/application.fam +++ b/applications/external/esp32cam_motion_detection/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Motion", + appid="mayhem_motion", name="[MAYHEM] Motion detection", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/esp32cam_nannycam/application.fam b/applications/external/esp32cam_nannycam/application.fam index 1c4831d7c..75f2be783 100644 --- a/applications/external/esp32cam_nannycam/application.fam +++ b/applications/external/esp32cam_nannycam/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_NannyCam", + appid="mayhem_nannycam", name="[MAYHEM] Nanny Cam", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/esp32cam_qrcode/application.fam b/applications/external/esp32cam_qrcode/application.fam index bd74f26b2..73c418510 100644 --- a/applications/external/esp32cam_qrcode/application.fam +++ b/applications/external/esp32cam_qrcode/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_QRcode", + appid="mayhem_qrcode", name="[MAYHEM] QR Code", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/game_of_life/application.fam b/applications/external/game_of_life/application.fam index 55e244d22..6962bf23e 100644 --- a/applications/external/game_of_life/application.fam +++ b/applications/external/game_of_life/application.fam @@ -1,5 +1,5 @@ App( - appid="GameOfLife", + appid="gameoflife", name="Game of Life", apptype=FlipperAppType.EXTERNAL, entry_point="game_of_life_app", diff --git a/applications/external/ifttt/application.fam b/applications/external/ifttt/application.fam index 6a3c4986e..cdb439f26 100644 --- a/applications/external/ifttt/application.fam +++ b/applications/external/ifttt/application.fam @@ -1,5 +1,5 @@ App( - appid="ESP8266_IFTTT_Virtual_Button", + appid="esp8266_ifttt_virtual_button", name="[ESP8266] IFTTT Virtual Button", apptype=FlipperAppType.EXTERNAL, entry_point="ifttt_virtual_button_app", diff --git a/applications/external/mandelbrot/application.fam b/applications/external/mandelbrot/application.fam index 773835fe5..82cc9871a 100644 --- a/applications/external/mandelbrot/application.fam +++ b/applications/external/mandelbrot/application.fam @@ -1,5 +1,5 @@ App( - appid="MandelbrotSet", + appid="mandelbrotset", name="Mandelbrot Set", apptype=FlipperAppType.EXTERNAL, entry_point="mandelbrot_app", diff --git a/applications/external/music_beeper/application.fam b/applications/external/music_beeper/application.fam index 39b9babba..c63a4f717 100644 --- a/applications/external/music_beeper/application.fam +++ b/applications/external/music_beeper/application.fam @@ -1,5 +1,5 @@ App( - appid="Music_Beeper", + appid="music_beeper", name="Music Beeper", apptype=FlipperAppType.EXTERNAL, entry_point="music_beeper_app", diff --git a/applications/external/music_beeper/music_beeper.c b/applications/external/music_beeper/music_beeper.c index f743e3c98..f3b4828d3 100644 --- a/applications/external/music_beeper/music_beeper.c +++ b/applications/external/music_beeper/music_beeper.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/applications/external/nrf24scan/application.fam b/applications/external/nrf24scan/application.fam index c346112e2..1e7bd3efe 100644 --- a/applications/external/nrf24scan/application.fam +++ b/applications/external/nrf24scan/application.fam @@ -1,5 +1,5 @@ App( - appid="Nrf24_Scanner", + appid="nrf24_scanner", name="[NRF24] Scanner", apptype=FlipperAppType.EXTERNAL, entry_point="nrf24scan_app", diff --git a/applications/external/ocarina/application.fam b/applications/external/ocarina/application.fam index 192cb2f16..8a28d0927 100644 --- a/applications/external/ocarina/application.fam +++ b/applications/external/ocarina/application.fam @@ -1,5 +1,5 @@ App( - appid="Ocarina", + appid="ocarina", name="Ocarina", apptype=FlipperAppType.EXTERNAL, entry_point="ocarina_app", diff --git a/applications/external/orgasmotron/application.fam b/applications/external/orgasmotron/application.fam index 2fd456339..29d968fb5 100644 --- a/applications/external/orgasmotron/application.fam +++ b/applications/external/orgasmotron/application.fam @@ -1,5 +1,5 @@ App( - appid="Orgasmotron", + appid="orgasmotron", name="Orgasmotron", apptype=FlipperAppType.EXTERNAL, entry_point="orgasmotron_app", diff --git a/applications/external/paint/application.fam b/applications/external/paint/application.fam index d727ab2d2..e4b7b4fa8 100644 --- a/applications/external/paint/application.fam +++ b/applications/external/paint/application.fam @@ -1,5 +1,5 @@ App( - appid="Paint", + appid="paint", name="Paint", apptype=FlipperAppType.EXTERNAL, entry_point="paint_app", diff --git a/applications/external/sam/application.fam b/applications/external/sam/application.fam index dc0641b3c..97b557db2 100644 --- a/applications/external/sam/application.fam +++ b/applications/external/sam/application.fam @@ -1,5 +1,5 @@ App( - appid="SAM", + appid="sam", name="SAM AYBABTU", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app", @@ -14,7 +14,7 @@ App( fap_icon_assets="icons", ) App( - appid="SAM_YES", + appid="sam_yes", name="SAM YES", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_yes", @@ -29,7 +29,7 @@ App( fap_icon_assets="icons", ) App( - appid="SAM_NO", + appid="sam_no", name="SAM NO", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_no", @@ -44,7 +44,7 @@ App( fap_icon_assets="icons", ) App( - appid="SAM_WTF", + appid="sam_wtf", name="SAM WTF", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_wtf", diff --git a/applications/external/subghz_bruteforcer/application.fam b/applications/external/subghz_bruteforcer/application.fam index f756ae51a..cfd354f48 100644 --- a/applications/external/subghz_bruteforcer/application.fam +++ b/applications/external/subghz_bruteforcer/application.fam @@ -1,5 +1,5 @@ App( - appid="SubGHz_Bruteforcer", + appid="subghz_bruteforcer", name="Sub-GHz Bruteforcer", apptype=FlipperAppType.EXTERNAL, entry_point="subbrute_app", diff --git a/applications/external/subghz_bruteforcer/subbrute_i.h b/applications/external/subghz_bruteforcer/subbrute_i.h index c50a7ed9b..59afaa231 100644 --- a/applications/external/subghz_bruteforcer/subbrute_i.h +++ b/applications/external/subghz_bruteforcer/subbrute_i.h @@ -16,7 +16,7 @@ #include #include -#include "SubGHz_Bruteforcer_icons.h" +#include "subghz_bruteforcer_icons.h" #include @@ -77,4 +77,4 @@ struct SubBruteState { void subbrute_show_loading_popup(void* context, bool show); void subbrute_text_input_callback(void* context); -void subbrute_popup_closed_callback(void* context); \ No newline at end of file +void subbrute_popup_closed_callback(void* context); diff --git a/applications/external/subghz_remote/application.fam b/applications/external/subghz_remote/application.fam index 7f12c31f9..e2229e505 100644 --- a/applications/external/subghz_remote/application.fam +++ b/applications/external/subghz_remote/application.fam @@ -1,5 +1,5 @@ App( - appid="SubGHz_Remote", + appid="subghz_remote", name="Sub-GHz Remote", apptype=FlipperAppType.EXTERNAL, entry_point="subghz_remote_app", diff --git a/applications/external/tama_p1/application.fam b/applications/external/tama_p1/application.fam index 93ee53aa5..e51281ff7 100644 --- a/applications/external/tama_p1/application.fam +++ b/applications/external/tama_p1/application.fam @@ -1,5 +1,5 @@ App( - appid="TAMA_P1", + appid="tama_p1", name="Tamagotchi", apptype=FlipperAppType.EXTERNAL, entry_point="tama_p1_app", diff --git a/applications/external/tanksgame/application.fam b/applications/external/tanksgame/application.fam index 748988297..f4fe49723 100644 --- a/applications/external/tanksgame/application.fam +++ b/applications/external/tanksgame/application.fam @@ -1,5 +1,5 @@ App( - appid="Tanks", + appid="tanks", name="Tanks", apptype=FlipperAppType.EXTERNAL, entry_point="tanks_game_app", diff --git a/applications/external/tanksgame/tanks_game.c b/applications/external/tanksgame/tanks_game.c index e8fb988fa..52ce6b36e 100644 --- a/applications/external/tanksgame/tanks_game.c +++ b/applications/external/tanksgame/tanks_game.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "constants.h" diff --git a/applications/external/timelapse/application.fam b/applications/external/timelapse/application.fam index ba5babc7e..f813b1f8e 100644 --- a/applications/external/timelapse/application.fam +++ b/applications/external/timelapse/application.fam @@ -1,5 +1,5 @@ App( - appid="GPIO_Timelapse", + appid="gpio_timelapse", name="[GPIO] Timelapse", apptype=FlipperAppType.EXTERNAL, entry_point="zeitraffer_app", diff --git a/applications/external/timelapse/zeitraffer.c b/applications/external/timelapse/zeitraffer.c index 7a300fdcf..c5430bfdb 100644 --- a/applications/external/timelapse/zeitraffer.c +++ b/applications/external/timelapse/zeitraffer.c @@ -5,7 +5,7 @@ #include #include #include "gpio_item.h" -#include "GPIO_Timelapse_icons.h" +#include "gpio_timelapse_icons.h" #define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/timelapse" #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/timelapse.conf" diff --git a/applications/external/videopoker/application.fam b/applications/external/videopoker/application.fam index e40953fb4..31c74422a 100644 --- a/applications/external/videopoker/application.fam +++ b/applications/external/videopoker/application.fam @@ -1,5 +1,5 @@ App( - appid="VideoPoker", + appid="videopoker", name="Video Poker", apptype=FlipperAppType.EXTERNAL, entry_point="video_poker_app", diff --git a/applications/external/wifi_deauther/application.fam b/applications/external/wifi_deauther/application.fam index 07ceb543b..5b784bb10 100644 --- a/applications/external/wifi_deauther/application.fam +++ b/applications/external/wifi_deauther/application.fam @@ -1,5 +1,5 @@ App( - appid="ESP8266_Wifi_Deauther_v2", + appid="esp8266_wifi_deauther_v2", name="[ESP8266] Deauther v2", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_deauther_app", From efa8cdaa7e6dcafa04348e95a17b8cf449ef38cf Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 19:58:55 +0100 Subject: [PATCH 081/370] Add back missing nfc icon --- assets/icons/NFC/ArrowC_1_36x36.png | Bin 0 -> 3692 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/icons/NFC/ArrowC_1_36x36.png diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0c6dd0cb2b7cb6eebb1507fa68a8dbda11b146 GIT binary patch literal 3692 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 literal 0 HcmV?d00001 From cd505b1b7b84131f35a717b8bd185bf404f16da8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 20:07:43 +0100 Subject: [PATCH 082/370] Why did this get deleted lol --- .../passgen/icons/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 applications/external/passgen/icons/Pin_back_arrow_10x8.png diff --git a/applications/external/passgen/icons/Pin_back_arrow_10x8.png b/applications/external/passgen/icons/Pin_back_arrow_10x8.png new file mode 100644 index 0000000000000000000000000000000000000000..3bafabd144864b575144c75b592e5eaf53974566 GIT binary patch literal 3606 zcmaJ@c{r49+rKTOBBhHB}+m>LbCLY=Y4wK?~kwVKJMeb&hxs?-|t+n40QpA+b4G8*k_>A)gsvzul2%)`{+ zGXO-B3u=_{$d$PU5YEZSn%Bo%6nB$X*pi8HtvlN(j>)<>oU^ms-{SJc!?CVM_kGpq zD|mb=fG|Jac@dmEE>EYKyFP!dPw~V2q0~L3V4zJ7VgZs-lDyFoU9CnK9lA z{|)s3FeAcdMKT|ltq9$x0m1;iQ-6nS!_cqj3MXxM0Gt2}LS)A!gg7{$QQxIe9%xhs z9ymYp6$g?4Aeep95(3@bioPky5s{%vM(c>C~+;D?q3rCl<9Vk3~u)C^5I%(w`)RT2PH zm)f7N?K9(ykBtnC`Hctjzt`uk1dC{xK3DmG+T--QM)Dliz9M@cHh&jC)x2t{F@ZnKih0C+}OXW@w z`v&$?T!Pj1rsQGSiPMN#jg(cf#BeEqd)~3u;mM}Qyx`i%uR_AH()f-rz&vtJ?~1BK z0wCjWh+r=QKw`~Oyt$4L(2|<}2>>cTD<8d+q=bD10syO=GrJ#HY?6E~&#jfte6C(u zt0YX=Xk{+Bqt-;ma^pzUR`Hw4DHbX&wa9MK#}7nQbGD=p$&@~a?~@uIls$T8lCHGT zTRHoMa^-n3QHw^99AP{1;ufE{Zb&OgDJ@PELckbai^>O2T$Dcqsc&TD3l~}jCU{~r zzv(gLjjtXx|H*H&$^=ebjw433!=?SMd>|aXa>3gB5?)oiL6JC$H*$+NBC6x}hAF7kW)t|J z9m26ua#NsV=VV?4pXG3D@mM_ij@FcBscZ$vT`c+>{Ka38#5<0qS`o5Kbu1s`Lk`}C ztNnHRw(Z$k$NrL*^Gd|*kZ!s*;vl|Vi-WL}unWTUV)XKz^G!Qs$eCE}Ne-py;|QoE ziVIFnDC2DAI9^+BdO1=ikF38qj1|k>fy+;lJzzvK8x_5E17Vq#bN5h7VfH)F-HXT@ zhwUgiVNOuz3x#rqq3K#J8H#9LzFuDEn{={2c`*Pw!K@JLkKSgT`X;p_=<}wD@rmf~ z;gVA4rJ@@!K08%{R8FWAD3_@~)3CQUyiHAObb-A`sHOQ|-+Z0sir>Ak`=mm`YuRLE zvRiUw^7vgB*AQ2;PWD|1mwT?8?;UeHb=$`Ek<+I_v3H91It$fZpB3&YZpDS;;+@(K zdF54mt)Bf!lqxwNW0P|pljlM#d!=%9yW%SZX%=tU#c&gu)D60B?{lPNX$l**VOcE< zdIIZ=4!P^c^-J)}8av)1B>n2);EeHy%mc04Tcui0=!xi=={@WUEb=RgEZW->(No>y zGtHP*oSy9AhtjjmvvjlOkrd=&s943GibEAK6}_QtUrgT;C)pEX^RMTnC;HoM=PBRw z=9RwiyZG%Idtrv4Jsg!__&(xHGl%#&=sLN)edgTIoh`h8iiEm=ymq_1zsj}0Uhw~9 z#8NW#s4ujm8iU4JvG{?xr?d;JWxCeN2BzQy;MMf~vb=1*A#83ixqIOEV` zVaGg#~3WwEx!kV?Q+q$;Ioo@pT$VAd^FJUK|pMWk7 z+6G@N*C4B;DJ`9n-?bZYSO3eQQfKCI=Av#Fcf@1azbbAvzVOP^{k?%t7-9b0z+hZ3 zaVn!cs{C&G8PM z+2JN0Mjo7#`(m!krk0qEMuRP#pvsP;1yp-=xo_t(VjQijbFbzedRSI|z~tIkmRs_| zzW)8E&_4stJKBW4G7xjb>97-2u07S9vv;%V`p9kjaQuUwaZ+YdW*$z8oKmXu9#*!q z%+XIrCsAsIJw|!0mU!Xy;)v!_$Xu^Na16FRuM}78B&~>r-qB$lQ9i;d$5deszcU!{ zTl=!4DREZuWEJOuQ~85O-Q_Hg*+EE+^)p4ySZAeheYhvC!k0y!={Us;;FYATIt}A- zuHORLec$46(H*yLp>@u>8zvVfHSws$-w!_}DiD%=UHO5jok!eG?^a6o;?lWyihn$? zDIXhlckt>wInSo_^n5%}_Ii2}Gnqe0E+&@qiXwmuR{ESqQ+U(U)H80A6kIb79 zf%9=Kr7f>pM2rYV(?^=0aC^Vq+>^Huk#*XW=eAmOudMomc28GLfB11cI@{U7;B zQ-8QzAye z?YX)QgQSmUMA3ROrqjb8(+}^Keqk~C{I7xACr^BG`h2tXW#7w|fwa?Q^Pou#Tc-nA z6Ux=gqvW7&R`EYy$;(ndrfyqZ_A8PP|3nOJFp782&dJ(|nq3+>oA{}~w;(&q!3^~- zt&hEkT}cb_JmgvBk8aC0Q(}I_mU%5U&3zn?_nfJue}^pk^lFtIEJ78dY$NHbLzw$V zXp^Kx-n6?(G4s3qJ66M%C`$TCPDSu}Lmjrwww;{p%X+9*d9fjae!jTBR?Bh)&695p|Np`_A@%C6Gkw(!c ztlQ|bD0BfD08GqSbOJGm#02}0{K-@lg#WAt0w(*SAnr!?Fncs1cZ-)AAzU~M!*noC|vOF)r0RvA`FmlWAHx@MBtF&>xaZy+5F>9 zprIfEOeP%(g@%WR>xUcY(-{6xxUsP@6o!Bz5PAX&y%08)Nnq(wLo|OgSdl`A3^JWb zrcuG`j07KAC=&${1pA*XDD;16sUiPVN>DQ>i$I6M^|Nl)Xlz**5m^jjZ zpQ#thS=L9?WiG40+mRzvqC`xB>H5sFVffs4KqX-!S)&$7{TGz=zWF=INHY2 z0tT}-KpPtw|HfL;h@lh`mH8X%`(G^lkJ$BrpwI=Ltw;=V7|GX$L8E~G&KgPnV=RW& zf8_fI>-)!83~m01g$ja!uJ`tT_4@agV1U-ee}`9~{5$?6s$k|Bg5ln!QST+V7#p3i zF4n&y*YC(C3v7{K(X_L&aAEcMczb*MMhV&2h)M`^tW<_XOB8+kL0OWLfY3%j)E-d2 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 From cca787b96d304c084e68c73f1cf13954f399ec41 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 20:35:45 +0100 Subject: [PATCH 083/370] Dont save setting files on load fail --- applications/services/bt/bt_service/bt.c | 4 +--- applications/services/desktop/desktop.c | 4 +--- applications/services/notification/notification_app.c | 6 +----- applications/services/power/power_service/power.c | 1 - 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 60ab1dd84..b8cb09734 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -121,9 +121,7 @@ Bt* bt_alloc() { bt->max_packet_size = FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX; bt->profile = BtProfileSerial; // Load settings - if(!bt_settings_load(&bt->bt_settings)) { - bt_settings_save(&bt->bt_settings); - } + bt_settings_load(&bt->bt_settings); // Keys storage bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH); // Alloc queue diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 956a1cbf8..719110356 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -457,10 +457,8 @@ int32_t desktop_srv(void* p) { Desktop* desktop = desktop_alloc(); - bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(!loaded) { + if(!DESKTOP_SETTINGS_LOAD(&desktop->settings)) { memset(&desktop->settings, 0, sizeof(desktop->settings)); - DESKTOP_SETTINGS_SAVE(&desktop->settings); } desktop_clock_toggle_view(desktop, desktop->settings.display_clock); diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index b7b789104..cce407cc4 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -497,11 +497,7 @@ int32_t notification_srv(void* p) { UNUSED(p); NotificationApp* app = notification_app_alloc(); - if(furi_hal_is_normal_boot()) { - if(!notification_load_settings(app)) { - notification_save_settings(app); - } - } + notification_load_settings(app); notification_vibro_off(); notification_sound_off(); diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 51fcb3895..a42d700b5 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -509,7 +509,6 @@ int32_t power_srv(void* p) { Power* power = power_alloc(); if(!LOAD_POWER_SETTINGS(&power->shutdown_idle_delay_ms)) { power->shutdown_idle_delay_ms = 0; - SAVE_POWER_SETTINGS(&power->shutdown_idle_delay_ms); } power_auto_shutdown_arm(power); power_update_info(power); From db65b8f09f0036ce40b53a4bdd6b7b627d5e8abc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 20:36:26 +0100 Subject: [PATCH 084/370] Discard lock flag if desktop settings load fails --- applications/services/desktop/desktop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 719110356..b10fa53a7 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -459,6 +459,7 @@ int32_t desktop_srv(void* p) { if(!DESKTOP_SETTINGS_LOAD(&desktop->settings)) { memset(&desktop->settings, 0, sizeof(desktop->settings)); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); } desktop_clock_toggle_view(desktop, desktop->settings.display_clock); From b48579e4a7a04f1f37358a562749ac8c604d43a0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 21:19:31 +0100 Subject: [PATCH 085/370] Add missing anims, fix butthurt values --- .../external/L1_Kaiju_128x64/frame_0.png | Bin 0 -> 1312 bytes .../external/L1_Kaiju_128x64/frame_1.png | Bin 0 -> 1302 bytes .../external/L1_Kaiju_128x64/frame_10.png | Bin 0 -> 1332 bytes .../external/L1_Kaiju_128x64/frame_11.png | Bin 0 -> 1228 bytes .../external/L1_Kaiju_128x64/frame_12.png | Bin 0 -> 1152 bytes .../external/L1_Kaiju_128x64/frame_13.png | Bin 0 -> 1152 bytes .../external/L1_Kaiju_128x64/frame_14.png | Bin 0 -> 1162 bytes .../external/L1_Kaiju_128x64/frame_15.png | Bin 0 -> 1209 bytes .../external/L1_Kaiju_128x64/frame_16.png | Bin 0 -> 1158 bytes .../external/L1_Kaiju_128x64/frame_17.png | Bin 0 -> 1161 bytes .../external/L1_Kaiju_128x64/frame_18.png | Bin 0 -> 828 bytes .../external/L1_Kaiju_128x64/frame_19.png | Bin 0 -> 817 bytes .../external/L1_Kaiju_128x64/frame_2.png | Bin 0 -> 1288 bytes .../external/L1_Kaiju_128x64/frame_20.png | Bin 0 -> 1222 bytes .../external/L1_Kaiju_128x64/frame_21.png | Bin 0 -> 1494 bytes .../external/L1_Kaiju_128x64/frame_22.png | Bin 0 -> 1685 bytes .../external/L1_Kaiju_128x64/frame_23.png | Bin 0 -> 1680 bytes .../external/L1_Kaiju_128x64/frame_24.png | Bin 0 -> 1690 bytes .../external/L1_Kaiju_128x64/frame_25.png | Bin 0 -> 1658 bytes .../external/L1_Kaiju_128x64/frame_26.png | Bin 0 -> 1716 bytes .../external/L1_Kaiju_128x64/frame_27.png | Bin 0 -> 1741 bytes .../external/L1_Kaiju_128x64/frame_28.png | Bin 0 -> 1686 bytes .../external/L1_Kaiju_128x64/frame_29.png | Bin 0 -> 1626 bytes .../external/L1_Kaiju_128x64/frame_3.png | Bin 0 -> 1305 bytes .../external/L1_Kaiju_128x64/frame_30.png | Bin 0 -> 1677 bytes .../external/L1_Kaiju_128x64/frame_31.png | Bin 0 -> 1639 bytes .../external/L1_Kaiju_128x64/frame_32.png | Bin 0 -> 1618 bytes .../external/L1_Kaiju_128x64/frame_33.png | Bin 0 -> 1595 bytes .../external/L1_Kaiju_128x64/frame_34.png | Bin 0 -> 1591 bytes .../external/L1_Kaiju_128x64/frame_35.png | Bin 0 -> 1560 bytes .../external/L1_Kaiju_128x64/frame_36.png | Bin 0 -> 1592 bytes .../external/L1_Kaiju_128x64/frame_37.png | Bin 0 -> 1494 bytes .../external/L1_Kaiju_128x64/frame_38.png | Bin 0 -> 1489 bytes .../external/L1_Kaiju_128x64/frame_39.png | Bin 0 -> 1438 bytes .../external/L1_Kaiju_128x64/frame_4.png | Bin 0 -> 1284 bytes .../external/L1_Kaiju_128x64/frame_40.png | Bin 0 -> 1438 bytes .../external/L1_Kaiju_128x64/frame_41.png | Bin 0 -> 1412 bytes .../external/L1_Kaiju_128x64/frame_42.png | Bin 0 -> 1425 bytes .../external/L1_Kaiju_128x64/frame_43.png | Bin 0 -> 1397 bytes .../external/L1_Kaiju_128x64/frame_44.png | Bin 0 -> 1217 bytes .../external/L1_Kaiju_128x64/frame_45.png | Bin 0 -> 1177 bytes .../external/L1_Kaiju_128x64/frame_46.png | Bin 0 -> 1300 bytes .../external/L1_Kaiju_128x64/frame_47.png | Bin 0 -> 1268 bytes .../external/L1_Kaiju_128x64/frame_5.png | Bin 0 -> 1318 bytes .../external/L1_Kaiju_128x64/frame_6.png | Bin 0 -> 1312 bytes .../external/L1_Kaiju_128x64/frame_7.png | Bin 0 -> 1301 bytes .../external/L1_Kaiju_128x64/frame_8.png | Bin 0 -> 1308 bytes .../external/L1_Kaiju_128x64/frame_9.png | Bin 0 -> 1336 bytes .../dolphin/external/L1_Kaiju_128x64/meta.txt | 50 ++++++++++++++++++ .../external/L1_Senpai_128x64/frame_0.png | Bin 0 -> 1756 bytes .../external/L1_Senpai_128x64/frame_1.png | Bin 0 -> 1841 bytes .../external/L1_Senpai_128x64/frame_10.png | Bin 0 -> 1846 bytes .../external/L1_Senpai_128x64/frame_11.png | Bin 0 -> 1824 bytes .../external/L1_Senpai_128x64/frame_12.png | Bin 0 -> 1826 bytes .../external/L1_Senpai_128x64/frame_13.png | Bin 0 -> 1862 bytes .../external/L1_Senpai_128x64/frame_14.png | Bin 0 -> 1815 bytes .../external/L1_Senpai_128x64/frame_15.png | Bin 0 -> 1855 bytes .../external/L1_Senpai_128x64/frame_16.png | Bin 0 -> 2009 bytes .../external/L1_Senpai_128x64/frame_17.png | Bin 0 -> 1918 bytes .../external/L1_Senpai_128x64/frame_18.png | Bin 0 -> 1686 bytes .../external/L1_Senpai_128x64/frame_19.png | Bin 0 -> 1593 bytes .../external/L1_Senpai_128x64/frame_2.png | Bin 0 -> 1879 bytes .../external/L1_Senpai_128x64/frame_20.png | Bin 0 -> 1281 bytes .../external/L1_Senpai_128x64/frame_21.png | Bin 0 -> 1318 bytes .../external/L1_Senpai_128x64/frame_22.png | Bin 0 -> 1102 bytes .../external/L1_Senpai_128x64/frame_23.png | Bin 0 -> 1537 bytes .../external/L1_Senpai_128x64/frame_24.png | Bin 0 -> 1414 bytes .../external/L1_Senpai_128x64/frame_25.png | Bin 0 -> 1486 bytes .../external/L1_Senpai_128x64/frame_26.png | Bin 0 -> 1364 bytes .../external/L1_Senpai_128x64/frame_27.png | Bin 0 -> 1325 bytes .../external/L1_Senpai_128x64/frame_28.png | Bin 0 -> 1278 bytes .../external/L1_Senpai_128x64/frame_29.png | Bin 0 -> 1179 bytes .../external/L1_Senpai_128x64/frame_3.png | Bin 0 -> 1861 bytes .../external/L1_Senpai_128x64/frame_30.png | Bin 0 -> 1198 bytes .../external/L1_Senpai_128x64/frame_31.png | Bin 0 -> 1204 bytes .../external/L1_Senpai_128x64/frame_32.png | Bin 0 -> 1248 bytes .../external/L1_Senpai_128x64/frame_33.png | Bin 0 -> 1669 bytes .../external/L1_Senpai_128x64/frame_34.png | Bin 0 -> 1767 bytes .../external/L1_Senpai_128x64/frame_35.png | Bin 0 -> 1832 bytes .../external/L1_Senpai_128x64/frame_4.png | Bin 0 -> 1769 bytes .../external/L1_Senpai_128x64/frame_5.png | Bin 0 -> 1869 bytes .../external/L1_Senpai_128x64/frame_6.png | Bin 0 -> 1893 bytes .../external/L1_Senpai_128x64/frame_7.png | Bin 0 -> 1835 bytes .../external/L1_Senpai_128x64/frame_8.png | Bin 0 -> 1772 bytes .../external/L1_Senpai_128x64/frame_9.png | Bin 0 -> 1827 bytes .../external/L1_Senpai_128x64/meta.txt | 23 ++++++++ .../dolphin/external/L2_Dj_128x64/frame_0.png | Bin 0 -> 1640 bytes .../dolphin/external/L2_Dj_128x64/frame_1.png | Bin 0 -> 1687 bytes .../external/L2_Dj_128x64/frame_10.png | Bin 0 -> 1630 bytes .../external/L2_Dj_128x64/frame_11.png | Bin 0 -> 1660 bytes .../external/L2_Dj_128x64/frame_12.png | Bin 0 -> 1637 bytes .../external/L2_Dj_128x64/frame_13.png | Bin 0 -> 1654 bytes .../external/L2_Dj_128x64/frame_14.png | Bin 0 -> 1667 bytes .../external/L2_Dj_128x64/frame_15.png | Bin 0 -> 1344 bytes .../external/L2_Dj_128x64/frame_16.png | Bin 0 -> 1251 bytes .../external/L2_Dj_128x64/frame_17.png | Bin 0 -> 1292 bytes .../external/L2_Dj_128x64/frame_18.png | Bin 0 -> 1498 bytes .../external/L2_Dj_128x64/frame_19.png | Bin 0 -> 1530 bytes .../dolphin/external/L2_Dj_128x64/frame_2.png | Bin 0 -> 1726 bytes .../external/L2_Dj_128x64/frame_20.png | Bin 0 -> 1698 bytes .../external/L2_Dj_128x64/frame_21.png | Bin 0 -> 1665 bytes .../external/L2_Dj_128x64/frame_22.png | Bin 0 -> 1809 bytes .../external/L2_Dj_128x64/frame_23.png | Bin 0 -> 1775 bytes .../external/L2_Dj_128x64/frame_24.png | Bin 0 -> 1758 bytes .../external/L2_Dj_128x64/frame_25.png | Bin 0 -> 1725 bytes .../external/L2_Dj_128x64/frame_26.png | Bin 0 -> 1835 bytes .../external/L2_Dj_128x64/frame_27.png | Bin 0 -> 1759 bytes .../external/L2_Dj_128x64/frame_28.png | Bin 0 -> 1462 bytes .../external/L2_Dj_128x64/frame_29.png | Bin 0 -> 1407 bytes .../dolphin/external/L2_Dj_128x64/frame_3.png | Bin 0 -> 1777 bytes .../external/L2_Dj_128x64/frame_30.png | Bin 0 -> 1408 bytes .../external/L2_Dj_128x64/frame_31.png | Bin 0 -> 1404 bytes .../external/L2_Dj_128x64/frame_32.png | Bin 0 -> 1327 bytes .../external/L2_Dj_128x64/frame_33.png | Bin 0 -> 1306 bytes .../external/L2_Dj_128x64/frame_34.png | Bin 0 -> 1341 bytes .../external/L2_Dj_128x64/frame_35.png | Bin 0 -> 1255 bytes .../external/L2_Dj_128x64/frame_36.png | Bin 0 -> 1059 bytes .../dolphin/external/L2_Dj_128x64/frame_4.png | Bin 0 -> 1727 bytes .../dolphin/external/L2_Dj_128x64/frame_5.png | Bin 0 -> 1641 bytes .../dolphin/external/L2_Dj_128x64/frame_6.png | Bin 0 -> 1635 bytes .../dolphin/external/L2_Dj_128x64/frame_7.png | Bin 0 -> 1588 bytes .../dolphin/external/L2_Dj_128x64/frame_8.png | Bin 0 -> 1608 bytes .../dolphin/external/L2_Dj_128x64/frame_9.png | Bin 0 -> 1610 bytes assets/dolphin/external/L2_Dj_128x64/meta.txt | 14 +++++ assets/dolphin/external/manifest.txt | 39 ++++++++++---- 125 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_10.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_11.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_12.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_13.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_14.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_15.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_16.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_17.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_18.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_19.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_20.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_21.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_22.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_23.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_24.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_25.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_26.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_27.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_28.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_29.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_30.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_31.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_32.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_33.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_34.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_35.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_36.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_37.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_38.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_39.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_40.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_41.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_42.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_43.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_44.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_45.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_46.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_47.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_7.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_8.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_9.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/meta.txt create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_10.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_11.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_12.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_13.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_14.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_15.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_16.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_17.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_18.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_19.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_20.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_21.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_22.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_23.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_24.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_25.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_26.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_27.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_28.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_29.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_30.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_31.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_32.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_33.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_34.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_35.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_7.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_8.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_9.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/meta.txt create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_0.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_1.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_10.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_11.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_12.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_13.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_14.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_15.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_16.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_17.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_18.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_19.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_2.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_20.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_21.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_22.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_23.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_24.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_25.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_26.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_27.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_28.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_29.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_3.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_30.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_31.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_32.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_33.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_34.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_35.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_36.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_4.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_5.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_6.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_7.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_8.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_9.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..8b8dc80bce09adfd26988d9021687d77b536961a GIT binary patch literal 1312 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7b76696c1140ed9f17af4fcccc840e8a07b32b GIT binary patch literal 1158 zcmV;11bO?3P)%JcTp? zv{oW`0;vKh&L>>}6$Q?wBEZL4B7lkl=TZ^i<17#h&}*D43Q5 ztaiR+Bf<%sjqyIQ2q4*b)e?|})&bP-S%W;Qqe4ch;l~r zASch7RY1pQh>!KmS2wa$jm*8CyVB4ljV zTCR;<3)+hu*ZR{iC1a9FJsa$RT05e!oa~=7bDXpYkby`2vk`!s{n-CK`vII?s~s!# zN|+J2rdy&M7ddwA3A|bWSxb;r1l;c)=SMEDk7)(ZK5+D`f==+f_po#3ca9f8L>8X` z*Z+sd!Q-*>DO=i?OuS=rqyR9dpIlLB*7otUlt^+msNcLoI7R@c&U9}>%X*Au3BZmW z_e3n?w^aaIr;lN@AB(NO8e1o#T}~`Ev}3SL0Nf^t7AgD9+T0D*ez=Wwf3ADtp(Qp z+QiNh_NnhiTiZH;vvurO;ns0&-QPcBc9(4i&2C^oo*Moou<%S}@*S*G$aOsX@m3Lp zz7$G5r1re1T|Mv4nCChpTpplt4CibJ)$XRE3q@!S;E4#)4wCi{qKULW8{WCnlp-Yw zv~GxSzhkz&_MU{d#vetXiaL=?z<-8~2@U+sixh^Vu!QxfjU#L0ND~Rf2w+)*v}i+_ zl~uD};{>i%JcCsP(ml8%fRt!p7qqs0IU5)&mIrU58$rgk-8lePl3-*cO3}8x*Iz}( zwWqg($7G`e*B_6AqMhnpR^Con6@Zl>{rlov!utRbjyH9(UR3(+ApDJ>S!b+h+7$oz Y1sh6G!Jt?Yg#Z8m07*qoM6N<$g7uaj0ssI2 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..013e2008a2f51e46a24ea78341d0916c5c7483fb GIT binary patch literal 1161 zcmV;41a|w0P)3~9W2H_#2~le4-xjm4|1^Uy6C#DKO!8|2b2Ie4hi-_3IT`?33fpm z0f-I>c0eit4(cPF0LKc{#<2h|2c!-#!fge1uMf1AEV>uuSP?8Cz_*|kRD{r#lwp|* z__kPJc0ZMbPjq5{5WFO)CVZkpfH15is3?4*Lx9)ggJ=i;4c5^FK73V8h(g;}R5iDFi@o(rRI(>#7}kC4_@Oez;EwKYNVXIjXzI z^dJxkZJyCVXPMtN$khsA70o|U@fg4pTxnIf-I4jc#WVqU2C$mlQr+PREz`6=az6aC z8CJwi0IlRJy{~Dn1kXk*nATg^JS#v5zI_i3Hvyy#c{Xi1lxn8iVr5zqv_E@D&`TZL zInh=C9s}^8Xf_Y7R9g1#197X^D%YC&Xc#I*tZ+M7PqTa1poA|4w&=4skA@A<3WEoA zmM$oF3)&p~^$Mgv8ipLJ0-<^&u($+^)p(*-aBc~kfH%F8N2JgoWmlG1q=%9O z=n4I)0C2XRF}lX$0<6M}#1dr)u!E)}Ve$W?8%(74Jb07K3SZOy$Xe0~5KUFuP4bPdbQPn6_6ou(d}+LPt_TUz34qX& zb}TLZ(a452vsrF|&q1VT6Vbhv2?9B!f(ZZ4f8PUANLI}o5oDH-()_c8ELjRhXlqtr z#hW`?gGgGJMMzqq2-%5*J{^pJrxa=(Cu@tnwf5YSX39O#K3g^jw8EoBmCzGP&njqU z+S5?t^R(}k3<5_$G&G)uR>0vkj27ITNYH}ZZ3{sb!S6(h*0?J6O-jWS?04oJ^-}DV#zBd5c3*afpEIitOQok7Bb%3ZaG6j0v b!2kFKs(*8sivuK+00000NkvXXu0mjf#}Wg} literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..795120e772fed8932017fd12c20a30b2fc3ab5be GIT binary patch literal 828 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 yLdN#b4ZO4o3>$ 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..d23ee492b07b731fde5b3b7f4847ea499e4e23c1 GIT binary patch literal 1685 zcmV;G25R|&qnJ%ymHr^`B z9Ai8qf#>->ea@L#2E$GM7H{bpTGkzVf59mIV4P6?F8nuuCcd-s#~9Bdz_`9^fWlPbTZuX`k-CDV;gzpAY?A z^Z?wNwUIK6%twzBe+?+UoZkA`17Dk&>&ZjULXuOXDW22wJWu6NBq7k_7h!`+unZ%5 z$oxPlUF{sab(TC5{fzNja#;M|*&9gV9bQ%GmD@w-X=^06Y2A2@egu{wTYJ#LfyQ4` zJf&yhU2(&lfyF4RbW3^s8Re-RfcCp|$aGFbah6d;l~3XAQQ~bqkMM_i04fw0u%h(n z;YB@(BwyyhEo9wphMwomx_EVBG8KfnFZ%Hk7Je(K3tZ1E_h%U^A2s@#!Q22hN-8e} zKRf;`x|fy*Sj?oKC4r5VcM;$huZmU2UseH{KEMw5t zMfP$Mk#Ho<$%e6Z72pXt#@Bkixg?9_xkh0PE1{;Y&Av zjVKQwLAvGiJ1Pe^7Bmp_t*|R4vRQ1c0;ElJJV0inUd)%|+vOE99@I)qDKxK|1;hhL zo1j8c!A>Y&lE*q6Eqrv*HK@$`>KQ?D0LqxpoHuC-8vSV?|y_AHYhsxHs^Gr*63G0d5%1 z7`>!SN=PI7ubxx;&jfntB#<{f0F_}k_&&3H_Cl_O_ab1y6NECA=2i~!w(`4(%3i&> zsPlPegtVS%nSWJ4zPXd}SdwE0w6lqx8RBxhGHxacY7mI{jZ^@-*0-ST9VtVX)@u>5 z#xZ(Ljj9LWG9Yo<2&9S!i7MUqZswhAxa;1e7+LX1-7uh4MOKrnB44Qtqyknme=k3% zHgR_0c2bG(0#<`<8AD{B5D(xXd*J9!+Koy=Ym8~ha_9C--$l>xPZ`+hz)8$#BB%*- zLMfkCPTs0!G(bFn6#_cXJ!H$JlSZKNLzl}OSvrs$KoTOdp0|&+ma>&gM0s%+7`VCq?~|`o~Nwv@BqmH5Vwi*U2-j0wLzMyy?mnEREwZPJN)MXP*_UcQU08S zX84w&?lyvomxTOV!Sq%m6+!7C+oY15XahoAqU_qrS-j}Ns@tNsn!5Q8xTipd04ito zRz|84Y5X*_J%FU}j*G7*b%+~`pmDpgrJbMleY+2!HEuZb3Qko4Ram^OMo^XqQZ5eY z{}+%+in8=}RRL%qqe2I1@EUe{xOb;%;k9TZO`hA0 zpk3uz)e|6<&BM49*56$PR1>yrZsBF!{CBGi)7~G@gf?4MaDH9GMCCSKE@!?A@LIbI z89{g5xCl*&x7SavqOyLMtGA4x6#~Z4ZzsCW_6`Tdzk2h~)yS#JCL97Z@c5#A$LB|( z&5wi-I`Z$e`TV<6i7XEtoJxhyTCbua>!M-i03C4UN|wFzkWnGv z1m9|2RPh!(KbsF&3(~F5Djoe!;UrEPP6b>(SPoz*{zeja3y2qAJ9+UnZ3FNWf9p-% fcS9$RcX<5*G3~Up?wLwo00000NkvXXu0mjfNd6$@ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..9d368900c94f1a6dd069d22c2a5f79cc5ce0d497 GIT binary patch literal 1680 zcmV;B251O}g^0MJHLB< z3T^4WCj4FWt?PVoT;n$fXw$y$dyC~q*A)f*J%``>(H`JQbouva{v!LPR(@UA$4m17 zuX!_am6*uP?_N*Rw}hZs*Gl@@eM#UN|6_c602C9ug>X{&qR3c!R{7~w61=6n6K}Ky zdiT*DpmV5ErIYAO{789#Hdfn5RtYul6A}IxV_f%r-<3n5ge5m5eeSqhzOHqs-o`WZ zC828P;LWr6@y2O;*XBeO0OkQWV{}sy3)40ewH}GKHrBE@iT?;660N8ZxMD?UMU}udAv*Q6eN?=+2uEZ}wLkeA)wX)k+WnBB|1lIlGlKIG{OzS8iFGqt=HKem8-v z2*a9N6>enz)v5sYGl3G(itwyzVJCKY9Ewh*(m9{^T);vmagms*RuNs{pDOnxFBnC1 zEXp|Ekp(J5v>H5$&LN_J^gdqrlkz4H2;N>9H=}_jd0J+l#cAPp(Mg&!@RDl}uepeI z9m9U$Gw*wOLIzI}{>oec8KSB@zE=~kZt_(8MnVfA-JDCKL)VR--6LCUhMw)3&?`pTo0lZ5|!V->*Q{cp-g&RmfHW zokS_SS9(5@!XiA3#h0g0$2x(P5)etZI=7P4cl5}b$3e%e(4cY_vq_8svOE2;nlyb40%do_<0BZ2e*GT@~U3SC~SRe;s# z>0(E}0@4S_)YO%A=X(j2O;_C|r)%w%^Zk^vkK;84=H~(E3!w1-ZU<~;w*G}u~^zxDD8UT_XsZqt(e`osW9A1ky(&Rba2tur- zb>p4@w-eYEKs8~DDi?ikJqzHUt=vJficE*nc06>mdn z-S_?VKZ8=#9Y|K475Y!>`%~;xoC^Nw1fmy~1b-rlJB6K(e-Y=}Rsc`%Pi>Ao4HaP` ayv-jxa^1*R%sztv00001vA}yFDaTPfG_w8 zsoK{V;~EKE*Vpvd>y=rS#^fXMdcD5!x904W?Ue2(`G07Xn(z!B!5Y7=>vlu^Cfw=( z+USwNuIu_SZW((1{Lc|EyyqsNPmS#QX&7mo`I0pCD}pso($wQ!ojbaBo*DkpOX4*S z_5j*I-dIFG;*ZepWCN@JIGJ4#p57kF3@Yaw$pBWwdt>ui;ja~z(8_o9?$X<_?|P5r z{>t~?^!Qr)|Ago%86xu|Zr6s{8i2!HRX)1#CLbykbF*3F`eKC}Hq zWcWG0a;#6s2%ho14Pm6gh(8ixk`9tqGw`yLH`j7151#H_&@zA)YPaz<|Be$#IT@Jr z!`j>AYh@r!AEEv3)+ogIQsBO`7e*9p4i<4z}NX8;K+%Dg#uX8?;<7uo2y z)}IEw2Us})GXqHe^652xQcjN+!x1Ww@?{*s(kJ7Hty2KPP_JcX052%JKoz=Fjqn#*zm79F%9 z&6YP0lpbK#Eh&($$$CDsfm>}*gsgL)MAVb4GA#R=>a^iTQzUivupfnEoSxuQ;tdZ$!KQ)1hP+v=E%8!N(MljYKP;Y zpxpyRV>;3!8nd7H?Z&UjY|YO&hTLn^sXkFU)=y8NYzaV=xWfrLWx!gC(9yc-hB;a% z=U3(atQcvbDyT*SyDmQsk#4Bo-CpB^Fj~z)OF#zN_^1PQo#wYXT{#1wi?aDLGv%|?MG~aV!^c;ipyKBx!6lO-#oHGew7p1GN5x#MBz#v zy#|#F$ucWYdVp2xL)KfVT5nLawhBvDFB4@^y#TZ1+LcKt_W%r7&6>;#NEz8B%WCsI zhnezwFOLo*6Qc4{dVnW>F#ZkDLOkV3YtWm7?>rPL0-nZ%qcC zY+3%49)JbSGkXd^qcnSLMXq+O9%jXV=l-Il@#xsc-IDO;hZD=KHGUdtAe#||91sPdU3Fy+T8U!g{nPBRoQV!p9Vf0)< zra_jXx|}de05fB)dLZwZwhNIHSYuo60e0NZtx&yoWypx$99DT{b<1Reb`KyMA^BM+ ztxEnoNfvJ1j?(??0J) zbd4x7SL=~J;p9aS3sy<2g4s~JH-TLP>}tNO+pVYs==WY`%AS`i`Z3b~DW?v5fJkub z3KYSvFF-Qo>I9yene;xjO28O?bXISs^m@r>B?pr~!gGbH6YO#v^Bw>#0TEG6-1UJbyC>a5Z4zE$ktwBuy4<=a3>*fRVbP262LdFRjA*R}(! k4F9CdkSF8hZK34#AGJsPjXYbvdH?_b07*qoM6N<$f=y#C>i_@% literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png new file mode 100644 index 0000000000000000000000000000000000000000..b40333654f83c6ccda04cbb4020621e79e7dfbdb GIT binary patch literal 1658 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 KMNUMnLSTZld>|zN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..0291dfb5830a140e9604c82b66e0fead0a9d8577 GIT binary patch literal 1741 zcmV;;1~U1HP)$@V{Y{3g8ONZp?cAc^=h3_`0oJ ze!%-ZVw~TKw-`Xwl05_A9%?;H*GT-SvNc2ajd<-X!rt{;a=@6p$Wr2wut zjRt*QTC+b(a%sq(UH2WqpTDkks0QGNOKD~3W%z3b5S78I0lMj{g6`fo==viO!1FA6 zph*5w2$y(kQH0;8*w?LSf_%^Ho6Anmfz~_E^Q?Pch`dWec3~6A>V@c}k}Xlt*Sz-7m!}qXbI`O%SU1{kduc90=uq0xGo! z3a^d_Qbg0iR0OC1b?Dz{bQ|pmu$Da|kR1ik+&9IDja_^7`5KRG=cWxTA zpvfwqM}gB30l}|JBWoz_-vsV^(re$#2t^G6(FL528Wn!1u4k{yocU`nv|e3wHj_OJ zy7}oGDz&(i{3y6?sz!517JAQQx_~I8a9&w^x+u)@LwJ3K@5UWni0lHQ{Hh*>NF662 z$=414S^P@@*85t${Hpiqfg{L?fTa5q3xyKh&NNKoQ<+U@_oQf*;Z;#S%3vT7z)Atp z`;y|PLL@ctPKztil+ZQ646=p+sR1M*BI|j9Hm!Jxe|5yopF^~}NJOJJvQEbsZX?*P0`A}WW(N4xLQ zl#0Ks(^Q>6Tl`%1$^FM`aJMz0i7(Fs83)AsD4;$&wkM@bn|`$QE&0y3ypG?y9nTXmH<94 z=%Ea!Uj@3Zp{aWB+5E$hhytpIpBc1d=I&k{6E#+CsN_K!{8S9=RPA8S0iqRcMp$I8 zt;v(+Kb1Zvr+-;!#@tE)Yw6zE9jtJg-a$?ecCzRwe|lt05l#UXQBK?%tYj5pjW#V< zxj1sIIvnDdGQh5J@Ac&ru!(r+wt>}+b`L%qaZd5&&sH3YI>%f8$!~Z@&h4@pc|Fhb_kRsbad#kjbyn#o=l*D%G@J_l>jKb(t*=Hi_y%Q5I63o2oGaufVVp3% j_9=sqPshn=A?5W4au)}*O&X|=00000NkvXXu0mjf^msif literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png new file mode 100644 index 0000000000000000000000000000000000000000..54a889d8151209f012e5b42e1da564793606c3a7 GIT binary patch literal 1686 zcmV;H25I?;P)wUdPS!FH+Q14MrQ7gCtMTXN!pCPM8V$s7-M{BD=lCN>$uj7JR8QwhAmKV@?)%GEc;faQT`gqR=-n+G+cDGI( z3M@-MGFM*6pX5y&y8@1t`@^-gE4%l|e&zGY-}g0|asVxUGEPMFbY)z#CUDFFc4IsF zT8-D%m9jtL0gzFRf$ZHwU*p3%mAFn6Rw&t>dM#|M#(VaS`<>6^&xnsaox8in1dc;N z$jy4`ROoAbNI|WH8G*)=RvoP9c|4<3qD4!SvzMn&MG3p{$W%r75&GWEugX3TBDAde zQ64SY_{_^wn+HI~*vjA0kDkT~GRpTt3mKBG9FyVc9Z`Zs#AC$_BkVW6fN&^d}j{e z!5xD3BD0rD1yt$-NT8dS%xFm(!voCqGI+ArKsk1z)Cru-V#beP!JBjKK&b*|rjOTrCpW^(0e%at{q1Bff6jV?auwjg zDGxAdVa{

A_%cY^nB~nLryOmg z7U49U=22AfNPGVgMYdEvpNqd3VkwD66E>@HMdrqGRj_~tkC#`hpViMCE4~fXHi0L# z3a~0XNUxTuz7?`aZ#gBItR?g;=G@#`sSq$HmNM?n>mcMM`hxI|h z90DXkpg`O+O%QE<>+=aIDWdv$=eL6Mba;5xM&MzVa0q}4W#-VvMm@KJ+O6N$JPosf z6gHwpK&@v%%ij&mVpRn%IzGdiTumSos#3}17`{Inmw}b4D(=oF*WW34*t9ZvH2?nq zUSJ_9%kRj3qlg+%Ch7I|tzgFMDLw@*;o$_3=D;|mx+tOBaJ<0smE{1^!S6`ob|ETv tSDy1v9l$*Uf_Ctym1|E$rmB}&>kp8-&f}bY-Pr&D002ovPDHLkV1goI?@a&z literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..969b91193d409a832d6d33a097670ca00399d371 GIT binary patch literal 1591 zcmV-72FUq|P)N{wWzs0r^zh-ZRrN*TD7s&hj5Nyx;{jwADBgsG+(0nsZ^C+HrpYADRK4=SlBA&5=9|AUP4f*WGgRceWk! z=kxi_vKwg(_Be<1yk{IK2go?7hh2U`4j|=n5IaV@{lGJl z{GF{;zDK{;*aT0^kBmKr|AevHNzii7(Vf=adgSBzt;RJufQM2}!W*8nXDCC^x$iW- zM?Rh3YTOUg$~NOh$C3<>@88A%5j~d-&_&y)zf+uaNRuPyo?V}MnnqD`8MaTa)XNCeWZ+{pkIz3_WZ zzjg@C&#+l*4j_4DCQ({V<^)#yNz{z5S2#b_No%=hO%7mD zLfiHDbyXI8E3_mGUyI}l9eSD>KvF_7JiSK8sW85U9cVo6awtzS2G4t(z%%A*9kH&d z5Wp=a-Sr^Is^m{%0Bv!t*v$ZRy!S7KMi$z#>#6dQtT=<+PUK+#cz$ONK=mt00xeWM zr(VqiI>gW4&H!V4*EPxjFqYI-=Kz40Wq~!}D=+0HsL7~~*If%IA;JLkS`?8m`mz|G z#&qr;H%=seRfYlX6w=KqnH*pYdx^IS?+_p*G!|;NX5$`Y4CT$pvxw;0p`#@Spb$fV zXF`?`;YD;o$5+F_B0cB`0Up_&@pl|CEn{Aj16UrkB!gPN z-AAz%x^=pl0VuZoMo=ZANY8r{#!e?V#Q>o?K(r$_84Mgsq=k-9cLrU>CO{+)_!SJW zDqdLbON*vCk#(VW*99!HuFe509W_`d$5rX@81z`Wau6!-gyE@d%X2@T0jQC@6XN=L z$KvSs#KKzj{#7|Z#U!|bMTH}E^s(Z$(+`KzQ-H_w$A}+0HSDPNRp>yAZB=r$mP1(% z;7P_{?9M{WX(s@jU;%6Se2fAHupWK5XQ-hn4R?Dk(B&B3@LiGvc+ipbtYn=qL{x@$ z(y{dQu5bF!!>Q1Y*p>yyPM~KeJ8%paX*-e10X)Ypj!|~TX)gR|y8RWTa)9Vr9lzg+ z6Nct;ByZb^_xA+uQUEG`r*@mQeB2@P#t=~Q6)_M`cgO#zF;6(btuW;PD^EOGgRd+< zcOnxCI+6a07?==^?b%9RcOS0bE>^fM1Xw(AmEmJYI296ZcY+&3kIDcMTBUtyl%5AU$shA>3<^T+O(&v|Hf>+}PbGfp7>I!=G??kzLpfEDl7k zS26+JOkIv}^#27|5#7NVMCmtk1kJ?tDCQVr+u>I_->#Ck$j-{{nB6r251Uqg*0TJH zm?$I)+vVk5^@T3Eo_?9I@NKg_#a7_FGjmSGI8j;j;`S;e)vqiE&DR5;S@p!S{sCONsyh002ovPDHLkV1kQQ?b84N literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png new file mode 100644 index 0000000000000000000000000000000000000000..a72cf1823ee1ad46ad72a8b4e1e9156c0dedcc88 GIT binary patch literal 1560 zcmV+z2Iu*SP)#en?wbq{JX}$ONb>MlP_v`U|ytY95QH*}ta|<5%J8)9=F8MDyFcma}&_&l2lAsxy zPJ%m|hKRHL0mEy&;D$zxqk$V*dTu!v#iZ92oYWItz5@r)xSWQMF(6|6+c0*2@BNE@VhDlk+2Ex8^@#%y!baO6~Yg+kN%Or}LYQ3ps!%q@08|Jy~sdgy3_p zG`>eZpWkfU4`^jK<3`8Q3{Rim76Br9P9wmsuzgC7Bv*sw1h+*1>%KK^83&M{O{!A% zEDdN*a8m@}bE4PTxL2eAir%|8;cOYhuokQoP#pm*`Q!c2na}Nt2%t@b-;*YE5}q7< zIs#}&i2&KV67k^i+!Gb7MWfF>um~2vR!4yG8qxn)1eg&OYtmAVL*UsV5oo)zG6Eoa z(f5*m+Z`Zkd}I;mm;;ux_%*}$ zQiRm5ydM++BB50B%3V8v%eeuw+?+B0m1wq=1882kLX>8cIf0peT4<*CGn}96WL>4U zkOLq}Si7FS&SJs0!XjbxUW6;`&=ZaTni87f`8_^PhVhZ;!1M7ghw_lI(ca?(o-t?Z zNOfIG0n%dfT|We&N`9vZU@b0+>IlHcd;g=*$U-B#o+}?=#Ve?GB2NSu?JGF|*Dr+x zBB8o>yP7w`A#Hzq1ZeGlU850TjHO+(Il#aRSzt@}%uA&SS~9Zp^{$1}5QzZ%UKEiu z`a+D)bGr0Q8Yhy!GSh%`3fUc1RyaUwc!@U)?-ZaB8YHx%Y}|ttLwOnbDk8e>&=KJP z9BK;iObAI4UPKqPwfXV0;yCDOf0E;TNda0YnoY_ITTud|r5!&0Ob0&Z?gd}r0INVa z09~`*KR*kQiY3zGM)OeA36MUwoDiaC_UZ^=9s_BJ^x&kSybD-y#6-rth65lC5i)2& zbC6TJcjvjfHD9DW&H;7~jvj3&F{AfAa=e6brxTot0HHcSw4*f{baL7mU2B_BD4}w* z(?Em={D=tPsU2wjH7BqLD7w(qbpb@y)f@onm<{cmP8UjZg4Xm;CTW{W{Hh4xNg3$b z-5Z`Yr>TZ_9exA{h*S(`EMU2Q*4lhG^*sS)qf(aU$29SmFSY-E%50YXv6y>;}$Ijim@cLoEHf6{iba z{Id@9x)bl;30zTNqySpQyVViU^6?ItH>QAP4j>sEMG&09U`pw-hMPr5JZg^tQ8jWrDJ6d5{Be;^_X zuA~%FcbAA)5OtsxQs`})5gFhAZCn;4?_6Q26xF*jf@;U{g6o&Y0W8CxAaSP<)w_!4 z@IK29Ff;sZweMRIF=mF-gdHFw)c;`(8BXJN03_7^?fQGco#r3AcOtGYWHw&_0000< KMNUMnLSTX%)YRDk literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png new file mode 100644 index 0000000000000000000000000000000000000000..9a13e7c67db281d96949e0be75aa7ee4197dfa96 GIT binary patch literal 1592 zcmV-82FLk{P)PbXFRCt{2UE6ZqAPmGE|Nk$$Po#>DgjN?WZn85O5*vg-Vp%v% zdOgqcv;nT`YQ2i~z4WtWM*?r7)#LT8dNZ)I?^*rdNT*hC5oqn{V4ml3B@s`+U-)4r zKx_Y!U_~z`ls_^*)2%`?%=8L8uO89;1H zljGedExV^MK*Z0o1mF&Ir&lC`h0!FTex1$$qvs_Ipsf_H%saG%klu~#&e~40wohV! z4!-CRyQxzE4}RZQC8y}S>$=22NyyOj3_BgG8DP|Z|GAHo#~{rBS|;*m2ut*K+-cr^ z<(3gij1w7PG`_F~KswRhOAH~EQew@(XwS5u1%a2VHGt-u=BJARX6(PC!L3~NS!fBd zk^vCEIIBVoAO&QUW!5`s#hns-50&zE98?FbNP^y{<`3fEhX|yV`p?z`QQ^Tw2zMS4 zIBy3X*Ql%rzO+cSlDGTD0NSdr2oN0vIykNU8({6t)fyn;-J zVgbunFPnLjz++P@C0}pa5HaKhdI~`7oCsvaTkTge0JnE%9T*87>D_2wLYDD0Hgo)umh2FL=@ z035fqGNLO-OM)GN&WwTDK>PAw*PL{GTm(^MokN4ZGdZfUlK~J7Koj)3uJ88%ykdoO z)b5Tz@9584f2DsP$3Usl45yGJIE4i=iTXMXz$?xOf~53U1bat9oz4IpW&bSP8VD*1 zwP$a^=^B7T)c}>?!lT1V0zOHeM93yh^f>q<89*Df=W>Hjbj8F}CAIes1i^6{pfVHD z!SNFZKX+@#7o`6RT@z{hBN>1qG9YQ9`?K8>z|4V=DhMaTR;p%;0dgnE`7r_Lej8r|Ob~ zp5-N*(u7-q_N;28jwTGN@TTKxM~VR`l_GwCw-b;-z1)x06LrFyruYDUIJ!U?(L(Hut+6>gsXQTvh@)x_LNhUDOVo4*)hA$o~o z4Lo`ah$?9`5j;s|F-AqwXTcZsa0@^$Ya++38Hgm6mI%>cNl9=*;3YiIgKq&EMj9X+ zY%jZTdh^~VP#YP)`Fxxfc#8BEARSvqM;UZ7tN5PEgg2+$wn3v{FPb{)C z>Gyr#?Js)oZS3~m8wKfnHg?MY#%I5D2tTLaAH!!)kZ^CJRS6`~@=5KKh` z?GJq5Z=Qh}@-6z2HGmgT0Z0P4uN{L7S7;2#^z1FA=c)27_6a2bR&Xc*_a#W}Cq8C1 zCLGI@D>stQBHwDCGy*^+pu*bgF(_hqoX?GA#fcHXqbExMt-~TQoBfm_R*br;+i4MC zcHA8Ssz@NEi4}&gIDLkbA^@}#-b&+sbO998GBK2yIfyd?B#y_35qei`XY#QvusxS+ zfGiSNW~P(^esT>U!CV92L9?U43Opo;r~%+Onp~Sz18}-=dQKw%9uymHri5g?tX$lh zo&=HL2vHdUGs4KeR+6cr5Y)_g>Hf+Ih{p?w0Jx6W ziCG$`lCOca7O=>k)c{z(kO*K!WfZ6`1icZUHQVm`r^eKb`(*5l0Gb*A`lNU8HjrB=kU$k&)R;d7r$>N~Hjul> z@*q_29Su+3&S+sZGBr4+2AB=@&7nFAr$+#c={JEE5@`EQ&jau396UI^2EY(M3iXhH zQjFT)mHRS@^wiYqm>`oaz@}u#p*yv4WKoqmN3@wP9i~T z^1souZZq-r7eZhGmL>eYKmUS-7pJ`>!UgOTQot+95%8x9E9(Rq#Cc-9^3esr-z~KO z#Og9K!${ zq)T?I2W3Hc<#FpFv+Ln`s&3Gvq)QT_0X2qXe2sF{djpLwxI3^cYH0}+0 zyI3~ErF8)3WDm3v#X9?`alIsv^7-zTFDk-`Uj=K*{1`zHZLNsq17DL_(yTMzJtPRU{3zTMJEJPFAvKYk(DCC8>pqsvg>d>X~f0&$?DIvpM-6N3MWkr`72v=2tCtrvM39zpxr$=J*{& w+%9TB?jEFXW0F?irG_|vx-T(jq07*qoM6N<$f*O#b7XSbN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png new file mode 100644 index 0000000000000000000000000000000000000000..93da7f4f94b06909289dcbdc89181a3c10a2f39d GIT binary patch literal 1489 zcmV;?1upuDP)1ZUZe~C;v8JmN$l)F2__2&N)} z^#yMgWdi9ag^HljB`=k;8D>#&ZpEYRhCqCyj zCY;NaD>stQBj0PEG6Fy)pu*Pi7!)xg&gbUx;=~9L(UT>B)nS>CRzGEk8KchXc3K3G z&W9twDiUaEVus-}PM2^}1b}wJM{VAZE`VZMC5AGRgE%8V;zW#?q4%nzBp=%X+jF`G zs3L)9WojwlH`f3f+%*86v^omRAVPwS8UW5?$+cBA0H+(Lrx*e7q}Xth5}NUR z5@doS#L5U@(F=a2^t<3d&iR}Hxv(rLP)!C-PPRNLFRvoOkjd#|(eDMnfbXpe;DV#~ z{^Mm;E(=iR3bJuJQ0W05Gn`M=Hrx?k*L!a-tMj#x!+pQX@RY$UMy%4Mwa>)>7lk(h zV9b#EM-5_j@Csmjs?UwKM{Cy;fJEE6KE?5VWj#?fR7q5YLwq0dO6$ z6H*#jCEo&XE#Q$Y)c{z(lnCI($|$h95R68E)_l(+WFaL2oK_z|z3&hK@VO|wtz>KM ze;YW%LT`1wnlN{W09qYY76CK?+2LnbSCP5Wta2n&?3Z}<0R}D$^SBl6$*&bZSOS&4 zEC%6F72S3h&KKoeYgMl~pPJJ$@13zT0$6GQ=#$>T-9T=mKm%29QFHzloE`x_+CXlQ z=Z8?ecQzt?8bF#Xi=CA5pPB|vj{q3rGl3owNP7objbJGtcXSRBoL&Q9 zs1JokNC1`5##_hEVpSV>+X#@Uh<6@|WV3ovb62*3kEsFh#1;e0tgO{rwpF-o1khk5 zfptU3JN7PkTn&&3?0jpTP>}|za5vKadn9ZetBgcd^48pcmkyvcUj)ehO=UGeCT6yl zXXUPl0w)mFbL&2x02#RrJ1To0;H0~O+3?(;Yfbn&fnH(bdJfdw*1L$A?P+wrRnIz5 z*L(fp!7D@IaU)2>aKLzR*T8XiYzwtq>^)ZEH3UTpu84v|mUOL^R5Le-DsV@#PW_p< z0l2C&jB7Py)S1;?1?;Laj+OX90GP|Yw}auUqQ#+b+D#%{3*90G%nG3#ZmkKV`K)8T z*F@xa=DZs|x&Zjza~H5R`N7g^L@Io|U7CeErGS#Z6zbXU=Md=tX4<@rf-`duWL0_- z|8=m(pjgqY)S^Wf?Yivo+FTIcI*K}0@IQB2%9Uw0swFj&&@QXU)U7JMipMS_zkt z?=3oEdGK>2HDg>F%PayY=VY%yF3m_fdG)~ck0fh%%a>(eQwq=&feLZw@OaC-+Wi6v z)QYhJ%RObI34^k^7vU7pnvWn6RJL^*tSKN{t`{(6MPx63AS8n5e*X-t^$$igT4cA) zR`k}|GRks!l{$|@u=|HihV9iW*%M8ru8Iq^2zI3aE3kfPHGt&!9Yx$O%-s7}Aq!tKO#sdDr?yb1A>zyo(u4_+ r5$eB~Lk4Nw1h9nq@2Wo+m^6O?V_bGuj^r&O00000NkvXXu0mjfe?7R4 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png new file mode 100644 index 0000000000000000000000000000000000000000..7510931b44bb100b314d9bc617843bd51a2cf164 GIT binary patch literal 1438 zcmV;P1!4M$P)$~B7-|wG(!YA*5C(dt?@6`<69HDoHRyL1<=sgX#!|F#% z?fe-fru-e}x9aC31oR!D7xe?e-#J7p0nR__L|VYLH(6#nfo6WnPE>hei-ksqb?`NA z46KaW30~tz#PA&%z#}K>0C0d(aHVlOic<1@->FK-VuDfUi*^FexnaGT0WeTT;M#MF z^fCgcge>}}*0&1-K;8D`FavlX?f$-vg%PmvRyo0L3;;J4)bTI_Xq5tmz=ZDGw7QuL zU|})}J2C);DGZre2=El}GlL0hx z^?zgXrQ`q}NK8QWp~`G0z{awMvr#8#b5inX{n|o7Zpb^iq;4bg;5%`FiA9|*c_b$c;x%a0ie*cSpg!#XsZ~Y&0Pw; zks=rXLSh}P?bU>W)P!}&bP9|bM~z1@K;}BQB&@IV!o$VbKupgDk6S(!?WG}`VA0PDQa<^Vk|j3zR%f@Mx%04a$~V*oh# zP9Y%jE+BHJaTsBMsJS)w01elUaX%VR*%`+`YrDSvO8s9?H~ws_WB^SL&^u_Y_46h$ z+Z#ROXJ)_|*qs5sj)9s^)S`au@r~nh0ICC)7@o8fS7CPsfMAorjQHfiNnv338~_5% z0kWPv!Esl4{>-O=n)b_20hrKHXYe+b=k+XP9s`ff0kAu14=k5T)~n|ea2x})Um}lz z-lHDZFCsL}$Rm!-0U9tSusFqtf_HryX~ikYYD4(j!X*Rl`rIK3mdz6FEFsSVMp=LZ zH2xEIJj4zRz{vn#yD&p!7zaCIj?Sa$3t!LJdE9&yvShGc>ysg%A0+Z_K$GZYv%*eK z12z3EPGw!&`(xsw44l2gdhB{bD8*uHnThuYMPkbA>o{SW(HmP}ZL#s6mIIu)mXA*g zm7D+r_Kq70ZLHp5MzJFcz^m`kG7~S&gwZ*G)?rQq_0FRsLO_GZ*!`|vu^V?`ffJpY zeLnzpG{k@%wXph3SWXbx&T?D`z@Xh#eO&MByvCogoBJ@N{n6mW5J0{kz%qGjt7V8R ztiD~`B@|h}eqVrfLD+7YZH(DQz*yp>5J2Vt4cc9W9Uq?p%n(+#lOEM#r8F1T2CL(2 zadBP-lR^rVf~3nMNjNVeLV>jVI74G?y>XoiuhzVT0{f34QfDtkX&Gh2fYfekWB2V~ zU0;v&k>-WK#@2+48DQTJz!?BrfCe*@Gz}BXII?oi)H@*Ov{_Jv01lMEGicZnzIM2) zfU>QYqm;oAPzs(WaJhqap^^hxlTuA0Wf?yTFa*s$#kfO2DK-c3V1z|R4d`W`kqfXk zu5_;wiETbAmV-wTqYj|We;~X@lR{P&YALp_jvawE4nqZa6f(MdX2)a;n_BV)Nq1?V z4*-S4jyN+jZ!1Q3a%Mkkh~RXNerwnWFz>V)eRX0sg4vE^1(YxJ3nNO2JK~&RYz$tx sXXQDt&#(hXhCl7;*eZB(oCv4+1L}9b5t%S*1^@s607*qoM6N<$f(+J@S^xk5 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..f99454b164c598c8b5e6ae11d8d9864b7e3077a6 GIT binary patch literal 1284 zcmV+f1^fDmP)#PQ8w{{HVu407ihiAtVW;!`7}vtu(r>w;lzx{D>EzvbeHm z=LIkV&}3)^cp@f!-vX`xq--vq|D0We&S?z^&=9by974z37NtkLfTnypKk5NcL>g8F zoCaFc&nhP+L_>&nJsV0=jUakRCpSMNRLkvAel!GX$z>#u(n6G0k$4Ds0L`sPF^FzJ z8f#%>?41qEk}V}>EMH0>>F0nr47~bZOWs;6KU;!#5dh(uDk2%I zXH7fh1X=GTDNZW?r19Q9x;ufR(Ayx{N#PV)EuWdIbrY>MwCmQ`ZURKyZ%g~rYIRDD z4sFhCWV3W>ZbW(3xQthZcmSzv(XRKdf$li=!U}^K59cX=RE`4=0qA@HA!7wOq}H#O zGewk7KU>xx^#a?y8+c;r$LoCU9-^VpQoaQ`FIs+X)d!Gz+9mSh}WX z`P+8_NA(m+S<@HrJb{$MWG!D(I{h381zUuG8OhEpcs=_FJmpIkFXai5JuD?a??LT~ zmiS%gMJ=Dpr^KUs@USEV>`<@IfE9~9;%9sRk-mw9sRa0n;-kr|l?z0}0GE>up~DG~ zZHD|Byo_8UfplFfH6y(P4MPaf(&MjVQB9!MIE-9dyH3G-2pL&i=9E@&l literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png new file mode 100644 index 0000000000000000000000000000000000000000..a17a14044a94054ff24eff8c1b51bac31c273251 GIT binary patch literal 1438 zcmV;P1!4M$P)kKQKvOJQ80A_|Ne}^p9Gsmcu&^)qeHk^&j-j0Un{- ztN*$#I*#MFweV$t57ZyB@A~`K6=~aR?eiJADE$>J*S)%}(*M)bh1E(P$omofqbRRj zq<*V)?+l^sfqX8n-5&kX`8lPB^)7;CM8#xe7NH-P#mdExY`&?c|?LK@#*S2{uUqHEZR0r;Hvt;qmfs07V1`aaTluP3fz z02Fp(0KVZ`A8VdY-dURDCRFMp?fTJVS;nklfRUXM?8pEks0<)^jx^Ys3~OxlBt)EG zh6zUcdAphcMz)RYd}9F5;v(sD2L?c3f^Z}8}S zZGt04qG<3=C$M5OItQ4s55fQ(T9}ZOSI6$V7@+AUdUw+P{VW4;Sga%Ug*t%ru>1j# zIe^C=Nsf0yXP_lv{d+UHLWP|W2k<0ovz}kU05dYJ1&AbLy=}^4 z>&LEif>{P2ir>Z}01p8~@Am^*Hp<6HPJpmyX9nQ!&0;2u!cPG_+m3oKIuVrYszfFS zAd26}X5n&xY$7r<7PV-780g=tLWTiQy0sjD10DiKED!~9$4cR4RtBnH-+xtjl2DQZ z^q6Ii-hrNkI~l-}1MvK;+yRK@o#1JUPqaS+gpYwU2CgW;?27+B2at?<;!d8$?hL@O zECfXGWQW;32jEa009l70^|=F;4)!q&u%Zf=-36I4@r*8{?2pa?ybmW;45j*k#v~OJ znZZXe0OA%d0>o*-;|MEqi;v0yRzk`dc+o-|w+nW%#e+8o$eiCjW2El}a6~SZ_`Ukw zh%f-!UT6+bQN-c$D(d}rV+T7l8~A4@|F9|{kz|jY3>g9>**o0^q7h~Amwz1UU7)Oi zO*V&ifq@JGJKY9)#z&kg`fTd&1kQX9pn8XCdUt^pi=%BNP9oWJ`u8j+@EmC*HnBYR zX{UglZ1Lih7KP}2F*OSIdMcMOQz(-3Yxdm%p2K9@O8ka^M%~wSl~PZWkJhon;`7b( z=nxPApZtoW-8Tf(;6xZbI>u!kp(+GaFVd4jz)6%l?)4Lopsq|6GNL02k9_WosJ0R> zAwZjWPq9U#=^1=OKs{dS0NG4mO7603V6h4++#vC!5b*MA;q7%h5%ebm3w{44XXT=J zD`=HAp-%x^uB18HFxH( zh0lT;DS3P4B(DeoNWGD9R_UEXoAcuWuY%;A(M3n!DXhe5$BBa52g?Dpg})<-y9h($07*qoM6N<$f~i@aj{pDw literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png new file mode 100644 index 0000000000000000000000000000000000000000..f763540c917b48c20a54984132b516ab47935de5 GIT binary patch literal 1412 zcmV-~1$+95P)hLzz;{h#>kuQEvwZiy!V{y1I$Kh&bN6xMN7?W4b5{{c@8a0}g5 z{pWeYaU9>RhKB(@P`}H*^XsS6(~j@b{fOKu{T(gUy}Iqv|H5>kwOjD4%Nc(VOvd-gHAhx$N z2sl|o$EqM0I%oD+>@x*`Gzrqga>iFVf5@NN)y)7BxO9_E7m}WF-#U+O2EYc-u1gc_ zF|vvVuM7e$H&@pH5&KFQfI>^H090OC0BF@F?l)^IozJbX74R^?=)bZCp!S&qAQOS(AtV{YS-5}3d3fx~ zAc!&mfd4eM0_7)voizGYf(hfX!% zWV0{@K(-KxjIDASP6PN_7cvY`rCX{2D9Tg7j0L=?T(MkwiOQ(zhx@NePaIlm0E{Se z-~hT4u4Dj94M6L&_5c8g2f^K(53oN2xL*S!2CmqE*%g1j2H=bu4@+0aCkDpu3_#Hu z0M=Iae1UN$~54ZF?M(0Uc1UuPc!LI=5dVlBS&GdK< zmFT(p)$lMtwY{Y^K-VUYM12{ZJ2)R;4?83q=)E_+S#kOXkjrhADS+E!*y%K|8eRtf z?;nR+DVkF{`p>}0n#t}?0Xv-rTE=^V%KL2M-wCw*9sqaVKEseTJ{@lXJ3!0D-nJ8O z06uc?waNm^^H6tt3)smPD_$He&(gsRcvV+U0A?2NdBT;THB7ec#CHn7j*o2R;!k&> z#bJk)M}zU!DZmO$dliObc~1)H$GgI4?Nl!lTtjZ=meV(^~{$LXSNR+k$qXt@c7o&Ry z1G8PmF3@@;+e*(yuS zb+9OzRp?qxQH9wgSF=Zj{GF(Oz?=eR;I@mUEoAG&X#r$OAsKWyA({d@5l#UBz5t0J z!y;MciKc*^ict%;`K3?^k=2lOgth*mh|~sGrho&$_n$^(Fw3jlby{S0ZVYi)y^=j_ z>Us6m=1%=v!BJ2nx#;ek|`=|f$13@63_$6Fk5UBk>stGx zC~Lmx=#g)Y?v){o6v*fD>g|yqwa=3p_B_v!6D%bvj4|T`dfzNoIzO`bqIy0h!;Ba~ zo1ET9&m)b6bV3xf!B-vp+Hq~($8j7lj_)VwF^h)i=YAjkeb!k~8bmo=!%hr9?J*in z29Q>AKc*=`gHWj*Xl^k4fjLvX(LkBY2!Y(lde`8DIpJ z0jxw4Kx2W3 zG3Yr?zuA>5={g##gFu>#r_1Uf=rTw8c=U5KfTo0%0(canR<5Q`H3Mjn(#>qT(B$;< zUOTD`P=Sb&C>p#n2&~+Ut^qRoL2H0W3P3V|N3Sawpv!LUJxJ;CSq9*+SVy`R5&)4D zfUE&LdP(zm4Vr;ggwbP}TA@PE zcvif1|4Ihn^N9=)HB=-8WUAr}q!qRT5eDejudD&MeoIq;_8{F^^!gd+(R)`0L6!kp zvlmANUIHL%ByS4P=H@vR>9pM$fFH{ulSY^C0xX~l!LHx#3}6+JOBf&uuTe7;hH@HB z1N2xGW*7h^TdM&$mN$YTbE1qRrI+j+sO#wUtJ0H%-vG!cVy)7dEli)Ya+Z3R|Ql_d9)aHt&ikU83WPUXD zk8CepBOOy;26?GnTWzD_%+uuYH~^Jer0cc7)okNAhgiv@^on}tpr!yT3bPLu-C9~T z#tcR~7=CVs;dZ3{4mAZ>t=*1@!V%iOqPr(O7fO$vs{amG3eaE`Xfwu()*W2_EGn62 z7r0Zv2z1LO^#iih>FGacm~|SRAGIPsDh0F_3y=(ESY(y~vg~7}O^f;?Iv}w8yMi;w z7y{LXj-Jcx4tTM>vi3Pzi~YB8Gw{AM;6}(81(^a?Ai}26EwtJPsCrPYq!jIyMy^f) zXt|O5qQ$9Z^Wy}2L5j|()6wq~R$_JFM8Wlg)c~5~uPEZK0^-G2r7V8WbO2b6|KvS1 f5}6tY5j6h*`5Py|3n0fx00000NkvXXu0mjfU$&E9 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png new file mode 100644 index 0000000000000000000000000000000000000000..7eadf75185ece2ac3ebd0906c97d054942ac276d GIT binary patch literal 1397 zcmV-*1&aEKP)RCt{2UE6Z&APj_z|NobL9y**7Rp`bF7P77K z&+{y`*8io3hXFoNzDvLJ=iBKi+h?ghGA>GfMoDF_F0FL3a3@}0(1N6#Bf;A;pGeCq1BnCt|cf|Qex{dUNGk_O6FaQE0 zhgit~7QOoKoPH1n(CR4tMpy1=5%Yp44}cIB#H*Fbb=;Fa!^$Gi|Pku08a=&GJuAw7@)~6<*$?W&(Vud zV;!#Tl3hUG7nuWCCWISjhRrl^E0t>#$rUp6JiIX}g1+w#3=kPpd-u_b*K|Y9yC(-| zy54#hpkYN0U|I26|CJ0t$2(fZj1ry@AjK)9@wpYY0v-lv?N{aiRKK?*Fv|m5e=U!Y zoFOW6wG%`cpcH*^WZ<9=B2Hip0g?e|3`H7kcLt!(qKJgiqbZ?zB5D^9eTf;|fdRB6 zav1|~f_fT_Akr6Qv_1^<&$2MX04Ui~4nV;sft<1y`X1FqUJ|)tx$qJl1J$l?zbZU& z_`?7rtO@~i%*@rAfh!rnk^}VfS!;cF0Io426OH!0oAFE8p8@`C0}+E}gn+x^wmCpQ z2$win1XG|-vF?D~8Gr)902;h6cxQ*%JqMs*9l*N<(~gL}N66X8bg-{v0PXN1TCZW| zm^dN}Irg?WKtI^b9$pjN*N?U-)=lxe>2R*cd zxgI74h+YR%!L;4TxsV}%8+)hQKs3D9=t~ZQdPvESbb)~k0Xy9WTKapO%KL8SzY{3M zYpTQ*G`IuxHfXWfTV?{j4IF`^CC~2+?yw8kNf#@o9l%37bb=xWMVx$Y>?=WYnAtKD z%l}ktyJbYv+YZL3O!##npdn*x_k@5Pq+AA}jybbJK(>+Y6auCkfEortU0kwgJ!kO9 z@WL`acPD}=VCE){_T?NQY9y{7o!n9Gu`-F`aJvwI za02dmHb%L}N*y@)XzCw10@sFnCSC?ff@UVrddPz`lH?FxmeN+ARF7QAqwtElb2uzU ziq=d0v=>0;CT%?WOq0d+=jt@tIzIE zW>BkqtsOb@uw?%h8AvSuK}ZCVi9jZhp1R2HfbPUrhNoJJ@88DFzu+iX`qTMD<>k=g>ZopM=qY>6!Qg9g!Rd;WYmMkIM6+eyIw200000NkvXXu0mjf DE2n)B literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png new file mode 100644 index 0000000000000000000000000000000000000000..5241195d32bc160a3f8f6e1f04eda860211538bd GIT binary patch literal 1217 zcmV;y1U~zTP)z1 zai;(_V&x3?A_cG!Q8PSJNGX5=5t%Cfg)zMlGcyrAyx~1eg1#>WU;qzzZ-7T9n9!hl zXFDpc6aWD9I2r{#UiMVfTC)Zip;_t7Ol)Tq??mncI1qW56A{5mk>oyr5i3sejzoaw z?HS~GfSWO!4lE+!SE>CJfP!5=m(axE6$y1N`uhkf23nw|02o$SKL-s6vP`syfv-OJ zk7Rh;DS!t-3$$fP$%UmkKzp15aN@eIUw<<^4N`qDdYwZHwsw7m0MI;Z-JYd?RuOP( zmS%46@u+X;^%Vk`-HxTuL^4y|k8E0cticle*Em2FMh@W-f%I!;BzP#_i1&2@h}dn5 z$o-{Yg_?HlaccLc1C+nE&x@=(V6|<;0Z`r}1rWJsepk>bqJm$FZ52?;q2~dtLF)}1 z6%(4{dt4qtMFpSs0p&ST01|owBt5S!tB@T!sV#5t>3e5`&-;h)e2f6JAX}c-c%*xH z8uA9eaYY2b6wLN1jurr7+5Fy_nW}L|AR_o4o+FFE2Fu_)q1fv|tKPjlsECdhfEN*K z_-IJgyO)RZjd+d}01sX-n9}xXt^fD$pCX2+6lE3Bku?DSU`QPx-NS1Hlye2FOT8$b zBORc@;sg!aR%&wv?hdA=c3!v?w-vwBBwhAC>R%wBk zxak_3-h0D;wbdLkj`}li!LHIO5tO|Z9n^pm|K`Mk9F3HlZeV~xG zcAp@KmBBA1NC3znaVz#>wAansMEC4R!n{KWTF|Wh=i zM;3szZAF*6IM?zXre(1B*(M00000NkvXXu0mjf=Jgy8 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3ea8e23af713aa1210d6e19693e576aea86d6e GIT binary patch literal 1177 zcmV;K1ZMk*P)Y8+afiT?l%M3TOcp1*+Q}B7m0ov{wHCARx)QRU}^y zDG{JI8m0bO#(Cla_nTl`xsni8G5oidK!B{!uO(MnGP*M|6Y6OK+>4?X*OP!E1R|julPl|e8IX2= z#ChYr8BxSd0M4NC_tK&*MtF8^h0uCCd#nl&j_-emhMNGKK^CJu4`hY$9$2DGPlEnu z4+*H!v7Zwi1=4Z=%J|uZKdmNu>v_bhVylF-)Q^T$2+b%^JDa)&<$Nx&MZa3N%}2wY zdZ(~=hLqe&B2-TvX^!#x6-fVR*xNvnz;Yy5qQ(uzJMTv*ZI=!kE|vAoj_#U7S&srf%c%ij<7o4(pZa-07?L@ z9EqOuqoGu%jGG@FW>@q(xWrD|`5s##1h6D4BP8Rb>&LI--=%vIu zb_C3jq2&7jJB5Fbpwk}%GnLbQU8MUrqErOf#qm*SjUP*Wk17Qp?FEc@S{Kg(XP2gu zYMRJ6{tOAi{~y2uZBcJ_zC}vDVRbw=j|U+Fc$vz9DxV^dTC7MQnlThOk;giZ=UPY- zc!!=D^q~s@s$ulT$U=eAA2dL70Pn7|$fn#JGvj>&+|qQG6l~SRxxdXq8Z)CQz{@~{ r<2}3olzuV5yMa+>WIFU51OMX}ZL*z$)XYxz00000NkvXXu0mjfwJRcX literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png new file mode 100644 index 0000000000000000000000000000000000000000..f4b263b199c8402088ad99c4dc27ab3c38f0bf2e GIT binary patch literal 1300 zcmV+v1?&2WP)W2D-85p<^O-V?-LPnye~A&P*RkfIgD9c7`y0J zKF{+!ki_Tn0RVskQx}Wzr^3i^{3XJ9_^;B~^E~i8&tD>3?2l3c3|tbNgBAh+ToRmu zHUa=#5}bfm0$l8mb^_ceus3c5_;Hlz02)RVI9(r@BUyGXm9{ciLV$0|FFAK((;=kcky4*qM5*a_JDt~((MX(7Ka6%66%u8}l@6alov2Vl>~!z|;Tp^-p3qa)`x?rZcvuW3~z zXo&#xq^s0Fiyg~Ca665%VOiX{4Yoz~r6tN-oB8*1n{0$FCjghdR@jWTqZZc^q2|Ty zI?620r^Zx8f;J&&weu}D@ZxzFOd_@S^YOcg(`G_uqwP_*fzo1WlaJ;k)b8STpCp87 zM^A(dn9y4gpeyui$yFplvx3URtFD7B#FW}#JKk}H2{3nv2ZtA|;~JRTOChE9BU;dIhRXBHMj{N)ui+1(x9s{`bgUh@Nqy9olEMdb(5+Bs;za59=Hi$VW3sb zlF$(~GQi0eM|51Tdqo^g`mL@++HwFbd9v7(9qADQ3rV)UWcHc?>t$KzfA>_m)jyzVvXL5;c2(UuW?}j9T zQEdS92TJhI)+54-R|#Ns`W!w-Tk2xQVk-j4p;lc$x+{3Iyj?~BZc#k2*4wLqL;4tl zG|q~|5r3A60__CICUE$Yg-U)_z*6r6z&~xM`vUxLtzW4hJT@cHu&4^IV_R zVb($d@As&-ls#?dd*)jDZeS%UO23Zsk#YuytdlI|VpdJuQU|EmtzC)Ld>oCRJH{Y6 z-y=6`*e(Kiq)@A!t|@vPTyZQ(k!RZ^&jc!_#2`F8$R-)oEhC=qz$J4gu{|xeA4qX+HE?~YLk;|sG7eg%2SUPIt`A^2)FThKJ zHYwQR+>TPva(!9>Ygn#2CcJj6mqcm~5J^ZpgX3&RNU&=6TbzKU+p4j7NuZ4(904>X zkLu7X`${$_L?ey|RhKZk^c}7OBvp$-l><0|WR)Y{%rQFpPA7r0_%MLapNNAxN~>R~ z20SQ}g7Mk(4Yz#1fYQ4FT!KX5+5NZl%b{Ec$U39bq4ya07ykon_@I`j=(wH$0000< KMNUMnLSTZO1z`dJ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png new file mode 100644 index 0000000000000000000000000000000000000000..1563ff39b27a83826ad84e5eb30d17b8d216a971 GIT binary patch literal 1268 zcmV6ZXSuAvd3*nNy%ItPdd9U0;0d7i&NS!1k1c?I~Od5L9!C@=fQ z>HGTn9vNe|_qR4dfNaU_`=@=oQfdj?Hude28xpzn(sd-eLk1n|Vu zlSx459vx!({^{@DG5R*6Aps_UC5PO#hy`0oq&}nfj*qB-WcCvvA zgnmz-O*(Cz&=4YB_l6RP01~U{|5NGYn5kiB*oaaqWZ}8E0kouMv~ZINssP*; zjY4_`YIw<$tsG1W0i_Ax$!04Uo}xFtR!c$Uf94sb^_wCio3U>8hE$EoZ-B?25BDJ zz0(9hk}!?^5@wWTcTyNV1b$2iq(u}V`syb>Y zQwYlP{FRFG8Nj>u|6pbQ^xl3yla` zwF3#^?*{f0()t)Fmqu)@vf(8*O&UFY(LR@yf@k9^68lCCT?vR0`l8L(=KHK=^&E*t z=P>>E0X&n@uG2oa$?s0;ER2M;9&}B$wT&Wt$Z>FM`ol#@uvI*%0&Px6NSfI_coF(y z2n9W~KoR|rQCmz6i1&}?BQtF+mKpA0RwedaAfL2(SB!T}MxW$!rED_d@I2|W&; zVY#>MuH+Kk_GTbs?9fv1uG;%b?0mM2u}ge$1=;$EP)YOFbE}8|NocMCsz~!X0uIheWdDkG7eriteMd~ z@B6+`faiIjNYuqf{HhZ4zArq_Qzh~%sUJ1+ox}HHQ2jDjKJWu&%Emqd?aKC^s0VL&D!zxo6 zw}Tk7YNxC90|aw`ZpvRR;{=8$#s6Qimt|#OHS0B5vxBk5XgZ%~57Kz=IgE9Td+7c2 z{Vd!BIAQ(TeDC|JuGcz`q_~~#+cK9#j3`Pyz^?v3XA$)J3|33>kbv92bxoS3b8D?4 zXBh#;LUum{iX6Z!VdLho-ZS%7k@b!FDG@42P(}dkGCM=Ev;l9s7muN`xVboaR0cN& zzmLacNl@Ae{F3tVLE?WH)cvl?bv$vMBU@%|?OVZB2^jWJSn=MqNC)k`j@RVHSUW8Z zBP4JUfYzm7LqzeM(mCssWX{Stx;76gDgsjupp{DF`YikH(9Vs@=Q8oC$HA6rL>oip zc-LhjfQ8*r85k?QN|s!dFxf)v!ugU8)VP6Bnt5Jk219OqFMHKNLEF)Xl>dKTFPL;mF$zs>CM#C`Ys`J_!0yM=}g#fLt zUI8shlh$RGA<2`@v1Kb*v&L%oueD%TB3jDPa3VqL`L}BX0oYvdY~kLH;^|x>>Io!e za`Ao^0=7I0$keV_hSny{%q_`WK@LmnmyQR>Su!O=Hq5!JfKhTpG23WV_SY&2?EWne zo5po4WBkqJQQ;6EC0a9AXi=18QD15kV`$F@1~}zP`<40%k$ynQ<6rJSM9Qv!Hn zde#~|_jh;yC*UPPI0Vp47GIBIr9!L2fYtq1gb8mL@Kgp{lE9w^i!y2Jwu;yuGFjHp z=G(#Edru{ol@F~t%zeKd>_o?Qa|SJbIC6*eU+_5oyMb4kXYsk8jk|=Y#nO+2ab+UL cRpxp80Znqgg|Y|Bf&c&j07*qoM6N<$g7rsmoB#j- literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..3f84ad47e3ded623ecc0323b5d64af350fd1e61a GIT binary patch literal 1312 zcmV+*1>gFKP)%Q;16>(ix1Bf|z0Dlld?)z@nbsKnc*=A;H@KC&0nG zFogie3%m!%1N=Cq5#V@%ci?z{1inPb;jt5BMDJ zQUE;}>`FSPg?pkz0BK{d>w4veb0WWO{#m36pl$x!(C4hJWfl>r{3pqmBES=y?~$KT zoHnMvKJsRn5J#`qbh(NI z$Yqb>J{`-6Fp~r@0#J9h0^F5KuaCep@zGeTV(=!79%w|EK>}z9c&ZGHDur((C2z%o zyG$+CkH$)}_?*!s__E7ZrO;j(MC=-u1Dl+&n*&+yhOIK;~}TCjw)OP@N~MG(!^0f}^or zlAVeDsGK1KND|1c!m_lH96HVdf4`E&YjcrYuqSLp?vovd0`#k4^pU@wm4}4YFX-<} z#FEHOU*rGt`)K5pEM3yy%<5`NCvcbKFrzrCaLFUl#*CD-NptXS{u7w;)vy(^qMjx) zFD?0#Hr|@Y?wjA*#M8hPn58)0)znHcj|}T|@8-|+tgj}(Y)G4J#bk{%9XnZm$JPXR zstK-6lr-Z&Z(u{}whG@d{5F(Vz!t|ZC?@VMXT0SX->b>g(n z*%O_#D6IJJ^)Sm1gGLx7Nr=hvP9v)0Xx&eX(exVaI?eheIT}_}3h)qs&Ib@OP)gJ+ z*2;w>S6#_tEK+5#-3LN;Dg9dWe>~e0;yjzrWm**i>WpBkK0s7S5xLdz)ZI%!Sf!^t zv^-4)+Yn%8yhDX0iv-#R@mwG?N6GT7{3C)bLO`S}dJ0|Ho|?SUztGCh$Vp=aTl4{T z+Gq<}`9?9y2S!~^{#=Xss4)2m0p7Op6eADdxh~QHM&X+0)8uGp z5%hgJzCwa90V3|pD-RhGtTGG@uWXHsmM7r%kj5ZN!s||Irqa%&GNL_lZ@uh!y#h~5 z*7AfJ8MG48yY8@e$`XLyoT9S%n#>4BH986&rF%EVV@$J1P;LZu_Y=Wrg4vB)SzUPY zl}N^~{dtW+N*+Cce=)?usFNni;|kdksN}VJj|>Tx^#M>WcvXy#VD>piM1C|e91>vE z+VqY`hlwcA)qqFVQ)`HjU`T*b;-V!UU+VgCcoRVSSkXha-1D@ip;Z8nK^2*ZYNB$j zC4*um!6k28UQd+({{?|8KGS7;vhnnGEUf|#_QO`c9D1yIJ! WF~&y6@>K)?0000&-uL|>!o_*$B0#|vZP;8qkLkA=s$H*W}iFLX*Ifg?b$?O@I~F zAC-@^7VYrs_-~DEMbzUU0DmC_4FtKeGOXm5=-<1L1-#=K>3(uq{CmFktlR(=0?LL* zwtuZdi5z<5Q0sA$#rJ^AufNZ(A(1Y-M;HNkHjI>6V1*saVlw-Yo*{amJpOCBfTnyt zzlEqhM1m%PGb;hKm2#In+WlJR6iFnl&$D^SN3?>A)%7S2C8M=k;R>_lXdz0gNIawn zpd~IB{{x}K=sX^Fq%Ng}PzccCik11cl!~jnVhGeOJZjnAvs=oq*Ou1Q zI7WB9Mx!=}ED5-rXvI?#A)QRVpwrb7vDTz2lGvffAmC-<+UF7(yz0RlBaPGa0R7W2 zX=AO29&w4*&nv(!Pbw!RrSQ+|Bvfh=)${q9T9?uSv@7#5dfL>dnJt3;>_j)qamG1ln|Qoqq&3hd3Psp6tYV5;^}I|6X}PY z=>SHQXN}7kWj6sL*4NVFmF7gssoAi+V20qH@<(|%hzE!?BoMN-xahS>S3qj+cp)>Q zeEzd#{ZTKlWh2NU5xV7!06%cc)kiA2$q2nj5BnSVW&ujyI7 zO3HT!fB6^IrACXaUKy?HW)#>mx6Id+o=FI|c_nOAPhmE^o_z!@%9lJ|WDi>q;8T=e z&=SAPyvUp~M(TxR6X7lT09qAsIw`DJ?2$g(`}}8%d!h(pl8d^ylFovX*W=Tnkz|X~=c8&n?t@wiJ!>rfCA84;EblN*6}6 z%S>y%$bNdNhb)>yfJTI@%ga)PyWLzi0xO?s z#M%Wiz|IeW_m7fKw)A&ia|2g!_vDJSL&07C;|NZy_mhDl>rw^mi00000 LNkvXXu0mjfjhAve literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8cf3f5d7c34f1eedec4def10bb28095f2e1c4a GIT binary patch literal 1308 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_10.png b/assets/dolphin/external/L1_Senpai_128x64/frame_10.png new file mode 100644 index 0000000000000000000000000000000000000000..e385018bf6531534e1d809b371b0172361c657af GIT binary patch literal 1846 zcmV-62g&$}P)m#z zFnBh_rO7V#LfW2f-}imnHre-G+TVt4+eAcO%5HowJVXR4zm?zk-gulXz>RaUiOAk| zI&uNX3N+Us@@^&UGq3KA^Rej|K;)OwSv(*jlq3;>?ik~Gc#6F4)TRt?_i9C=wi@gi zBMz#66J;HqB7CQ27F?4AA+jvCAFn=Zd!rvuRG3|Gs~k%P)>hX;yT7%)6}}#j03v=Y zfXX9J};9_LYeTGNeN9XCjJk zG|nU;TVws6wiR^_&>BX&pGE4PbEdfZ-A36xP_R^Cdc z#mcJdtL1;N#b>*_mFvk=ClHN?(#usDK#%g@o*^&2*T zer8`s?X94;9%8HQ7fo;mMs|LK=Z4r?p*or8j8v>$B(sRQ4vr?*-g^NYVxXOX-9f0I zNEXEK3VM|vDatBn20TlHWMsNTvOlw|_Iwelml4*IAd?xO*Hz328WGs8n=ux1foOeL zIdTb4rfeiQ3u3IG$V^$GBa$Jj@ktn!JF+-d!I7zF`hB9xk+1Or#Eg4j$XJe197(2L z5+G$oeS5JL>sy4}2C~YYAgJ63MC}kf{l1+9+6r00B7u7*G#jJJ z%Mvn*okryvUCS32!2Cqi(4t}~KGCOFvC5TMa`jkA-Tv!Cp*Dqx(y=PCal}4RFsbbz(~Q-m^1wMNHng? ztZbQayx3{vKx&j&^b!)J*Ja}1C55g~o^bC-Vdf8;` z^DCK6M(S+JtY2SYUdz52q<67r@=$hwP7Y^cv_Bh#S1iaCl&+C-0lkP)%8aFm`l-lL zHT7Pbt0}SiwyQ`#kf_h zQq&blaGDG76ui>ET7sRe0F?3`iCF~rYf?Xl!QN7yiIL;nQOqE~A0qPignkY~Pd^% zX&3N+pz@=c%uiN)!xf_N26ltGIzWU3unJ~p_qxdf*)ECIDIxk4)=Hwj5g?P317WVQ zjRwIM`Y3DXDSNgxXnnm9AR7&T2lGFI)ox?8HP9A46$7AKud5S5%k0(Zy~Ufk6WMrH z*(;9BzrGsgb^1v`pC;;=2P|1c^0B*;SuWw_7eX>=5z0}|^qW=&HZGeBOZ%d9u%Tz3 zXq}_}JnI`-GEZl3F+he<>_Z%9?Ycykaw(we=mxf{y<1anz0rqk+!#QcmgvGJy30R; z1X({mDvw_L8ASI;pvPlvG9<8TgLL*r058zETHsfeC%bb{@Kv=iRZH65M}72K0wmwI zZSUU;uzm)S013MS(PU=G5G|TEn`m5S2_p-cl_%=EI*zRmza5;J$V&5`CE3UVTP32t zJ-?cfZ$zGm1gsDA8r5%j*a8bSxEHOCAQIln&@)~o*hT-etX_YkF@grN)KT9kK(}xY zf~pEYsfs?3srtpEvk3$wf0P1i%p~%RwXoU119dzr*h37QO@ftHz>}Z(<(bIpKr2-1 z&Tc^4<|Pf(6?F&EpkB7XT*s%7Ad+8OyOD9cI-V=A{@#+*K0|?Q>Z@U%f3s2Y%vA9T zB&f7m<+8kZtMUv5oTZ7$pI6 zyOt2G91&F4d+N$e5Tr0YpiTYSK#`xU5&`+Jk$Mem(MI9qCDCjR8LUWPI+;FED&&=CdA~%;8g%?M!`u zi<`(2MTu`I$+qwNzHJ-r`%dHkz_x8fL@#YOel0wR2nv6+-}trhIGceR=VBwGeONjQ z0muuC&_ML=CFC=!?~U`Z@f?8Yr_i;0fQVEj@f7IJF|LOv(W|8nVOZA71&PLTXwNjo zLH%z>aUHxQO6TCMmJmvM=FChmiI%}KgQkyUk|7NM0PHK z+C70;v^3oL!+LC^1hA~v%lftWT}du2*MEnFetapwOJlNh^8_zxMe>1&Qla;q@bVju zGnG)=WBy(pOY9tA4D_sD%hWCJz~4vD7d%T}5eBN~7OO&%msdrNO{GsnFHf}zECEO$ zckRXSh-F)) z&aEZJ9q^1x@@VlY={zkBiDcoLfIWthR!1rW^g4?bF+XZ?=vjmxP1(@?Xzj?@o|>=` z;}I>_+*HIPpx4n2T8WA!&+?tECOpN{b6cr{KhhWL~?Icikh%)9eTv<>zR=Q+up2(b) zgmj>?o|h01$-vP-iHxT>JEqSd%KNHNCY7t=CHeTT4~25!@c-K2ETVbDRe-!-7p)mO zD3Pn?PW!&&GH_P-R!$&2fknGBY;<}Zi^kP$vg06hVFbIuBIHXG+{ zlDEh_R_;`Z|6a)+h5Csgj%<9q7Dmaa!62~s@VA08VJ_}aWRDt`&kjezntXEr% ze7-{X)wrxFb@nt5t^W)x`(_ZgV$0>B>Hr<4TnRD@&v=k42%RGo0;=#z%7~}%=u{9@ zDC;EeYJ#ld^zR0GakFcqEl+YqFLM+QR{(BR+C|Y(I7{ePQn*t*0J_M~o~@ey#t)np z0xTh{5)iVy|GO9HQh>jj=p2IVG3qKoT|o&t6yOgc`n!S7A-Gybf3_01N5Noo{v@K` ziRe2K{q~7Bd#Jpe{V+=hPiO37RssImw(Xno{9}NQtIz%%!x>5t4gvq2czqjj^HT*s zVBJ$_Sb4`kc#?M@s{s{23RpY4=R+3owj2>9K0?vuS;&!j!J7g^YI4Bb`Sr{sqMLqH zMX=f%4B9UW5YhHeO!__rNC{SW1Mbm7IRLu#y1F88o~=HWFtE}+BZ03B317by>Rpyx zS=bz0QL`45>Dwugh`?8cgr_A~>+W7Ai4sREj8(56albGF#GlqiH2*O&F2}bVAfjQ~ zhd3%$*MUHl5{AaRcSf)Rl9$S@Df*C&nFDZF<6YQ9KLfWCnK(Yfk1W%=y@{oGNbu1+ z49}~)DS$P>)!b;7AFb5#`gM93PBYp*qXgFc+qS)bFF?i_?fUcxMer84(&bAY(2?*= zZehiJM3eXLoELsO*khDvZTyQWS!ENfh{6~e2uuMR_(7-1_#4~Zp>UWkB zc>Hs3S4CApq@aHsN zkIpS$uSBO3N6@h@E19(Lav8*3g_oU5AxpQYg@6j8N^qnX;A4sM33G<_faP^G`Z7tE zZSbdJQ7q9)3YiR9%nVu;#kiUhRI~9;4~FDpRb_)m-tQ@ip-GUy_=K|b8xvW%!+4zm zieWST2DD8(5*p8zP@~m?Mfd3m!?Lstkf)g0?1+#x1(eS_viS&jc&#LFRv+FE{nzmI zTfrJL79?>wp=3nPm9k|6l-x_e>tLDWdD&$KlHROR%p`r98};BNqh6BAX?SzUHne~8 zxkd*)d&rW`iY>@utc4(k5hE2?RY686s=QbU7b>@+h=e7yzHBl++WrN)SX(KFyxIZ) O0000lE>hmW@WGRyB&Rwv znb;IXLd5HUl<&6h`@U@(?fXvS|AB4Wh=^X=Zv0w!5D`@VXut7m<8ihCH_pXIMEme` zlmbu`7^#8iJxa)HR^J=vW8)lv=%>;(K0rjOk$47lbBycZN%ZQeLm8I$az&zhIkan< z;h_Gvp{&D`NcYspf^#E5@FI&G$Lg=$d!vpgYK$(pRE{(T?OxswdH=ZgR`_~A10b@w z0BZLXW{suc<`3(!jS|4}UXS%_`MW|ctMo$&Y# z$C*Z`tugtCu3k4r;2`;W4##w7c9DwlesUm+ZZtjK5rE((nv*l9phxy zjfm)>ExzX6qn$5)v{bbESf%003LvZeAx~ugLwlww(40sgTu!-eG6FsM7i|K5O@8+7 zmx9WE$XeyN=m9%WQGT>K8f})`Ug<3RWQ+>&0wn9(Rxp}ePK5eP1ojeXR8U%@HG8CQD-mLSOn4X;)s=Zg-s2sjma|hD3G$W zc|5PT#wJou1GUbUAgEnFKqTz1V*4F9DiTGNiS9$N^!v6GC_6+2^8~IuP&ZbFWNn_v zoE1VkP*(j(=wUM=#$vjBvQ@(}^_6zt78tH4<{ z&fBDHk$EicRLTEd$sUFJMi56fzi2{oXd4xLjH8HSS9Gw($wWR2R@GF2`@@jR1X0$j zJ&e4*LiyFatQmE7HxE7k8CdqqAnwIh$V1fu+Dy3;Bn!_(kSi#iBa{NF@Jh-^r0~Y6 zAZk$7PTtiNS;gtU4fOJ6*Fsy7(A8+^e*MqN8w@(ywH2r$hjBkfB{$HUEtt zI4uQON?4^JIJB2(sI#s}ywwBj_-IKZxk>hH(zT)foNRM&ND*gT?ui zh<+!c??m+5C!Xw~@^bcNmJXTDSjVgZ{IhM_H{<%p0Bu*F{T{;^Mi5Q`|Lu5v8+r3n z6+d8|Q)pOu#y@0|XCSKq4L~Yb+q>ttERbzEB1U|Kq07CHBYDA_0YqwYAl&)&%p{_V zepE%UIvR|zUko5(?Vm{cJ_X1KRzw3H(L)>noqAo}5V*`%pGq278J>~CSEhuozZB{n zmRwoc9NbW|9+c_ZsgQ`mSEYofrC96iUXnzKqYcKY*N-?~m;sVcYa^O}jg0H@jRQn1 zOnZr=;&mMeR4HL-vU__3DXVx$f zY0*#(ff=%k{z+LQ5hN;yS6yYE0TFZwb0a9L0JKrWH)OIE#?jRT0+K(fq^|intUZDV zWHsQq1SZf_6k)gvNEtGICmw7Ro z^P_Xi&nwYs#1V8nmqjKmyc`DcP~ovtsbm=zwG>c+X9P%&>iCs=BlC7f0lvnbb4Zdd z+u)yyMY%*PDI^*49=7UT+QU|frZs-Gf0S5u0qMu8%7%=*-%}2TCP5106Ux$WOk{C~ z@i_xDpVcoqhqh=(0_A7kK46jbj1I@RPj8{DMa}?4ikZcZGI)kPk07*qoM6N<$f=Lf+7ytkO literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_13.png b/assets/dolphin/external/L1_Senpai_128x64/frame_13.png new file mode 100644 index 0000000000000000000000000000000000000000..a996443fe46be0e95aa380d17883daf5dacbb55a GIT binary patch literal 1862 zcmV-M2f6r(P)YNI1HuJ0Q1=Y|C|2OJkDSS?n`ycFz0a5VmpNo zP&APxiV}AzZnu5k_ifu`-*;($H*DJ`BJ$F9B3BUrm z>nMh2EZZv`M>l?!05f#5N>(wd>~~A+O#w7h*@K+QxXNi`Mj`aPy{IZsMu1s& ztM0Gn|G_q&S$AvKi>WRknh%wit15sV;kPw6&xZD@JwxM0cx7$KqoRGY%KOw`*aZ5S z{Tz+AjM{!kTPz!(#f+p8fmz**xmXB9`@`B% zNO&?~BgR=0V;Mzm$}$}(8S)yRgi*dDk7ETKxq2qwC+Zx<8ZSZ2ga?L<=NQG2%G9d_ zNLUeVFSlZ}MaXR+>+CUt+U)`|Zhuv_KMO}FP*#{|9D*m`x068IAwHw_$UF41RTvdBYux$6vtD1Tcz8KOoK{o4Ui?PqI z=N`3mb=_GXZ_V$bEF>HwV_&crA^n}t_A$Q6XHkqQC5h)T+gr-V z3QBNV2=D~FGQL^`J6iy#Z`=0GdHzv|4ykl|!;$Mf!no2Q z;8&vZt+~uk7JS1MvhW6WgSt9FgbH8<%+BuhkOjP5601u>^ee2Diu$GinVK9hbE+B5 z+8nmgM|nF>*t5MsqxGTy*=+bq?%$p!9yRAmto8=lqo;BJbnA6BMX=C3dwqIu@n%{g zo6ib+#gX~ft6?6|t0sN+IN2)dnFlOcMEtS4l35`Ez8&1a?(R|fWdEE6_Cu2_PnEvC z>tw%>@(hqEL55#TTg&k+2godn{fOg8t3&Sl>ek+XuA>{6B6zo^-nzAXkj!8efUKPL z^X~GmB#cphtksPwx~BwsJ~k$!1k8i$wl@Xv5{;`Tej>i0vzCLBuWBAt^`z~4M5EUe zApW*(dw(y$X8A}75VI>VYF!x~qfOIpQ;&(}s_@|P6Va~DWBbFmgEI?R>E5$SHnPE1 zh={fqS2O&L@DnKkqk%r7{tXY?V8JG5V`d&wM6@dIKKsn}Lw_`{9=*{VK?8Z}O!m)s zZ*UKSsshl;qC-ffJoI8*6*Cf1#6MDk5gxL9&hWan8h9YWvywfefwPsMSIE5<#N7Xi zswXQ0El@2xYk+plOBtv;Y68)uUbVnN$B{}f5(*;zwYT#y;sv}=VB@`2Qu_=gvZ+_Y zy!d9bKAshgv;->9{WDgaq1QW}WyCWRI30F+J)zgft5DG24DLx_v>}!_+gLBJ z7|Fm#;;l_yS7z@lpUN1Q?SllW^D*RsS^l%{yZ_I{_gWWgpH(K!rZY;*6KEvt-lR}J zv&k-MXL|;(gKc>iWtSBoFEJv3(X1QA#t4XNB$Ug{K%l0oQh@Lj#3%_W%F@07*qoM6N<$f*OH~ Aa{vGU literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_14.png b/assets/dolphin/external/L1_Senpai_128x64/frame_14.png new file mode 100644 index 0000000000000000000000000000000000000000..628d58b93a3143142a7dd78ec0a0dca53023a984 GIT binary patch literal 1815 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_16.png b/assets/dolphin/external/L1_Senpai_128x64/frame_16.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec3727989dd33181d644399bfecf66a2146b9d6 GIT binary patch literal 2009 zcmV;~2PXK5P)I;*U-BV`q?}SN%P|IO01b$e z#;)lq*Loxp1j#1Q;69dZ+qPv{WZO0wUk8?D5fOQM_QtP;4-tWzKc2twYvbc?2i~|B zi->IFq@xsoqQFQEBKs&|-$iZTxF3tw03ts&-O~di;*umI&|71?9#)a(lR7pdI=y-$ zQC|)3oktwh{!Ntgu!{I6m05625`-+WSUXXBukRautmq-T;C6E?8N9x_9XkD^?|Z|~ z2PA+&`n~+!5tmWuzbh?dX>R!bGvYyjr-#MTE!%i& zi_rrNsnF*cU{jgb=V~%|Ys~oabPq5(W~bj%>QQvipV|9`?4-|51GDVSBxOcM_OQA3 zMdazNH$b!j7ARb2Ih;A!QR#SO!OzeWGe}T5RP1MKYexVPep5VlM*S+cjSM05xE-9S zAv!myjE$q~tH=NF9Q022c&-&<)2Cg#tn9XaB7fa7P!Dc9p~0gKWX{h+dICA>Q<)=*vdFG(kM?&(jk8=63B*na%qB7m zGxL4~y`g83*V>M>mBuawpod=L$Px5>*s%7Zuz!ZyzY{Y^;6)Qe0jNDW~5J6k#n9s$|f8A3qeTH%EW< zI#3Y>|MFoD8(RxpB?oW|Avp%J`rrhqi+HOXZ{kAuTmfYx*xcT1hy8h>Ws5}^==QD zJ`8rkQwkB_6W>JSPZ9Y?M1H47miWZqQQk$T-irVt*F~%=bVro{|B1*K5&6C>%L?AR z17&URx(%0 z1kc8`?~bhFjsV1D{}z!y*v+?2W@B9imed1B1z~5CvO{IJq8yBh#mnVwuWf+qV5*2sx71?+o*u zlTsysXTKW-J89$y3DEvb`%|&D-L zlnD@Pyz2A}u&2-Z5&4z*8%4U#T&s5^Q5%m0c8wlmq7)Eytc21^n+|4(u(^_GBLbD?hS*_x|N2IWo0Zaj2{>y663gas$@W^S6z?||*ZSDDs z)&<#){EXVOLKeNFz8N$OFv_wbeqNab8982w1fD%ljOx$yMUWTXY)GG3@>SA7H(hl4 zp|$YZApa|&o%P4_1MP?QrF;`;CyoZMZn< zXQcqIZ6!Rj=vFR_?mp;1Mn&8~k(e?BuLi^fWLl|i^38^SR3&)xL#YFiFIpr0d{+kQ z92E_!9j{>6LX19QuhY+4;0#^jjCrHEN5ruQ(#{!ee&p{{dy)L7f{_uXPvq2*cBs8~ r3RpV>6#_(?g-}Ux00000NkvXXu0mjfjU?0v literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_17.png b/assets/dolphin/external/L1_Senpai_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..11b247eca27efdf52b168912f14183521e1c1b63 GIT binary patch literal 1918 zcmV-^2Z8vBP)OPdIS%Uoa+Gy=i1;U!*>LS72w7yYa-#m;*cZm}K#lB%TjN+~@W$$X==AroFN3cK zbO4c<3!wJQV38~jFaEF|t)~Fd>CITbcYlwN%P90;c`amDoyAvi%uff{H5OC1%^%6cjVdko#y1rU1bh{mDlk<}2aUWjyl zCF6^5`xx~_^_eO^(HYwV35W#h6WtYv#xNVoa0stXQC)nd#|-0VVVk`M8U1>HTf6c0 z&Fb;cAJF8;;7E983^X1os28E1uTJ}B8b1r$B79n`qnzElEbZ=nyk{cjb#Np^8z%Bx zJ;vVu_TQE0gEEMfMwYjoD4;c6iPXY^!^uG^m~fwqI4AY$;5 zA|qbE8Wjh~`c|AZ<5t<+NB2_e`o@HIG!UI!k7-&(?-1r$vf6Q%>j~xiOopJLqwr@X z(54VsIyS1fo$u~o$X0)5`)6`z?SYq4Gvi`4~R%;#d(hsMo%AR zOg1)*0ISNf$oRHQeAa3A?qq#}k4A%8W0~ksscec2aDbV{iDX@o@WJvx;M1B}he8Gd#R zL^Y1t`gTORBIy3rI>3J-^4?n89fAqXZDxO#HB9x&;eJn<@fS;|kcQpBm zYT+!|%-oeZ7+ueb0xZH8asnKnQk3~<-#%mwDYueIADzE4#uk;%asq273;S$$fJO43 z4fNV)g+K4apm}Gb@)nNKjd22R>{-w{?l^#_lxhFfTKjY60uY=2sD_%Rm?1A-h`Zw6>p)I8a<6+}%fl5tNBXUB z_}y;^VN0Enqd=muC=Z^^x~u6_<@Amim?<>b{@MKYz&`zo7?$~4VG$ubi~X*0I_4_q zVCI}*R7QrE&F!992SA1fsAWO?Y)7$D3pJyzDg)6=d}DGeA`><--A3_8;}}lsIG2E2EBg z7`zk5rqfwft+A;572&R{jDw;KL5)YB#2Eb?3j_zsWBsDGBWO(sbu@X z6nP?ErrTjD_^DG;P^jLqtlSZdjAd#bNAzH?g`y*?f;kfzedS3pZW1ey+Wgxf1S5+z_m; zS(-RfJMbbfdpO~L7-7@9i1k;2BxU$hNv5N4NC~mOOWP(Hc^IAJgKgIx+>!2c&(eX(CEYeoxMBIzbYFcaZ8&dcE$l)8?1ONa407*qoM6N<$ Ef|8}Dz5oCK literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_18.png b/assets/dolphin/external/L1_Senpai_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..bb15041331abe5f7a1007befcd37135b1f0b6381 GIT binary patch literal 1686 zcmV;H25I?;P)V9!Ergj2gmjfkWw1@dEpt`J3!8vQc6QU zFT5h1;7MU#zD94S`e5B9WsJ}%w$)GL=$T>fL zkWu+P8KK-6cpEFqOMBU8RHTolY_u-BC8fv?L_~MlfExh)9uX*=M%!Pt1w_Bgep(+3 z(LH_#Xg1)w*Y2aWzM+Vg6fTV+qZmcB$2i)4?lGjNjc3uUG5P%YJueG*5DBc7WR%O` zI+klUPe-)B%QpPGwQol!F*`bulWX9PuoG)&L1WcP%C&oKLKfBX$VV)Ju5(#ZDKv?h zNuLLrPAD%yr<76p)EEXCSu;NW{=xv3vR1_nd6zV}rkt1dJW0_~nQ z>79V~O@0pnCD1kM?gi2dF#uwSo7PB9MBCdeCx{-?X0wOaC^ZH|w2!v39^-nq5*eVH z-H=^#&UCYAq6p9?j~0PXKEf1oQAeTmcxH`feJ|IJIxTn@fPda=dqsiV*N{*ykoQA< z_%%(=OjvW}_iJMmpf&N1(K2VRW7X^n8GtL+qEsZS-z{n6uym_Yy|xXGgk)tDd^ILe z89*w}2iGPay^g-GbchiVs%7Hk-i-NZHj>{pR(oLp-q`E)H3dD8iPJ3OTZZ( z;&Q(ik#*7nO)|obduy#T5+aq6M@Xwb$LM4YjXzuVX#J61=$dw7LH%UHeMeqZqZMs` z^CnZ=F?ON+opTWhX#=FY$0>;DH3#r2P=j|oEmt@Tj&iCRyLx3q23Q%HtBu}z?6StF zv0RT%HGO;64?YfV9aP(&tUAG2A?LyKo`~_ivSwk*ABK2nef9n?WlOZ`Vi{RFf+fSV z9wXQ6k^IkafL&o3VrJpS>i{14J$fQ%y@Ph|4xU9jOC0dy9KYu6E1^0-(9?bs(Px(y zi*e*uGYfKSX*mg$J~w_r)|H(L_nnCTxHP{|MI>>QNhcah`+2k>dV3=%a18uGMBj+$ z7ZH7NYkL58iT)M)yEYaAAO9W{_Lts_|A#g={+ThtalQQ`e;fFNh(7)Q{Zz@LhYDBe zVL`I+3OpSHTgt-!k?Cza*$%b2v@UcsLZjP2R8H&m@${!*9kjEh@jcifN8peZMz38^ z_0i+_Dd(KI5Qo5@MD(?)(MH%I8H}FWN0jG1?3UifKITEtO6)MNXi0d~Ga6GTlmkeG zB#vIlR?IB-)?`YwIzg>(6lONBMa>S;)Pfa}HR{zbYn-0SpOo=n0BKZD_IySsK$vyp z8uid=lvAPcpaVqS=6MfJ(DL>Y!t5iPR}Tq`#qa#fg;zut+nee4z7*rh)4X^*3*dvV zWPnjo)e9{F?q!A}woz}6lTvzp7O=8By811c2qJQ=60Sw}jaAo>K1ByjhP$SJlp$s< zk|n2h4C8&YYse8+I)MaYg+~s66#9rj#&3O%3$0_#3AC|#P&>-2?*!Hsf=3I-$cH3H z6>g$=5RtvlO0+sv3#@shl-eHaJ{}}=Bu)jZDqbG6JPmymR20KSn7}h0(ix56#Zw(X zcGmX9T@$xn^h96n2-wO5R(o%yR%7X>N}z14F3Q&?wZ101~@v>wTT{)Ko)scjug!ac)!vXL6cR(%j4jh z5Q3G&3AnX3T=vsTp`|#$l|i4Dqa0l@LW{q;uA#A!)kbS)$!MAD z3;Q4jK#^X{EHx!oRJc6bQtdoEL(f(w(Arvk&=%o2!8N}WYE`c+#~BUetln#@;mrw3 zTMaGjq0Rm2U&TfheL7D9`qlc4hNat)n6dX;1tiVkEyoX40xjVMM`z!bT+i0F0ud(g gj3t5cHEmn|2Mp`eeNyah;Q#;t07*qoM6N<$f`7{!ssI20 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_19.png b/assets/dolphin/external/L1_Senpai_128x64/frame_19.png new file mode 100644 index 0000000000000000000000000000000000000000..f953c8ef1950ee6ae7dd15ce03c7a56d931e8f5c GIT binary patch literal 1593 zcmV-92FCe`P)q$gGRCt{2T-%P@I1H7I01I><`v0HVhb~Z{fcBvygTwMJQgS9a zK;a~oC5k+}Nb>aPz4!LU%i`QEz#Dt2G|ZN4=Zr|=E<>E8DMC$=5?+gYU$*G=b0UK1+>G7e;7h( z3ZUBK0t4Q|xrYl$(wNeOvN4F@F;JB^S4N4mHGZ<(Y9wU60CEH%=d)(+hQzypL-4pB;AL?Qxlp2N8D&%ST;{&ToKwM^Lt1}T5*NKx5gXY zk`g4PREAusZ!XtMSeYWuoyNE3R7x4J2-U~{$I2|UEUN_XN`h-^mD@YN1$8ZsflQ&X7cv7B*Px}Ct>K#_J z!;_##;pcI`+{CC!%uY_~oAXAiOrGe$$-L9^2iU`)h|vZtk-$?6aBZ`QVCm0WFIc=Z zI#y;Od(N;>GT69k>4JhMEM_PIMllOKMJF6XD(-gSlqN&vYz&wdEV(~Yc)@Y*3`)LP znJeZD^?!2Vgev&pJ?u4FfjF*F_Io^Z1u7!Z4qOoc8`NVouF~~*epG?GNI@0F&j_*x z;MTR8vLl{9qrA*wC_4U3F;@kv)3%280jNeWzvJJyPOY9llM*o|^oK7RX_EgYv@F0Xob**FqrMJ8?>4U+d)6usv6 z0fqn{pMN*zC|MkgX~TJ=WE3h|tw6@g#j+nVLr{;5*FJ!DZLR$PpC4Y2iLe(Efy&{# zmRgZI!t%_HWi7i80Kb2H{toX)mN*&9KKT0%^**B1I{c-4SNSh^hHTnQ2>+Bq6 z!#`d{QBdCrq`t3MuHYTFG49=nFl+qi29y=lss-AQJPV)%q70G&ZX5U+Q04jApbF7J zgvT9(%R*%?)%vo0ULliZoKFz|H(Kp|-c1s&53AgfUA(V`5fOSPUI^J85e7U4jrPBr z2-^5Ke^;RV{|ThR4n{NzWGICyj8^xS-R;niB)nAs!{UkmrZ`+^4L!L#B04u-7;*4ykMQ5pe; zFy2TIUF+_8Bj_%O5&`Qtl>l&%h+!x)R3Db?#w>|FM4x)w~n|u7Q$?AcfGC+tXgGj5HQ3 rBfvFaBKs|Q^e#JYt*w7w!Giw*R39cKIu>1m00000NkvXXu0mjfTl?|s literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_2.png b/assets/dolphin/external/L1_Senpai_128x64/frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..36c3b4abe1768c8cace03ee61dea063f00ada2d5 GIT binary patch literal 1879 zcmV-d2dMaoP)_^tiM?~TXV3fwpsn~3af zr=t*nyg&;LBJW9H&NE%DZ+PZX2CT{5F*cF<#_$s*c<(LqQ>lkTjN+Vu(7%y+Wl?pZSeJg1Q79a z0n{ED%#+1%=MU?#wGzPFy;0WB;&)GS>AC(pEX>Cj0bUx5rCVfpD=U)^WJrZR&qS2p zXq-txw#NE19V_ZNKpPnCewL|w-a&uPK3|A-`id~n``ltxNQ&~R>}I3#Mdalwo4_-G z1#;I>4A1OruXG$;_*n+b$jOqdvZ=D4t*kc!Xr;0baw_90Pa89Y(DU}9N}!AZv-(yl zE!I|DU#K%C;Yv9P$Gy6VK=P1^A31TKZFl0Q(D2^mk zFA0#bBH3PU#bk?+(?HhQQv|i!8_2l*RoVV596_M0G0`{#&%SR@0&Rz^Vv)i)~i%AH2_8ePkm5WwO@G|-~rDLyf#SFtLTS#tGwNj?7SABEZyA}YtK$mS7O!A$*T z$zq9IZ|=13J1zr9jql|I))RQLGuJCR&GI^)Ec84CwO0WY2>Biy)0^d|^szum=@d#!-pmEbrjSsdzpQUe(lt@QWcm6J)bq zb~E-m+J&tCT@6O+Y{{(OUm+YU`(}{d#h%MU)q(XQvkg2d&t~Bj4{`;gYotOzFQSq% z<0)pyX)>y=-Wzi@L-ykI?*>M3lP!LA=b`cJ^J*MDpNGQpM;PY}5`>R}!Q%WaB7cg=cMe1C@~yedkz?VrV{XG0vhW6Wg1UOW!rupIuYtjL`Qc4m zu=DIs!Vx8^%~4j)`$m9FO%B@16lG=DnS@~Xdz7c}jLitrize%Z0ISZ`wS4sSZwmSaCx7^($d*0`KH&)}$-lKO)za zy-M`xTVF@-Tc2LM{6a{kEMg0c6h9KtVkC2!u-&5PKG8Zy@;vVw5!|!cO9;ri7Tr%X zQvV%UUTGaZ0d{??VZ4%%jc4nPJce$2%K>!l86DVUT-R!3-TSCMdhurv-6MgXkB!NY zz^)Cl*&6}8MB{3)w5m^b=b+@P>S3y$w0(|b^m+oM-?nY<-wUvD29W?My8_Y8%+MiP zH0?IgywS6Rs*a3&Om=l1+aG>AICCQ_-Fuc~BMXe`Yb)D}s~P=9^odBoWUxxX_HTID z28*aPMEyq)DQ|V?nXi)UqCc&xCvP-I&_JF#l8q8{3->`#)c~ke(GO&*aq;MC0s-kC zmB1P^i9BO1Y&Gyef@dXrh=H?7u+j^7`m?w^6ImN*gKFE^4QR)_l!3aV77$J9RSPV1 zdo%9vFl%geVa*HFTlIt*g2 zMDKDN7qc-xJGXtm5;IBMurjF?=|5NbssgO5@C2g7$ht)q0(y`o!I54-j}^(C)Qu#2 zC=_JY#U|;t4gORtiY2V1(Bp0L!EUaH7*`{~hz_G9Kw;MsqO~J}>V8k%nJIz{rYE$e zUz;e3lT{+17&g+cfi2o7yg1BIx$Y&1Sv{VSF+7%6fIP*>VrPsTxzcJj-=kl&fjoG9 zyb1-q(m8&N^NnB0G;JG-RgsJc%F`kl{G1*w%<88c9(3=GI5TJ*I$TN5$#9sIAH?tN^?*F8!@bg3) R;+6ma002ovPDHLkV1jF*qVE6z literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_20.png b/assets/dolphin/external/L1_Senpai_128x64/frame_20.png new file mode 100644 index 0000000000000000000000000000000000000000..d683b9f625f773ebfba1ae7b290f962e80c5a0f6 GIT binary patch literal 1281 zcmV+c1^)VpP)5Y=Xt`7%i<+kfE%&632<@T1h_bE0$dz70WOZ$$j*=BKtDgv6a6>u zgr$~&+IM+pm*nb*N&|b!?;(0ZIf`w^7A^Y-Axpq6%0G_dNmh#Nt-M$}S>R59U6fz? zu9u@Ci-ZK!_q^e@1V{#-&2>s#dxltMgrx+~Fuo_&aU2h;Y=cFQiEaCplAs`(499t% zr*-72b#)`=erb_;!u7p;eEOi3*UI2Hn+Mu$R@X(f<4pjn`q zA@IYXU6!yl-<#xVw}ab2D~sY=MYUHgtjAeW^CELAXE*_7Yya#SPhhUT{=3A2TQ6XZ zS}rV(TSfq@!B|_E1=>l>LQn}pJpi@dKYG>+*c#ot=fY!by*|R1fDxjVUO*{BXW2*L zA{_x{mKGro2hvDBitI}WV4<+c4IhB<##ZF!jWx^(V3GTe-dR1_J;jgC9as@Gg6L+a zf?c;T7t{rIwqgxTJb-1&H&d)+^JUg&Cgpp3ffoSzWJXP%1qre#UJC6PUIFO)+8HN| zx<}wCd$#iR2#*-R!m6y@GY1*TxdWbpKx%E8%5E%j-@!7jf?*cGP}DB~_^OuX1`8JF z5G=BQ?*QkaA;1~_KHN@&qv@M{9J7S5gy<- z{89b~bd(vqq&=IqhD$^jhv` z6I=iBK-=d?##&a6yy$lWSGl{DnLr&4bFMvEX2q+KoU7)LQ?6%w01vE=1FV$ae%^}- zyqAjFx-n3_M2#l`aFvwdxK@3&P=1X}!CwJJ0;$&t{h2rodIQGtr z)h?$^uO&kCQ?VX(08U%Is-A>Il6Sf7XR&?&VIc_MSwYsyuOV%hv(~Ou3U@r%#?>mQ r{?DM5f?~?cWI0rDl#eZCt7C>wS1=;$EP)zbW*x_N?8Gyj zPpVexP7){n{F`aZaU2KS_^^1{Ex-*Qlu}INZUUSfrIfDnC8YT5Ezpg4SgoDHO@I@@ zQ}Gq)2vACaJ1_kYsnV@ymW}{7Vl2Ifn*d3$Qt22s0XD%?;WHsKRT)_0|1N7-0j_Mb zpr?FkF7=^3z^a5ZiqIaipOyfbtA!OF%xmvy2!It@!@#V2Ta7a*z9J0)B&Y}MaR^={ zidMQsM$O(*5P%XGdjU_ydmu{&?}6!bS<<7 zvy=e%pfy6)XRYHY86uRP)yg@95}-Zg*#pAkz2~;dkquheLJ80cGi$+~73W#nTc`44*V@mg2qTdRZY@#RZtm2}o zhaHxx^_W{5L0aZg^}dJly~potNVSy^XG|KndVtvj!kxdJ%BSQ3$d&z5GFjxD_5dEW zxhK5iy>&iopFz4-n?qUGGZP@2Rd~yT4^~0~ue?AzPpf=w)uARpq~fja-_z=Q`{NQ- z>Uk@)d56XF@tk4;WV3jHIIG9N7KYc8uB}xNwSK#{c3#e_3<;1?@c?^c{pe+tSve~3 z8Y9JXeHG5DbgO_}ikCEo0DR@L#P|w}MS4RTu3D5*XaekfAFuzX9OO%me`OO(jZs z(mM!`#*U=Mm#jmDBda%faZvsb0QfUA0dBU`p9oNM*R^q}w@dk0MoTY*DpQB!40i#q z0Pr0EegVK&<=Dr?H|8ACu6!nGlqSKJlJIF>B>~<5;1j%C{3Y7RvqV@`22x$p(VCL; z)%Ld5u+p8r6J(akg1X$fow8TA&`?khcZ)mH49#q%C66}igoPwguyEEi9Jv=17 zvmPMQ>a~ZTDJzcxzL}&*s%bzShvJPOP8e@UUxRAD_i~SCvwBQ~-qkhryHbknzl7n1 zydh~6)$d2K+1gX@&6gdc7O+bd5bg5WrbXwXrgwSc6)LhRUvo~Q3PR-pRw=)|p{u{c zpB1eqz#c@J9CDr_oAM>Vdmrsf>;a-nJSw?q*X3mBZJ2BI_B;pm-PzGQACU*xrTi9> zya3PHuF-pz`u=L@?P*AW)yNG?;Zkc$)m(B24N5~GWv csIj~87qbzVAj79-wg3PC07*qoM6N<$f|ZkdMF0Q* literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_22.png b/assets/dolphin/external/L1_Senpai_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..dd241d24ae578441ddb0870387494b574909677c GIT binary patch literal 1102 zcmV-U1hM;xP)-TmB;NUUqxyH1B)8AVfAY!<; z+;zDqyd(WQ{~k9oP^t!yJZO~YA-;x(_>$Lh4yC%g0Ig1I zitZ8qBZyxUcIN)Udb8mrG(s|D-m| zEL+yIuGBu-hxGLd@vrL=MS!0G@ErhtA>z0EA=e9kY@gLnDjmfiXuE^wxURCBK8tc5&r@Jq_1h=T|f~@cmRGe zsfO^c_j3#|nw&%#U~AE}6Go2s9N}AS96krAokT?uJF#M>YbS3OvwrxjVI%@X4WHi8 zN9(Yu$LOp#xeoSBm?Gv9H8^IJgm0_u@X&SeDxxnhl)NxS`;rErFTTe36b;}}1q2XV za7x~8ZWb?n4n=FMJ{e%etdE*c^r*GFB>b!yS|@ngWA;h{SKN66>?L{)?Xs*>DkL{0w58fltrhnwVxG* zUfaFQC@u+;mQT^rt-OqDWGU!YxENR2NRjLmH0X_o$N*G0DLc<*3oNgzTP9%V05ihT z_j0b5N1iD;oVhzR%GQ`J2;snaf35iB&Uu0dZXXvxW909Zh$k(O( zO0^WikN9>h|9pUaT`EO@{AWjeJBAFPJp+4*+7W&oZl4h1b@_4ljQeK<9kG4+00gC3 UrPkqm<^TWy07*qoM6N<$f~Qyv(f|Me literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_23.png b/assets/dolphin/external/L1_Senpai_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..944bdc74e99bc530de3bbf37368532a2dedaa377 GIT binary patch literal 1537 zcmV+c2LAbpP)Mu1*` zAAPD^?QR1S=z%19rX0s{;KbYFvYUYudvPkjo8we~H^->}Z;n#|-W;a_yg4oaP)b4X zS}|%j>YoKKAG7#bjp+R8wE#Gd<3K3|Gp;ii;hjquS@O9=g}PJtBTv=3e+6V2so4Wm zjP6gY!Ez5UVxr#D-)2lY((ctf!g3GLd)8wxbF%zd#fZ+Ud8#^hX-&3!fSHy?X&lFK z`^?_|ODT7s-H?>ox451M=y{On z0<_v^fqEH^=)ANT(YaYPQ3j0?T&&Umh)L<%Vp4obIFg@)dWbdJLsWR5Z)j*)yt40sH{)FMz+S1hsTV+ODyN-nqG#uMxD~&r+|~ zjAz9y3j=#Ce1M+-{=k>To8t#c92g&AM#r!sQvo#mTNHSP@B#cj3s5lei7(*;0pSDV z7fiHcWVK%%OY9bYz21(4dmzMr^qK)MOSARDNmk%k$86D-czJ}ta)$6*#ul<tzP53*|0%XKi2m(u`)?ih4*5U&nE{6yTpy%7?bGHP5XWO`b2%$ZDu!^cEz;3wY}r zvm$*CO2=FW0Pqnl;Ar|=f~eVxm<36>B7v}m-iX$<)p*oVi=H)F$Cm`H5k9y6Q9w8& z7U;dkwZigSG_KlQITPMDj(YuepZE9Z^nTMAh?kev_!M_rz%KXcp*vIfRxi*K*hrY! z9>6+h_i^w}XeKGOu`IS{D`=fpc$CR&6xM#%XA-_w@Lp(yH#)4+rhQR(gs&N+H!6=$NhN7Ei;+v%)LptnDrm08)N{^}+}6`+o#k1_Yi@@VwoO?amsHw1Ubk zCD4qcP6HoR@SdPED*Whn$-i^HRtX<4YyrSy34aGq8FdC)SCF1(1>~ku@My5g;7+Va zlSZ^7!l%OJ;6k4v{9zW*8Z63C1gmQbJszUMr&kIt{OA^AJ`3m#wi=pB+e%~@fY$Qs z)vE|z#Tou>;HnB5w|2T1JD@QD%|VgocPTT-I3tkgaD@IpKn7!(d;1KpmF145akvNI z?N)5T-xOfQzS|OflwM|aPt&GHuO)D446X!L!E3V<$qRh@+dwTr%CyDuW(YnLvkicI n1xX2{LXp+GQ5kXiRA#>bsTpnn;ED`h00000NkvXXu0mjfG)dy~ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_24.png b/assets/dolphin/external/L1_Senpai_128x64/frame_24.png new file mode 100644 index 0000000000000000000000000000000000000000..3f445593af79067b2b2ab416f92953a1ed9c4cd5 GIT binary patch literal 1414 zcmV;11$p|3P)N znNNylld*v@KgN*kQA#Or<7M$!Ex?VnxDDXNaT~yk<2Ha7$87*Fj@tlU93cj99LLgY z@9zx0BdlH*>UwzB0gmH(c$Py=SKnap$vhOLAzewSN8~@6#_3DzP9%z^GAyu z^}fncWolm>>KJ^@Gy?Ysz`^SI()rqPGpL4;xrEzB0O_7<_52Er@}+w-^$grN?D5`^ ztj|^vAi~=$G^KgMOccq=EqdUdF+BoMNsz}?l03DLDyC-?+_;s>$5CgL3#CSY)!snC znlotWu7a26PelN1jrINLisy0TdUIA{7v%#ypArD(a)Sc^N+mpkHW6Ea**%|{vy<{M zyduEadT|dDW>(=m7NeE1m7N&mUWItSRnBU)qC$fKF7eO4|?dPmWA8`gM! zopY2@iuU*<_2{UCjFJn0S0*=xnq!rh%8{a;y)}Z~7}8+g8s!1ck)A&&YkSfLE`H|6J)=+!$0>~93<=dm{W&HRaX$&BY09Qej0nE^|@Uie3 zIa*Jj4@ve|2~i9+?=yfi{s`m6^k?898z)DrPjZa*6c7!sg|S92P3ZPW5NF3~A|7?` zA@S@?Y-W#LSR;W)Lh3o^L528U0_1@VhlbFEIc`{%TT_R#}WhJ6r52(^M=>yAR8wDUA6?E_m^2{ER`#9O6F!G zGJn#@AYHSwPQa5ZGRnOYT%jQ4Q9XN%DsAs#^b*u|jFvvPma`R7*Gk}(m9L$HVQEy& zD2!|K(r%ZnAx1ls_GD8^+3&}p8hC_YD-EsJdq)Y1 zp4I^@UkF(Sud{MX%X;M6*>9xAR*LQmA*qN+u)O>7y8$PIQX|;P06e{%qPtS@o?DA% zuV?E3J7lmQk`l*7pAGfd>!S5u7oGsHBoFLm$Pvdj3C&&hcTV}E`@O_)MDI+uIAWo_ z!rq977wu?xHku@ORHoid8LE|vyy;3KCxo!G9mjFN6Az31Yz3Y;7f%8_IGzM}a6Ad{;CK??!SN)(gJTB(*tQLN z*9NWKRjyaT40~(2vi667@?s0$7y5%8Xtu zj--D*9jtO!1dV`RznCHb9&98MNZ+drB01ZWUyQan16I2aM*y!7O^NoMPNH}1^^qoy zLGuMKR&{;@7YW*Z>!Jd4uN#Mw_B4MJyTVghP zev|H->HtkR7C=`^4;JTglCd)c+O;@k|_SKTFYCcl=+^Wfh)gdGK4bCY%axiu-#A`;1;OxxalGB_8 zkMpu3hWadOvIa+0M$6hv&C*6JBXw%!D*oOBuUi#^RvITzo%Ee#6bfphts5h*fMcqgeEr z1FSUhN@%6i6$?teVq6q=nFMMWJ@Kqajwo&j5QqYQa@2_<63Ol*3VMi|5dnSzzch(IIZauq$CtyhwOju}b(yt27A4W2Q%gx7mq>=FPoEGz6#*%(>^=QU1dsMM0f z(Hg$z5a)Zzg~@ak{rvPy1J6q^7|2(@;_hUd{Q@2QU_BXMQdUc=Lk_ zs08qa_sr^B7AmWkUOhPb%-D{kG1OA>Lm|p)X_tMb^Q9R~f49^x z=s7DTr`P($&RAcx{QDyt^uj7mLhm zB#2_Rd}C~TwbN;NQOj`;*&H}VR zk0VDmJnfEFA?#%D_$@n!(nk+SKHF=~mjb;WhH{QrF#z<$Ki2WgTnbHj003UfB5F`m z5$*x>ekfcZhd(uB+BM4YHD8GeV0Bc~peTCG7;Rfe;pu;6Wn&cp;B$1Dk#txo>bF88 zfybFrpg4jQTt6SX0DQ$2xbK#RDXh+4X?7H3l0a%_26nIX)ll0im3!6!uBs5NB%oX0 z7ShnnY0ER)Emm zaN9e*aiblzr9jH9-~d`{heLD{UwR1uXgi&ZYz^ssX}QQqGHEy^0Y+ucEQpc-U!|Qz zh8+^J2=FVZohg{{j)JBHwQ?94lOTft-vQuvLOZ8L^w5Z=+GnWRcK9j(1^}-B@Dl*O z=9^?k_TiHC#SIQ#xo#;>Pikp!Y3VPT2lxa3oc|jpJAXBJ12!%@*b7n~!b5;J0QmC$ z{iWHe1u;7o@SM41+4(>K#ei4IrY0+bSZOx^5LW@|o^(IMuB=_aI$mhRNEM(j-_%fh zJ1YYb0{lp~q5)@C0Sqe{r23J19|Ul)?3=rn&d4S~O$G2Vw{+y4(Z6O_79RdDpjRhn z1Kkd4j-K(-i)XvaoiP+V{C6;|z`f$NDzb>nIp1SLZ)6@29mnzb-@&+IPl@Y=jA%LP zd}_xGNaL@aH171r?CjcEkm3AM&rUj4=E_O!?6mONou8=!BkGL=_iRpi9IU}hf>!5^ zaDFstr2YMr1c)Ny$eA6}0AP!Ry|6fcv@&><02ARUk9EDUI?QrNv|8d!N^1jLKmh=9wuY9J z@aT65tibt#>D$0rGlY^r+8W_97RTfEdOE-5+2Ju@B3x<-pmL~@z_Tn^%g+YQ`VJ!i z_uxl9FiW4_T|l(17d?)<0p6A17);yI5whm^$o7w7#CUos%~LM{7Uyb;WI0kjGlna` zC`8k`9?Vd(?7p8;0iw%{v`kpb_Cl{WwJwndXa!B6tRR|MWOzCP^b9gra6Co^3H}1X WEBEEL?wO_l0000T1`t9(2w}7s+h8gKh>Cy0l75)R03PL! z!4wAYD1RId;6nkWYlPI#bA6OQ7DosH=Xrv=0$|&(T!BaVBXP7)s^$7%ECPVDwnwuA zX3F2e`)7XF>I?wzYyM8kk1@`9p3}aonE~E!bTUAG{~eGoWuEyM1v3Kxd_Wl6rRxfl zBO&+j_-A>)8gY=PdK&L}XMnV!##%28zOn&uOIZ4D(tFmP2T86JvCpCPY_F&1LaZM7 zb8EU)`O-L6qzsfkhhnMsb~v*DFIM(`tw&pwpZAm6tV@g3-<<`dgHWDMecnO&06*zk zNBOO>$vNi^&A^0rND8JN)N}YkS z=WF8!P7Vs35gb&N`_kFQqNLl0jYqLz}gsKCzQND?*r6R zzQ0b`ey5#xtAVA@Rvijm zmC8M+LH}1A##<@$;{8x)D<$s|0BSR)9GaTFrVtgc*13LS03+h=TK<=|12)#ADuKQ5 z-+6%Q11!V zEHwmgCeX_W9tL1=b@_^hkjDvBKdS1Q-k!1Nc$tIk$wge8fIfFd5sd<7rXD z0KNkFS>ZX`gDVVKV7V_iQh+xAuK>OS_^cIZg;;}Ryf2-l#3fkb#gVx5C!(srhOxyhY-T*xeK`V!s zF=}N2Q`e455w#9>)LhR?8A@rtLFykrcc}z=D9ejT!&l0;rFQE()Lt5-c8`3wisrR> zd;~SA9OdU_SuJWU4JCQB&!hU^#sDaK1LXDJV+7p29C{wQ(Hcj$&7hXcKwd-FY~Q9b zm8yN34V=-3XL5to>^`P_y6y(Hjs>)e&t)pfr7?n;3_&ZyKPhI9EFeYgjG%R-Esasy z#_f$#M!?OtXD?t%v<|Ac-MJBa1rkg_VQ&Ti`ql+3w3y!Gk*_pX&v&)fFmAyB6s%rI zox<@bj$(@|hdWwh2m=7^Sl}xCYS&N8xV_F4Xk(QWw_<=AMwNQMw(z4#y?TJl1WK$n zi~)eUEYLc;(B{{pDPA(zy!{po&|=~##Y@yWgAr(I7eMlJGBo~Wkc6ufHaeq_cHBn5 z5(mA^V704Yl{reQlj2^$Xysoi50^H89(+KWlPP{0R%4G~v?0#~i(@SN(=czo-Wshp zyd>m7Y5F~dX$)ZW#I26fnx7j%KF3p~fu4ZWvxCR&MAv4D1@N{a%PRSCC<_oN*p$*z9D>JDgbx5={jIHxr)dE^@NBDB zm`nIF54=EEbNtRY7wwBu=7q~!=9+TOnS1lS7jIM`J@wBd{FV^wxp3tzb7rG}MLfkZ z%{hPlQDy4q*kaeud$(HA2k>UZ7r5Hnj|Fc*s@Jo|TWM@o=Fif{6@-t&(C9I?*0_u% zr34Q=pxH{GS%jksR7>qo_0Gq-R+KMC`F82^d%OeK8nET}(elwy&YN{UBWoqe^u-6@ z3PvDVo1+P{7mYxqx^?}LwUT~&i2`OrP3-K#r_ipmq#Pq_#VHVJ0cL~ywhI49N;5Kt z0v8h2pB8@t^A!bfozCJ8IS47Cqw(4cBWqPF!B!7qZzZ@3+*;SqYQ>10NAz2}u@}-nPdj^1i z>m3`%Qhg;C#=>rIC0JX)1UMyV zW&ODN+JhktO93WfQG!y2tFNio!b<`EIJI+Wtlqm6T}TH+8ax!>Cjfjnv~zkarkN9B zo%grqX_|*m8U3nsF5Us)4FG-v!1wME1rT-kVi|)*NZPoc8lU!#f%f#V*8=>7PvL*S zL4l!v?BLxqM`dKBOt0KWbI{~;ATIuRFNqEhM67j~W$pfQOzp`A-5 z61YT;l;FX5T7mkxM>Ozs9Ng6!&#tM&p0`hPBd2T&)J2v0xpf?+O>G;kyg*OCxGjqU z{4&%z9NOzuoFmnf$gzyA9#?tp;j@fdfFDD>pODdOR}!9OEk4M~1P0>etWl027cuna z4PF%BP8UN)LaNsJzqb{Lidb%E0cI<>_eraCC(v?ka2W-#3f5u;G?~XpUjX;fGX`p& zqypS&8vy820! zE@s!p_mm*C-gm1%?f5;R4utCOtO96GlCFwIfjDqkZ9VUGrWBS_g3E0I-2P2DLnFtx z6h{z#t1rCGrIg~gfL2~Rx;_PWygJhEQH9@X|LH;qS-?0*X;C!`?oprG)1^Zds*DzD zztxo2KDUfTLx=@v4WRX=jMk^h*Unt=zP;c6k@B;J51>c{Mdk#wp_WJ6bx1}j0vEqa zP!WSvRucXmxLbf;0oHG)wQlctC?&vkq&{&O?F*t2{Oq?MK&b%HAz@Vek`*Z50bYFy zt8YSWDH>m9Pt+D*h7!y`n*fsPOKoZdPxZCO+YfNDOrRD{nZ1)S9Nxkop(_y-=@3Ry zkGbBvz2>7?8>+v(Gp*eg;Or@4&Rj=C9-8udb%7 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_29.png b/assets/dolphin/external/L1_Senpai_128x64/frame_29.png new file mode 100644 index 0000000000000000000000000000000000000000..e2767bd65540a0a0a456f6e0f8938ffd9ea77722 GIT binary patch literal 1179 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 M07*qoM6N<$f`pe5KL7v# literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_31.png b/assets/dolphin/external/L1_Senpai_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..037bdc8ed6eba76a6f3431eac40d0af8ee688ee7 GIT binary patch literal 1204 zcmV;l1WWsgP)PeYpey z0OpY(-%TF{d4KMfiYZRgjHD|SwL|jd9V;rdglg3)s*aK*(zNC(M(MHdJKW2%B&X97 z84Y=I9i9O?L+}&6mjHGR*~m#!0(z0aEBK?AF92F!p7sDeL%lk}C7_2bXBhcU)y39n z;H;BCNgEb6^>kSEdlboPhj;*$C75#@9C-(hZB8k$UiPSMd-$_<5@6K6q;;e^qwN+n8};x6 zh;R~@+Yl`q(%KWA05z<9Ge_EP4Q0MBv295L%)*gIe!@k71z|Z8Ek}T8Yw!qo?m?C# zz?spjL`rZGU?F&%&K{x^92vFIDYH2CH4>ub0Lw#?Mglb2p?3wtd*O7N}VSzOXwZ40*qRNkE=JZaV<6h7RM&Q;@AXO9Gd`(xV|J-;Fx!i&PcSKekr4+8sYrGWU32?VB9-zJF)>qp*UW4!i7!7P9mBJA~Dr65Upqb-N z%GbPCC<0i+pdLu(xRdfTq_R0YDuTF$q|^5Znaik2_hZex?a`7NeJM1`{X&N=e}?l z)w{1VNT6MZ>fby9M!ih01@PY2UF!`rpvcWe0V~GiXNQ+o^xhg~R0(kAmk=!;k4^$F zrx~C^fGf;kw0JyXy4e%?{ay$VC8v!Mm6RjLQ}C2SN-0(*5P%liogFhz5{!_;moPAr zpnK!-5qr6Ej@Ec*O3M*z8p|8q?Z zjY4}%wJe}4L2G`mFmQV1YhZQedO=x&);=r*_+=Qg46e?oViL6Abp%WgiBNl#?@b)( zd=G&6t6w}Zs$j)i2h#Z-`^(p3RKdy>B>0jF-d2DHB^Pb>PAc0`Wn{JI`!dJ~ZGneI zv0fy=cMnwCCV&uGkfp-Cowkv{cMkdAXB2=tZ+wDM%&77%uloURJdS3bDcv%Iw87&n z8THmh){c^w+rZXM)vDkwRx(CgfmVO*I{4Z40UF5llKBT9%&-Nuq}UfAGl7~HofS11 z;0qV35ALUFtF^c0D?#b9eE#LW02n85)uW*G0Z5B5Bu5Bdr`D#q=daxaQqT9jz5(7* z0C#mu)e&-Yac$HbtPikac2Il#aWcYfHHC46lIno7Djc4dLFErSJgbo4$|KLRZmSXq z0A2`)pOKeARQhms5@_E~^I?oA_$cSiA1r5aN}`ng-tc79=4W>bC1%@H#aMlv1L7 z06baGOGHIWI2vd@bxD6IIm<8+E1h!gF{=MZ5XGe03*8O)9ZP7^yi&z;=DOy&b4ed6 zyH^`m>zmsirTJ)`E#;3|;10m=X!U12-dmeOF2~{ZYtDKrJp2G33hP9U{CF1t0000< KMNUMnLSTX?97I_F literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_33.png b/assets/dolphin/external/L1_Senpai_128x64/frame_33.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e7799db4dd44dccb311dab62120e0203c85f71 GIT binary patch literal 1669 zcmV;027394P)j%|4n`=#|$QL4&9nzu^v|~u0;=M zbhRv#G)ZxhTwTxeJj*v~t%dx#@r{vsiPCEGrTa8`o@+;~H6_?5p&4K`EozgD3?6$P zBOh+V4@>80LC&z%u~+_DG<|Ev)NC@^2@Wm4Z2ZOu#dAF)njTxw`!aG-9!KcTjWi^~ zQUgX7V%7;bkL>^pU>PWjTQ+$98~8SY234 zPGHHg9bf`R89!3kQG-Y1xb01#u^7GbxqUco$}iJR{E7hE5p;O;gH|D$LszVYwrON- z9<{mcx0n7-0RecbY|T)fITk-YCeiUa!LfG$&Wrb zf%HSiN|F0`&vi@ZD9?NCrSY$|ex-nYB4|U}$EV>OMTQZO=pz{=zEaexrIb6@*b(3n z)+$DGDIzP}D-VwdoDUl3k*wDGRRi?Abnx!T>VxVu%h%IQXgsKGdCF>k-?+dVeLy>M zQQb-_9PyDBuXXs;aN`VjG58zOs+o<5G5LK6lH9e{ubsecY#mpmM$INsS)`Xx&hxzA z2k3drDmmIG(}`vliP`opviCm| z{4Jf81@(gi@PbJ@5~V{PW}}NlRODshRr)N$aQ?Fk>PGwSFvuIudlWwEQ0eL-a|ZZ= zn{@z3>ABj#<}KFC5)ouQ+`H!Xj2Fe5xkK`q7-c?Ru z$xowQ7Fh@3hDJ`x9T*j|uCIn^tqHG^N4AR3-~`@#u80DfuDwO1>o7eoMG} zAp{AdffMHpxgeZZbfxlpr28!eQ(9=e2bpJz&~2Y7&@=HAj_y0p6W0Kims)~KcpLj4YO~m9oR*GG P00000NkvXXu0mjfIqw{B literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_34.png b/assets/dolphin/external/L1_Senpai_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..a28aac4e0f1e76750594be887fe23ec7778094db GIT binary patch literal 1767 zcmVzK!>l%Rx%kk#J$3<$ z9d}D2B~oNj@=PwJl=Q*d;&xhr56l=ATGJ2*nlIlXfwtfqjvFq)CS!@}*JgSDO-R_mDS zK@ch?g<>R-ioL2MoYXridxwshodhz2)6T(mS3=b}YJezO=kFP}S5HjeUjO{n=5e=2 zc0Yc81qX=e+3VCT2J`f>(l54`V_1<|!(Ho$LM=y#2s(|KJC31c=UMB9pDlwk9a`*= zbN-3|Y@mp85f8lrBF698?J4Q=Wn(hukk7O}-ZP8>md#NAI}0^d?!)lJNFh%06r#Iv zBy4Lk(4aGRu4T;n&Wc2g=;u;O@k%~4tTt(@lGu&j1@+pe#OGz@bFEKQ50>X!oCYEL z+~iE>xL2jbESRd}qy_jcw_u*^=pgj~E_*mIAd<7l{}%(ONdG(1nrX zs7}$b<@g#ow@5pVbJvW`6c6VW!g8ZKvjR?*juVKo;s{Rd`WzSqjf8JL;7t+8-N z;#{v5!RTE8LyPel0rYr*C-Vep)XqLow}>2zJB@IlQOCObP1^H|rlf>N3i0G?^&!%% zvQ!(#ihM1=_M>{c-wNM{mEWbpl!!lvfb(UMvIe1&-}`&zhm`iGyj>R=TYsB%Vt zEW9OxT*2rPp%l;yudIti3U8biU0OrE)#qx4?8Wh44Ds^jiiKteTC3{zi2Re-?f`OE z$^YfMT{vlkt2n`t2!NrLae^H?KZ;d`x`GoNO97UFSLRoAu+s)$CGSqma)7@ljk67S zOL-C_$GM}J;Q)W6l>V78&NlSa<2#IV!U^1?V6r%Wrj&k9DgBjF`t8)c{3sX^Z8^eS zt=lentq$;S&iRM&{-xj@Qpx8PyKf{{%L}J~pG4(H4VkZO_=+pA@Cxk)bu~d$|2_cQ z8HUkc-y4EhT@vE>s|n(h14L?aqRch6(IB*i-WBaEW6zETb*u*mh_d1D5dKH7I&G|u z1~Q_DH~_o#y1FAsovk*#w|Fymq9zZGz2eCH*RO_IgT4~!la6}wfF%k~Kkcq$R!VsI z3n7uRh}Ebk^Cs$`$<^kfm3>}0SkW_2w$9#oR`m5EnPsyl4iNDu?Mob|c3q%KxfIxS zbcMF7ty@!Tz1f#+Ob#GJi+5q;-R19af@mHe)kiM=3`e(|K+4nlM4SNEhS}`N0W6|% zHR9K*Pju%n^3@t)S|e%w?2XYI37CF4=jY!Gz&yj8fGN8I-ehLz;4PXAn@p}23B3pz z)yEsVRSveNza1Qz$ja~@b+VBKMkBJZt-Ko1Z$ux@3A8cT&#ZmL4Idyh;TBsRfu}s` z&?B#f?6QASS8Kdcjv&DzwKp~+=m_&5sMP?hRFMZVt$A^GHNn92_ex-inT|YTEwCC; z|62Hr@WwoFwiB$30+#+-UY?1l4WvP(?QjEPpNBF~Hk1Q+Lak~+OC7J`1fKpf+KtGu z+E}Ro^F2B#zGFl-^s8Z3enXZ#vsAo-6SVYMm9i|p)$)!J&d_C$7E0tU$GjNL`BB;U z^GeKg;)<1|HYH9HBY3zBlA*#HkP#yci&_fkLDUI$MgiG3rPxi~2-#z)AhIq<((&0% z%X64Zw30&hNAjUfuE#O1<^&@;j5+~JyXX-0Idf3i?k*c^iZFvofmr%gL0+D0bp)2f zM%q@a(y^X7oB3NuI|pTY%$@I>iX-)555Sma>$waFi2a{qp&Y=uqF~ zlpep+-^vBNKG9s=6K$+i&`Sb)9H7SuS!A$M2)?)G8(E7w?tf9NempQLiU0rr002ov JPDHLkV1lcdZI%E4 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_35.png b/assets/dolphin/external/L1_Senpai_128x64/frame_35.png new file mode 100644 index 0000000000000000000000000000000000000000..04f8c1a7f30fbdc51cb1c9d7ba2d0a6b5e7f87e5 GIT binary patch literal 1832 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 LNkvXXu0mjfzS&Re literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_5.png b/assets/dolphin/external/L1_Senpai_128x64/frame_5.png new file mode 100644 index 0000000000000000000000000000000000000000..7a111afd03b4a20d23675dadfdf070cf0be65452 GIT binary patch literal 1869 zcmV-T2eSByP)q4TA|+B}Q9YjHJkRqu4mr_tw`xSrca#v!!aQB}Jed#Ag#>&<6N#i&nI8dg>Sy~c0)F!vwcnnrKN&2=XpjR!mx19z?s6xtC`9Gz3xRt&Sy^?`i#-Xb2hX;UOPH$ zPtVv$amLHNFvUa!^qSqEN{pQ3S-pF!iOlgl`#w?UEZ0N?F*?PJm#+$&9;%%wa&{KT zSe~5h)JJoRI8Ot;&XFRh-7bI$`&Ddz1$IRu)|hAP*KERT2>py;=1 zu9c+^4xD#`~;>n*S%I;qTY4qk7Pe~2$qC(HHi#V=2ZP7eEkn+B9USa&vyzDu3?` zu=Q~^&j?rpM9#P4HgSe9vU_*?cLTG$xoV@MlQ&vMRbrHP={9Cv+Z_Ofd`tdz;Vz?B ztP`arMpkJG@L)rn#|f(Ce>ZppHQLMDQb1PzRLMl_{SH(*VGI9E3SH#@e=nM63wp0n zD-CsbBwFDBe~8FG3+CB^-HblN-A<5R1%u7`Q$&6jk-tRbw|A@288G(~QQs<^?%2m( z2l)3mjvvA2zYNfIb@c-&MyK8TNvD9HCB_eyT)aD?=M)|)pYb=HP$^dl=m6Hh-rc<( zQlQ%fZF5#_663@>9Npf9?BoTP6yQCYL~T1m*35M$J1>(4X63QEXt|XS!(Ji)Bj_%& zGIQPetH7?MYJEDQQ#XO$v-{nS;BCezd1KdLdbINr;olo@7?x%XIO4HFa zQvsBEPZ;G<#r^Jg3Jk`Hv1WBZ+O@SMv-XDtYaY3;$bud_K;olV|8UB z2&+d{UG=O15p;|2VNkCD(6wqiNzsY&=Gu5KMQ3tVx zMowxS7!g_Ot6@=TVp;OaQt=&}fE{y36o~ekC6Ux0gm|GupK_ZQ*__YHwy!I((uoHdJKn)Ee1_MK1-6Aaeqw$LRbcV;Q*>QGhudmZaM@_^)D7F7ZkVONJt4 z+FT=!;VpKQXh!po_GgLZDj@qrRaw(f_j}sOkPu`rEzp*JO;E%g+Up)@d2G&QWO$pl z6DU9H_J)Y0_t>z_`^+{f+TF zEw=Q_7J?K;ij1H)3fOV8r!^v6sN7;<4eIsrQks389pL`}5S7jm`a^e200000NkvXX Hu0mjfzsZOo literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_6.png b/assets/dolphin/external/L1_Senpai_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..318c7eca0a6779c44ac6a7e40e50333a19f5dbb7 GIT binary patch literal 1893 zcmV-r2b%baP)Li{6-5le3coMyz)L{(k^m0R@u^ien z&2dow+fmlxNu;0D$cA$#LGU7rlwLc-UeR}=m11E z7eMWv!7N!CZvL21H3gROE=H(QdWcyM4SqJ?u5s0 zIL>rJZIAh9bu6)afHBZJ{TfrZq67aNyp+s24P@Oi7qI#9%bQGGL& zme!WtU#|Z{+kEYGk9NM8>;k;`Pv{&O98aJb>)`lV~+BfOEPyRuh zfIpKzd*h{|vLEtRDHkoU12ZRoz)C~fUZFmb=bBS#<2;>-#Oq*hapSW^;E)H(1;`VG zEmb+!~i?Q#K;u)m7!ci{*JiW(D*L$K`oaT6#zL>2QCt}Lh< zv&M@$q{p3R^_tzww-lh|32&f8CQ^K3PS0XhDwEDtiIRN$>z6`h3E`DvSwwlnRnVzl zmn@CQRk_o?@3;({HNM3OWF)X;cczWbsAI`O_aUhLDuC|-5HiL`1kf|EH)luwo?+v> zP3jhz$Kp;i7gDp1rT#_`M>fA`N^)oy63sotZUgfgZL@7LLRCP(9K~dX5~p1o{1n=Fgiyl1ytdcl#xi`$;s%F z4fWQTs~NJ2)4v<&<;@ivjV`o~EK84`&%)sjz)zL-qUb1`X@tsJB{A;6DG>m@$k3jx zm^X`6j*`(w@}n-DPD=rnfmh~N>0oDv;)+3MZvO_xoLvs^caxmMAm37+iHJcv(<6^2 z9S-mZ5&hF3=P*?2@gtn%NGBL@uY$qm{7FQ=6VYEp^xL<-*+WEh_4|m7Zq|GqX*E{WA8p_Jdj_$_=mK%^!I%A9(PsLxOcQk-yV3ZACDg4XZ#LSrL zI##ER)zKhb2BNoKS91gjuCr{ftRWBu%X(__Nn@|LGJpLmVPgX8&DT{3(}28A*4Vn$ z?`(Ki-~2iHdGx8`?OzCqltpOLv+`!NjMJW3WuI3L4pi2O^!DUgk;{u@md!pmK;(4r ze%vGZpVg-fE&6JwTZ0#^EE1@V*Vc_9hRFdq0r6hgL_*h9(DCwQ$mwu&%L({=+L(wF zNc$k0eK>$cG_Fqkh-^bgPYxnq*{UHMNyleTMl}*3{kCoU{9b^}Gsp>$vMVrqx*|Gy zo5sT?ALGrH(ZSNklbxMM_J`jNjx1zlc#k^S%mzy%BH316jp#R{kLLuM46Ra-{RbYh zLBcByQU4iu%1a%h;Xe9I_CtTTu9m!6j=+H;wI>@9bP4k!C~E*zs*IClohVJ_EOF}~ zqDX(OgsbWh)w3h&%4*<&1b2}=7?=*BC?^chFSSdvgFQE@hXuZI%Y)_7;+J; zy^dBUp_f5gDB-7E=EZ2vkIpTBj-aYTrxOo!asdQH5+it30Wwrr0ueEiVNpu~6-1rD z3xBg!fi`x`m(kBcG!;blMUwQ7w%xE2nk4#MJ4G|63so6fKcsJD914@3KH2qay7A|h z{u+=&AP438y)J?@!JZ*CqeSv#))At{iJU@dEV-@@Sb11O=D9@>qk23u9w`N6!23r| zaYSBqyvB$-2^>#5)ktXdu__eww+8bj(Ay9xT-#WSSIjW*nS3kB>q_>n@u|q+l6{at zIUhq2SmQtYzT5v?eD!o`?OkP3x13p7mO(vZS4km%Cdn>p*Y*s)4wmJe$1XEKQDVjb zv#cBCMh}^?6tQqQPlWaJc1w=M3t;3Rvj58%OUcNXNFJ(2n@m*@$+tu`2dJhpE=!v` fYHVvSEXVi{Ub^TjTGP(Q00000NkvXXu0mjfD1DE3 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_7.png b/assets/dolphin/external/L1_Senpai_128x64/frame_7.png new file mode 100644 index 0000000000000000000000000000000000000000..b56b995dc61244f395c0ab2713e32e00b8909c94 GIT binary patch literal 1835 zcmV+`2h{k9P)#P5NWd)xPY-?okReW&qvVB0n#qL*toel0wR2r7SEzwvA1akc z0#Fnfse$M{O2~Uw+Z*R&;~aqKr_wb(Kt!sMcm{NHjO*b^^y;ZY8J72QMWVhO+C9y1 zQ2X0Z`tT&uJvFl6+(;0-$RfwF+G~Ap)bT_YqYEyTBaK1p%iAIEAAN6y_X8RLk8r(2s4D0G9W9tY6FD6>@2X{yQzK$Cm-TTuhd3p5ocMy(T$u;zszo9#31;*Rdl40it<*BlUX++ zqKCHlns<-ueDR~DqSeMK4Ld7LvT(d2R>)LtrBe$S*8Yq5|)Rxqo4 zIk!fPSHN>FDWfH;9Or3SNQ8xJ3ibp>RvoDf(B~{Va(>j~(7lK}nz5nv(XJzBduqx? zf=8@e3saGZfIdeTs1p@Wo|QXWO?ZZ<>Gz2md%4CUh?W;etbA42)X;5Ama#{Hl%>t% zdA&6@k#ZWSHMRsn*X095!hRRqUxA|{QB;|zAA+Udx1B)QAS#$AaOHtkqcbFH^F-#X z5YmCl^Sq>h2m?nQB{GrX?3_M>sO(*-Od3}uO7i|+FNJdD@V{E;ETTN(DnQY1)mSs* zAR<@gPW!&&GH_P;7AKIAz#4mH+E^KNtg)m=%4fY=tRPZs5B95z~gEdYj@>#H|rV892hEyhqvR>_B z;7{sspr{awSL>o{1n=P&!8_1ytdcl#xi`jZ;C? zpsby|t0}UI(|;T2<;_(KZAFqRMwz2l>z*uM8AFF$sQ^%XJ2MnA=4S_m^FZZwr%@n-2WJ$?dr4NV>rVI!YSa#j@P%5H$PSJ z1J*f(hR!qoA(K1<=>{|asbFpIo_A@GtsM~~KElxDUdWNW;LQLcH8~LO{CZ{*(M3P1 zBIu3=W9%0Lh*` z%I4sPn)RSe+m#B5D7-5rJT1jqXZMmMN*rx4R=s}2`N9m4eA+c4`vu#`UIy3W8wZG3 znD!D!#p_leP^E;S$?okDbU@02PEEaNhhDNVIRFne-hoZ@9k@U4YCp+VSZThTuJ1XULZ_ zpgcRnEv&qc#^mj-{TWXd*%$sg*b{V2)`h5%RTkeOS8{C6OFNywGXi-H$rIr-4|yRX zh}CD-Fc4|cAc@WDau1PP$-bizBr1njU1@zaBi3XeFgJoS3g8p+&wN8BTVX^Ml`)zu zko-|4t(u=hGn$I=Z`+Tp;4ZRLcZ_#@3>sbU^fMFTW0! z1jI`Xd=fOcmO5sQKpTIyDnR!=xyHOzRHK$uG%`|Yz$j&eei~+#CXywuEEQLlKbj|0 zJv1{;R}`?;I!YqLKMvK(AU>H@GF73KKbb|95ACt|l|~$aR%vArscaBl4ukl@dhAqs zlw8zmZbq;oL6u|7Mif34QJyeo#vVXx9N|jxGrtD^RV>OST1kPd&U)CYrAHN3WyDzp zRoVE;ssZT-aV6eX>h>uILz5td@d;%r7!z6CVSLV@8Y-=R)-xOlO-4(p(Q3gO_n~>W z7s4w_km;d|J+JWQ4n)$0<}|X6X3XO+a!>?wComGuWD3y8Vh01y7=bre2VYqUi{NoS zh9WRp`_abyck$I+tzBQWI7fMG6j35LJC2o#5pRnSWOy7b!?njQGst>oc0BDKV!&D- z4_*ZFWQxdP)%{wTjhxmh$zM2t<@G#-%W&f5GmS0JqN6Ivz#?2xV?&S%)!N8=Su#GZ Z{R`QooeYqW*75)V002ovPDHLkV1hqFYH$Dm literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_8.png b/assets/dolphin/external/L1_Senpai_128x64/frame_8.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4b8757060ecd792db21380ce0c77ab5129d93c GIT binary patch literal 1772 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 RW^(`l002ovPDHLkV1i{(Q6&HX literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/meta.txt b/assets/dolphin/external/L1_Senpai_128x64/meta.txt new file mode 100644 index 000000000..122dafe7e --- /dev/null +++ b/assets/dolphin/external/L1_Senpai_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 16 +Active frames: 22 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 0 12 13 14 0 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 +Active cycles: 1 +Frame rate: 2 +Duration: 360 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 5 +Y: 29 +Text: SENPAI !!! +AlignH: Right +AlignV: Center +StartFrame: 28 +EndFrame: 31 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_0.png b/assets/dolphin/external/L2_Dj_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..95f72f901a5e03e63373957cbb188c9ae4673162 GIT binary patch literal 1640 zcmaJ=eNfY87!O~VNeuA$d_XjMR_@J904APtJopZ9>n2_@i7=U z7bDo5qK$XJUPju&uN5^?pmsbByVGQPN$hY<6bqD1?xCG>Ooqzsc5iND-YzkV`(MUu zy+r^u)WlR3N~EUfj2LD#=ya%2uU9Bh zW1=F3L=`@4y2B;X4uf5Kfw#kBWmel>Bf*-eP~sNUg5V^jLI@Q}A{L4z5lWGWsgxvv zlZ;h5foESS-*~G63rNH1Q==wv5?9fj0#Tuu0wI+Ij*tw6A_QqAX(dfqX^TQC2NLDp z&UvMid?wJI-tEiT03R=>i*taH62R;jKKImvxto-^G}{Q)(b3Vbd+KgLkUuD{F5A;P zl-J?7g@(qAj(+>#&CXml|0Zr_S82ZHZeDvy*Ju;jJ-hsezA0>E#l3uYha5U^Z>Hrj;ykkbVy( zL46TIbF*1n{J{pT?v6xo9<)U43Tx~)`z5sFVb6jaPj47mAKvhutpZ*b7P-a!N8;9@ z3ro9Gg5bIvmscG*Qq}j>>KVNk`=Zg*T_+_c-ONz+{$JhD?ojH!F6!FjbH_(KGvo4T zNzE+3jDH7L?fGc0wxi?flfyyRHyoX}sd35a<>mwOs(`D<6810nBs+(le86u<`ih#^ zy}u>s^$RmR4?>Tb!eblzdgovIYz}*P@xhY*>qgA)p<$=CdWoYxzNapsLOtW%ntDsQ z4(XcR_@lDhoOz~JcYDjDsQpb_D|=EW^|a>Zb?vTdi!SxwV{T1quXIbck(b-2s<>lA z=A4kxkjeloaz{{gXXn+Y0edb!OMh$%zf~Jw|8dQ2`@z*mMF^XA;pRVs=Vx#4^z56% zZH|gqDAqKcY&;qg@kMJ$LtJNE;i=`Vk5=CdHl3_0S3S)2d> literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_1.png b/assets/dolphin/external/L2_Dj_128x64/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..32e13541d83eb0571414851957a4b8a5647a8c03 GIT binary patch literal 1687 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 F(|@~CN2UM( literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_11.png b/assets/dolphin/external/L2_Dj_128x64/frame_11.png new file mode 100644 index 0000000000000000000000000000000000000000..eca4a1296ee3dedf9ebd3b010c7be8763eaaa44d GIT binary patch literal 1660 zcmaJ?eM}Q)7{5{wsDfm1laGv?g=~Cy?e%)Jca>^OTLfY&D~+Nvlk448D)z3l2egQz z&P`+lb&HF*5tVG6ZU%;lafqm>bCdXm&S7L5V{Aj6_<=^iIbm0z&OeNoyL;dFx##yh zzvnAgn{QeiC5jgT01%a%qc`)bkAE}~LjL_%#5NS?f3o1<(va*E4De$9i*@h7#onB$x(skEY^LQ)}@u&y;y8II!t8wdjN8j#z|aD)e9+(|(T zdfH98>`uZsFu3yd|`-ndX_A6&C63R}g zNCl2rQA`ODq!ouyxeSL08ID0Ti6W>>VO3Bt6}0mryh4W~a-|N<&eCSfWCp!f3mbGg zxdJw1$g?n59>nG{ZjN9`I_TTZ`<}+iUy4=fTr|P4E(^=93wA<&G0U;;V%7;Fs00O5 zj09ZYE@pshv5veQDF#%C<$5)DPfr$!W1Y1VI&SiC}zV51%cWKt6VJQ zDM|yK^IRtdMSOb(w$EmRKRlZ*n&C&v#m`RjuGTz$?q=jKu^9MuVq(I7eb27|5aP|% zYb~DMKO62ao8Vc74Sf^-H=EP3ttUd7empeZ(3|R&+hf0Q;(q`5)48`byTd&P(!KSar-7QgDb=rJ z&T`&d`M9wt0jL?BCvFSNU#0tZ>{H)9Anj>VXyYf=wq5Vog}8LpwTUB};}U-})+H7T zyCc2c9;bG~psDNE`jSGAgkRkqo!QyGaG<8BCY%ur<(!Py7+XdCH-GNfn*7VCuWO4S zW?$NYWptGfwe?u|!gQD4GPjN0FE>PQ@LxoifZAKo*1Jp5wymV%+~(7C3= zug)%Nm)_oJr5ZLj?B5mH6a|b_e7;%|e<5CDB7|^Q^al=e`ddFmJQ3vi1m(_9>yz(d z_m21ku{QINt;uwuD_q^`&-l;zS;6s_ZjuPu#^3Jhie;4 zyLPa-gK5c88&ma)<23R0+<~1tYBdc)&5FT<^wL4?>*-}9<3k^IS%z2iSJmAJsg5Z) z=-GC=_CRD(3>tlY{eqT(^ME0G`&$14Z_}mH{MDggW7cQ?+;Du^+dJH8x4jJ)Ua2{> z>~g(N{eyW!S9Mu|rl%ztC@&cbi+K=%9jh01KLkF$DySH{zaTx*GV;{|W z(mqGi4mm_>8_+bWlaD8 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_12.png b/assets/dolphin/external/L2_Dj_128x64/frame_12.png new file mode 100644 index 0000000000000000000000000000000000000000..5f92e47fdd7cdffb93e2cb52b64dd87858071c6b GIT binary patch literal 1637 zcmaJ>dr;GM98U}4iz153Ly=JC&`Fv!P1~fSyxO9@S=M1UVS3s$Noj#LAq}>8J|gGy zrW?~u-E?z$zHcWJ(Xs6We9hwyH@Tfo*ii8?<=n3w_Ddp2oF7Az&gJk~7)NuB}nY1YMnRv%f$ z6f-1UQ`N>~13=U|ucJgLvCTD5tWQOTbyOiAC!ql#J2%9UR0Sh|E~d=uH!AzSX;Fe+ z+NhkbwIMdn#CW`Omh#NJr9}>EX$3{l%G?|D~;}^&f<3u(8^i0oS1JryX=bTvALAFSW}{O2qN2UDfzlDU z3&ZsgNx29J)2IoER1-MFP#B7-buJx^&=EV&!|O~0s?nS9!h-xlwc29N&qpjKlSYSF zw3-4O(L}ITe?TDp6ch37m3$9l^)JL4OguvhEbn01%19^JJ*>b6JS+#Im)P=Yp6esi$rpIx}MbJ1zXtZj*4#!ZO zaw~`N^fT!jYE?-hg~3JG7;qFv^(3Q#^oUvm;W`Y3aEd@640q$Cj>O!gOQVD(ig37d zp6R5BNNUgU_UUX$ho{rU_@$BJrP)#7ubq|VF2+9BVUgB>fq{LUySf2D?y{Qm9igsY zx3_IRgN!ow4h-}jIOpQsRJJK?UC=?6Mvx9J;F@;a+2+2oO1 zzFu|8wr5WPm{rl>cBR_o?VslQ;wJ2+GX8Bz5(}ap-f{vhE4e9VYWf%j&fiQpdyJY` zOU$$%*xg^6bf)SndlFoK{Crc!jXgK~$GZ9)*^TqA3itBjoK&zWy7utr?zXq&2mdHJ zxxBe<)8);DE#u-x^glR%B{nZXFAt4KzbMW(T^ftZpO_NMM+YaZPEL9#-!QKd(N3|?l$+N920Vr zFP%*C{FK(bA_hDPFNyz4eOL>;GC6hA)+a3)7y5K@qDQ2c_;&$X6VUwqHlTK-%;Ou| zfQ)WaC?*2STDRRjQ@UF=ry+4_IhW8oL3Dfqsm|GNR+p#JT(`^_Q)>Mgd}BGEQg`r! zhDCXn)?hKFtqTFdw>`ZNj>NZL+dsWDkQU`!d}o1jBL$*qqDNK!5nhZ?zPej}$m- F{{s^?M>YTe literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_13.png b/assets/dolphin/external/L2_Dj_128x64/frame_13.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1017ce259d7b511bfb2d6c565cbe8350ca929f GIT binary patch literal 1654 zcmaJ?dr;GM98X(TD+i*^sccSTIXtJPP1CeZdK6j-NN*H-(8=R)Gfh$`&?cmT7Iu#6 zx%0&-6BVc898TSx;Hg99oDZhsd~ABCi0_-5!tmxD+t_?;Se62H{^6NR^85Y1`F_6N z&-Zm%ZO>W|8~t`P006Pp3?f@xgW_XW$i#P#V!cgV;)S$4A%}Gdev)SZ3(Yzi(CQ(J zm~4imtIE$aNdO>Q>2~A^dA3X=#d=g^SVtA`a3UH2l2QU3NtH4J=wyoAUX!x_+fz!= zO`DXtIvZ@`Qki0RMm5jmRA)J;>QV})l_|+!Qotx0@Gt@i20Ue6pD|!k4%;<~`>CoRG!Tarm{Q(;wl)TU|0hha1{k3FzQ5T#x)N_U_`5i5%GpJh*5(Y5fmJ~ zl%hwTb{VsY^ihA}$)qe61kR{d`~7~EU#nvIBDDs`@vsd9fkX|+SLGGR0Oa*0L@W@D zkK)~&;AXvG*dpmmSS)Z_YN}Qb zr|GowF<2YHTD?Aj^ioX3w_Ef*iq*dsYfR-CQeb%p%a%nt!CuS?tgo2mKm=8x;A|U7 zxxHb@d*LdL1Who!yON>PdDa6Cr`hO!1qGK*t4Ex$6GAZD0HFp9gPb^xLAX|@G3YTA z!6=t<6i>gDzL8cHC1My{gpCnHFvLJITF3xvv=F985eTDj7(y`@M(Rn_MLM-gwMbDN z?wpr8DIyZvGrWB<8{*-`bTM9Wq ztw&pOz2Czz1pefd5QH{lj9a@_|D7_VI224w&N(ydvp>vR>5|1K zPA)D6LQyN|o{oI!{$+p9>*{yNexKO+bO$#6cz(h~cv^Ew5882L_OS=V(n-Lf-uP9a zRbc!50(0nn-M!Ymi<&N-Q0&rVOg;Hi_d%D=UK^8d?5nsUZV&00oqu@dl6=44_kExD zKJR_sOUjLz^FGOa40A zApvPyvSe9;0XDePCc(98qBlk|m?0vJ6Hrjqy+*E`=d049h_kF15l4j9L&IWuFToFrtvb2zNtrL@h_v z2nvo}63(N?W>=et^f7i0Y}cQ9lj!B~r&l#A69$r5fy zYNKsxs}jd7D5io)s|ANpg&c=SIgUY;6-7|F(xSA%wy>QS;W4=i!L>wcda5>EF4qxS zEv!pRQz&6wf+7`z6=AI2)#^x1Qws?O#M!Xq?%Z%IiyVW>>^H#9CtWE2;BZHH%gY{ax9> zC)HKLoj@ynAyGVdW}pYi-+d>lv*q>s_e8*!d-?!-{^s#Mkx@3S4ghlgou8e}8w!e( zo|e7I+wsUVyr^6^#C3}9o~_>}zP;?9_57YcelI$Alo{-4NMmufIf zueUd?-I3BUNI+E6Kz>!b)aW;L$IX)5-hF!t0+qd*p?LS1!jfsT z?gkSAZId@$l73gx`nIv0f9xsCyL*^Th_Kfh9_(zX<2iyA9m3NwRBY6_*jWKz*%zXtfZu9Xi~;@8j}*ap0E4 r14aC?w7aOMg&+C!?=^uYKr=29SRN~Sr=Tku5B(POx=f-uHE-R2HCSU* literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_15.png b/assets/dolphin/external/L2_Dj_128x64/frame_15.png new file mode 100644 index 0000000000000000000000000000000000000000..9b796498c0c48c2c9125e98dbfea9c0c812345ef GIT binary patch literal 1344 zcmaJ>YfKzf6rKgPB1nVPmPk@M9co&mv-4zjX2KR0cA?vJaiwdam25kYyD))yI5V&d z{%}KUjP(aL#-t@ptfo~HZSg^DLSwYs z@0s(R^PO|gnb#6+trca}We9>)#P&qmq0GUjv9uVz7fMIsP(0yAJKgf-Jl&v>fno4Ks|yKzp5oW-Ol(vQ8D#^M^_8p1m2;$+-v20hxIAsg%;YD+3Zy^5e>4ZG3$tOyDo8(r(j?6gBn@wzqD6`oX%=0* zFc{HR(_(w1Wi=k$g|HsiwM2r*WHNya6EN*=f)WJ5^Fh-%w7{J~!@?YQmWe;VsrJIO%{ z5beM*`)viDNxHTGhV^@JAkPr85udd+cra35#8moqV7ReJ2!me&s-}txC-5@M2XRS} z1)OCl0hcI&$AQAqEXB#3N~#4v>*0ASNDJXeOG{I@g`%R7aF~oXH!~a=tz(*ak||(g zhT}?x0t&G;7<&~PeIQnBwt?iDcG5Ka3Z0PXFntSWt`@PAkGGP9+w4`#|5U23UWM4^GX_9#jE#~Z>3eC z2Bu*Z*oZvM(?JO^c#xzRoaa~?=M{m(Sw786oW!OjnZXE1k?=a_zD_ErV0(JYwb_81 zwdn!|94Q;l&SPbN`~>H2Lt9@`cUJ=oZ-7!G~sv4Z*RGdpLt(;QFk`(M1HN3K3S3x z?h*foSF4IX?k2lNh@xByx$Xb`^^t|eft<`8dnruc+pu@~*vV4j;ATZxtVWa@+h?~b zAKm-(&eCFaY&_K3@Yn9CrTJH8o$=WevIB~D-@hQ>q4&yD>PQtJ6~on$xmA#|9Td*{sg zzVn@P&v~ypxqrvDC$|9rc9cu`8d*E!$qXjQ``X|_g)C1*g{kO(XGJX(V30LE9aCio zHE<0hbMEK`JO)7Gq+OqirYaMf;W;ddeb~0+6Epy0>9&uIX&g~HZrE;${`1RoG-aD9 z`nAytU-5IeX_w{$d|-aEZp=>`(4^C^P-AV45I8tORNI+xL#>^n*LXFukIfuSt+_>$k#Zo)oQUViS>d8CqM{e4pC%?2NTY@5o$ATIMQRt9rDs^R}-Z0a4>=X}i7aOlkj200UJ zqz5MTdvKuGk*LvL4Qz5SXeRF&vkrEnay~_qKdfn+nxQ~dm(?VL3>`AEBtQlUP-U2{M}m zxqGNOQ7@2nb#?X3Pam(4-PLkFTW|mT$CYn}&QseH)w`?5=P&=b=aZ!$sL6>UOx}E? ze`{Z3VDQ4?q0HHi+qZJ!SMYAf&9*X!GiMjm)XTRaej`)8)PMSg+Ye_-nOl!u1k1Ru zZ-4*Z>EFQa{^vT4Q^$6_4{FQC<*#3o|8x3-qRjV+yf zhlH=c+dVen?|J(B1@P>hcfzX&hlV?U{WG}u`Ri{dmbV-{HVi&{Jo7>NE_-Fyjpr|2 zTL4D}sArCE_upE&xcB$dO&9!q{oi4KUt%P4Zs1<$BuIdp=XWj~{U~dZTtT@onO_<| Ga^gRXW|=zx literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_17.png b/assets/dolphin/external/L2_Dj_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..80863f0b6935ebcdc353deb69a6b5cf4d0c4d4ad GIT binary patch literal 1292 zcmaJ>Z)n_P7|*p`cWu3O-I%%|8ZwZ%HJ9W~E|~=*oE27&jfM8X;EgN@(wi zDFRHO&Nw4+p4zXX5s%dHq>@&r(Imqp;?XFbh{af*PK4M&fo7Z7lEZ|nc-Xltt%@l=YD+WXAJXoq~g(Bb#dwD4_l7W{^N3W<Y-MP88j#+;%e)*wV`r+Q!^6&d*!PZ}nesb;Ey{|`4O@H^E zwHM}3|8eWz{nxu@dbl(Br3>c^cWWQC9SPidt0z+1Om{ACD`0t~x literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_18.png b/assets/dolphin/external/L2_Dj_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..b4527bc83306f43a5ee138258cee037f073fc276 GIT binary patch literal 1498 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 BgtY(w literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_20.png b/assets/dolphin/external/L2_Dj_128x64/frame_20.png new file mode 100644 index 0000000000000000000000000000000000000000..f63904f29b2f44112bcec74fe738a623b2d9f1c8 GIT binary patch literal 1698 zcmaJ?dr;GM91joWAtK1;K-?K3r>LY&)6k}cN=r+z&LYE3!P|s1O&^0cr46(KL#|9- zn};{2r`s5cn~F}y&BIgY1|lfp&f()w<_XGl^9eqVbHYiN0(Ji3Etll?`+f8Me7~RX z>r#`Eo;EXhZZH4P$O!qoHCl07u%@S#pxQdIFA(RpyXv>l2gnYFjEWxI?eeOyVxm#ChUsYy+_Q2 zzzG#5PXbMn%2H>5a@t0LA`SwR2*Lw}A`Xe52&P4Kls+Cr5tPqGQ1*s-sF;U|Q4E~A zAl9Qzrx#}`lvDoLlLRs{j8)9#Ivfs;gU_LD1};w|5_xQ(D9mcW_6-(>aKaY*0|#Jld{u=Hj=H0et{Rv=I{RiTNol=L6e1M%EG7=5{Ui6(V28& zQXs;$7%qefQY(TnK2HP_JP{64B!*%o<-AG$O6ysoE zoSGm_7LR1Hr%IDSD=3?(h|(!-v>BX8v)J?o3i<@T0M#N|7{x_G7!%?+tQF~SSj11@ z2?aQY;-ns$!s}j3-(;(@5;hF0myH-laa2f9d{~I^_%JTOP#7mg2#n!+oDdM0p3w3k zE=!T?>73U($tz;p)3bdw8|>lLbWs*|q-^Z$Sax10Vdrjo#!5{pySm-(@B12V0Dy0U zN+HuY|LDjLvUDJU^W5&shfb(Mc97N&&EKt1yG}dtkoa=q$2;L=4PA!F!QPzKvZ8Hc zrBR_SpW&_dKk+HQveD;pf6U9hM8~c31N5$es%9uKE;mrxxX~JhxoUm-$0K8BC?WaH z)pfaJbr(-ss~u~ul_U3BOmT+&y~w&!V_^I6`_WRVaZ{jlJg8}-czauB{Gzz#x^P27 z-8va<4hU=7bTs*MS{WV`8VVDQp}l98`YS@nfG-OL&Byx+D}LQPXWHXE@ag7Rzl&am zjXvFe?^MsK`m>8Gm9x_;G*tV-871A4S+Z}}3dkFW3huX8G+p?L7tyOpdlXa~o}Z}s z=9&A#7fU{#QPocYj=_0PvxevOJdGbZV%_&}=gI3ML62R&)y0{;Ys@30f9EwGuwS}+ zMRos@uG*sU)`~L>qQuXs9DPxGg_4ZT_jGno4_4gL?1T*uNv>&o=k{Rr+GRuF`2GEoX$eX;jmbqa)9i>YV9q z(A{mL>RW$~Uca<&%sUtm1R#hl@4Frf3s6%T<53TGiW?9wj})A>}Aon4&N`}L>w58Jsjn05N)j;9{8cm aKN6?|A`4=UbUyd|HB_nTio+?nu73e(FMSmN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_21.png b/assets/dolphin/external/L2_Dj_128x64/frame_21.png new file mode 100644 index 0000000000000000000000000000000000000000..076448fa940b42f0740c62feeb1943e00616371f GIT binary patch literal 1665 zcmaJ?c~BE)6kotbz=O_+tyqsGqP55-dnMVd1PBBKAs8W5v>j!$*+2-%#?4{^s6(mX zF)gOmv6Sji4=T0}W(4VBhoYk3RmXx>@MzFt$I&S`)3H>ebOXfthtr+i{l4$*`@Q#j z*X%Z3_L3>#Gs6J@n39pM$r08H;YkV&5#9r#JG8saj+#EE!4y`F8~(u_y3OkiUJ#Q8D|y>LltXSy2Q%Z_zaCoEPRm|%|?=z zD+vQmC?JY9C?Om}ln{j|35cO_6i4I+xe+!7?7RxE(WD>*f@so`)oBQls!^-q)RYuV z4yVenWCF$l*bJM4r))G6@NE`+k7HB*7fYsa48^mYo@I*zouDgZdDc};o2?Golrq|4H$3hT(oNJDS^EdU7Kl%Y}U zU40#^dbtj8a`W?%LyH%`6}ly&*VFx1gE{omDr;oyu{FcK{O{^Fb-JF$&zg8K7dr4+ z?~w=CS(C?*)wptKuE=0Xn&zI_b}E1Bg&0r5*LNmPmX~}PUNL1y=Sb7>y<1o8Y4jdw zZwI{t+h-?EjY?Yk!SuPF)76JR{zTM0i402I&`O?c{Az0W(ETd!jF?BcS1P;S-@NUT zw|O-ud+50|P~8crXXo}~y|vc>Ji+6%sFxQQYDJe*KRO238~!=9sJwO0a&JHNr24_H zf9hdKeX(DBrn+rw{q7)DO`oOcc{H+PZDZq=?CH@WXlH---V0|tkIZM~(+^a(INuw( zT4OqFQk|cX*MerlNwcD^H)tO04-2liX_40fK_NG-Md{Os%+j6hzpSY8w60E!o94c) zQ;GI%=^Nl6$i7s(j zdVXBY@C|y|hNy>;k>!u(HFk)Epp~_O|TUWV$e-xag2+53mE$H?f-@US&5NnTHw%hKG{ubHTrC$+!GWa?jx>IuR=61uGuuCUn-P(;VNI@mp(>qsi0_UI(@nS&LHE==?~46>Y3P-=bBIav+ZV(4UU$iMgUUA8-f& literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_22.png b/assets/dolphin/external/L2_Dj_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..8651f12f8bd91d684d5943af135697e9e7d700d0 GIT binary patch literal 1809 zcmaJ?dr;GM9FMfpg1~U&alT`4t}1DhG_+|2ZJ`AVR-x9T&KGH#N)OxAHcDYbD|!NR zDpSun>vN~>oKBs}eBcH?PQ|I9PCZbZcy@=6gQ@rcL6#~w|M1Ku`Tc(1d_Ujs=li;p zWll;94;>f^06@5Ig2uqDUhYu|dEDDCT&3rh0j$=Jr~4z zbXlxQgC@1dA9qTEa#_}?lt}XP^Tqj6G2@yeK@|!`zy^lFoCfSJaImBYcDUnuEof*r z<+3?h8{+^27D+QRpG|@|wr|6*JN5c^h8^ynWN~Secu1!N6(bV6JrLU)Z8vM6|EKXm zZMU(&NlOg0o0;#TxSCkwdcj=&KJF->$gxpQcG-5HL;Q7>>%!G7DnqweumoMw5aPD2Apct5Z=_t5K^FZAyw% zhG-L{$pj+p#p)bxmUK{buWuXYy9bMW6st^e(Im^bj0`ikw-YjR8J2P9GENY~#W*O^ zla$R7kc21&&+GSfnTT)$&9ek5!rR@V0tO-&XW=6~~MkwSkE++`stgsNULYjce zWdx29lojg1Ti#3G+g9ZyTo}$?HcA2`FgZy}VL5_IVM2yuFhMC07$>X*DI;+!X_i6~ zj-n*cIq!8+uZU~U!1mp2aEEu(MLW2Wa&fcMYI2R_<}NsMqEXAO-QC?g>bEp+YX!u9OJ&N0#opWU_y|KR&zITY9Ip_V0eMJ9@lr5#9y+=*fn)byNzk7lC0BorAn|VQRv$u&DmcOnOTTQ7J>G zhekI#=Dnzt+{!u@RNbtPSw2{-EV#in0n@Srpi{@2x@pMC8^Ydx{vsjItU&pdSwt-PK6 zS#91uzkh93W838TKX{SGFAjy2XD+JPiNxPi?ZGqDUD3RhoxbRnlB(asvjpeDc{lUj zv5%xHy?x|)qk^-W_*<2p^{;2$-gaQ!6ID$GPfH)q{0bT(hl$$bfOqq}+0Tj_59@Bl zjU9S4v>`9m{LA*Tk~ZfPzb;+?y6=^15A7P(rRI$glsD#HWVeqmsb4sALi%jlynp4V za<~K7N`<9QE9(ZzJC96RKl(Ud6*}uwwpg@c)ssK#D^c&&(jToVK{cst%vzNZeD3QV zg(0gJjm|w`l%o{tsyvoy!0K literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_23.png b/assets/dolphin/external/L2_Dj_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..d2d8e7e5144697f5932a8e920d673c29de730372 GIT binary patch literal 1775 zcmaJ?dsNeA6pz&j7MvoYMe!I5Dr}}r(om8}kw>Xisk~nfkECfT9ki)!kb=j7xD8aC z98sLQDNNBRzEBxc(BeT*!8zt@o6Z9WPKIoXZWA|;E=6$u;V~!4_kB0_ckl1MPS!>* znm<-JMF;@ESapOlmS6MvC)jx;|Gw+IQNu5uTzE1U$LhIk%EAC4I#$boY9qCbiDf8V z&hm>)AOMUkG{h%!$(l%#W{nccp(C*w%{&?a0_WMx6rI6vpq5!?Fe$_j>g;0Bpi_vG z{57z~9LA&@B62NET<)THIyZwRbmDnIV4#iU4Hy}Y0&T`jla;h7#DjK8e(w-V#o(X{ zm!S~9B$cd*2E$kj0}>J#qG1>T0|*HXqcEmLb&P%vh{C8$3ZwiDAt;GpB#MDUmzeix z(do%prE16@e^Q9kInGQ&$8HUlU(Y!KjvJNGSPn*<6nWcyXmKu$Y*ao$&TrBfHjW=pr z<8#c6G?uZlSr(eFiQan{%;)dzjvR_S8#2mb;0r@#Dp@+q$e1{_QX%HQNOT4rNy`ab zi{SwfMQaHNlOY5|Ap{OFG=^e`Tr1bXx?wwS!Yh?w2#%oss?ZP>f`ltWLg4VQFqs?< z_m_p@uxuEsHd#5!L^H#_4ZQCmEcR9`8D?Q9jr>kvr}V;Y2@c_baZ5VIKRGl@uKqQUAF;1(5F_0 z#M^Gxr>r+M!p_~#U+mk*WMy_Ve7>g&Giat+e_b9p2MucYtMLBx#}m6+AJq1@rWQOk zugaFBJfAwZcgvHgAIC+vA|ujMa-*JYTzZ~ZAIla6Kb=!QvD>uk;^awgBGI=y zi^lj=8;;$-=i)WWUN&X2M|EQLqWZ_joT1en=hE)~5*ZkBo-1j({CDYEdl+4qme*0T z#3$z7=DSgu3gp`8Sg%>v(!JWN?;SN-7d8Rg6VTcHZhldb_N0wBT*J!CPMmYn`%E0A zDxN9Ln6_)vchjA^Dy|7dWmZ9AZovpb+VsiAmFI&V4qR4~Qjbdd`_qMmU;CUYT~oE- z>B9lfTTQnHeiH52ay@u={o(O(Er*(wY8)yRt9<*uX`UeL$$58i?=f<8acYVxe~;>< zJLUQDRy*fXywp3dc%1+PQrWW}8RGC`*ApTRJH5Abstihw*}dKhtfTvd3=4=pY)nc0 zbH5oF?_1e56}W<}My>>Rl(vtAPqoz5_4$>TUea%~f5rqBc;%1T+1Q)rm7<^DRTs3U zB~Fkq-s8Bi8FM*UFI=ctIgpU_t}q?kR5Xz4CslQ!?&)|jU% z7oG+hl01P~J?Gr>>s=eT6-OT}c{aTYsF?rH{_2>(t?frwGc#{Y+t1aO^vnPT?r>V!jYxL0%?X@U^8Rn^mmX{4IxE}-zSoP& zy}Kp@Uo@%H%Upq-8oX9dT`bygb?JOJKhUeMBIE~~d+^P13EKtNfNrPXHwb!#`Q294 zX3{>PyiHJ78{^yDwk5C46+CILikyN1&rT&4v^vWVK*8kk;KaohZSQtPjOaX6a75s9 yvUy!}=USJNI%3t#4?5Nq2z<^=`zm1bJb^14=IVc=Yf@ysZb_a9ICv+}Yjl``*6ad%t(hu85DD z=IcGmo5$n%YGPG7Zq4N$1%D9t{*%8}%Pj${I)R-+g-D@v6k*I< z)JTQ%c!P?~`UEyXJ6%T776IYX5jZV24$b3*M>=f;nMSdIfl4u3lrIAu27#RVCJ7t^!3&j$E(~@qr%baqs*RG7)yTn2e=v86U z1*F#~F(Oj7_9MnysxI^*@bwYTNah zHcF_Y?6iX+xtf@Q2f$qZ-tWkz$gz>lV9Z=$h;$WAIxLix)u`kk_eEed8)c+eiW@Lo z0uiJ^3SlBd3K57DhbR(5F+^+-8)4&sop<5YF>shTDjJT7QpO;NTBTIN>gZ^Z7*>ag zqHtI=fYn&-EMX<70pDiMcONz?=Dk>1G(!?j!Fnh1W8~-1mR)~g>X^|Ll|zt2{D102!jX|aukKG z&Uveo21HzYy0&j-gFC#LF3QS{6vNF<2YSrO&7EibbiJBedwP0yUMy?p@!T(IR7$huUd5+gHGv&pc0Q~_ z;GiGxlt0|!@dZD>E{Li)c5jK!DxMTJ6S4V?_s~sv>9OJ{e>Ac&8C0xX>%R>C+S_yX z;a{`o#BWINf-hhWpC?Tlx2Fpt|0vd-xF4MuTiC2w*?c4CK$OkmxowLcx8G{s z;t_gSH@xA8RCvJ&~N&egMHju_DHv%X1{FDt3L!3`k~ZW^vJt^ZURI^F$C%=JVnrF6O6CW8lkV#RB7FVlJo~DCSj3Lp8D2hT#;niWp0_Yqz*n6dKDZ<) zY_kt~Rxy9a;=9MMOC}^g_7vrA_eyO*-0u4}PILtA4qvDqJgVcHyzTb9=y86ED)r~5 zM+~0Y)eU>ZW^XC_DJ-#dX3>PbUf(*9qLO{GrMuQAZK`YixTPuW$dG`%xw>>O{pj*Q z^CHiXxgPp!MUBHwU5OaAbL5aStBNm;E%@s|#(vD}@UG((a^<8rV(TF06p+O4DXc50 zuR_;OezvZ@w9XmG3n;$Y_Ce{Bk(t4d97{I0hh!hz?|(tko<4Q{kkp2~f%kWZOc~6p ziBWhQ8#-rb?1S64_~1-M)mYyf>)cj@S-{;uyFNc?GvzcO2XDW zzBPT3J+~>t{g>8?Imv{g^hB{k+2#{6-InQ@wblKGoF`agjBHf)fPRuSuZFr^EGztX z)_K1m6gQuZux0pM@l(8*IIUV(5_D|Gxbn+mtNAUv?=*RSXa8t{6T2J#q;(e%U(3{Y m9xdPYnpbYGA+s`o6VAJ76ODEz(@cAr<7@t&1M4ul8wm%fm%$l z9j#N_3|3pmTD-8eQ#%wD9kBw%dbKi&P8E@3)QY0@Xi>o{-Edg{aNOD5@B7}q-+RAz z&3>AlnHmxl83X_bNl#Pf@T-%5k^_AB_g?{D==fzer^)AXStD0P*%+WSuzChcH&ew- z4nrBLmz`h|0PtC5%FE~Sbr~ehnuU}{N9ZtHc{BhCNe(MTmoXfqXNpZ01$?Kq4Tekx z1^iB&4%JyzOo=J2#>V8#`vgYhxg}5JhMd6+<$)kVY{S*JB387z<%2CJ~_+e5o4t;1Z6rk|I%6Rh6(xB4lmFBC%X9_t?NNgx5gq)fSF&AQt<)DGO@G zPTNdY&cs?Ek3~w)R&WZKXFCyw*{ah$H*B#_CW}v-$U#{}Vj(Irn?13OYumXT=6@P5 z)wbtVTNzOfV`nRDG+z_ryeTlBzgIi*DDrH`ESrfh3{|dX=?XJr;nLL#nExU)m<%K> zl@ofLkRcSUmm|1DEJrA@oIn^F$8fP!FEyZsDLXI2YqV&bG)0ALQQYz)P*wmg_u^|bJz+@C7L1d^{f)G+1LkL=qA~<0rC@F;-DZK<1 z@f1a#&UvAerbK*udbZDJgFif`MLAU&dAg7tK02v>}lu&!24jj zTAAm#cxYpmZ6IERD|Hr$-+O z!L^tzpAQJ~bKAoMlS$Afu(kT(mydzaxQ<}Z0|I2A;9dw=9MaSoc4tY^YwfwA1_jd^#YL4}boG-Z53Ewv z6?KwVTKCmA;Fa_0f7Vm_d&@wU1Q;cKW4=XX2z9l4oyoWqVKk4^nKIy@g{Tg z(~~>>&irtaiTJIw?TqWjKXV6b%7u50+tGntEvI?`+R$2;8kTu!Z0m_?I9k~l3-5Sq p$L=z@*M-{Ao%15wAD*1Od@Y7yv0!TN{e&hCES_xAnX`@L&+UgogD zox^&B0RTE@4AErsE5|=6p+WrnL1>AdU!qy9ksZNUSug3LL8^r@(@=(;oIq#Oq$PjS zNjedLpgA^!ku~av5)@;XkO3Wu&+g>W03;^+oFtV?vyhpdU~?pipYJ~;hHRE3@#q9S zqIagz6Kz8ZT=a;7VFs!omr_~8$%CLoAHf^2(<}-3?0F71;Y$*~w@dK*fLJPq-m9>= zN#YNrjQUI{jd9VCN`k-?g2*7HNT<$vuQWuaZ!9ttg-E2K7U_!6j0>Z5W`(IzA$8-hM_!m+QDXMlEnNMiN$6iD1{0) zW4IC~DYFX3 zQZ`3G5+A72hoCjI%Ql&|q`Me9^ghjm?K2dt333H$M$9mZtCTRN#BtcHvf!{vo*+{y za16yMtGEqs`6PWGT9udZVL02_5IBmXN|KhtN<=1yaRr9LIHf{h47cK>g2b$(SuU3H z6s3XA`J|KDMSOb(wjXDMKYW}n+QE;Mi=UnGT^(io+=XNgHE8*@wY9bCTE$I%NaHdz zsRm!e&J`DyJwigY*IHZayv3Il{%M&{Uulm&m^l`@pYr?aDTkY;Ha~jqY3Wn=Ze6pb zk-p!-xje0<#nyCsP6TMU|8kJ?@#?e5x5icYe&T9d$iIdLKRL9QdsBKY*K@dh(YdJJ zhoXR_E@DFRz^1|7-WbW9lRO)VD!bkaW};NkyzobD{6~nuA=6(n+3^DG=yDRtxO7ug*Rn5A+zUe!9 zWGCM90-U0_7Hj}8->z(q_h((2C2v}66dH86pn84SK|&r}-%tkhQRjsV#F+w9g8$Ln zVauo90TEo0Kpm4L;?4+&vHjJN0!8=+a7Faz!dFE6cffvY|LwYw<3;ijT(4;>f+{;6 zsO~=58O!1O7J>YQveFvpv@>Q}T=d4!t&=zFk7)N_0#!jmZunXK&Ek#$j90)@9xOcZSN}Sdc#9?%Z_mYI*m$+hWi3Swj_(Vk^b8($WtJZwBL^+h;%w`UZ*w4iU5 yGFN08BYf;tn8$_B*jzjYJUzoFv|1P3+QkHP*F=sS+q}rY?<+$)OtV#On*JZ+TCjKk literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_27.png b/assets/dolphin/external/L2_Dj_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..39ddf46ab3bd195b3e75567a3f91021720c9e1c8 GIT binary patch literal 1759 zcmaJ?Yfuwc6ut?F6yJ_u#Ti)_P*gVAM@Tl&Ktd8El^|j&R7KfrHY7^2Fz> zAERxp7ObESZGB4_6_i?VtbzkoYIQ`bwQ3cgsN;yWDs+Qj{o%N?yZ7F+=R4;+ui4_X z)X4)v!b1Q61N6z-bbj^mk19BTf8PvVXW*9+oGz1_&RRGRMsYbcQl7 zTyTn+1VF$Fn=zBiG^8kK)-Iy_IwG&#$)f?7l;Cwz^n8YcOiYf=p@bjS*1?d?tb{Y- z45-1WVXU^~0v9vAAk|10%%|mMI3XUIVcP3)zdVoZdJ?RI}`-P&$0o%x@}JGI@$ zg-%AC&bZk;7tPni64?vp^Y?y7enp;*Vyer=7lz8!vUHxEad3LA66U{%%r>)vmdZ(! zgp?r^ZIUAr0+S;YCMOYwmf#XhYLc2!bFZCu;ggaQF||%jBqgeoFifXat5KasLr76w z9Fa((L@!qFaC4M{W_o?wc;7u({JmI(#>G$^>oT%zZf_@~Sy_&CTUjTBOGFYV#z4_F zhhH+zU!~VUYZ;diCt*D{4EarWMB?Pd|_X z(!kwm2liYT0v~BwvQ`t(mU2dLdw02U^|SUFq6%q5!o3^feTzR8Zhd$(y1lLi6K}B= z=D(EwF!$o3zN+w!E!%ep+M@-x@zzN4`dTxHJNEqWk-OW7$;P0Sv_^L4W0Jc{QPVBsFpFVzBq>H-D=J4n9;Jnl0}% z1NV!Q+g56vM$O{kF9Uq{hs67C6vnG|bQXZ!{rscSDsu8qQ?FWxczOczZUGoKjse-7sMnTQ0(YR0*4wk;U(q~Fg&DqM*;>`mg;ouFG z-oeo2*|{Kp9rZ%6aI8bCI?=JFI=XS@cG<5{;KzVr^=02?#f3i@ZEelKP+Rhi{zb<@ z`||NZ)%k>s5==OFO(GpqUk84PuU~X)VCAOcu6R`ivF=32g{l>w5Jm20rLW#_3;J?R z@|^Dn2`Yq8SVL#|y^pNzAT*%)j-JjAo0j#W2+XUyH*_L+YOMOKO9-m;!)r_TG{A7* zm_kGNa`lwuG3iel1M7F1moTxj!Y*x&R*?~Dd7HDPzjr)dy>oWMsf>oaTH@w~J-Ke5 zDSpSu&I~;@qkLxIFl^eM}o=7(WVhfH1qs)S1oQZV~t4wRhK}y*sGwL0eYk7f-q=zR5L@a+T0)#p!o_h07zoQc568m|kr)$qATxFuuuqCD z2t1>r*E^6|QdL|fT&ij^Oq(%O#4sGT(`FGPFj62SIrs!jV1(6z5#UB~g273KAmOA>3;ct8uMz$ z0?kp`;+JD;qb7oy1XrcNAb5;Xq2?#G|J;Ng~}pHkho2-Ntl$fGaugTEx}n2YxkD0UL5zhSr+q@mRfC?r_fqL zVb&DZ7m4Y7M3hs$72tagYr7|wDb-|NSG9nuHl#YCGNkHiETl$Zf;5xxYK|9`NK&#U zS*6*a-Lj@^l_jsHhT)ksGs-*^f`wKaAz%VZP_!K-?G%Lyv_zq_wGg-4D3YMWATo!S z?n>Wms{#oKBbs8vPy|KTdD)8EG2Dt$Hj+Rok;YJx3R1j{Cxg6TMJ#}#CD}Q5by7+M z+B3PmGaKM=XS(DF7%2_R&aayucYwJwR+a}mV4a?x?z`B18G_P|`rK?FKJw?AR|kgR zEU)vI=|1(NE$?}b-CA<_jbqMF3hK4JM_kK)8+z!`_OdIB;u&r0q4OVCyV$Y(E2j@; zO>Vos=EN>%r}LS#fwG=~!-F1Z`BY!DwZSwv(QJe#6l2ZA3&x%apYh5>=gFtq*R{QJ z(th9Ou>)z(_MI|Z|KjJI#0UPJx9iu`og;3HU7cL^K-U=ee%FHWe>d(u@p^Snq51o> z{foQWh_y#23MI%4)!Du^tlZv}(8q-wcdlu~aClr;Qd4Tmzi@wUhQZi-BE(ABxt9jt z^^Kliy?tlmZ2E!l=>ALtH}a{M>o=J8z`MPN7P958Gv^jE@Y5Lud)m8Jx^j-^I?5on zz2NV~8;pZzMsH;H8`uu$xyi4KuC-je+Li&|;@E>3a9}Mo-2GGCmYx-v`)aqc{U46s z-VF_}`2AX_pd(>2y?T+!%W|0>e7Lv&o9tz+t@Uqn)kiM;VE!!ERXz3Nru_$&P8v+{ zplijYkp-W3Bwlmor`^mhFUp(x=U8Ifq8IWi#v+u^#^ nn=?Fl^SeV|8BXJuyPeN5bB81L~h5FJCem|@tcg&F+lUB9?%E63=4vCBBTurrX&W%Szjpu)AcUV-Cd zcw=#lkid*v!c@jIalvdcx`o6z=SDPP)2PwR#kruExUh(hjWH8h1m6P=_Xpavec$)# z^ZcIQ^VJ@IW%=5yC$bO(S?k^As)B1AK4qDy@O>k*-v^gm!(C(S&_YIwr~^chwID#f zVX+QW0a0$?yC&~qq7P_%`h;Ej_MPcl5kpvxBq zJ?e6ZuX1^o{lSwRt2c}YZ?Uwrw3u6Hv!>TsNRHzY7!-v=4LsVa8e$ArqXkI>7l=x_ z5-}7_MH7f(P-`;m7-YK?MmXa0tpKahfq!YNsvQlq zMu4RXM71Vef;9;hB#mMI{@+nT5whW5&=pu1vC*YTO<|xKUY8w%U(B*1^OBWggA8lK zMJdSP3{7&lNOCL=B!*&0YtSkaauR1Xdy*yuht^4XnH`?8k3#iuh$GMTCYV=iZL^2kx!Hq zH6eLAQKhAzT|ifwf$Y(>FuIs#URi}gsF=1=K_ZA#EN8BA{^_LDD#DWhk7LI09$b5Gz_mCL{)F%mOJ|5}orv zCnZI&JrmoN*?@jSoDILzvsaM8iICpFO6#+L~7Zw)Yy*4zCAf^tlO9;e%{QcN? z_Gf73tLvs0zT0lymEOB^!zTlU+dq!&EghYa_Df@Zo1SlOyi|03bL-b3%;F`FqxhB75eX$TK?`u%+XpYvmPM`+G{c$OUKK=yUq zOFz-~>Cp*wE@OS kWlgiQ#)aE=FPFznh;lgdO7=+pfy6J->#lUY@2Kti2RHiax&QzG literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_3.png b/assets/dolphin/external/L2_Dj_128x64/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..40d1314c95fb07190f995925e69f77df558f8cbf GIT binary patch literal 1777 zcmaJ?dsNeA6ptX!!4ySyz*i{vnx;+D7Scc`l(rV7h)@;h)TU`F9ki)QfP#v}Q*{jR zdFIq1zQHL+Jvtd4buwS`wZTzgQykzFA7ew9=wLFtRH*Y0k2y)c@4LCbdw=(JQk0TB zEzm#A9{_;BM71K7TkYHv=j+M6+kDq)xFwWTrn703k6 zxlG0afae;MHl0n^Bw++)=Ho6MzRhgq&;SrS)n>(sOp*ol z%6R>x(lsey0!5Rcln+A$3=6?%DW8B5Sgc13q;U#}z=%iyBis!M5lkq?5HUD#@i>pP z!HA_QR0ICFlZ=V0wEt3n9Z)(UTHIID)~Q+w`w!m zJS!{%gg+iqwJ|0#kB#5G5 zr9>o0VUZh~Xkl2~LXd9XCeHT&7I`NYOQ1=drD!chWw|>cC4*uqCWEqqh?p-1Cu(rQ zWN}F*xvJD3w1T8fIix{FQ)cj0nlaN`C>SN8C`1qIAq161L*i%@h4fMb3Q0v0VRRHK zMo_}Y8^9ahNMC=eauO~KtD6mmA}A7#lOiY@7K$J=N{m1#A%!6^YDDoUTx`VkBA$Sw zC~$So8=d49aqa2azMc*4@OrvP3pY|UH#>C0@?dW6yi=02N^W&Jo%?R>_!R&=IuaG} zTHEbE<_%hVAND=xbap>JwCSSv#`WJY*yHNSr8x~ruy?@aZU4qjty|%2&41}Ps)ZTS z&~SME%!2JJ+GanU&~aKNubvfj*2q3x(4}Lv!`8HnpS+}FOw~qo3sp?4yxSN6+UJI6 z72g=TXIoO>V`^=^v)M|&)ZeWeyY$Ph_Jp9F+a8cbS9pEB^t2=Ifm>A4AL0V{EP1mIw>30iOq)uLK>okRiy>lBR_MbYy7I(x7p2uiCOf!87(} zk5z|#q>DSg_b(_iyk=9Yj@Nvp&^OL;0t-6rr`t2MQ@y$I`QhH~oe^hz07hD{rf}=> z9+CXV+XsOP-|`fDw)Y&gb_S5;p(o|;L)Y>I~$)hhJ)JV4SL__$2QpOs=CTkLiU2}Zy^Ck6b=tZFpw6s>+jai0+s<# z^N0?}R1^f9de$R(`qlBW62E&tLqLxVj2K8B>;-$hl`ZNR@@)IWI8|WPqcW(p?quk`u<+*0)$>P* ziZ*rVhj^E4+BFC zJF#+Jeazt5`P9_F!lmPGhd8q@gs07T@cjKlF_)j!%eOkN?RIpXE*gHe_u|cez*Vto z|AtCQL*c}l9bcn2gFoJKY{Vd6AO4iJeTzH-fQAKrUcxWfcGpiRQJJhbB-gF{58GOv ANdN!< literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_30.png b/assets/dolphin/external/L2_Dj_128x64/frame_30.png new file mode 100644 index 0000000000000000000000000000000000000000..27c297e8d59c983d0a06ce9afafdc39064216039 GIT binary patch literal 1408 zcmaJ>YfKzf6rP0z)~)4H)TFJ3af+4#v-4zkX37ft;Mp#$+cgr4hIuVaV0YHpp}TC` zWUFb~{;1#&joKz9n$}lR5lz%I^00|sYcrp@Gh{}AEc8zbMHO( zeCK@U+;irLx1naed6OA}p!IdNZXcKv;H@;JgYPeQznpE;XP>c}<2Kk}Ak> zU65~;eKId~z4(n>4ngUM6n~T6*iKh9X;17Tj_~ z)ReHUs3ACMkr&iX-HrgZ%V7k=9?z=bP-G=pAZ=KT4`YM{$AZCRY)je^-6#J~V@>Ud zzbhd3k*Wl5o2sjkHZ=^Bw1tL?JiMrcl9Fx7 zTUrjI}Ii=#9XV0bG}2Y7)(FhCJYR?a<@loEmR zOfFY@18i2iOAdjS(m?M#)c2JN`YzME(_aPV#l^*wmrq=QAjAGTx5FR%@%PJrWWS7? zGR7|49sFy*wFU^`;UMVO|*cWakyoStc6*)cm!&`@H_nlwp zF*0}a)EyHKOdH1AzQu0*-9MH0ld-SA$%!u=fd-^IjZyKO4L24ZYPkN0@rcQlQp|;`e;wT z!GC)6s?uWge(8EM@qK>%lf7TuY&N5b(0pO#_Qxp0+f&D1r{rP@#gJW*+n|L}axNu4OzVvwe9JU;vjA4Jq zh#}s1JneAzVjwa1dMUhMYAG5>&wUl@9Nq!_S~Bo(?&b%_kdQPJ~{u%69q#* e7k@BOlQ2NG;zy>fj4vzsPpqqIaG!Rzy!0Pj5bA>f literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_31.png b/assets/dolphin/external/L2_Dj_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..f2aefbfae2f1ed51e8e87c69f47d42b6d57b6a7b GIT binary patch literal 1404 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 UNHl%vIsb2!jCaRAjU1c&4=x3@iU0rr literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_34.png b/assets/dolphin/external/L2_Dj_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..81f133ac5592569c6963b813e7eee54087a5195b GIT binary patch literal 1341 zcmaJ>YfKzf6dr^uO>wc>#DuoN>0qKnXXm-QGZVJmC+%vNlI_M4k(7DdWk;AtX9jn` zqy_t<`k(ow@g( zd%knNbM85FsIg(2ckPC?2!eQ%+v90C_rN#2rWAfJujxy{sme(-IlGLklNT+3L=-~; zXi^i~KpKcj*WPbI9fFh|R5MLZQ)-7G8=6ln==faCglGh*+v=L4+zuR60&S`u!e+<5 zz))2QVY~e)GG)d_+=< zkCu`Z(((Fbe{dJVa*ksP1d-3@eR;-bSZxHw^L)VuP2*4lx4U#lbaCCTDO!jFTeeiw zQ4Jj}SQI6r(+Oda?NS(;nM$n~*6rnF!L$*sXcCld| zWhfpODW1cD%+f3skOB&+6z!~rrM|($R-x}@Y~+DhA!Y&5F|3SXbQCM0F=sf2oij|7W_>LBbV`&}y&$PA+|p9eabT&h z0HxkCG;}e|g1QQYte*+c5-H&{#|LpX$Z@#DD;&-)_nFO2{78XwZa_kaP&;`s z^86 zSUq@pTkhoJga3XP9cY=_GmpMM7uzXC{=`}2Ir8(V-PEWZzWVV$rB99_?|#t3jSb&D zG1W6sO5ahlsJpK9YyZ!`hdtM;-btp$kBDg4~+HYXme{t=gri$nls2pT3LVj*o_N?A6hce5Fd%Q GzV;v5Zqt+i literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_35.png b/assets/dolphin/external/L2_Dj_128x64/frame_35.png new file mode 100644 index 0000000000000000000000000000000000000000..c828207d33ed6ec94a71c904597dd24b4805b3b3 GIT binary patch literal 1255 zcmaJ=U1%It6dt>whW_M5Y@;ZQ(+X8PGk1P=cV?2!-_E94%%;m`!T1t-XXftiG?|~y zOuCblYMP4L2dhGbRV)#;STGjEKd=xj6j5KaNeZtO$oFf;eg zne%<;JLjHrB0scm`<92d5CpM3JD?WuI*p&6));Tnc@?~m%q&H& zx&&h>YE9HgE>ET%50NqhXdM8KOv;Q7cpz%Lfl7Nx9`FJSczn|wuW+Kmi)7=Xa7522 zDFwB^5f6W*s7esH3d>fjRi-L1j#p+mS(YOXo~N+~?N8bP3~Afn)nrhSuY0B&n2t?G zj8JnXf)s_-uBBnQx!gLj?Kg^r%f^P#WjO}0mKEi;>g@*w^uLS^z5U{(i`WA4oe59J zXHweL1mpVMJ5c0^)hPQt6CVs5R~>!ALUxc?JlW9@lWEyg^ zL?d0~MJ}Nw3}7^QHo^-6P(`37`uq9-$7R&sUXV$rg#^gNg+2)gO>EZo18D208Qa9M z8`$_gu}a!QFmSx07njEsKIvB=dj zE9M3gN^v2t}wNUBbI7S+%zqE-u$Dv>gZA(l_taKlmb6d2eFRBM&489_+hz z;oRS%y1e+=hxU!5->~+!w`-g+9qTS^J^3%!Rd4_0{H@X1+h;c|-u(Kt>8>Yhm#FZN zPIPSkw4VL>^Sw*Z`U@RseQS$A5P8p$J+jU16A`K z2Y(-ZyuRG}QEjL#{A=d>(F2!fj?A69zV+hL^Eb*X?~Zl9v-{2p^RoC@aI~eJIOg8x U{4;a%BF>k{W`@*reFtX#193vGHUIzs literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_36.png b/assets/dolphin/external/L2_Dj_128x64/frame_36.png new file mode 100644 index 0000000000000000000000000000000000000000..fc923b40236dee851d9c3b1f60a64d0050b67350 GIT binary patch literal 1059 zcmaJ=&2G~`5O!4wRh2k$fYZvA_>ow9y>^nd)zBt!N+Z=JN+S_*Vr{P*)A|Q{OWdAN zC4@Nf25{mE55NI9@&pJZj@*#?2w|PXrH863+dDI!Z~o^^fA`__)my8QBwg=q+XHdG z6z}HBviSa4dFzPlChv~;UJ~#L&6w2k6PJNrMEBW%QUCb)ceXA`%df-Xh>x5d%S$4K zmO4rir2;KU>l;N%y#vO9%l5;#DW84*B7@Lx%8zRfa?%bPhubF^+dJ7EdM5{->B}1r zzng(Hff}|n!@>qw99ekG{Jv{)cg?c0N2#nL1#3a+G^0qFPSQg-NPJVmwRod^ZGh zzFjO9E0u}ed3UFHBKGcDe`nYg_jEe_@bmp|L4ls#8Wz9)ep^}++$orTinljQvOH(E Yze-?JTKaM0*{hQ8b$9KL?Wd>z0Hr%cDgXcg literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_4.png b/assets/dolphin/external/L2_Dj_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..d372ff643b40c4cf27bc810fb25a3ed239535b5d GIT binary patch literal 1727 zcmaJ?c~BE)6kiBRhRdT$MX|0y8B{jeY(lcTLx4aMi4uws6;RO4W&<_xrxL@AuyCU9;nM`l^5xU6V3nUzLjT25#P7;MKG0|tsFK)WH`XvOUk&Y)eK**nBM4mhYn zr%548t5Y5Cmd0AZwNjFRMp;RV z-c0LFM$loA(3mo435Q`j6o$d9QoS*3v<@eWNgK~jn0b6I%rh7qu?=cl=~(K28t>G$ zsx!?LFP5^JGAtxh6P@1(n91M!9XS*kHu%RDJyRGWU1lOP43v>p$|M}-3s3WwtkP(u2_s33_|`MNhq0pfV(|zIMbIXT+GI)}>4cb66K%4ln#>>~m4iCG2GgPtCJ5q- zMW_%#NgZbxuYD_hL#@h4m@v#EY;Y7o5ivmtATi7rK&VKFKq!g9kPy|OgoqI82#tWl zV<_?*o%2>Fjfj}`bZpYwXXb8nOq5#AtOElB`~N6w0|4h! zN|{t`Z#k9V9^C+Y6g^v|cS*1A&?cSPF?M`c%kkols=C1J<@3kDFJ4_`ClV*XJO8+! zox!f^_vyi+_Sly-^|^R=-`s(FaNBrO*MH{L9GK+s-Q$JN>Smw6rTXpA*nNQ;8eA2X z9w*z$n~!S!{wE~ffz64j7pBc;6$59x;t87FbTPD|9L4h&xE59jLtLzJo4L-T0dTU< z-<`VZ2eW{r2_bi@XY}TG^pqRQI@~Ebj>uyfq2m5tBmo(HY z__3&Mpdz+)c+p zEi2yNr+jBFH==i9Nqyr1<@Ur!KF8FpE8{#%XU_1cJ&<0os!v`}=~>dus{scZn=&U{ z-G6S>QO@NJe`?}a#Y#JW)$b02JK8(8*vgiEOAD?=`ezsHE4nxPNQ}QUVaB8l;+-=KlMOI!zDSddDuouQvmIZjsBsC!Fjr4Os1%)+;r1jmjZSE;h9VFzIpQee$VfD z-sjnnVVoB_ar#6607CWY+AMjkke}q>0Qub?ywxBt(ULA#$`&nB3G3tmjafADpx(yj z^I1G=E-gCCCjmgfIw2=l$~7!tIMJqHy*>)J%^{-!AZf1K!E(!a2{iHff?W;WKY9WJ z1+yAjlxRQ@s5xSGJs^&CuLAV3M1W32eN?f^J)(-Nm@o&l8F$N_Vi~z?j zNRH?<)3RUk^6&70eZw@b?6|CjMn zZ&yyKgI8wpF0t6j$!B7T@qy*~y*iNBQC7pe?-b;NVGFe)S8U_$l3uHZf~8TbmoX+p|2A%tfC~ktfB+N2n7MYV_-SK z?zO~uPiZu1E$mEO*I zp_6UZqZfQKqt4>>;xjM z^8PHOwjt_zU~pS+L{S8m^zfUe#+G2q@4#fs@xV*_1Uz{|?YxsM->)fCLNVHs=M&a@PPedfEmY;l0M93#=g}bGU*!Tk%Yz zPkH-Z==ck+4_@og4?2$*o4Xe&4sOhnxVNG!>Tb1EDx=nS3GrlSf`5CEc~4!{hr^kX zU^x9pdxWr&udCkP&l&a|>i7FAB~toiO5v_W?wT5O>-PFzvFL)vnEJACzn->bHMzSj zJMlhE*tOFsD_d=Rxv8Y=*ur$n-U-=@&)0Jgn;G`AM@#g1GnzEFTS_m*9y{e9IDJQE z@wu*B$H>_quhq7MZB4Rd85gd9FC=i+uDUq#>mTz&_I2%Eqe?%tEpE!Y2T!&>uoI&hrEhO+t@MJ8-10H(1?aH75N0`nbEpTRxhfH&ZWW7d2gq>8$F!Z2WfVx#EJR z1=R<-kakt;o3lyd{!Kf|Gp6pn_T}wl=5XSpye9pUwJh&n9kBgWwI{X%Xd4dJ?Hpg* QB=;Vm*BQ0VDS4~^1HhwTkpKVy literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_6.png b/assets/dolphin/external/L2_Dj_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1e84a11eeb499fcd47e7709e166c714229afe5 GIT binary patch literal 1635 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 a;?v;Bx|Au6L*e|`zuj&tqdJRfTmA#Jq%n&C literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_9.png b/assets/dolphin/external/L2_Dj_128x64/frame_9.png new file mode 100644 index 0000000000000000000000000000000000000000..05de5d5c698a86baccd74cfe33d1109ee566f793 GIT binary patch literal 1610 zcmaJ?eNfY87*FY-RYd$4;Mce;$Tm0ACTVDsj7keF*o^{*I5_7`lQe}4+JrQW!qls% zb4>51c<1~=xedSADd@J-`89URhTeJNoJ=VSH#ZzR*%XIt?mU(Pb^hU*OY*+&ljrw5 zzvt^xo0pRnGjY~L003ef3vKzz8c-gKR;|2$)qd<$mYK4>NM0nk9l8c<#6f1aibVx_%_lOD_08-QZBF&a^GRSbnyw?oD#SE^5Usx@1i>h-9*y>W2bU|qgz*Ik_81Qg14f;K0ULWN*L!)*nWgimj zA#hYhE;U1Aq>7w*V1^)ZAgM!O7C}(ZMCw=sLvRLjaqjsbhF}Igf+;tQVibx~7!HnK zkm6Btxv6|x=D0uQWQIy)S)}y(%F0Szr9mf1#d?$^$&d{UgB1q%nM#HWRYfs3fT-PY{$azh)(BY!(QKbvXr#x{j{h@b%@^M3B@+5?UVDl|7ko` z+gDH}a{7GECsatRQWJMl7_8*)*^WYr3L7d{;+4YCWj2AW@Niz)VKYO@7oCfDQLK?9 z7@RP{G|P}MZa_(xMo9wZSRBJqBV%+SuCSe_;W3k)w51!xRbht|!tr)~bp`2}2CCp$Lq?Of+YJO$cg$2_ufd1WO_?PPhr$NaJprF+h5SqCV6) zPjpgPq_k&f`*=2#!{h1Vyvj&P%ItLP`YBGCyQsYE0=u$~jEwBPzUKx2L}WQ^)&hUm zo$YT+XA!Oa>wkOX;<~QBn7V_(e%mts)!T`xh`H12)j!U;=6Bz#Oan$@fu38(fL6=y zNHIRq(5=b)VDjF(NkH}=L-&SmJLV1qs({niGM?YH=EceC#=$8`Q-hj7{gn3o&jo?E z(zd2eh*B{t|4ynq`_6-?nxLh{r!^MdY zC$;gLk^YF851H$lZ!L#-^{JECRQ0OaSb|!JnRBA?=;}?wvpR!l3lKP7f3iO!5`2)@ z_=UCtys5c+DeGLeba8gAs{MdZb@N$M@Qcm}vksJ!osZl6;O; zqZVUKS5pJ1I`6?&2&DzA+N}en>UA zv$^-BzOD#!)VsT4T$(-3&eWm3`4IgLP`WPP Date: Wed, 7 Jun 2023 21:21:55 +0100 Subject: [PATCH 086/370] Typo --- assets/dolphin/external/manifest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index d3f026b73..24dd8ab9d 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -102,7 +102,7 @@ Weight: 4 Name: L2_Wake_up_128x64 Min butthurt: 0 Max butthurt: 14 -Min level: 11 +Min level: 12 Max level: 30 Weight: 3 From 9af3c22a6a3acef2652ced06b3646a928cad9f9e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:39:52 +0300 Subject: [PATCH 087/370] Fix ProtoView issue #503 --- applications/external/protoview/signal_file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/external/protoview/signal_file.c b/applications/external/protoview/signal_file.c index c60a6a181..886573a06 100644 --- a/applications/external/protoview/signal_file.c +++ b/applications/external/protoview/signal_file.c @@ -48,8 +48,9 @@ bool save_signal(ProtoViewApp* app, const char* filename) { for(int j = 0; regs[j]; j += 2) { furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]); } - size_t len = furi_string_size(file_content); - furi_string_set_char(custom, len - 1, '\n'); + //size_t len = furi_string_size(file_content); + //furi_string_set_char(custom, len - 1, '\n'); + furi_string_cat(custom, "\n"); furi_string_cat(file_content, custom); furi_string_free(custom); } From 87f70655a2817acc99552d01915827aaa0f8931b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:54:01 +0300 Subject: [PATCH 088/370] Remove broken modulation that was causing buffer Overrun Fixes issue #506 --- applications/main/subghz/subghz.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index d30bd79f2..97e2e8c34 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -211,7 +211,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { flipper_format_free(temp_fm_preset3); - // # HND - FM presets + // # HND - FM preset FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); flipper_format_write_string_cstr( temp_fm_preset4, @@ -221,16 +221,6 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz_setting_load_custom_preset(setting, (const char*)"HND_1", temp_fm_preset4); flipper_format_free(temp_fm_preset4); - - FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset5, - (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset5); - subghz_setting_load_custom_preset(setting, (const char*)"HND_2", temp_fm_preset5); - - flipper_format_free(temp_fm_preset5); } // custom presets loading - end From af2ecbc3ed35a1e6b52af7a5b07decd03a2a5186 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 00:26:10 +0300 Subject: [PATCH 089/370] SCD30 Unitemp --- applications/external/unitemp/Sensors.c | 3 +- applications/external/unitemp/Sensors.h | 5 + .../external/unitemp/assets/co2_11x14.png | Bin 0 -> 176 bytes applications/external/unitemp/sensors/SCD30.c | 438 ++++++++++++++++++ applications/external/unitemp/sensors/SCD30.h | 59 +++ .../external/unitemp/views/General_view.c | 38 ++ 6 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 applications/external/unitemp/assets/co2_11x14.png create mode 100644 applications/external/unitemp/sensors/SCD30.c create mode 100644 applications/external/unitemp/sensors/SCD30.h diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c index 666438bfa..ae90ce4d5 100644 --- a/applications/external/unitemp/Sensors.c +++ b/applications/external/unitemp/Sensors.c @@ -78,7 +78,8 @@ const Interface SPI = { static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT21, &DHT22, &Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10, &SHT30, &GXHT30, &LM75, &HDC1080, &BMP180, - &BMP280, &BME280, &BME680, &MAX31855, &MAX6675}; + &BMP280, &BME280, &BME680, &MAX31855, &MAX6675, + &SCD30}; const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) { if(index > SENSOR_TYPES_COUNT) return NULL; diff --git a/applications/external/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h index d2b7c07af..25b9cb49e 100644 --- a/applications/external/unitemp/Sensors.h +++ b/applications/external/unitemp/Sensors.h @@ -24,6 +24,7 @@ #define UT_TEMPERATURE 0b00000001 #define UT_HUMIDITY 0b00000010 #define UT_PRESSURE 0b00000100 +#define UT_CO2 0b00001000 //Статусы опроса датчика typedef enum { @@ -31,6 +32,7 @@ typedef enum { UT_DATA_TYPE_TEMP_HUM = UT_TEMPERATURE | UT_HUMIDITY, UT_DATA_TYPE_TEMP_PRESS = UT_TEMPERATURE | UT_PRESSURE, UT_DATA_TYPE_TEMP_HUM_PRESS = UT_TEMPERATURE | UT_HUMIDITY | UT_PRESSURE, + UT_DATA_TYPE_TEMP_HUM_CO2 = UT_TEMPERATURE | UT_HUMIDITY | UT_CO2, } SensorDataType; //Типы возвращаемых данных @@ -121,6 +123,8 @@ typedef struct Sensor { float hum; //Атмосферное давление float pressure; + // Концентрация CO2 + float co2; //Тип датчика const SensorType* type; //Статус последнего опроса датчика @@ -329,4 +333,5 @@ const GPIO* #include "./sensors/HDC1080.h" #include "./sensors/MAX31855.h" #include "./sensors/MAX6675.h" +#include "./sensors/SCD30.h" #endif diff --git a/applications/external/unitemp/assets/co2_11x14.png b/applications/external/unitemp/assets/co2_11x14.png new file mode 100644 index 0000000000000000000000000000000000000000..2a2b5e068d6153d1f30dfc93acd3b0279ee8d551 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2?f#ZI0f96(URk}M!fC3NGbie=qQ>#|?bzTh2Q%vH}y3IC$ZRvemx8YG!hReRKhg7T% zs+8bg=d#Wzp$PyIY(cC5 literal 0 HcmV?d00001 diff --git a/applications/external/unitemp/sensors/SCD30.c b/applications/external/unitemp/sensors/SCD30.c new file mode 100644 index 000000000..b5f15b50d --- /dev/null +++ b/applications/external/unitemp/sensors/SCD30.c @@ -0,0 +1,438 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library + +#include "SCD30.h" +#include "../interfaces/I2CSensor.h" +//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h> + +inline static uint16_t load16(uint8_t* b) { + uint16_t x; + memcpy(&x, b, 2); + return x; +} + +inline static uint32_t load32(uint8_t* b) { + uint32_t x; + memcpy(&x, b, 4); + return x; +} + +inline static void store16(uint8_t* b, uint16_t i) { + memcpy(b, &i, 2); +} + +inline static void store32(uint8_t* b, uint32_t i) { + memcpy(b, &i, 4); +} + +#if BYTE_ORDER == BIG_ENDIAN +#define htobe16(x) (x) +#define htobe32(x) (x) +#define htole16(x) __builtin_bswap16(x) +#define htole32(x) __builtin_bswap32(x) +#define be16toh(x) (x) +#define be32toh(x) (x) +#define le16toh(x) __builtin_bswap16(x) +#define le32toh(x) __builtin_bswap32(x) +#elif BYTE_ORDER == LITTLE_ENDIAN +#define htobe16(x) __builtin_bswap16(x) +#define htobe32(x) __builtin_bswap32(x) +#define htole16(x) (x) +#define htole32(x) (x) +#define be16toh(x) __builtin_bswap16(x) +#define be32toh(x) __builtin_bswap32(x) +#define le16toh(x) (x) +#define le32toh(x) (x) +#else +#error "What kind of system is this?" +#endif + +#define load16_le(b) (le16toh(load16(b))) +#define load32_le(b) (le32toh(load32(b))) +#define store16_le(b, i) (store16(b, htole16(i))) +#define store32_le(b, i) (store32(b, htole32(i))) + +#define load16_be(b) (be16toh(load16(b))) +#define load32_be(b) (be32toh(load32(b))) +#define store16_be(b, i) (store16(b, htobe16(i))) +#define store32_be(b, i) (store32(b, htobe32(i))) + +typedef union { + uint16_t array16[2]; + uint8_t array8[4]; + float value; +} ByteToFl; + +bool unitemp_SCD30_alloc(Sensor* sensor, char* args); +bool unitemp_SCD30_init(Sensor* sensor); +bool unitemp_SCD30_deinit(Sensor* sensor); +UnitempStatus unitemp_SCD30_update(Sensor* sensor); +bool unitemp_SCD30_free(Sensor* sensor); + +const SensorType SCD30 = { + .typename = "SCD30", + .interface = &I2C, + .datatype = UT_DATA_TYPE_TEMP_HUM_CO2, + .pollingInterval = 2000, + .allocator = unitemp_SCD30_alloc, + .mem_releaser = unitemp_SCD30_free, + .initializer = unitemp_SCD30_init, + .deinitializer = unitemp_SCD30_deinit, + .updater = unitemp_SCD30_update}; + +#define SCD30_ID 0x61 + +#define COMMAND_CONTINUOUS_MEASUREMENT 0x0010 +#define COMMAND_SET_MEASUREMENT_INTERVAL 0x4600 +#define COMMAND_GET_DATA_READY 0x0202 +#define COMMAND_READ_MEASUREMENT 0x0300 +#define COMMAND_AUTOMATIC_SELF_CALIBRATION 0x5306 +#define COMMAND_SET_FORCED_RECALIBRATION_FACTOR 0x5204 +#define COMMAND_SET_TEMPERATURE_OFFSET 0x5403 +#define COMMAND_SET_ALTITUDE_COMPENSATION 0x5102 +#define COMMAND_RESET 0xD304 // Soft reset +#define COMMAND_STOP_MEAS 0x0104 +#define COMMAND_READ_FW_VER 0xD100 + +static bool dataAvailable(Sensor* sensor) __attribute__((unused)); +static bool readMeasurement(Sensor* sensor) __attribute__((unused)); +static void reset(Sensor* sensor) __attribute__((unused)); + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused)); +static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused)); + +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused)); + +static bool setForcedRecalibrationFactor(Sensor* sensor, uint16_t concentration) + __attribute__((unused)); +static uint16_t getAltitudeCompensation(Sensor* sensor) __attribute__((unused)); +static bool setAltitudeCompensation(Sensor* sensor, uint16_t altitude) __attribute__((unused)); +static bool setAmbientPressure(Sensor* sensor, uint16_t pressure_mbar) __attribute__((unused)); + +static float getTemperatureOffset(Sensor* sensor) __attribute__((unused)); +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused)); + +static bool beginMeasuringWithSettings(Sensor* sensor, uint16_t pressureOffset) + __attribute__((unused)); +static bool beginMeasuring(Sensor* sensor) __attribute__((unused)); +static bool stopMeasurement(Sensor* sensor) __attribute__((unused)); + +static bool setMeasurementInterval(Sensor* sensor, uint16_t interval) __attribute__((unused)); +static uint16_t getMeasurementInterval(Sensor* sensor) __attribute__((unused)); + +bool unitemp_SCD30_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + i2c_sensor->minI2CAdr = SCD30_ID << 1; + i2c_sensor->maxI2CAdr = SCD30_ID << 1; + return true; +} + +bool unitemp_SCD30_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_SCD30_init(Sensor* sensor) { + if(beginMeasuring(sensor) == true) { // Start continuous measurements + setMeasurementInterval(sensor, SCD30.pollingInterval / 1000); + setAutoSelfCalibration(sensor, true); + setAmbientPressure(sensor, 0); + } else + return false; + + return true; +} + +bool unitemp_SCD30_deinit(Sensor* sensor) { + return stopMeasurement(sensor); +} + +UnitempStatus unitemp_SCD30_update(Sensor* sensor) { + readMeasurement(sensor); + return UT_SENSORSTATUS_OK; +} + +static uint8_t computeCRC8(uint8_t* message, uint8_t len) { + uint8_t crc = 0xFF; // Init with 0xFF + for(uint8_t x = 0; x < len; x++) { + crc ^= message[x]; // XOR-in the next input byte + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; // No output reflection +} + +// Sends a command along with arguments and CRC +static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) { + static const uint8_t cmdSize = 5; + + uint8_t bytes[cmdSize]; + uint8_t* pointer = bytes; + store16_be(pointer, command); + pointer += 2; + uint8_t* argPos = pointer; + store16_be(pointer, arguments); + pointer += 2; + *pointer = computeCRC8(argPos, pointer - argPos); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +// Sends just a command, no arguments, no CRC +static bool sendCommand(Sensor* sensor, uint16_t command) { + static const uint8_t cmdSize = 2; + + uint8_t bytes[cmdSize]; + store16_be(bytes, command); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) { + static const uint8_t regSize = 2; + + if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[regSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0; + + return load16_be(bytes); +} + +static bool loadWord(uint8_t* buff, uint16_t* val) { + uint16_t tmp = load16_be(buff); + uint8_t expectedCRC = computeCRC8(buff, 2); + if(buff[2] != expectedCRC) return false; + *val = tmp; + return true; +} + +static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) { + static const uint8_t respSize = 3; + + if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[respSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false; + + return loadWord(bytes, val); +} + +static bool loadFloat(uint8_t* buff, float* val) { + // ByteToFl tmp; + size_t cntr = 0; + uint8_t floatBuff[4]; + for(size_t i = 0; i < 2; i++) { + floatBuff[cntr++] = buff[0]; + floatBuff[cntr++] = buff[1]; + uint8_t expectedCRC = computeCRC8(buff, 2); + if(buff[2] != expectedCRC) return false; + buff += 3; + } + uint32_t tmpVal = load32_be(floatBuff); + *val = *(float*)&tmpVal; + return true; +} + +// Get 18 bytes from SCD30 +// Updates global variables with floats +// Returns true if success +static bool readMeasurement(Sensor* sensor) { + // Verify we have data from the sensor + if(!dataAvailable(sensor)) { + return false; + } + + if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) { + FURI_LOG_E(APP_NAME, "Sensor did not ACK"); + return false; // Sensor did not ACK + } + + float tempCO2 = 0; + float tempHumidity = 0; + float tempTemperature = 0; + + furi_delay_ms(3); + + static const uint8_t respSize = 18; + uint8_t buff[respSize]; + uint8_t* bytes = buff; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) { + FURI_LOG_E(APP_NAME, "Error while read measures"); + return false; + } + + bool error = false; + if(loadFloat(bytes, &tempCO2)) { + sensor->co2 = tempCO2; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing CO2"); + error = true; + } + + bytes += 6; + if(loadFloat(bytes, &tempTemperature)) { + sensor->temp = tempTemperature; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing temp"); + error = true; + } + + bytes += 6; + if(loadFloat(bytes, &tempHumidity)) { + sensor->hum = tempHumidity; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing humidity"); + error = true; + } + + return !error; +} + +static void reset(Sensor* sensor) { + sendCommand(sensor, COMMAND_RESET); +} + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) { + return sendCommandWithCRC( + sensor, COMMAND_AUTOMATIC_SELF_CALIBRATION, enable); // Activate continuous ASC +} + +// Get the current ASC setting +static bool getAutoSelfCalibration(Sensor* sensor) { + return 1 == readRegister(sensor, COMMAND_AUTOMATIC_SELF_CALIBRATION); +} + +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) { + return getSettingValue(sensor, COMMAND_READ_FW_VER, val); +} + +// Set the forced recalibration factor. See 1.3.7. +// The reference CO2 concentration has to be within the range 400 ppm ≤ cref(CO2) ≤ 2000 ppm. +static bool setForcedRecalibrationFactor(Sensor* sensor, uint16_t concentration) { + if(concentration < 400 || concentration > 2000) { + return false; // Error check. + } + return sendCommandWithCRC(sensor, COMMAND_SET_FORCED_RECALIBRATION_FACTOR, concentration); +} + +// Get the temperature offset. See 1.3.8. +static float getTemperatureOffset(Sensor* sensor) { + union { + int16_t signed16; + uint16_t unsigned16; + } signedUnsigned; // Avoid any ambiguity casting int16_t to uint16_t + signedUnsigned.unsigned16 = readRegister(sensor, COMMAND_SET_TEMPERATURE_OFFSET); + + return ((float)signedUnsigned.signed16) / 100.0; +} + +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) { + // Temp offset is only positive. See: https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library/issues/27#issuecomment-971986826 + //"The SCD30 offset temperature is obtained by subtracting the reference temperature from the SCD30 output temperature" + // https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/9.5_CO2/Sensirion_CO2_Sensors_SCD30_Low_Power_Mode.pdf + + if(tempOffset < 0.0) return false; + + uint16_t value = tempOffset * 100; + + return sendCommandWithCRC(sensor, COMMAND_SET_TEMPERATURE_OFFSET, value); +} + +// Get the altitude compenstation. See 1.3.9. +static uint16_t getAltitudeCompensation(Sensor* sensor) { + return readRegister(sensor, COMMAND_SET_ALTITUDE_COMPENSATION); +} + +// Set the altitude compenstation. See 1.3.9. +static bool setAltitudeCompensation(Sensor* sensor, uint16_t altitude) { + return sendCommandWithCRC(sensor, COMMAND_SET_ALTITUDE_COMPENSATION, altitude); +} + +// Set the pressure compenstation. This is passed during measurement startup. +// mbar can be 700 to 1200 +static bool setAmbientPressure(Sensor* sensor, uint16_t pressure_mbar) { + if(pressure_mbar != 0 || pressure_mbar < 700 || pressure_mbar > 1200) { + return false; + } + return sendCommandWithCRC(sensor, COMMAND_CONTINUOUS_MEASUREMENT, pressure_mbar); +} + +// Begins continuous measurements +// Continuous measurement status is saved in non-volatile memory. When the sensor +// is powered down while continuous measurement mode is active SCD30 will measure +// continuously after repowering without sending the measurement command. +// Returns true if successful +static bool beginMeasuringWithSettings(Sensor* sensor, uint16_t pressureOffset) { + return sendCommandWithCRC(sensor, COMMAND_CONTINUOUS_MEASUREMENT, pressureOffset); +} + +// Overload - no pressureOffset +static bool beginMeasuring(Sensor* sensor) { + return beginMeasuringWithSettings(sensor, 0); +} + +// Stop continuous measurement +static bool stopMeasurement(Sensor* sensor) { + return sendCommand(sensor, COMMAND_STOP_MEAS); +} + +// Sets interval between measurements +// 2 seconds to 1800 seconds (30 minutes) +static bool setMeasurementInterval(Sensor* sensor, uint16_t interval) { + if(interval < 2 || interval > 1800) return false; + if(!sendCommandWithCRC(sensor, COMMAND_SET_MEASUREMENT_INTERVAL, interval)) return false; + uint16_t verInterval = readRegister(sensor, COMMAND_SET_MEASUREMENT_INTERVAL); + if(verInterval != interval) { + FURI_LOG_E(APP_NAME, "Measure interval wrong! Val: %02x", verInterval); + return false; + } + return true; +} + +// Gets interval between measurements +// 2 seconds to 1800 seconds (30 minutes) +static uint16_t getMeasurementInterval(Sensor* sensor) { + uint16_t interval = 0; + getSettingValue(sensor, COMMAND_SET_MEASUREMENT_INTERVAL, &interval); + return interval; +} + +// Returns true when data is available +static bool dataAvailable(Sensor* sensor) { + return 1 == readRegister(sensor, COMMAND_GET_DATA_READY); +} \ No newline at end of file diff --git a/applications/external/unitemp/sensors/SCD30.h b/applications/external/unitemp/sensors/SCD30.h new file mode 100644 index 000000000..1ebfbb411 --- /dev/null +++ b/applications/external/unitemp/sensors/SCD30.h @@ -0,0 +1,59 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_SCD30 +#define UNITEMP_SCD30 + +#include "../unitemp.h" +#include "../Sensors.h" + +extern const SensorType SCD30; +/** + * @brief Выделение памяти и установка начальных значений датчика SCD30 + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_SCD30_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика SCD30 + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_SCD30_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD30_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * @param sensor Указатель на датчик + * @return Статус опроса датчика + */ +UnitempStatus unitemp_SCD30_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD30_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c index 2c8d389bf..dd045d336 100644 --- a/applications/external/unitemp/views/General_view.c +++ b/applications/external/unitemp/views/General_view.c @@ -159,7 +159,34 @@ static void _draw_pressure(Canvas* canvas, Sensor* sensor) { canvas_draw_str(canvas, x + 52, y + 13, "kPa"); } else if(app->settings.pressure_unit == UT_PRESSURE_HPA) { canvas_draw_str(canvas, x + 67, y + 13, "hPa"); + +static void _draw_co2(Canvas* canvas, Sensor* sensor, Color color) { + const uint8_t x = 29, y = 39; + //Рисование рамки + canvas_draw_rframe(canvas, x, y, 75, 20, 3); + if(color == ColorBlack) { + canvas_draw_rbox(canvas, x, y, 75, 19, 3); + canvas_invert_color(canvas); + } else { + canvas_draw_rframe(canvas, x, y, 75, 19, 3); } + + //Рисование иконки + canvas_draw_icon(canvas, x + 3, y + 3, &I_co2_11x14); + + int16_t concentration_int = sensor->co2; + // int8_t concentration_dec = (int16_t)(sensor->co2 * 10) % 10; + + //Целая часть + if(concentration_int > 9999) { + snprintf(app->buff, BUFF_SIZE, "MAX "); + canvas_set_font(canvas, FontPrimary); + } else { + snprintf(app->buff, BUFF_SIZE, "%d", concentration_int); + canvas_set_font(canvas, FontBigNumbers); + } + + canvas_draw_str_aligned(canvas, x + 70, y + 10, AlignRight, AlignCenter, app->buff); } static void _draw_singleSensor(Canvas* canvas, Sensor* sensor, const uint8_t pos[2], Color color) { @@ -320,6 +347,17 @@ static void _draw_carousel_values(Canvas* canvas) { canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]); _draw_pressure(canvas, unitemp_sensor_getActive(generalview_sensor_index)); break; + case UT_DATA_TYPE_TEMP_HUM_CO2: + _draw_temperature( + canvas, + unitemp_sensor_getActive(generalview_sensor_index), + temp_positions[2][0], + temp_positions[2][1], + ColorWhite); + _draw_humidity( + canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]); + _draw_co2(canvas, unitemp_sensor_getActive(generalview_sensor_index), ColorWhite); + break; } } From 47734f24599edb39c9bd4ceec9196d00802bbcc2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 00:26:50 +0300 Subject: [PATCH 090/370] Fix --- applications/external/unitemp/views/General_view.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c index dd045d336..22d724935 100644 --- a/applications/external/unitemp/views/General_view.c +++ b/applications/external/unitemp/views/General_view.c @@ -159,6 +159,8 @@ static void _draw_pressure(Canvas* canvas, Sensor* sensor) { canvas_draw_str(canvas, x + 52, y + 13, "kPa"); } else if(app->settings.pressure_unit == UT_PRESSURE_HPA) { canvas_draw_str(canvas, x + 67, y + 13, "hPa"); + } +} static void _draw_co2(Canvas* canvas, Sensor* sensor, Color color) { const uint8_t x = 29, y = 39; From ffda6ad32102b27e493cf0d49e434997f11938a7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:03:07 +0300 Subject: [PATCH 091/370] Fix? I have no way to check if sensor still works --- applications/external/unitemp/sensors/SCD30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/unitemp/sensors/SCD30.c b/applications/external/unitemp/sensors/SCD30.c index b5f15b50d..627130da7 100644 --- a/applications/external/unitemp/sensors/SCD30.c +++ b/applications/external/unitemp/sensors/SCD30.c @@ -263,7 +263,7 @@ static bool loadFloat(uint8_t* buff, float* val) { buff += 3; } uint32_t tmpVal = load32_be(floatBuff); - *val = *(float*)&tmpVal; + memcpy(val, &tmpVal, sizeof(float)); return true; } From 016249c982ac4d8ac68427e607eab68f232a246a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:05:38 +0300 Subject: [PATCH 092/370] bump ver --- applications/external/unitemp/unitemp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h index 4d184b2c3..69cd8cf4f 100644 --- a/applications/external/unitemp/unitemp.h +++ b/applications/external/unitemp/unitemp.h @@ -40,7 +40,7 @@ //Имя приложения #define APP_NAME "Unitemp" //Версия приложения -#define UNITEMP_APP_VER "1.2" +#define UNITEMP_APP_VER "1.3" //Путь хранения файлов плагина #define APP_PATH_FOLDER "/ext/unitemp" //Имя файла с настройками From c5f062be9a0715ef34016e4fabf186df681e681c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:30:29 +0300 Subject: [PATCH 093/370] Rename and remove old apps --- ReadMe.md | 4 +- applications/external/flipfrid/LICENSE.md | 8 - applications/external/flipfrid/README.md | 36 - .../external/flipfrid/application.fam | 12 - applications/external/flipfrid/flipfrid.c | 276 -------- applications/external/flipfrid/flipfrid.h | 94 --- .../scene/flipfrid_scene_entrypoint.c | 201 ------ .../scene/flipfrid_scene_entrypoint.h | 8 - .../scene/flipfrid_scene_load_custom_uids.c | 85 --- .../scene/flipfrid_scene_load_custom_uids.h | 9 - .../flipfrid/scene/flipfrid_scene_load_file.c | 193 ----- .../flipfrid/scene/flipfrid_scene_load_file.h | 9 - .../scene/flipfrid_scene_run_attack.c | 666 ------------------ .../scene/flipfrid_scene_run_attack.h | 8 - .../scene/flipfrid_scene_select_field.c | 160 ----- .../scene/flipfrid_scene_select_field.h | 9 - applications/external/ibtn_fuzzer/LICENSE.md | 8 - .../external/ibtn_fuzzer/application.fam | 12 - .../external/ibtn_fuzzer/ibtnfuzzer.c | 280 -------- .../external/ibtn_fuzzer/ibtnfuzzer.h | 94 --- .../ibtn_fuzzer/images/ibutt_10px.png | Bin 304 -> 0 bytes .../scene/ibtnfuzzer_scene_entrypoint.c | 201 ------ .../scene/ibtnfuzzer_scene_entrypoint.h | 8 - .../scene/ibtnfuzzer_scene_load_custom_uids.c | 86 --- .../scene/ibtnfuzzer_scene_load_custom_uids.h | 9 - .../scene/ibtnfuzzer_scene_load_file.c | 293 -------- .../scene/ibtnfuzzer_scene_load_file.h | 9 - .../scene/ibtnfuzzer_scene_run_attack.c | 501 ------------- .../scene/ibtnfuzzer_scene_run_attack.h | 8 - .../scene/ibtnfuzzer_scene_select_field.c | 160 ----- .../scene/ibtnfuzzer_scene_select_field.h | 9 - .../application.fam | 16 +- .../{pacs_fuzzer => multi_fuzzer}/fuzzer.c | 0 .../{pacs_fuzzer => multi_fuzzer}/fuzzer_i.h | 0 .../helpers/fuzzer_custom_event.h | 0 .../helpers/fuzzer_types.h | 0 .../icons}/125_10px.png | Bin .../icons/ButtonLeft_4x7.png | Bin .../icons/ButtonRight_4x7.png | Bin .../icons/Ok_btn_9x9.png | Bin .../icons/Pin_arrow_up_7x9.png | Bin .../icons/Pin_back_arrow_10x8.png | Bin .../icons}/ibutt_10px.png | Bin .../icons}/rfid_10px.png | Bin .../lib/worker/fake_worker.c | 0 .../lib/worker/fake_worker.h | 0 .../lib/worker/protocol.c | 0 .../lib/worker/protocol.h | 0 .../lib/worker/protocol_i.h | 0 .../scenes/fuzzer_scene.c | 0 .../scenes/fuzzer_scene.h | 0 .../scenes/fuzzer_scene_attack.c | 0 .../scenes/fuzzer_scene_config.h | 0 .../scenes/fuzzer_scene_field_editor.c | 0 .../scenes/fuzzer_scene_main.c | 0 .../{pacs_fuzzer => multi_fuzzer}/todo.md | 0 .../views/attack.c | 0 .../views/attack.h | 0 .../views/field_editor.c | 0 .../views/field_editor.h | 0 .../views/main_menu.c | 0 .../views/main_menu.h | 0 .../external/pacs_fuzzer/icons/125_10px.png | Bin 308 -> 0 bytes .../external/pacs_fuzzer/icons/ibutt_10px.png | Bin 304 -> 0 bytes .../external/pacs_fuzzer/icons/rfid_10px.png | Bin 2389 -> 0 bytes 65 files changed, 10 insertions(+), 3462 deletions(-) delete mode 100644 applications/external/flipfrid/LICENSE.md delete mode 100644 applications/external/flipfrid/README.md delete mode 100644 applications/external/flipfrid/application.fam delete mode 100644 applications/external/flipfrid/flipfrid.c delete mode 100644 applications/external/flipfrid/flipfrid.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_load_file.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_load_file.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_run_attack.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_run_attack.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_select_field.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_select_field.h delete mode 100644 applications/external/ibtn_fuzzer/LICENSE.md delete mode 100644 applications/external/ibtn_fuzzer/application.fam delete mode 100644 applications/external/ibtn_fuzzer/ibtnfuzzer.c delete mode 100644 applications/external/ibtn_fuzzer/ibtnfuzzer.h delete mode 100644 applications/external/ibtn_fuzzer/images/ibutt_10px.png delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h rename applications/external/{pacs_fuzzer => multi_fuzzer}/application.fam (81%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/fuzzer.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/fuzzer_i.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/helpers/fuzzer_custom_event.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/helpers/fuzzer_types.h (100%) rename applications/external/{flipfrid/images => multi_fuzzer/icons}/125_10px.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/ButtonLeft_4x7.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/ButtonRight_4x7.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/Ok_btn_9x9.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/Pin_arrow_up_7x9.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/Pin_back_arrow_10x8.png (100%) rename applications/external/{ibtn_fuzzer => multi_fuzzer/icons}/ibutt_10px.png (100%) rename applications/external/{flipfrid => multi_fuzzer/icons}/rfid_10px.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/fake_worker.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/fake_worker.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/protocol.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/protocol.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/protocol_i.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene_attack.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene_config.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene_field_editor.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene_main.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/todo.md (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/attack.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/attack.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/field_editor.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/field_editor.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/main_menu.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/main_menu.h (100%) delete mode 100644 applications/external/pacs_fuzzer/icons/125_10px.png delete mode 100644 applications/external/pacs_fuzzer/icons/ibutt_10px.png delete mode 100644 applications/external/pacs_fuzzer/icons/rfid_10px.png diff --git a/ReadMe.md b/ReadMe.md index 23128cac0..f9e759e0b 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -134,7 +134,8 @@ You can support us by using links or addresses below: ### Community apps included: -- **RFID Fuzzer** [(by Ganapati & @xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/54) & New protocols by @mvanzanten +- **RFID Fuzzer** [(by @gid9798)](https://github.com/DarkFlippers/unleashed-firmware/pull/507) (original by Ganapati & xMasterX) +- **iButton Fuzzer** [(by @gid9798)](https://github.com/DarkFlippers/unleashed-firmware/pull/507) (original by xMasterX) - **Sub-GHz bruteforcer** [(by @derskythe & xMasterX)](https://github.com/derskythe/flipperzero-subbrute) [(original by Ganapati & xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/57) - **Sub-GHz playlist** [(by darmiel)](https://github.com/DarkFlippers/unleashed-firmware/pull/62) - ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module) @@ -157,7 +158,6 @@ You can support us by using links or addresses below: - Morse Code [(by wh00hw)](https://github.com/wh00hw/MorseCodeFAP) - **Unitemp - Temperature sensors reader** (DHT11/22, DS18B20, BMP280, HTU21x and more) [(by quen0n)](https://github.com/quen0n/unitemp-flipperzero) - BH1750 - Lightmeter [(by oleksiikutuzov)](https://github.com/oleksiikutuzov/flipperzero-lightmeter) -- **iButton Fuzzer** [(by xMasterX)](https://github.com/xMasterX/ibutton-fuzzer) - HEX Viewer [(by QtRoS)](https://github.com/QtRoS/flipper-zero-hex-viewer) - POCSAG Pager [(by xMasterX & Shmuma)](https://github.com/xMasterX/flipper-pager) - Text Viewer [(by kowalski7cc & kyhwana)](https://github.com/kowalski7cc/flipper-zero-text-viewer/tree/refactor-text-app) diff --git a/applications/external/flipfrid/LICENSE.md b/applications/external/flipfrid/LICENSE.md deleted file mode 100644 index a856581c9..000000000 --- a/applications/external/flipfrid/LICENSE.md +++ /dev/null @@ -1,8 +0,0 @@ -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * @G4N4P4T1 wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - */ \ No newline at end of file diff --git a/applications/external/flipfrid/README.md b/applications/external/flipfrid/README.md deleted file mode 100644 index 83d849454..000000000 --- a/applications/external/flipfrid/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Flipfrid - -Basic LFRFID Fuzzer. - -## Why - -Flipfrid is a simple Rfid fuzzer using lfrfid protocols (125khz). -Objective is to provide a simple to use fuzzer to test readers by emulating various cards. - -- EM4100 cards use a 1 byte customer id and 4 bytes card id. -- HIDProx cards use a 2 byte customer id and 3 byte card id. - -## How - -1) Select the Protocol with the left and right arrows -2) Select the Mode with the up and down arrows -3) Set TD (Time delay) between ID switch if you need, or left it as is -4) Click Start - -### Info - -There are 4 Protocols: -- EM4100 -- HIDProx -- PAC/Stanley -- H10301 - -There are 4 modes: -- Default Values: Try factory/default keys and emulate one after the other. -- BF customer id: An iteration from 0X00 to 0XFF on the first byte. -- Load Dump file: Load an existing dump (.rfid) generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF; -- Uids list: Iterate over an input text file (one uid per line) and emulate one after the other. - - -TODO : -- Add second byte test to `BF customer id` diff --git a/applications/external/flipfrid/application.fam b/applications/external/flipfrid/application.fam deleted file mode 100644 index 1ddd6ba50..000000000 --- a/applications/external/flipfrid/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="rfid_fuzzer", - name="RFID Fuzzer", - apptype=FlipperAppType.EXTERNAL, - entry_point="flipfrid_start", - requires=["gui", "storage", "dialogs", "input", "notification"], - stack_size=2 * 1024, - order=15, - fap_icon="rfid_10px.png", - fap_category="Tools", - fap_icon_assets="images", -) diff --git a/applications/external/flipfrid/flipfrid.c b/applications/external/flipfrid/flipfrid.c deleted file mode 100644 index 1cf7be865..000000000 --- a/applications/external/flipfrid/flipfrid.c +++ /dev/null @@ -1,276 +0,0 @@ -#include "flipfrid.h" - -#include "scene/flipfrid_scene_entrypoint.h" -#include "scene/flipfrid_scene_load_file.h" -#include "scene/flipfrid_scene_select_field.h" -#include "scene/flipfrid_scene_run_attack.h" -#include "scene/flipfrid_scene_load_custom_uids.h" - -#define RFIDFUZZER_APP_FOLDER "/ext/rfidfuzzer" - -static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - FlipFridState* flipfrid_state = ctx; - furi_mutex_acquire(flipfrid_state->mutex, FuriWaitForever); - - // Draw correct Canvas - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_draw(canvas, flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_draw(canvas, flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_draw(canvas, flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_draw(canvas, flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_draw(canvas, flipfrid_state); - break; - } - - furi_mutex_release(flipfrid_state->mutex); -} - -void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - FlipFridEvent event = { - .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; - furi_message_queue_put(event_queue, &event, 25); -} - -static void flipfrid_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - FlipFridEvent event = { - .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; - furi_message_queue_put(event_queue, &event, 25); -} - -FlipFridState* flipfrid_alloc() { - FlipFridState* flipfrid = malloc(sizeof(FlipFridState)); - flipfrid->notification_msg = furi_string_alloc(); - flipfrid->attack_name = furi_string_alloc(); - flipfrid->proto_name = furi_string_alloc(); - flipfrid->data_str = furi_string_alloc(); - - flipfrid->main_menu_items[0] = furi_string_alloc_set("Default Values"); - flipfrid->main_menu_items[1] = furi_string_alloc_set("BF Customer ID"); - flipfrid->main_menu_items[2] = furi_string_alloc_set("Load File"); - flipfrid->main_menu_items[3] = furi_string_alloc_set("Load UIDs from file"); - - flipfrid->main_menu_proto_items[0] = furi_string_alloc_set("EM4100"); - flipfrid->main_menu_proto_items[1] = furi_string_alloc_set("HIDProx"); - flipfrid->main_menu_proto_items[2] = furi_string_alloc_set("PAC/Stanley"); - flipfrid->main_menu_proto_items[3] = furi_string_alloc_set("H10301"); - - flipfrid->previous_scene = NoneScene; - flipfrid->current_scene = SceneEntryPoint; - flipfrid->is_running = true; - flipfrid->is_attacking = false; - flipfrid->key_index = 0; - flipfrid->menu_index = 0; - flipfrid->menu_proto_index = 0; - - flipfrid->attack = FlipFridAttackDefaultValues; - flipfrid->notify = furi_record_open(RECORD_NOTIFICATION); - - flipfrid->data[0] = 0x00; - flipfrid->data[1] = 0x00; - flipfrid->data[2] = 0x00; - flipfrid->data[3] = 0x00; - flipfrid->data[4] = 0x00; - flipfrid->data[5] = 0x00; - - flipfrid->payload[0] = 0x00; - flipfrid->payload[1] = 0x00; - flipfrid->payload[2] = 0x00; - flipfrid->payload[3] = 0x00; - flipfrid->payload[4] = 0x00; - flipfrid->payload[5] = 0x00; - - //Dialog - flipfrid->dialogs = furi_record_open(RECORD_DIALOGS); - - return flipfrid; -} - -void flipfrid_free(FlipFridState* flipfrid) { - //Dialog - furi_record_close(RECORD_DIALOGS); - notification_message(flipfrid->notify, &sequence_blink_stop); - - // Strings - furi_string_free(flipfrid->notification_msg); - furi_string_free(flipfrid->attack_name); - furi_string_free(flipfrid->proto_name); - furi_string_free(flipfrid->data_str); - - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(flipfrid->main_menu_items[i]); - } - - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(flipfrid->main_menu_proto_items[i]); - } - - // The rest - free(flipfrid); -} - -// ENTRYPOINT -int32_t flipfrid_start(void* p) { - UNUSED(p); - // Input - FURI_LOG_I(TAG, "Initializing input"); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent)); - FlipFridState* flipfrid_state = flipfrid_alloc(); - - flipfrid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!flipfrid_state->mutex) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_NOTIFICATION); - flipfrid_free(flipfrid_state); - return 255; - } - - Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, RFIDFUZZER_APP_FOLDER)) { - FURI_LOG_E(TAG, "Could not create folder %s", RFIDFUZZER_APP_FOLDER); - } - furi_record_close(RECORD_STORAGE); - - // Configure view port - FURI_LOG_I(TAG, "Initializing viewport"); - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, flipfrid_draw_callback, flipfrid_state); - view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue); - - // Configure timer - FURI_LOG_I(TAG, "Initializing timer"); - FuriTimer* timer = - furi_timer_alloc(flipfrid_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second - - // Register view port in GUI - FURI_LOG_I(TAG, "Initializing gui"); - Gui* gui = (Gui*)furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // Init values - FlipFridEvent event; - while(flipfrid_state->is_running) { - // Get next event - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); - if(event_status == FuriStatusOk) { - if(event.evt_type == EventTypeKey) { - //Handle event key - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_event(event, flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_event(event, flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_event(event, flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_event(event, flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_event(event, flipfrid_state); - break; - } - - } else if(event.evt_type == EventTypeTick) { - //Handle event tick - if(flipfrid_state->current_scene != flipfrid_state->previous_scene) { - // Trigger Exit Scene - switch(flipfrid_state->previous_scene) { - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_exit(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_exit(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_exit(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_exit(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_exit(flipfrid_state); - break; - case NoneScene: - break; - } - - // Trigger Entry Scene - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_enter(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_enter(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_enter(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_enter(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_enter(flipfrid_state); - break; - } - flipfrid_state->previous_scene = flipfrid_state->current_scene; - } - - // Trigger Tick Scene - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_tick(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_tick(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_tick(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_tick(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_tick(flipfrid_state); - break; - } - view_port_update(view_port); - } - } - } - - // Cleanup - furi_timer_stop(timer); - furi_timer_free(timer); - - FURI_LOG_I(TAG, "Cleaning up"); - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_mutex_free(flipfrid_state->mutex); - flipfrid_free(flipfrid_state); - - return 0; -} \ No newline at end of file diff --git a/applications/external/flipfrid/flipfrid.h b/applications/external/flipfrid/flipfrid.h deleted file mode 100644 index b95f9f75f..000000000 --- a/applications/external/flipfrid/flipfrid.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#define TAG "FlipFrid" - -typedef enum { - FlipFridAttackDefaultValues, - FlipFridAttackBfCustomerId, - FlipFridAttackLoadFile, - FlipFridAttackLoadFileCustomUids, -} FlipFridAttacks; - -typedef enum { - EM4100, - HIDProx, - PAC, - H10301, -} FlipFridProtos; - -typedef enum { - NoneScene, - SceneEntryPoint, - SceneSelectFile, - SceneSelectField, - SceneAttack, - SceneLoadCustomUids, -} FlipFridScene; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType evt_type; - InputKey key; - InputType input_type; -} FlipFridEvent; - -// STRUCTS -typedef struct { - FuriMutex* mutex; - bool is_running; - bool is_attacking; - FlipFridScene current_scene; - FlipFridScene previous_scene; - NotificationApp* notify; - u_int8_t menu_index; - u_int8_t menu_proto_index; - - FuriString* data_str; - uint8_t data[6]; - uint8_t payload[6]; - uint8_t attack_step; - FlipFridAttacks attack; - FlipFridProtos proto; - FuriString* attack_name; - FuriString* proto_name; - FuriString* main_menu_items[4]; - FuriString* main_menu_proto_items[4]; - - DialogsApp* dialogs; - FuriString* notification_msg; - uint8_t key_index; - LFRFIDWorker* worker; - ProtocolDict* dict; - ProtocolId protocol; - bool workr_rund; - bool attack_stop_called; - - uint8_t time_between_cards; - - // Used for custom dictionnary - Stream* uids_stream; -} FlipFridState; \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c deleted file mode 100644 index f4b39aa66..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "flipfrid_scene_entrypoint.h" - -void flipfrid_scene_entrypoint_menu_callback( - FlipFridState* context, - uint32_t index, - uint32_t proto_index) { - switch(index) { - case FlipFridAttackDefaultValues: - context->attack = FlipFridAttackDefaultValues; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Default Values"); - break; - case FlipFridAttackBfCustomerId: - context->attack = FlipFridAttackBfCustomerId; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Bad Customer ID"); - break; - case FlipFridAttackLoadFile: - context->attack = FlipFridAttackLoadFile; - context->current_scene = SceneSelectFile; - furi_string_set(context->attack_name, "Load File"); - break; - case FlipFridAttackLoadFileCustomUids: - context->attack = FlipFridAttackLoadFileCustomUids; - context->current_scene = SceneLoadCustomUids; - furi_string_set(context->attack_name, "Load Custom UIDs"); - break; - default: - break; - } - - switch(proto_index) { - case EM4100: - context->proto = EM4100; - furi_string_set(context->proto_name, "EM4100"); - break; - case HIDProx: - context->proto = HIDProx; - furi_string_set(context->proto_name, "HIDProx"); - break; - case PAC: - context->proto = PAC; - furi_string_set(context->proto_name, "PAC/Stanley"); - break; - case H10301: - context->proto = H10301; - furi_string_set(context->proto_name, "H10301"); - break; - default: - break; - } -} - -void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) { - // Clear the previous payload - context->payload[0] = 0x00; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - - context->menu_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_items[i] = furi_string_alloc(); - }*/ - - context->menu_proto_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_proto_items[i] = furi_string_alloc(); - }*/ -} - -void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - if(context->menu_index < FlipFridAttackLoadFileCustomUids) { - context->menu_index++; - } - break; - case InputKeyUp: - if(context->menu_index > FlipFridAttackDefaultValues) { - context->menu_index--; - } - break; - case InputKeyLeft: - if(context->menu_proto_index > EM4100) { - context->menu_proto_index--; - } else if(context->menu_proto_index == EM4100) { - context->menu_proto_index = H10301; - } - break; - case InputKeyRight: - if(context->menu_proto_index < H10301) { - context->menu_proto_index++; - } else if(context->menu_proto_index == H10301) { - context->menu_proto_index = EM4100; - } - break; - case InputKeyOk: - flipfrid_scene_entrypoint_menu_callback( - context, context->menu_index, context->menu_proto_index); - break; - case InputKeyBack: - context->is_running = false; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(context->main_menu_items != NULL) { - if(context->main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > FlipFridAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 24, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index])); - - if(context->menu_index < FlipFridAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index + 1])); - } - - if(context->menu_proto_index > EM4100) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_proto_items[context->menu_proto_index])); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - - if(context->menu_proto_index < H10301) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index + 1])); - } - } - } -} \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h deleted file mode 100644 index 29ca5bdfa..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_entrypoint_on_enter(FlipFridState* context); -void flipfrid_scene_entrypoint_on_exit(FlipFridState* context); -void flipfrid_scene_entrypoint_on_tick(FlipFridState* context); -void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c deleted file mode 100644 index 32157556b..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "flipfrid_scene_load_custom_uids.h" -#include "flipfrid_scene_run_attack.h" -#include "flipfrid_scene_entrypoint.h" - -#define LFRFID_UIDS_EXTENSION ".txt" -#define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer" - -bool flipfrid_load_uids(FlipFridState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - context->uids_stream = buffered_file_stream_alloc(storage); - result = - buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); - // Close if loading fails - if(!result) { - buffered_file_stream_close(context->uids_stream); - return false; - } - return result; -} - -bool flipfrid_load_custom_uids_from_file(FlipFridState* context) { - // Input events and views are managed by file_select - FuriString* uid_path; - uid_path = furi_string_alloc(); - furi_string_set(uid_path, RFIDFUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, LFRFID_UIDS_EXTENSION, &I_125_10px); - browser_options.base_path = RFIDFUZZER_APP_PATH_FOLDER; - browser_options.hide_ext = false; - - bool res = dialog_file_browser_show(context->dialogs, uid_path, uid_path, &browser_options); - - if(res) { - res = flipfrid_load_uids(context, furi_string_get_cstr(uid_path)); - } - - furi_string_free(uid_path); - - return res; -} - -void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context) { - if(flipfrid_load_custom_uids_from_file(context)) { - // Force context loading - flipfrid_scene_run_attack_on_enter(context); - context->current_scene = SceneAttack; - } else { - flipfrid_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context) { - UNUSED(context); - UNUSED(canvas); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h deleted file mode 100644 index a8ed982b6..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context); -bool flipfrid_load_custom_uids_from_file(FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_file.c b/applications/external/flipfrid/scene/flipfrid_scene_load_file.c deleted file mode 100644 index 5f3f1a31b..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_file.c +++ /dev/null @@ -1,193 +0,0 @@ -#include "flipfrid_scene_load_file.h" -#include "flipfrid_scene_entrypoint.h" - -#define LFRFID_APP_EXTENSION ".rfid" -#define LFRFID_APP_PATH_FOLDER "/ext/lfrfid" - -bool flipfrid_load(FlipFridState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - FuriString* temp_str; - temp_str = furi_string_alloc(); - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Error open file"); - break; - } - - // FileType - if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Filetype"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Filetypes"); - break; - } else { - FURI_LOG_I(TAG, "Filetype: %s", furi_string_get_cstr(temp_str)); - } - - // Key type - if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key type"); - break; - } else { - FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); - - if(context->proto == EM4100) { - if(strcmp(furi_string_get_cstr(temp_str), "EM4100") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == PAC) { - if(strcmp(furi_string_get_cstr(temp_str), "PAC/Stanley") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == H10301) { - if(strcmp(furi_string_get_cstr(temp_str), "H10301") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else { - if(strcmp(furi_string_get_cstr(temp_str), "HIDProx") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } - } - - // Data - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(context->proto == EM4100) { - if(furi_string_size(context->data_str) != 14) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == PAC) { - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == H10301) { - if(furi_string_size(context->data_str) != 8) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else { - if(furi_string_size(context->data_str) != 17) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } - - // String to uint8_t - for(uint8_t i = 0; i < 6; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - - result = true; - } while(0); - furi_string_free(temp_str); - flipper_format_free(fff_data_file); - if(result) { - FURI_LOG_I(TAG, "Loaded successfully"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Source loaded."); - } - return result; -} - -void flipfrid_scene_load_file_on_enter(FlipFridState* context) { - if(flipfrid_load_protocol_from_file(context)) { - context->current_scene = SceneSelectField; - } else { - flipfrid_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void flipfrid_scene_load_file_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_file_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context) { - UNUSED(context); - UNUSED(canvas); -} - -bool flipfrid_load_protocol_from_file(FlipFridState* context) { - FuriString* user_file_path; - user_file_path = furi_string_alloc(); - furi_string_set(user_file_path, LFRFID_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, LFRFID_APP_EXTENSION, &I_125_10px); - browser_options.base_path = LFRFID_APP_PATH_FOLDER; - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show( - context->dialogs, user_file_path, user_file_path, &browser_options); - - if(res) { - res = flipfrid_load(context, furi_string_get_cstr(user_file_path)); - } - - furi_string_free(user_file_path); - - return res; -} \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_file.h b/applications/external/flipfrid/scene/flipfrid_scene_load_file.h deleted file mode 100644 index ca82daab4..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_file.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_load_file_on_enter(FlipFridState* context); -void flipfrid_scene_load_file_on_exit(FlipFridState* context); -void flipfrid_scene_load_file_on_tick(FlipFridState* context); -void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context); -bool flipfrid_load_protocol_from_file(FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c deleted file mode 100644 index 5f40313ba..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c +++ /dev/null @@ -1,666 +0,0 @@ -#include "flipfrid_scene_run_attack.h" -#include - -uint8_t counter = 0; - -uint8_t id_list[17][5] = { - {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha - {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha - {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha - {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_hid[14][6] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, // Incremental UID - {0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID - {0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_pac[17][4] = { - {0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d}, // From arha - {0x34, 0x00, 0x29, 0x3d}, // From arha - {0x04, 0xdf, 0x00, 0x00}, // From arha - {0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_h[14][3] = { - {0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56}, // Incremental UID - {0x56, 0x34, 0x12}, // Decremental UID - {0xCA, 0xCA, 0xCA}, // From arha -}; - -void flipfrid_scene_run_attack_on_enter(FlipFridState* context) { - context->time_between_cards = 10; - context->attack_step = 0; - context->attack_stop_called = false; - context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); - context->worker = lfrfid_worker_alloc(context->dict); - if(context->proto == HIDProx) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx"); - } else if(context->proto == PAC) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "PAC/Stanley"); - } else if(context->proto == H10301) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "H10301"); - } else { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100"); - } -} - -void flipfrid_scene_run_attack_on_exit(FlipFridState* context) { - if(context->workr_rund) { - lfrfid_worker_stop(context->worker); - lfrfid_worker_stop_thread(context->worker); - context->workr_rund = false; - } - lfrfid_worker_free(context->worker); - protocol_dict_free(context->dict); - notification_message(context->notify, &sequence_blink_stop); -} - -void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { - if(context->is_attacking) { - if(1 == counter) { - protocol_dict_set_data(context->dict, context->protocol, context->payload, 6); - lfrfid_worker_free(context->worker); - context->worker = lfrfid_worker_alloc(context->dict); - lfrfid_worker_start_thread(context->worker); - lfrfid_worker_emulate_start(context->worker, context->protocol); - context->workr_rund = true; - } else if(0 == counter) { - if(context->workr_rund) { - lfrfid_worker_stop(context->worker); - lfrfid_worker_stop_thread(context->worker); - context->workr_rund = false; - furi_delay_ms(200); - } - switch(context->attack) { - case FlipFridAttackDefaultValues: - if(context->proto == EM4100) { - context->payload[0] = id_list[context->attack_step][0]; - context->payload[1] = id_list[context->attack_step][1]; - context->payload[2] = id_list[context->attack_step][2]; - context->payload[3] = id_list[context->attack_step][3]; - context->payload[4] = id_list[context->attack_step][4]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = id_list_pac[context->attack_step][0]; - context->payload[1] = id_list_pac[context->attack_step][1]; - context->payload[2] = id_list_pac[context->attack_step][2]; - context->payload[3] = id_list_pac[context->attack_step][3]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = id_list_h[context->attack_step][0]; - context->payload[1] = id_list_h[context->attack_step][1]; - context->payload[2] = id_list_h[context->attack_step][2]; - - if(context->attack_step == 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = id_list_hid[context->attack_step][0]; - context->payload[1] = id_list_hid[context->attack_step][1]; - context->payload[2] = id_list_hid[context->attack_step][2]; - context->payload[3] = id_list_hid[context->attack_step][3]; - context->payload[4] = id_list_hid[context->attack_step][4]; - context->payload[5] = id_list_hid[context->attack_step][5]; - - if(context->attack_step == 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackBfCustomerId: - if(context->proto == EM4100) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackLoadFile: - if(context->proto == EM4100) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - context->payload[5] = context->data[5]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackLoadFileCustomUids: - if(context->proto == EM4100) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 11) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 11) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 5; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == PAC) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 9) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 9) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 4; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == H10301) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 7) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 7) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 3; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 13) break; - break; - } - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(end_of_list) break; - if(furi_string_size(context->data_str) != 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 6; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } - } - } - if(counter > context->time_between_cards) { - counter = 0; - } else { - counter++; - } - } -} - -void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - break; - case InputKeyUp: - break; - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 5) { - context->time_between_cards--; - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 70) { - context->time_between_cards++; - } - } - break; - case InputKeyOk: - counter = 0; - if(!context->is_attacking) { - notification_message(context->notify, &sequence_blink_start_blue); - context->is_attacking = true; - } else { - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } - break; - case InputKeyBack: - context->is_attacking = false; - counter = 0; - - notification_message(context->notify, &sequence_blink_stop); - if(context->attack_stop_called) { - context->attack_stop_called = false; - context->attack_step = 0; - if(context->attack == FlipFridAttackLoadFileCustomUids) { - furi_string_reset(context->data_str); - stream_rewind(context->uids_stream); - buffered_file_stream_close(context->uids_stream); - } - - furi_string_reset(context->notification_msg); - context->current_scene = SceneEntryPoint; - } - - context->attack_stop_called = true; - break; - default: - break; - } - } - if(event.input_type == InputTypeLong) { - switch(event.key) { - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 0) { - if((context->time_between_cards - 10) > 5) { - context->time_between_cards -= 10; - } - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 70) { - context->time_between_cards += 10; - } - } - break; - default: - break; - } - } - } -} - -void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(context->attack_name)); - - char uid[18]; - char speed[16]; - if(context->proto == HIDProx) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4], - context->payload[5]); - } else if(context->proto == PAC) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3]); - } else if(context->proto == H10301) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2]); - } else { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4]); - } - - canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, uid); - - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 64, 26, AlignCenter, AlignTop, furi_string_get_cstr(context->proto_name)); - - snprintf(speed, sizeof(speed), "Time delay: %d", context->time_between_cards); - - //canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Speed:"); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, speed); - //char start_stop_msg[20]; - if(context->is_attacking) { - elements_button_center(canvas, "Stop"); - //snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); - } else { - elements_button_center(canvas, "Start"); - elements_button_left(canvas, "TD -"); - elements_button_right(canvas, "+ TD"); - } - //canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h deleted file mode 100644 index ae56d35e7..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_run_attack_on_enter(FlipFridState* context); -void flipfrid_scene_run_attack_on_exit(FlipFridState* context); -void flipfrid_scene_run_attack_on_tick(FlipFridState* context); -void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context); diff --git a/applications/external/flipfrid/scene/flipfrid_scene_select_field.c b/applications/external/flipfrid/scene/flipfrid_scene_select_field.c deleted file mode 100644 index ccb49e910..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_select_field.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "flipfrid_scene_select_field.h" - -void flipfrid_center_displayed_key(FlipFridState* context, uint8_t index) { - char key_cstr[18]; - uint8_t key_len = 18; - uint8_t str_index = (index * 3); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - int key_index = 0; - - if(context->proto == EM4100) { - key_len = 16; - } - if(context->proto == PAC) { - key_len = 13; - } - if(context->proto == H10301) { - key_len = 10; - } - - for(uint8_t i = 0; i < data_len; i++) { - if(context->data[i] < 9) { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "0%X ", context->data[i]); - } else { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "%X ", context->data[i]); - } - } - - char display_menu[17] = { - 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; - - if(index > 1) { - display_menu[0] = key_cstr[str_index - 6]; - display_menu[1] = key_cstr[str_index - 5]; - } else { - display_menu[0] = ' '; - display_menu[1] = ' '; - } - - if(index > 0) { - display_menu[3] = key_cstr[str_index - 3]; - display_menu[4] = key_cstr[str_index - 2]; - } else { - display_menu[3] = ' '; - display_menu[4] = ' '; - } - - display_menu[7] = key_cstr[str_index]; - display_menu[8] = key_cstr[str_index + 1]; - - if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { - display_menu[11] = key_cstr[str_index + 3]; - display_menu[12] = key_cstr[str_index + 4]; - } else { - display_menu[11] = ' '; - display_menu[12] = ' '; - } - - if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { - display_menu[14] = key_cstr[str_index + 6]; - display_menu[15] = key_cstr[str_index + 7]; - } else { - display_menu[14] = ' '; - display_menu[15] = ' '; - } - - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, display_menu); -} - -void flipfrid_scene_select_field_on_enter(FlipFridState* context) { - furi_string_reset(context->notification_msg); -} - -void flipfrid_scene_select_field_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_select_field_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - const char* key_cstr = furi_string_get_cstr(context->data_str); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - - // don't look, it's ugly but I'm a python dev so... - uint8_t nb_bytes = 0; - for(uint8_t i = 0; i < strlen(key_cstr); i++) { - if(' ' == key_cstr[i]) { - nb_bytes++; - } - } - - switch(event.key) { - case InputKeyDown: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] - 1); - } - } - break; - case InputKeyUp: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] + 1); - } - } - break; - case InputKeyLeft: - if(context->key_index > 0) { - context->key_index = context->key_index - 1; - } - break; - case InputKeyRight: - if(context->key_index < nb_bytes) { - context->key_index = context->key_index + 1; - } - break; - case InputKeyOk: - furi_string_reset(context->notification_msg); - context->current_scene = SceneAttack; - break; - case InputKeyBack: - context->key_index = 0; - furi_string_reset(context->notification_msg); - context->current_scene = SceneSelectFile; - break; - default: - break; - } - FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); - } - } -} - -void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 12, 5, AlignLeft, AlignTop, "Left and right: select byte"); - canvas_draw_str_aligned(canvas, 12, 15, AlignLeft, AlignTop, "Up and down: adjust byte"); - - char msg_index[18]; - canvas_set_font(canvas, FontPrimary); - snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); - - flipfrid_center_displayed_key(context, context->key_index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignTop, furi_string_get_cstr(context->notification_msg)); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_select_field.h b/applications/external/flipfrid/scene/flipfrid_scene_select_field.h deleted file mode 100644 index 5533e321c..000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_select_field.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_select_field_on_enter(FlipFridState* context); -void flipfrid_scene_select_field_on_exit(FlipFridState* context); -void flipfrid_scene_select_field_on_tick(FlipFridState* context); -void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context); -void center_displayed_key(FlipFridState* context, uint8_t index); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/LICENSE.md b/applications/external/ibtn_fuzzer/LICENSE.md deleted file mode 100644 index ba3b84456..000000000 --- a/applications/external/ibtn_fuzzer/LICENSE.md +++ /dev/null @@ -1,8 +0,0 @@ -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * @xMasterX and @G4N4P4T1(made original version) wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - */ \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/application.fam b/applications/external/ibtn_fuzzer/application.fam deleted file mode 100644 index 87c02a913..000000000 --- a/applications/external/ibtn_fuzzer/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="ibtn_fuzzer", - name="iButton Fuzzer", - apptype=FlipperAppType.EXTERNAL, - entry_point="ibtnfuzzer_start", - requires=["gui", "storage", "dialogs", "input", "notification"], - stack_size=1 * 1024, - order=15, - fap_icon="ibutt_10px.png", - fap_category="Tools", - fap_icon_assets="images", -) diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.c b/applications/external/ibtn_fuzzer/ibtnfuzzer.c deleted file mode 100644 index 825a55560..000000000 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.c +++ /dev/null @@ -1,280 +0,0 @@ -#include "ibtnfuzzer.h" - -#include "scene/ibtnfuzzer_scene_entrypoint.h" -#include "scene/ibtnfuzzer_scene_load_file.h" -#include "scene/ibtnfuzzer_scene_select_field.h" -#include "scene/ibtnfuzzer_scene_run_attack.h" -#include "scene/ibtnfuzzer_scene_load_custom_uids.h" - -#define IBTNFUZZER_APP_FOLDER "/ext/ibtnfuzzer" - -static void ibtnfuzzer_draw_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - iBtnFuzzerState* ibtnfuzzer_state = ctx; - furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever); - - // Draw correct Canvas - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_draw(canvas, ibtnfuzzer_state); - break; - } - - furi_mutex_release(ibtnfuzzer_state->mutex); -} - -void ibtnfuzzer_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - iBtnFuzzerEvent event = { - .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; - furi_message_queue_put(event_queue, &event, 25); -} - -static void ibtnfuzzer_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - iBtnFuzzerEvent event = { - .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; - furi_message_queue_put(event_queue, &event, 25); -} - -iBtnFuzzerState* ibtnfuzzer_alloc() { - iBtnFuzzerState* ibtnfuzzer = malloc(sizeof(iBtnFuzzerState)); - ibtnfuzzer->notification_msg = furi_string_alloc(); - ibtnfuzzer->attack_name = furi_string_alloc(); - ibtnfuzzer->proto_name = furi_string_alloc(); - ibtnfuzzer->data_str = furi_string_alloc(); - - ibtnfuzzer->main_menu_items[0] = furi_string_alloc_set("Default Values"); - ibtnfuzzer->main_menu_items[1] = furi_string_alloc_set("Load File"); - ibtnfuzzer->main_menu_items[2] = furi_string_alloc_set("Load UIDs from file"); - - ibtnfuzzer->main_menu_proto_items[0] = furi_string_alloc_set("DS1990"); - ibtnfuzzer->main_menu_proto_items[1] = furi_string_alloc_set("Metakom"); - ibtnfuzzer->main_menu_proto_items[2] = furi_string_alloc_set("Cyfral"); - - ibtnfuzzer->previous_scene = NoneScene; - ibtnfuzzer->current_scene = SceneEntryPoint; - ibtnfuzzer->is_running = true; - ibtnfuzzer->is_attacking = false; - ibtnfuzzer->key_index = 0; - ibtnfuzzer->menu_index = 0; - ibtnfuzzer->menu_proto_index = 0; - - ibtnfuzzer->attack = iBtnFuzzerAttackDefaultValues; - ibtnfuzzer->notify = furi_record_open(RECORD_NOTIFICATION); - - ibtnfuzzer->data[0] = 0x00; - ibtnfuzzer->data[1] = 0x00; - ibtnfuzzer->data[2] = 0x00; - ibtnfuzzer->data[3] = 0x00; - ibtnfuzzer->data[4] = 0x00; - ibtnfuzzer->data[5] = 0x00; - ibtnfuzzer->data[6] = 0x00; - ibtnfuzzer->data[7] = 0x00; - - ibtnfuzzer->payload[0] = 0x00; - ibtnfuzzer->payload[1] = 0x00; - ibtnfuzzer->payload[2] = 0x00; - ibtnfuzzer->payload[3] = 0x00; - ibtnfuzzer->payload[4] = 0x00; - ibtnfuzzer->payload[5] = 0x00; - ibtnfuzzer->payload[6] = 0x00; - ibtnfuzzer->payload[7] = 0x00; - - //Dialog - ibtnfuzzer->dialogs = furi_record_open(RECORD_DIALOGS); - - return ibtnfuzzer; -} - -void ibtnfuzzer_free(iBtnFuzzerState* ibtnfuzzer) { - //Dialog - furi_record_close(RECORD_DIALOGS); - notification_message(ibtnfuzzer->notify, &sequence_blink_stop); - - // Strings - furi_string_free(ibtnfuzzer->notification_msg); - furi_string_free(ibtnfuzzer->attack_name); - furi_string_free(ibtnfuzzer->proto_name); - furi_string_free(ibtnfuzzer->data_str); - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(ibtnfuzzer->main_menu_items[i]); - } - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(ibtnfuzzer->main_menu_proto_items[i]); - } - - // The rest - free(ibtnfuzzer); -} - -// ENTRYPOINT -int32_t ibtnfuzzer_start(void* p) { - UNUSED(p); - // Input - FURI_LOG_I(TAG, "Initializing input"); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(iBtnFuzzerEvent)); - iBtnFuzzerState* ibtnfuzzer_state = ibtnfuzzer_alloc(); - - ibtnfuzzer_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!ibtnfuzzer_state->mutex) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_NOTIFICATION); - ibtnfuzzer_free(ibtnfuzzer_state); - return 255; - } - - Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, IBTNFUZZER_APP_FOLDER)) { - FURI_LOG_E(TAG, "Could not create folder %s", IBTNFUZZER_APP_FOLDER); - } - furi_record_close(RECORD_STORAGE); - - // Configure view port - FURI_LOG_I(TAG, "Initializing viewport"); - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, ibtnfuzzer_draw_callback, ibtnfuzzer_state); - view_port_input_callback_set(view_port, ibtnfuzzer_input_callback, event_queue); - - // Configure timer - FURI_LOG_I(TAG, "Initializing timer"); - FuriTimer* timer = - furi_timer_alloc(ibtnfuzzer_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second - - // Register view port in GUI - FURI_LOG_I(TAG, "Initializing gui"); - Gui* gui = (Gui*)furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // Init values - iBtnFuzzerEvent event; - while(ibtnfuzzer_state->is_running) { - // Get next event - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); - //furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever); - if(event_status == FuriStatusOk) { - if(event.evt_type == EventTypeKey) { - //Handle event key - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_event(event, ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_event(event, ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_event(event, ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_event(event, ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_event(event, ibtnfuzzer_state); - break; - } - - } else if(event.evt_type == EventTypeTick) { - //Handle event tick - if(ibtnfuzzer_state->current_scene != ibtnfuzzer_state->previous_scene) { - // Trigger Exit Scene - switch(ibtnfuzzer_state->previous_scene) { - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_exit(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_exit(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_exit(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_exit(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_exit(ibtnfuzzer_state); - break; - case NoneScene: - break; - } - - // Trigger Entry Scene - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_enter(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_enter(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_enter(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_enter(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_enter(ibtnfuzzer_state); - break; - } - ibtnfuzzer_state->previous_scene = ibtnfuzzer_state->current_scene; - } - - // Trigger Tick Scene - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_tick(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_tick(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_tick(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_tick(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_tick(ibtnfuzzer_state); - break; - } - view_port_update(view_port); - } - } - //furi_mutex_release(ibtnfuzzer_state->mutex); - } - - // Cleanup - furi_timer_stop(timer); - furi_timer_free(timer); - - FURI_LOG_I(TAG, "Cleaning up"); - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_mutex_free(ibtnfuzzer_state->mutex); - ibtnfuzzer_free(ibtnfuzzer_state); - - return 0; -} \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.h b/applications/external/ibtn_fuzzer/ibtnfuzzer.h deleted file mode 100644 index 7a9e2b537..000000000 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#define TAG "iBtnFuzzer" - -typedef enum { - iBtnFuzzerAttackDefaultValues, - iBtnFuzzerAttackLoadFile, - iBtnFuzzerAttackLoadFileCustomUids, -} iBtnFuzzerAttacks; - -typedef enum { - DS1990, - Metakom, - Cyfral, -} iBtnFuzzerProtos; - -typedef enum { - NoneScene, - SceneEntryPoint, - SceneSelectFile, - SceneSelectField, - SceneAttack, - SceneLoadCustomUids, -} iBtnFuzzerScene; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType evt_type; - InputKey key; - InputType input_type; -} iBtnFuzzerEvent; - -// STRUCTS -typedef struct { - FuriMutex* mutex; - bool is_running; - bool is_attacking; - iBtnFuzzerScene current_scene; - iBtnFuzzerScene previous_scene; - NotificationApp* notify; - u_int8_t menu_index; - u_int8_t menu_proto_index; - - FuriString* data_str; - uint8_t data[8]; - uint8_t payload[8]; - uint8_t attack_step; - iBtnFuzzerAttacks attack; - iBtnFuzzerProtos proto; - FuriString* attack_name; - FuriString* proto_name; - FuriString* main_menu_items[3]; - FuriString* main_menu_proto_items[3]; - - DialogsApp* dialogs; - FuriString* notification_msg; - uint8_t key_index; - iButtonWorker* worker; - iButtonKey* key; - iButtonProtocolId keytype; - iButtonProtocols* protocols; - bool workr_rund; - bool enter_rerun; - bool attack_stop_called; - - uint8_t time_between_cards; - - // Used for custom dictionnary - Stream* uids_stream; -} iBtnFuzzerState; \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/images/ibutt_10px.png b/applications/external/ibtn_fuzzer/images/ibutt_10px.png deleted file mode 100644 index 2fdaf123a657c00c9c84632ca3c151674e451ae1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bW_|craZ$KesPZ!4!j_b)kjvx52z42i= z^Wk##wtdVzTiGS{99;2>!TC2M!yZeXz}?LkD}l;YOI#yLQW8s2t&)pUffR$0fsvuE zfvK*cNr<75m9c@9v4ysQft7*5`ikN&C>nC}Q!>*kp&E>VdO{3LtqcsU49p-Jly36? Qy~)7f>FVdQ&MBb@0C$~I0{{R3 diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c deleted file mode 100644 index 1dd239c3b..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "ibtnfuzzer_scene_entrypoint.h" - -void ibtnfuzzer_scene_entrypoint_menu_callback( - iBtnFuzzerState* context, - uint32_t index, - uint32_t proto_index) { - switch(index) { - case iBtnFuzzerAttackDefaultValues: - context->attack = iBtnFuzzerAttackDefaultValues; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Default Values"); - break; - case iBtnFuzzerAttackLoadFile: - context->attack = iBtnFuzzerAttackLoadFile; - context->current_scene = SceneSelectFile; - furi_string_set(context->attack_name, "Load File"); - break; - case iBtnFuzzerAttackLoadFileCustomUids: - context->attack = iBtnFuzzerAttackLoadFileCustomUids; - context->current_scene = SceneLoadCustomUids; - furi_string_set(context->attack_name, "Load Custom UIDs"); - break; - default: - break; - } - - switch(proto_index) { - case DS1990: - context->proto = DS1990; - furi_string_set(context->proto_name, "DS1990"); - break; - case Metakom: - context->proto = Metakom; - furi_string_set(context->proto_name, "Metakom"); - break; - case Cyfral: - context->proto = Cyfral; - furi_string_set(context->proto_name, "Cyfral"); - break; - default: - break; - } -} - -void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context) { - // Clear the previous payload - context->payload[0] = 0x00; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - context->payload[6] = 0x00; - context->payload[7] = 0x00; - - context->menu_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_items[i] = furi_string_alloc(); - }*/ - - context->menu_proto_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_proto_items[i] = furi_string_alloc(); - }*/ -} - -void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context) { - context->enter_rerun = false; -} - -void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_entrypoint_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { - context->menu_index++; - } - break; - case InputKeyUp: - if(context->menu_index > iBtnFuzzerAttackDefaultValues) { - context->menu_index--; - } - break; - case InputKeyLeft: - if(context->menu_proto_index > DS1990) { - context->menu_proto_index--; - } else if(context->menu_proto_index == DS1990) { - context->menu_proto_index = Cyfral; - } - break; - case InputKeyRight: - if(context->menu_proto_index < Cyfral) { - context->menu_proto_index++; - } else if(context->menu_proto_index == Cyfral) { - context->menu_proto_index = DS1990; - } - break; - case InputKeyOk: - ibtnfuzzer_scene_entrypoint_menu_callback( - context, context->menu_index, context->menu_proto_index); - break; - case InputKeyBack: - context->is_running = false; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - if(!context->enter_rerun) { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->enter_rerun = true; - } - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(context->main_menu_items != NULL) { - if(context->main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > iBtnFuzzerAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 24, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index])); - - if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index + 1])); - } - - if(context->menu_proto_index > DS1990) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); - - canvas_set_font(canvas, FontPrimary); - if(context->main_menu_proto_items[context->menu_proto_index] != NULL) { - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index])); - } - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - - if(context->menu_proto_index < Cyfral) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index + 1])); - } - } - } -} \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h deleted file mode 100644 index b77aec369..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c deleted file mode 100644 index 07199ab46..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "ibtnfuzzer_scene_load_custom_uids.h" -#include "ibtnfuzzer_scene_run_attack.h" -#include "ibtnfuzzer_scene_entrypoint.h" - -#define IBTNFUZZER_UIDS_EXTENSION ".txt" -#define IBTNFUZZER_APP_PATH_FOLDER "/ext/ibtnfuzzer" - -bool ibtnfuzzer_load_uids(iBtnFuzzerState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - context->uids_stream = buffered_file_stream_alloc(storage); - result = - buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); - // Close if loading fails - if(!result) { - buffered_file_stream_close(context->uids_stream); - return false; - } - return result; -} - -bool ibtnfuzzer_load_custom_uids_from_file(iBtnFuzzerState* context) { - // Input events and views are managed by file_select - FuriString* uid_path; - uid_path = furi_string_alloc(); - furi_string_set(uid_path, IBTNFUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, IBTNFUZZER_UIDS_EXTENSION, &I_ibutt_10px); - browser_options.base_path = IBTNFUZZER_APP_PATH_FOLDER; - browser_options.hide_ext = false; - - bool res = dialog_file_browser_show(context->dialogs, uid_path, uid_path, &browser_options); - - if(res) { - res = ibtnfuzzer_load_uids(context, furi_string_get_cstr(uid_path)); - } - - furi_string_free(uid_path); - - return res; -} - -void ibtnfuzzer_scene_load_custom_uids_on_enter(iBtnFuzzerState* context) { - if(ibtnfuzzer_load_custom_uids_from_file(context)) { - // Force context loading - ibtnfuzzer_scene_run_attack_on_enter(context); - context->current_scene = SceneAttack; - } else { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void ibtnfuzzer_scene_load_custom_uids_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_custom_uids_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_custom_uids_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_load_custom_uids_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - UNUSED(context); - UNUSED(canvas); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h deleted file mode 100644 index bb51c7079..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_load_custom_uids_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_draw(Canvas* canvas, iBtnFuzzerState* context); -bool ibtnfuzzer_load_custom_uids_from_file(iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c deleted file mode 100644 index 92f79a424..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c +++ /dev/null @@ -1,293 +0,0 @@ -#include "ibtnfuzzer_scene_load_file.h" -#include "ibtnfuzzer_scene_entrypoint.h" - -#define IBUTTON_FUZZER_APP_EXTENSION ".ibtn" -#define IBUTTON_FUZZER_APP_PATH_FOLDER "/ext/ibutton" - -bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - FuriString* temp_str; - temp_str = furi_string_alloc(); - bool key_v2 = false; - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Error open file"); - break; - } - - // FileType - if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Filetype"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Filetypes"); - break; - } else { - FURI_LOG_I(TAG, "Filetype: %s", furi_string_get_cstr(temp_str)); - } - - // Key type - if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key type, checking for typ2.."); - - if(!flipper_format_rewind(fff_data_file)) { - FURI_LOG_E(TAG, "Failed to rewind file"); - break; - } - if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { - furi_string_reset(context->notification_msg); - furi_string_set( - context->notification_msg, "Missing or incorrect Protocol or Key type"); - break; - } - FURI_LOG_I(TAG, "Key type V2: %s", furi_string_get_cstr(temp_str)); - key_v2 = true; - - if(context->proto == DS1990) { - if(strcmp(furi_string_get_cstr(temp_str), "DS1990") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == Cyfral) { - if(strcmp(furi_string_get_cstr(temp_str), "Cyfral") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else { - if(strcmp(furi_string_get_cstr(temp_str), "Metakom") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } - } else { - FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); - - if(context->proto == DS1990) { - if(strcmp(furi_string_get_cstr(temp_str), "Dallas") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == Cyfral) { - if(strcmp(furi_string_get_cstr(temp_str), "Cyfral") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else { - if(strcmp(furi_string_get_cstr(temp_str), "Metakom") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } - } - if(!key_v2) { - // Data - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(context->proto == DS1990) { - if(furi_string_size(context->data_str) != 23) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == Cyfral) { - if(furi_string_size(context->data_str) != 5) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else { - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - } else { - // Data - if(context->proto == DS1990) { - if(!flipper_format_read_string(fff_data_file, "Rom Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Rom Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Rom Data"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(furi_string_size(context->data_str) != 23) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - } else if(context->proto == Cyfral) { - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Data"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(furi_string_size(context->data_str) != 5) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - } else { - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Data"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - } - } - - result = true; - } while(0); - furi_string_free(temp_str); - flipper_format_free(fff_data_file); - if(result) { - FURI_LOG_I(TAG, "Loaded successfully"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Source loaded."); - } - return result; -} - -void ibtnfuzzer_scene_load_file_on_enter(iBtnFuzzerState* context) { - if(ibtnfuzzer_load_protocol_from_file(context)) { - context->current_scene = SceneSelectField; - } else { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void ibtnfuzzer_scene_load_file_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_file_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_file_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_load_file_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - UNUSED(context); - UNUSED(canvas); -} - -bool ibtnfuzzer_load_protocol_from_file(iBtnFuzzerState* context) { - FuriString* user_file_path; - user_file_path = furi_string_alloc(); - furi_string_set(user_file_path, IBUTTON_FUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, IBUTTON_FUZZER_APP_EXTENSION, &I_ibutt_10px); - browser_options.base_path = IBUTTON_FUZZER_APP_PATH_FOLDER; - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show( - context->dialogs, user_file_path, user_file_path, &browser_options); - - if(res) { - res = ibtnfuzzer_load(context, furi_string_get_cstr(user_file_path)); - } - - furi_string_free(user_file_path); - - return res; -} \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h deleted file mode 100644 index 34031d08c..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_load_file_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_draw(Canvas* canvas, iBtnFuzzerState* context); -bool ibtnfuzzer_load_protocol_from_file(iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c deleted file mode 100644 index 13ec6e6be..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ /dev/null @@ -1,501 +0,0 @@ -#include "ibtnfuzzer_scene_run_attack.h" -#include - -uint8_t counter = 0; - -uint8_t id_list_ds1990[18][8] = { - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал - {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах - {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% - {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% - {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) - {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF - {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 - {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni -}; - -uint8_t id_list_metakom[17][4] = { - {0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d}, // ?? - {0x34, 0x00, 0x29, 0x3d}, // ?? - {0x04, 0xdf, 0x00, 0x00}, // ?? - {0xCA, 0xCA, 0xCA, 0xCA}, // ?? -}; - -uint8_t id_list_cyfral[16][2] = { - {0x00, 0x00}, // Null bytes - {0xFF, 0xFF}, // Only FF - {0x11, 0x11}, // Only 11 - {0x22, 0x22}, // Only 22 - {0x33, 0x33}, // Only 33 - {0x44, 0x44}, // Only 44 - {0x55, 0x55}, // Only 55 - {0x66, 0x66}, // Only 66 - {0x77, 0x77}, // Only 77 - {0x88, 0x88}, // Only 88 - {0x99, 0x99}, // Only 99 - {0x12, 0x34}, // Incremental UID - {0x56, 0x34}, // Decremental UID - {0xCA, 0xCA}, // ?? - {0x8E, 0xC9}, // Elevator code - {0x6A, 0x50}, // VERY fresh code from smartkey -}; - -void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) { - context->time_between_cards = 8; - context->attack_step = 0; - context->attack_stop_called = false; - context->protocols = ibutton_protocols_alloc(); - context->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(context->protocols)); - context->worker = ibutton_worker_alloc(context->protocols); - if(context->proto == Metakom) { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Metakom"); - } else if(context->proto == Cyfral) { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Cyfral"); - } else { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "DS1990"); - } - context->workr_rund = false; -} - -void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context) { - if(context->workr_rund) { - ibutton_worker_stop(context->worker); - ibutton_worker_stop_thread(context->worker); - context->workr_rund = false; - } - ibutton_key_free(context->key); - ibutton_worker_free(context->worker); - ibutton_protocols_free(context->protocols); - notification_message(context->notify, &sequence_blink_stop); -} - -void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { - if(context->is_attacking) { - if(1 == counter) { - ibutton_worker_start_thread(context->worker); - ibutton_key_set_protocol_id(context->key, context->keytype); - iButtonEditableData data; - ibutton_protocols_get_editable_data(context->protocols, context->key, &data); - data.size = sizeof(context->payload); - for(size_t i = 0; i < data.size; i++) { - data.ptr[i] = context->payload[i]; - } - - ibutton_worker_emulate_start(context->worker, context->key); - context->workr_rund = true; - } else if(0 == counter) { - if(context->workr_rund) { - ibutton_worker_stop(context->worker); - ibutton_worker_stop_thread(context->worker); - context->workr_rund = false; - furi_delay_ms(500); - } - switch(context->attack) { - case iBtnFuzzerAttackDefaultValues: - if(context->proto == DS1990) { - context->payload[0] = id_list_ds1990[context->attack_step][0]; - context->payload[1] = id_list_ds1990[context->attack_step][1]; - context->payload[2] = id_list_ds1990[context->attack_step][2]; - context->payload[3] = id_list_ds1990[context->attack_step][3]; - context->payload[4] = id_list_ds1990[context->attack_step][4]; - context->payload[5] = id_list_ds1990[context->attack_step][5]; - context->payload[6] = id_list_ds1990[context->attack_step][6]; - context->payload[7] = id_list_ds1990[context->attack_step][7]; - - if(context->attack_step == 17) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == Metakom) { - context->payload[0] = id_list_metakom[context->attack_step][0]; - context->payload[1] = id_list_metakom[context->attack_step][1]; - context->payload[2] = id_list_metakom[context->attack_step][2]; - context->payload[3] = id_list_metakom[context->attack_step][3]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = id_list_cyfral[context->attack_step][0]; - context->payload[1] = id_list_cyfral[context->attack_step][1]; - - if(context->attack_step == 15) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } - case iBtnFuzzerAttackLoadFile: - if(context->proto == DS1990) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - context->payload[5] = context->data[5]; - context->payload[6] = context->data[6]; - context->payload[7] = context->data[7]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == Cyfral) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } - - case iBtnFuzzerAttackLoadFileCustomUids: - if(context->proto == DS1990) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 17) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 17) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 8; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == Cyfral) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 5) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 5) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 2; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 9) break; - break; - } - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(end_of_list) break; - if(furi_string_size(context->data_str) != 9) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 4; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } - } - } - - if(counter > context->time_between_cards) { - counter = 0; - } else { - counter++; - } - } -} - -void ibtnfuzzer_scene_run_attack_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - break; - case InputKeyUp: - break; - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 4) { - context->time_between_cards--; - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 80) { - context->time_between_cards++; - } - } - break; - case InputKeyOk: - counter = 0; - if(!context->is_attacking) { - notification_message(context->notify, &sequence_blink_start_blue); - context->is_attacking = true; - } else { - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } - break; - case InputKeyBack: - context->is_attacking = false; - counter = 0; - - notification_message(context->notify, &sequence_blink_stop); - if(context->attack_stop_called) { - context->attack_stop_called = false; - context->attack_step = 0; - if(context->attack == iBtnFuzzerAttackLoadFileCustomUids) { - furi_string_reset(context->data_str); - stream_rewind(context->uids_stream); - buffered_file_stream_close(context->uids_stream); - } - - furi_string_reset(context->notification_msg); - context->current_scene = SceneEntryPoint; - } - - context->attack_stop_called = true; - break; - default: - break; - } - } - if(event.input_type == InputTypeLong) { - switch(event.key) { - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 4) { - if((context->time_between_cards - 10) > 4) { - context->time_between_cards -= 10; - } - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 80) { - context->time_between_cards += 10; - } - } - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_run_attack_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(context->attack_name)); - - char uid[25]; - char speed[16]; - if(context->proto == Metakom) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3]); - } else if(context->proto == Cyfral) { - snprintf(uid, sizeof(uid), "%02X:%02X", context->payload[0], context->payload[1]); - } else { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4], - context->payload[5], - context->payload[6], - context->payload[7]); - } - - canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, uid); - - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 64, 26, AlignCenter, AlignTop, furi_string_get_cstr(context->proto_name)); - - snprintf(speed, sizeof(speed), "Time delay: %d", context->time_between_cards); - - //canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Speed:"); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, speed); - //char start_stop_msg[20]; - if(context->is_attacking) { - elements_button_center(canvas, "Stop"); - //snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); - } else { - elements_button_center(canvas, "Start"); - elements_button_left(canvas, "TD -"); - elements_button_right(canvas, "+ TD"); - } - //canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h deleted file mode 100644 index 9e44478f7..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_draw(Canvas* canvas, iBtnFuzzerState* context); diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c deleted file mode 100644 index f3217f65e..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "ibtnfuzzer_scene_select_field.h" - -void ibtnfuzzer_center_displayed_key(iBtnFuzzerState* context, uint8_t index) { - char key_cstr[25]; - uint8_t key_len = 25; - uint8_t str_index = (index * 3); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - int key_index = 0; - - if(context->proto == DS1990) { - key_len = 25; - } - if(context->proto == Metakom) { - key_len = 13; - } - if(context->proto == Cyfral) { - key_len = 7; - } - - for(uint8_t i = 0; i < data_len; i++) { - if(context->data[i] < 9) { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "0%X ", context->data[i]); - } else { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "%X ", context->data[i]); - } - } - - char display_menu[17] = { - 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; - - if(index > 1) { - display_menu[0] = key_cstr[str_index - 6]; - display_menu[1] = key_cstr[str_index - 5]; - } else { - display_menu[0] = ' '; - display_menu[1] = ' '; - } - - if(index > 0) { - display_menu[3] = key_cstr[str_index - 3]; - display_menu[4] = key_cstr[str_index - 2]; - } else { - display_menu[3] = ' '; - display_menu[4] = ' '; - } - - display_menu[7] = key_cstr[str_index]; - display_menu[8] = key_cstr[str_index + 1]; - - if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { - display_menu[11] = key_cstr[str_index + 3]; - display_menu[12] = key_cstr[str_index + 4]; - } else { - display_menu[11] = ' '; - display_menu[12] = ' '; - } - - if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { - display_menu[14] = key_cstr[str_index + 6]; - display_menu[15] = key_cstr[str_index + 7]; - } else { - display_menu[14] = ' '; - display_menu[15] = ' '; - } - - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, display_menu); -} - -void ibtnfuzzer_scene_select_field_on_enter(iBtnFuzzerState* context) { - furi_string_reset(context->notification_msg); -} - -void ibtnfuzzer_scene_select_field_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_select_field_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_select_field_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - const char* key_cstr = furi_string_get_cstr(context->data_str); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - - // don't look, it's ugly but I'm a python dev so... - uint8_t nb_bytes = 0; - for(uint8_t i = 0; i < strlen(key_cstr); i++) { - if(' ' == key_cstr[i]) { - nb_bytes++; - } - } - - switch(event.key) { - case InputKeyDown: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] - 1); - } - } - break; - case InputKeyUp: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] + 1); - } - } - break; - case InputKeyLeft: - if(context->key_index > 0) { - context->key_index = context->key_index - 1; - } - break; - case InputKeyRight: - if(context->key_index < nb_bytes) { - context->key_index = context->key_index + 1; - } - break; - case InputKeyOk: - furi_string_reset(context->notification_msg); - context->current_scene = SceneAttack; - break; - case InputKeyBack: - context->key_index = 0; - furi_string_reset(context->notification_msg); - context->current_scene = SceneSelectFile; - break; - default: - break; - } - FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); - } - } -} - -void ibtnfuzzer_scene_select_field_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 12, 5, AlignLeft, AlignTop, "Left and right: select byte"); - canvas_draw_str_aligned(canvas, 12, 15, AlignLeft, AlignTop, "Up and down: adjust byte"); - - char msg_index[18]; - canvas_set_font(canvas, FontPrimary); - snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); - - ibtnfuzzer_center_displayed_key(context, context->key_index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignTop, furi_string_get_cstr(context->notification_msg)); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h deleted file mode 100644 index b6c684c3b..000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_select_field_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_draw(Canvas* canvas, iBtnFuzzerState* context); -void center_displayed_key(iBtnFuzzerState* context, uint8_t index); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/multi_fuzzer/application.fam similarity index 81% rename from applications/external/pacs_fuzzer/application.fam rename to applications/external/multi_fuzzer/application.fam index 6098322ac..a0ce1be0a 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/multi_fuzzer/application.fam @@ -1,6 +1,6 @@ App( - appid="pacs_fuzzer_ibtn", - name="iButton Fuzzer [B]", + appid="fuzzer_ibtn", + name="iButton Fuzzer", apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_ibtn", requires=[ @@ -11,8 +11,8 @@ App( "notification", ], stack_size=2 * 1024, - fap_icon="icons/rfid_10px.png", - fap_category="Debug", + fap_icon="icons/ibutt_10px.png", + fap_category="Tools", fap_private_libs=[ Lib( name="worker", @@ -24,8 +24,8 @@ App( ) App( - appid="pacs_fuzzer_rfid", - name="RFID Fuzzer [B]", + appid="fuzzer_rfid", + name="RFID Fuzzer", apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_rfid", requires=[ @@ -36,8 +36,8 @@ App( "notification", ], stack_size=2 * 1024, - fap_icon="icons/125_10px.png", - fap_category="Debug", + fap_icon="icons/rfid_10px.png", + fap_category="Tools", fap_private_libs=[ Lib( name="worker", diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/multi_fuzzer/fuzzer.c similarity index 100% rename from applications/external/pacs_fuzzer/fuzzer.c rename to applications/external/multi_fuzzer/fuzzer.c diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/multi_fuzzer/fuzzer_i.h similarity index 100% rename from applications/external/pacs_fuzzer/fuzzer_i.h rename to applications/external/multi_fuzzer/fuzzer_i.h diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/multi_fuzzer/helpers/fuzzer_custom_event.h similarity index 100% rename from applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h rename to applications/external/multi_fuzzer/helpers/fuzzer_custom_event.h diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/multi_fuzzer/helpers/fuzzer_types.h similarity index 100% rename from applications/external/pacs_fuzzer/helpers/fuzzer_types.h rename to applications/external/multi_fuzzer/helpers/fuzzer_types.h diff --git a/applications/external/flipfrid/images/125_10px.png b/applications/external/multi_fuzzer/icons/125_10px.png similarity index 100% rename from applications/external/flipfrid/images/125_10px.png rename to applications/external/multi_fuzzer/icons/125_10px.png diff --git a/applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png b/applications/external/multi_fuzzer/icons/ButtonLeft_4x7.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png rename to applications/external/multi_fuzzer/icons/ButtonLeft_4x7.png diff --git a/applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png b/applications/external/multi_fuzzer/icons/ButtonRight_4x7.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png rename to applications/external/multi_fuzzer/icons/ButtonRight_4x7.png diff --git a/applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png b/applications/external/multi_fuzzer/icons/Ok_btn_9x9.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png rename to applications/external/multi_fuzzer/icons/Ok_btn_9x9.png diff --git a/applications/external/pacs_fuzzer/icons/Pin_arrow_up_7x9.png b/applications/external/multi_fuzzer/icons/Pin_arrow_up_7x9.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/Pin_arrow_up_7x9.png rename to applications/external/multi_fuzzer/icons/Pin_arrow_up_7x9.png diff --git a/applications/external/pacs_fuzzer/icons/Pin_back_arrow_10x8.png b/applications/external/multi_fuzzer/icons/Pin_back_arrow_10x8.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/Pin_back_arrow_10x8.png rename to applications/external/multi_fuzzer/icons/Pin_back_arrow_10x8.png diff --git a/applications/external/ibtn_fuzzer/ibutt_10px.png b/applications/external/multi_fuzzer/icons/ibutt_10px.png similarity index 100% rename from applications/external/ibtn_fuzzer/ibutt_10px.png rename to applications/external/multi_fuzzer/icons/ibutt_10px.png diff --git a/applications/external/flipfrid/rfid_10px.png b/applications/external/multi_fuzzer/icons/rfid_10px.png similarity index 100% rename from applications/external/flipfrid/rfid_10px.png rename to applications/external/multi_fuzzer/icons/rfid_10px.png diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/multi_fuzzer/lib/worker/fake_worker.c similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/fake_worker.c rename to applications/external/multi_fuzzer/lib/worker/fake_worker.c diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/multi_fuzzer/lib/worker/fake_worker.h similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/fake_worker.h rename to applications/external/multi_fuzzer/lib/worker/fake_worker.h diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/multi_fuzzer/lib/worker/protocol.c similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/protocol.c rename to applications/external/multi_fuzzer/lib/worker/protocol.c diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/multi_fuzzer/lib/worker/protocol.h similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/protocol.h rename to applications/external/multi_fuzzer/lib/worker/protocol.h diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/multi_fuzzer/lib/worker/protocol_i.h similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/protocol_i.h rename to applications/external/multi_fuzzer/lib/worker/protocol_i.h diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene.c similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene.c rename to applications/external/multi_fuzzer/scenes/fuzzer_scene.c diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene.h b/applications/external/multi_fuzzer/scenes/fuzzer_scene.h similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene.h rename to applications/external/multi_fuzzer/scenes/fuzzer_scene.h diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_attack.c similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c rename to applications/external/multi_fuzzer/scenes/fuzzer_scene_attack.c diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/multi_fuzzer/scenes/fuzzer_scene_config.h similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h rename to applications/external/multi_fuzzer/scenes/fuzzer_scene_config.h diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_field_editor.c similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c rename to applications/external/multi_fuzzer/scenes/fuzzer_scene_field_editor.c diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_main.c similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c rename to applications/external/multi_fuzzer/scenes/fuzzer_scene_main.c diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/multi_fuzzer/todo.md similarity index 100% rename from applications/external/pacs_fuzzer/todo.md rename to applications/external/multi_fuzzer/todo.md diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/multi_fuzzer/views/attack.c similarity index 100% rename from applications/external/pacs_fuzzer/views/attack.c rename to applications/external/multi_fuzzer/views/attack.c diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/multi_fuzzer/views/attack.h similarity index 100% rename from applications/external/pacs_fuzzer/views/attack.h rename to applications/external/multi_fuzzer/views/attack.h diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/multi_fuzzer/views/field_editor.c similarity index 100% rename from applications/external/pacs_fuzzer/views/field_editor.c rename to applications/external/multi_fuzzer/views/field_editor.c diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/multi_fuzzer/views/field_editor.h similarity index 100% rename from applications/external/pacs_fuzzer/views/field_editor.h rename to applications/external/multi_fuzzer/views/field_editor.h diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/multi_fuzzer/views/main_menu.c similarity index 100% rename from applications/external/pacs_fuzzer/views/main_menu.c rename to applications/external/multi_fuzzer/views/main_menu.c diff --git a/applications/external/pacs_fuzzer/views/main_menu.h b/applications/external/multi_fuzzer/views/main_menu.h similarity index 100% rename from applications/external/pacs_fuzzer/views/main_menu.h rename to applications/external/multi_fuzzer/views/main_menu.h diff --git a/applications/external/pacs_fuzzer/icons/125_10px.png b/applications/external/pacs_fuzzer/icons/125_10px.png deleted file mode 100644 index ce01284a2c1f3eb413f581b84f1fb3f3a2a7223b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bWZjP>yH&963)5S4_<9hOs!iI<>&pI=m5)bW_|craZ$KesPZ!4!j_b)kjvx52z42i= z^Wk##wtdVzTiGS{99;2>!TC2M!yZeXz}?LkD}l;YOI#yLQW8s2t&)pUffR$0fsvuE zfvK*cNr<75m9c@9v4ysQft7*5`ikN&C>nC}Q!>*kp&E>VdO{3LtqcsU49p-Jly36? Qy~)7f>FVdQ&MBb@0C$~I0{{R3 diff --git a/applications/external/pacs_fuzzer/icons/rfid_10px.png b/applications/external/pacs_fuzzer/icons/rfid_10px.png deleted file mode 100644 index 8097f477552333f695733baf98f72e52ae840206..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2389 zcmcIleQXnD7{6^!#>U44<0gnZixCj6_tCqycdm@C?Y2%j%BZlR1g>}Qb?4f<$dYAbBp?=L>s9H44zi+G=z@ z$+GSR2r_^Bu5APL?}u;SJTN7D;oJYQXJbzy83Gd~}8wyVLJ?VRDO5w9Zzw8@g?;6Z|IeLEIrsbQiG#zUHZ2RGh zXD*aUd-8i4_PpADs0}HdxBi3jq2Z#Q^P&Dr^r?|WO%j;%cM#(P)0|Mc8; z{t8g`*A^{V)i?clE}f@2H&CjT}F< z?2jues!qd7PS)z04FoBfX?^mLy)Tp_$Rt&cG?`7IrJSH9?7UT9dorQHXauRON@~2& z3QRN#VzT0~4fhY&P+9cYRxu$Wr1?OLT-T+86?9@-1c8#I)9z+Pr-LUJp%g)pI7#A^ z8>2{$U^#~a&AepaJ-|V!`|Vrt9lHFc42XX!YK-a5tz}b zn0yjbjJa6^KQIJc)=XJdPz#Zds%@sn2DzpWkAYyf`V1Rfhy zjlu{PBk2f5aSnoGTnh;YM-b`I5OjjboBR#IOoSjf8osX&Rz+FroJeRW#03?@@6WtU}<<5`k-GmIOPTuus$(zJDPkQf=2B!Xdi7eP5vyx@MnDzsUZu=b~oE2;v- z$W@bzGC*KNw}3fBr-TVK?Z%=6L1S(pkqisLNim1EOqXHr@bS^87Ap}ViVmI>S)RcJ zh9WV*(*TPy(I`d}G$F90;3Qy6q1W>I)VQjLR1sDe;)?<&sd|Ek{*e=W4B(m)v)l~P z;VJ5514`GK>5mm)eP$Jx(Uj>pUa-9Gu?d#Q0Om>GmdB{x#CWFnceDTqI*$11FhiBh z4qgY|7_9WanhU=fd4q2spSnP~C5On1n8Y&u^S%9C@(-&evej?~Ro2+Su!z(GxDJJ~1-%>yfuE?__2!EG(Q-w?lmFd+!g0U&StM zdu5hu`a91J8fzAOad#fF`0WbM%BH;qRp;*CJ^0}3t1p(6l^kTQe)IdHQfq1l0}cMe I)$5-48`f?Vpa1{> From 98e0f5a8f6552c02dae3e4964cd721f17be0aa9d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 02:59:59 +0300 Subject: [PATCH 094/370] Update docs --- ReadMe.md | 2 +- documentation/SubGHzRemoteProg.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index f9e759e0b..0c545682c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -96,7 +96,7 @@ Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMaste - CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - Nice Flor S -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)] -- Keeloq: BFT Mitto [External seed calculation required (For info contact me in Discord: Nano#8998)] -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) +- Keeloq: BFT Mitto -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - Star Line - Security+ v1 & v2 (encoders was made in OFW) diff --git a/documentation/SubGHzRemoteProg.md b/documentation/SubGHzRemoteProg.md index 7cd8ea68a..e53319b2f 100644 --- a/documentation/SubGHzRemoteProg.md +++ b/documentation/SubGHzRemoteProg.md @@ -60,6 +60,8 @@ Watch this videos to learn more (videos in Russian language): https://www.youtub ## BFT Mitto +How to create new remote and bind it to receiver (will not conflict with original remotes): + 1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> BFT Mitto 433Mhz 2. Open your new remote file 3. You need to be in minimum 3 meters to receiver @@ -68,6 +70,32 @@ Watch this videos to learn more (videos in Russian language): https://www.youtub 6. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec 7. Done? +OR + +1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> BFT Mitto 433Mhz +2. Open your new remote file +3. Open your receiver board box +4. **Watch this video**: https://www.youtube.com/watch?v=5QXMBKI_-Ls +5. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec -> Will act like holding Button 1 & 2 on original remote as shown on video +6. Done? + +-- + +How to get seed to make full clone of your remote (**will conflict with original remote!!!!!**): + +**WARNING!!!! This method can desync your original remote, please avoid using it! It can be used in rare cases like when your remote works poorly or has broken buttons and you want to replace it with flipper** + +1. Open `Read` in SubGHz on your flipper +2. (ONLY FOR ORIGINAL REMOTES) Hold all buttons on your remote at same time, example -> for 2 button remote - press them both at same time and hold OR press hidden button on back of remote with a pin or paper clip +3. You will receive signal on your flipper, open that signal and see `Fix:` value, it should start from `F` like `F00F1C9B` +4. If `Fix:` is showing first `F` see `Hop:` value -> This is your remote Seed +5. Write down Hop value +6. Press button on your remote that you want to clone and receive its signal on your flipper +7. Open and write down `Fix:` value where first digit will be same as your button ID `Btn:` +8. Create new remote using BFT Mitto [Manual] - Enter FIX from step 7, enter counter `FF F9`, enter seed from step 5 +9. Using counter values like `FF F9` can help bypassing current original remote counter value, and in result it also can fully desync original remote, only one remote can work at same time using this method +10. Throw away your original remote since now it needs to be re-added into receiver board :C + ## CAME Atomo 1. Use google to find instructions - `how to program new CAME Atomo remote into receiver` From 5496fa29584e1882c4563966698e01c5a1b9a680 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 03:00:53 +0300 Subject: [PATCH 095/370] upd anims --- .ci_files/anims_ofw.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.ci_files/anims_ofw.txt b/.ci_files/anims_ofw.txt index 4fa369f82..e3d3314c0 100644 --- a/.ci_files/anims_ofw.txt +++ b/.ci_files/anims_ofw.txt @@ -120,6 +120,13 @@ Min level: 2 Max level: 2 Weight: 3 +Name: L2_Dj_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 3 +Weight: 4 + Name: L3_Furippa3_128x64 Min butthurt: 0 Max butthurt: 6 From 81f42afed0474a4c580ff698563c072eeab1b58a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:11:38 +0100 Subject: [PATCH 096/370] Connection go much brrr --- applications/main/bad_kb/bad_kb_app.c | 96 +++---------------- applications/main/bad_kb/bad_kb_app.h | 5 - .../main/bad_kb/helpers/ducky_script.c | 95 ++++++++++++++++++ .../main/bad_kb/helpers/ducky_script.h | 13 +++ 4 files changed, 119 insertions(+), 90 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index c2d7e1fe3..1bcfc57f8 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -125,74 +125,6 @@ void bad_kb_config_switch_remember_mode(BadKbApp* app) { bad_kb_reload_worker(app); } -int32_t bad_kb_connection_init(BadKbApp* app) { - app->prev_config.usb_mode = furi_hal_usb_get_config(); - furi_hal_usb_set_config(NULL, NULL); - - strcpy( - app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - memcpy( - app->prev_config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); - app->prev_config.bt_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); - - bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(app->bt); - bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); - if(strcmp(app->config.bt_name, "") != 0) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); - } - if(app->bt_remember) { - furi_hal_bt_set_profile_mac_addr( - FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - } else { - if(memcmp( - app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) != - 0) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); - } - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - } - bt_set_profile(app->bt, BtProfileHidKeyboard); - if(strcmp(app->config.bt_name, "") == 0) { - strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - } - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == - 0) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); - } - if(app->is_bt) { - furi_hal_bt_start_advertising(); - if(app->bt_remember) { - bt_enable_peer_key_update(app->bt); - } else { - bt_disable_peer_key_update(app->bt); - } - } else { - furi_hal_bt_stop_advertising(); - } - - return 0; -} - -void bad_kb_connection_deinit(BadKbApp* app) { - furi_hal_usb_set_config(app->prev_config.usb_mode, NULL); - - bt_disconnect(app->bt); - bt_keys_storage_set_default_path(app->bt); - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mode); - bt_set_profile(app->bt, BtProfileSerial); - bt_enable_peer_key_update(app->bt); -} - BadKbApp* bad_kb_app_alloc(char* arg) { BadKbApp* app = malloc(sizeof(BadKbApp)); @@ -258,23 +190,17 @@ BadKbApp* bad_kb_app_alloc(char* arg) { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - if(furi_hal_usb_is_locked()) { - app->error = BadKbAppErrorCloseRpc; - app->conn_init_thread = NULL; - scene_manager_next_scene(app->scene_manager, BadKbSceneError); + app->conn_mode = BadKbConnModeNone; + app->conn_init_thread = + furi_thread_alloc_ex("BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_conn_refresh, app); + furi_thread_start(app->conn_init_thread); + if(!furi_string_empty(app->file_path)) { + app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); + bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); + scene_manager_next_scene(app->scene_manager, BadKbSceneWork); } else { - app->conn_init_thread = furi_thread_alloc_ex( - "BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_connection_init, app); - furi_thread_start(app->conn_init_thread); - if(!furi_string_empty(app->file_path)) { - app->bad_kb_script = - bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); - bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); - scene_manager_next_scene(app->scene_manager, BadKbSceneWork); - } else { - furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER); - scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); - } + furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); } return app; @@ -317,8 +243,8 @@ void bad_kb_app_free(BadKbApp* app) { if(app->conn_init_thread) { furi_thread_join(app->conn_init_thread); furi_thread_free(app->conn_init_thread); - bad_kb_connection_deinit(app); } + bad_kb_conn_reset(app); // Close records furi_record_close(RECORD_GUI); diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 20bc87ce4..662e05181 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -1,7 +1,6 @@ #pragma once #include "bad_kb_app.h" -#include "bad_kb_paths.h" #include "scenes/bad_kb_scene.h" #include "helpers/ducky_script.h" @@ -29,7 +28,3 @@ typedef enum { } BadKbAppView; void bad_kb_config_switch_remember_mode(BadKbApp* app); - -int32_t bad_kb_connection_init(BadKbApp* app); - -void bad_kb_connection_deinit(BadKbApp* app); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 57742a464..8ffb1430f 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -566,6 +566,93 @@ static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) { return flags; } +int32_t bad_kb_conn_refresh(BadKbApp* app) { + bool bt = app->is_bt; + + if(app->conn_mode != BadKbConnModeNone) bad_kb_conn_reset(app); + + if(bt) { + strcpy( + app->prev_config.bt_name, + furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + memcpy( + app->prev_config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_ADDRESS_LEN); + app->prev_config.bt_mode = + furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); + bt_timeout = bt_hid_delays[LevelRssi39_0]; + bt_disconnect(app->bt); + bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); + if(strcmp(app->config.bt_name, "") != 0) { + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); + } + if(app->bt_remember) { + furi_hal_bt_set_profile_mac_addr( + FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + } else { + if(memcmp( + app->config.bt_mac, + (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, + BAD_KB_MAC_ADDRESS_LEN) != 0) { + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); + } + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); + } + furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); + if(strcmp(app->config.bt_name, "") == 0) { + strcpy( + app->config.bt_name, + furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + } + if(memcmp( + app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == + 0) { + memcpy( + app->config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_ADDRESS_LEN); + } + if(app->bt_remember) { + bt_enable_peer_key_update(app->bt); + } else { + bt_disable_peer_key_update(app->bt); + } + furi_hal_bt_start_advertising(); + + app->conn_mode = BadKbConnModeBt; + + } else { + app->prev_config.usb_mode = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(NULL, NULL)); + + app->conn_mode = BadKbConnModeUsb; + } + + return 0; +} + +void bad_kb_conn_reset(BadKbApp* app) { + if(app->conn_mode == BadKbConnModeBt) { + bt_disconnect(app->bt); + bt_keys_storage_set_default_path(app->bt); + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, app->prev_config.bt_mode); + furi_check(bt_set_profile(app->bt, BtProfileSerial)); + bt_enable_peer_key_update(app->bt); + + } else if(app->conn_mode == BadKbConnModeUsb) { + furi_check(furi_hal_usb_set_config(app->prev_config.usb_mode, NULL)); + } + + app->conn_mode = BadKbConnModeNone; +} + static int32_t bad_kb_worker(void* context) { BadKbScript* bad_kb = context; @@ -579,9 +666,17 @@ static int32_t bad_kb_worker(void* context) { bad_kb->line_prev = furi_string_alloc(); bad_kb->string_print = furi_string_alloc(); + if(bad_kb->app->conn_init_thread) { + furi_thread_join(bad_kb->app->conn_init_thread); + furi_thread_free(bad_kb->app->conn_init_thread); + bad_kb->app->conn_init_thread = NULL; + } + if(bad_kb->bt) { + if(bad_kb->app->conn_mode != BadKbConnModeBt) bad_kb_conn_refresh(bad_kb->app); bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb); } else { + if(bad_kb->app->conn_mode != BadKbConnModeUsb) bad_kb_conn_refresh(bad_kb->app); furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, bad_kb); } diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index ecdf8fc02..f4b9a4924 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -14,6 +14,7 @@ extern "C" { #include #include #include "../views/bad_kb_view.h" +#include "../bad_kb_paths.h" #define FILE_BUFFER_LEN 16 @@ -127,6 +128,12 @@ typedef struct { GapPairing bt_mode; } BadKbConfig; +typedef enum { + BadKbConnModeNone, + BadKbConnModeUsb, + BadKbConnModeBt, +} BadKbConnMode; + struct BadKbApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -149,6 +156,8 @@ struct BadKbApp { bool bt_remember; BadKbConfig config; BadKbConfig prev_config; + + BadKbConnMode conn_mode; FuriThread* conn_init_thread; FuriThread* switch_mode_thread; }; @@ -157,6 +166,10 @@ int32_t bad_kb_config_switch_mode(BadKbApp* app); void bad_kb_config_refresh_menu(BadKbApp* app); +int32_t bad_kb_conn_refresh(BadKbApp* app); + +void bad_kb_conn_reset(BadKbApp* app); + #ifdef __cplusplus } #endif From cefad3dde63609a5b4106a34b35b4bb3ec858d83 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:32:24 +0100 Subject: [PATCH 097/370] Begone, uselessness! --- .../main/bad_kb/helpers/ducky_script.h | 1 - .../main/bad_kb/scenes/bad_kb_scene_error.c | 26 ------------------- 2 files changed, 27 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index f4b9a4924..9e4ed77f4 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -118,7 +118,6 @@ extern const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN]; typedef enum { BadKbAppErrorNoFiles, - BadKbAppErrorCloseRpc, } BadKbAppError; typedef struct { diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_error.c b/applications/main/bad_kb/scenes/bad_kb_scene_error.c index 53393016c..eac04c448 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_error.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_error.c @@ -1,5 +1,4 @@ #include "../bad_kb_app.h" -#include static void bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { @@ -26,31 +25,6 @@ void bad_kb_scene_error_on_enter(void* context) { "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); widget_add_button_element( app->widget, GuiButtonTypeLeft, "Back", bad_kb_scene_error_event_callback, app); - } else if(app->error == BadKbAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->is_nsfw) { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Pull out from\nPC or phone to\nuse me like this."); - } else { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } } view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewError); From 1a45bf4f85e728aa8daaea54c275649b80817865 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:32:49 +0100 Subject: [PATCH 098/370] Format --- .../external/multi_fuzzer/application.fam | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/applications/external/multi_fuzzer/application.fam b/applications/external/multi_fuzzer/application.fam index a0ce1be0a..6cd7969e1 100644 --- a/applications/external/multi_fuzzer/application.fam +++ b/applications/external/multi_fuzzer/application.fam @@ -4,21 +4,21 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_ibtn", requires=[ - "gui", + "gui", "storage", - "dialogs", - "input", + "dialogs", + "input", "notification", ], stack_size=2 * 1024, fap_icon="icons/ibutt_10px.png", fap_category="Tools", fap_private_libs=[ - Lib( - name="worker", - cdefines=["IBUTTON_PROTOCOL"], - ), - ], + Lib( + name="worker", + cdefines=["IBUTTON_PROTOCOL"], + ), + ], fap_icon_assets="icons", fap_icon_assets_symbol="fuzzer", ) @@ -29,21 +29,21 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_rfid", requires=[ - "gui", + "gui", "storage", - "dialogs", - "input", + "dialogs", + "input", "notification", ], stack_size=2 * 1024, fap_icon="icons/rfid_10px.png", fap_category="Tools", fap_private_libs=[ - Lib( - name="worker", - cdefines=["RFID_125_PROTOCOL"], - ), - ], + Lib( + name="worker", + cdefines=["RFID_125_PROTOCOL"], + ), + ], fap_icon_assets="icons", fap_icon_assets_symbol="fuzzer", ) From 436194e6c79ce143dee9ee25f3581812908cd346 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 8 Jun 2023 08:47:27 +0400 Subject: [PATCH 099/370] [FL-3346] fbt: added Flipper selection when multiple are connected over USB (#2723) * fbt: added Flipper selection when multiple are connected over USB * scripts: serial_cli: added --port (-p) option --- SConstruct | 6 ++++-- scripts/fbt_tools/fbt_dist.py | 2 +- scripts/fbt_tools/fbt_extapps.py | 3 +-- scripts/flipper/utils/cdc.py | 2 +- scripts/serial_cli.py | 6 +++++- scripts/ufbt/SConstruct | 4 ++-- scripts/ufbt/commandline.scons | 5 +++++ site_scons/commandline.scons | 5 +++++ 8 files changed, 24 insertions(+), 9 deletions(-) diff --git a/SConstruct b/SConstruct index e2568287d..b51154f70 100644 --- a/SConstruct +++ b/SConstruct @@ -171,7 +171,7 @@ distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_ fap_deploy = distenv.PhonyTarget( "fap_deploy", - "${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps", + "${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps", source=Dir("#/assets/resources/apps"), ) @@ -323,7 +323,9 @@ distenv.PhonyTarget( ) # Start Flipper CLI via PySerial's miniterm -distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py") +distenv.PhonyTarget( + "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}" +) # Find blackmagic probe diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index a43d62e9d..e47898bd9 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -132,7 +132,7 @@ def generate(env): "UsbInstall": Builder( action=[ Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf' + '${PYTHON3} "${SELFUPDATE_SCRIPT}" -p ${FLIP_PORT} ${UPDATE_BUNDLE_DIR}/update.fuf' ), Touch("${TARGET}"), ] diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 1a1bad29e..16d5dcbab 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -431,7 +431,7 @@ def AddAppLaunchTarget(env, appname, launch_target_name): # print(deploy_sources, flipp_dist_paths) env.PhonyTarget( launch_target_name, - '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', + '${PYTHON3} "${APP_RUN_SCRIPT}" -p ${FLIP_PORT} ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', source=deploy_sources, FLIPPER_FILE_TARGETS=flipp_dist_paths, EXTRA_ARGS=run_script_extra_ars, @@ -443,7 +443,6 @@ def generate(env, **kw): env.SetDefault( EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", - STORAGE_SCRIPT="${FBT_SCRIPT_DIR}/storage.py", ) if not env["VERBOSE"]: env.SetDefault( diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index 7c7351670..956408859 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -6,7 +6,7 @@ def resolve_port(logger, portname: str = "auto"): if portname != "auto": return portname # Try guessing - flippers = list(list_ports.grep("flip")) + flippers = list(list_ports.grep("flip_")) if len(flippers) == 1: flipper = flippers[0] logger.info(f"Using {flipper.serial_number} on {flipper.device}") diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py index 6dae68be6..8e35d57fa 100644 --- a/scripts/serial_cli.py +++ b/scripts/serial_cli.py @@ -1,3 +1,4 @@ +import argparse import logging import os import subprocess @@ -8,7 +9,10 @@ from flipper.utils.cdc import resolve_port def main(): logger = logging.getLogger() - if not (port := resolve_port(logger, "auto")): + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="CDC Port", default="auto") + args = parser.parse_args() + if not (port := resolve_port(logger, args.port)): logger.error("Is Flipper connected via USB and not in DFU mode?") return 1 subprocess.call( diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index a1acd270a..8812a4e55 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -342,7 +342,7 @@ else: appenv.PhonyTarget( "cli", - '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"', + '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', ) # Linter @@ -469,7 +469,7 @@ if dolphin_src_dir.exists(): ) dist_env.PhonyTarget( "dolphin_ext", - '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py send "${SOURCE}" /ext/dolphin', + '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send "${SOURCE}" /ext/dolphin', source=ufbt_build_dir.Dir("dolphin"), ) else: diff --git a/scripts/ufbt/commandline.scons b/scripts/ufbt/commandline.scons index a9b91bbca..349b4ef25 100644 --- a/scripts/ufbt/commandline.scons +++ b/scripts/ufbt/commandline.scons @@ -71,6 +71,11 @@ vars.AddVariables( validator=PathVariable.PathIsDir, default="", ), + ( + "FLIP_PORT", + "CDC Port of Flipper to use, if multiple are connected", + "auto", + ), ) Return("vars") diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index e31927c59..b70b5cff5 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -243,6 +243,11 @@ vars.AddVariables( " app can check what version it is being built for.", "Official", ), + ( + "FLIP_PORT", + "Full port name of Flipper to use, if multiple Flippers are connected", + "auto", + ), ) Return("vars") From c186d2b0ccb44ab9b2adb1d0aad1efd9219492ac Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 8 Jun 2023 07:30:53 +0200 Subject: [PATCH 100/370] added ISO15693 (NfcV) reading, saving, emulating and revealing from privacy mode (unlock) (#2316) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added support for ISO15693 (NfcV) emulation, added support for reading SLIX tags * SLIX: fixed crash situation when an invalid password was requested * ISO15693: show emulate menu when opening file * rename NfcV emulate scene to match other NfcV names * optimize allocation size for signals * ISO15693: further optimizations of allocation and free code * ISO15693: reduce latency on state machine reset * respond with block security status when option flag is set * increased maximum memory size to match standard added security status handling/load/save added SELECT/QUIET handling more fine grained allocation routines and checks fix memset sizes * added "Listen NfcV Reader" to sniff traffic from reader to card * added correct description to delete menu * also added DSFID/AFI handling and locking * increase sniff log size * scale NfcV frequency a bit, add echo mode, fix signal level at the end * use symbolic modulated/unmodulated GPIO levels * honor AFI field, decrease verbosity and removed debug code * refactor defines for less namespace pollution by using NFCV_ prefixes * correct an oversight that original cards return an generic error when addressing outside block range * use inverse modulation, increasing readable range significantly * rework and better document nfc chip initialization * nfcv code review fixes * Disable accidentally left on signal debug gpio output * Improve NFCV Read/Info GUIs. Authored by @xMasterX, committed by @nvx * Fix crash that occurs when you exit from NFCV emulation and start it again. Authored by @xMasterX, committed by @nvx * Remove delay from emulation loop. This improves compatibility when the reader is Android. * Lib: digital signal debug output pin info Co-authored-by: Tiernan Messmer Co-authored-by: MX <10697207+xMasterX@users.noreply.github.com> Co-authored-by: gornekich Co-authored-by: あく --- .../main/nfc/helpers/nfc_custom_event.h | 2 + applications/main/nfc/nfc.c | 3 + .../main/nfc/scenes/nfc_scene_config.h | 7 + .../main/nfc/scenes/nfc_scene_delete.c | 7 +- .../main/nfc/scenes/nfc_scene_extra_actions.c | 20 + .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 162 +- .../main/nfc/scenes/nfc_scene_nfcv_emulate.c | 169 ++ .../nfc/scenes/nfc_scene_nfcv_key_input.c | 48 + .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 68 + .../nfc/scenes/nfc_scene_nfcv_read_success.c | 94 ++ .../main/nfc/scenes/nfc_scene_nfcv_sniff.c | 155 ++ .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 154 ++ .../nfc/scenes/nfc_scene_nfcv_unlock_menu.c | 60 + applications/main/nfc/scenes/nfc_scene_read.c | 5 + applications/main/nfc/scenes/nfc_scene_rpc.c | 7 + .../main/nfc/scenes/nfc_scene_saved_menu.c | 3 + firmware/targets/f7/api_symbols.csv | 8 + lib/digital_signal/digital_signal.c | 13 +- lib/nfc/nfc_device.c | 376 ++++- lib/nfc/nfc_device.h | 4 + lib/nfc/nfc_worker.c | 245 ++- lib/nfc/nfc_worker.h | 11 + lib/nfc/nfc_worker_i.h | 2 + lib/nfc/protocols/nfcv.c | 1398 +++++++++++++++++ lib/nfc/protocols/nfcv.h | 291 ++++ lib/nfc/protocols/slix.c | 412 +++++ lib/nfc/protocols/slix.h | 46 + 27 files changed, 3737 insertions(+), 33 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_menu.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c create mode 100644 lib/nfc/protocols/nfcv.c create mode 100644 lib/nfc/protocols/nfcv.h create mode 100644 lib/nfc/protocols/slix.c create mode 100644 lib/nfc/protocols/slix.h diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 4227a5b14..aa932a3d8 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -12,4 +12,6 @@ enum NfcCustomEvent { NfcCustomEventDictAttackSkip, NfcCustomEventRpcLoad, NfcCustomEventRpcSessionClose, + NfcCustomEventUpdateLog, + NfcCustomEventSaveShadow, }; diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 4540f5d9f..f68b7f2f2 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -290,6 +290,9 @@ int32_t nfc_app(void* p) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9da07dfd..f11d14798 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -14,6 +14,13 @@ ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_menu, NfcaMenu) +ADD_SCENE(nfc, nfcv_menu, NfcVMenu) +ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) +ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) +ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) +ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate) +ADD_SCENE(nfc, nfcv_sniff, NfcVSniff) +ADD_SCENE(nfc, nfcv_read_success, NfcVReadSuccess) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index cbb52bfd0..0808db45a 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -31,6 +31,8 @@ void nfc_scene_delete_on_enter(void* context) { nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); NfcProtocol protocol = nfc->dev->dev_data.protocol; + const char* nfc_type = "NFC-A"; + if(protocol == NfcDeviceProtocolEMV) { furi_string_set(temp_str, "EMV bank card"); } else if(protocol == NfcDeviceProtocolMifareUl) { @@ -39,12 +41,15 @@ void nfc_scene_delete_on_enter(void* context) { furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_set(temp_str, "MIFARE DESFire"); + } else if(protocol == NfcDeviceProtocolNfcV) { + furi_string_set(temp_str, "ISO15693 tag"); + nfc_type = "NFC-V"; } else { furi_string_set(temp_str, "Unknown ISO tag"); } widget_add_string_element( nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); + widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type); furi_string_free(temp_str); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 66aaf5a26..7f5bc7e75 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -4,6 +4,8 @@ enum SubmenuIndex { SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, + SubmenuIndexNfcVUnlock, + SubmenuIndexNfcVSniff, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -34,6 +36,18 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_add_item( + submenu, + "Unlock SLIX-L", + SubmenuIndexNfcVUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Listen NfcV Reader", + SubmenuIndexNfcVSniff, + nfc_scene_extra_actions_submenu_callback, + nfc); submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); @@ -58,6 +72,12 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + consumed = true; + } else if(event.event == SubmenuIndexNfcVSniff) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff); + consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index b44bb5e64..eb2f939c6 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -41,19 +41,165 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n"); + } else if(protocol == NfcDeviceProtocolNfcV) { + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } // Set tag iso data - char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; - furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - furi_string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + if(protocol == NfcDeviceProtocolNfcV) { + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + + furi_string_cat_printf(temp_str, "UID:\n"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + + furi_string_cat_printf( + temp_str, + "DSFID: %02X %s\n", + nfcv_data->dsfid, + (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "AFI: %02X %s\n", + nfcv_data->afi, + (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); + furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "Type: Plain\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "Type: SLIX\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " EAS %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Read %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); + furi_string_cat_printf( + temp_str, + " Write %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); + furi_string_cat_printf( + temp_str, + " Privacy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); + furi_string_cat_printf( + temp_str, + " Destroy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); + furi_string_cat_printf( + temp_str, + " EAS %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Privacy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); + furi_string_cat_printf( + temp_str, + " Destroy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); + furi_string_cat_printf( + temp_str, + " EAS %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "Type: SLIX2\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Read %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); + furi_string_cat_printf( + temp_str, + " Write %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); + furi_string_cat_printf( + temp_str, + " Privacy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); + furi_string_cat_printf( + temp_str, + " Destroy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); + furi_string_cat_printf( + temp_str, + " EAS %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + + furi_string_cat_printf( + temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size); + + int maxBlocks = nfcv_data->block_num; + if(maxBlocks > 32) { + maxBlocks = 32; + furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks); + } + + for(int block = 0; block < maxBlocks; block++) { + const char* status = (nfcv_data->security_status[block] & 0x01) ? "(lck)" : ""; + for(int pos = 0; pos < nfcv_data->block_size; pos++) { + furi_string_cat_printf( + temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); + } + furi_string_cat_printf(temp_str, " %s\n", status); + } + + } else { + char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf( + temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); + furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); } - furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); - furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); // Set application specific data if(protocol == NfcDeviceProtocolMifareDesfire) { @@ -139,6 +285,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(protocol == NfcDeviceProtocolMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); + } else if(protocol == NfcDeviceProtocolNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c new file mode 100644 index 000000000..3dd7c460b --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -0,0 +1,169 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) + +enum { + NfcSceneNfcVEmulateStateWidget, + NfcSceneNfcVEmulateStateTextBox, +}; + +bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + } + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_emulate_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_emulate_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); + widget_add_string_multiline_element( + widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V"); + if(strcmp(nfc->dev->dev_name, "")) { + furi_string_printf(info_str, "%s", nfc->dev->dev_name); + } else { + for(uint8_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(info_str, "%02X ", data->uid[i]); + } + } + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 52, 40, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_emulate_widget_callback, nfc); + } + } +} + +void nfc_scene_nfcv_emulate_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_emulate_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_nfcv_emulate_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVEmulate); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_emulate_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateTextBox); + } + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_emulate_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c new file mode 100644 index 000000000..cc53c4dcb --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -0,0 +1,48 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_nfcv_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + memcpy(data->key_privacy, nfc->byte_input_store, 4); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_nfcv_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter The Password In Hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_nfcv_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_nfcv_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c new file mode 100644 index 000000000..7c6780b7c --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -0,0 +1,68 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexEmulate, + SubmenuIndexInfo, +}; + +void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfcv_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatNfcV; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_nfcv_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c new file mode 100644 index 000000000..bdf7692cc --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c @@ -0,0 +1,94 @@ +#include "../nfc_i.h" + +void nfc_scene_nfcv_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_read_success_on_enter(void* context) { + Nfc* nfc = context; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + // Setup view + Widget* widget = nfc->widget; + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfcv_read_success_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_nfcv_read_success_widget_callback, nfc); + + FuriString* temp_str = furi_string_alloc(); + + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + + widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + notification_message_block(nfc->notifications, &sequence_set_green_255); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_nfcv_read_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + + return consumed; +} + +void nfc_scene_nfcv_read_success_on_exit(void* context) { + Nfc* nfc = context; + + notification_message_block(nfc->notifications, &sequence_reset_green); + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c new file mode 100644 index 000000000..2c0f17981 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c @@ -0,0 +1,155 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (800) + +enum { + NfcSceneNfcVSniffStateWidget, + NfcSceneNfcVSniffStateTextBox, +}; + +bool nfc_scene_nfcv_sniff_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_sniff_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_sniff_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_sniff_widget_config(Nfc* nfc, bool data_received) { + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Listen NfcV"); + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_sniff_widget_callback, nfc); + } +} + +void nfc_scene_nfcv_sniff_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_sniff_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVSniff, + &nfc->dev->dev_data, + nfc_scene_nfcv_sniff_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_sniff_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVSniff); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_sniff_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVSniffStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_sniff_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c new file mode 100644 index 000000000..26de304de --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -0,0 +1,154 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneNfcVUnlockStateIdle, + NfcSceneNfcVUnlockStateDetecting, + NfcSceneNfcVUnlockStateUnlocked, + NfcSceneNfcVUnlockStateAlreadyUnlocked, + NfcSceneNfcVUnlockStateNotSupportedCard, +} NfcSceneNfcVUnlockState; + +static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + if(event == NfcWorkerEventNfcVPassKey) { + memcpy(data->key_privacy, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_nfcv_unlock_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { + FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data); + NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); + + uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock); + if(curr_state != state) { + Popup* popup = nfc->popup; + if(state == NfcSceneNfcVUnlockStateDetecting) { + popup_reset(popup); + popup_set_text( + popup, "Put figurine on\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneNfcVUnlockStateUnlocked) { + popup_reset(popup); + + if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { + snprintf( + nfc->dev->dev_name, + sizeof(nfc->dev->dev_name), + "SLIX_%02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + nfc->dev->format = NfcDeviceSaveFormatNfcV; + + if(nfc_save_file(nfc)) { + popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); + } else { + popup_set_header( + popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop); + } + } else { + popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); + } + + notification_message(nfc->notifications, &sequence_single_vibro); + //notification_message(nfc->notifications, &sequence_success); + + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { + popup_reset(popup); + + popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) { + popup_reset(popup); + popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop); + popup_set_text(popup, nfcv_data->error, 4, 22, AlignLeft, AlignTop); + popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state); + } +} + +void nfc_scene_nfcv_unlock_on_enter(void* context) { + Nfc* nfc = context; + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVUnlockAndSave, + &nfc->dev->dev_data, + nfc_scene_nfcv_unlock_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventAborted) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneNfcVUnlockMenu); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c new file mode 100644 index 000000000..9c4c81fbd --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexNfcVUnlockMenuManual, + SubmenuIndexNfcVUnlockMenuTonieBox, +}; + +void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + submenu_add_item( + submenu, + "Enter PWD Manually", + SubmenuIndexNfcVUnlockMenuManual, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As TonieBox", + SubmenuIndexNfcVUnlockMenuTonieBox, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcVUnlockMenuManual) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 938f2da67..5a1814b6c 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -68,6 +68,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; + } else if(event.event == NfcWorkerEventReadNfcV) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); // Set unlock password input to 0xFFFFFFFF only on fresh read diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index 60d01a30d..d06ee7564 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { &nfc->dev->dev_data, nfc_scene_rpc_emulate_callback, nfc); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_rpc_emulate_callback, + nfc); } else { nfc_worker_start( nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 4573cdc45..8412c17bc 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) { } else if( (nfc->dev->format == NfcDeviceSaveFormatMifareUl && mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || + nfc->dev->format == NfcDeviceSaveFormatNfcV || nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); @@ -118,6 +119,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b6eb8c765..e2de36836 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2090,6 +2090,14 @@ Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* +Function,+,nfcv_emu_deinit,void,NfcVData* +Function,+,nfcv_emu_init,void,"FuriHalNfcDevData*, NfcVData*" +Function,+,nfcv_emu_loop,_Bool,"FuriHalNfcTxRxContext*, FuriHalNfcDevData*, NfcVData*, uint32_t" +Function,+,nfcv_emu_send,void,"FuriHalNfcTxRxContext*, NfcVData*, uint8_t*, uint8_t, NfcVSendFlags, uint32_t" +Function,-,nfcv_inventory,ReturnCode,uint8_t* +Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" +Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*" +Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 94dbeeab9..39aa9cbc6 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -9,7 +9,7 @@ #include /* must be on bank B */ -#define DEBUG_OUTPUT gpio_ext_pb3 +// For debugging purposes use `--extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3` fbt option struct ReloadBuffer { uint32_t* buffer; /* DMA ringbuffer */ @@ -194,9 +194,9 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { uint32_t bit_set = internals->gpio->pin; uint32_t bit_reset = internals->gpio->pin << 16; -#ifdef DEBUG_OUTPUT - bit_set |= DEBUG_OUTPUT.pin; - bit_reset |= DEBUG_OUTPUT.pin << 16; +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + bit_set |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin; + bit_reset |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << 16; #endif if(signal->start_level) { @@ -540,8 +540,9 @@ bool digital_sequence_send(DigitalSequence* sequence) { struct ReloadBuffer* dma_buffer = sequence->dma_buffer; furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -#ifdef DEBUG_OUTPUT - furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + furi_hal_gpio_init( + &DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); #endif if(sequence->bake) { diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 517913070..9646c262e 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -58,6 +58,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare Classic"); } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { furi_string_set(format_string, "Mifare DESFire"); + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + furi_string_set(format_string, "ISO15693"); } else { furi_string_set(format_string, "Unknown"); } @@ -93,6 +95,11 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; return true; } + if(furi_string_start_with_str(format_string, "ISO15693")) { + dev->format = NfcDeviceSaveFormatNfcV; + dev->dev_data.protocol = NfcDeviceProtocolNfcV; + return true; + } return false; } @@ -650,7 +657,327 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } -// Leave for backward compatibility +static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_uint8 = 0; + + if(!flipper_format_write_comment_cstr(file, "Data Storage Format Identifier")) break; + if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_write_comment_cstr(file, "Application Family Identifier")) break; + if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + temp_uint32 = data->block_num; + if(!flipper_format_write_comment_cstr(file, "Number of memory blocks, usually 0 to 256")) + break; + if(!flipper_format_write_uint32(file, "Block Count", &temp_uint32, 1)) break; + if(!flipper_format_write_comment_cstr(file, "Size of a single memory block, usually 4")) + break; + if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_write_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + if(!flipper_format_write_comment_cstr( + file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) + break; + if(!flipper_format_write_hex( + file, "Security Status", data->security_status, 1 + data->block_num)) + break; + if(!flipper_format_write_comment_cstr( + file, + "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) + break; + temp_uint8 = (uint8_t)data->sub_type; + if(!flipper_format_write_hex(file, "Subtype", &temp_uint8, 1)) break; + + switch(data->sub_type) { + case NfcVTypePlain: + if(!flipper_format_write_comment_cstr(file, "End of ISO15693 parameters")) break; + saved = true; + break; + case NfcVTypeSlix: + saved = nfc_device_save_slix_data(file, dev); + break; + case NfcVTypeSlixS: + saved = nfc_device_save_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + saved = nfc_device_save_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + saved = nfc_device_save_slix2_data(file, dev); + break; + default: + break; + } + } while(false); + + return saved; +} + +bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + memset(data, 0x00, sizeof(NfcVData)); + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_value = 0; + + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; + data->block_num = temp_uint32; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_read_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + + /* optional, as added later */ + if(flipper_format_key_exist(file, "Security Status")) { + if(!flipper_format_read_hex( + file, "Security Status", data->security_status, 1 + data->block_num)) + break; + } + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; + data->sub_type = temp_value; + + switch(data->sub_type) { + case NfcVTypePlain: + parsed = true; + break; + case NfcVTypeSlix: + parsed = nfc_device_load_slix_data(file, dev); + break; + case NfcVTypeSlixS: + parsed = nfc_device_load_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + parsed = nfc_device_load_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + parsed = nfc_device_load_slix2_data(file, dev); + break; + default: + break; + } + } while(false); + + return parsed; +} + +static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + EmvData* data = &dev->dev_data.emv_data; + uint32_t data_temp = 0; + + do { + // Write Bank card specific data + if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break; + if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break; + if(!flipper_format_write_string_cstr(file, "Name", data->name)) break; + if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break; + if(data->exp_mon) { + uint8_t exp_data[2] = {data->exp_mon, data->exp_year}; + if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break; + } + if(data->country_code) { + data_temp = data->country_code; + if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break; + } + if(data->currency_code) { + data_temp = data->currency_code; + if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break; + } + saved = true; + } while(false); + + return saved; +} + bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; EmvData* data = &dev->dev_data.emv_data; @@ -1069,23 +1396,32 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; // Write nfc device type if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic")) + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693")) break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) - break; + // Write UID + if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; - if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + + if(dev->format != NfcDeviceSaveFormatNfcV) { + // Write ATQA, SAK + if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + } + // Save more data if necessary if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_save_mifare_ul_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_save_nfcv_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatBankCard) { + if(!nfc_device_save_bank_card_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { // Save data if(!nfc_device_save_mifare_classic_data(file, dev)) break; @@ -1160,18 +1496,20 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_parse_format_string(dev, temp_str)) break; // Read and parse UID, ATQA and SAK if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; - if(!(data_cnt == 4 || data_cnt == 7)) break; + if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break; data->uid_len = data_cnt; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(version == version_with_lsb_atqa) { - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; - } else { - uint8_t atqa[2] = {}; - if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; - data->atqa[0] = atqa[1]; - data->atqa[1] = atqa[0]; + if(dev->format != NfcDeviceSaveFormatNfcV) { + if(version == version_with_lsb_atqa) { + if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + } else { + uint8_t atqa[2] = {}; + if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; + data->atqa[0] = atqa[1]; + data->atqa[1] = atqa[0]; + } + if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; } - if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; // Load CUID uint8_t* cuid_start = data->uid; if(data->uid_len == 7) { @@ -1186,6 +1524,8 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_load_mifare_classic_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_load_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_load_nfcv_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_load_bank_card_data(file, dev)) break; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index df37ec3df..20df4f891 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -31,6 +32,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, + NfcDeviceProtocolNfcV } NfcProtocol; typedef enum { @@ -39,6 +41,7 @@ typedef enum { NfcDeviceSaveFormatMifareUl, NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareDesfire, + NfcDeviceSaveFormatNfcV, } NfcDeviceSaveFormat; typedef struct { @@ -73,6 +76,7 @@ typedef struct { MfUltralightData mf_ul_data; MfClassicData mf_classic_data; MifareDesfireData mf_df_data; + NfcVData nfcv_data; }; FuriString* parsed_data; } NfcDeviceData; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index daa8fee59..293f1ce70 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -111,6 +111,14 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_classic_dict_attack(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { nfc_worker_analyze_reader(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + nfc_worker_nfcv_emulate(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVSniff) { + nfc_worker_nfcv_sniff(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { + nfc_worker_nfcv_unlock(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + nfc_worker_nfcv_unlock(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -118,6 +126,236 @@ int32_t nfc_worker_task(void* context) { return 0; } +static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + NfcVReader reader = {}; + + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + furi_hal_nfc_sleep(); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; + if(!nfcv_read_card(&reader, nfc_data, nfcv_data)) break; + + read_success = true; + } while(false); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} + +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_emu_init(nfc_data, nfcv_data); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + if(nfcv_data->modified) { + nfc_worker->callback(NfcWorkerEventNfcVContentChanged, nfc_worker->context); + nfcv_data->modified = false; + } + } + } + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_data->sub_type = NfcVTypeSniff; + nfcv_emu_init(nfc_data, nfcv_data); + + while(nfc_worker->state == NfcWorkerStateNfcVSniff) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + } + } + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + FuriHalNfcTxRxContext tx_rx = {}; + uint8_t* key_data = nfcv_data->sub_data.slix.key_privacy; + uint32_t key = 0; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + furi_hal_nfc_sleep(); + + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || + (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); + furi_hal_nfc_ll_poll(); + if(furi_hal_nfc_ll_set_mode( + FuriHalNfcModePollNfcv, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48) != + FuriHalNfcReturnOk) { + break; + } + + furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER); + furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER); + furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); + furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCV); + + FURI_LOG_D(TAG, "Detect presence"); + ReturnCode ret = slix_get_random(nfcv_data); + + if(ret == ERR_NONE) { + /* there is some chip, responding with a RAND */ + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + FURI_LOG_D(TAG, " Chip detected. In privacy?"); + ret = nfcv_inventory(NULL); + + if(ret == ERR_NONE) { + /* chip is also visible, so no action required, just save */ + if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + NfcVReader reader = {}; + + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + FURI_LOG_D(TAG, " => failed, wait for chip to disappear."); + snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + } else { + FURI_LOG_D(TAG, " => success, wait for chip to disappear."); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + } else { + FURI_LOG_D(TAG, " => success, wait for chip to disappear."); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + FURI_LOG_D(TAG, " => chip is already visible, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + key_data[0] = 0; + key_data[1] = 0; + key_data[2] = 0; + key_data[3] = 0; + + } else { + /* chip is invisible, try to unlock */ + FURI_LOG_D(TAG, " chip is invisible, unlocking"); + + if(nfcv_data->auth_method == NfcVAuthMethodManual) { + key |= key_data[0] << 24; + key |= key_data[1] << 16; + key |= key_data[2] << 8; + key |= key_data[3] << 0; + + ret = slix_unlock(nfcv_data, 4); + } else { + key = 0x7FFD6E5B; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_unlock(nfcv_data, 4); + + if(ret != ERR_NONE) { + /* main key failed, trying second one */ + FURI_LOG_D(TAG, " trying second key after resetting"); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + if(slix_get_random(nfcv_data) != ERR_NONE) { + FURI_LOG_D(TAG, " reset failed"); + } + + key = 0x0F0F0F0F; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_unlock(nfcv_data, 4); + } + } + if(ret != ERR_NONE) { + /* unlock failed */ + FURI_LOG_D(TAG, " => failed, wait for chip to disappear."); + snprintf( + nfcv_data->error, sizeof(nfcv_data->error), "Passwords not\naccepted"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + /* wait for disappearing */ + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + } + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + } + + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -317,7 +555,12 @@ void nfc_worker_read(NfcWorker* nfc_worker) { event = NfcWorkerEventReadUidNfcF; break; } else if(nfc_data->type == FuriHalNfcTypeV) { - event = NfcWorkerEventReadUidNfcV; + FURI_LOG_I(TAG, "NfcV detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { + FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); + } + event = NfcWorkerEventReadNfcV; break; } } else { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 8e993fc6a..722f14857 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -18,6 +18,10 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVEmulate, + NfcWorkerStateNfcVUnlock, + NfcWorkerStateNfcVUnlockAndSave, + NfcWorkerStateNfcVSniff, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -39,6 +43,7 @@ typedef enum { NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, + NfcWorkerEventReadNfcV, // Nfc worker common events NfcWorkerEventSuccess, @@ -69,6 +74,9 @@ typedef enum { // Mifare Ultralight events NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key + NfcWorkerEventNfcVCommandExecuted, + NfcWorkerEventNfcVContentChanged, } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); @@ -87,3 +95,6 @@ void nfc_worker_start( void* context); void nfc_worker_stop(NfcWorker* nfc_worker); +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker); +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker); \ No newline at end of file diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 701ecb90c..b678573ec 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include struct NfcWorker { diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c new file mode 100644 index 000000000..ac818b7a4 --- /dev/null +++ b/lib/nfc/protocols/nfcv.c @@ -0,0 +1,1398 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfcv.h" +#include "nfc_util.h" +#include "slix.h" + +#define TAG "NfcV" + +/* macros to map "modulate field" flag to GPIO level */ +#define GPIO_LEVEL_MODULATED NFCV_LOAD_MODULATION_POLARITY +#define GPIO_LEVEL_UNMODULATED (!GPIO_LEVEL_MODULATED) + +/* timing macros */ +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + +ReturnCode nfcv_inventory(uint8_t* uid) { + uint16_t received = 0; + rfalNfcvInventoryRes res; + ReturnCode ret = ERR_NONE; + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + if(uid != NULL) { + memcpy(uid, res.UID, NFCV_UID_LENGTH); + } + } + + return ret; +} + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) { + UNUSED(reader); + + uint16_t received = 0; + for(size_t block = 0; block < nfcv_data->block_num; block++) { + uint8_t rxBuf[32]; + FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1)); + + ReturnCode ret = ERR_NONE; + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + ret = rfalNfcvPollerReadSingleBlock( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + if(ret != ERR_NONE) { + FURI_LOG_D(TAG, "failed to read: %d", ret); + return ret; + } + memcpy( + &(nfcv_data->data[block * nfcv_data->block_size]), &rxBuf[1], nfcv_data->block_size); + FURI_LOG_D( + TAG, + " %02X %02X %02X %02X", + nfcv_data->data[block * nfcv_data->block_size + 0], + nfcv_data->data[block * nfcv_data->block_size + 1], + nfcv_data->data[block * nfcv_data->block_size + 2], + nfcv_data->data[block * nfcv_data->block_size + 3]); + } + + return ERR_NONE; +} + +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + uint8_t rxBuf[32]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerGetSystemInformation( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + nfc_data->type = FuriHalNfcTypeV; + nfc_data->uid_len = NFCV_UID_LENGTH; + /* UID is stored reversed in this response */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + nfc_data->uid[pos] = rxBuf[2 + (NFCV_UID_LENGTH - 1 - pos)]; + } + nfcv_data->dsfid = rxBuf[NFCV_UID_LENGTH + 2]; + nfcv_data->afi = rxBuf[NFCV_UID_LENGTH + 3]; + nfcv_data->block_num = rxBuf[NFCV_UID_LENGTH + 4] + 1; + nfcv_data->block_size = rxBuf[NFCV_UID_LENGTH + 5] + 1; + nfcv_data->ic_ref = rxBuf[NFCV_UID_LENGTH + 6]; + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + FURI_LOG_D( + TAG, + " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", + nfcv_data->dsfid, + nfcv_data->afi, + nfcv_data->block_num, + nfcv_data->block_size, + nfcv_data->ic_ref); + return ret; + } + FURI_LOG_D(TAG, "Failed: %d", ret); + + return ret; +} + +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(reader); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } + + if(nfcv_read_blocks(reader, nfcv_data) != ERR_NONE) { + return false; + } + + if(slix_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX detected"); + nfcv_data->sub_type = NfcVTypeSlix; + } else if(slix2_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX2 detected"); + nfcv_data->sub_type = NfcVTypeSlix2; + } else if(slix_s_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-S detected"); + nfcv_data->sub_type = NfcVTypeSlixS; + } else if(slix_l_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-L detected"); + nfcv_data->sub_type = NfcVTypeSlixL; + } else { + nfcv_data->sub_type = NfcVTypePlain; + } + + return true; +} + +void nfcv_crc(uint8_t* data, uint32_t length) { + uint32_t reg = 0xFFFF; + + for(size_t i = 0; i < length; i++) { + reg = reg ^ ((uint32_t)data[i]); + for(size_t j = 0; j < 8; j++) { + if(reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + uint16_t crc = ~(uint16_t)(reg & 0xffff); + + data[length + 0] = crc & 0xFF; + data[length + 1] = crc >> 8; +} + +void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { + furi_assert(signals); + + if(signals->nfcv_resp_one) { + digital_signal_free(signals->nfcv_resp_one); + } + if(signals->nfcv_resp_zero) { + digital_signal_free(signals->nfcv_resp_zero); + } + if(signals->nfcv_resp_sof) { + digital_signal_free(signals->nfcv_resp_sof); + } + if(signals->nfcv_resp_eof) { + digital_signal_free(signals->nfcv_resp_eof); + } + signals->nfcv_resp_one = NULL; + signals->nfcv_resp_zero = NULL; + signals->nfcv_resp_sof = NULL; + signals->nfcv_resp_eof = NULL; +} + +bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { + furi_assert(air); + furi_assert(signals); + + bool success = true; + + if(!signals->nfcv_resp_one) { + /* logical one: unmodulated then 8 pulses */ + signals->nfcv_resp_one = digital_signal_alloc( + slowdown * (air->nfcv_resp_unmod->edge_cnt + 8 * air->nfcv_resp_pulse->edge_cnt)); + if(!signals->nfcv_resp_one) { + return false; + } + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 8; i++) { + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); + } + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_zero) { + /* logical zero: 8 pulses then unmodulated */ + signals->nfcv_resp_zero = digital_signal_alloc( + slowdown * (8 * air->nfcv_resp_pulse->edge_cnt + air->nfcv_resp_unmod->edge_cnt)); + if(!signals->nfcv_resp_zero) { + return false; + } + for(size_t i = 0; i < slowdown * 8; i++) { + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); + } + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); + } + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_sof) { + /* SOF: unmodulated, 24 pulses, logic 1 */ + signals->nfcv_resp_sof = digital_signal_alloc( + slowdown * (3 * air->nfcv_resp_unmod->edge_cnt + 24 * air->nfcv_resp_pulse->edge_cnt) + + signals->nfcv_resp_one->edge_cnt); + if(!signals->nfcv_resp_sof) { + return false; + } + for(size_t i = 0; i < slowdown * 3; i++) { + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 24; i++) { + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); + } + success &= digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_eof) { + /* EOF: logic 0, 24 pulses, unmodulated */ + signals->nfcv_resp_eof = digital_signal_alloc( + signals->nfcv_resp_zero->edge_cnt + + slowdown * (24 * air->nfcv_resp_pulse->edge_cnt + 3 * air->nfcv_resp_unmod->edge_cnt) + + air->nfcv_resp_unmod->edge_cnt); + if(!signals->nfcv_resp_eof) { + return false; + } + success &= digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); + for(size_t i = 0; i < slowdown * 23; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); + } + /* we don't want to add the last level as we just want a transition to "unmodulated" again */ + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_half_pulse); + } + } + return success; +} + +bool nfcv_emu_alloc(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + if(!nfcv_data->frame) { + nfcv_data->frame = malloc(NFCV_FRAMESIZE_MAX); + if(!nfcv_data->frame) { + return false; + } + } + + if(!nfcv_data->emu_air.nfcv_signal) { + /* assuming max frame length is 255 bytes */ + nfcv_data->emu_air.nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); + if(!nfcv_data->emu_air.nfcv_signal) { + return false; + } + } + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + /* unmodulated 256/fc or 1024/fc signal as building block */ + nfcv_data->emu_air.nfcv_resp_unmod = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + return false; + } + nfcv_data->emu_air.nfcv_resp_unmod->start_level = GPIO_LEVEL_UNMODULATED; + nfcv_data->emu_air.nfcv_resp_unmod->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_unmod->edge_cnt = 1; + } + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_pulse->start_level = GPIO_LEVEL_MODULATED; + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[1] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_cnt = 2; + } + + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_half_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_half_pulse->start_level = GPIO_LEVEL_MODULATED; + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_cnt = 1; + } + + bool success = true; + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); + + if(!success) { + FURI_LOG_E(TAG, "Failed to allocate signals"); + return false; + } + + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_SOF, + nfcv_data->emu_air.signals_high.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT0, + nfcv_data->emu_air.signals_high.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT1, + nfcv_data->emu_air.signals_high.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_EOF, + nfcv_data->emu_air.signals_high.nfcv_resp_eof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_SOF, + nfcv_data->emu_air.signals_low.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT0, + nfcv_data->emu_air.signals_low.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT1, + nfcv_data->emu_air.signals_low.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_EOF, + nfcv_data->emu_air.signals_low.nfcv_resp_eof); + + return true; +} + +void nfcv_emu_free(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + if(nfcv_data->frame) { + free(nfcv_data->frame); + } + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + } + if(nfcv_data->emu_air.nfcv_resp_unmod) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); + } + if(nfcv_data->emu_air.nfcv_resp_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); + } + if(nfcv_data->emu_air.nfcv_resp_half_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_half_pulse); + } + if(nfcv_data->emu_air.nfcv_signal) { + digital_sequence_free(nfcv_data->emu_air.nfcv_signal); + } + if(nfcv_data->emu_air.reader_signal) { + // Stop pulse reader and disable bus before free + pulse_reader_stop(nfcv_data->emu_air.reader_signal); + // Free pulse reader + pulse_reader_free(nfcv_data->emu_air.reader_signal); + } + + nfcv_data->frame = NULL; + nfcv_data->emu_air.nfcv_resp_unmod = NULL; + nfcv_data->emu_air.nfcv_resp_pulse = NULL; + nfcv_data->emu_air.nfcv_resp_half_pulse = NULL; + nfcv_data->emu_air.nfcv_signal = NULL; + nfcv_data->emu_air.reader_signal = NULL; + + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_high); + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_low); +} + +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time) { + furi_assert(tx_rx); + furi_assert(nfcv); + + /* picked default value (0) to match the most common format */ + if(!flags) { + flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | + NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; + } + + if(flags & NfcVSendFlagsCrc) { + nfcv_crc(data, length); + length += 2; + } + + /* depending on the request flags, send with high or low rate */ + uint32_t bit0 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT0 : NFCV_SIG_LOW_BIT0; + uint32_t bit1 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT1 : NFCV_SIG_LOW_BIT1; + uint32_t sof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_SOF : NFCV_SIG_LOW_SOF; + uint32_t eof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_EOF : NFCV_SIG_LOW_EOF; + + digital_sequence_clear(nfcv->emu_air.nfcv_signal); + + if(flags & NfcVSendFlagsSof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, sof); + } + + for(int bit_total = 0; bit_total < length * 8; bit_total++) { + uint32_t byte_pos = bit_total / 8; + uint32_t bit_pos = bit_total % 8; + uint8_t bit_val = 0x01 << bit_pos; + + digital_sequence_add(nfcv->emu_air.nfcv_signal, (data[byte_pos] & bit_val) ? bit1 : bit0); + } + + if(flags & NfcVSendFlagsEof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, eof); + } + + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + digital_sequence_set_sendtime(nfcv->emu_air.nfcv_signal, send_time); + digital_sequence_send(nfcv->emu_air.nfcv_signal); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(data, length * 8, false, tx_rx->sniff_context); + } +} + +static void nfcv_revuidcpy(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + dst[pos] = src[NFCV_UID_LENGTH - 1 - pos]; + } +} + +static int nfcv_revuidcmp(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + if(dst[pos] != src[NFCV_UID_LENGTH - 1 - pos]) { + return 1; + } + } + return 0; +} + +void nfcv_emu_handle_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + if(nfcv_data->echo_mode) { + nfcv_emu_send( + tx_rx, + nfcv_data, + nfcv_data->frame, + nfcv_data->frame_length, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); + ctx->response_flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof; + ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4380); + + if(ctx->flags & NFCV_REQ_FLAG_DATA_RATE) { + ctx->response_flags |= NfcVSendFlagsHighRate; + } + if(ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) { + ctx->response_flags |= NfcVSendFlagsTwoSubcarrier; + } + + if(ctx->payload_offset + 2 > nfcv_data->frame_length) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "command 0x%02X, but packet is too short", ctx->command); +#endif + return; + } + + /* standard behavior is implemented */ + if(ctx->addressed) { + uint8_t* address = &nfcv_data->frame[ctx->address_offset]; + if(nfcv_revuidcmp(address, nfc_data->uid)) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", ctx->command); + FURI_LOG_D( + TAG, + " dest: %02X%02X%02X%02X%02X%02X%02X%02X", + address[7], + address[6], + address[5], + address[4], + address[3], + address[2], + address[1], + address[0]); + FURI_LOG_D( + TAG, + " our UID: %02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); +#endif + return; + } + } + + if(ctx->selected && !nfcv_data->selected) { +#ifdef NFCV_VERBOSE + FURI_LOG_D( + TAG, + "selected card shall execute command 0x%02X, but we were not selected", + ctx->command); +#endif + return; + } + + /* then give control to the card subtype specific protocol filter */ + if(ctx->emu_protocol_filter != NULL) { + if(ctx->emu_protocol_filter(tx_rx, nfc_data, nfcv_data)) { + if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE + FURI_LOG_D( + TAG, "Received command %s (handled by filter)", nfcv_data->last_command); +#endif + } + return; + } + } + + switch(ctx->command) { + case NFCV_CMD_INVENTORY: { + bool respond = false; + + if(ctx->flags & NFCV_REQ_FLAG_AFI) { + uint8_t afi = nfcv_data->frame[ctx->payload_offset]; + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + respond = true; + } + + if(!nfcv_data->quiet && respond) { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + } else { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (quiet)"); + } + break; + } + + case NFCV_CMD_STAY_QUIET: { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET"); + nfcv_data->quiet = true; + break; + } + + case NFCV_CMD_LOCK_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + nfcv_data->security_status[block] |= 0x01; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK BLOCK %d", block); + break; + } + + case NFCV_CMD_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->dsfid = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE DSFID %02X", id); + break; + } + + case NFCV_CMD_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->afi = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE AFI %02X", id); + break; + } + + case NFCV_CMD_LOCK_DSFID: { + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->security_status[0] |= NfcVLockBitDsfid; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK DSFID"); + break; + } + + case NFCV_CMD_LOCK_AFI: { + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->security_status[0] |= NfcVLockBitAfi; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK AFI"); + break; + } + + case NFCV_CMD_SELECT: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->selected = true; + nfcv_data->quiet = false; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SELECT"); + break; + } + + case NFCV_CMD_RESET_TO_READY: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->quiet = false; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "RESET_TO_READY"); + break; + } + + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + if(block + blocks <= nfcv_data->block_num) { + uint8_t buffer_pos = 0; + + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + + for(int block_index = 0; block_index < blocks; block_index++) { + int block_current = block + block_index; + /* prepend security status */ + if(ctx->flags & NFCV_REQ_FLAG_OPTION) { + ctx->response_buffer[buffer_pos++] = + nfcv_data->security_status[1 + block_current]; + } + /* then the data block */ + memcpy( + &ctx->response_buffer[buffer_pos], + &nfcv_data->data[nfcv_data->block_size * block_current], + nfcv_data->block_size); + buffer_pos += nfcv_data->block_size; + } + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); + + break; + } + + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { + uint8_t blocks = 1; + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t data_pos = ctx->payload_offset + 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[data_pos] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[data_pos]; + uint32_t data_len = nfcv_data->block_size * blocks; + + if((block + blocks) <= nfcv_data->block_num && + (data_pos + data_len + 2) == nfcv_data->frame_length) { + ctx->response_buffer[0] = NFCV_NOERROR; + memcpy( + &nfcv_data->data[nfcv_data->block_size * block], + &nfcv_data->frame[data_pos], + data_len); + nfcv_data->modified = true; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE MULTI BLOCK %d, %d blocks", + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE BLOCK %d <- %02X %02X %02X %02X", + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case NFCV_CMD_GET_SYSTEM_INFO: { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = NFCV_SYSINFO_FLAG_DSFID | NFCV_SYSINFO_FLAG_AFI | + NFCV_SYSINFO_FLAG_MEMSIZE | NFCV_SYSINFO_FLAG_ICREF; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; /* DSFID */ + ctx->response_buffer[buffer_pos++] = nfcv_data->afi; /* AFI */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_num - 1; /* number of blocks */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_size - 1; /* block size */ + ctx->response_buffer[buffer_pos++] = nfcv_data->ic_ref; /* IC reference */ + + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); + break; + } + + case NFCV_CMD_CUST_ECHO_MODE: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->echo_mode = true; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO mode"); + break; + } + + case NFCV_CMD_CUST_ECHO_DATA: { + nfcv_emu_send( + tx_rx, + nfcv_data, + &nfcv_data->frame[ctx->payload_offset], + nfcv_data->frame_length - ctx->payload_offset - 2, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "unsupported: %02X", + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); +#endif + } +} + +void nfcv_emu_sniff_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); + + char flags_string[5]; + + snprintf( + flags_string, + 5, + "%c%c%c%d", + (ctx->flags & NFCV_REQ_FLAG_INVENTORY) ? + 'I' : + (ctx->addressed ? 'A' : (ctx->selected ? 'S' : '*')), + ctx->advanced ? 'X' : ' ', + (ctx->flags & NFCV_REQ_FLAG_DATA_RATE) ? 'h' : 'l', + (ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) ? 2 : 1); + + switch(ctx->command) { + case NFCV_CMD_INVENTORY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s INVENTORY", flags_string); + break; + } + + case NFCV_CMD_STAY_QUIET: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s STAYQUIET", flags_string); + nfcv_data->quiet = true; + break; + } + + case NFCV_CMD_LOCK_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK %d", + flags_string, + block); + break; + } + + case NFCV_CMD_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR DSFID %d", + flags_string, + id); + break; + } + + case NFCV_CMD_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR AFI %d", + flags_string, + id); + break; + } + + case NFCV_CMD_LOCK_DSFID: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK DSFID", + flags_string); + break; + } + + case NFCV_CMD_LOCK_AFI: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s LOCK AFI", flags_string); + break; + } + + case NFCV_CMD_SELECT: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s SELECT", flags_string); + break; + } + + case NFCV_CMD_RESET_TO_READY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s RESET", flags_string); + break; + } + + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s READ %d cnt: %d", + flags_string, + block, + blocks); + + break; + } + + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + uint8_t data_pos = 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos]; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d, cnd %d", + flags_string, + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d %02X %02X %02X %02X", + flags_string, + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case NFCV_CMD_GET_SYSTEM_INFO: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s SYSTEMINFO", + flags_string); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s unsupported: %02X", + flags_string, + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + } +} + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(!nfcv_emu_alloc(nfcv_data)) { + FURI_LOG_E(TAG, "Failed to allocate structures"); + nfcv_data->ready = false; + return; + } + + strcpy(nfcv_data->last_command, ""); + nfcv_data->quiet = false; + nfcv_data->selected = false; + nfcv_data->modified = false; + + /* everything is initialized */ + nfcv_data->ready = true; + + /* ensure the GPIO is already in unmodulated state */ + furi_hal_gpio_init(&gpio_spi_r_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + + rfal_platform_spi_acquire(); + /* stop operation to configure for transparent and passive mode */ + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + /* set enable, rx_enable and field detector enable */ + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + /* explicitely set the modulation resistor in case system config changes for some reason */ + st25r3916WriteRegister( + ST25R3916_REG_PT_MOD, + (0 << ST25R3916_REG_PT_MOD_ptm_res_shift) | (15 << ST25R3916_REG_PT_MOD_pt_res_shift)); + /* target mode: target, other fields do not have any effect as we use transparent mode */ + st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ); + /* let us modulate the field using MOSI, read ASK modulation using IRQ */ + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + /* if not set already, initialize the default protocol handler */ + if(!nfcv_data->emu_protocol_ctx) { + nfcv_data->emu_protocol_ctx = malloc(sizeof(NfcVEmuProtocolCtx)); + if(nfcv_data->sub_type == NfcVTypeSniff) { + nfcv_data->emu_protocol_handler = &nfcv_emu_sniff_packet; + } else { + nfcv_data->emu_protocol_handler = &nfcv_emu_handle_packet; + } + } + + FURI_LOG_D(TAG, "Starting NfcV emulation"); + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + switch(nfcv_data->sub_type) { + case NfcVTypeSlixL: + FURI_LOG_D(TAG, " Card type: SLIX-L"); + slix_l_prepare(nfcv_data); + break; + case NfcVTypeSlixS: + FURI_LOG_D(TAG, " Card type: SLIX-S"); + slix_s_prepare(nfcv_data); + break; + case NfcVTypeSlix2: + FURI_LOG_D(TAG, " Card type: SLIX2"); + slix2_prepare(nfcv_data); + break; + case NfcVTypeSlix: + FURI_LOG_D(TAG, " Card type: SLIX"); + slix_prepare(nfcv_data); + break; + case NfcVTypePlain: + FURI_LOG_D(TAG, " Card type: Plain"); + break; + case NfcVTypeSniff: + FURI_LOG_D(TAG, " Card type: Sniffing"); + break; + } + + /* allocate a 512 edge buffer, more than enough */ + nfcv_data->emu_air.reader_signal = + pulse_reader_alloc(&gpio_nfc_irq_rfid_pull, NFCV_PULSE_BUFFER); + /* timebase shall be 1 ns */ + pulse_reader_set_timebase(nfcv_data->emu_air.reader_signal, PulseReaderUnitNanosecond); + /* and configure to already calculate the number of bits */ + pulse_reader_set_bittime(nfcv_data->emu_air.reader_signal, NFCV_PULSE_DURATION_NS); + /* this IO is fed into the µC via a diode, so we need a pulldown */ + pulse_reader_set_pull(nfcv_data->emu_air.reader_signal, GpioPullDown); + + /* start sampling */ + pulse_reader_start(nfcv_data->emu_air.reader_signal); +} + +void nfcv_emu_deinit(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + nfcv_emu_free(nfcv_data); + + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + nfcv_data->emu_protocol_ctx = NULL; + } + + /* set registers back to how we found them */ + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0x00); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x08); + rfal_platform_spi_release(); +} + +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + bool ret = false; + uint32_t frame_state = NFCV_FRAME_STATE_SOF1; + uint32_t periods_previous = 0; + uint32_t frame_pos = 0; + uint32_t byte_value = 0; + uint32_t bits_received = 0; + uint32_t timeout = timeout_ms * 1000; + bool wait_for_pulse = false; + + if(!nfcv_data->ready) { + return false; + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + uint8_t period_buffer[NFCV_DIAGNOSTIC_DUMP_SIZE]; + uint32_t period_buffer_pos = 0; +#endif + + while(true) { + uint32_t periods = pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout); + uint32_t timestamp = DWT->CYCCNT; + + /* when timed out, reset to SOF state */ + if(periods == PULSE_READER_NO_EDGE || periods == PULSE_READER_LOST_EDGE) { + break; + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos < sizeof(period_buffer)) { + period_buffer[period_buffer_pos++] = periods; + } +#endif + + /* short helper for detecting a pulse position */ + if(wait_for_pulse) { + wait_for_pulse = false; + if(periods != 1) { + frame_state = NFCV_FRAME_STATE_RESET; + } + continue; + } + + switch(frame_state) { + case NFCV_FRAME_STATE_SOF1: + if(periods == 1) { + frame_state = NFCV_FRAME_STATE_SOF2; + } else { + frame_state = NFCV_FRAME_STATE_SOF1; + break; + } + break; + + case NFCV_FRAME_STATE_SOF2: + /* waiting for the second low period, telling us about coding */ + if(periods == 6) { + frame_state = NFCV_FRAME_STATE_CODING_256; + periods_previous = 0; + wait_for_pulse = true; + } else if(periods == 4) { + frame_state = NFCV_FRAME_STATE_CODING_4; + periods_previous = 2; + wait_for_pulse = true; + } else { + frame_state = NFCV_FRAME_STATE_RESET; + } + break; + + case NFCV_FRAME_STATE_CODING_256: + if(periods_previous > periods) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + + if(periods > 512) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } + + periods_previous = 512 - (periods + 1); + byte_value = (periods - 1) / 2; + if(frame_pos < NFCV_FRAMESIZE_MAX) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } + + wait_for_pulse = true; + + break; + + case NFCV_FRAME_STATE_CODING_4: + if(periods_previous > periods) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + periods_previous = 0; + + byte_value >>= 2; + bits_received += 2; + + if(periods == 1) { + byte_value |= 0x00 << 6; + periods_previous = 6; + } else if(periods == 3) { + byte_value |= 0x01 << 6; + periods_previous = 4; + } else if(periods == 5) { + byte_value |= 0x02 << 6; + periods_previous = 2; + } else if(periods == 7) { + byte_value |= 0x03 << 6; + periods_previous = 0; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } else { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(bits_received >= 8) { + if(frame_pos < NFCV_FRAMESIZE_MAX) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } + bits_received = 0; + } + wait_for_pulse = true; + break; + } + + /* post-state-machine cleanup and reset */ + if(frame_state == NFCV_FRAME_STATE_RESET) { + frame_state = NFCV_FRAME_STATE_SOF1; + } else if(frame_state == NFCV_FRAME_STATE_EOF) { + nfcv_data->frame_length = frame_pos; + nfcv_data->eof_timestamp = timestamp; + break; + } + } + + if(frame_state == NFCV_FRAME_STATE_EOF) { + /* we know that this code uses TIM2, so stop pulse reader */ + pulse_reader_stop(nfcv_data->emu_air.reader_signal); + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(nfcv_data->frame, frame_pos * 8, false, tx_rx->sniff_context); + } + nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); + + pulse_reader_start(nfcv_data->emu_air.reader_signal); + ret = true; + + } +#ifdef NFCV_VERBOSE + else { + if(frame_state != NFCV_FRAME_STATE_SOF1) { + FURI_LOG_T(TAG, "leaving while in state: %lu", frame_state); + } + } +#endif + +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos) { + FURI_LOG_T(TAG, "pulses:"); + for(uint32_t pos = 0; pos < period_buffer_pos; pos++) { + FURI_LOG_T(TAG, " #%lu: %u", pos, period_buffer[pos]); + } + } +#endif + + return ret; +} diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h new file mode 100644 index 000000000..87a696737 --- /dev/null +++ b/lib/nfc/protocols/nfcv.h @@ -0,0 +1,291 @@ +#pragma once + +#include +#include + +#include +#include +#include "nfc_util.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* true: modulating releases load, false: modulating adds load resistor to field coil */ +#define NFCV_LOAD_MODULATION_POLARITY (false) + +#define NFCV_FC (13560000.0f) /* MHz */ +#define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC / 32) / 2.0f) /* 1.1799 µs */ +#define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ +#define NFCV_PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) + +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum number of blocks is defined as 256 */ +#define NFCV_BLOCKS_MAX 256 +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum size of blocks is defined as 32 */ +#define NFCV_BLOCKSIZE_MAX 32 +/* the resulting memory size a card can have */ +#define NFCV_MEMSIZE_MAX (NFCV_BLOCKS_MAX * NFCV_BLOCKSIZE_MAX) +/* ISO/IEC 15693-3:2019(E) 7.1b: standard allows up to 8192, the maxium frame length that we are expected to receive/send is less */ +#define NFCV_FRAMESIZE_MAX (1 + NFCV_MEMSIZE_MAX + NFCV_BLOCKS_MAX) + +/* maximum string length for log messages */ +#define NFCV_LOG_STR_LEN 128 +/* maximum of pulses to be buffered by pulse reader */ +#define NFCV_PULSE_BUFFER 512 + +//#define NFCV_DIAGNOSTIC_DUMPS +//#define NFCV_DIAGNOSTIC_DUMP_SIZE 256 +//#define NFCV_VERBOSE + +/* helpers to calculate the send time based on DWT->CYCCNT */ +#define NFCV_FDT_USEC(usec) ((usec)*64) +#define NFCV_FDT_FC(ticks) ((ticks)*6400 / 1356) + +/* state machine when receiving frame bits */ +#define NFCV_FRAME_STATE_SOF1 0 +#define NFCV_FRAME_STATE_SOF2 1 +#define NFCV_FRAME_STATE_CODING_4 2 +#define NFCV_FRAME_STATE_CODING_256 3 +#define NFCV_FRAME_STATE_EOF 4 +#define NFCV_FRAME_STATE_RESET 5 + +/* sequences for every section of a frame */ +#define NFCV_SIG_SOF 0 +#define NFCV_SIG_BIT0 1 +#define NFCV_SIG_BIT1 2 +#define NFCV_SIG_EOF 3 +#define NFCV_SIG_LOW_SOF 4 +#define NFCV_SIG_LOW_BIT0 5 +#define NFCV_SIG_LOW_BIT1 6 +#define NFCV_SIG_LOW_EOF 7 + +/* various constants */ +#define NFCV_COMMAND_RETRIES 5 +#define NFCV_UID_LENGTH 8 + +/* ISO15693 protocol flags */ +typedef enum { + /* ISO15693 protocol flags when INVENTORY is NOT set */ + NFCV_REQ_FLAG_SUB_CARRIER = (1 << 0), + NFCV_REQ_FLAG_DATA_RATE = (1 << 1), + NFCV_REQ_FLAG_INVENTORY = (1 << 2), + NFCV_REQ_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_REQ_FLAG_SELECT = (1 << 4), + NFCV_REQ_FLAG_ADDRESS = (1 << 5), + NFCV_REQ_FLAG_OPTION = (1 << 6), + /* ISO15693 protocol flags when INVENTORY flag is set */ + NFCV_REQ_FLAG_AFI = (1 << 4), + NFCV_REQ_FLAG_NB_SLOTS = (1 << 5) +} NfcVRequestFlags; + +/* ISO15693 protocol flags */ +typedef enum { + NFCV_RES_FLAG_ERROR = (1 << 0), + NFCV_RES_FLAG_VALIDITY = (1 << 1), + NFCV_RES_FLAG_FINAL = (1 << 2), + NFCV_RES_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_RES_FLAG_SEC_LEN1 = (1 << 4), + NFCV_RES_FLAG_SEC_LEN2 = (1 << 5), + NFCV_RES_FLAG_WAIT_EXT = (1 << 6), +} NfcVRsponseFlags; + +/* flags for SYSINFO response */ +typedef enum { + NFCV_SYSINFO_FLAG_DSFID = (1 << 0), + NFCV_SYSINFO_FLAG_AFI = (1 << 1), + NFCV_SYSINFO_FLAG_MEMSIZE = (1 << 2), + NFCV_SYSINFO_FLAG_ICREF = (1 << 3) +} NfcVSysinfoFlags; + +/* ISO15693 command codes */ +typedef enum { + /* mandatory command codes */ + NFCV_CMD_INVENTORY = 0x01, + NFCV_CMD_STAY_QUIET = 0x02, + /* optional command codes */ + NFCV_CMD_READ_BLOCK = 0x20, + NFCV_CMD_WRITE_BLOCK = 0x21, + NFCV_CMD_LOCK_BLOCK = 0x22, + NFCV_CMD_READ_MULTI_BLOCK = 0x23, + NFCV_CMD_WRITE_MULTI_BLOCK = 0x24, + NFCV_CMD_SELECT = 0x25, + NFCV_CMD_RESET_TO_READY = 0x26, + NFCV_CMD_WRITE_AFI = 0x27, + NFCV_CMD_LOCK_AFI = 0x28, + NFCV_CMD_WRITE_DSFID = 0x29, + NFCV_CMD_LOCK_DSFID = 0x2A, + NFCV_CMD_GET_SYSTEM_INFO = 0x2B, + NFCV_CMD_READ_MULTI_SECSTATUS = 0x2C, + /* advanced command codes */ + NFCV_CMD_ADVANCED = 0xA0, + /* flipper zero custom command codes */ + NFCV_CMD_CUST_ECHO_MODE = 0xDE, + NFCV_CMD_CUST_ECHO_DATA = 0xDF +} NfcVCommands; + +/* ISO15693 Response error codes */ +typedef enum { + NFCV_NOERROR = 0x00, + NFCV_ERROR_CMD_NOT_SUP = 0x01, // Command not supported + NFCV_ERROR_CMD_NOT_REC = 0x02, // Command not recognized (eg. parameter error) + NFCV_ERROR_CMD_OPTION = 0x03, // Command option not supported + NFCV_ERROR_GENERIC = 0x0F, // No additional Info about this error + NFCV_ERROR_BLOCK_UNAVAILABLE = 0x10, + NFCV_ERROR_BLOCK_LOCKED_ALREADY = 0x11, // cannot lock again + NFCV_ERROR_BLOCK_LOCKED = 0x12, // cannot be changed + NFCV_ERROR_BLOCK_WRITE = 0x13, // Writing was unsuccessful + NFCV_ERROR_BLOCL_WRITELOCK = 0x14 // Locking was unsuccessful +} NfcVErrorcodes; + +typedef enum { + NfcVLockBitDsfid = 1, + NfcVLockBitAfi = 2, +} NfcVLockBits; + +typedef enum { + NfcVAuthMethodManual, + NfcVAuthMethodTonieBox, +} NfcVAuthMethod; + +typedef enum { + NfcVTypePlain = 0, + NfcVTypeSlix = 1, + NfcVTypeSlixS = 2, + NfcVTypeSlixL = 3, + NfcVTypeSlix2 = 4, + NfcVTypeSniff = 255, +} NfcVSubtype; + +typedef enum { + NfcVSendFlagsNormal = 0, + NfcVSendFlagsSof = 1 << 0, + NfcVSendFlagsCrc = 1 << 1, + NfcVSendFlagsEof = 1 << 2, + NfcVSendFlagsOneSubcarrier = 0, + NfcVSendFlagsTwoSubcarrier = 1 << 3, + NfcVSendFlagsLowRate = 0, + NfcVSendFlagsHighRate = 1 << 4 +} NfcVSendFlags; + +typedef struct { + uint8_t key_read[4]; + uint8_t key_write[4]; + uint8_t key_privacy[4]; + uint8_t key_destroy[4]; + uint8_t key_eas[4]; + uint8_t rand[2]; + bool privacy; +} NfcVSlixData; + +typedef union { + NfcVSlixData slix; +} NfcVSubtypeData; + +typedef struct { + DigitalSignal* nfcv_resp_sof; + DigitalSignal* nfcv_resp_one; + DigitalSignal* nfcv_resp_zero; + DigitalSignal* nfcv_resp_eof; +} NfcVEmuAirSignals; + +typedef struct { + PulseReader* reader_signal; + DigitalSignal* nfcv_resp_pulse; /* pulse length, fc/32 */ + DigitalSignal* nfcv_resp_half_pulse; /* half pulse length, fc/32 */ + DigitalSignal* nfcv_resp_unmod; /* unmodulated length 256/fc */ + NfcVEmuAirSignals signals_high; + NfcVEmuAirSignals signals_low; + DigitalSequence* nfcv_signal; +} NfcVEmuAir; + +typedef void (*NfcVEmuProtocolHandler)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); +typedef bool (*NfcVEmuProtocolFilter)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); + +/* the default ISO15693 handler context */ +typedef struct { + uint8_t flags; /* ISO15693-3 flags of the header as specified */ + uint8_t command; /* ISO15693-3 command at offset 1 as specified */ + bool selected; /* ISO15693-3 flags: selected frame */ + bool addressed; /* ISO15693-3 flags: addressed frame */ + bool advanced; /* ISO15693-3 command: advanced command */ + uint8_t address_offset; /* ISO15693-3 offset of the address in frame, if addressed is set */ + uint8_t payload_offset; /* ISO15693-3 offset of the payload in frame */ + + uint8_t response_buffer[NFCV_FRAMESIZE_MAX]; /* pre-allocated response buffer */ + NfcVSendFlags response_flags; /* flags to use when sending response */ + uint32_t send_time; /* timestamp when to send the response */ + + NfcVEmuProtocolFilter emu_protocol_filter; +} NfcVEmuProtocolCtx; + +typedef struct { + /* common ISO15693 fields, being specified in ISO15693-3 */ + uint8_t dsfid; + uint8_t afi; + uint8_t ic_ref; + uint16_t block_num; + uint8_t block_size; + uint8_t data[NFCV_MEMSIZE_MAX]; + uint8_t security_status[1 + NFCV_BLOCKS_MAX]; + bool selected; + bool quiet; + + bool modified; + bool ready; + bool echo_mode; + + /* specfic variant infos */ + NfcVSubtype sub_type; + NfcVSubtypeData sub_data; + NfcVAuthMethod auth_method; + + /* precalced air level data */ + NfcVEmuAir emu_air; + + uint8_t* frame; /* [NFCV_FRAMESIZE_MAX] ISO15693-2 incoming raw data from air layer */ + uint8_t frame_length; /* ISO15693-2 length of incoming data */ + uint32_t eof_timestamp; /* ISO15693-2 EOF timestamp, read from DWT->CYCCNT */ + + /* handler for the protocol layer as specified in ISO15693-3 */ + NfcVEmuProtocolHandler emu_protocol_handler; + void* emu_protocol_ctx; + /* runtime data */ + char last_command[NFCV_LOG_STR_LEN]; + char error[NFCV_LOG_STR_LEN]; +} NfcVData; + +typedef struct { + uint16_t blocks_to_read; + int16_t blocks_read; +} NfcVReader; + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); +ReturnCode nfcv_inventory(uint8_t* uid); +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +void nfcv_emu_deinit(NfcVData* nfcv_data); +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms); +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c new file mode 100644 index 000000000..ec3afc248 --- /dev/null +++ b/lib/nfc/protocols/slix.c @@ -0,0 +1,412 @@ + +#include +#include "nfcv.h" +#include "slix.h" +#include "nfc_util.h" +#include +#include "furi_hal_nfc.h" +#include + +#define TAG "SLIX" + +static uint32_t slix_read_be(uint8_t* data, uint32_t length) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < length; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} + +uint8_t slix_get_ti(FuriHalNfcDevData* nfc_data) { + return (nfc_data->uid[3] >> 3) & 3; +} + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 2) { + return true; + } + return false; +} + +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 1) { + return true; + } + return false; +} + +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x02)) { + return true; + } + return false; +} + +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x03)) { + return true; + } + return false; +} + +ReturnCode slix_get_random(NfcVData* data) { + uint16_t received = 0; + uint8_t rxBuf[32]; + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_GET_RANDOM_NUMBER, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + NULL, + NULL, + 0, + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + if(received != 3) { + return ERR_PROTO; + } + if(data != NULL) { + data->sub_data.slix.rand[0] = rxBuf[2]; + data->sub_data.slix.rand[1] = rxBuf[1]; + } + } + + return ret; +} + +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { + furi_assert(rand); + + uint16_t received = 0; + uint8_t rxBuf[32]; + uint8_t cmd_set_pass[] = { + password_id, + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0], + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0]}; + uint8_t* password = NULL; + + switch(password_id) { + case SLIX_PASS_READ: + password = data->sub_data.slix.key_read; + break; + case SLIX_PASS_WRITE: + password = data->sub_data.slix.key_write; + break; + case SLIX_PASS_PRIVACY: + password = data->sub_data.slix.key_privacy; + break; + case SLIX_PASS_DESTROY: + password = data->sub_data.slix.key_destroy; + break; + case SLIX_PASS_EASAFI: + password = data->sub_data.slix.key_eas; + break; + default: + break; + } + + if(!password) { + return ERR_NOTSUPP; + } + + for(int pos = 0; pos < 4; pos++) { + cmd_set_pass[1 + pos] ^= password[3 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_SET_PASSWORD, + RFAL_NFCV_REQ_FLAG_DATA_RATE, + NFCV_MANUFACTURER_NXP, + NULL, + cmd_set_pass, + sizeof(cmd_set_pass), + rxBuf, + sizeof(rxBuf), + &received); + + return ret; +} + +bool slix_generic_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in, + uint32_t password_supported) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + + if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && + ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "command 0x%02X ignored, privacy mode", + ctx->command); + FURI_LOG_D(TAG, "%s", nfcv_data->last_command); + return true; + } + + bool handled = false; + + switch(ctx->command) { + case NFCV_CMD_NXP_GET_RANDOM_NUMBER: { + slix->rand[0] = furi_hal_random_get(); + slix->rand[1] = furi_hal_random_get(); + + ctx->response_buffer[0] = NFCV_NOERROR; + ctx->response_buffer[1] = slix->rand[1]; + ctx->response_buffer[2] = slix->rand[0]; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 3, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "GET_RANDOM_NUMBER -> 0x%02X%02X", + slix->rand[0], + slix->rand[1]); + + handled = true; + break; + } + + case NFCV_CMD_NXP_SET_PASSWORD: { + uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + + if(!(password_id & password_supported)) { + break; + } + + uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + uint8_t* rand = slix->rand; + uint8_t* password = NULL; + uint8_t password_rcv[4]; + + switch(password_id) { + case SLIX_PASS_READ: + password = slix->key_read; + break; + case SLIX_PASS_WRITE: + password = slix->key_write; + break; + case SLIX_PASS_PRIVACY: + password = slix->key_privacy; + break; + case SLIX_PASS_DESTROY: + password = slix->key_destroy; + break; + case SLIX_PASS_EASAFI: + password = slix->key_eas; + break; + default: + break; + } + + if(!password) { + break; + } + + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; + } + uint32_t pass_expect = slix_read_be(password, 4); + uint32_t pass_received = slix_read_be(password_rcv, 4); + + /* if the password is all-zeroes, just accept any password*/ + if(!pass_expect || pass_expect == pass_received) { + switch(password_id) { + case SLIX_PASS_READ: + break; + case SLIX_PASS_WRITE: + break; + case SLIX_PASS_PRIVACY: + slix->privacy = false; + nfcv_data->modified = true; + break; + case SLIX_PASS_DESTROY: + FURI_LOG_D(TAG, "Pooof! Got destroyed"); + break; + case SLIX_PASS_EASAFI: + break; + default: + break; + } + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX OK", + password_id, + pass_received); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL", + password_id, + pass_received, + pass_expect); + } + handled = true; + break; + } + + case NFCV_CMD_NXP_ENABLE_PRIVACY: { + ctx->response_buffer[0] = NFCV_NOERROR; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "NFCV_CMD_NXP_ENABLE_PRIVACY"); + + slix->privacy = true; + handled = true; + break; + } + } + + return handled; +} + +bool slix_l_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter( + tx_rx, + nfc_data, + nfcv_data_in, + SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_l_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_l_protocol_filter; +} + +bool slix_s_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix_s_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_s_protocol_filter; +} + +bool slix_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_protocol_filter; +} + +bool slix2_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix2_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix2_protocol_filter; +} diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h new file mode 100644 index 000000000..701fa2f82 --- /dev/null +++ b/lib/nfc/protocols/slix.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include "nfc_util.h" +#include + +#define NFCV_MANUFACTURER_NXP 0x04 + +/* ISO15693-3 CUSTOM NXP COMMANDS */ +#define NFCV_CMD_NXP_SET_EAS 0xA2 +#define NFCV_CMD_NXP_RESET_EAS 0xA3 +#define NFCV_CMD_NXP_LOCK_EAS 0xA4 +#define NFCV_CMD_NXP_EAS_ALARM 0xA5 +#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 +#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 +#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define NFCV_CMD_NXP_SET_PASSWORD 0xB3 +#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 +#define NFCV_CMD_NXP_DESTROY 0xB9 +#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA + +/* available passwords */ +#define SLIX_PASS_READ 0x01 +#define SLIX_PASS_WRITE 0x02 +#define SLIX_PASS_PRIVACY 0x04 +#define SLIX_PASS_DESTROY 0x08 +#define SLIX_PASS_EASAFI 0x10 + +#define SLIX_PASS_ALL \ + (SLIX_PASS_READ | SLIX_PASS_WRITE | SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI) + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); + +ReturnCode slix_get_random(NfcVData* data); +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); + +void slix_prepare(NfcVData* nfcv_data); +void slix_s_prepare(NfcVData* nfcv_data); +void slix_l_prepare(NfcVData* nfcv_data); +void slix2_prepare(NfcVData* nfcv_data); From 3226254876d993b9dc7d54a8c069b8fe5d4cf670 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 8 Jun 2023 10:16:01 +0400 Subject: [PATCH 101/370] [FL-3351] github: re-enabled f18 build (#2743) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * github: re-enabled f18 build * scripts: storage: better transfer logging * Fix PVS warnings Co-authored-by: あく --- .github/workflows/build.yml | 6 +++--- .../main/nfc/scenes/nfc_scene_nfcv_emulate.c | 2 +- lib/nfc/nfc_device.c | 2 +- lib/nfc/nfc_worker.c | 16 +++++++-------- lib/nfc/protocols/nfcv.c | 4 ++-- lib/nfc/protocols/slix.c | 2 +- scripts/flipper/storage.py | 20 +++++++++++-------- 7 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e8b76a02c..fb0f13e20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ on: pull_request: env: - TARGETS: f7 + TARGETS: f7 f18 DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work @@ -96,8 +96,8 @@ jobs: - name: 'Copy map analyser files' if: ${{ !github.event.pull_request.head.repo.fork }} run: | - cp build/f7-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map - cp build/f7-firmware-*/firmware.elf map_analyser_files/firmware.elf + cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map + cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf cp ${{ github.event_path }} map_analyser_files/event.json - name: 'Analyse map file' diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c index 3dd7c460b..d812988bd 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -50,7 +50,7 @@ static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); widget_add_string_multiline_element( widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V"); - if(strcmp(nfc->dev->dev_name, "")) { + if(strcmp(nfc->dev->dev_name, "") != 0) { furi_string_printf(info_str, "%s", nfc->dev->dev_name); } else { for(uint8_t i = 0; i < data->uid_len; i++) { diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 9646c262e..952fca254 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -808,7 +808,7 @@ static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { return saved; } -bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { +bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524 bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; memset(data, 0, sizeof(NfcVSlixData)); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 293f1ce70..974bb0a4d 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -297,10 +297,10 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { ret = slix_unlock(nfcv_data, 4); } else { key = 0x7FFD6E5B; - key_data[0] = key >> 24; - key_data[1] = key >> 16; - key_data[2] = key >> 8; - key_data[3] = key >> 0; + key_data[0] = (key >> 24) & 0xFF; + key_data[1] = (key >> 16) & 0xFF; + key_data[2] = (key >> 8) & 0xFF; + key_data[3] = (key >> 0) & 0xFF; ret = slix_unlock(nfcv_data, 4); if(ret != ERR_NONE) { @@ -317,10 +317,10 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { } key = 0x0F0F0F0F; - key_data[0] = key >> 24; - key_data[1] = key >> 16; - key_data[2] = key >> 8; - key_data[3] = key >> 0; + key_data[0] = (key >> 24) & 0xFF; + key_data[1] = (key >> 16) & 0xFF; + key_data[2] = (key >> 8) & 0xFF; + key_data[3] = (key >> 0) & 0xFF; ret = slix_unlock(nfcv_data, 4); } } diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index ac818b7a4..3c37153d8 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -438,7 +438,7 @@ void nfcv_emu_send( furi_assert(nfcv); /* picked default value (0) to match the most common format */ - if(!flags) { + if(flags == NfcVSendFlagsNormal) { flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; } @@ -1326,7 +1326,7 @@ bool nfcv_emu_loop( bits_received += 2; if(periods == 1) { - byte_value |= 0x00 << 6; + byte_value |= 0x00 << 6; // -V684 periods_previous = 6; } else if(periods == 3) { byte_value |= 0x01 << 6; diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index ec3afc248..1c14c0bf9 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -381,7 +381,7 @@ void slix_prepare(NfcVData* nfcv_data) { ctx->emu_protocol_filter = &slix_protocol_filter; } -bool slix2_protocol_filter( +bool slix2_protocol_filter( // -V524 FuriHalNfcTxRxContext* tx_rx, FuriHalNfcDevData* nfc_data, void* nfcv_data_in) { diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index f4d622bfe..2c9c043d5 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -257,12 +257,12 @@ class FlipperStorage: self.read.until(self.CLI_PROMPT) ftell = file.tell() - percent = str(math.ceil(ftell / filesize * 100)) - total_chunks = str(math.ceil(filesize / buffer_size)) - current_chunk = str(math.ceil(ftell / buffer_size)) + percent = math.ceil(ftell / filesize * 100) + total_chunks = math.ceil(filesize / buffer_size) + current_chunk = math.ceil(ftell / buffer_size) approx_speed = ftell / (time.time() - start_time + 0.0001) sys.stdout.write( - f"\r{percent}%, chunk {current_chunk} of {total_chunks} @ {approx_speed/1024:.2f} kb/s" + f"\r<{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" ) sys.stdout.flush() print() @@ -270,6 +270,7 @@ class FlipperStorage: def read_file(self, filename: str): """Receive file from Flipper, and get filedata (bytes)""" buffer_size = self.chunk_size + start_time = time.time() self.send_and_wait_eol( 'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r" ) @@ -290,10 +291,13 @@ class FlipperStorage: filedata.extend(self.port.read(chunk_size)) read_size = read_size + chunk_size - percent = str(math.ceil(read_size / size * 100)) - total_chunks = str(math.ceil(size / buffer_size)) - current_chunk = str(math.ceil(read_size / buffer_size)) - sys.stdout.write(f"\r{percent}%, chunk {current_chunk} of {total_chunks}") + percent = math.ceil(read_size / size * 100) + total_chunks = math.ceil(size / buffer_size) + current_chunk = math.ceil(read_size / buffer_size) + approx_speed = read_size / (time.time() - start_time + 0.0001) + sys.stdout.write( + f"\r>{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" + ) sys.stdout.flush() print() self.read.until(self.CLI_PROMPT) From e5343fdc9a44397a80221c8d26722a17082975f5 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 8 Jun 2023 01:28:08 -0700 Subject: [PATCH 102/370] Scripts: WiFi board updater (#2625) * Scripts: wifi updater * WiFi board updater: lint, process download error * WiFi board updater: auto cleanup temp dir * Scripts: fix server address --- scripts/wifi_board.py | 240 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100755 scripts/wifi_board.py diff --git a/scripts/wifi_board.py b/scripts/wifi_board.py new file mode 100755 index 000000000..3f89ebdc6 --- /dev/null +++ b/scripts/wifi_board.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 + +from flipper.app import App +from serial.tools.list_ports_common import ListPortInfo + +import logging +import os +import tempfile +import subprocess +import serial.tools.list_ports as list_ports +import json +import requests +import tarfile + + +class UpdateDownloader: + UPDATE_SERVER = "https://update.flipperzero.one" + UPDATE_PROJECT = "/blackmagic-firmware" + UPDATE_INDEX = UPDATE_SERVER + UPDATE_PROJECT + "/directory.json" + UPDATE_TYPE = "full_tgz" + + CHANNEL_ID_ALIAS = { + "dev": "development", + "rc": "release-candidate", + "r": "release", + "rel": "release", + } + + def __init__(self): + self.logger = logging.getLogger() + + def download(self, channel_id: str, dir: str) -> bool: + # Aliases + if channel_id in self.CHANNEL_ID_ALIAS: + channel_id = self.CHANNEL_ID_ALIAS[channel_id] + + # Make directory + if not os.path.exists(dir): + self.logger.info(f"Creating directory {dir}") + os.makedirs(dir) + + # Download json index + self.logger.info(f"Downloading {self.UPDATE_INDEX}") + response = requests.get(self.UPDATE_INDEX) + if response.status_code != 200: + self.logger.error(f"Failed to download {self.UPDATE_INDEX}") + return False + + # Parse json index + try: + index = json.loads(response.content) + except Exception as e: + self.logger.error(f"Failed to parse json index: {e}") + return False + + # Find channel + channel = None + for channel_candidate in index["channels"]: + if channel_candidate["id"] == channel_id: + channel = channel_candidate + break + + # Check if channel found + if channel is None: + self.logger.error( + f"Channel '{channel_id}' not found. Valid channels: {', '.join([c['id'] for c in index['channels']])}" + ) + return False + + self.logger.info(f"Using channel '{channel_id}'") + + # Get latest version + try: + version = channel["versions"][0] + except Exception as e: + self.logger.error(f"Failed to get version: {e}") + return False + + self.logger.info(f"Using version '{version['version']}'") + + # Get changelog + changelog = None + try: + changelog = version["changelog"] + except Exception as e: + self.logger.error(f"Failed to get changelog: {e}") + + # print changelog + if changelog is not None: + self.logger.info(f"Changelog:") + for line in changelog.split("\n"): + if line.strip() == "": + continue + self.logger.info(f" {line}") + + # Find file + file_url = None + for file_candidate in version["files"]: + if file_candidate["type"] == self.UPDATE_TYPE: + file_url = file_candidate["url"] + break + + if file_url is None: + self.logger.error(f"File not found") + return False + + # Make file path + file_name = file_url.split("/")[-1] + file_path = os.path.join(dir, file_name) + + # Download file + self.logger.info(f"Downloading {file_url} to {file_path}") + with open(file_path, "wb") as f: + response = requests.get(file_url) + f.write(response.content) + + # Unzip tgz + self.logger.info(f"Unzipping {file_path}") + with tarfile.open(file_path, "r") as tar: + tar.extractall(dir) + + return True + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "-c", "--channel", help="Channel name", default="release" + ) + self.parser.set_defaults(func=self.update) + + # logging + self.logger = logging.getLogger() + + def find_wifi_board(self) -> bool: + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + blackmagics: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore + daps: list[ListPortInfo] = list(list_ports.grep("CMSIS-DAP")) # type: ignore + + return len(blackmagics) > 0 or len(daps) > 0 + + def find_wifi_board_bootloader(self): + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + ports: list[ListPortInfo] = list(list_ports.grep("ESP32-S2")) # type: ignore + + if len(ports) == 0: + # Blackmagic probe serial port not found, will be handled later + pass + elif len(ports) > 1: + raise Exception("More than one WiFi board found") + else: + port = ports[0] + if os.name == "nt": + port.device = f"\\\\.\\{port.device}" + return port.device + + def update(self): + try: + port = self.find_wifi_board_bootloader() + except Exception as e: + self.logger.error(f"{e}") + return 1 + + if self.args.port != "auto": + port = self.args.port + + available_ports = [p[0] for p in list(list_ports.comports())] + if port not in available_ports: + self.logger.error(f"Port {port} not found") + return 1 + + if port is None: + if self.find_wifi_board(): + self.logger.error("WiFi board found, but not in bootloader mode.") + self.logger.info("Please hold down BOOT button and press RESET button") + else: + self.logger.error("WiFi board not found") + self.logger.info( + "Please connect WiFi board to your computer, hold down BOOT button and press RESET button" + ) + return 1 + + # get temporary dir + with tempfile.TemporaryDirectory() as temp_dir: + downloader = UpdateDownloader() + + # download latest channel update + try: + if not downloader.download(self.args.channel, temp_dir): + self.logger.error(f"Cannot download update") + return 1 + except Exception as e: + self.logger.error(f"Cannot download update: {e}") + return 1 + + with open(os.path.join(temp_dir, "flash.command"), "r") as f: + flash_command = f.read() + + flash_command = flash_command.replace("\n", "").replace("\r", "") + flash_command = flash_command.replace("(PORT)", port) + + # We can't reset the board after flashing via usb + flash_command = flash_command.replace( + "--after hard_reset", "--after no_reset_stub" + ) + + args = flash_command.split(" ")[0:] + args = list(filter(None, args)) + + esptool_params = [] + esptool_params.extend(args) + + self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"') + + process = subprocess.Popen( + esptool_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=temp_dir, + bufsize=1, + universal_newlines=True, + ) + + while process.poll() is None: + if process.stdout is not None: + for line in process.stdout: + self.logger.debug(f"{line.strip()}") + + if process.returncode != 0: + self.logger.error(f"Failed to flash WiFi board") + else: + self.logger.info("WiFi board flashed successfully") + self.logger.info("Press RESET button on WiFi board to start it") + + return process.returncode + + +if __name__ == "__main__": + Main()() From e3e64e5e839926f2c3b8a190fcc9c1c3ee1a2a4b Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 8 Jun 2023 13:42:02 +0400 Subject: [PATCH 103/370] [FL-3267] ble: refactored bt gatt characteristics setup (#2587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ble: refactored bt gatt characteristics setup * ble: naming fixes, small optimizations * ble: expanded bitfields; fixed pvs warnings * ble: fixed pvs warnings for real * ble: using FlipperGattCharacteristicDataPropsFixed for char[] props * ble: removed flipper_gatt_characteristic_props_const_char * ble: gatt: naming changes * ble: gatt: fixed device_info service constant attrs sizes * ble: gatt: copy descriptors to char instances; reworked hid chars to be callback-based; moved max size getter to callback with NULL data; added comments * ble: gatt: removed hid_svc_report_data_callback * ble: hid svc: better double loop idx naming * ble: hid svc: simplified hid_svc_update_info * ble: gatt: removed magic values; fixed type for HidSvcGattCharacteristicInfo * ble: gatt: moved long uuids to separate files Co-authored-by: gornekich Co-authored-by: あく --- applications/services/bt/bt_service/bt.c | 2 +- firmware/targets/f7/ble_glue/app_debug.c | 4 +- firmware/targets/f7/ble_glue/ble_app.c | 91 ++--- .../targets/f7/ble_glue/dev_info_service.c | 220 ------------ firmware/targets/f7/ble_glue/hid_service.c | 332 ------------------ .../ble_glue/{ => services}/battery_service.c | 118 +++---- .../ble_glue/{ => services}/battery_service.h | 0 .../f7/ble_glue/services/dev_info_service.c | 176 ++++++++++ .../{ => services}/dev_info_service.h | 0 .../services/dev_info_service_uuid.inc | 3 + .../targets/f7/ble_glue/services/gatt_char.c | 123 +++++++ .../targets/f7/ble_glue/services/gatt_char.h | 96 +++++ .../f7/ble_glue/services/hid_service.c | 293 ++++++++++++++++ .../f7/ble_glue/{ => services}/hid_service.h | 3 +- .../ble_glue/{ => services}/serial_service.c | 195 +++++----- .../ble_glue/{ => services}/serial_service.h | 0 .../ble_glue/services/serial_service_uuid.inc | 12 + firmware/targets/f7/furi_hal/furi_hal_bt.c | 3 +- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 10 +- .../targets/f7/furi_hal/furi_hal_bt_serial.c | 6 +- .../targets/furi_hal_include/furi_hal_bt.h | 2 +- .../furi_hal_include/furi_hal_bt_serial.h | 2 +- 22 files changed, 898 insertions(+), 793 deletions(-) delete mode 100644 firmware/targets/f7/ble_glue/dev_info_service.c delete mode 100644 firmware/targets/f7/ble_glue/hid_service.c rename firmware/targets/f7/ble_glue/{ => services}/battery_service.c (53%) rename firmware/targets/f7/ble_glue/{ => services}/battery_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/services/dev_info_service.c rename firmware/targets/f7/ble_glue/{ => services}/dev_info_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc create mode 100644 firmware/targets/f7/ble_glue/services/gatt_char.c create mode 100644 firmware/targets/f7/ble_glue/services/gatt_char.h create mode 100644 firmware/targets/f7/ble_glue/services/hid_service.c rename firmware/targets/f7/ble_glue/{ => services}/hid_service.h (87%) rename firmware/targets/f7/ble_glue/{ => services}/serial_service.c (57%) rename firmware/targets/f7/ble_glue/{ => services}/serial_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/services/serial_service_uuid.inc diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 2dcea3485..1b12ee303 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -1,7 +1,7 @@ #include "bt_i.h" -#include "battery_service.h" #include "bt_keys_storage.h" +#include #include #include #include diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index b443bee21..d28852822 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -196,14 +196,14 @@ static void APPD_SetCPU2GpioConfig(void) { gpio_config.Pin = gpiob_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); LL_GPIO_Init(GPIOB, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list); + LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); } if(gpioc_pin_list != 0) { gpio_config.Pin = gpioc_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); LL_GPIO_Init(GPIOC, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); + LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); } } diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 37d8f7cd0..c0418d9fe 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -33,6 +33,51 @@ static int32_t ble_app_hci_thread(void* context); static void ble_app_hci_event_handler(void* pPayload); static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); +static const HCI_TL_HciInitConf_t hci_tl_config = { + .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, + .StatusNotCallBack = ble_app_hci_status_not_handler, +}; + +static const SHCI_C2_CONFIG_Cmd_Param_t config_param = { + .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, + .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, + .BleNvmRamAddress = (uint32_t)ble_app_nvm, + .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, +}; + +static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { + .Header = {{0, 0, 0}}, // Header unused + .Param = { + .pBleBufferAddress = 0, // pBleBufferAddress not used + .BleBufferSize = 0, // BleBufferSize not used + .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, + .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, + .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, + .NumOfLinks = CFG_BLE_NUM_LINK, + .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, + .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, + .MblockCount = CFG_BLE_MBLOCK_COUNT, + .AttMtu = CFG_BLE_MAX_ATT_MTU, + .SlaveSca = CFG_BLE_SLAVE_SCA, + .MasterSca = CFG_BLE_MASTER_SCA, + .LsSource = CFG_BLE_LSE_SOURCE, + .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, + .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, + .ViterbiEnable = CFG_BLE_VITERBI_MODE, + .Options = CFG_BLE_OPTIONS, + .HwVersion = 0, + .max_coc_initiator_nbr = 32, + .min_tx_power = 0, + .max_tx_power = 0, + .rx_model_config = 1, + /* New stack (13.3->15.0) */ + .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB + .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB + .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) + }}; + bool ble_app_init() { SHCI_CmdStatus_t status; ble_app = malloc(sizeof(BleApp)); @@ -44,58 +89,16 @@ bool ble_app_init() { furi_thread_start(ble_app->thread); // Initialize Ble Transport Layer - HCI_TL_HciInitConf_t hci_tl_config = { - .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, - .StatusNotCallBack = ble_app_hci_status_not_handler, - }; hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); // Configure NVM store for pairing data - SHCI_C2_CONFIG_Cmd_Param_t config_param = { - .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, - .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, - .BleNvmRamAddress = (uint32_t)ble_app_nvm, - .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, - }; - status = SHCI_C2_Config(&config_param); + status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); if(status) { FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); } // Start ble stack on 2nd core - SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { - .Header = {{0, 0, 0}}, // Header unused - .Param = { - .pBleBufferAddress = 0, // pBleBufferAddress not used - .BleBufferSize = 0, // BleBufferSize not used - .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, - .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, - .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, - .NumOfLinks = CFG_BLE_NUM_LINK, - .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, - .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, - .MblockCount = CFG_BLE_MBLOCK_COUNT, - .AttMtu = CFG_BLE_MAX_ATT_MTU, - .SlaveSca = CFG_BLE_SLAVE_SCA, - .MasterSca = CFG_BLE_MASTER_SCA, - .LsSource = CFG_BLE_LSE_SOURCE, - .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, - .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, - .ViterbiEnable = CFG_BLE_VITERBI_MODE, - .Options = CFG_BLE_OPTIONS, - .HwVersion = 0, - .max_coc_initiator_nbr = 32, - .min_tx_power = 0, - .max_tx_power = 0, - .rx_model_config = 1, - /* New stack (13.3->15.0) */ - .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB - .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB - .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) - }}; - status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); + status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); if(status) { FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); } diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c deleted file mode 100644 index 8bdb2eea8..000000000 --- a/firmware/targets/f7/ble_glue/dev_info_service.c +++ /dev/null @@ -1,220 +0,0 @@ -#include "dev_info_service.h" -#include "app_common.h" -#include - -#include -#include -#include - -#define TAG "BtDevInfoSvc" - -typedef struct { - uint16_t service_handle; - uint16_t man_name_char_handle; - uint16_t serial_num_char_handle; - uint16_t firmware_rev_char_handle; - uint16_t software_rev_char_handle; - uint16_t rpc_version_char_handle; - FuriString* version_string; - char hardware_revision[4]; -} DevInfoSvc; - -static DevInfoSvc* dev_info_svc = NULL; - -static const char dev_info_man_name[] = "Flipper Devices Inc."; -static const char dev_info_serial_num[] = "1.0"; -static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); - -static const uint8_t dev_info_rpc_version_uuid[] = - {0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03}; - -void dev_info_svc_start() { - dev_info_svc = malloc(sizeof(DevInfoSvc)); - dev_info_svc->version_string = furi_string_alloc_printf( - "%s %s %s %s", - version_get_githash(NULL), - version_get_gitbranch(NULL), - version_get_gitbranchnum(NULL), - version_get_builddate(NULL)); - snprintf( - dev_info_svc->hardware_revision, - sizeof(dev_info_svc->hardware_revision), - "%d", - version_get_target(NULL)); - tBleStatus status; - - // Add Device Information Service - uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; - status = aci_gatt_add_service( - UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 11, &dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); - } - - // Add characteristics - uuid = MANUFACTURER_NAME_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_man_name), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->man_name_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status); - } - uuid = SERIAL_NUMBER_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_serial_num), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->serial_num_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add serial number char: %d", status); - } - uuid = FIRMWARE_REVISION_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - strlen(dev_info_svc->hardware_revision), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->firmware_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status); - } - uuid = SOFTWARE_REVISION_UUID; - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_16, - (Char_UUID_t*)&uuid, - furi_string_size(dev_info_svc->version_string), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->software_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add software revision char: %d", status); - } - status = aci_gatt_add_char( - dev_info_svc->service_handle, - UUID_TYPE_128, - (const Char_UUID_t*)dev_info_rpc_version_uuid, - strlen(dev_info_rpc_version), - CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &dev_info_svc->rpc_version_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status); - } - - // Update characteristics - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->man_name_char_handle, - 0, - strlen(dev_info_man_name), - (uint8_t*)dev_info_man_name); - if(status) { - FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->serial_num_char_handle, - 0, - strlen(dev_info_serial_num), - (uint8_t*)dev_info_serial_num); - if(status) { - FURI_LOG_E(TAG, "Failed to update serial number char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->firmware_rev_char_handle, - 0, - strlen(dev_info_svc->hardware_revision), - (uint8_t*)dev_info_svc->hardware_revision); - if(status) { - FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->software_rev_char_handle, - 0, - furi_string_size(dev_info_svc->version_string), - (uint8_t*)furi_string_get_cstr(dev_info_svc->version_string)); - if(status) { - FURI_LOG_E(TAG, "Failed to update software revision char: %d", status); - } - status = aci_gatt_update_char_value( - dev_info_svc->service_handle, - dev_info_svc->rpc_version_char_handle, - 0, - strlen(dev_info_rpc_version), - (uint8_t*)dev_info_rpc_version); - if(status) { - FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status); - } -} - -void dev_info_svc_stop() { - tBleStatus status; - if(dev_info_svc) { - furi_string_free(dev_info_svc->version_string); - // Delete service characteristics - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status); - } - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status); - } - status = aci_gatt_del_char( - dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status); - } - status = aci_gatt_del_char( - dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status); - } - status = - aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status); - } - // Delete service - status = aci_gatt_del_service(dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); - } - free(dev_info_svc); - dev_info_svc = NULL; - } -} - -bool dev_info_svc_is_started() { - return dev_info_svc != NULL; -} diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c deleted file mode 100644 index 47d242d4d..000000000 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ /dev/null @@ -1,332 +0,0 @@ -#include "hid_service.h" -#include "app_common.h" -#include - -#include - -#define TAG "BtHid" - -typedef struct { - uint16_t svc_handle; - uint16_t protocol_mode_char_handle; - uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; - uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; - uint16_t report_map_char_handle; - uint16_t info_char_handle; - uint16_t ctrl_point_char_handle; -} HIDSvc; - -static HIDSvc* hid_svc = NULL; - -static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { - SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; - hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); - evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; - // aci_gatt_attribute_modified_event_rp0* attribute_modified; - if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { - if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { - // Process modification events - ret = SVCCTL_EvtAckFlowEnable; - } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { - // Process notification confirmation - ret = SVCCTL_EvtAckFlowEnable; - } - } - return ret; -} - -void hid_svc_start() { - tBleStatus status; - hid_svc = malloc(sizeof(HIDSvc)); - Service_UUID_t svc_uuid = {}; - Char_Desc_Uuid_t desc_uuid = {}; - Char_UUID_t char_uuid = {}; - - // Register event handler - SVCCTL_RegisterSvcHandler(hid_svc_event_handler); - // Add service - svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; - /** - * Add Human Interface Device Service - */ - status = aci_gatt_add_service( - UUID_TYPE_16, - &svc_uuid, - PRIMARY_SERVICE, - 2 + /* protocol mode */ - (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + - (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + - 2, /* Service + Report Map + HID Information + HID Control Point */ - &hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add HID service: %d", status); - } - // Add Protocol mode characteristics - char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, - ATTR_PERMISSION_NONE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->protocol_mode_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status); - } - // Update Protocol mode characteristic - uint8_t protocol_mode = 1; - status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode); - if(status) { - FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); - } - -#if(HID_SVC_REPORT_COUNT != 0) - for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { - if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547 - uint8_t buf[2] = {i + 1, 1}; // 1 input - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { - uint8_t buf[2] = {i + 1, 2}; // 2 output - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } else { - uint8_t buf[2] = {i + 1, 3}; // 3 feature - char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAX_LEN, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &(hid_svc->report_char_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); - } - - desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; - status = aci_gatt_add_char_desc( - hid_svc->svc_handle, - hid_svc->report_char_handle[i], - UUID_TYPE_16, - &desc_uuid, - HID_SVC_REPORT_REF_LEN, - HID_SVC_REPORT_REF_LEN, - buf, - ATTR_PERMISSION_NONE, - ATTR_ACCESS_READ_WRITE, - GATT_DONT_NOTIFY_EVENTS, - MIN_ENCRY_KEY_SIZE, - CHAR_VALUE_LEN_CONSTANT, - &(hid_svc->report_ref_desc_handle[i])); - if(status) { - FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); - } - } - } -#endif - // Add Report Map characteristic - char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_REPORT_MAP_MAX_LEN, - CHAR_PROP_READ, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &hid_svc->report_map_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); - } - - // Add Information characteristic - char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_INFO_LEN, - CHAR_PROP_READ, - ATTR_PERMISSION_NONE, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->info_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status); - } - // Add Control Point characteristic - char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID; - status = aci_gatt_add_char( - hid_svc->svc_handle, - UUID_TYPE_16, - &char_uuid, - HID_SVC_CONTROL_POINT_LEN, - CHAR_PROP_WRITE_WITHOUT_RESP, - ATTR_PERMISSION_NONE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_CONSTANT, - &hid_svc->ctrl_point_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); - } -} - -bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = aci_gatt_update_char_value( - hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_update_info(uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - tBleStatus status = - aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); - if(status) { - FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); - return false; - } - return true; -} - -bool hid_svc_is_started() { - return hid_svc != NULL; -} - -void hid_svc_stop() { - tBleStatus status; - if(hid_svc) { - // Delete characteristics - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); - } -#if(HID_SVC_INPUT_REPORT_COUNT != 0) - for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); - } - } -#endif - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); - } - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); - } - status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); - } - // Delete service - status = aci_gatt_del_service(hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); - } - // Delete buffer size mutex - free(hid_svc); - hid_svc = NULL; - } -} diff --git a/firmware/targets/f7/ble_glue/battery_service.c b/firmware/targets/f7/ble_glue/services/battery_service.c similarity index 53% rename from firmware/targets/f7/ble_glue/battery_service.c rename to firmware/targets/f7/ble_glue/services/battery_service.c index 8c371efad..63f736b3b 100644 --- a/firmware/targets/f7/ble_glue/battery_service.c +++ b/firmware/targets/f7/ble_glue/services/battery_service.c @@ -1,5 +1,7 @@ #include "battery_service.h" #include "app_common.h" +#include "gatt_char.h" + #include #include @@ -7,12 +9,6 @@ #define TAG "BtBatterySvc" -typedef struct { - uint16_t svc_handle; - uint16_t battery_level_char_handle; - uint16_t power_state_char_handle; -} BatterySvc; - enum { // Common states BatterySvcPowerStateUnknown = 0b00, @@ -40,13 +36,44 @@ typedef struct { _Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); -static BatterySvc* battery_svc = NULL; - #define BATTERY_POWER_STATE (0x2A1A) static const uint16_t service_uuid = BATTERY_SERVICE_UUID; -static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; -static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; + +typedef enum { + BatterySvcGattCharacteristicBatteryLevel = 0, + BatterySvcGattCharacteristicPowerState, + BatterySvcGattCharacteristicCount, +} BatterySvcGattCharacteristicId; + +static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = + {[BatterySvcGattCharacteristicBatteryLevel] = + {.name = "Battery Level", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [BatterySvcGattCharacteristicPowerState] = { + .name = "Power State", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = BATTERY_POWER_STATE, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + +typedef struct { + uint16_t svc_handle; + FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount]; +} BatterySvc; + +static BatterySvc* battery_svc = NULL; void battery_svc_start() { battery_svc = malloc(sizeof(BatterySvc)); @@ -58,53 +85,19 @@ void battery_svc_start() { if(status) { FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); } - // Add Battery level characteristic - status = aci_gatt_add_char( - battery_svc->svc_handle, - UUID_TYPE_16, - (Char_UUID_t*)&battery_level_char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &battery_svc->battery_level_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); + for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); } - // Add Power state characteristic - status = aci_gatt_add_char( - battery_svc->svc_handle, - UUID_TYPE_16, - (Char_UUID_t*)&power_state_char_uuid, - 1, - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &battery_svc->power_state_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); - } - // Update power state charachteristic + battery_svc_update_power_state(); } void battery_svc_stop() { tBleStatus status; if(battery_svc) { - // Delete Battery level characteristic - status = - aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); - } - // Delete Power state characteristic - status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); + for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); } // Delete Battery service status = aci_gatt_del_service(battery_svc->svc_handle); @@ -126,13 +119,10 @@ bool battery_svc_update_level(uint8_t battery_charge) { return false; } // Update battery level characteristic - FURI_LOG_D(TAG, "Updating battery level characteristic"); - tBleStatus result = aci_gatt_update_char_value( - battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); - if(result) { - FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); - } - return result != BLE_STATUS_SUCCESS; + return flipper_gatt_characteristic_update( + battery_svc->svc_handle, + &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], + &battery_charge); } bool battery_svc_update_power_state() { @@ -152,15 +142,9 @@ bool battery_svc_update_power_state() { power_state.charging = BatterySvcPowerStateNotCharging; power_state.discharging = BatterySvcPowerStateDischarging; } - FURI_LOG_D(TAG, "Updating power state characteristic"); - tBleStatus result = aci_gatt_update_char_value( + + return flipper_gatt_characteristic_update( battery_svc->svc_handle, - battery_svc->power_state_char_handle, - 0, - 1, - (uint8_t*)&power_state); - if(result) { - FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); - } - return result != BLE_STATUS_SUCCESS; + &battery_svc->chars[BatterySvcGattCharacteristicPowerState], + &power_state); } diff --git a/firmware/targets/f7/ble_glue/battery_service.h b/firmware/targets/f7/ble_glue/services/battery_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/battery_service.h rename to firmware/targets/f7/ble_glue/services/battery_service.h diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.c b/firmware/targets/f7/ble_glue/services/dev_info_service.c new file mode 100644 index 000000000..cc95bb2fc --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/dev_info_service.c @@ -0,0 +1,176 @@ +#include "dev_info_service.h" +#include "app_common.h" +#include "gatt_char.h" +#include + +#include +#include +#include + +#include "dev_info_service_uuid.inc" + +#define TAG "BtDevInfoSvc" + +typedef enum { + DevInfoSvcGattCharacteristicMfgName = 0, + DevInfoSvcGattCharacteristicSerial, + DevInfoSvcGattCharacteristicFirmwareRev, + DevInfoSvcGattCharacteristicSoftwareRev, + DevInfoSvcGattCharacteristicRpcVersion, + DevInfoSvcGattCharacteristicCount, +} DevInfoSvcGattCharacteristicId; + +#define DEVICE_INFO_HARDWARE_REV_SIZE 4 +typedef struct { + uint16_t service_handle; + FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount]; + FuriString* version_string; + char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE]; +} DevInfoSvc; + +static DevInfoSvc* dev_info_svc = NULL; + +static const char dev_info_man_name[] = "Flipper Devices Inc."; +static const char dev_info_serial_num[] = "1.0"; +static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); + +static bool dev_info_char_firmware_rev_callback( + const void* context, + const uint8_t** data, + uint16_t* data_len) { + const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; + *data_len = sizeof(dev_info_svc->hardware_revision); + if(data) { + *data = (const uint8_t*)&dev_info_svc->hardware_revision; + } + return false; +} + +static bool dev_info_char_software_rev_callback( + const void* context, + const uint8_t** data, + uint16_t* data_len) { + const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; + *data_len = furi_string_size(dev_info_svc->version_string); + if(data) { + *data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string); + } + return false; +} + +static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] = + {[DevInfoSvcGattCharacteristicMfgName] = + {.name = "Manufacturer Name", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_man_name) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_man_name, + .uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicSerial] = + {.name = "Serial Number", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_serial_num) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_serial_num, + .uuid.Char_UUID_16 = SERIAL_NUMBER_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicFirmwareRev] = + {.name = "Firmware Revision", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.context = &dev_info_svc, + .data.callback.fn = dev_info_char_firmware_rev_callback, + .uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicSoftwareRev] = + {.name = "Software Revision", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.context = &dev_info_svc, + .data.callback.fn = dev_info_char_software_rev_callback, + .uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [DevInfoSvcGattCharacteristicRpcVersion] = { + .name = "RPC Version", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(dev_info_rpc_version) - 1, + .data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version, + .uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + +void dev_info_svc_start() { + dev_info_svc = malloc(sizeof(DevInfoSvc)); + dev_info_svc->version_string = furi_string_alloc_printf( + "%s %s %s %s", + version_get_githash(NULL), + version_get_gitbranch(NULL), + version_get_gitbranchnum(NULL), + version_get_builddate(NULL)); + snprintf( + dev_info_svc->hardware_revision, + sizeof(dev_info_svc->hardware_revision), + "%d", + version_get_target(NULL)); + tBleStatus status; + + // Add Device Information Service + uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; + status = aci_gatt_add_service( + UUID_TYPE_16, + (Service_UUID_t*)&uuid, + PRIMARY_SERVICE, + 1 + 2 * DevInfoSvcGattCharacteristicCount, + &dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); + } + + for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + dev_info_svc->service_handle, + &dev_info_svc_chars[i], + &dev_info_svc->characteristics[i]); + flipper_gatt_characteristic_update( + dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL); + } +} + +void dev_info_svc_stop() { + tBleStatus status; + if(dev_info_svc) { + furi_string_free(dev_info_svc->version_string); + // Delete service characteristics + for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete( + dev_info_svc->service_handle, &dev_info_svc->characteristics[i]); + } + // Delete service + status = aci_gatt_del_service(dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); + } + free(dev_info_svc); + dev_info_svc = NULL; + } +} + +bool dev_info_svc_is_started() { + return dev_info_svc != NULL; +} diff --git a/firmware/targets/f7/ble_glue/dev_info_service.h b/firmware/targets/f7/ble_glue/services/dev_info_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/dev_info_service.h rename to firmware/targets/f7/ble_glue/services/dev_info_service.h diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc new file mode 100644 index 000000000..ad520f62e --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc @@ -0,0 +1,3 @@ +#define DEV_INVO_RPC_VERSION_UID \ + { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } + diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c new file mode 100644 index 000000000..9b6a44f61 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/gatt_char.c @@ -0,0 +1,123 @@ +#include "gatt_char.h" + +#include + +#define TAG "GattChar" + +#define GATT_MIN_READ_KEY_SIZE (10) + +void flipper_gatt_characteristic_init( + uint16_t svc_handle, + const FlipperGattCharacteristicParams* char_descriptor, + FlipperGattCharacteristicInstance* char_instance) { + furi_assert(char_descriptor); + furi_assert(char_instance); + + // Copy the descriptor to the instance, since it may point to stack memory + // TODO: only copy if really comes from stack + char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); + memcpy( + (void*)char_instance->characteristic, + char_descriptor, + sizeof(FlipperGattCharacteristicParams)); + + uint16_t char_data_size = 0; + if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { + char_data_size = char_descriptor->data.fixed.length; + } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { + char_descriptor->data.callback.fn( + char_descriptor->data.callback.context, NULL, &char_data_size); + } + + tBleStatus status = aci_gatt_add_char( + svc_handle, + char_descriptor->uuid_type, + &char_descriptor->uuid, + char_data_size, + char_descriptor->char_properties, + char_descriptor->security_permissions, + char_descriptor->gatt_evt_mask, + GATT_MIN_READ_KEY_SIZE, + char_descriptor->is_variable, + &char_instance->handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); + } + + char_instance->descriptor_handle = 0; + if((status == 0) && char_descriptor->descriptor_params) { + uint8_t const* char_data = NULL; + const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = + char_descriptor->descriptor_params; + bool release_data = char_data_descriptor->data_callback.fn( + char_data_descriptor->data_callback.context, &char_data, &char_data_size); + + status = aci_gatt_add_char_desc( + svc_handle, + char_instance->handle, + char_data_descriptor->uuid_type, + &char_data_descriptor->uuid, + char_data_descriptor->max_length, + char_data_size, + char_data, + char_data_descriptor->security_permissions, + char_data_descriptor->access_permissions, + char_data_descriptor->gatt_evt_mask, + GATT_MIN_READ_KEY_SIZE, + char_data_descriptor->is_variable, + &char_instance->descriptor_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); + } + if(release_data) { + free((void*)char_data); + } + } +} + +void flipper_gatt_characteristic_delete( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance) { + tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); + if(status) { + FURI_LOG_E( + TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); + } + free((void*)char_instance->characteristic); +} + +bool flipper_gatt_characteristic_update( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance, + const void* source) { + furi_assert(char_instance); + const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; + FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); + + const uint8_t* char_data = NULL; + uint16_t char_data_size = 0; + bool release_data = false; + if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { + char_data = char_descriptor->data.fixed.ptr; + if(source) { + char_data = (uint8_t*)source; + } + char_data_size = char_descriptor->data.fixed.length; + } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { + const void* context = char_descriptor->data.callback.context; + if(source) { + context = source; + } + release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); + } + + tBleStatus result = aci_gatt_update_char_value( + svc_handle, char_instance->handle, 0, char_data_size, char_data); + if(result) { + FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); + } + if(release_data) { + free((void*)char_data); + } + return result != BLE_STATUS_SUCCESS; +} \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.h b/firmware/targets/f7/ble_glue/services/gatt_char.h new file mode 100644 index 000000000..959ab67a4 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/gatt_char.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Callback signature for getting characteristic data +// Is called when characteristic is created to get max data length. Data ptr is NULL in this case +// The result is passed to aci_gatt_add_char as "Char_Value_Length" +// For updates, called with a context - see flipper_gatt_characteristic_update +// Returns true if *data ownership is transferred to the caller and will be freed +typedef bool (*cbFlipperGattCharacteristicData)( + const void* context, + const uint8_t** data, + uint16_t* data_len); + +typedef enum { + FlipperGattCharacteristicDataFixed, + FlipperGattCharacteristicDataCallback, +} FlipperGattCharacteristicDataType; + +typedef struct { + Char_Desc_Uuid_t uuid; + struct { + cbFlipperGattCharacteristicData fn; + const void* context; + } data_callback; + uint8_t uuid_type; + uint8_t max_length; + uint8_t security_permissions; + uint8_t access_permissions; + uint8_t gatt_evt_mask; + uint8_t is_variable; +} FlipperGattCharacteristicDescriptorParams; + +typedef struct { + const char* name; + FlipperGattCharacteristicDescriptorParams* descriptor_params; + union { + struct { + const uint8_t* ptr; + uint16_t length; + } fixed; + struct { + cbFlipperGattCharacteristicData fn; + const void* context; + } callback; + } data; + Char_UUID_t uuid; + // Some packed bitfields to save space + FlipperGattCharacteristicDataType data_prop_type : 2; + uint8_t is_variable : 2; + uint8_t uuid_type : 2; + uint8_t char_properties; + uint8_t security_permissions; + uint8_t gatt_evt_mask; +} FlipperGattCharacteristicParams; + +_Static_assert( + sizeof(FlipperGattCharacteristicParams) == 36, + "FlipperGattCharacteristicParams size must be 36 bytes"); + +typedef struct { + const FlipperGattCharacteristicParams* characteristic; + uint16_t handle; + uint16_t descriptor_handle; +} FlipperGattCharacteristicInstance; + +// Initialize a characteristic instance; copies the characteristic descriptor into the instance +void flipper_gatt_characteristic_init( + uint16_t svc_handle, + const FlipperGattCharacteristicParams* char_descriptor, + FlipperGattCharacteristicInstance* char_instance); + +// Delete a characteristic instance; frees the copied characteristic descriptor from the instance +void flipper_gatt_characteristic_delete( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance); + +// Update a characteristic instance; if source==NULL, uses the data from the characteristic +// - For fixed data, fixed.ptr is used as the source if source==NULL +// - For callback-based data, collback.context is passed as the context if source==NULL +bool flipper_gatt_characteristic_update( + uint16_t svc_handle, + FlipperGattCharacteristicInstance* char_instance, + const void* source); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c new file mode 100644 index 000000000..cde26b267 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -0,0 +1,293 @@ +#include "hid_service.h" +#include "app_common.h" +#include +#include "gatt_char.h" + +#include + +#define TAG "BtHid" + +typedef enum { + HidSvcGattCharacteristicProtocolMode = 0, + HidSvcGattCharacteristicReportMap, + HidSvcGattCharacteristicInfo, + HidSvcGattCharacteristicCtrlPoint, + HidSvcGattCharacteristicCount, +} HidSvcGattCharacteristicId; + +typedef struct { + uint8_t report_idx; + uint8_t report_type; +} HidSvcReportId; + +static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); + +static bool + hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { + const HidSvcReportId* report_id = context; + *data_len = sizeof(HidSvcReportId); + if(data) { + *data = (const uint8_t*)report_id; + } + return false; +} + +typedef struct { + const void* data_ptr; + uint16_t data_len; +} HidSvcDataWrapper; + +static bool + hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { + const HidSvcDataWrapper* report_data = context; + if(data) { + *data = report_data->data_ptr; + *data_len = report_data->data_len; + } else { + *data_len = HID_SVC_REPORT_MAP_MAX_LEN; + } + return false; +} + +static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { + [HidSvcGattCharacteristicProtocolMode] = + {.name = "Protocol Mode", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicReportMap] = + {.name = "Report Map", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = hid_svc_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [HidSvcGattCharacteristicInfo] = + {.name = "HID Information", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = HID_SVC_INFO_LEN, + .data.fixed.ptr = NULL, + .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicCtrlPoint] = + {.name = "HID Control Point", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, + .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, +}; + +static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { + .uuid_type = UUID_TYPE_16, + .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, + .max_length = HID_SVC_REPORT_REF_LEN, + .data_callback.fn = hid_svc_char_desc_data_callback, + .security_permissions = ATTR_PERMISSION_NONE, + .access_permissions = ATTR_ACCESS_READ_WRITE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT, +}; + +static const FlipperGattCharacteristicParams hid_svc_report_template = { + .name = "Report", + .data_prop_type = FlipperGattCharacteristicDataCallback, + .data.callback.fn = hid_svc_report_data_callback, + .data.callback.context = NULL, + .uuid.Char_UUID_16 = REPORT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE, +}; + +typedef struct { + uint16_t svc_handle; + FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; + FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; + FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; + FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; +} HIDSvc; + +static HIDSvc* hid_svc = NULL; + +static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { + SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); + evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; + // aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + // Process modification events + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { + // Process notification confirmation + ret = SVCCTL_EvtAckFlowEnable; + } + } + return ret; +} + +void hid_svc_start() { + tBleStatus status; + hid_svc = malloc(sizeof(HIDSvc)); + Service_UUID_t svc_uuid = {}; + + // Register event handler + SVCCTL_RegisterSvcHandler(hid_svc_event_handler); + // Add service + svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; + /** + * Add Human Interface Device Service + */ + status = aci_gatt_add_service( + UUID_TYPE_16, + &svc_uuid, + PRIMARY_SERVICE, + 2 + /* protocol mode */ + (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + + 2, /* Service + Report Map + HID Information + HID Control Point */ + &hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add HID service: %d", status); + } + + for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); + } + uint8_t protocol_mode = 1; + flipper_gatt_characteristic_update( + hid_svc->svc_handle, + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], + &protocol_mode); + + // reports + FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; + FlipperGattCharacteristicParams report_char; + HidSvcReportId report_id; + + memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); + memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); + + hid_svc_char_descr.data_callback.context = &report_id; + report_char.descriptor_params = &hid_svc_char_descr; + + typedef struct { + uint8_t report_type; + uint8_t report_count; + FlipperGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + report_id.report_type = hid_report_chars[report_type_idx].report_type; + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + report_id.report_idx = report_idx + 1; + flipper_gatt_characteristic_init( + hid_svc->svc_handle, + &report_char, + &hid_report_chars[report_type_idx].chars[report_idx]); + } + } +} + +bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); +} + +bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); + + HidSvcDataWrapper report_data = { + .data_ptr = data, + .data_len = len, + }; + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); +} + +bool hid_svc_update_info(uint8_t* data) { + furi_assert(data); + furi_assert(hid_svc); + + return flipper_gatt_characteristic_update( + hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); +} + +bool hid_svc_is_started() { + return hid_svc != NULL; +} + +void hid_svc_stop() { + tBleStatus status; + if(hid_svc) { + // Delete characteristics + for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); + } + + typedef struct { + uint8_t report_count; + FlipperGattCharacteristicInstance* chars; + } HidSvcReportCharProps; + + HidSvcReportCharProps hid_report_chars[] = { + {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, + {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, + {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, + }; + + for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); + report_type_idx++) { + for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; + report_idx++) { + flipper_gatt_characteristic_delete( + hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); + } + } + + // Delete service + status = aci_gatt_del_service(hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); + } + free(hid_svc); + hid_svc = NULL; + } +} diff --git a/firmware/targets/f7/ble_glue/hid_service.h b/firmware/targets/f7/ble_glue/services/hid_service.h similarity index 87% rename from firmware/targets/f7/ble_glue/hid_service.h rename to firmware/targets/f7/ble_glue/services/hid_service.h index 723460d49..211adcd6c 100644 --- a/firmware/targets/f7/ble_glue/hid_service.h +++ b/firmware/targets/f7/ble_glue/services/hid_service.h @@ -25,4 +25,5 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); -bool hid_svc_update_info(uint8_t* data, uint16_t len); +// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) +bool hid_svc_update_info(uint8_t* data); diff --git a/firmware/targets/f7/ble_glue/serial_service.c b/firmware/targets/f7/ble_glue/services/serial_service.c similarity index 57% rename from firmware/targets/f7/ble_glue/serial_service.c rename to firmware/targets/f7/ble_glue/services/serial_service.c index c6421dc28..ab009bbfc 100644 --- a/firmware/targets/f7/ble_glue/serial_service.c +++ b/firmware/targets/f7/ble_glue/services/serial_service.c @@ -1,17 +1,67 @@ #include "serial_service.h" #include "app_common.h" #include +#include "gatt_char.h" #include +#include "serial_service_uuid.inc" + #define TAG "BtSerialSvc" +typedef enum { + SerialSvcGattCharacteristicTx = 0, + SerialSvcGattCharacteristicRx, + SerialSvcGattCharacteristicFlowCtrl, + SerialSvcGattCharacteristicStatus, + SerialSvcGattCharacteristicCount, +} SerialSvcGattCharacteristicId; + +static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { + [SerialSvcGattCharacteristicTx] = + {.name = "TX", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, + .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [SerialSvcGattCharacteristicRx] = + {.name = "RX", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, + .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [SerialSvcGattCharacteristicFlowCtrl] = + {.name = "Flow control", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(uint32_t), + .uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [SerialSvcGattCharacteristicStatus] = { + .name = "RPC status", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = sizeof(SerialServiceRpcStatus), + .uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, + .is_variable = CHAR_VALUE_LEN_CONSTANT}}; + typedef struct { uint16_t svc_handle; - uint16_t rx_char_handle; - uint16_t tx_char_handle; - uint16_t flow_ctrl_char_handle; - uint16_t rpc_status_char_handle; + FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; FuriMutex* buff_size_mtx; uint32_t buff_size; uint16_t bytes_ready_to_receive; @@ -21,17 +71,6 @@ typedef struct { static SerialSvc* serial_svc = NULL; -static const uint8_t service_uuid[] = - {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; -static const uint8_t char_tx_uuid[] = - {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -static const uint8_t char_rx_uuid[] = - {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -static const uint8_t flow_ctrl_uuid[] = - {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -static const uint8_t rpc_status_uuid[] = - {0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; - static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); @@ -40,11 +79,14 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; - if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { + if(attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) { // Descriptor handle ret = SVCCTL_EvtAckFlowEnable; FURI_LOG_D(TAG, "RX descriptor event"); - } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { + } else if( + attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); if(serial_svc->callback) { furi_check( @@ -70,7 +112,9 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } ret = SVCCTL_EvtAckFlowEnable; - } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { + } else if( + attribute_modified->Attr_Handle == + serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { SerialServiceRpcStatus* rpc_status = (SerialServiceRpcStatus*)attribute_modified->Attr_Data; if(*rpc_status == SerialServiceRpcStatusNotActive) { @@ -97,18 +141,12 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { } static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { - tBleStatus ble_status = aci_gatt_update_char_value( - serial_svc->svc_handle, - serial_svc->rpc_status_char_handle, - 0, - sizeof(SerialServiceRpcStatus), - (uint8_t*)&status); - if(ble_status) { - FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status); - } + flipper_gatt_characteristic_update( + serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); } void serial_svc_start() { + UNUSED(serial_svc_chars); tBleStatus status; serial_svc = malloc(sizeof(SerialSvc)); // Register event handler @@ -116,72 +154,17 @@ void serial_svc_start() { // Add service status = aci_gatt_add_service( - UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); + UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); } - // Add RX characteristics - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)char_rx_uuid, - SERIAL_SVC_DATA_LEN_MAX, - CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, - ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_VARIABLE, - &serial_svc->rx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status); + // Add characteristics + for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); } - // Add TX characteristic - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)char_tx_uuid, - SERIAL_SVC_DATA_LEN_MAX, - CHAR_PROP_READ | CHAR_PROP_INDICATE, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_VARIABLE, - &serial_svc->tx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status); - } - // Add Flow Control characteristic - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)flow_ctrl_uuid, - sizeof(uint32_t), - CHAR_PROP_READ | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ, - GATT_DONT_NOTIFY_EVENTS, - 10, - CHAR_VALUE_LEN_CONSTANT, - &serial_svc->flow_ctrl_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); - } - // Add RPC status characteristic - status = aci_gatt_add_char( - serial_svc->svc_handle, - UUID_TYPE_128, - (const Char_UUID_t*)rpc_status_uuid, - sizeof(SerialServiceRpcStatus), - CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, - ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - GATT_NOTIFY_ATTRIBUTE_WRITE, - 10, - CHAR_VALUE_LEN_CONSTANT, - &serial_svc->rpc_status_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status); - } serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); // Allocate buffer size mutex serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); @@ -196,13 +179,12 @@ void serial_svc_set_callbacks( serial_svc->context = context; serial_svc->buff_size = buff_size; serial_svc->bytes_ready_to_receive = buff_size; + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - aci_gatt_update_char_value( + flipper_gatt_characteristic_update( serial_svc->svc_handle, - serial_svc->flow_ctrl_char_handle, - 0, - sizeof(uint32_t), - (uint8_t*)&buff_size_reversed); + &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], + &buff_size_reversed); } void serial_svc_notify_buffer_is_empty() { @@ -213,13 +195,12 @@ void serial_svc_notify_buffer_is_empty() { if(serial_svc->bytes_ready_to_receive == 0) { FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); serial_svc->bytes_ready_to_receive = serial_svc->buff_size; + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - aci_gatt_update_char_value( + flipper_gatt_characteristic_update( serial_svc->svc_handle, - serial_svc->flow_ctrl_char_handle, - 0, - sizeof(uint32_t), - (uint8_t*)&buff_size_reversed); + &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], + &buff_size_reversed); } furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } @@ -227,22 +208,8 @@ void serial_svc_notify_buffer_is_empty() { void serial_svc_stop() { tBleStatus status; if(serial_svc) { - // Delete characteristics - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status); - } - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status); - } - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); - } - status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status); + for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); } // Delete service status = aci_gatt_del_service(serial_svc->svc_handle); @@ -273,7 +240,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { tBleStatus result = aci_gatt_update_char_value_ext( 0, serial_svc->svc_handle, - serial_svc->tx_char_handle, + serial_svc->chars[SerialSvcGattCharacteristicTx].handle, remained ? 0x00 : 0x02, data_len, value_offset, diff --git a/firmware/targets/f7/ble_glue/serial_service.h b/firmware/targets/f7/ble_glue/services/serial_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/serial_service.h rename to firmware/targets/f7/ble_glue/services/serial_service.h diff --git a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc new file mode 100644 index 000000000..a297d9ad6 --- /dev/null +++ b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc @@ -0,0 +1,12 @@ + +static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ + { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; + +#define SERIAL_SVC_TX_CHAR_UUID \ + { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_RX_CHAR_UUID \ + { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_FLOW_CONTROL_UUID \ + { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +#define SERIAL_SVC_RPC_STATUS_UUID \ + { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 3639094f7..6ff9f0e3b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -8,8 +8,7 @@ #include #include #include -#include "battery_service.h" - +#include #include #define TAG "FuriHalBt" diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 8259be2f6..7ec712af4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -1,11 +1,11 @@ #include #include -#include "usb_hid.h" -#include "dev_info_service.h" -#include "battery_service.h" -#include "hid_service.h" +#include +#include +#include #include +#include #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) @@ -153,7 +153,7 @@ void furi_hal_bt_hid_start() { FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, }; - hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); + hid_svc_update_info(hid_info_val); } void furi_hal_bt_hid_stop() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c index 2539e6bd0..2927d946f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -1,7 +1,7 @@ #include -#include "dev_info_service.h" -#include "battery_service.h" -#include "serial_service.h" +#include +#include +#include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 6ba38cb5e..4d538265d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h index 1b6e79ab0..0472d31d1 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h @@ -1,6 +1,6 @@ #pragma once -#include "serial_service.h" +#include #ifdef __cplusplus extern "C" { From ce25d6339ef26e3e5ae0ae93acecf42ea9df8f0e Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Thu, 8 Jun 2023 13:53:29 +0300 Subject: [PATCH 104/370] Byte input add feature: editor without keyboard --- .../services/gui/modules/byte_input.c | 238 +++++++++++++----- 1 file changed, 171 insertions(+), 67 deletions(-) diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index b2d21f7ae..554a7ca5c 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -25,7 +25,7 @@ typedef struct { bool selected_high_nibble; uint8_t selected_byte; - int8_t selected_row; // row -1 - input, row 0 & 1 - keyboard + int8_t selected_row; // row -2 - mini_editor, -1 - input, row 0 & 1 - keyboard uint8_t selected_column; uint8_t first_visible_byte; } ByteInputModel; @@ -149,6 +149,8 @@ static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { return byte; } +const char num_to_char[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + /** * @brief Draw input box (common view) * @@ -158,6 +160,10 @@ static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { const uint8_t text_x = 8; const uint8_t text_y = 25; + const uint8_t text_y2 = 40; + const bool draw_index_line = + (model->selected_row == -2) && + (model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes + 1) <= 100); elements_slightly_rounded_frame(canvas, 6, 14, 116, 15); @@ -225,6 +231,27 @@ static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { text_y, byte_input_get_nibble_text(model->bytes[i], false)); } + + if(draw_index_line) { + canvas_draw_glyph( + canvas, text_x + 2 + byte_position * 14, text_y2, num_to_char[(i + 1) / 10]); + + canvas_draw_glyph( + canvas, text_x + 8 + byte_position * 14, text_y2, num_to_char[(i + 1) % 10]); + } + } + + if((model->selected_row == -2) && + (model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes + 1) > 100)) { + char str[20]; + + canvas_set_font(canvas, FontSecondary); + snprintf(str, 20, "Selected index"); + canvas_draw_str(canvas, text_x, text_y2, str); + + canvas_set_font(canvas, FontPrimary); + snprintf(str, 20, "%u", (model->selected_byte + 1)); + canvas_draw_str(canvas, text_x + 75, text_y2, str); } if(model->bytes_count - model->first_visible_byte > max_drawable_bytes) { @@ -379,6 +406,17 @@ static void byte_input_inc_selected_byte(ByteInputModel* model) { } } +static void byte_input_inc_selected_byte_mini(ByteInputModel* model) { + if((model->selected_byte < model->bytes_count - 1) || model->selected_high_nibble) { + if(!model->selected_high_nibble) { + model->selected_high_nibble = !model->selected_high_nibble; + byte_input_inc_selected_byte(model); + } else { + model->selected_high_nibble = !model->selected_high_nibble; + } + } +} + /** * @brief Decrease selected byte position * @@ -397,6 +435,17 @@ static void byte_input_dec_selected_byte(ByteInputModel* model) { } } +static void byte_input_dec_selected_byte_mini(ByteInputModel* model) { + if(model->selected_byte > 0 || !model->selected_high_nibble) { + if(model->selected_high_nibble) { + model->selected_high_nibble = !model->selected_high_nibble; + byte_input_dec_selected_byte(model); + } else { + model->selected_high_nibble = !model->selected_high_nibble; + } + } +} + /** * @brief Call input callback * @@ -436,8 +485,18 @@ static void byte_input_clear_selected_byte(ByteInputModel* model) { * @param model */ static void byte_input_handle_up(ByteInputModel* model) { - if(model->selected_row > -1) { + if(model->selected_row > -2) { model->selected_row -= 1; + } else if(model->selected_row == -2) { + if(!model->selected_high_nibble) { + model->bytes[model->selected_byte] = (model->bytes[model->selected_byte] & 0xF0) | + ((model->bytes[model->selected_byte] + 1) & 0x0F); + } else { + model->bytes[model->selected_byte] = + ((model->bytes[model->selected_byte] + 0x10) & 0xF0) | + (model->bytes[model->selected_byte] & 0x0F); + } + byte_input_call_changed_callback(model); } } @@ -447,12 +506,24 @@ static void byte_input_handle_up(ByteInputModel* model) { * @param model */ static void byte_input_handle_down(ByteInputModel* model) { - if(byte_input_keyboard_selected(model)) { - if(model->selected_row < keyboard_row_count - 1) { - model->selected_row += 1; + if(model->selected_row != -2) { + if(byte_input_keyboard_selected(model)) { + if(model->selected_row < keyboard_row_count - 1) { + model->selected_row += 1; + } + } else { + byte_input_transition_from_keyboard(model); } } else { - byte_input_transition_from_keyboard(model); + if(!model->selected_high_nibble) { + model->bytes[model->selected_byte] = (model->bytes[model->selected_byte] & 0xF0) | + ((model->bytes[model->selected_byte] - 1) & 0x0F); + } else { + model->bytes[model->selected_byte] = + ((model->bytes[model->selected_byte] - 0x10) & 0xF0) | + (model->bytes[model->selected_byte] & 0x0F); + } + byte_input_call_changed_callback(model); } } @@ -461,7 +532,7 @@ static void byte_input_handle_down(ByteInputModel* model) { * * @param model */ -static void byte_input_handle_left(ByteInputModel* model) { +static void byte_input_handle_left(ByteInputModel* model) { // XXX if(byte_input_keyboard_selected(model)) { if(model->selected_column > 0) { model->selected_column -= 1; @@ -469,7 +540,11 @@ static void byte_input_handle_left(ByteInputModel* model) { model->selected_column = byte_input_get_row_size(model->selected_row) - 1; } } else { - byte_input_dec_selected_byte(model); + if(model->selected_row != -2) { + byte_input_dec_selected_byte(model); + } else { + byte_input_dec_selected_byte_mini(model); + } } } @@ -478,7 +553,7 @@ static void byte_input_handle_left(ByteInputModel* model) { * * @param model */ -static void byte_input_handle_right(ByteInputModel* model) { +static void byte_input_handle_right(ByteInputModel* model) { // XXX if(byte_input_keyboard_selected(model)) { if(model->selected_column < byte_input_get_row_size(model->selected_row) - 1) { model->selected_column += 1; @@ -486,7 +561,11 @@ static void byte_input_handle_right(ByteInputModel* model) { model->selected_column = 0; } } else { - byte_input_inc_selected_byte(model); + if(model->selected_row != -2) { + byte_input_inc_selected_byte(model); + } else { + byte_input_inc_selected_byte_mini(model); + } } } @@ -514,6 +593,8 @@ static void byte_input_handle_ok(ByteInputModel* model) { } byte_input_call_changed_callback(model); } + } else if(model->selected_row == -2) { + byte_input_call_input_callback(model); } else { byte_input_transition_from_keyboard(model); } @@ -541,68 +622,77 @@ static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { byte_input_draw_input(canvas, model); } - for(uint8_t row = 0; row < keyboard_row_count; row++) { - const uint8_t column_count = byte_input_get_row_size(row); - const ByteInputKey* keys = byte_input_get_row(row); + if(model->selected_row == -2) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 3, 52, &I_Pin_back_arrow_10x8); + canvas_draw_str_aligned(canvas, 16, 60, AlignLeft, AlignBottom, "back to keyboard"); + } else { + // Draw keyboard + for(uint8_t row = 0; row < keyboard_row_count; row++) { + const uint8_t column_count = byte_input_get_row_size(row); + const ByteInputKey* keys = byte_input_get_row(row); - for(size_t column = 0; column < column_count; column++) { - if(keys[column].value == enter_symbol) { - canvas_set_color(canvas, ColorBlack); - if(model->selected_row == row && model->selected_column == column) { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeySaveSelected_24x11); - } else { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeySave_24x11); - } - } else if(keys[column].value == backspace_symbol) { - canvas_set_color(canvas, ColorBlack); - if(model->selected_row == row && model->selected_column == column) { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeyBackspaceSelected_16x9); - } else { - canvas_draw_icon( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - &I_KeyBackspace_16x9); - } - } else { - if(model->selected_row == row && model->selected_column == column) { + for(size_t column = 0; column < column_count; column++) { + if(keys[column].value == enter_symbol) { canvas_set_color(canvas, ColorBlack); - canvas_draw_box( - canvas, - keyboard_origin_x + keys[column].x - 3, - keyboard_origin_y + keys[column].y - 10, - 11, - 13); - canvas_set_color(canvas, ColorWhite); - } else if(model->selected_row == -1 && row == 0 && model->selected_column == column) { + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySaveSelected_24x11); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySave_24x11); + } + } else if(keys[column].value == backspace_symbol) { canvas_set_color(canvas, ColorBlack); - canvas_draw_frame( - canvas, - keyboard_origin_x + keys[column].x - 3, - keyboard_origin_y + keys[column].y - 10, - 11, - 13); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspaceSelected_16x9); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspace_16x9); + } } else { - canvas_set_color(canvas, ColorBlack); - } + if(model->selected_row == row && model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + keyboard_origin_x + keys[column].x - 3, + keyboard_origin_y + keys[column].y - 10, + 11, + 13); + canvas_set_color(canvas, ColorWhite); + } else if( + model->selected_row == -1 && row == 0 && + model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame( + canvas, + keyboard_origin_x + keys[column].x - 3, + keyboard_origin_y + keys[column].y - 10, + 11, + 13); + } else { + canvas_set_color(canvas, ColorBlack); + } - canvas_draw_glyph( - canvas, - keyboard_origin_x + keys[column].x, - keyboard_origin_y + keys[column].y, - keys[column].value); + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + keys[column].value); + } } } } @@ -656,6 +746,20 @@ static bool byte_input_view_input_callback(InputEvent* event, void* context) { } } + if(event->type == InputTypeShort && event->key == InputKeyBack) { + // Back to keyboard + with_view_model( + byte_input->view, + ByteInputModel * model, + { + if(model->selected_row == -2) { + model->selected_row += 1; + consumed = true; + }; + }, + true); + } + if((event->type == InputTypeLong || event->type == InputTypeRepeat) && event->key == InputKeyBack) { with_view_model( From 1c306a04fcc5b432d4d50931a39ff0acd58be303 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 8 Jun 2023 16:43:05 +0400 Subject: [PATCH 105/370] [FL-3359] github: added debugapps artifact; packaging resources per-target (#2750) * github: added debugapps artifact; packaging resources per-target * github: target name fixes * github: fixed path for debug apps * scripts: dist: removed lib stub artifact * scripts: fixed broken SDK * github: removed unused step --- .github/workflows/build.yml | 29 ++++++++++------------------- scripts/sconsdist.py | 11 ----------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fb0f13e20..9fbcb16eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,10 +52,8 @@ jobs: - name: 'Make artifacts directory' run: | - rm -rf artifacts - rm -rf map_analyser_files - mkdir artifacts - mkdir map_analyser_files + rm -rf artifacts map_analyser_files + mkdir artifacts map_analyser_files - name: 'Bundle scripts' if: ${{ !github.event.pull_request.head.repo.fork }} @@ -66,28 +64,21 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET copro_dist updater_package \ - ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} - done - - - name: 'Move upload files' - if: ${{ !github.event.pull_request.head.repo.fork }} - run: | - set -e - for TARGET in ${TARGETS}; do + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET_HW copro_dist updater_package \ + ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} mv dist/${TARGET}-*/* artifacts/ + tar czpf "artifacts/flipper-z-${TARGET}-resources-${SUFFIX}.tgz" \ + -C assets resources + ./fbt TARGET_HW=$TARGET_HW fap_dist + tar czpf "artifacts/flipper-z-${TARGET}-debugapps-${SUFFIX}.tgz" \ + -C dist/${TARGET}-*/apps/Debug . done - name: "Check for uncommitted changes" run: | git diff --exit-code - - name: 'Bundle resources' - if: ${{ !github.event.pull_request.head.repo.fork }} - run: | - tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources - - name: 'Bundle core2 firmware' if: ${{ !github.event.pull_request.head.repo.fork }} run: | diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 23f9526a0..2cf43dce0 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -84,17 +84,6 @@ class Main(App): if exists(sdk_folder := join(obj_directory, foldertype)): self.note_dist_component(foldertype, "dir", sdk_folder) - # TODO: remove this after everyone migrates to new uFBT - self.create_zip_stub("lib") - - def create_zip_stub(self, foldertype): - with zipfile.ZipFile( - self.get_dist_path(self.get_dist_file_name(foldertype, "zip")), - "w", - zipfile.ZIP_DEFLATED, - ) as _: - pass - def copy(self) -> int: self._dist_components: dict[str, str] = dict() self.projects: dict[str, ProjectDir] = dict( From bff592126675de07e27d0f2749736119f2917b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 8 Jun 2023 22:05:35 +0900 Subject: [PATCH 106/370] FuriHal: always clock SMPS from HSI (#2643) * FuriHal: always clock SMPS from HSI * FuriHal: add clock startup time check, ensure that we conform to core2 config value * FuriHal: set sleep mode to legacy if clock startup time is too high --------- Co-authored-by: hedger --- firmware/targets/f7/furi_hal/furi_hal_clock.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index 9d228f0f5..770b74296 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -48,6 +49,10 @@ void furi_hal_clock_init() { LL_RCC_LSI1_Enable(); while(!LS_CLOCK_IS_READY()) ; + + /* RF wakeup */ + LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); + LL_EXTI_EnableIT_0_31( LL_EXTI_LINE_18); /* Why? Because that's why. See RM0434, Table 61. CPU1 vector table. */ LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_18); @@ -111,7 +116,7 @@ void furi_hal_clock_init() { LL_RCC_SetCLK48ClockSource(LL_RCC_CLK48_CLKSOURCE_PLLSAI1); LL_RCC_HSI_EnableInStopMode(); // Ensure that MR is capable of work in STOP0 - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); + LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1); LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE); @@ -124,8 +129,8 @@ void furi_hal_clock_switch_to_hsi() { while(!LL_RCC_HSI_IsReady()) ; - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI); LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); + furi_assert(LL_RCC_GetSMPSClockSource() == LL_RCC_SMPS_CLKSOURCE_HSI); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) ; @@ -138,6 +143,7 @@ void furi_hal_clock_switch_to_hsi() { } void furi_hal_clock_switch_to_pll() { + uint32_t clock_start_time = DWT->CYCCNT; LL_RCC_HSE_Enable(); LL_RCC_PLL_Enable(); LL_RCC_PLLSAI1_Enable(); @@ -156,10 +162,15 @@ void furi_hal_clock_switch_to_pll() { ; LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); - LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE); while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) ; + + uint32_t total = DWT->CYCCNT - clock_start_time; + if(total > (20 * 0x148)) { + furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep); + furi_crash("Slow HSE/PLL startup"); + } } void furi_hal_clock_suspend_tick() { From 253478dba588c00bcd63705400f8522a7c795301 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov Date: Sat, 13 May 2023 01:21:34 +0300 Subject: [PATCH 107/370] SubGHz: Fix notifications * Emit error notification on memory full * Set IDLE notification (led off) at Info scene if Memory Full * Enable Rx notification and Rx after deletion of item after "Memory full" state --- applications/main/subghz/scenes/subghz_scene_receiver.c | 9 +++++++-- .../main/subghz/scenes/subghz_scene_receiver_info.c | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 26e92e983..43da1161a 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -113,6 +113,9 @@ static void subghz_scene_add_to_history_callback( subghz_history_get_type_protocol(history, idx)); subghz_scene_receiver_update_statusbar(subghz); + if(subghz_history_get_text_space_left(subghz->history, NULL)) { + notification_message(subghz->notifications, &sequence_error); + } } subghz_receiver_reset(receiver); furi_string_free(item_name); @@ -183,7 +186,9 @@ void subghz_scene_receiver_on_enter(void* context) { } } - subghz->state_notifications = SubGhzNotificationStateRx; + if(!subghz_history_get_text_space_left(subghz->history, NULL)) { + subghz->state_notifications = SubGhzNotificationStateRx; + } subghz_txrx_rx_start(subghz->txrx); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); @@ -244,7 +249,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case SubGhzCustomEventViewReceiverConfig: - subghz->state_notifications = SubGhzNotificationStateIDLE; + // Actually signals are received but SubGhzNotificationStateRx is not working inside Config Scene subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_set_scene_state( subghz->scene_manager, SubGhzViewIdReceiver, SubGhzCustomEventManagerSet); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index e3ff485d2..fc1c1a8e9 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -109,6 +109,10 @@ void subghz_scene_receiver_info_on_enter(void* context) { subghz_custom_btns_reset(); subghz_scene_receiver_info_draw_widget(subghz); + + if(!subghz_history_get_text_space_left(subghz->history, NULL)) { + subghz->state_notifications = SubGhzNotificationStateRx; + } } bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { @@ -142,7 +146,9 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_txrx_rx_start(subghz->txrx); subghz_txrx_hopper_unpause(subghz->txrx); - subghz->state_notifications = SubGhzNotificationStateRx; + if(!subghz_history_get_text_space_left(subghz->history, NULL)) { + subghz->state_notifications = SubGhzNotificationStateRx; + } } return true; } else if(event.event == SubGhzCustomEventSceneReceiverInfoSave) { From fd6b8cd99a08b6bced6f7d6d992819dcdc27fdfa Mon Sep 17 00:00:00 2001 From: Nikita Vostokov Date: Sat, 13 May 2023 02:02:23 +0300 Subject: [PATCH 108/370] Don't reset cursor position after 2x Back btn press --- applications/main/subghz/scenes/subghz_scene_receiver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 43da1161a..257fe2222 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -134,6 +134,7 @@ void subghz_scene_receiver_on_enter(void* context) { subghz_txrx_set_preset(subghz->txrx, "AM650", subghz->last_settings->frequency, NULL, 0); subghz_history_reset(history); subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); + subghz->idx_menu_chosen = 0; } subghz_view_receiver_set_lock(subghz->subghz_receiver, subghz_is_locked(subghz)); @@ -209,7 +210,6 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz->state_notifications = SubGhzNotificationStateIDLE; subghz_txrx_stop(subghz->txrx); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); - subghz->idx_menu_chosen = 0; subghz_txrx_set_rx_calback(subghz->txrx, NULL, subghz); if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { From 5c2e7f9e08edaf04815d21c44aaf0686560c75c4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 15:53:24 +0100 Subject: [PATCH 109/370] Auto unlock rpc for u2f --- .../main/u2f/scenes/u2f_scene_error.c | 25 ------------------- applications/main/u2f/u2f_app.c | 14 ++++------- applications/main/u2f/u2f_app_i.h | 1 - 3 files changed, 5 insertions(+), 35 deletions(-) diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 33839a61a..36e490f1c 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -25,31 +25,6 @@ void u2f_scene_error_on_enter(void* context) { "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); widget_add_button_element( app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); - } else if(app->error == U2fAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->is_nsfw) { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Pull out from\nPC or phone to\nuse me like this."); - } else { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } } view_dispatcher_switch_to_view(app->view_dispatcher, U2fAppViewError); diff --git a/applications/main/u2f/u2f_app.c b/applications/main/u2f/u2f_app.c index db94a398c..1c8db40d0 100644 --- a/applications/main/u2f/u2f_app.c +++ b/applications/main/u2f/u2f_app.c @@ -49,16 +49,12 @@ U2fApp* u2f_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view)); - if(furi_hal_usb_is_locked()) { - app->error = U2fAppErrorCloseRpc; - scene_manager_next_scene(app->scene_manager, U2fSceneError); + furi_hal_usb_unlock(); + if(u2f_data_check(true)) { + scene_manager_next_scene(app->scene_manager, U2fSceneMain); } else { - if(u2f_data_check(true)) { - scene_manager_next_scene(app->scene_manager, U2fSceneMain); - } else { - app->error = U2fAppErrorNoFiles; - scene_manager_next_scene(app->scene_manager, U2fSceneError); - } + app->error = U2fAppErrorNoFiles; + scene_manager_next_scene(app->scene_manager, U2fSceneError); } return app; diff --git a/applications/main/u2f/u2f_app_i.h b/applications/main/u2f/u2f_app_i.h index 2896684c3..7a06caf05 100644 --- a/applications/main/u2f/u2f_app_i.h +++ b/applications/main/u2f/u2f_app_i.h @@ -18,7 +18,6 @@ typedef enum { U2fAppErrorNoFiles, - U2fAppErrorCloseRpc, } U2fAppError; typedef enum { From f2cc1fcac1f5c3dc82c24a1096ceb6d00466d7f2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:51:02 +0100 Subject: [PATCH 110/370] Add sanity checks for pin code validity --- applications/services/desktop/desktop.c | 8 ++++++-- applications/services/desktop/helpers/pin.c | 9 +++++++++ applications/services/desktop/helpers/pin.h | 2 ++ .../services/desktop/scenes/desktop_scene_lock_menu.c | 9 +++++---- .../scenes/desktop_settings_scene_pin_auth.c | 2 +- .../scenes/desktop_settings_scene_pin_menu.c | 3 ++- 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index b10fa53a7..9d48c580e 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -237,7 +237,7 @@ static void desktop_clock_timer_callback(void* context) { } void desktop_lock(Desktop* desktop, bool pin_lock) { - pin_lock = pin_lock && desktop->settings.pin_code.length > 0; + pin_lock = pin_lock && desktop_pin_is_valid(&desktop->settings.pin_code); if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { furi_hal_rtc_set_pin_fails(0); } @@ -457,7 +457,11 @@ int32_t desktop_srv(void* p) { Desktop* desktop = desktop_alloc(); - if(!DESKTOP_SETTINGS_LOAD(&desktop->settings)) { + bool ok = DESKTOP_SETTINGS_LOAD(&desktop->settings); + if(ok) { + ok = desktop_pin_is_valid(&desktop->settings.pin_code); + } + if(!ok) { memset(&desktop->settings, 0, sizeof(desktop->settings)); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); } diff --git a/applications/services/desktop/helpers/pin.c b/applications/services/desktop/helpers/pin.c index d6ab48df2..b41001552 100644 --- a/applications/services/desktop/helpers/pin.c +++ b/applications/services/desktop/helpers/pin.c @@ -53,3 +53,12 @@ bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) { return result; } + +bool desktop_pin_is_valid(const PinCode* pin_code) { + bool ok = pin_code->length >= MIN_PIN_SIZE && pin_code->length <= MAX_PIN_SIZE; + for(size_t i = 0; ok && i < pin_code->length; i++) { + ok = ok && (pin_code->data[i] == InputKeyUp || pin_code->data[i] == InputKeyDown || + pin_code->data[i] == InputKeyRight || pin_code->data[i] == InputKeyLeft); + } + return ok; +} diff --git a/applications/services/desktop/helpers/pin.h b/applications/services/desktop/helpers/pin.h index e5410723e..ce9fcfec3 100644 --- a/applications/services/desktop/helpers/pin.h +++ b/applications/services/desktop/helpers/pin.h @@ -9,3 +9,5 @@ void desktop_pin_lock_error_notify(); uint32_t desktop_pin_lock_get_fail_timeout(); bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2); + +bool desktop_pin_is_valid(const PinCode* pin_code); diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index c56fd16f2..d1c7e48bc 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -27,7 +27,8 @@ void desktop_scene_lock_menu_on_enter(void* context) { DESKTOP_SETTINGS_LOAD(&desktop->settings); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); - desktop_lock_menu_set_pin_state(desktop->lock_menu, desktop->settings.pin_code.length > 0); + desktop_lock_menu_set_pin_state( + desktop->lock_menu, desktop_pin_is_valid(&desktop->settings.pin_code)); desktop_lock_menu_set_stealth_mode_state( desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); desktop_lock_menu_set_idx(desktop->lock_menu, 3); @@ -63,7 +64,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); if(check_pin_changed) { DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock_menu_set_pin_state(desktop->lock_menu, true); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock(desktop, true); @@ -90,7 +91,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventLockPin: desktop_scene_lock_menu_save_settings(desktop); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock(desktop, true); } else { LoaderStatus status = @@ -105,7 +106,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventLockPinOff: desktop_scene_lock_menu_save_settings(desktop); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock(desktop, true); Power* power = furi_record_open(RECORD_POWER); furi_delay_ms(500); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c index f153aac7a..86886ead9 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -34,7 +34,7 @@ void desktop_settings_scene_pin_auth_on_enter(void* context) { DesktopSettingsApp* app = context; DESKTOP_SETTINGS_LOAD(&app->settings); - furi_assert(app->settings.pin_code.length > 0); + furi_assert(desktop_pin_is_valid(&app->settings.pin_code)); desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index 5721846c6..bd9dda727 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -4,6 +4,7 @@ #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" +#include "desktop/helpers/pin.h" #define SCENE_EVENT_SET_PIN 0 #define SCENE_EVENT_CHANGE_PIN 1 @@ -19,7 +20,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { Submenu* submenu = app->submenu; submenu_reset(submenu); - if(!app->settings.pin_code.length) { + if(!desktop_pin_is_valid(&app->settings.pin_code)) { submenu_add_item( submenu, "Set Pin", From d8ae931b7cd0e24cbe6a97ca600cd5ce1215f711 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 23:21:22 +0300 Subject: [PATCH 111/370] Fix led descriptor? --- .../f7/ble_glue/services/hid_service.c | 68 ++++++++++++++++++- .../f7/ble_glue/services/hid_service.h | 4 ++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c index cde26b267..55c738933 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.c +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -12,6 +12,7 @@ typedef enum { HidSvcGattCharacteristicReportMap, HidSvcGattCharacteristicInfo, HidSvcGattCharacteristicCtrlPoint, + HidSvcGattCharacteristicLed, HidSvcGattCharacteristicCount, } HidSvcGattCharacteristicId; @@ -49,6 +50,17 @@ static bool return false; } +static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { + .uuid_type = UUID_TYPE_16, + .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, + .max_length = HID_SVC_REPORT_REF_LEN, + .data_callback.fn = hid_svc_char_desc_data_callback, + .security_permissions = ATTR_PERMISSION_NONE, + .access_permissions = ATTR_ACCESS_READ_WRITE, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_CONSTANT, +}; + static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { [HidSvcGattCharacteristicProtocolMode] = {.name = "Protocol Mode", @@ -92,6 +104,20 @@ static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteris .security_permissions = ATTR_PERMISSION_NONE, .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, .is_variable = CHAR_VALUE_LEN_CONSTANT}, + [HidSvcGattCharacteristicLed] = + { + .name = "HID LED State", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = 1, + .uuid.Char_UUID_16 = REPORT_CHAR_UUID, + .uuid_type = UUID_TYPE_16, + .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, + .security_permissions = ATTR_PERMISSION_NONE, + .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | + GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, + .is_variable = CHAR_VALUE_LEN_CONSTANT, + .descriptor_params = &hid_svc_char_descr_led, + }, }; static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { @@ -124,6 +150,9 @@ typedef struct { FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; + // led state + HidLedStateEventCallback led_state_event_callback; + void* led_state_ctx; } HIDSvc; static HIDSvc* hid_svc = NULL; @@ -140,6 +169,32 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { // Process notification confirmation ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { + // Process write request + aci_gatt_write_permit_req_event_rp0* req = + (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; + + furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); + + // this check is likely to be incorrect, it will actually work in our case + // but we need to investigate gatt api to see what is the rules + // that specify attibute handle value from char handle (or the reverse) + if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { + hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); + aci_gatt_write_resp( + req->Connection_Handle, + req->Attribute_Handle, + 0x00, /* write_status = 0 (no error))*/ + 0x00, /* err_code */ + req->Data_Length, + req->Data); + aci_gatt_write_char_value( + req->Connection_Handle, + hid_svc->chars[HidSvcGattCharacteristicLed].handle, + req->Data_Length, + req->Data); + ret = SVCCTL_EvtAckFlowEnable; + } } } return ret; @@ -163,8 +218,8 @@ void hid_svc_start() { PRIMARY_SERVICE, 2 + /* protocol mode */ (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + - (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + - 2, /* Service + Report Map + HID Information + HID Control Point */ + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + + 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ &hid_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add HID service: %d", status); @@ -250,6 +305,15 @@ bool hid_svc_update_info(uint8_t* data) { hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); } +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { + furi_assert(hid_svc); + furi_assert(callback); + furi_assert(context); + + hid_svc->led_state_event_callback = callback; + hid_svc->led_state_ctx = context; +} + bool hid_svc_is_started() { return hid_svc != NULL; } diff --git a/firmware/targets/f7/ble_glue/services/hid_service.h b/firmware/targets/f7/ble_glue/services/hid_service.h index 211adcd6c..4d0ed4c4f 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.h +++ b/firmware/targets/f7/ble_glue/services/hid_service.h @@ -15,6 +15,8 @@ #define HID_SVC_REPORT_COUNT \ (HID_SVC_INPUT_REPORT_COUNT + HID_SVC_OUTPUT_REPORT_COUNT + HID_SVC_FEATURE_REPORT_COUNT) +typedef uint16_t (*HidLedStateEventCallback)(uint8_t state, void* ctx); + void hid_svc_start(); void hid_svc_stop(); @@ -27,3 +29,5 @@ bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16 // Expects data to be of length HID_SVC_INFO_LEN (4 bytes) bool hid_svc_update_info(uint8_t* data); + +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); From f4cc929a99805d634d5e8da1c42ee3d1af0853a3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 9 Jun 2023 00:53:16 +0300 Subject: [PATCH 112/370] Fix descriptor --- firmware/targets/f7/ble_glue/services/hid_service.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c index 55c738933..11f10b7b3 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.c +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -50,11 +50,16 @@ static bool return false; } +// LED Descriptor params for BadBT + +static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; + static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { .uuid_type = UUID_TYPE_16, .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, .max_length = HID_SVC_REPORT_REF_LEN, .data_callback.fn = hid_svc_char_desc_data_callback, + .data_callback.context = led_desc_context_buf, .security_permissions = ATTR_PERMISSION_NONE, .access_permissions = ATTR_ACCESS_READ_WRITE, .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, @@ -106,7 +111,8 @@ static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteris .is_variable = CHAR_VALUE_LEN_CONSTANT}, [HidSvcGattCharacteristicLed] = { - .name = "HID LED State", + .name = + "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars .data_prop_type = FlipperGattCharacteristicDataFixed, .data.fixed.length = 1, .uuid.Char_UUID_16 = REPORT_CHAR_UUID, @@ -170,6 +176,8 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { // Process notification confirmation ret = SVCCTL_EvtAckFlowEnable; } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { + // LED Characteristic and descriptor for BadBT to get numlock state for altchars + // // Process write request aci_gatt_write_permit_req_event_rp0* req = (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; From cb97cbbb27a86e2512c71198fe1c5e7b9a910032 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 9 Jun 2023 01:29:39 +0100 Subject: [PATCH 113/370] Fix for 1.13.3 stack --- firmware/targets/f7/ble_glue/ble_app.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 03ab2625e..7a2148b6b 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -70,12 +70,6 @@ static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { .min_tx_power = 0, .max_tx_power = 0, .rx_model_config = 1, - /* New stack (13.3->15.0) */ - .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB - .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB - .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) }}; bool ble_app_init() { From fbb6fe53868382f72adf92e00db186c285f2fa3e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 9 Jun 2023 01:35:30 +0100 Subject: [PATCH 114/370] Add stack upgrade patch file for future reference --- stack_1.15.0_upgrade.patch | 329 +++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 stack_1.15.0_upgrade.patch diff --git a/stack_1.15.0_upgrade.patch b/stack_1.15.0_upgrade.patch new file mode 100644 index 000000000..2df58a508 --- /dev/null +++ b/stack_1.15.0_upgrade.patch @@ -0,0 +1,329 @@ +diff --git a/fbt_options.py b/fbt_options.py +index 7c5857e09f..acc6b8a982 100644 +--- a/fbt_options.py ++++ b/fbt_options.py +@@ -22,7 +22,7 @@ DIST_SUFFIX = "XFW-0048_03062023" + COPRO_OB_DATA = "scripts/ob.data" + + # Must match lib/stm32wb_copro version +-COPRO_CUBE_VERSION = "1.13.3" ++COPRO_CUBE_VERSION = "1.15.0" + + COPRO_CUBE_DIR = "lib/stm32wb_copro" + +diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h +index 214c85acd2..8eaf230859 100644 +--- a/firmware/targets/f7/ble_glue/app_common.h ++++ b/firmware/targets/f7/ble_glue/app_common.h +@@ -33,6 +33,7 @@ extern "C" { + #include + + #include ++#include + + #include "app_conf.h" + +diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h +index aaa755a362..ee5115cfed 100644 +--- a/firmware/targets/f7/ble_glue/app_conf.h ++++ b/firmware/targets/f7/ble_glue/app_conf.h +@@ -8,6 +8,8 @@ + + #define CFG_TX_POWER (0x19) /* +0dBm */ + ++#define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR ++ + /** + * Define Advertising parameters + */ +diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c +index 35f53ae22c..d288528223 100644 +--- a/firmware/targets/f7/ble_glue/app_debug.c ++++ b/firmware/targets/f7/ble_glue/app_debug.c +@@ -33,7 +33,8 @@ PLACE_IN_SECTION("MB_MEM2") + ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; + PLACE_IN_SECTION("MB_MEM2") + ALIGN(4) +-static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; ++static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = ++ {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}, 0, 0, 0, 0, 0}; + + /** + * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT +diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c +index 7a2148b6b8..c0418d9fe8 100644 +--- a/firmware/targets/f7/ble_glue/ble_app.c ++++ b/firmware/targets/f7/ble_glue/ble_app.c +@@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; + PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; + + _Static_assert( +- sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 49, +- "Ble stack config structure size mismatch"); ++ sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, ++ "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); + + typedef struct { + FuriMutex* hci_mtx; +@@ -70,6 +70,12 @@ static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { + .min_tx_power = 0, + .max_tx_power = 0, + .rx_model_config = 1, ++ /* New stack (13.3->15.0) */ ++ .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set ++ .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set ++ .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB ++ .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB ++ .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) + }}; + + bool ble_app_init() { +diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h +index 0e4c8b398d..85f734b62c 100644 +--- a/firmware/targets/f7/ble_glue/ble_const.h ++++ b/firmware/targets/f7/ble_glue/ble_const.h +@@ -23,6 +23,7 @@ + #include + #include + #include "osal.h" ++#include "compiler.h" + + /* Default BLE variant */ + #ifndef BASIC_FEATURES +@@ -34,6 +35,9 @@ + #ifndef LL_ONLY + #define LL_ONLY 0 + #endif ++#ifndef LL_ONLY_BASIC ++#define LL_ONLY_BASIC 0 ++#endif + #ifndef BEACON_ONLY + #define BEACON_ONLY 0 + #endif +diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h +index 1c39628197..98a93d7126 100644 +--- a/firmware/targets/f7/ble_glue/compiler.h ++++ b/firmware/targets/f7/ble_glue/compiler.h +@@ -5,7 +5,7 @@ + ***************************************************************************** + * @attention + * +- * Copyright (c) 2018-2022 STMicroelectronics. ++ * Copyright (c) 2018-2023 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file +@@ -18,6 +18,14 @@ + #ifndef COMPILER_H__ + #define COMPILER_H__ + ++#ifndef __PACKED_STRUCT ++#define __PACKED_STRUCT PACKED(struct) ++#endif ++ ++#ifndef __PACKED_UNION ++#define __PACKED_UNION PACKED(union) ++#endif ++ + /** + * @brief This is the section dedicated to IAR toolchain + */ +diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c +index 9dfb5af89c..641eccc4ea 100644 +--- a/firmware/targets/f7/ble_glue/gap.c ++++ b/firmware/targets/f7/ble_glue/gap.c +@@ -1,5 +1,6 @@ + #include "gap.h" + ++#include "app_common.h" + #include + + #include +@@ -100,7 +101,7 @@ static void gap_verify_connection_parameters(Gap* gap) { + SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + hci_event_pckt* event_pckt; + evt_le_meta_event* meta_evt; +- evt_blue_aci* blue_evt; ++ evt_blecore_aci* blue_evt; + hci_le_phy_update_complete_event_rp0* evt_le_phy_update_complete; + uint8_t tx_phy; + uint8_t rx_phy; +@@ -112,7 +113,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + furi_mutex_acquire(gap->state_mutex, FuriWaitForever); + } + switch(event_pckt->evt) { +- case EVT_DISCONN_COMPLETE: { ++ case HCI_DISCONNECTION_COMPLETE_EVT_CODE: { + hci_disconnection_complete_event_rp0* disconnection_complete_event = + (hci_disconnection_complete_event_rp0*)event_pckt->data; + if(disconnection_complete_event->Connection_Handle == gap->service.connection_handle) { +@@ -131,10 +132,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + gap->on_event_cb(event, gap->context); + } break; + +- case EVT_LE_META_EVENT: ++ case HCI_LE_META_EVT_CODE: + meta_evt = (evt_le_meta_event*)event_pckt->data; + switch(meta_evt->subevent) { +- case EVT_LE_CONN_UPDATE_COMPLETE: { ++ case HCI_LE_CONNECTION_UPDATE_COMPLETE_SUBEVT_CODE: { + hci_le_connection_update_complete_event_rp0* event = + (hci_le_connection_update_complete_event_rp0*)meta_evt->data; + gap->connection_params.conn_interval = event->Conn_Interval; +@@ -148,7 +149,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + break; + } + +- case EVT_LE_PHY_UPDATE_COMPLETE: ++ case HCI_LE_PHY_UPDATE_COMPLETE_SUBEVT_CODE: + evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; + if(evt_le_phy_update_complete->Status) { + FURI_LOG_E( +@@ -164,7 +165,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + } + break; + +- case EVT_LE_CONN_COMPLETE: { ++ case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: { + hci_le_connection_complete_event_rp0* event = + (hci_le_connection_complete_event_rp0*)meta_evt->data; + gap->connection_params.conn_interval = event->Conn_Interval; +@@ -191,16 +192,16 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + } + break; + +- case EVT_VENDOR: +- blue_evt = (evt_blue_aci*)event_pckt->data; ++ case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: ++ blue_evt = (evt_blecore_aci*)event_pckt->data; + switch(blue_evt->ecode) { + aci_gap_pairing_complete_event_rp0* pairing_complete; + +- case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: ++ case ACI_GAP_LIMITED_DISCOVERABLE_VSEVT_CODE: + FURI_LOG_I(TAG, "Limited discoverable event"); + break; + +- case EVT_BLUE_GAP_PASS_KEY_REQUEST: { ++ case ACI_GAP_PASS_KEY_REQ_VSEVT_CODE: { + // Generate random PIN code + uint32_t pin = rand() % 999999; //-V1064 + aci_gap_pass_key_resp(gap->service.connection_handle, pin); +@@ -213,7 +214,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + gap->on_event_cb(event, gap->context); + } break; + +- case EVT_BLUE_ATT_EXCHANGE_MTU_RESP: { ++ case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: { + aci_att_exchange_mtu_resp_event_rp0* pr = (void*)blue_evt->data; + FURI_LOG_I(TAG, "Rx MTU size: %d", pr->Server_RX_MTU); + // Set maximum packet size given header size is 3 bytes +@@ -222,32 +223,28 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + gap->on_event_cb(event, gap->context); + } break; + +- case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: ++ case ACI_GAP_AUTHORIZATION_REQ_VSEVT_CODE: + FURI_LOG_D(TAG, "Authorization request event"); + break; + +- case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: ++ case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE: + FURI_LOG_D(TAG, "Slave security initiated"); + break; + +- case EVT_BLUE_GAP_BOND_LOST: ++ case ACI_GAP_BOND_LOST_VSEVT_CODE: + FURI_LOG_D(TAG, "Bond lost event. Start rebonding"); + aci_gap_allow_rebond(gap->service.connection_handle); + break; + +- case EVT_BLUE_GAP_DEVICE_FOUND: +- FURI_LOG_D(TAG, "Device found event"); +- break; +- +- case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: ++ case ACI_GAP_ADDR_NOT_RESOLVED_VSEVT_CODE: + FURI_LOG_D(TAG, "Address not resolved event"); + break; + +- case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: ++ case ACI_GAP_KEYPRESS_NOTIFICATION_VSEVT_CODE: + FURI_LOG_D(TAG, "Key press notification event"); + break; + +- case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: { ++ case ACI_GAP_NUMERIC_COMPARISON_VALUE_VSEVT_CODE: { + uint32_t pin = + ((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value; + FURI_LOG_I(TAG, "Verify numeric comparison: %06lu", pin); +@@ -257,7 +254,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + break; + } + +- case EVT_BLUE_GAP_PAIRING_CMPLT: ++ case ACI_GAP_PAIRING_COMPLETE_VSEVT_CODE: + pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; + if(pairing_complete->Status) { + FURI_LOG_E( +@@ -275,11 +272,11 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { + } + break; + +- case EVT_BLUE_GAP_PROCEDURE_COMPLETE: ++ case ACI_L2CAP_CONNECTION_UPDATE_RESP_VSEVT_CODE: + FURI_LOG_D(TAG, "Procedure complete event"); + break; + +- case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: { ++ case ACI_L2CAP_CONNECTION_UPDATE_REQ_VSEVT_CODE: { + uint16_t result = + ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result; + if(result == 0) { +@@ -402,7 +399,7 @@ static void gap_init_svc(Gap* gap) { + CFG_ENCRYPTION_KEY_SIZE_MAX, + conf_used_fixed_pin, // 0x0 for no pin + 0, +- PUBLIC_ADDR); ++ CFG_IDENTITY_ADDRESS); + // Configure whitelist + aci_gap_configure_whitelist(); + } +@@ -437,7 +434,7 @@ static void gap_advertise_start(GapState new_state) { + ADV_IND, + min_interval, + max_interval, +- PUBLIC_ADDR, ++ CFG_IDENTITY_ADDRESS, + 0, + strlen(gap->service.adv_name), + (uint8_t*)gap->service.adv_name, +diff --git a/lib/stm32wb_copro b/lib/stm32wb_copro +index d685979c28..6c9c54f056 160000 +--- a/lib/stm32wb_copro ++++ b/lib/stm32wb_copro +@@ -1 +1 @@ +-Subproject commit d685979c282c9f38d561dd1ea8e6fdbd735d7362 ++Subproject commit 6c9c54f05669b2c4d436df58bb691d3b0d7c86df +diff --git a/scripts/ob.data b/scripts/ob.data +index 5276a5103b..605faccbfc 100644 +--- a/scripts/ob.data ++++ b/scripts/ob.data +@@ -14,7 +14,7 @@ IWDGSTOP:0x1:rw + IWDGSW:0x1:rw + IPCCDBA:0x0:rw + ESE:0x1:r +-SFSA:0xD7:r ++SFSA:0xD5:r + FSD:0x0:r + DDS:0x1:r + C2OPT:0x1:r +@@ -22,7 +22,7 @@ NBRSD:0x0:r + SNBRSA:0xD:r + BRSD:0x0:r + SBRSA:0x12:r +-SBRV:0x35C00:r ++SBRV:0x35400:r + PCROP1A_STRT:0x1FF:r + PCROP1A_END:0x0:r + PCROP_RDP:0x1:rw From af5f44ed56108107e771e47c73b2da0277962f0f Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Fri, 9 Jun 2023 10:25:20 +0200 Subject: [PATCH 115/370] Automatic timestamp for fbt --- fbt_options.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fbt_options.py b/fbt_options.py index 7c5857e09..bb4ee592c 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -1,5 +1,6 @@ from pathlib import Path import posixpath +import datetime # For more details on these options, run 'fbt -h' @@ -16,7 +17,9 @@ DEBUG = 0 # Suffix to add to files when building distribution # If OS environment has DIST_SUFFIX set, it will be used instead -DIST_SUFFIX = "XFW-0048_03062023" + +# How about we add the timestamp automatically. Solves some problems +DIST_SUFFIX = f"XFW-0048_{datetime.datetime.today().strftime('%d%m%Y')}" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" From 62939dd28b5903a0509c154a3158f6d4fad374a0 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Fri, 9 Jun 2023 03:49:26 -0700 Subject: [PATCH 116/370] Core2, SRAM2: provide safety gap (#2754) * Core2, SRAM2: use ob, provide safety gap * thread: comment about critical section and scheduler state --- .../targets/f7/furi_hal/furi_hal_memory.c | 82 ++++++++++--------- furi/core/thread.c | 2 + 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_memory.c b/firmware/targets/f7/furi_hal/furi_hal_memory.c index 9716f1e52..7f69b90ca 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_memory.c +++ b/firmware/targets/f7/furi_hal/furi_hal_memory.c @@ -4,6 +4,9 @@ #define TAG "FuriHalMemory" +// STM(TM) Copro(TM) bug(TM) workaround size +#define RAM2B_COPRO_GAP_SIZE_KB 2 + typedef enum { SRAM_A, SRAM_B, @@ -30,53 +33,47 @@ void furi_hal_memory_init() { return; } - if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) { - FURI_LOG_E(TAG, "C2 start timeout"); - return; - } - FuriHalMemory* memory = malloc(sizeof(FuriHalMemory)); - const BleGlueC2Info* c2_ver = ble_glue_get_c2_info(); + uint32_t sbrsa = (FLASH->SRRVR & FLASH_SRRVR_SBRSA_Msk) >> FLASH_SRRVR_SBRSA_Pos; + uint32_t snbrsa = (FLASH->SRRVR & FLASH_SRRVR_SNBRSA_Msk) >> FLASH_SRRVR_SNBRSA_Pos; - if(c2_ver->mode == BleGlueC2ModeStack) { - uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; - uint32_t sram2a_unprotected_size = (32 - c2_ver->MemorySizeSram2A) * 1024; - uint32_t sram2b_unprotected_size = (32 - c2_ver->MemorySizeSram2B) * 1024; + uint32_t sram2a_busy_size = (uint32_t)&__sram2a_free__ - (uint32_t)&__sram2a_start__; + uint32_t sram2a_unprotected_size = (sbrsa)*1024; + uint32_t sram2b_unprotected_size = (snbrsa)*1024; - memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; - memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; + // STM(TM) Copro(TM) bug(TM) workaround + sram2b_unprotected_size -= 1024 * RAM2B_COPRO_GAP_SIZE_KB; - if(sram2a_unprotected_size > sram2a_busy_size) { - memory->region[SRAM_A].size = sram2a_unprotected_size - sram2a_busy_size; - } else { - memory->region[SRAM_A].size = 0; + memory->region[SRAM_A].start = (uint8_t*)&__sram2a_free__; + memory->region[SRAM_B].start = (uint8_t*)&__sram2b_start__; + + if(sram2a_unprotected_size > sram2a_busy_size) { + memory->region[SRAM_A].size = sram2a_unprotected_size - sram2a_busy_size; + } else { + memory->region[SRAM_A].size = 0; + } + memory->region[SRAM_B].size = sram2b_unprotected_size; + + FURI_LOG_I( + TAG, "SRAM2A: 0x%p, %lu", memory->region[SRAM_A].start, memory->region[SRAM_A].size); + FURI_LOG_I( + TAG, "SRAM2B: 0x%p, %lu", memory->region[SRAM_B].start, memory->region[SRAM_B].size); + + if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) { + if((memory->region[SRAM_A].size > 0)) { + FURI_LOG_I(TAG, "SRAM2A clear"); + memset(memory->region[SRAM_A].start, 0, memory->region[SRAM_A].size); } - memory->region[SRAM_B].size = sram2b_unprotected_size; - - FURI_LOG_I( - TAG, "SRAM2A: 0x%p, %lu", memory->region[SRAM_A].start, memory->region[SRAM_A].size); - FURI_LOG_I( - TAG, "SRAM2B: 0x%p, %lu", memory->region[SRAM_B].start, memory->region[SRAM_B].size); - - if((memory->region[SRAM_A].size > 0) || (memory->region[SRAM_B].size > 0)) { - if((memory->region[SRAM_A].size > 0)) { - FURI_LOG_I(TAG, "SRAM2A clear"); - memset(memory->region[SRAM_A].start, 0, memory->region[SRAM_A].size); - } - if((memory->region[SRAM_B].size > 0)) { - FURI_LOG_I(TAG, "SRAM2B clear"); - memset(memory->region[SRAM_B].start, 0, memory->region[SRAM_B].size); - } - furi_hal_memory = memory; - FURI_LOG_I(TAG, "Enabled"); - } else { - free(memory); - FURI_LOG_E(TAG, "No SRAM2 available"); + if((memory->region[SRAM_B].size > 0)) { + FURI_LOG_I(TAG, "SRAM2B clear"); + memset(memory->region[SRAM_B].start, 0, memory->region[SRAM_B].size); } + furi_hal_memory = memory; + FURI_LOG_I(TAG, "Enabled"); } else { free(memory); - FURI_LOG_E(TAG, "No Core2 available"); + FURI_LOG_E(TAG, "No SRAM2 available"); } } @@ -89,15 +86,20 @@ void* furi_hal_memory_alloc(size_t size) { return NULL; } + void* allocated_memory = NULL; + FURI_CRITICAL_ENTER(); for(int i = 0; i < SRAM_MAX; i++) { if(furi_hal_memory->region[i].size >= size) { void* ptr = furi_hal_memory->region[i].start; furi_hal_memory->region[i].start += size; furi_hal_memory->region[i].size -= size; - return ptr; + allocated_memory = ptr; + break; } } - return NULL; + FURI_CRITICAL_EXIT(); + + return allocated_memory; } size_t furi_hal_memory_get_free() { diff --git a/furi/core/thread.c b/furi/core/thread.c index facbcb411..657b867d1 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -56,6 +56,8 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread); /** Catch threads that are trying to exit wrong way */ __attribute__((__noreturn__)) void furi_thread_catch() { //-V1082 + // If you're here it means you're probably doing something wrong + // with critical sections or with scheduler state asm volatile("nop"); // extra magic furi_crash("You are doing it wrong"); //-V779 __builtin_unreachable(); From 0e4344a83c48ec07cbecb52a6aa9168328ad3fdb Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Fri, 9 Jun 2023 04:02:47 -0700 Subject: [PATCH 117/370] Services: simplify api (#2540) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/external/hid_app/hid.c | 4 ++-- applications/external/mfkey32/mfkey32.c | 2 +- .../picopass/scenes/picopass_scene_device_info.c | 2 +- .../picopass/scenes/picopass_scene_read_card.c | 2 +- .../scenes/picopass_scene_read_card_success.c | 2 +- .../scenes/picopass_scene_read_factory_success.c | 2 +- .../picopass/scenes/picopass_scene_save_success.c | 2 +- .../picopass/scenes/picopass_scene_write_card.c | 2 +- .../scenes/picopass_scene_write_card_success.c | 2 +- .../picopass/scenes/picopass_scene_write_key.c | 2 +- applications/external/snake_game/snake_game.c | 2 +- applications/main/bad_usb/helpers/ducky_script.c | 4 ++-- applications/main/gpio/scenes/gpio_scene_start.c | 2 +- applications/main/ibutton/ibutton.c | 4 ++-- .../main/ibutton/scenes/ibutton_scene_read.c | 2 +- .../ibutton/scenes/ibutton_scene_read_key_menu.c | 2 +- .../main/ibutton/scenes/ibutton_scene_save_name.c | 4 ++-- .../ibutton/scenes/ibutton_scene_saved_key_menu.c | 2 +- .../main/ibutton/scenes/ibutton_scene_start.c | 2 +- applications/main/infrared/infrared.c | 2 +- .../scenes/common/infrared_scene_universal_common.c | 2 +- .../main/infrared/scenes/infrared_scene_learn.c | 2 +- .../scenes/infrared_scene_learn_enter_name.c | 2 +- applications/main/lfrfid/lfrfid.c | 4 ++-- .../main/lfrfid/scenes/lfrfid_scene_extra_actions.c | 4 ++-- applications/main/lfrfid/scenes/lfrfid_scene_read.c | 2 +- .../main/lfrfid/scenes/lfrfid_scene_read_key_menu.c | 2 +- .../main/lfrfid/scenes/lfrfid_scene_save_name.c | 4 ++-- .../main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c | 2 +- applications/main/lfrfid/scenes/lfrfid_scene_start.c | 2 +- applications/main/nfc/nfc.c | 8 ++++---- .../nfc/scenes/nfc_scene_mf_classic_dict_attack.c | 4 ++-- .../main/nfc/scenes/nfc_scene_mf_classic_keys_add.c | 2 +- .../main/nfc/scenes/nfc_scene_mf_classic_menu.c | 6 +++--- .../main/nfc/scenes/nfc_scene_mf_classic_update.c | 2 +- .../nfc/scenes/nfc_scene_mf_classic_update_success.c | 2 +- .../main/nfc/scenes/nfc_scene_mf_classic_write.c | 2 +- .../nfc/scenes/nfc_scene_mf_classic_write_success.c | 2 +- .../main/nfc/scenes/nfc_scene_mf_desfire_menu.c | 4 ++-- .../main/nfc/scenes/nfc_scene_mf_ultralight_menu.c | 4 ++-- .../nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c | 4 ++-- applications/main/nfc/scenes/nfc_scene_nfca_menu.c | 4 ++-- .../main/nfc/scenes/nfc_scene_nfcv_key_input.c | 2 +- applications/main/nfc/scenes/nfc_scene_nfcv_menu.c | 4 ++-- applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c | 2 +- .../main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c | 2 +- applications/main/nfc/scenes/nfc_scene_read.c | 12 ++++++------ applications/main/nfc/scenes/nfc_scene_save_name.c | 4 ++-- applications/main/nfc/scenes/nfc_scene_saved_menu.c | 4 ++-- applications/main/nfc/scenes/nfc_scene_start.c | 4 ++-- .../main/subghz/scenes/subghz_scene_read_raw.c | 4 ++-- .../main/subghz/scenes/subghz_scene_receiver.c | 2 +- .../main/subghz/scenes/subghz_scene_save_name.c | 4 ++-- applications/main/subghz/scenes/subghz_scene_start.c | 2 +- .../main/subghz/scenes/subghz_scene_transmitter.c | 2 +- applications/main/u2f/scenes/u2f_scene_main.c | 2 +- .../services/desktop/scenes/desktop_scene_debug.c | 4 ++-- applications/services/dolphin/dolphin.c | 5 +++-- applications/services/dolphin/dolphin.h | 9 +-------- firmware/targets/f18/api_symbols.csv | 4 ++-- firmware/targets/f7/api_symbols.csv | 4 ++-- 61 files changed, 94 insertions(+), 100 deletions(-) diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index a4f64589d..ea408c392 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -377,7 +377,7 @@ int32_t hid_usb_app(void* p) { bt_hid_connection_status_changed_callback(BtStatusConnected, app); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); @@ -417,7 +417,7 @@ int32_t hid_ble_app(void* p) { furi_hal_bt_start_advertising(); bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); diff --git a/applications/external/mfkey32/mfkey32.c b/applications/external/mfkey32/mfkey32.c index d4b2d3e4a..5e790b01f 100644 --- a/applications/external/mfkey32/mfkey32.c +++ b/applications/external/mfkey32/mfkey32.c @@ -1112,7 +1112,7 @@ void mfkey32(ProgramState* program_state) { } if(keyarray_size > 0) { // TODO: Should we use DolphinDeedNfcMfcAdd? - DOLPHIN_DEED(DolphinDeedNfcMfcAdd); + dolphin_deed(DolphinDeedNfcMfcAdd); } napi_mf_classic_nonce_array_free(nonce_arr); napi_mf_classic_dict_free(user_dict); diff --git a/applications/external/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c index 41caeabf5..bb149aa6b 100644 --- a/applications/external/picopass/scenes/picopass_scene_device_info.c +++ b/applications/external/picopass/scenes/picopass_scene_device_info.c @@ -19,7 +19,7 @@ void picopass_scene_device_info_on_enter(void* context) { FuriString* wiegand_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc(); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Setup view PicopassBlock* AA1 = picopass->dev->dev_data.AA1; diff --git a/applications/external/picopass/scenes/picopass_scene_read_card.c b/applications/external/picopass/scenes/picopass_scene_read_card.c index 96ec7c668..c1cc7249c 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card.c @@ -10,7 +10,7 @@ void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context void picopass_scene_read_card_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c index cc18ac066..ffe7195b7 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -21,7 +21,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { FuriString* wiegand_str = furi_string_alloc(); FuriString* sio_str = furi_string_alloc(); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); diff --git a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c index bc07bb953..f5fcd10fd 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c @@ -19,7 +19,7 @@ void picopass_scene_read_factory_success_on_enter(void* context) { FuriString* title = furi_string_alloc_set("Factory Default"); FuriString* subtitle = furi_string_alloc_set(""); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); diff --git a/applications/external/picopass/scenes/picopass_scene_save_success.c b/applications/external/picopass/scenes/picopass_scene_save_success.c index e92d91fb4..3b0a1cadd 100644 --- a/applications/external/picopass/scenes/picopass_scene_save_success.c +++ b/applications/external/picopass/scenes/picopass_scene_save_success.c @@ -8,7 +8,7 @@ void picopass_scene_save_success_popup_callback(void* context) { void picopass_scene_save_success_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/scenes/picopass_scene_write_card.c b/applications/external/picopass/scenes/picopass_scene_write_card.c index a905dca95..ce396fc10 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_card.c +++ b/applications/external/picopass/scenes/picopass_scene_write_card.c @@ -9,7 +9,7 @@ void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* contex void picopass_scene_write_card_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/picopass/scenes/picopass_scene_write_card_success.c b/applications/external/picopass/scenes/picopass_scene_write_card_success.c index 4bbca816a..cd760272f 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_write_card_success.c @@ -18,7 +18,7 @@ void picopass_scene_write_card_success_on_enter(void* context) { Widget* widget = picopass->widget; FuriString* str = furi_string_alloc_set("Write Success!"); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); // Send notification notification_message(picopass->notifications, &sequence_success); diff --git a/applications/external/picopass/scenes/picopass_scene_write_key.c b/applications/external/picopass/scenes/picopass_scene_write_key.c index 0f417e1c3..806a2b5a8 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_key.c +++ b/applications/external/picopass/scenes/picopass_scene_write_key.c @@ -9,7 +9,7 @@ void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context void picopass_scene_write_key_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); // Setup view Popup* popup = picopass->popup; diff --git a/applications/external/snake_game/snake_game.c b/applications/external/snake_game/snake_game.c index 3cf9b6d53..6852cb215 100644 --- a/applications/external/snake_game/snake_game.c +++ b/applications/external/snake_game/snake_game.c @@ -346,7 +346,7 @@ int32_t snake_game_app(void* p) { notification_message_block(notification, &sequence_display_backlight_enforce_on); - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); SnakeEvent event; for(bool processing = true; processing;) { diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index 5a834ad0a..f194178a0 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -426,7 +426,7 @@ static int32_t bad_usb_worker(void* context) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtStartStop) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + dolphin_deed(DolphinDeedBadUsbPlayScript); delay_val = 0; bad_usb->buf_len = 0; bad_usb->st.line_cur = 0; @@ -449,7 +449,7 @@ static int32_t bad_usb_worker(void* context) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + dolphin_deed(DolphinDeedBadUsbPlayScript); delay_val = 0; bad_usb->buf_len = 0; bad_usb->st.line_cur = 0; diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index 027267793..421936488 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -89,7 +89,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == GpioStartEventUsbUart) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); if(!furi_hal_usb_is_locked()) { - DOLPHIN_DEED(DolphinDeedGpioUartBridge); + dolphin_deed(DolphinDeedGpioUartBridge); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); } else { scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 79999adb2..ad5b233b5 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -282,14 +282,14 @@ int32_t ibutton_app(void* arg) { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else { view_dispatcher_attach_to_gui( ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen); if(key_loaded) { //-V547 scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); } diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index a840fb7b7..f360c3ac4 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -38,7 +38,7 @@ bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) { ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess); scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess); - DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess); + dolphin_deed(DolphinDeedIbuttonReadSuccess); } else { scene_manager_next_scene(scene_manager, iButtonSceneReadError); diff --git a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c index 716f72c7d..1555f2cc2 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read_key_menu.c @@ -75,7 +75,7 @@ bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(scene_manager, iButtonSceneSaveName); } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexViewData) { scene_manager_next_scene(scene_manager, iButtonSceneViewData); } else if(event.event == SubmenuIndexWriteBlank) { diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_name.c b/applications/main/ibutton/scenes/ibutton_scene_save_name.c index 4ad0315e5..7bd49df83 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_name.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_name.c @@ -58,9 +58,9 @@ bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene( ibutton->scene_manager, iButtonSceneAddType)) { - DOLPHIN_DEED(DolphinDeedIbuttonAdd); + dolphin_deed(DolphinDeedIbuttonAdd); } else { - DOLPHIN_DEED(DolphinDeedIbuttonSave); + dolphin_deed(DolphinDeedIbuttonSave); } } else { diff --git a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c index 80fca28b5..fc0cf42e2 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c +++ b/applications/main/ibutton/scenes/ibutton_scene_saved_key_menu.c @@ -48,7 +48,7 @@ bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent even consumed = true; if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(scene_manager, iButtonSceneEmulate); - DOLPHIN_DEED(DolphinDeedIbuttonEmulate); + dolphin_deed(DolphinDeedIbuttonEmulate); } else if(event.event == SubmenuIndexWriteBlank) { ibutton->write_mode = iButtonWriteModeBlank; scene_manager_next_scene(scene_manager, iButtonSceneWrite); diff --git a/applications/main/ibutton/scenes/ibutton_scene_start.c b/applications/main/ibutton/scenes/ibutton_scene_start.c index 37bf96f39..63a4cf869 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_start.c +++ b/applications/main/ibutton/scenes/ibutton_scene_start.c @@ -33,7 +33,7 @@ bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == SubmenuIndexRead) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead); - DOLPHIN_DEED(DolphinDeedIbuttonRead); + dolphin_deed(DolphinDeedIbuttonRead); } else if(event.event == SubmenuIndexSaved) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey); } else if(event.event == SubmenuIndexAdd) { diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index 685dd57ec..5957cdb13 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -319,7 +319,7 @@ void infrared_tx_start_signal(Infrared* infrared, InfraredSignal* signal) { infrared_worker_set_decoded_signal(infrared->worker, message); } - DOLPHIN_DEED(DolphinDeedIrSend); + dolphin_deed(DolphinDeedIrSend); infrared_play_notification_message(infrared, InfraredNotificationMessageBlinkStartSend); infrared_worker_tx_set_get_signal_callback( diff --git a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c index d55d8d0a6..96f28cc48 100644 --- a/applications/main/infrared/scenes/common/infrared_scene_universal_common.c +++ b/applications/main/infrared/scenes/common/infrared_scene_universal_common.c @@ -70,7 +70,7 @@ bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent e uint32_t record_count; if(infrared_brute_force_start( brute_force, infrared_custom_event_get_value(event.event), &record_count)) { - DOLPHIN_DEED(DolphinDeedIrSend); + dolphin_deed(DolphinDeedIrSend); infrared_scene_universal_common_show_popup(infrared, record_count); } else { scene_manager_next_scene(scene_manager, InfraredSceneErrorDatabases); diff --git a/applications/main/infrared/scenes/infrared_scene_learn.c b/applications/main/infrared/scenes/infrared_scene_learn.c index 48699a71f..46646c6d6 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn.c +++ b/applications/main/infrared/scenes/infrared_scene_learn.c @@ -28,7 +28,7 @@ bool infrared_scene_learn_on_event(void* context, SceneManagerEvent event) { if(event.event == InfraredCustomEventTypeSignalReceived) { infrared_play_notification_message(infrared, InfraredNotificationMessageSuccess); scene_manager_next_scene(infrared->scene_manager, InfraredSceneLearnSuccess); - DOLPHIN_DEED(DolphinDeedIrLearnSuccess); + dolphin_deed(DolphinDeedIrLearnSuccess); consumed = true; } } diff --git a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c index a8772a985..104a4cb7b 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_enter_name.c @@ -50,7 +50,7 @@ bool infrared_scene_learn_enter_name_on_event(void* context, SceneManagerEvent e if(success) { scene_manager_next_scene(scene_manager, InfraredSceneLearnDone); - DOLPHIN_DEED(DolphinDeedIrSave); + dolphin_deed(DolphinDeedIrSave); } else { dialog_message_show_storage_error(infrared->dialogs, "Failed to save file"); const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 85a00eea0..edde23804 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -183,14 +183,14 @@ int32_t lfrfid_app(void* p) { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); } else { furi_string_set(app->file_path, args); lfrfid_load_key_data(app, app->file_path, true); view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); } } else { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index fac2ebcec..1aed9a03c 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -58,12 +58,12 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) if(event.event == SubmenuIndexASK) { app->read_type = LFRFIDWorkerReadTypeASKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexPSK) { app->read_type = LFRFIDWorkerReadTypePSKOnly; scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexRAW) { scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_read.c index 5f1959728..d04ce41d4 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read.c @@ -81,7 +81,7 @@ bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { notification_message(app->notifications, &sequence_success); furi_string_reset(app->file_name); scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); + dolphin_deed(DolphinDeedRfidReadSuccess); consumed = true; } else if(event.event == LfRfidEventReadStartPSK) { if(app->read_type == LFRFIDWorkerReadTypeAuto) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c index 081c47912..36f0d6d93 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c @@ -44,7 +44,7 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c index 87e110f18..771f2f603 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_name.c @@ -59,9 +59,9 @@ bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSavedKeyMenu)) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene(scene_manager, LfRfidSceneSaveType)) { - DOLPHIN_DEED(DolphinDeedRfidAdd); + dolphin_deed(DolphinDeedRfidAdd); } else { - DOLPHIN_DEED(DolphinDeedRfidSave); + dolphin_deed(DolphinDeedRfidSave); } } else { scene_manager_search_and_switch_to_previous_scene( diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index d3c3d389a..206074e9b 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -43,7 +43,7 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index 2d83ba53b..8a01fc707 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -49,7 +49,7 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexRead) { scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); - DOLPHIN_DEED(DolphinDeedRfidRead); + dolphin_deed(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexSaved) { // Like in the other apps, explicitly save the scene state diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index f68b7f2f2..56d98a8c6 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -286,18 +286,18 @@ int32_t nfc_app(void* p) { if(nfc_device_load(nfc->dev, p, true)) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } } else { // Exit app diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index cb2f3a82d..5bd24d7ea 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -111,7 +111,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent } else { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } } else if(event.event == NfcWorkerEventAborted) { @@ -123,7 +123,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); // Counting failed attempts too - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } } else if(event.event == NfcWorkerEventCardDetected) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c index b122aa225..3a999f031 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_keys_add.c @@ -37,7 +37,7 @@ bool nfc_scene_mf_classic_keys_add_on_event(void* context, SceneManagerEvent eve nfc->scene_manager, NfcSceneMfClassicKeysWarnDuplicate); } else if(mf_classic_dict_add_key(dict, nfc->byte_input_store)) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); - DOLPHIN_DEED(DolphinDeedNfcMfcAdd); + dolphin_deed(DolphinDeedNfcMfcAdd); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c index 67b2a8530..9c4163676 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_menu.c @@ -54,14 +54,14 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); consumed = true; } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c index aacf77f77..ffef1b7b9 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update.c @@ -34,7 +34,7 @@ static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) { void nfc_scene_mf_classic_update_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c index fef8fd5e9..fb1868459 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c @@ -8,7 +8,7 @@ void nfc_scene_mf_classic_update_success_popup_callback(void* context) { void nfc_scene_mf_classic_update_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); notification_message(nfc->notifications, &sequence_success); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c index 3543cbc58..20ebfcc70 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write.c @@ -34,7 +34,7 @@ static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) { void nfc_scene_mf_classic_write_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); scene_manager_set_scene_state( nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c index 2f2a3beb1..00030d4fe 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c @@ -8,7 +8,7 @@ void nfc_scene_mf_classic_write_success_popup_callback(void* context) { void nfc_scene_mf_classic_write_success_on_enter(void* context) { Nfc* nfc = context; - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); notification_message(nfc->notifications, &sequence_success); diff --git a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c index bee63d775..9cebefedf 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_desfire_menu.c @@ -50,9 +50,9 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c index e7a494d27..b3bd780f4 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_menu.c @@ -60,9 +60,9 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexUnlock) { diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c index 16efae9de..af2eca0ce 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_warn.c @@ -61,7 +61,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultRight) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } else if(event.event == DialogExResultLeft) { if(auth_method == MfUltralightAuthMethodAuto) { @@ -79,7 +79,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve if(event.type == SceneManagerEventTypeCustom) { if(event.event == DialogExResultCenter) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c index 30f63945c..9779470a3 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfca_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfca_menu.c @@ -43,9 +43,9 @@ bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexEmulateUid) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c index cc53c4dcb..13d903c4b 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -32,7 +32,7 @@ bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c index 7c6780b7c..60eb354e8 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -42,9 +42,9 @@ bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexInfo) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c index 26de304de..38d7ad563 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -76,7 +76,7 @@ void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { popup_set_timeout(popup, 1500); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { popup_reset(popup); diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c index 9c4c81fbd..2f7367256 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c @@ -45,7 +45,7 @@ bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 5a1814b6c..1690a9557 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -61,34 +61,34 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { (event.event == NfcWorkerEventReadUidNfcV)) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadUidNfcA) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadNfcV) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); // Set unlock password input to 0xFFFFFFFF only on fresh read memset(nfc->byte_input_store, 0xFF, sizeof(nfc->byte_input_store)); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDone) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfDesfire) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index 007274226..a7b97aac0 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -67,9 +67,9 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddSave); + dolphin_deed(DolphinDeedNfcAddSave); } else { - DOLPHIN_DEED(DolphinDeedNfcSave); + dolphin_deed(DolphinDeedNfcSave); } consumed = true; } else { diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 8412c17bc..b3205554a 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -124,11 +124,11 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index a01f871ab..c9e8bf78c 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -51,7 +51,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); nfc->dev->dev_data.read_mode = NfcReadModeAuto; scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); - DOLPHIN_DEED(DolphinDeedNfcRead); + dolphin_deed(DolphinDeedNfcRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { scene_manager_set_scene_state( @@ -60,7 +60,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { if(sd_exist) { nfc_device_data_clear(&nfc->dev->dev_data); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); - DOLPHIN_DEED(DolphinDeedNfcDetectReader); + dolphin_deed(DolphinDeedNfcDetectReader); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); } diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 6e576a861..a29f86a07 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -204,7 +204,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { } else { if(scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneSaved) || !scene_manager_has_previous_scene(subghz->scene_manager, SubGhzSceneStart)) { - DOLPHIN_DEED(DolphinDeedSubGhzSend); + dolphin_deed(DolphinDeedSubGhzSend); } // set callback end tx subghz_txrx_set_raw_file_encoder_worker_callback_end( @@ -259,7 +259,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { } else { SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); if(subghz_protocol_raw_save_to_file_init(decoder_raw, RAW_FILE_NAME, &preset)) { - DOLPHIN_DEED(DolphinDeedSubGhzRawRec); + dolphin_deed(DolphinDeedSubGhzRawRec); subghz_txrx_rx_start(subghz->txrx); subghz->state_notifications = SubGhzNotificationStateRx; subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index dcc22b91c..6771f8213 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -163,7 +163,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { case SubGhzCustomEventViewReceiverOK: subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); - DOLPHIN_DEED(DolphinDeedSubGhzReceiverInfo); + dolphin_deed(DolphinDeedSubGhzReceiverInfo); consumed = true; break; case SubGhzCustomEventViewReceiverConfig: diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 2a292a1ef..7d0a4f4f8 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -137,9 +137,9 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { // Ditto, for RAW signals } else if(scene_manager_has_previous_scene( subghz->scene_manager, SubGhzSceneSetType)) { - DOLPHIN_DEED(DolphinDeedSubGhzAddManually); + dolphin_deed(DolphinDeedSubGhzAddManually); } else { - DOLPHIN_DEED(DolphinDeedSubGhzSave); + dolphin_deed(DolphinDeedSubGhzSave); } return true; } else { diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index a41e4b06f..0ab5f123e 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -92,7 +92,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); - DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); + dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer); return true; } else if(event.event == SubmenuIndexTest) { scene_manager_set_scene_state( diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 1c193c179..274dd61ad 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -61,7 +61,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { if(subghz_tx_start(subghz, subghz_txrx_get_fff_data(subghz->txrx))) { subghz->state_notifications = SubGhzNotificationStateTx; subghz_scene_transmitter_update_data_show(subghz); - DOLPHIN_DEED(DolphinDeedSubGhzSend); + dolphin_deed(DolphinDeedSubGhzSend); } return true; } else if(event.event == SubGhzCustomEventViewTransmitterSendStop) { diff --git a/applications/main/u2f/scenes/u2f_scene_main.c b/applications/main/u2f/scenes/u2f_scene_main.c index 251bc4d99..992236e7a 100644 --- a/applications/main/u2f/scenes/u2f_scene_main.c +++ b/applications/main/u2f/scenes/u2f_scene_main.c @@ -68,7 +68,7 @@ bool u2f_scene_main_on_event(void* context, SceneManagerEvent event) { notification_message(app->notifications, &sequence_blink_magenta_10); } else if(event.event == U2fCustomEventAuthSuccess) { notification_message_block(app->notifications, &sequence_set_green_255); - DOLPHIN_DEED(DolphinDeedU2fAuthorized); + dolphin_deed(DolphinDeedU2fAuthorized); furi_timer_start(app->timer, U2F_SUCCESS_TIMEOUT); app->event_cur = U2fCustomEventNone; u2f_view_set_state(app->u2f_view, U2fMsgSuccess); diff --git a/applications/services/desktop/scenes/desktop_scene_debug.c b/applications/services/desktop/scenes/desktop_scene_debug.c index e79c56e11..a5bd3a6b1 100644 --- a/applications/services/desktop/scenes/desktop_scene_debug.c +++ b/applications/services/desktop/scenes/desktop_scene_debug.c @@ -34,13 +34,13 @@ bool desktop_scene_debug_on_event(void* context, SceneManagerEvent event) { break; case DesktopDebugEventDeed: - dolphin_deed(dolphin, DolphinDeedTestRight); + dolphin_deed(DolphinDeedTestRight); desktop_debug_get_dolphin_data(desktop->debug_view); consumed = true; break; case DesktopDebugEventWrongDeed: - dolphin_deed(dolphin, DolphinDeedTestLeft); + dolphin_deed(DolphinDeedTestLeft); desktop_debug_get_dolphin_data(desktop->debug_view); consumed = true; break; diff --git a/applications/services/dolphin/dolphin.c b/applications/services/dolphin/dolphin.c index 93a9b3095..579b400ad 100644 --- a/applications/services/dolphin/dolphin.c +++ b/applications/services/dolphin/dolphin.c @@ -13,12 +13,13 @@ static void dolphin_update_clear_limits_timer_period(Dolphin* dolphin); -void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) { - furi_assert(dolphin); +void dolphin_deed(DolphinDeed deed) { + Dolphin* dolphin = (Dolphin*)furi_record_open(RECORD_DOLPHIN); DolphinEvent event; event.type = DolphinEventTypeDeed; event.deed = deed; dolphin_event_send_async(dolphin, &event); + furi_record_close(RECORD_DOLPHIN); } DolphinStats dolphin_stats(Dolphin* dolphin) { diff --git a/applications/services/dolphin/dolphin.h b/applications/services/dolphin/dolphin.h index 8757e2a37..1035247e7 100644 --- a/applications/services/dolphin/dolphin.h +++ b/applications/services/dolphin/dolphin.h @@ -26,18 +26,11 @@ typedef enum { DolphinPubsubEventUpdate, } DolphinPubsubEvent; -#define DOLPHIN_DEED(deed) \ - do { \ - Dolphin* dolphin = (Dolphin*)furi_record_open("dolphin"); \ - dolphin_deed(dolphin, deed); \ - furi_record_close("dolphin"); \ - } while(0) - /** Deed complete notification. Call it on deed completion. * See dolphin_deed.h for available deeds. In futures it will become part of assets. * Thread safe, async */ -void dolphin_deed(Dolphin* dolphin, DolphinDeed deed); +void dolphin_deed(DolphinDeed deed); /** Retrieve dolphin stats * Thread safe, blocking diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index f551a09c1..85f09f1c3 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,29.0,, +Version,+,30.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -648,7 +648,7 @@ Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" Function,-,div,div_t,"int, int" -Function,+,dolphin_deed,void,"Dolphin*, DolphinDeed" +Function,+,dolphin_deed,void,DolphinDeed Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index e2de36836..afa761632 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,29.0,, +Version,+,30.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -775,7 +775,7 @@ Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" Function,-,div,div_t,"int, int" -Function,+,dolphin_deed,void,"Dolphin*, DolphinDeed" +Function,+,dolphin_deed,void,DolphinDeed Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed From 53f62057667f36246175cfe2af6db8d6492b53e5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 9 Jun 2023 14:14:05 +0300 Subject: [PATCH 118/370] dolphin did --- applications/external/arkanoid/arkanoid_game.c | 2 +- applications/external/blackjack/blackjack.c | 4 ++-- applications/external/bomberduck/bomberduck.c | 4 ++-- applications/external/doom/doom.c | 2 +- applications/external/flappy_bird/flappy_bird.c | 2 +- applications/external/game15/game15.c | 2 +- applications/external/game_2048/game_2048.c | 2 +- applications/external/heap_defence_game/heap_defence.c | 2 +- applications/external/minesweeper/minesweeper.c | 4 ++-- applications/external/solitaire/solitaire.c | 4 ++-- applications/external/swd_probe/swd_probe_app.c | 2 +- applications/external/tetris_game/tetris_game.c | 2 +- applications/external/tictactoe_game/tictactoe_game.c | 2 +- applications/external/totp/totp_app.c | 2 +- applications/external/zombiez/zombiez.c | 4 ++-- applications/main/lfrfid/lfrfid.c | 2 +- applications/main/nfc/nfc.c | 2 +- applications/main/nfc/scenes/nfc_scene_read.c | 2 +- applications/main/subghz/scenes/subghz_scene_start.c | 2 +- 19 files changed, 24 insertions(+), 24 deletions(-) diff --git a/applications/external/arkanoid/arkanoid_game.c b/applications/external/arkanoid/arkanoid_game.c index a8b846579..2adf2e30b 100644 --- a/applications/external/arkanoid/arkanoid_game.c +++ b/applications/external/arkanoid/arkanoid_game.c @@ -399,7 +399,7 @@ int32_t arkanoid_game_app(void* p) { gui_add_view_port(gui, view_port, GuiLayerFullscreen); // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); GameEvent event; for(bool processing = true; processing;) { diff --git a/applications/external/blackjack/blackjack.c b/applications/external/blackjack/blackjack.c index 44db29087..c4ef8d3a7 100644 --- a/applications/external/blackjack/blackjack.c +++ b/applications/external/blackjack/blackjack.c @@ -276,7 +276,7 @@ void dealer_tick(GameState* game_state) { if(dealer_score >= DEALER_MAX) { if(dealer_score > 21 || dealer_score < player_score) { - DOLPHIN_DEED(DolphinDeedPluginGameWin); + dolphin_deed(DolphinDeedPluginGameWin); enqueue( &(game_state->queue_state), game_state, @@ -571,7 +571,7 @@ int32_t blackjack_app(void* p) { AppEvent event; // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); diff --git a/applications/external/bomberduck/bomberduck.c b/applications/external/bomberduck/bomberduck.c index 2c1c2940a..79471c99e 100644 --- a/applications/external/bomberduck/bomberduck.c +++ b/applications/external/bomberduck/bomberduck.c @@ -383,7 +383,7 @@ int32_t bomberduck_app(void* p) { return 255; } - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); // Создаем новый view port ViewPort* view_port = view_port_alloc(); // Создаем callback отрисовки, без контекста @@ -458,7 +458,7 @@ int32_t bomberduck_app(void* p) { world.running = 0; world.level += 1; if(world.level % 5 == 0) { - DOLPHIN_DEED(DolphinDeedPluginGameWin); + dolphin_deed(DolphinDeedPluginGameWin); } } for(int i = 0; i < world.bombs_count; i++) { diff --git a/applications/external/doom/doom.c b/applications/external/doom/doom.c index 2fb99a635..b9cea97a0 100644 --- a/applications/external/doom/doom.c +++ b/applications/external/doom/doom.c @@ -991,7 +991,7 @@ int32_t doom_app() { music_player_worker_start(plugin_state->music_instance->worker); #endif // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); diff --git a/applications/external/flappy_bird/flappy_bird.c b/applications/external/flappy_bird/flappy_bird.c index 709219bb5..2a50868f2 100644 --- a/applications/external/flappy_bird/flappy_bird.c +++ b/applications/external/flappy_bird/flappy_bird.c @@ -309,7 +309,7 @@ int32_t flappy_game_app(void* p) { gui_add_view_port(gui, view_port, GuiLayerFullscreen); // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); GameEvent event; for(bool processing = true; processing;) { diff --git a/applications/external/game15/game15.c b/applications/external/game15/game15.c index 43d2e12f4..d81cea9be 100644 --- a/applications/external/game15/game15.c +++ b/applications/external/game15/game15.c @@ -464,7 +464,7 @@ int32_t game15_app() { FPS, (SandboxRenderCallback)render_callback, (SandboxEventHandler)game_event_handler); // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); sandbox_loop(); sandbox_free(); diff --git a/applications/external/game_2048/game_2048.c b/applications/external/game_2048/game_2048.c index a1e6361b6..a7f84a411 100644 --- a/applications/external/game_2048/game_2048.c +++ b/applications/external/game_2048/game_2048.c @@ -401,7 +401,7 @@ int32_t game_2048_app() { gui_add_view_port(gui, view_port, GuiLayerFullscreen); // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); bool is_finished = false; while(!is_finished) { diff --git a/applications/external/heap_defence_game/heap_defence.c b/applications/external/heap_defence_game/heap_defence.c index 0958d4363..3c8483c75 100644 --- a/applications/external/heap_defence_game/heap_defence.c +++ b/applications/external/heap_defence_game/heap_defence.c @@ -535,7 +535,7 @@ int32_t heap_defence_app(void* p) { game->animation = AnimationPause; // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); GameEvent event = {0}; while(event.input.key != InputKeyBack) { diff --git a/applications/external/minesweeper/minesweeper.c b/applications/external/minesweeper/minesweeper.c index 37332b51d..c169b075e 100644 --- a/applications/external/minesweeper/minesweeper.c +++ b/applications/external/minesweeper/minesweeper.c @@ -240,7 +240,7 @@ static bool game_won(Minesweeper* minesweeper_state) { dialog_message_set_buttons(message, NULL, "Play again", NULL); // Call dolphin deed when we win the game - DOLPHIN_DEED(DolphinDeedPluginGameWin); + dolphin_deed(DolphinDeedPluginGameWin); DialogMessageButton choice = dialog_message_show(minesweeper_state->dialogs, message); dialog_message_free(message); @@ -397,7 +397,7 @@ int32_t minesweeper_app(void* p) { gui_add_view_port(gui, view_port, GuiLayerFullscreen); // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); PluginEvent event; for(bool processing = true; processing;) { diff --git a/applications/external/solitaire/solitaire.c b/applications/external/solitaire/solitaire.c index 7d39ae636..4970fd8d9 100644 --- a/applications/external/solitaire/solitaire.c +++ b/applications/external/solitaire/solitaire.c @@ -276,7 +276,7 @@ void tick(GameState* game_state, NotificationApp* notification) { if(game_state->state == GameStatePlay) { if(game_state->top_cards[0].character == 11 && game_state->top_cards[1].character == 11 && game_state->top_cards[2].character == 11 && game_state->top_cards[3].character == 11) { - DOLPHIN_DEED(DolphinDeedPluginGameWin); + dolphin_deed(DolphinDeedPluginGameWin); game_state->state = GameStateAnimate; return; } @@ -492,7 +492,7 @@ int32_t solitaire_app(void* p) { AppEvent event; // Call Dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150); diff --git a/applications/external/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c index e8846b9e2..27ef03f5d 100644 --- a/applications/external/swd_probe/swd_probe_app.c +++ b/applications/external/swd_probe/swd_probe_app.c @@ -3130,7 +3130,7 @@ int32_t swd_probe_app_main(void* p) { DBGS("swd_execute_script"); swd_execute_script(app, ANY_PATH("swd_scripts/startup.swd")); - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); DBGS("processing"); for(bool processing = true; processing;) { diff --git a/applications/external/tetris_game/tetris_game.c b/applications/external/tetris_game/tetris_game.c index 5752e1fc3..07534d725 100644 --- a/applications/external/tetris_game/tetris_game.c +++ b/applications/external/tetris_game/tetris_game.c @@ -389,7 +389,7 @@ int32_t tetris_game_app() { uint8_t downRepeatCounter = 0; // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); for(bool processing = true; processing;) { // This 10U implicitly sets the game loop speed. downRepeatCounter relies on this value diff --git a/applications/external/tictactoe_game/tictactoe_game.c b/applications/external/tictactoe_game/tictactoe_game.c index e617e7c18..6ad076a4a 100644 --- a/applications/external/tictactoe_game/tictactoe_game.c +++ b/applications/external/tictactoe_game/tictactoe_game.c @@ -333,7 +333,7 @@ int32_t tictactoe_game_app(void* p) { gui_add_view_port(gui, view_port, GuiLayerFullscreen); // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); GameEvent event; for(bool processing = true; processing;) { diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 8b0a8c1fa..57e5ff04f 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -173,7 +173,7 @@ int32_t totp_app() { } // Affecting dolphin level - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); // Set system callbacks ViewPort* view_port = view_port_alloc(); diff --git a/applications/external/zombiez/zombiez.c b/applications/external/zombiez/zombiez.c index 602eea723..a9bf74f7a 100644 --- a/applications/external/zombiez/zombiez.c +++ b/applications/external/zombiez/zombiez.c @@ -232,7 +232,7 @@ static void tick(PluginState* const plugin_state) { free(z); plugin_state->zombies[i] = NULL; plugin_state->score++; - //if(plugin_state->score % 15 == 0) DOLPHIN_DEED(getRandomDeed()); + //if(plugin_state->score % 15 == 0) dolphin_deed(getRandomDeed()); //} } else if(z->position.x <= WALL_X && z->position.x > 0) { // zombie got to the wall plugin_state->zombies_count -= 1; @@ -315,7 +315,7 @@ int32_t zombiez_game_app(void* p) { gui_add_view_port(gui, view_port, GuiLayerFullscreen); // Call dolphin deed on game start - DOLPHIN_DEED(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); PluginEvent event; bool isRunning = true; diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index 576779cd1..6f7dd7432 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -190,7 +190,7 @@ int32_t lfrfid_app(void* p) { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); - DOLPHIN_DEED(DolphinDeedRfidEmulate); + dolphin_deed(DolphinDeedRfidEmulate); } else { // TODO: exit properly lfrfid_free(app); diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index e1f416eee..110907832 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -292,7 +292,7 @@ int32_t nfc_app(void* p) { dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); dolphin_deed(DolphinDeedNfcEmulate); diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 1e8976617..f254e08f8 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -93,7 +93,7 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { } else if(event.event == NfcWorkerEventReadBankCard) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index ba405c737..eceedda5e 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -110,7 +110,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexFrequencyAnalyzer); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); - DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); + dolphin_deed(DolphinDeedSubGhzFrequencyAnalyzer); return true; } else if(event.event == SubmenuIndexTest) { scene_manager_set_scene_state( From 49d842e213f2a85be759bc4e5234281cbadf163f Mon Sep 17 00:00:00 2001 From: clashlab Date: Fri, 9 Jun 2023 14:18:32 +0200 Subject: [PATCH 119/370] weather_station: add oregon3 with THGR221 (#2748) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: hedger Co-authored-by: あく --- .../weather_station/protocols/oregon3.c | 365 ++++++++++++++++++ .../weather_station/protocols/oregon3.h | 6 + .../protocols/protocol_items.c | 3 +- .../protocols/protocol_items.h | 1 + 4 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 applications/external/weather_station/protocols/oregon3.c create mode 100644 applications/external/weather_station/protocols/oregon3.h diff --git a/applications/external/weather_station/protocols/oregon3.c b/applications/external/weather_station/protocols/oregon3.c new file mode 100644 index 000000000..a211c5ad3 --- /dev/null +++ b/applications/external/weather_station/protocols/oregon3.c @@ -0,0 +1,365 @@ +#include "oregon3.h" + +#include +#include +#include +#include +#include "ws_generic.h" + +#include +#include + +#define TAG "WSProtocolOregon3" + +static const SubGhzBlockConst ws_oregon3_const = { + .te_long = 1100, + .te_short = 500, + .te_delta = 300, + .min_count_bit_for_found = 32, +}; + +#define OREGON3_PREAMBLE_BITS 28 +#define OREGON3_PREAMBLE_MASK 0b1111111111111111111111111111 +// 24 ones + 0101 (inverted A) +#define OREGON3_PREAMBLE 0b1111111111111111111111110101 + +// Fixed part contains: +// - Sensor type: 16 bits +// - Channel: 4 bits +// - ID (changes when batteries are changed): 8 bits +// - Battery status: 4 bits +#define OREGON3_FIXED_PART_BITS (16 + 4 + 8 + 4) +#define OREGON3_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) +#define OREGON3_CHECKSUM_BITS 8 + +// bit indicating the low battery +#define OREGON3_FLAG_BAT_LOW 0x4 + +/// Documentation for Oregon Scientific protocols can be found here: +/// https://www.osengr.org/Articles/OS-RF-Protocols-IV.pdf +// Sensors ID +#define ID_THGR221 0xf824 + +struct WSProtocolDecoderOregon3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + ManchesterState manchester_state; + bool prev_bit; + + uint8_t var_bits; + uint64_t var_data; +}; + +typedef struct WSProtocolDecoderOregon3 WSProtocolDecoderOregon3; + +typedef enum { + Oregon3DecoderStepReset = 0, + Oregon3DecoderStepFoundPreamble, + Oregon3DecoderStepVarData, +} Oregon3DecoderStep; + +void* ws_protocol_decoder_oregon3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderOregon3* instance = malloc(sizeof(WSProtocolDecoderOregon3)); + instance->base.protocol = &ws_protocol_oregon3; + instance->generic.protocol_name = instance->base.protocol->name; + instance->generic.humidity = WS_NO_HUMIDITY; + instance->generic.temp = WS_NO_TEMPERATURE; + instance->generic.btn = WS_NO_BTN; + instance->generic.channel = WS_NO_CHANNEL; + instance->generic.battery_low = WS_NO_BATT; + instance->generic.id = WS_NO_ID; + instance->prev_bit = false; + return instance; +} + +void ws_protocol_decoder_oregon3_free(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + free(instance); +} + +void ws_protocol_decoder_oregon3_reset(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + instance->decoder.parser_step = Oregon3DecoderStepReset; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); + instance->prev_bit = false; + instance->var_data = 0; + instance->var_bits = 0; +} + +static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { + bool is_long = false; + + if(DURATION_DIFF(duration, ws_oregon3_const.te_long) < ws_oregon3_const.te_delta) { + is_long = true; + } else if(DURATION_DIFF(duration, ws_oregon3_const.te_short) < ws_oregon3_const.te_delta) { + is_long = false; + } else { + return ManchesterEventReset; + } + + if(level) + return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh; + else + return is_long ? ManchesterEventLongLow : ManchesterEventShortLow; +} + +// From sensor id code return amount of bits in variable section +// https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific +static uint8_t oregon3_sensor_id_var_bits(uint16_t sensor_id) { + switch(sensor_id) { + case ID_THGR221: + default: + // nibbles: temp + hum + '0' + return (4 + 2 + 1) * 4; + } +} + +static void ws_oregon3_decode_const_data(WSBlockGeneric* ws_block) { + ws_block->id = OREGON3_SENSOR_ID(ws_block->data); + ws_block->channel = (ws_block->data >> 12) & 0xF; + ws_block->battery_low = (ws_block->data & OREGON3_FLAG_BAT_LOW) ? 1 : 0; +} + +static uint16_t ws_oregon3_bcd_decode_short(uint32_t data) { + return (data & 0xF) * 10 + ((data >> 4) & 0xF); +} + +static float ws_oregon3_decode_temp(uint32_t data) { + int32_t temp_val; + temp_val = ws_oregon3_bcd_decode_short(data >> 4); + temp_val *= 10; + temp_val += (data >> 12) & 0xF; + if(data & 0xF) temp_val = -temp_val; + return (float)temp_val / 10.0; +} + +static void ws_oregon3_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { + switch(sensor_id) { + case ID_THGR221: + default: + ws_b->humidity = ws_oregon3_bcd_decode_short(data >> 4); + ws_b->temp = ws_oregon3_decode_temp(data >> 12); + break; + } +} + +void ws_protocol_decoder_oregon3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + // Oregon v3.0 protocol is inverted + ManchesterEvent event = level_and_duration_to_event(!level, duration); + + // low-level bit sequence decoding + if(event == ManchesterEventReset) { + instance->decoder.parser_step = Oregon3DecoderStepReset; + instance->prev_bit = false; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + } + if(manchester_advance( + instance->manchester_state, event, &instance->manchester_state, &instance->prev_bit)) { + subghz_protocol_blocks_add_bit(&instance->decoder, instance->prev_bit); + } + + switch(instance->decoder.parser_step) { + case Oregon3DecoderStepReset: + // waiting for fixed oregon3 preamble + if(instance->decoder.decode_count_bit >= OREGON3_PREAMBLE_BITS && + ((instance->decoder.decode_data & OREGON3_PREAMBLE_MASK) == OREGON3_PREAMBLE)) { + instance->decoder.parser_step = Oregon3DecoderStepFoundPreamble; + instance->decoder.decode_count_bit = 0; + instance->decoder.decode_data = 0UL; + } + break; + case Oregon3DecoderStepFoundPreamble: + // waiting for fixed oregon3 data + if(instance->decoder.decode_count_bit == OREGON3_FIXED_PART_BITS) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->decoder.decode_data = 0UL; + instance->decoder.decode_count_bit = 0; + + // reverse nibbles in decoded data as oregon v3.0 is LSB first + instance->generic.data = (instance->generic.data & 0x55555555) << 1 | + (instance->generic.data & 0xAAAAAAAA) >> 1; + instance->generic.data = (instance->generic.data & 0x33333333) << 2 | + (instance->generic.data & 0xCCCCCCCC) >> 2; + + ws_oregon3_decode_const_data(&instance->generic); + instance->var_bits = + oregon3_sensor_id_var_bits(OREGON3_SENSOR_ID(instance->generic.data)); + + if(!instance->var_bits) { + // sensor is not supported, stop decoding, but showing the decoded fixed part + instance->decoder.parser_step = Oregon3DecoderStepReset; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } else { + instance->decoder.parser_step = Oregon3DecoderStepVarData; + } + } + break; + case Oregon3DecoderStepVarData: + // waiting for variable (sensor-specific data) + if(instance->decoder.decode_count_bit == instance->var_bits + OREGON3_CHECKSUM_BITS) { + instance->var_data = instance->decoder.decode_data & 0xFFFFFFFFFFFFFFFF; + + // reverse nibbles in var data + instance->var_data = (instance->var_data & 0x5555555555555555) << 1 | + (instance->var_data & 0xAAAAAAAAAAAAAAAA) >> 1; + instance->var_data = (instance->var_data & 0x3333333333333333) << 2 | + (instance->var_data & 0xCCCCCCCCCCCCCCCC) >> 2; + + ws_oregon3_decode_var_data( + &instance->generic, + OREGON3_SENSOR_ID(instance->generic.data), + instance->var_data >> OREGON3_CHECKSUM_BITS); + + instance->decoder.parser_step = Oregon3DecoderStepReset; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } +} + +uint8_t ws_protocol_decoder_oregon3_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_oregon3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + ret = ws_block_generic_serialize(&instance->generic, flipper_format, preset); + if(ret != SubGhzProtocolStatusOk) return ret; + uint32_t temp = instance->var_bits; + if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { + FURI_LOG_E(TAG, "Error adding VarBits"); + return SubGhzProtocolStatusErrorParserOthers; + } + if(!flipper_format_write_hex( + flipper_format, + "VarData", + (const uint8_t*)&instance->var_data, + sizeof(instance->var_data))) { + FURI_LOG_E(TAG, "Error adding VarData"); + return SubGhzProtocolStatusErrorParserOthers; + } + return ret; +} + +SubGhzProtocolStatus + ws_protocol_decoder_oregon3_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + uint32_t temp_data; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = ws_block_generic_deserialize(&instance->generic, flipper_format); + if(ret != SubGhzProtocolStatusOk) { + break; + } + if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { + FURI_LOG_E(TAG, "Missing VarLen"); + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + instance->var_bits = (uint8_t)temp_data; + if(!flipper_format_read_hex( + flipper_format, + "VarData", + (uint8_t*)&instance->var_data, + sizeof(instance->var_data))) { //-V1051 + FURI_LOG_E(TAG, "Missing VarData"); + ret = SubGhzProtocolStatusErrorParserOthers; + break; + } + if(instance->generic.data_count_bit != ws_oregon3_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); + ret = SubGhzProtocolStatusErrorValueBitCount; + break; + } + } while(false); + return ret; +} + +static void oregon3_append_check_sum(uint32_t fix_data, uint64_t var_data, FuriString* output) { + uint8_t sum = fix_data & 0xF; + uint8_t ref_sum = var_data & 0xFF; + var_data >>= 4; + + for(uint8_t i = 1; i < 8; i++) { + fix_data >>= 4; + var_data >>= 4; + sum += (fix_data & 0xF) + (var_data & 0xF); + } + + // swap calculated sum nibbles + sum = (((sum >> 4) & 0xF) | (sum << 4)) & 0xFF; + if(sum == ref_sum) + furi_string_cat_printf(output, "Sum ok: 0x%hhX", ref_sum); + else + furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); +} + +void ws_protocol_decoder_oregon3_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderOregon3* instance = context; + furi_string_cat_printf( + output, + "%s\r\n" + "ID: 0x%04lX, ch: %d, bat: %d, rc: 0x%02lX\r\n", + instance->generic.protocol_name, + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (uint32_t)(instance->generic.data >> 4) & 0xFF); + + if(instance->var_bits > 0) { + furi_string_cat_printf( + output, + "Temp:%d.%d C Hum:%d%%", + (int16_t)instance->generic.temp, + abs( + ((int16_t)(instance->generic.temp * 10) - + (((int16_t)instance->generic.temp) * 10))), + instance->generic.humidity); + oregon3_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); + } +} + +const SubGhzProtocolDecoder ws_protocol_oregon3_decoder = { + .alloc = ws_protocol_decoder_oregon3_alloc, + .free = ws_protocol_decoder_oregon3_free, + + .feed = ws_protocol_decoder_oregon3_feed, + .reset = ws_protocol_decoder_oregon3_reset, + + .get_hash_data = ws_protocol_decoder_oregon3_get_hash_data, + .serialize = ws_protocol_decoder_oregon3_serialize, + .deserialize = ws_protocol_decoder_oregon3_deserialize, + .get_string = ws_protocol_decoder_oregon3_get_string, +}; + +const SubGhzProtocol ws_protocol_oregon3 = { + .name = WS_PROTOCOL_OREGON3_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_oregon3_decoder, +}; diff --git a/applications/external/weather_station/protocols/oregon3.h b/applications/external/weather_station/protocols/oregon3.h new file mode 100644 index 000000000..ec51ddb00 --- /dev/null +++ b/applications/external/weather_station/protocols/oregon3.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +#define WS_PROTOCOL_OREGON3_NAME "Oregon3" +extern const SubGhzProtocol ws_protocol_oregon3; diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index cd4bae76d..93dc25488 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -11,6 +11,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_lacrosse_tx, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, + &ws_protocol_oregon3, &ws_protocol_acurite_592txr, &ws_protocol_ambient_weather, &ws_protocol_auriol_th, @@ -21,4 +22,4 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { const SubGhzProtocolRegistry weather_station_protocol_registry = { .items = weather_station_protocol_registry_items, - .size = COUNT_OF(weather_station_protocol_registry_items)}; \ No newline at end of file + .size = COUNT_OF(weather_station_protocol_registry_items)}; diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index 0398c11f2..712eb07f2 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -11,6 +11,7 @@ #include "lacrosse_tx.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" +#include "oregon3.h" #include "acurite_592txr.h" #include "ambient_weather.h" #include "auriol_hg0601a.h" From 2312fe5bfc8cc388291acda33ab2aa72ff025743 Mon Sep 17 00:00:00 2001 From: hedger Date: Fri, 9 Jun 2023 16:27:47 +0400 Subject: [PATCH 120/370] [FL-3361] fbt: stable build dates (#2751) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * scripts: using commit date for clean build timestamp; current day otherwise * scripts: version: Removing GIT_COMMIT_DATE from final data Co-authored-by: あく --- scripts/version.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/scripts/version.py b/scripts/version.py index f00c1f531..e68f7b41d 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -40,12 +40,23 @@ class GitVersion: except subprocess.CalledProcessError: version = "unknown" + if "SOURCE_DATE_EPOCH" in os.environ: + commit_date = datetime.utcfromtimestamp( + int(os.environ["SOURCE_DATE_EPOCH"]) + ) + else: + commit_date = datetime.strptime( + self._exec_git("log -1 --format=%cd").strip(), + "%a %b %d %H:%M:%S %Y %z", + ) + return { "GIT_COMMIT": commit, "GIT_BRANCH": branch, "VERSION": version, "BUILD_DIRTY": dirty and 1 or 0, "GIT_ORIGIN": ",".join(self._get_git_origins()), + "GIT_COMMIT_DATE": commit_date, } def _get_git_origins(self): @@ -102,10 +113,11 @@ class Main(App): def generate(self): current_info = GitVersion(self.args.sourcedir).get_version_info() - if "SOURCE_DATE_EPOCH" in os.environ: - build_date = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"])) - else: - build_date = date.today() + build_date = ( + date.today() + if current_info["BUILD_DIRTY"] + else current_info["GIT_COMMIT_DATE"] + ) current_info.update( { @@ -115,6 +127,8 @@ class Main(App): } ) + del current_info["GIT_COMMIT_DATE"] + version_values = [] for key in current_info: val = current_info[key] From 4900e8b7a2f416c106fe09567cbbfcf1b0d18e70 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Fri, 9 Jun 2023 15:41:40 +0300 Subject: [PATCH 121/370] [FL-3284] Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys (#2620) * I was outplayed by the C programming language * Fix emulating empty keys as 0s * Add exceptions for Detect Reader * Sync api_symbols.csv for F18 * Outplayed by the C language [X2] Co-authored-by: Aleksandr Kutuzov --- firmware/targets/f18/api_symbols.csv | 2 +- firmware/targets/f7/api_symbols.csv | 4 ++-- lib/nfc/nfc_worker.c | 26 +++++++++++++++++++------ lib/nfc/protocols/mifare_classic.c | 29 +++++++++++++++++++++++----- lib/nfc/protocols/mifare_classic.h | 5 ++++- 5 files changed, 51 insertions(+), 15 deletions(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 85f09f1c3..d429de655 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.0,, +Version,+,30.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index afa761632..80b4eedbd 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.0,, +Version,+,30.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1957,7 +1957,7 @@ Function,-,mf_classic_dict_get_total_keys,uint32_t,MfClassicDict* Function,-,mf_classic_dict_is_key_present,_Bool,"MfClassicDict*, uint8_t*" Function,-,mf_classic_dict_is_key_present_str,_Bool,"MfClassicDict*, FuriString*" Function,-,mf_classic_dict_rewind,_Bool,MfClassicDict* -Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*" +Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*, _Bool" Function,-,mf_classic_get_classic_type,MfClassicType,"uint8_t, uint8_t, uint8_t" Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*, uint8_t*" Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 974bb0a4d..a6bb93f59 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -917,7 +917,8 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyA, !deactivated, cuid)) { mf_classic_set_key_found(data, i, MfClassicKeyA, key); - FURI_LOG_D(TAG, "Key A found"); + FURI_LOG_D( + TAG, "Key A found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); uint64_t found_key; @@ -939,8 +940,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key A is marked as found and matches the searching key, invalidate it + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + if(mf_classic_is_key_found(data, i, MfClassicKeyA) && - data->block[i].value[0] == key) { + memcmp(found_key, current_key, 6) == 0) { mf_classic_set_key_not_found(data, i, MfClassicKeyA); is_key_a_found = false; FURI_LOG_D(TAG, "Key %dA not found in attack", i); @@ -950,7 +957,8 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { - FURI_LOG_D(TAG, "Key B found"); + FURI_LOG_D( + TAG, "Key B found: %04lx%08lx", (uint32_t)(key >> 32), (uint32_t)key); mf_classic_set_key_found(data, i, MfClassicKeyB, key); nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); @@ -958,8 +966,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key B is marked as found and matches the searching key, invalidate it + uint8_t found_key[6]; + memcpy(found_key, data->block[i].value + 10, 6); + + uint8_t current_key[6]; + memcpy(current_key, &key, 6); + if(mf_classic_is_key_found(data, i, MfClassicKeyB) && - data->block[i].value[10] == key) { + memcmp(found_key, current_key, 6) == 0) { mf_classic_set_key_not_found(data, i, MfClassicKeyB); is_key_b_found = false; FURI_LOG_D(TAG, "Key %dB not found in attack", i); @@ -1004,7 +1018,7 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { furi_hal_nfc_listen_start(nfc_data); while(nfc_worker->state == NfcWorkerStateMfClassicEmulate) { //-V1044 if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { - mf_classic_emulator(&emulator, &tx_rx); + mf_classic_emulator(&emulator, &tx_rx, false); } } if(emulator.data_changed) { @@ -1291,7 +1305,7 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { NfcProtocol protocol = reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8); if(protocol == NfcDeviceProtocolMifareClassic) { - mf_classic_emulator(&emulator, &tx_rx); + mf_classic_emulator(&emulator, &tx_rx, true); } } else { reader_no_data_received_cnt++; diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index d2d7467dc..ebe49a4a0 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -845,7 +845,10 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data return sectors_read; } -bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) { +bool mf_classic_emulator( + MfClassicEmulator* emulator, + FuriHalNfcTxRxContext* tx_rx, + bool is_reader_analyzer) { furi_assert(emulator); furi_assert(tx_rx); bool command_processed = false; @@ -892,11 +895,27 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { - key = nfc_util_bytes2num(sector_trailer->key_a, 6); - access_key = MfClassicKeyA; + if(mf_classic_is_key_found( + &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyA) || + is_reader_analyzer) { + key = nfc_util_bytes2num(sector_trailer->key_a, 6); + access_key = MfClassicKeyA; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } else { - key = nfc_util_bytes2num(sector_trailer->key_b, 6); - access_key = MfClassicKeyB; + if(mf_classic_is_key_found( + &emulator->data, mf_classic_get_sector_by_block(block), MfClassicKeyB) || + is_reader_analyzer) { + key = nfc_util_bytes2num(sector_trailer->key_b, 6); + access_key = MfClassicKeyB; + } else { + FURI_LOG_D(TAG, "Key not known"); + command_processed = true; + break; + } } uint32_t nonce = prng_successor(DWT->CYCCNT, 32) ^ 0xAA; diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index c03350f2e..b3e3cbdf8 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -199,7 +199,10 @@ uint8_t mf_classic_read_card( uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data); -bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); +bool mf_classic_emulator( + MfClassicEmulator* emulator, + FuriHalNfcTxRxContext* tx_rx, + bool is_reader_analyzer); void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto); From 605f9190b0fd8540ffe69204a3820130b70b4adb Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 9 Jun 2023 13:55:28 +0100 Subject: [PATCH 122/370] Fix dolphin deed's --- applications/external/dtmf_dolphin/dtmf_dolphin.c | 2 +- applications/external/esp8266_deauth/esp8266_deauth.c | 2 +- .../external/gpioreader_b/scenes/gpio_scene_start.c | 2 +- applications/external/ir_remote/infrared_remote_app.c | 2 +- applications/external/mousejacker/mousejacker.c | 2 +- applications/external/multi_fuzzer/fuzzer.c | 2 +- applications/external/nfc_magic/nfc_magic.c | 2 +- applications/external/nrf24scan/nrf24scan.c | 2 +- applications/external/nrfsniff/nrfsniff.c | 2 +- applications/external/picopass/picopass.c | 2 +- applications/external/pocsag_pager/pocsag_pager_app.c | 2 +- applications/external/protoview/app.c | 2 +- applications/external/sentry_safe/sentry_safe.c | 2 +- applications/external/spectrum_analyzer/spectrum_analyzer.c | 2 +- applications/external/subghz_bruteforcer/subbrute.c | 2 +- applications/external/subghz_playlist/playlist.c | 2 +- applications/external/subghz_remote/subghz_remote_app.c | 2 +- applications/external/wifi_deauther/wifi_deauther_app.c | 2 +- .../external/wifi_marauder_companion/wifi_marauder_app.c | 2 +- applications/external/zombiez/zombiez.c | 2 +- applications/main/bad_kb/helpers/ducky_script.c | 4 ++-- applications/main/nfc/nfc.c | 2 +- applications/main/nfc/scenes/nfc_scene_felica_menu.c | 4 ++-- applications/main/nfc/scenes/nfc_scene_nfcf_menu.c | 4 ++-- applications/main/nfc/scenes/nfc_scene_read.c | 6 +++--- 25 files changed, 30 insertions(+), 30 deletions(-) diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin.c b/applications/external/dtmf_dolphin/dtmf_dolphin.c index 732b22ef8..4447fb278 100644 --- a/applications/external/dtmf_dolphin/dtmf_dolphin.c +++ b/applications/external/dtmf_dolphin/dtmf_dolphin.c @@ -83,7 +83,7 @@ int32_t dtmf_dolphin_app(void* p) { UNUSED(p); DTMFDolphinApp* app = app_alloc(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); app_free(app); diff --git a/applications/external/esp8266_deauth/esp8266_deauth.c b/applications/external/esp8266_deauth/esp8266_deauth.c index 35b9a850b..76551f60c 100644 --- a/applications/external/esp8266_deauth/esp8266_deauth.c +++ b/applications/external/esp8266_deauth/esp8266_deauth.c @@ -321,7 +321,7 @@ int32_t esp8266_deauth_app(void* p) { SWiFiDeauthApp* app = malloc(sizeof(SWiFiDeauthApp)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); esp8266_deauth_app_init(app); furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonUp, GpioModeOutputPushPull); diff --git a/applications/external/gpioreader_b/scenes/gpio_scene_start.c b/applications/external/gpioreader_b/scenes/gpio_scene_start.c index 57cab91a5..19d0173fa 100644 --- a/applications/external/gpioreader_b/scenes/gpio_scene_start.c +++ b/applications/external/gpioreader_b/scenes/gpio_scene_start.c @@ -97,7 +97,7 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == GpioStartEventUsbUart) { scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, GpioItemUsbUart); if(!furi_hal_usb_is_locked()) { - DOLPHIN_DEED(DolphinDeedGpioUartBridge); + dolphin_deed(DolphinDeedGpioUartBridge); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); } else { scene_manager_next_scene(app->scene_manager, GpioSceneUsbUartCloseRpc); diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c index 7056fae89..0e21d5560 100644 --- a/applications/external/ir_remote/infrared_remote_app.c +++ b/applications/external/ir_remote/infrared_remote_app.c @@ -161,7 +161,7 @@ static void app_input_callback(InputEvent* input_event, void* ctx) { int32_t infrared_remote_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); // App button string IRApp* app = malloc(sizeof(IRApp)); diff --git a/applications/external/mousejacker/mousejacker.c b/applications/external/mousejacker/mousejacker.c index 24bc7c67d..1727940f3 100644 --- a/applications/external/mousejacker/mousejacker.c +++ b/applications/external/mousejacker/mousejacker.c @@ -287,7 +287,7 @@ void start_mjthread(PluginState* plugin_state) { int32_t mousejacker_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); PluginState* plugin_state = malloc(sizeof(PluginState)); mousejacker_state_init(plugin_state); diff --git a/applications/external/multi_fuzzer/fuzzer.c b/applications/external/multi_fuzzer/fuzzer.c index c4cf441e4..fd0ec90db 100644 --- a/applications/external/multi_fuzzer/fuzzer.c +++ b/applications/external/multi_fuzzer/fuzzer.c @@ -21,7 +21,7 @@ static void fuzzer_app_tick_event_callback(void* context) { } PacsFuzzerApp* fuzzer_app_alloc() { - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); PacsFuzzerApp* app = malloc(sizeof(PacsFuzzerApp)); diff --git a/applications/external/nfc_magic/nfc_magic.c b/applications/external/nfc_magic/nfc_magic.c index b14e34072..0fce8bb66 100644 --- a/applications/external/nfc_magic/nfc_magic.c +++ b/applications/external/nfc_magic/nfc_magic.c @@ -174,7 +174,7 @@ int32_t nfc_magic_app(void* p) { UNUSED(p); NfcMagic* nfc_magic = nfc_magic_alloc(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneStart); view_dispatcher_run(nfc_magic->view_dispatcher); diff --git a/applications/external/nrf24scan/nrf24scan.c b/applications/external/nrf24scan/nrf24scan.c index d7a8b3d31..f1182cff9 100644 --- a/applications/external/nrf24scan/nrf24scan.c +++ b/applications/external/nrf24scan/nrf24scan.c @@ -1332,7 +1332,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { int32_t nrf24scan_app(void* p) { UNUSED(p); APP = malloc(sizeof(Nrf24Scan)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); APP->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); diff --git a/applications/external/nrfsniff/nrfsniff.c b/applications/external/nrfsniff/nrfsniff.c index 1c2ea543f..822d151c1 100644 --- a/applications/external/nrfsniff/nrfsniff.c +++ b/applications/external/nrfsniff/nrfsniff.c @@ -315,7 +315,7 @@ static void start_sniffing() { int32_t nrfsniff_app(void* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); uint8_t address[5] = {0}; uint32_t start = 0; hexlify(address, 5, top_address); diff --git a/applications/external/picopass/picopass.c b/applications/external/picopass/picopass.c index e3271ae48..ecee75375 100644 --- a/applications/external/picopass/picopass.c +++ b/applications/external/picopass/picopass.c @@ -201,7 +201,7 @@ int32_t picopass_app(void* p) { UNUSED(p); picopass_migrate_from_old_folder(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); Picopass* picopass = picopass_alloc(); scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart); diff --git a/applications/external/pocsag_pager/pocsag_pager_app.c b/applications/external/pocsag_pager/pocsag_pager_app.c index b52b2f37f..dc61c35f9 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app.c +++ b/applications/external/pocsag_pager/pocsag_pager_app.c @@ -202,7 +202,7 @@ int32_t pocsag_pager_app(void* p) { UNUSED(p); POCSAGPagerApp* pocsag_pager_app = pocsag_pager_app_alloc(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(pocsag_pager_app->view_dispatcher); pocsag_pager_app_free(pocsag_pager_app); diff --git a/applications/external/protoview/app.c b/applications/external/protoview/app.c index 70205a0a4..0f72fb927 100644 --- a/applications/external/protoview/app.c +++ b/applications/external/protoview/app.c @@ -262,7 +262,7 @@ static bool keyboard_view_dispatcher_navigation_callback(void* ctx) { int32_t protoview_app_entry(void* p) { UNUSED(p); ProtoViewApp* app = protoview_app_alloc(); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); /* Create a timer. We do data analysis in the callback. */ FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); diff --git a/applications/external/sentry_safe/sentry_safe.c b/applications/external/sentry_safe/sentry_safe.c index a0667b686..9c7445463 100644 --- a/applications/external/sentry_safe/sentry_safe.c +++ b/applications/external/sentry_safe/sentry_safe.c @@ -85,7 +85,7 @@ int32_t sentry_safe_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Event)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); SentryState* sentry_state = malloc(sizeof(SentryState)); diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c index 6250ac039..be3e043d9 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/external/spectrum_analyzer/spectrum_analyzer.c @@ -403,7 +403,7 @@ void spectrum_analyzer_free(SpectrumAnalyzer* instance) { int32_t spectrum_analyzer_app(void* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); SpectrumAnalyzer* spectrum_analyzer = spectrum_analyzer_alloc(); InputEvent input; diff --git a/applications/external/subghz_bruteforcer/subbrute.c b/applications/external/subghz_bruteforcer/subbrute.c index 99334c05e..dbe83a5c4 100644 --- a/applications/external/subghz_bruteforcer/subbrute.c +++ b/applications/external/subghz_bruteforcer/subbrute.c @@ -174,7 +174,7 @@ void subbrute_popup_closed_callback(void* context) { int32_t subbrute_app(void* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); SubBruteState* instance = subbrute_alloc(); view_dispatcher_attach_to_gui( instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); diff --git a/applications/external/subghz_playlist/playlist.c b/applications/external/subghz_playlist/playlist.c index 1373c14a2..6576ec748 100644 --- a/applications/external/subghz_playlist/playlist.c +++ b/applications/external/subghz_playlist/playlist.c @@ -697,7 +697,7 @@ void playlist_free(Playlist* app) { int32_t playlist_app(void* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); // create playlist folder { diff --git a/applications/external/subghz_remote/subghz_remote_app.c b/applications/external/subghz_remote/subghz_remote_app.c index ed9800781..4fcd19d34 100644 --- a/applications/external/subghz_remote/subghz_remote_app.c +++ b/applications/external/subghz_remote/subghz_remote_app.c @@ -147,7 +147,7 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) { int32_t subghz_remote_app(void* p) { UNUSED(p); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc(); furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER); diff --git a/applications/external/wifi_deauther/wifi_deauther_app.c b/applications/external/wifi_deauther/wifi_deauther_app.c index 2c9ea17fc..2e05a1ce0 100644 --- a/applications/external/wifi_deauther/wifi_deauther_app.c +++ b/applications/external/wifi_deauther/wifi_deauther_app.c @@ -25,7 +25,7 @@ static void wifi_deauther_app_tick_event_callback(void* context) { WifideautherApp* wifi_deauther_app_alloc() { WifideautherApp* app = malloc(sizeof(WifideautherApp)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); app->gui = furi_record_open(RECORD_GUI); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index 821987a8a..c7f03061a 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -24,7 +24,7 @@ static void wifi_marauder_app_tick_event_callback(void* context) { WifiMarauderApp* wifi_marauder_app_alloc() { WifiMarauderApp* app = malloc(sizeof(WifiMarauderApp)); - DOLPHIN_DEED(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); app->gui = furi_record_open(RECORD_GUI); app->dialogs = furi_record_open(RECORD_DIALOGS); diff --git a/applications/external/zombiez/zombiez.c b/applications/external/zombiez/zombiez.c index 590edf97d..08fd76f82 100644 --- a/applications/external/zombiez/zombiez.c +++ b/applications/external/zombiez/zombiez.c @@ -231,7 +231,7 @@ static void tick(PluginState* const plugin_state) { free(z); plugin_state->zombies[i] = NULL; plugin_state->score++; - //if(plugin_state->score % 15 == 0) DOLPHIN_DEED(getRandomDeed()); + //if(plugin_state->score % 15 == 0) dolphin_deed(getRandomDeed()); //} } else if(z->position.x <= WALL_X && z->position.x > 0) { // zombie got to the wall plugin_state->zombies_count -= 1; diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 8ffb1430f..3724dfb45 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -730,7 +730,7 @@ static int32_t bad_kb_worker(void* context) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtStartStop) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadKbPlayScript); + dolphin_deed(DolphinDeedBadKbPlayScript); delay_val = 0; bad_kb->buf_len = 0; bad_kb->st.line_cur = 0; @@ -754,7 +754,7 @@ static int32_t bad_kb_worker(void* context) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadKbPlayScript); + dolphin_deed(DolphinDeedBadKbPlayScript); delay_val = 0; bad_kb->buf_len = 0; bad_kb->st.line_cur = 0; diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 8254100aa..1b83ffad7 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -296,7 +296,7 @@ int32_t nfc_app(char* p) { dolphin_deed(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); dolphin_deed(DolphinDeedNfcEmulate); diff --git a/applications/main/nfc/scenes/nfc_scene_felica_menu.c b/applications/main/nfc/scenes/nfc_scene_felica_menu.c index b989047d6..75b61cb2d 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_menu.c @@ -53,9 +53,9 @@ bool nfc_scene_felica_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexUnlock) { diff --git a/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c index cae7055b1..69815dab1 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcf_menu.c @@ -49,9 +49,9 @@ bool nfc_scene_nfcf_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + dolphin_deed(DolphinDeedNfcAddEmulate); } else { - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); } consumed = true; } else if(event.event == SubmenuIndexUnlock) { diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 190ddca6a..45b0f4a7e 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -92,7 +92,7 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { } else if(event.event == NfcWorkerEventReadBankCard) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadMfClassicDictAttackRequired) { if(mf_classic_dict_check_presence(MfClassicDictTypeSystem)) { @@ -104,12 +104,12 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { } else if(event.event == NfcWorkerEventReadUidNfcF) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcfReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventReadFelica) { notification_message(nfc->notifications, &sequence_success); scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaReadSuccess); - DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + dolphin_deed(DolphinDeedNfcReadSuccess); consumed = true; } else if(event.event == NfcWorkerEventCardDetected) { nfc_scene_read_set_state(nfc, NfcSceneReadStateReading); From b61a84313a5fa1ef1427979ac6a5168562cc59ca Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 9 Jun 2023 17:58:32 +0300 Subject: [PATCH 123/370] Update subbrute --- applications/external/subbrute | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/subbrute b/applications/external/subbrute index c55814712..d02418362 160000 --- a/applications/external/subbrute +++ b/applications/external/subbrute @@ -1 +1 @@ -Subproject commit c55814712d07cb5983c0c46dfc1386ab973358c3 +Subproject commit d02418362472a6f4b2c3c1c80bbdc12e52b69396 From e407c8382951d28c7255719bc698074a3ee7b950 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 9 Jun 2023 16:04:15 +0100 Subject: [PATCH 124/370] Dont allow rpc unlock by default if pin locked --- .../xtreme_app_scene_interface_lockscreen.c | 21 ++++++++++++++++++- applications/services/desktop/desktop.c | 5 ++++- .../targets/furi_hal_include/furi_hal_rtc.h | 2 +- lib/xtreme/settings.c | 6 ++++++ lib/xtreme/xtreme.h | 1 + 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c index 632401a7c..21a570bfb 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c @@ -3,6 +3,7 @@ enum VarItemListIndex { VarItemListIndexLockOnBoot, VarItemListIndexFormatOn10BadPins, + VarItemListIndexPinUnlockFromApp, VarItemListIndexShowTime, VarItemListIndexShowSeconds, VarItemListIndexShowDate, @@ -31,6 +32,14 @@ static void xtreme_app_scene_interface_lockscreen_bad_pins_format_changed(Variab app->save_settings = true; } +static void xtreme_app_scene_interface_lockscreen_pin_unlock_from_app_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->pin_unlock_from_app = value; + app->save_settings = true; +} + static void xtreme_app_scene_interface_lockscreen_lockscreen_time_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); @@ -89,13 +98,23 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Format on 10 bad PINs", + "Format on 10 Bad PINs", 2, xtreme_app_scene_interface_lockscreen_bad_pins_format_changed, app); variable_item_set_current_value_index(item, xtreme_settings->bad_pins_format); variable_item_set_current_value_text(item, xtreme_settings->bad_pins_format ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, + "PIN Unlock From App", + 2, + xtreme_app_scene_interface_lockscreen_pin_unlock_from_app_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->pin_unlock_from_app); + variable_item_set_current_value_text( + item, xtreme_settings->pin_unlock_from_app ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Show Time", diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 9d48c580e..131b6136f 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -439,7 +439,9 @@ bool desktop_api_is_locked(Desktop* instance) { void desktop_api_unlock(Desktop* instance) { furi_assert(instance); - view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); + if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) || XTREME_SETTINGS()->pin_unlock_from_app) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopLockedEventUnlocked); + } } FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) { @@ -464,6 +466,7 @@ int32_t desktop_srv(void* p) { if(!ok) { memset(&desktop->settings, 0, sizeof(desktop->settings)); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + furi_hal_rtc_set_pin_fails(0); } desktop_clock_toggle_view(desktop, desktop->settings.display_clock); diff --git a/firmware/targets/furi_hal_include/furi_hal_rtc.h b/firmware/targets/furi_hal_include/furi_hal_rtc.h index 186d22f07..98dfc7952 100644 --- a/firmware/targets/furi_hal_include/furi_hal_rtc.h +++ b/firmware/targets/furi_hal_include/furi_hal_rtc.h @@ -27,7 +27,7 @@ typedef struct { typedef enum { FuriHalRtcFlagDebug = (1 << 0), FuriHalRtcFlagFactoryReset = (1 << 1), - FuriHalRtcFlagLock = (1 << 2), + FuriHalRtcFlagLock = (1 << 2), // WITH PIN, on OFW also for keypad (removes option to do both) FuriHalRtcFlagC2Update = (1 << 3), FuriHalRtcFlagHandOrient = (1 << 4), FuriHalRtcFlagLegacySleep = (1 << 5), diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index 023cbcbc9..bdceb6900 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -14,6 +14,7 @@ XtremeSettings xtreme_settings = { .wii_menu = true, // ON .lock_on_boot = false, // OFF .bad_pins_format = false, // OFF + .pin_unlock_from_app = false, // OFF .lockscreen_time = true, // ON .lockscreen_seconds = false, // OFF .lockscreen_date = true, // ON @@ -75,6 +76,10 @@ void XTREME_SETTINGS_LOAD() { x->bad_pins_format = b; } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "pin_unlock_from_app", &b, 1)) { + x->pin_unlock_from_app = b; + } + flipper_format_rewind(file); if(flipper_format_read_bool(file, "lock_on_boot", &b, 1)) { x->lock_on_boot = b; } @@ -173,6 +178,7 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "fallback_anim", &x->fallback_anim, 1); flipper_format_write_bool(file, "wii_menu", &x->wii_menu, 1); flipper_format_write_bool(file, "bad_pins_format", &x->bad_pins_format, 1); + flipper_format_write_bool(file, "pin_unlock_from_app", &x->pin_unlock_from_app, 1); flipper_format_write_bool(file, "lock_on_boot", &x->lock_on_boot, 1); flipper_format_write_bool(file, "lockscreen_time", &x->lockscreen_time, 1); flipper_format_write_bool(file, "lockscreen_seconds", &x->lockscreen_seconds, 1); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 89a126201..b2a86e9c0 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -23,6 +23,7 @@ typedef struct { bool wii_menu; bool lock_on_boot; bool bad_pins_format; + bool pin_unlock_from_app; bool lockscreen_time; bool lockscreen_seconds; bool lockscreen_date; From c1bf94da12cb6ae710b5df41d861234a2efee6fa Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 9 Jun 2023 16:24:09 +0100 Subject: [PATCH 125/370] Update build workflows --- .github/workflows/build.yml | 4 ++-- .github/workflows/hotfix.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- .github/workflows/sonarcloud.yaml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37ec991e6..038954256 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,8 +28,8 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET updater_package + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET_HW updater_package done - name: "Check for uncommitted changes" diff --git a/.github/workflows/hotfix.yml b/.github/workflows/hotfix.yml index 9b78d6e2b..d8430d396 100644 --- a/.github/workflows/hotfix.yml +++ b/.github/workflows/hotfix.yml @@ -35,8 +35,8 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET updater_package + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET_HW FORCE_NO_DIRTY=1 updater_package done - name: "Check for uncommitted changes" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b74a7e315..0fce2a019 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,8 +35,8 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET updater_package + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET_HW FORCE_NO_DIRTY=1 updater_package done - name: "Check for uncommitted changes" diff --git a/.github/workflows/sonarcloud.yaml b/.github/workflows/sonarcloud.yaml index e763aaad7..58a6568a2 100644 --- a/.github/workflows/sonarcloud.yaml +++ b/.github/workflows/sonarcloud.yaml @@ -53,8 +53,8 @@ jobs: mkdir ${{ env.BUILD_WRAPPER_OUT_DIR }} set -e for TARGET in ${TARGETS}; do - TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build "./fbt TARGET_HW=$TARGET updater_package" + TARGET_HW="$(echo "${TARGET}" | sed 's/f//')"; \ + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} ./sonar-build "./fbt TARGET_HW=$TARGET_HW updater_package" done - name: Run sonar-scanner From 711b83b2ac990e238aacaca17cb72a2b23d4fbbd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 9 Jun 2023 18:29:47 +0100 Subject: [PATCH 126/370] Add missing bt hid releases --- .../main/bad_kb/helpers/ducky_script.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 3724dfb45..9ada3ce1e 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -869,7 +869,11 @@ static int32_t bad_kb_worker(void* context) { worker_state = BadKbStateRunning; } else if(flags & WorkerEvtDisconnect) { worker_state = BadKbStateNotConnected; // Disconnected - furi_hal_hid_kb_release_all(); + if(bad_kb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } } bad_kb->st.state = worker_state; continue; @@ -884,11 +888,19 @@ static int32_t bad_kb_worker(void* context) { } else if(flags & WorkerEvtStartStop) { worker_state = BadKbStateIdle; // Stop executing script bad_kb->st.state = worker_state; - furi_hal_hid_kb_release_all(); + if(bad_kb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } } else if(flags & WorkerEvtDisconnect) { worker_state = BadKbStateNotConnected; // Disconnected bad_kb->st.state = worker_state; - furi_hal_hid_kb_release_all(); + if(bad_kb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } } else if(flags & WorkerEvtPauseResume) { if(pause_state == BadKbStateRunning) { if(delay_val > 0) { From fef5efb38afd4e9b9ac12ea4747fbd14f48bf6db Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 9 Jun 2023 22:10:21 +0300 Subject: [PATCH 127/370] Update IR assets Add new ACs and TCL tv --- assets/resources/infrared/assets/ac.ir | 113 ++++++++++++++++++++++++- assets/resources/infrared/assets/tv.ir | 40 ++++++++- 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index b4bea9dcb..27c70051e 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -471,7 +471,7 @@ frequency: 38000 duty_cycle: 0.330000 data: 4467 4390 571 1583 572 505 595 1560 572 1583 572 505 572 505 596 1559 596 482 596 481 597 1559 626 451 655 422 625 1529 596 1559 596 481 572 1582 573 1583 571 505 572 1583 595 1560 594 1561 592 1562 594 1561 593 1562 593 484 593 1563 592 485 592 485 592 485 592 485 593 484 593 485 592 485 592 1562 593 485 592 1563 592 1562 593 1562 594 483 593 485 593 1562 592 485 593 1561 593 484 593 484 593 484 593 1562 593 1562 592 5163 4462 4370 592 1563 593 484 592 1563 592 1563 592 485 592 485 593 1562 593 484 593 485 592 1562 593 484 593 485 592 1562 593 1562 593 485 592 1563 592 1563 592 485 592 1563 592 1562 593 1562 593 1563 592 1563 592 1562 592 485 593 1562 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 1563 592 1563 593 485 592 485 592 1563 592 485 592 1563 591 485 593 485 592 485 592 1563 592 1563 591 # -# Model: Chigo AC +# Model: Chigo CS-21H3A-B155 name: Off type: raw frequency: 38000 @@ -507,3 +507,114 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 6064 7357 602 1634 602 1635 602 1634 603 1634 603 1634 603 1634 602 1634 602 1635 627 489 626 490 600 516 600 517 599 518 598 517 599 518 598 517 599 1639 598 1639 598 1638 599 1638 599 1639 598 1639 598 1639 598 1639 598 517 599 518 598 518 598 518 598 518 598 518 598 518 598 518 598 1639 598 1639 598 518 598 1639 598 1639 598 1639 598 1639 598 1639 598 518 598 518 598 1639 598 518 598 518 598 518 598 518 598 518 598 1639 598 518 598 518 598 1639 598 1639 598 518 598 518 598 1640 597 518 598 1639 598 1640 597 518 598 518 598 1639 598 1639 598 519 597 518 598 1640 597 1640 597 519 597 1640 597 1640 597 519 597 1640 597 1640 597 519 597 519 597 1641 596 519 597 519 597 1640 597 519 597 519 597 1640 597 519 597 1640 597 519 597 1641 596 520 596 519 597 1641 596 519 597 1641 596 520 596 1641 596 520 596 1642 595 1641 596 7363 596 +# +# Model: Tosot T24H-ILF/I/T24H-ILU/O +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9067 4428 600 1590 599 481 597 481 598 482 596 484 595 487 592 511 568 511 568 511 568 1621 569 511 568 1622 568 511 568 511 568 511 569 511 568 511 569 511 568 511 568 511 568 511 568 1622 568 511 568 511 568 511 568 511 569 511 568 511 568 1622 568 511 568 1622 568 511 568 511 568 1622 568 511 648 20161 569 511 568 511 568 511 568 511 568 511 569 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 1622 568 511 568 511 568 511 568 511 568 511 568 511 569 511 568 511 568 511 569 511 568 512 568 511 568 511 568 511 568 1622 568 1622 568 1622 568 511 648 40402 9171 4434 594 1621 569 510 569 511 568 511 568 510 569 511 568 511 568 511 568 511 568 1621 569 511 568 1621 569 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 1622 568 511 568 511 568 511 569 511 568 511 568 511 568 1622 568 1622 568 1622 568 512 568 512 567 1622 568 512 673 20162 568 511 568 511 568 510 569 510 569 511 568 511 568 511 568 511 569 511 568 511 569 511 568 511 568 511 568 511 568 511 568 511 569 511 568 511 569 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 569 511 568 1622 568 511 568 1622 568 512 567 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9087 4428 598 1591 598 482 596 482 596 1594 595 1595 594 1596 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 1597 593 486 593 486 593 486 593 486 593 487 592 486 593 1597 592 487 592 1597 592 487 592 487 592 1597 593 487 671 20132 593 1596 593 486 593 486 593 486 593 1596 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 1597 592 487 592 486 593 486 593 486 593 487 592 486 593 486 593 487 592 487 592 487 592 487 592 487 592 487 592 486 593 487 592 1597 593 1597 592 487 672 40393 9168 4432 594 1596 593 486 593 486 593 1597 593 1596 593 1597 592 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 487 592 487 592 486 593 486 593 1597 592 487 592 486 593 487 592 487 592 487 592 487 592 1597 593 1597 593 1597 592 487 592 487 592 1597 592 487 698 20131 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 486 593 1597 593 486 593 1597 592 487 592 486 593 487 592 486 593 487 592 487 592 487 592 487 592 1597 592 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9065 4429 598 482 597 482 597 1593 596 1594 595 1596 594 1597 593 487 592 487 592 487 593 1598 592 1598 592 1598 592 488 592 487 592 488 591 511 568 487 592 488 591 511 568 511 568 488 591 1599 591 511 568 511 568 511 568 511 568 511 569 511 568 1622 568 511 568 1622 568 511 568 511 568 1622 568 511 648 20137 593 1598 592 487 593 487 592 488 591 1599 591 488 591 487 592 488 591 511 568 488 591 511 568 489 590 511 568 1599 591 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 569 511 568 511 568 511 569 511 568 511 568 1622 568 1622 568 1622 568 511 648 40403 9170 4434 594 486 593 487 592 1597 593 1598 592 1598 592 1597 593 487 592 487 592 487 593 1598 592 1598 592 1598 592 488 591 488 591 488 591 511 568 488 591 488 591 511 568 511 569 511 568 1622 568 511 568 511 568 511 568 511 569 511 568 511 568 1622 568 1622 568 1622 568 511 569 511 568 1622 568 511 674 20138 592 488 591 488 591 511 568 488 591 511 568 511 568 511 568 511 568 511 568 511 568 489 590 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 511 568 1622 568 511 568 1622 568 511 568 511 568 511 569 511 568 511 568 1622 568 511 569 511 568 1622 568 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9104 4416 604 474 604 475 603 1587 602 1588 601 1590 599 1590 599 480 600 481 598 1591 599 1591 599 1591 599 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 599 481 598 1592 598 481 598 481 599 481 598 481 598 481 598 481 598 1592 598 481 598 1592 598 481 598 481 598 1592 598 481 678 20131 599 1591 599 481 598 481 598 481 598 1591 599 481 599 481 598 481 598 481 599 481 598 481 598 481 598 481 598 1592 598 481 598 481 599 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 678 40398 9182 4421 599 479 600 480 599 1591 599 1591 599 1591 599 1591 599 480 599 481 598 1591 599 1591 599 1591 599 481 598 481 598 481 598 481 598 481 599 481 598 481 599 480 599 481 599 481 598 1592 598 481 598 481 598 481 599 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 1592 598 481 704 20131 599 481 598 481 598 481 599 480 599 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1592 598 481 598 1592 598 481 599 481 598 481 598 481 598 481 598 481 598 1592 598 481 598 482 597 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9045 4446 576 1613 576 503 575 503 601 1589 575 1615 574 1617 572 506 573 507 572 1618 571 1618 572 1618 571 507 572 507 572 507 572 507 572 508 571 507 572 507 572 507 572 508 571 508 571 1618 571 508 571 508 571 508 571 508 571 508 571 508 571 1619 571 508 571 1618 571 508 571 508 571 1619 571 508 651 20153 571 1618 571 508 571 508 571 507 572 1618 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 1619 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 1619 571 508 571 1619 570 1619 650 40414 9150 4450 573 1617 572 507 572 508 571 1618 572 1618 571 1618 571 508 571 508 571 1619 570 1618 572 1618 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 509 570 508 571 1619 571 509 570 509 570 509 570 508 571 509 570 509 570 1619 570 1620 569 1620 570 509 570 509 570 1620 570 509 676 20154 570 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 509 570 508 571 508 571 508 571 508 571 509 570 509 570 509 570 509 570 1619 570 509 570 1620 569 509 570 509 570 509 570 509 570 509 570 1620 569 1621 569 1620 569 1620 570 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9102 4396 624 476 602 1587 602 478 600 1590 600 1591 599 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 481 599 481 598 1592 598 481 598 481 598 481 599 481 599 481 598 481 599 1592 598 481 598 1592 598 482 597 481 598 1592 598 481 678 20131 599 1592 598 481 598 481 598 481 598 1592 598 481 598 481 598 481 599 481 598 481 598 481 598 481 599 481 598 1592 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 482 597 481 598 481 598 481 598 481 598 481 598 481 598 482 597 1592 598 1592 598 1592 677 40398 9182 4421 599 480 599 1591 599 481 599 1592 598 1592 598 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 1592 598 481 599 481 598 482 597 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 482 597 1592 598 482 703 20131 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 599 481 598 481 598 1592 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1592 598 1592 598 +# +# Model: LG General +name: Off +type: parsed +protocol: NECext +address: 81 66 00 00 +command: 81 7E 00 00 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8725 4071 527 1527 501 521 501 517 500 520 500 1545 499 523 498 521 521 525 495 529 470 550 470 549 494 527 470 552 471 549 470 552 471 547 495 529 470 548 471 1572 470 1574 470 548 494 1551 470 547 470 552 470 551 470 1571 496 1547 469 1573 471 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8755 4064 528 1524 502 523 501 517 501 520 500 1543 500 519 498 524 497 523 497 527 496 521 496 525 521 500 496 527 496 1545 496 525 496 526 496 1545 495 1550 496 1541 496 1549 496 523 495 1546 496 526 496 523 521 499 496 1550 495 1544 495 1548 495 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8783 4042 556 1497 502 521 501 518 501 520 500 1546 499 517 499 523 497 525 496 527 522 496 497 523 497 524 496 528 522 1516 521 500 496 526 497 1542 520 501 496 527 496 522 497 524 496 1548 496 521 521 500 521 500 520 503 497 521 521 500 497 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8721 4070 527 1526 501 524 501 517 501 518 500 1544 500 519 498 524 497 526 496 550 471 549 470 549 470 551 494 531 470 548 470 549 470 552 470 1572 494 526 494 528 470 551 470 549 470 1573 470 551 470 548 495 1548 470 1572 469 551 494 527 495 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8780 4015 578 1498 553 471 526 494 525 494 524 1520 525 495 523 498 522 499 521 502 522 497 522 499 522 500 521 1519 522 500 521 499 521 1520 546 1496 521 503 523 494 522 500 521 499 522 1520 521 499 522 499 522 502 522 1518 521 501 521 1522 522 +# +# Model: Shivaki General +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3163 1577 577 1009 579 1008 579 342 485 342 485 342 485 1033 579 339 462 342 485 1033 553 1034 552 342 485 1035 550 342 484 343 483 1039 547 1040 546 341 486 1041 546 1041 545 341 486 341 486 1041 545 341 486 340 487 1041 545 341 486 341 486 340 487 340 488 340 487 340 487 341 486 341 486 340 487 340 487 341 486 340 487 341 487 341 486 341 486 341 486 340 487 341 486 340 487 340 487 1041 545 341 486 341 486 1042 545 1041 545 341 486 340 487 340 487 340 487 340 487 341 486 1042 544 341 486 1042 544 341 486 341 486 341 486 341 486 341 486 341 486 341 486 341 486 1042 544 1042 544 1042 544 1042 544 340 488 341 486 341 486 341 486 341 486 341 486 341 486 341 486 341 486 340 487 340 487 341 487 341 486 341 486 341 487 341 486 341 486 341 486 341 486 341 486 341 486 341 486 341 486 341 486 340 487 341 486 341 487 341 486 341 487 341 486 341 486 340 487 1043 543 1043 544 341 486 1043 543 341 486 1043 543 1044 542 340 487 340 488 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3193 1549 579 1007 580 1007 579 342 485 342 485 342 511 1007 579 338 463 342 485 1034 552 1034 551 342 484 1035 550 342 484 340 487 1039 547 1040 546 341 487 1041 545 1041 545 340 487 341 487 1041 545 341 486 340 487 1041 545 341 486 341 486 340 487 340 487 341 486 340 487 341 486 341 486 340 487 341 486 340 487 340 488 341 486 340 487 340 487 340 487 341 487 1041 545 340 487 340 487 1041 545 340 488 341 486 1042 544 1042 544 341 487 341 486 341 486 340 487 341 487 341 486 1042 544 1042 544 1042 544 1042 545 341 486 341 487 341 486 341 486 1042 545 341 486 1042 544 1042 544 1042 544 1042 544 341 487 341 486 340 487 341 486 341 486 340 487 341 486 340 487 341 486 341 486 341 486 341 486 341 487 341 486 341 486 341 486 340 488 341 486 341 486 341 486 341 486 341 486 340 488 341 486 341 486 341 487 341 486 341 486 341 486 1043 544 341 486 341 486 341 487 1043 543 341 486 341 486 341 486 341 486 1044 542 340 487 341 486 341 486 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3169 1547 579 1006 580 1006 580 342 485 342 485 342 484 1033 553 342 485 342 485 1033 552 1034 551 342 484 1035 550 342 484 342 485 1039 546 1040 546 342 485 1040 546 1040 546 342 485 343 484 1040 546 342 485 342 485 1040 546 342 486 342 485 343 484 342 485 343 484 342 485 342 485 342 485 343 484 342 485 342 485 342 485 343 484 342 485 342 485 342 485 342 485 1041 545 342 485 343 484 1041 545 342 485 343 484 1041 545 342 485 343 484 342 485 342 485 342 485 342 485 342 485 1041 545 342 485 343 484 342 485 342 485 342 485 343 484 342 485 1041 545 342 485 1041 545 1041 545 1041 545 1041 545 342 485 342 485 343 484 342 485 343 484 342 485 342 485 342 485 342 485 342 485 342 485 342 485 343 484 342 485 342 485 342 485 342 485 342 485 342 485 342 485 342 485 343 484 342 485 342 485 342 485 342 485 342 485 343 484 342 485 1042 544 343 484 343 484 342 485 1042 544 341 486 342 485 342 485 343 484 342 485 342 485 342 485 340 487 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3162 1605 523 1064 522 1064 523 343 484 343 484 343 484 1063 523 343 484 343 484 1063 523 1063 523 343 484 1063 523 343 484 343 484 1065 521 1066 520 343 484 1068 518 1068 518 343 484 343 484 1068 518 343 484 343 484 1068 518 343 484 343 484 343 484 343 484 343 484 342 485 343 484 343 485 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 1068 518 343 484 343 484 1069 517 343 484 343 485 1069 517 343 484 343 484 343 485 343 484 343 484 343 484 343 485 343 484 343 484 343 484 1070 516 344 483 344 483 344 484 343 484 1070 517 344 483 1094 493 1070 517 1093 493 1094 492 344 483 343 484 343 484 343 484 343 484 343 484 343 485 343 484 343 484 343 484 344 483 343 484 344 483 343 484 344 483 343 484 344 483 343 484 343 484 344 483 344 483 343 484 343 484 344 483 343 484 343 484 343 484 344 483 343 484 1094 492 343 484 344 484 343 484 1094 492 1094 492 1094 492 1094 492 343 484 344 483 343 484 343 484 344 483 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3138 1604 523 1063 523 1063 523 343 484 343 484 343 484 1063 523 343 484 343 484 1062 523 1063 523 343 484 1063 522 343 484 343 484 1065 521 1066 519 343 484 1067 519 1067 519 343 484 343 484 1067 519 343 484 343 484 1067 519 343 484 343 484 343 484 343 484 343 484 343 484 343 484 342 485 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 1068 518 343 484 343 484 1068 518 343 484 343 484 1068 518 1068 518 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 1067 519 343 484 343 484 343 484 343 484 1067 519 343 484 1067 519 1067 519 1068 518 1067 519 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 343 484 1068 518 343 484 343 484 343 484 1068 518 1068 518 343 484 343 484 1068 518 343 484 343 484 343 484 343 484 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3165 1578 549 1037 549 1037 550 342 485 343 484 343 513 1034 551 340 487 340 487 1035 551 1035 550 340 486 1037 548 341 485 341 485 1041 545 1041 545 341 486 1042 544 1042 544 340 487 341 486 1042 544 341 486 340 488 1042 544 341 487 341 486 341 486 341 461 343 509 341 487 341 461 343 484 343 510 341 486 341 461 343 484 343 509 341 462 343 509 341 462 343 484 1068 518 343 510 341 461 1068 518 343 484 343 484 343 484 1068 518 343 484 343 484 343 484 343 484 342 485 343 485 1068 518 1068 518 1068 518 343 485 343 484 343 485 343 484 343 484 343 484 343 484 343 484 1068 518 1068 518 1068 518 343 484 343 484 343 485 343 484 343 484 343 484 343 484 343 484 343 484 342 485 343 485 343 484 343 484 343 484 343 484 343 484 343 484 343 485 343 484 343 484 343 484 343 484 343 484 343 485 343 484 343 484 343 484 343 484 343 484 1070 516 343 484 343 484 343 484 1071 515 343 484 1094 492 343 484 343 484 343 485 344 483 343 484 343 484 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index ab608cf1b..fa1e0c67a 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,6 +1,6 @@ Filetype: IR library file Version: 1 -# Last Updated 17th May, 2023 +# Last Updated 9 Jun, 2023 # Last Checked 17th May, 2023 # name: Power @@ -2047,3 +2047,41 @@ type: parsed protocol: Samsung32 address: 3E 00 00 00 command: 0D 00 00 00 +# +# TCL LED49D2930 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3994 3969 552 1945 551 1945 552 1945 551 1945 552 946 551 947 550 1947 548 951 546 1953 542 979 518 1979 517 981 492 1006 491 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 2005 492 1006 492 2005 492 1006 492 2006 491 9051 3963 3999 491 2006 491 2006 491 2006 491 2006 491 1007 491 1007 491 2006 491 1007 491 2006 491 1007 491 2006 491 1007 491 1007 491 1007 491 1006 492 1007 491 2006 491 2006 491 1007 491 2006 491 1007 491 2006 491 1007 491 2006 491 9052 3962 4000 491 2006 491 2006 491 2006 491 2006 491 1007 491 1007 491 2006 491 1007 491 2006 491 1007 491 2006 491 1007 491 1007 491 1007 491 1007 491 1007 491 2006 491 2007 490 1007 491 2006 491 1007 491 2006 491 1007 491 2006 490 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4055 3941 605 1891 551 1947 550 1946 551 1946 551 947 551 947 550 1947 549 949 548 1951 545 1977 519 1978 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 981 517 1979 518 980 518 980 518 980 518 980 518 9026 3989 3975 517 1980 517 1980 517 1979 518 1980 517 980 518 980 518 1979 518 981 517 1980 517 1980 517 1980 517 1980 517 980 518 981 517 981 517 981 517 1980 517 1980 517 981 517 1980 517 981 517 981 517 981 517 981 517 9027 3988 3975 517 1980 517 1980 517 1980 517 1980 516 981 517 982 516 1981 516 982 516 1981 516 1981 516 1981 516 1981 516 982 516 982 515 982 516 982 516 1982 515 1981 516 982 515 1981 516 983 515 982 516 982 515 982 516 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4027 3970 551 1946 550 1946 551 1946 551 1946 551 946 551 947 550 1947 549 949 547 1951 545 1978 518 1979 492 1006 492 1007 491 1006 492 1006 492 1006 492 2006 491 2006 491 1006 492 2006 491 1007 491 1007 491 1006 492 2006 491 9053 3964 4000 491 2006 491 2006 491 2006 491 2006 491 1007 491 1007 491 2006 491 1007 491 2006 491 2006 491 2006 491 1007 491 1007 491 1007 491 1007 491 1007 491 2006 491 2006 491 1007 491 2006 491 1007 491 1007 491 1007 491 2006 491 9053 3963 4001 491 2006 491 2006 491 2007 490 2007 490 1007 491 1007 491 2007 490 1007 491 2006 491 2006 491 2007 490 1008 490 1007 491 1008 490 1007 491 1008 490 2007 490 2007 490 1008 490 2007 490 1008 490 1008 489 1008 490 2007 490 +# +name: Ch_next +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4021 3941 551 1946 550 1946 551 1946 551 1945 552 946 551 947 550 1947 549 950 547 1952 544 1977 519 979 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 980 518 9026 3990 3974 517 1980 517 1980 517 1980 517 1980 517 981 517 981 517 1980 517 981 517 1979 518 1979 518 980 517 1980 517 981 517 981 517 981 517 980 518 1980 517 1980 517 980 517 1980 517 981 516 981 517 1980 517 981 516 +# +name: Ch_prev +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4022 3941 551 1946 551 1946 577 1919 578 1919 578 920 552 946 551 1946 550 947 550 1949 547 1952 544 978 520 979 519 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 1980 517 9027 3990 3974 517 1980 517 1980 517 1980 517 1980 517 981 517 981 517 1980 517 981 517 1980 517 1980 517 981 517 981 517 981 517 981 517 981 517 981 517 1980 517 1980 517 981 517 1980 517 981 517 982 491 2006 516 1981 516 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3995 3968 552 1944 552 1946 550 1946 550 1946 551 947 550 948 549 1947 549 1949 547 1952 544 1978 518 1979 492 2005 492 1006 492 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 1006 492 1006 492 1006 492 1006 492 1006 492 9051 3963 4000 491 2005 492 2006 491 2006 491 2006 491 1007 491 1007 491 2006 516 1981 516 1980 491 2006 517 1980 517 1980 517 981 517 981 517 981 517 981 517 1980 517 1981 516 981 517 981 517 981 517 981 517 981 491 1007 491 9052 3988 3975 491 2006 491 2006 491 2006 514 1983 490 1007 491 1007 491 2006 491 2006 491 2006 491 2006 491 2006 491 2006 512 986 491 1007 491 1007 491 1007 491 2006 491 2006 491 1007 491 1007 490 1007 491 1007 491 1008 490 1007 491 From 75859e9a7eeb2fcd1c1bd0f15e931119dfdd1071 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 10 Jun 2023 04:45:11 +0300 Subject: [PATCH 128/370] Update changelog --- CHANGELOG.md | 61 ++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f81527b72..0534ce4fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,38 +1,37 @@ ### New changes * If you have copied any apps manually into `apps` folder - remove `apps` folder or that specific apps you copied on your microSD before installing this release to avoid issues due to OFW API version update! If you using regular builds or extra pack builds (e) without your manually added apps, all included apps will be installed automatically, no extra actions needed! +* Settings->LCD and Notifications will be resetted to default due to new Contrast setting from OFW ----- -* Only in release 052 -> **Multiple Extra pack apps was fixed!** -> TAMA P1, Flizzer Tracker, Video Player, Music Tracker -* NFC V: Remove delay from emulation loop. This improves compatibility when the reader is Android. -* Plugins: iButton Fuzzer -> Fix v2 key files load (all new saved files) -### Previous changes -* SubGHz Remote: Fixed BinRAW support, + many other fixes (by @gid9798 | PR #492) -* SubGHz: Fix KL: Stilmatic support + add manually support -* SubGHz: Keeloq mfname refactoring (by @gid9798 | PR #479) -* Desktop Clock: Some improvements and fixes (by @gid9798 | PR #490) -* LF RFID: Cleanup duplicated code (by @gid9798 | PR #493) -* NFC V: Code review fixes + some GUI rework (by @nvx & @xMasterX) -* NFC V: Fixed crash when exiting emulation and starting it again -* Infrared: Use Universal AC Remote from OFW, same for Audio remote, and rename buttons in OFW naming scheme -* Infrared: Update universal remote assets (by @amec0e) -* GUI Keyboard: Fix crash when renaming files with long file name (Fixed issue #489) -* Misc: Fix APP_IDs to match new regex (regex check will be added in OFW soon) -* Plugins: Protoview, WAV Player, DTMF Dolphin - fixed all known crashes, ported to latest hal bus system -* Plugins: ESP8266 Deauther - Crash fix (Fixed issue #497) -* Plugins: Update -> Mifare Nested [(by AloneLiberty)](https://github.com/AloneLiberty/FlipperNested) -* Plugins: Update -> TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) -* Plugins: Update -> ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-wifi-marauder) -* Plugins: Update -> UART Terminal [(by cool4uma)](https://github.com/cool4uma/UART_Terminal/tree/main) -* OFW: FuriHal: disable bus re-initialization on early init and extra asserts for AHB1,AHB2,AHB3 which must be left intact on entering to FUS -> **Fixes updater error 1-100** -* OFW: NFC: fix MFC timings -> **Fixes issues with Mifare Classic emulation that could happen after unlshd-049 release** -* OFW: Update dolphin.py -* OFW: NFC Magic: Fix gen1 writing with invalid BCC (lost fix from PR 2511) -* OFW: SubGhz: fix flipper crashes after exiting broadcast blocking message and crash cli +* Plugins: **New RFID 125KHz and iButton Fuzzers (remake from scratch + new features)** (by @gid9798 | PR #507) +* Plugins: SubGHz Bruteforcer -> Time delay (between signals) setting (hold Up in main screen(says Up to Save)) + allow more repeats (by @gid9798 & @xMasterX) +* GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509) +* Infrared: Update universal remote assets - add new ACs and TCL TV +* SubGHz: Remove broken modulation that was causing buffer overrun (fixes issue #506) +* SubGHz: Notifications fixes (by @wosk | PR #464) +* Plugins: Unitemp SCD30 support (PR in unitemp repo by @divinebird / fixed by @xMasterX) +* Plugins: Fix ProtoView issue #503 -> (Broken saved files with custom modulation) +* API: Add furi_hal_version_uid_default (+ Fix TOTP) (by @ClaraCrazy | PR #502) +* OFW: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys +* OFW: fbt: stable build dates +* OFW: weather_station: add oregon3 with THGR221 +* OFW: Services: simplify api (DOLPHIN_DEED->dolphin_deed - function instead of macros + remake all apps in extra pack and main fw to use new API) +* OFW: Core2, SRAM2: provide safety gap +* OFW: FuriHal: always clock SMPS from HSI +* OFW: ble: refactored bt gatt characteristics setup (+ remake of BT HID Led descriptor in new way to work with this changes) +* OFW: Scripts: WiFi board updater +* OFW: github: re-enabled f18 build +* OFW: added ISO15693 (NfcV) (was already added before, so we just updated it with latest changes) +* OFW: fbt: added Flipper selection when multiple are connected over USB +* OFW: fbt, ufbt: added checks for appid in app manifests +* OFW: Fix core2 permisions +* OFW: SubGhz: add subghz_protocol_registry external API (was already in our API but in different way) +* OFW: Furi: smaller critical enter and critical exit macro +* OFW: Serial_CLI: Fixing serial cli logger error so it sounds more concise +* OFW: Remove unused resources * OFW: Dolphin: new animation -* OFW: fbt: added hooks for build & dist environments; added FW_ORIGIN_* macro for apps & SDK -* OFW: FuriHal: add bus abstraction -> **Breaking API change, API version was changed from 27.x to 28.x** -* OFW: Implement support for reading Opal card (Sydney, Australia) -* OFW: BadUSB: script execution pause -* OFW: IR Universal AC: Add Carrier 42QHB12D8S +* OFW: f7: add PB9 to debug pins +* OFW: Settings: add contrast adjustment -> **Settings->LCD and Notifications will be resetted to default values one time after installing** +* OFW: FuriHal: add system setting to device info, bump device info version #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) From d87745f2bee5100d345dcaf0aa75436548a8eb28 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 10 Jun 2023 05:22:34 +0300 Subject: [PATCH 129/370] Update TOTP --- CHANGELOG.md | 9 +-- applications/external/totp/features_config.h | 5 ++ .../external/totp/services/crypto/crypto.c | 11 ++-- .../totp/services/idle_timeout/idle_timeout.c | 66 +++++++++++++++++++ .../totp/services/idle_timeout/idle_timeout.h | 44 +++++++++++++ applications/external/totp/totp_app.c | 38 +++++++---- .../external/totp/types/plugin_state.h | 9 +++ 7 files changed, 163 insertions(+), 19 deletions(-) create mode 100644 applications/external/totp/services/idle_timeout/idle_timeout.c create mode 100644 applications/external/totp/services/idle_timeout/idle_timeout.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 0534ce4fb..cd2eb93c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,13 @@ ----- * Plugins: **New RFID 125KHz and iButton Fuzzers (remake from scratch + new features)** (by @gid9798 | PR #507) * Plugins: SubGHz Bruteforcer -> Time delay (between signals) setting (hold Up in main screen(says Up to Save)) + allow more repeats (by @gid9798 & @xMasterX) -* GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509) -* Infrared: Update universal remote assets - add new ACs and TCL TV -* SubGHz: Remove broken modulation that was causing buffer overrun (fixes issue #506) -* SubGHz: Notifications fixes (by @wosk | PR #464) +* Plugins: Update TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) * Plugins: Unitemp SCD30 support (PR in unitemp repo by @divinebird / fixed by @xMasterX) * Plugins: Fix ProtoView issue #503 -> (Broken saved files with custom modulation) +* SubGHz: Remove broken modulation that was causing buffer overrun (fixes issue #506) +* SubGHz: Notifications fixes (by @wosk | PR #464) +* GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509) +* Infrared: Update universal remote assets - add new ACs and TCL TV * API: Add furi_hal_version_uid_default (+ Fix TOTP) (by @ClaraCrazy | PR #502) * OFW: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys * OFW: fbt: stable build dates diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h index 15e81b28a..59a1e0ecc 100644 --- a/applications/external/totp/features_config.h +++ b/applications/external/totp/features_config.h @@ -1,3 +1,8 @@ +// Application automatic lock timeout if user IDLE. (ticks) +#ifndef TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC +#define TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC (60) +#endif + // Include Bluetooth token input automation #define TOTP_BADBT_TYPE_ENABLED diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 448529100..8c20fe785 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -6,10 +6,11 @@ #include "memset_s.h" #define CRYPTO_KEY_SLOT (2) -#define CRYPTO_VERIFY_KEY "FFF_Crypto_pass" #define CRYPTO_VERIFY_KEY_LENGTH (16) #define CRYPTO_ALIGNMENT_FACTOR (16) +static const char* CRYPTO_VERIFY_KEY = "FFF_Crypto_pass"; + uint8_t* totp_crypto_encrypt( const uint8_t* plain_data, const size_t plain_data_length, @@ -107,7 +108,7 @@ CryptoSeedIVResult plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; plugin_state->crypto_verify_data = totp_crypto_encrypt( - (uint8_t*)CRYPTO_VERIFY_KEY, + (const uint8_t*)CRYPTO_VERIFY_KEY, CRYPTO_VERIFY_KEY_LENGTH, &plugin_state->iv[0], &plugin_state->crypto_verify_data_length); @@ -122,7 +123,7 @@ CryptoSeedIVResult bool totp_crypto_verify_key(const PluginState* plugin_state) { size_t decrypted_key_length; - const uint8_t* decrypted_key = totp_crypto_decrypt( + uint8_t* decrypted_key = totp_crypto_decrypt( plugin_state->crypto_verify_data, plugin_state->crypto_verify_data_length, &plugin_state->iv[0], @@ -133,5 +134,7 @@ bool totp_crypto_verify_key(const PluginState* plugin_state) { if(decrypted_key[i] != CRYPTO_VERIFY_KEY[i]) key_valid = false; } + free(decrypted_key); + return key_valid; -} \ No newline at end of file +} diff --git a/applications/external/totp/services/idle_timeout/idle_timeout.c b/applications/external/totp/services/idle_timeout/idle_timeout.c new file mode 100644 index 000000000..9df1b5487 --- /dev/null +++ b/applications/external/totp/services/idle_timeout/idle_timeout.c @@ -0,0 +1,66 @@ +#include "idle_timeout.h" +#include +#include + +#define IDLE_TIMER_CHECK_PERIODICITY_SEC (1) +#define SEC_TO_TICKS(sec) ((sec)*1000) + +struct IdleTimeoutContext { + FuriTimer* timer; + bool activity_reported; + void* on_idle_callback_context; + IDLE_TIMEOUT_CALLBACK on_idle_callback; + uint16_t timeout_sec; + uint16_t idle_period_sec; + bool idle_handled; +}; + +static void idle_timer_callback(void* context) { + IdleTimeoutContext* instance = context; + if(instance->activity_reported) { + instance->idle_period_sec = 0; + instance->idle_handled = false; + instance->activity_reported = false; + } else if(!instance->idle_handled) { + if(instance->idle_period_sec >= instance->timeout_sec) { + instance->idle_handled = + instance->on_idle_callback(instance->on_idle_callback_context); + } else { + instance->idle_period_sec += IDLE_TIMER_CHECK_PERIODICITY_SEC; + } + } +} + +IdleTimeoutContext* idle_timeout_alloc( + uint16_t timeout_sec, + IDLE_TIMEOUT_CALLBACK on_idle_callback, + void* on_idle_callback_context) { + IdleTimeoutContext* instance = malloc(sizeof(IdleTimeoutContext)); + if(instance == NULL) return NULL; + + instance->timer = furi_timer_alloc(&idle_timer_callback, FuriTimerTypePeriodic, instance); + if(instance->timer == NULL) return NULL; + + instance->timeout_sec = timeout_sec; + instance->on_idle_callback = on_idle_callback; + instance->on_idle_callback_context = on_idle_callback_context; + return instance; +} + +void idle_timeout_start(IdleTimeoutContext* context) { + furi_timer_start(context->timer, SEC_TO_TICKS(IDLE_TIMER_CHECK_PERIODICITY_SEC)); +} + +void idle_timeout_stop(IdleTimeoutContext* context) { + furi_timer_stop(context->timer); +} + +void idle_timeout_report_activity(IdleTimeoutContext* context) { + context->activity_reported = true; +} + +void idle_timeout_free(IdleTimeoutContext* context) { + furi_timer_stop(context->timer); + furi_timer_free(context->timer); + free(context); +} diff --git a/applications/external/totp/services/idle_timeout/idle_timeout.h b/applications/external/totp/services/idle_timeout/idle_timeout.h new file mode 100644 index 000000000..12825e454 --- /dev/null +++ b/applications/external/totp/services/idle_timeout/idle_timeout.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +typedef struct IdleTimeoutContext IdleTimeoutContext; + +typedef bool (*IDLE_TIMEOUT_CALLBACK)(void* context); + +/** + * @brief Initializes a new instance of IDLE timeout + * @param timeout_sec IDLE timeout in seconds + * @param on_idle_callback callback function to trigger when IDLE timeout happened + * @param on_idle_callback_context callback function context + * @return IDLE timeout context + */ +IdleTimeoutContext* idle_timeout_alloc( + uint16_t timeout_sec, + IDLE_TIMEOUT_CALLBACK on_idle_callback, + void* on_idle_callback_context); + +/** + * @brief Starts IDLE timeout + * @param context IDLE timeout context + */ +void idle_timeout_start(IdleTimeoutContext* context); + +/** + * @brief Stops IDLE timeout + * @param context IDLE timeout context + */ +void idle_timeout_stop(IdleTimeoutContext* context); + +/** + * @brief Reports activity to IDLE timeout + * @param context IDLE timeout context + */ +void idle_timeout_report_activity(IdleTimeoutContext* context); + +/** + * @brief Disposes IDLE timeout and releases all the resources + * @param context IDLE timeout context + */ +void idle_timeout_free(IdleTimeoutContext* context); diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 57e5ff04f..642e27c61 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -18,8 +18,6 @@ #include "services/crypto/crypto.h" #include "cli/cli.h" -#define IDLE_TIMEOUT (60000) - static void render_callback(Canvas* const canvas, void* ctx) { furi_assert(ctx); PluginState* plugin_state = ctx; @@ -106,6 +104,17 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) { return true; } +static bool on_user_idle(void* context) { + PluginState* plugin_state = context; + if(plugin_state->current_scene != TotpSceneAuthentication && + plugin_state->current_scene != TotpSceneStandby) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); + return true; + } + + return false; +} + static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->selected_font = 0; plugin_state->gui = furi_record_open(RECORD_GUI); @@ -128,10 +137,23 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { } #endif + if(plugin_state->pin_set) { + plugin_state->idle_timeout_context = + idle_timeout_alloc(TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC, &on_user_idle, plugin_state); + idle_timeout_start(plugin_state->idle_timeout_context); + } else { + plugin_state->idle_timeout_context = NULL; + } + return true; } static void totp_plugin_state_free(PluginState* plugin_state) { + if(plugin_state->idle_timeout_context != NULL) { + idle_timeout_stop(plugin_state->idle_timeout_context); + idle_timeout_free(plugin_state->idle_timeout_context); + } + furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); @@ -185,14 +207,13 @@ int32_t totp_app() { PluginEvent event; bool processing = true; - uint32_t last_user_interaction_time = furi_get_tick(); while(processing) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) == FuriStatusOk) { if(event_status == FuriStatusOk) { - if(event.type == EventTypeKey) { - last_user_interaction_time = furi_get_tick(); + if(event.type == EventTypeKey && plugin_state->idle_timeout_context != NULL) { + idle_timeout_report_activity(plugin_state->idle_timeout_context); } if(event.type == EventForceCloseApp) { @@ -200,11 +221,6 @@ int32_t totp_app() { } else { processing = totp_scene_director_handle_event(&event, plugin_state); } - } else if( - plugin_state->pin_set && plugin_state->current_scene != TotpSceneAuthentication && - plugin_state->current_scene != TotpSceneStandby && - furi_get_tick() - last_user_interaction_time > IDLE_TIMEOUT) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } view_port_update(view_port); diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index 97d126330..234894d58 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -6,6 +6,7 @@ #include "../features_config.h" #include "../ui/totp_scenes_enum.h" #include "../services/config/config_file_context.h" +#include "../services/idle_timeout/idle_timeout.h" #include "notification_method.h" #include "automation_method.h" #ifdef TOTP_BADBT_TYPE_ENABLED @@ -48,6 +49,9 @@ typedef struct { */ float timezone_offset; + /** + * @brief Config file context + */ ConfigFileContext* config_file_context; /** @@ -101,4 +105,9 @@ typedef struct { */ TotpBtTypeCodeWorkerContext* bt_type_code_worker_context; #endif + + /** + * @brief IDLE timeout context + */ + IdleTimeoutContext* idle_timeout_context; } PluginState; From f7195adbda9ed48c8aaed85e327cc12de143ee55 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 10 Jun 2023 06:01:23 +0300 Subject: [PATCH 130/370] OFW PR 2756 --- CHANGELOG.md | 1 + applications/services/dialogs/dialogs.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd2eb93c9..cc5af4149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509) * Infrared: Update universal remote assets - add new ACs and TCL TV * API: Add furi_hal_version_uid_default (+ Fix TOTP) (by @ClaraCrazy | PR #502) +* OFW PR 2756: fix: make dialog_file_browser_set_basic_options initialize all fields (by JarvisCraft) * OFW: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys * OFW: fbt: stable build dates * OFW: weather_station: add oregon3 with THGR221 diff --git a/applications/services/dialogs/dialogs.c b/applications/services/dialogs/dialogs.c index 3908ca31b..10c08a991 100644 --- a/applications/services/dialogs/dialogs.c +++ b/applications/services/dialogs/dialogs.c @@ -9,12 +9,13 @@ void dialog_file_browser_set_basic_options( const char* extension, const Icon* icon) { options->extension = extension; + options->base_path = NULL; options->skip_assets = true; + options->hide_dot_files = true; options->icon = icon; options->hide_ext = true; options->item_loader_callback = NULL; options->item_loader_context = NULL; - options->base_path = NULL; } static DialogsApp* dialogs_app_alloc() { From 26e38bc7925e751dfd97003e7ce6d4142407cdc2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 10 Jun 2023 06:02:56 +0300 Subject: [PATCH 131/370] Upd readme --- ReadMe.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReadMe.md b/ReadMe.md index 0c545682c..6256d5905 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -57,6 +57,7 @@ Our Discord Community: * Sub-GHz -> Short press OK in frequency analyzer to save detected frequency for usage in Read modes * Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu and automatically use selected frequency * SubGHz -> New option to use timestamps + protocol name when you saving file, instead of random name - Enable in `Radio Settings -> Time in names = ON` +* SubGHz Bruteforcer plugin -> Time delay (between signals) setting (hold Up in main screen(says Up to Save)) + configure repeats in protocols list by pressing right button on selected protocol * SubGHz -> Read mode UI improvements (scrolling text, + shows time when signal was received) (by @wosk) * Sub-GHz -> External CC1101 module support (Hardware SPI used) * SubGHz -> **Hold right in received signal list to delete selected signal** From 78119a519acd8a03516201339c76c0ef9a3a939a Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Thu, 22 Dec 2022 09:37:37 +1000 Subject: [PATCH 132/370] add picopass emulation --- .../picopass/lib/loclass/optimized_cipher.c | 17 +- .../picopass/lib/loclass/optimized_cipher.h | 17 +- .../picopass/lib/loclass/optimized_elite.c | 6 +- .../picopass/lib/loclass/optimized_elite.h | 2 +- .../external/picopass/loclass_writer.c | 100 ++++ .../external/picopass/loclass_writer.h | 20 + applications/external/picopass/picopass.c | 18 + .../external/picopass/picopass_device.h | 60 ++ applications/external/picopass/picopass_i.h | 9 + .../external/picopass/picopass_worker.c | 516 +++++++++++++++++- .../external/picopass/picopass_worker.h | 4 + .../external/picopass/picopass_worker_i.h | 2 + .../external/picopass/rfal_picopass.c | 3 +- .../external/picopass/rfal_picopass.h | 2 + .../picopass/scenes/picopass_scene_config.h | 2 + .../picopass/scenes/picopass_scene_emulate.c | 58 ++ .../picopass/scenes/picopass_scene_loclass.c | 80 +++ .../scenes/picopass_scene_saved_menu.c | 10 + .../picopass/scenes/picopass_scene_start.c | 9 + .../external/picopass/views/loclass.c | 106 ++++ .../external/picopass/views/loclass.h | 22 + 21 files changed, 1054 insertions(+), 9 deletions(-) create mode 100644 applications/external/picopass/loclass_writer.c create mode 100644 applications/external/picopass/loclass_writer.h create mode 100644 applications/external/picopass/scenes/picopass_scene_emulate.c create mode 100644 applications/external/picopass/scenes/picopass_scene_loclass.c create mode 100644 applications/external/picopass/views/loclass.c create mode 100644 applications/external/picopass/views/loclass.h diff --git a/applications/external/picopass/lib/loclass/optimized_cipher.c b/applications/external/picopass/lib/loclass/optimized_cipher.c index 94df07bae..01d48817d 100644 --- a/applications/external/picopass/lib/loclass/optimized_cipher.c +++ b/applications/external/picopass/lib/loclass/optimized_cipher.c @@ -280,7 +280,22 @@ void loclass_opt_doTagMAC_2( loclass_opt_output(div_key_p, &_init, mac); } -void loclass_iclass_calc_div_key(uint8_t* csn, uint8_t* key, uint8_t* div_key, bool elite) { +void loclass_opt_doBothMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t rmac[4], + uint8_t tmac[4], + const uint8_t* div_key_p) { + loclass_opt_suc(div_key_p, &_init, nr, 4, false); + // Save internal state for reuse before outputting + LoclassState_t nr_state = _init; + loclass_opt_output(div_key_p, &_init, rmac); + // Feed the 32 0 bits for the tag mac + loclass_opt_suc(div_key_p, &nr_state, NULL, 0, true); + loclass_opt_output(div_key_p, &nr_state, tmac); +} + +void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite) { if(elite) { uint8_t keytable[128] = {0}; uint8_t key_index[8] = {0}; diff --git a/applications/external/picopass/lib/loclass/optimized_cipher.h b/applications/external/picopass/lib/loclass/optimized_cipher.h index 2158f0acf..c96c97d8a 100644 --- a/applications/external/picopass/lib/loclass/optimized_cipher.h +++ b/applications/external/picopass/lib/loclass/optimized_cipher.h @@ -93,6 +93,21 @@ void loclass_opt_doTagMAC_2( uint8_t mac[4], const uint8_t* div_key_p); +/** + * The same as loclass_opt_doTagMAC_2, but calculates both the reader and tag MACs at the same time + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param rmac - where to store the reader MAC + * @param tmac - where to store the tag MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doBothMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t rmac[4], + uint8_t tmac[4], + const uint8_t* div_key_p); + void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]); -void loclass_iclass_calc_div_key(uint8_t* csn, uint8_t* key, uint8_t* div_key, bool elite); +void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite); #endif // OPTIMIZED_CIPHER_H diff --git a/applications/external/picopass/lib/loclass/optimized_elite.c b/applications/external/picopass/lib/loclass/optimized_elite.c index 34e987060..e198a410b 100644 --- a/applications/external/picopass/lib/loclass/optimized_elite.c +++ b/applications/external/picopass/lib/loclass/optimized_elite.c @@ -153,7 +153,7 @@ Definition 14. Define the rotate key function loclass_rk : (F 82 ) 8 × N → (F loclass_rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] loclass_rk(x [0] . . . x [7] , n + 1) = loclass_rk(loclass_rl(x [0] ) . . . loclass_rl(x [7] ), n) **/ -static void loclass_rk(uint8_t* key, uint8_t n, uint8_t* outp_key) { +static void loclass_rk(const uint8_t* key, uint8_t n, uint8_t* outp_key) { memcpy(outp_key, key, 8); uint8_t j; while(n-- > 0) { @@ -172,7 +172,7 @@ static void loclass_desdecrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8 mbedtls_des_crypt_ecb(&loclass_ctx_dec, input, output); } -static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8_t* output) { +static void loclass_desencrypt_iclass(const uint8_t* iclass_key, uint8_t* input, uint8_t* output) { uint8_t key_std_format[8] = {0}; loclass_permutekey_rev(iclass_key, key_std_format); mbedtls_des_setkey_enc(&loclass_ctx_enc, key_std_format); @@ -185,7 +185,7 @@ static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8 * @param loclass_hash1 loclass_hash1 * @param key_sel output key_sel=h[loclass_hash1[i]] */ -void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) { +void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable) { /** *Expected: * High Security Key Table diff --git a/applications/external/picopass/lib/loclass/optimized_elite.h b/applications/external/picopass/lib/loclass/optimized_elite.h index 5343ebb07..fba512a86 100644 --- a/applications/external/picopass/lib/loclass/optimized_elite.h +++ b/applications/external/picopass/lib/loclass/optimized_elite.h @@ -53,6 +53,6 @@ void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]); * @param k output */ void loclass_hash1(const uint8_t* csn, uint8_t* k); -void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable); +void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable); #endif diff --git a/applications/external/picopass/loclass_writer.c b/applications/external/picopass/loclass_writer.c new file mode 100644 index 000000000..273fa67eb --- /dev/null +++ b/applications/external/picopass/loclass_writer.c @@ -0,0 +1,100 @@ +#include "loclass_writer.h" + +#include +#include +#include +#include +#include + +struct LoclassWriter { + Stream* file_stream; +}; + +#define LOCLASS_LOGS_PATH EXT_PATH("apps_data/picopass/.loclass.log") + +LoclassWriter* loclass_writer_alloc() { + LoclassWriter* instance = malloc(sizeof(LoclassWriter)); + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->file_stream = buffered_file_stream_alloc(storage); + if(!buffered_file_stream_open( + instance->file_stream, LOCLASS_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) { + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + free(instance); + instance = NULL; + } + + furi_record_close(RECORD_STORAGE); + + return instance; +} + +void loclass_writer_free(LoclassWriter* instance) { + furi_assert(instance != NULL); + + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + free(instance); +} + +bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start) { + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + FuriString* str = furi_string_alloc_printf( + "loclass-v1-info ts %lu %s\n", curr_ts, start ? "started" : "finished"); + bool write_success = stream_write_string(instance->file_stream, str); + furi_string_free(str); + return write_success; +} + +bool loclass_writer_write_params( + LoclassWriter* instance, + uint8_t log_no, + const uint8_t csn[8], + const uint8_t epurse[8], + const uint8_t nr[4], + const uint8_t mac[4]) { + furi_assert(instance != NULL); + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + FuriString* str = furi_string_alloc_printf( + "loclass-v1-mac ts %lu no %u " + "csn %02x%02x%02x%02x%02x%02x%02x%02x " + "cc %02x%02x%02x%02x%02x%02x%02x%02x " + "nr %02x%02x%02x%02x " + "mac %02x%02x%02x%02x\n", + curr_ts, + log_no, + csn[0], + csn[1], + csn[2], + csn[3], + csn[4], + csn[5], + csn[6], + csn[7], + epurse[0], + epurse[1], + epurse[2], + epurse[3], + epurse[4], + epurse[5], + epurse[6], + epurse[7], + nr[0], + nr[1], + nr[2], + nr[3], + mac[0], + mac[1], + mac[2], + mac[3]); + bool write_success = stream_write_string(instance->file_stream, str); + furi_string_free(str); + return write_success; +} \ No newline at end of file diff --git a/applications/external/picopass/loclass_writer.h b/applications/external/picopass/loclass_writer.h new file mode 100644 index 000000000..dd7a4560c --- /dev/null +++ b/applications/external/picopass/loclass_writer.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +typedef struct LoclassWriter LoclassWriter; + +LoclassWriter* loclass_writer_alloc(); + +void loclass_writer_free(LoclassWriter* instance); + +bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start); + +bool loclass_writer_write_params( + LoclassWriter* instance, + uint8_t log_no, + const uint8_t csn[8], + const uint8_t epurse[8], + const uint8_t nr[4], + const uint8_t mac[4]); diff --git a/applications/external/picopass/picopass.c b/applications/external/picopass/picopass.c index 6737d8077..04339a602 100644 --- a/applications/external/picopass/picopass.c +++ b/applications/external/picopass/picopass.c @@ -79,6 +79,10 @@ Picopass* picopass_alloc() { PicopassViewDictAttack, dict_attack_get_view(picopass->dict_attack)); + picopass->loclass = loclass_alloc(); + view_dispatcher_add_view( + picopass->view_dispatcher, PicopassViewLoclass, loclass_get_view(picopass->loclass)); + return picopass; } @@ -112,6 +116,9 @@ void picopass_free(Picopass* picopass) { view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack); dict_attack_free(picopass->dict_attack); + view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewLoclass); + loclass_free(picopass->loclass); + // Worker picopass_worker_stop(picopass->worker); picopass_worker_free(picopass->worker); @@ -153,6 +160,13 @@ static const NotificationSequence picopass_sequence_blink_start_cyan = { NULL, }; +static const NotificationSequence picopass_sequence_blink_start_magenta = { + &message_blink_start_10, + &message_blink_set_color_magenta, + &message_do_not_reset, + NULL, +}; + static const NotificationSequence picopass_sequence_blink_stop = { &message_blink_stop, NULL, @@ -162,6 +176,10 @@ void picopass_blink_start(Picopass* picopass) { notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan); } +void picopass_blink_emulate_start(Picopass* picopass) { + notification_message(picopass->notifications, &picopass_sequence_blink_start_magenta); +} + void picopass_blink_stop(Picopass* picopass) { notification_message(picopass->notifications, &picopass_sequence_blink_stop); } diff --git a/applications/external/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h index 7fc35ebda..04c0c6aab 100644 --- a/applications/external/picopass/picopass_device.h +++ b/applications/external/picopass/picopass_device.h @@ -7,6 +7,7 @@ #include #include "rfal_picopass.h" +#include "loclass_writer.h" #include #include #include "helpers/iclass_elite_dict.h" @@ -24,6 +25,49 @@ #define PICOPASS_AIA_BLOCK_INDEX 5 #define PICOPASS_PACS_CFG_BLOCK_INDEX 6 +// Personalization Mode +#define PICOPASS_FUSE_PERS 0x80 +// Crypt1 // 1+1 (crypt1+crypt0) means secured and keys changable +#define PICOPASS_FUSE_CRYPT1 0x10 +// Crypt0 // 1+0 means secure and keys locked, 0+1 means not secured, 0+0 means disable auth entirely +#define PICOPASS_FUSE_CRTPT0 0x08 +#define PICOPASS_FUSE_CRYPT10 (PICOPASS_FUSE_CRYPT1 | PICOPASS_FUSE_CRTPT0) +// Read Access, 1 meanns anonymous read enabled, 0 means must auth to read applicaion +#define PICOPASS_FUSE_RA 0x01 + +// PicoPass command bytes: +// Low nibble used for command +// High nibble used for options and checksum (MSB) +// The only option we care about in 15693 mode is the key +// which is only used by READCHECK, so for simplicity we +// don't bother breaking down the command and flags into parts +// READ: ADDRESS(1) CRC16(2) -> DATA(8) CRC16(2) +// IDENTIFY: No args -> ASNB(8) CRC16(2) +#define PICOPASS_CMD_READ_OR_IDENTIFY 0x0C +// ADDRESS(1) CRC16(2) -> DATA(32) CRC16(2) +#define PICOPASS_CMD_READ4 0x06 +// ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) -> DATA(8) CRC16(2) +#define PICOPASS_CMD_UPDATE 0x87 +// ADDRESS(1) -> DATA(8) +#define PICOPASS_CMD_READCHECK_KD 0x88 +// ADDRESS(1) -> DATA(8) +#define PICOPASS_CMD_READCHECK_KC 0x18 +// CHALLENGE(4) READERSIGNATURE(4) -> CHIPRESPONSE(4) +#define PICOPASS_CMD_CHECK 0x05 +// No args -> SOF +#define PICOPASS_CMD_ACTALL 0x0A +// No args -> SOF +#define PICOPASS_CMD_ACT 0x8E +// ASNB(8)|SERIALNB(8) -> SERIALNB(8) CRC16(2) +#define PICOPASS_CMD_SELECT 0x81 +// No args -> SERIALNB(8) CRC16(2) +#define PICOPASS_CMD_DETECT 0x0F +// No args -> SOF +#define PICOPASS_CMD_HALT 0x00 +// PAGE(1) CRC16(2) -> BLOCK1(8) CRC16(2) +#define PICOPASS_CMD_PAGESEL 0x84 + +#define PICOPASS_APP_FOLDER ANY_PATH("picopass") #define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_SHADOW_EXTENSION ".pas" @@ -49,6 +93,13 @@ typedef enum { PicopassDeviceSaveFormatLF, } PicopassDeviceSaveFormat; +typedef enum { + PicopassEmulatorStateHalt, + PicopassEmulatorStateIdle, + PicopassEmulatorStateActive, + PicopassEmulatorStateSelected, +} PicopassEmulatorState; + typedef struct { bool valid; uint8_t bitLength; @@ -80,6 +131,15 @@ typedef struct { IclassEliteDictAttackData iclass_elite_dict_attack_data; } PicopassDeviceData; +typedef struct { + PicopassEmulatorState state; + LoclassState_t cipher_state; + uint8_t key_block_num; // in loclass mode used to store csn# + bool loclass_mode; + bool loclass_got_std_key; + LoclassWriter* loclass_writer; +} PicopassEmulatorCtx; + typedef struct { Storage* storage; DialogsApp* dialogs; diff --git a/applications/external/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h index 9147cfa0c..651ccc010 100644 --- a/applications/external/picopass/picopass_i.h +++ b/applications/external/picopass/picopass_i.h @@ -22,6 +22,7 @@ #include "scenes/picopass_scene.h" #include "views/dict_attack.h" +#include "views/loclass.h" #include #include @@ -29,6 +30,10 @@ #define PICOPASS_TEXT_STORE_SIZE 128 +#define LOCLASS_NUM_CSNS 9 +// Collect 2 MACs per CSN to account for keyroll modes +#define LOCLASS_MACS_TO_COLLECT (LOCLASS_NUM_CSNS * 2) + enum PicopassCustomEvent { // Reserve first 100 events for button types and indexes, starting from 0 PicopassCustomEventReserved = 100, @@ -63,6 +68,7 @@ struct Picopass { TextInput* text_input; Widget* widget; DictAttack* dict_attack; + Loclass* loclass; }; typedef enum { @@ -72,6 +78,7 @@ typedef enum { PicopassViewTextInput, PicopassViewWidget, PicopassViewDictAttack, + PicopassViewLoclass, } PicopassView; Picopass* picopass_alloc(); @@ -82,6 +89,8 @@ void picopass_text_store_clear(Picopass* picopass); void picopass_blink_start(Picopass* picopass); +void picopass_blink_emulate_start(Picopass* picopass); + void picopass_blink_stop(Picopass* picopass); void picopass_show_loading_popup(void* context, bool show); diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index e671552c5..024ed41fc 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -1,9 +1,25 @@ #include "picopass_worker_i.h" #include +#include #define TAG "PicopassWorker" +#define HAS_MASK(x, b) ((x & b) == b) + +// CSNs from Proxmark3 repo +static const uint8_t loclass_csns[LOCLASS_NUM_CSNS][PICOPASS_BLOCK_LEN] = { + {0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0}, + {0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0}, + {0x10, 0x97, 0x83, 0x7B, 0xF7, 0xFF, 0x12, 0xE0}, + {0x13, 0x97, 0x82, 0x7A, 0xF7, 0xFF, 0x12, 0xE0}, + {0x07, 0x0E, 0x0D, 0xF9, 0xF7, 0xFF, 0x12, 0xE0}, + {0x14, 0x96, 0x84, 0x76, 0xF7, 0xFF, 0x12, 0xE0}, + {0x17, 0x96, 0x85, 0x71, 0xF7, 0xFF, 0x12, 0xE0}, + {0xCE, 0xC5, 0x0F, 0x77, 0xF7, 0xFF, 0x12, 0xE0}, + {0xD2, 0x5A, 0x82, 0xF8, 0xF7, 0xFF, 0x12, 0xE0}, +}; + static void picopass_worker_enable_field() { furi_hal_nfc_ll_txrx_on(); furi_hal_nfc_exit_sleep(); @@ -68,6 +84,21 @@ void picopass_worker_stop(PicopassWorker* picopass_worker) { furi_assert(picopass_worker); furi_assert(picopass_worker->thread); + if(furi_thread_get_state(picopass_worker->thread) == FuriThreadStateStopped) { + return; + } + + if(picopass_worker->state == PicopassWorkerStateBroken || + picopass_worker->state == PicopassWorkerStateReady) { + return; + } + + if(picopass_worker->state != PicopassWorkerStateEmulate && + picopass_worker->state != PicopassWorkerStateLoclass) { + // Can't do this while emulating in transparent mode as SPI isn't active + picopass_worker_disable_field(ERR_NONE); + } + if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) { picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); furi_thread_join(picopass_worker->thread); @@ -587,15 +618,22 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { int32_t picopass_worker_task(void* context) { PicopassWorker* picopass_worker = context; - picopass_worker_enable_field(); if(picopass_worker->state == PicopassWorkerStateDetect) { + picopass_worker_enable_field(); picopass_worker_detect(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateWrite) { + picopass_worker_enable_field(); picopass_worker_write(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateWriteKey) { + picopass_worker_enable_field(); picopass_worker_write_key(picopass_worker); } else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { + picopass_worker_enable_field(); picopass_worker_elite_dict_attack(picopass_worker); + } else if(picopass_worker->state == PicopassWorkerStateEmulate) { + picopass_worker_emulate(picopass_worker, false); + } else if(picopass_worker->state == PicopassWorkerStateLoclass) { + picopass_worker_emulate(picopass_worker, true); } else if(picopass_worker->state == PicopassWorkerStateStop) { FURI_LOG_D(TAG, "Worker state stop"); // no-op @@ -749,3 +787,479 @@ void picopass_worker_write_key(PicopassWorker* picopass_worker) { furi_delay_ms(100); } } + +// from proxmark3 armsrc/iclass.c rotateCSN +static void picopass_anticoll_csn(uint8_t* rotated_csn, const uint8_t* original_csn) { + for(uint8_t i = 0; i < 8; i++) { + rotated_csn[i] = (original_csn[i] >> 3) | (original_csn[(i + 1) % 8] << 5); + } +} + +static void picopass_append_crc(uint8_t* buf, uint16_t size) { + uint16_t crc = rfalPicoPassCalculateCcitt(0xE012, buf, size); + + buf[size] = crc & 0xFF; + buf[size + 1] = crc >> 8; +} + +static inline void picopass_emu_read_blocks( + NfcVData* nfcv_data, + uint8_t* buf, + uint8_t block_num, + uint8_t block_count) { + memcpy( + buf, nfcv_data->data + (block_num * PICOPASS_BLOCK_LEN), block_count * PICOPASS_BLOCK_LEN); +} + +static inline void picopass_emu_write_blocks( + NfcVData* nfcv_data, + const uint8_t* buf, + uint8_t block_num, + uint8_t block_count) { + memcpy( + nfcv_data->data + (block_num * PICOPASS_BLOCK_LEN), buf, block_count * PICOPASS_BLOCK_LEN); +} + +static void picopass_init_cipher_state(NfcVData* nfcv_data, PicopassEmulatorCtx* ctx) { + uint8_t cc[PICOPASS_BLOCK_LEN]; + uint8_t key[PICOPASS_BLOCK_LEN]; + + picopass_emu_read_blocks(nfcv_data, cc, PICOPASS_EPURSE_BLOCK_INDEX, 1); + picopass_emu_read_blocks(nfcv_data, key, ctx->key_block_num, 1); + + ctx->cipher_state = loclass_opt_doTagMAC_1(cc, key); +} + +static void + loclass_update_csn(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, PicopassEmulatorCtx* ctx) { + // collect two nonces in a row for each CSN + uint8_t csn_num = (ctx->key_block_num / 2) % LOCLASS_NUM_CSNS; + memcpy(nfc_data->uid, loclass_csns[csn_num], PICOPASS_BLOCK_LEN); + picopass_emu_write_blocks(nfcv_data, loclass_csns[csn_num], PICOPASS_CSN_BLOCK_INDEX, 1); +} + +static void picopass_emu_handle_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + PicopassEmulatorCtx* ctx = nfcv_data->emu_protocol_ctx; + uint8_t response[34]; + uint8_t response_length = 0; + uint8_t key_block_num = PICOPASS_KD_BLOCK_INDEX; + + const uint8_t block_ff[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + if(nfcv_data->frame_length < 1) { + return; + } + + switch(nfcv_data->frame[0]) { + case PICOPASS_CMD_ACTALL: // No args + if(nfcv_data->frame_length != 1) { + return; + } + + if(ctx->state != PicopassEmulatorStateHalt) { + ctx->state = PicopassEmulatorStateActive; + } + + // Send SOF only + break; + case PICOPASS_CMD_ACT: // No args + if(nfcv_data->frame_length != 1 || ctx->state != PicopassEmulatorStateActive) { + return; + } + + // Send SOF only + break; + case PICOPASS_CMD_HALT: // No args + if(nfcv_data->frame_length != 1 || ctx->state != PicopassEmulatorStateSelected) { + return; + } + + // Technically we should go to StateHalt, but since we can't detect the field dropping we drop to idle instead + ctx->state = PicopassEmulatorStateIdle; + + // Send SOF only + break; + case PICOPASS_CMD_READ_OR_IDENTIFY: + if(nfcv_data->frame_length == 1 && + ctx->state == PicopassEmulatorStateActive) { // PICOPASS_CMD_IDENTIFY + // ASNB(8) CRC16(2) + picopass_anticoll_csn(response, nfc_data->uid); + picopass_append_crc(response, PICOPASS_BLOCK_LEN); + response_length = PICOPASS_BLOCK_LEN + 2; + break; + } else if( + nfcv_data->frame_length == 4 && + ctx->state == PicopassEmulatorStateSelected) { // PICOPASS_CMD_READ ADDRESS(1) CRC16(2) + if(nfcv_data->frame[1] >= PICOPASS_MAX_APP_LIMIT) { + return; + } + + // TODO: Check CRC? + // TODO: Check auth? + + // DATA(8) CRC16(2) + if(nfcv_data->frame[1] == PICOPASS_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_KC_BLOCK_INDEX) { + // Reading Kd or Kc blocks always returns FF's + memcpy(response, block_ff, PICOPASS_BLOCK_LEN); + } else { + picopass_emu_read_blocks(nfcv_data, response, nfcv_data->frame[1], 1); + } + picopass_append_crc(response, PICOPASS_BLOCK_LEN); + response_length = PICOPASS_BLOCK_LEN + 2; + break; + } + + return; + case PICOPASS_CMD_READ4: // ADDRESS(1) CRC16(2) + if(nfcv_data->frame_length != 4 || ctx->state != PicopassEmulatorStateSelected || + nfcv_data->frame[1] + 4 >= PICOPASS_MAX_APP_LIMIT) { + return; + } + + // TODO: Check CRC? + // TODO: Check auth? + + uint8_t blockNum = nfcv_data->frame[1]; + + // DATA(32) CRC16(2) + picopass_emu_read_blocks(nfcv_data, response, blockNum, 4); + if(blockNum == 4) { + // Kc is block 4, so just redact first block of response + memcpy(response, block_ff, PICOPASS_BLOCK_LEN); + } else if(blockNum < 4) { + // Kd is block 3 + uint8_t* kdOffset = response + ((3 - blockNum) * PICOPASS_BLOCK_LEN); + memcpy(kdOffset, block_ff, PICOPASS_BLOCK_LEN); + if(blockNum != 0) { + // Redact Kc + memcpy(kdOffset + PICOPASS_BLOCK_LEN, block_ff, PICOPASS_BLOCK_LEN); + } + } + picopass_append_crc(response, PICOPASS_BLOCK_LEN * 4); + response_length = (PICOPASS_BLOCK_LEN * 4) + 2; + break; + case PICOPASS_CMD_SELECT: // ASNB(8)|SERIALNB(8) + if(nfcv_data->frame_length != 9) { + return; + } + + uint8_t select_csn[PICOPASS_BLOCK_LEN]; + if(ctx->state == PicopassEmulatorStateHalt || ctx->state == PicopassEmulatorStateIdle) { + memcpy(select_csn, nfc_data->uid, PICOPASS_BLOCK_LEN); + } else { + picopass_anticoll_csn(select_csn, nfc_data->uid); + } + + if(memcmp(nfcv_data->frame + 1, select_csn, PICOPASS_BLOCK_LEN)) { + if(ctx->state == PicopassEmulatorStateActive) { + ctx->state = PicopassEmulatorStateIdle; + } else if(ctx->state == PicopassEmulatorStateSelected) { + // Technically we should go to StateHalt, but since we can't detect the field dropping we drop to idle instead + ctx->state = PicopassEmulatorStateIdle; + } + + return; + } + + ctx->state = PicopassEmulatorStateSelected; + + // SERIALNB(8) CRC16(2) + memcpy(response, nfc_data->uid, PICOPASS_BLOCK_LEN); + picopass_append_crc(response, PICOPASS_BLOCK_LEN); + + response_length = PICOPASS_BLOCK_LEN + 2; + break; + case PICOPASS_CMD_READCHECK_KC: // ADDRESS(1) + key_block_num = PICOPASS_KC_BLOCK_INDEX; + // fallthrough + case PICOPASS_CMD_READCHECK_KD: // ADDRESS(1) + if(nfcv_data->frame_length != 2 || nfcv_data->frame[1] != PICOPASS_EPURSE_BLOCK_INDEX || + ctx->state != PicopassEmulatorStateSelected) { + return; + } + + if(ctx->key_block_num != key_block_num && !ctx->loclass_mode) { + ctx->key_block_num = key_block_num; + picopass_init_cipher_state(nfcv_data, ctx); + } + + // DATA(8) + picopass_emu_read_blocks(nfcv_data, response, nfcv_data->frame[1], 1); + response_length = PICOPASS_BLOCK_LEN; + break; + case PICOPASS_CMD_CHECK: // CHALLENGE(4) READERSIGNATURE(4) + if(nfcv_data->frame_length != 9 || ctx->state != PicopassEmulatorStateSelected) { + return; + } + + if(ctx->loclass_mode) { + // LOCLASS Reader attack mode + + // Copy EPURSE + uint8_t cc[PICOPASS_BLOCK_LEN]; + picopass_emu_read_blocks(nfcv_data, cc, PICOPASS_EPURSE_BLOCK_INDEX, 1); + + // Check if the nonce is from a standard key + uint8_t key[PICOPASS_BLOCK_LEN]; + loclass_iclass_calc_div_key(nfc_data->uid, picopass_iclass_key, key, false); + ctx->cipher_state = loclass_opt_doTagMAC_1(cc, key); + + uint8_t rmac[4]; + loclass_opt_doBothMAC_2(ctx->cipher_state, nfcv_data->frame + 1, rmac, response, key); + + if(!memcmp(nfcv_data->frame + 5, rmac, 4)) { + // MAC from reader matches Standard Key, keyroll mode or non-elite keyed reader. + // Either way no point logging it. + + FURI_LOG_W(TAG, "loclass: standard key detected during collection"); + ctx->loclass_got_std_key = true; + + ctx->state = PicopassEmulatorStateIdle; + return; + } + + // Copy CHALLENGE (nr) and READERSIGNATURE (mac) from frame + uint8_t nr[4]; + memcpy(nr, nfcv_data->frame + 1, 4); + uint8_t mac[4]; + memcpy(mac, nfcv_data->frame + 5, 4); + + FURI_LOG_I(TAG, "loclass: got nr/mac pair"); + loclass_writer_write_params( + ctx->loclass_writer, ctx->key_block_num, nfc_data->uid, cc, nr, mac); + + // Rotate to the next CSN + ctx->key_block_num = (ctx->key_block_num + 1) % (LOCLASS_NUM_CSNS * 2); + loclass_update_csn(nfc_data, nfcv_data, ctx); + + ctx->state = PicopassEmulatorStateIdle; + + return; + } + + uint8_t key[PICOPASS_BLOCK_LEN]; + picopass_emu_read_blocks(nfcv_data, key, ctx->key_block_num, 1); + + uint8_t rmac[4]; + loclass_opt_doBothMAC_2(ctx->cipher_state, nfcv_data->frame + 1, rmac, response, key); + + if(memcmp(nfcv_data->frame + 5, rmac, 4)) { + // Bad MAC from reader, do not send a response. + FURI_LOG_I(TAG, "Got bad MAC from reader"); + return; + } + + // CHIPRESPONSE(4) + response_length = 4; + break; + case PICOPASS_CMD_UPDATE: // ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) + if((nfcv_data->frame_length != 12 && nfcv_data->frame_length != 14) || + ctx->state != PicopassEmulatorStateSelected) { + return; + } + + if(nfcv_data->frame[1] >= PICOPASS_MAX_APP_LIMIT) { + return; + } + + uint8_t cfgBlock[PICOPASS_BLOCK_LEN]; + picopass_emu_read_blocks(nfcv_data, cfgBlock, PICOPASS_CONFIG_BLOCK_INDEX, 1); + bool persMode = HAS_MASK(cfgBlock[7], PICOPASS_FUSE_PERS); + + if((nfcv_data->frame[1] == PICOPASS_CSN_BLOCK_INDEX) // CSN is always read only + || + (!persMode && + !HAS_MASK(cfgBlock[3], 0x80)) // Chip is in RO mode, no updated possible (even ePurse) + || (!persMode && + nfcv_data->frame[1] == + PICOPASS_AIA_BLOCK_INDEX) // AIA can only be set in personalisation mode + || (!persMode && + (nfcv_data->frame[1] == PICOPASS_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_KC_BLOCK_INDEX) && + (!HAS_MASK(cfgBlock[7], PICOPASS_FUSE_CRYPT10)))) { + return; // TODO: Is this the right response? + } + + if(nfcv_data->frame[1] >= 6 && nfcv_data->frame[1] <= 12) { + if(!HAS_MASK( + cfgBlock[3], + 1 << (nfcv_data->frame[1] - 6))) { // bit0 is block6, up to bit6 being block12 + // Block is marked as read-only, deny writing + return; // TODO: Is this the right response? + } + } + + // TODO: Check CRC/SIGN depending on if in secure mode + // Check correct key + // -> Kd only allows decrementing e-Purse + // -> per-app controlled by key access config + //bool keyAccess = HAS_MASK(cfgBlock[5], 0x01); + // -> must auth with that key to change it + + uint8_t blockOffset = nfcv_data->frame[1]; + uint8_t block[PICOPASS_BLOCK_LEN]; + switch(nfcv_data->frame[1]) { + case PICOPASS_CONFIG_BLOCK_INDEX: + block[0] = cfgBlock[0]; // Applications Limit + block[1] = cfgBlock[1] & nfcv_data->frame[3]; // OTP + block[2] = cfgBlock[2] & nfcv_data->frame[4]; // OTP + block[3] = cfgBlock[3] & nfcv_data->frame[5]; // Block Write Lock + block[4] = cfgBlock[4]; // Chip Config + block[5] = cfgBlock[5]; // Memory Config + block[6] = nfcv_data->frame[8]; // EAS + block[7] = cfgBlock[7]; // Fuses + + // Some parts allow w (but not e) if in persMode + if(persMode) { + block[0] &= nfcv_data->frame[2]; // Applications Limit + block[4] &= nfcv_data->frame[6]; // Chip Config + block[5] &= nfcv_data->frame[7]; // Memory Config + block[7] &= nfcv_data->frame[9]; // Fuses + } else { + // Fuses allows setting Crypt1/0 from 1 to 0 only during application mode + block[7] &= nfcv_data->frame[9] | ~PICOPASS_FUSE_CRYPT10; + } + break; + case PICOPASS_EPURSE_BLOCK_INDEX: + // ePurse updates swap first and second half of the block each update + memcpy(block + 4, nfcv_data->frame + 2, 4); + memcpy(block, nfcv_data->frame + 6, 4); + break; + case PICOPASS_KD_BLOCK_INDEX: + // fallthrough + case PICOPASS_KC_BLOCK_INDEX: + if(!persMode) { + picopass_emu_read_blocks(nfcv_data, block, blockOffset, 1); + for(uint8_t i = 0; i < sizeof(PICOPASS_BLOCK_LEN); i++) + block[i] ^= nfcv_data->frame[i + 2]; + break; + } + // Use default case when in personalisation mode + // fallthrough + default: + memcpy(block, nfcv_data->frame + 2, PICOPASS_BLOCK_LEN); + break; + } + + picopass_emu_write_blocks(nfcv_data, block, blockOffset, 1); + + if((nfcv_data->frame[1] == ctx->key_block_num || + nfcv_data->frame[1] == PICOPASS_EPURSE_BLOCK_INDEX) && + !ctx->loclass_mode) + picopass_init_cipher_state(nfcv_data, ctx); + + // DATA(8) CRC16(2) + if(nfcv_data->frame[1] == PICOPASS_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_KD_BLOCK_INDEX) { + // Key updates always return FF's + memcpy(response, block_ff, PICOPASS_BLOCK_LEN); + } else { + memcpy(response, block, PICOPASS_BLOCK_LEN); + } + picopass_append_crc(response, PICOPASS_BLOCK_LEN); + response_length = PICOPASS_BLOCK_LEN + 2; + break; + case PICOPASS_CMD_PAGESEL: // PAGE(1) CRC16(2) + // Chips with a single page do not answer to this command + // BLOCK1(8) CRC16(2) + return; + case PICOPASS_CMD_DETECT: + // TODO - not used by iClass though + return; + default: + return; + } + + NfcVSendFlags flags = NfcVSendFlagsSof | NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; + if(response_length > 0) { + flags |= NfcVSendFlagsEof; + } + + nfcv_emu_send( + tx_rx, + nfcv_data, + response, + response_length, + flags, + nfcv_data->eof_timestamp + NFCV_FDT_FC(4000)); // 3650 is ~254uS 4000 is ~283uS +} + +void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode) { + FuriHalNfcTxRxContext tx_rx = {}; + PicopassEmulatorCtx emu_ctx = { + .state = PicopassEmulatorStateIdle, + .key_block_num = PICOPASS_KD_BLOCK_INDEX, + .loclass_mode = loclass_mode, + .loclass_got_std_key = false, + .loclass_writer = NULL, + }; + FuriHalNfcDevData nfc_data = { + .uid_len = PICOPASS_BLOCK_LEN, + }; + NfcVData* nfcv_data = malloc(sizeof(NfcVData)); + nfcv_data->block_size = PICOPASS_BLOCK_LEN; + nfcv_data->emu_protocol_ctx = &emu_ctx; + nfcv_data->emu_protocol_handler = &picopass_emu_handle_packet; + + PicopassDeviceData* dev_data = picopass_worker->dev_data; + PicopassBlock* blocks = dev_data->AA1; + + if(loclass_mode) { + // Setup blocks for loclass attack + emu_ctx.key_block_num = 0; + loclass_update_csn(&nfc_data, nfcv_data, &emu_ctx); + + uint8_t conf[8] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C}; + picopass_emu_write_blocks(nfcv_data, conf, PICOPASS_CONFIG_BLOCK_INDEX, 1); + + uint8_t epurse[8] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + picopass_emu_write_blocks(nfcv_data, epurse, PICOPASS_EPURSE_BLOCK_INDEX, 1); + + uint8_t aia[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + picopass_emu_write_blocks(nfcv_data, aia, PICOPASS_AIA_BLOCK_INDEX, 1); + + emu_ctx.loclass_writer = loclass_writer_alloc(); + loclass_writer_write_start_stop(emu_ctx.loclass_writer, true); + } else { + memcpy(nfc_data.uid, blocks[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); + memcpy(nfcv_data->data, blocks, sizeof(dev_data->AA1)); + picopass_init_cipher_state(nfcv_data, &emu_ctx); + } + + uint8_t last_loclass_csn_num = 0; + bool loclass_got_std_key = false; + + nfcv_emu_init(&nfc_data, nfcv_data); + while(picopass_worker->state == PicopassWorkerStateEmulate || + picopass_worker->state == PicopassWorkerStateLoclass) { + if(nfcv_emu_loop(&tx_rx, &nfc_data, nfcv_data, 500)) { + if(picopass_worker->callback) { + if((loclass_mode) && (last_loclass_csn_num != emu_ctx.key_block_num)) { + last_loclass_csn_num = emu_ctx.key_block_num; + picopass_worker->callback( + PicopassWorkerEventLoclassGotMac, picopass_worker->context); + } else if((loclass_mode) && !loclass_got_std_key && emu_ctx.loclass_got_std_key) { + loclass_got_std_key = true; + picopass_worker->callback( + PicopassWorkerEventLoclassGotStandardKey, picopass_worker->context); + } else { + picopass_worker->callback( + PicopassWorkerEventSuccess, picopass_worker->context); + } + } + } + } + + if(emu_ctx.loclass_writer) { + loclass_writer_write_start_stop(emu_ctx.loclass_writer, false); + loclass_writer_free(emu_ctx.loclass_writer); + } + + nfcv_emu_deinit(nfcv_data); + free(nfcv_data); +} diff --git a/applications/external/picopass/picopass_worker.h b/applications/external/picopass/picopass_worker.h index e9d37481b..642e4c962 100644 --- a/applications/external/picopass/picopass_worker.h +++ b/applications/external/picopass/picopass_worker.h @@ -15,6 +15,8 @@ typedef enum { PicopassWorkerStateWrite, PicopassWorkerStateWriteKey, PicopassWorkerStateEliteDictAttack, + PicopassWorkerStateEmulate, + PicopassWorkerStateLoclass, // Transition PicopassWorkerStateStop, } PicopassWorkerState; @@ -32,6 +34,8 @@ typedef enum { PicopassWorkerEventCardDetected, PicopassWorkerEventNewDictKeyBatch, PicopassWorkerEventNoDictFound, + PicopassWorkerEventLoclassGotMac, + PicopassWorkerEventLoclassGotStandardKey, } PicopassWorkerEvent; typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); diff --git a/applications/external/picopass/picopass_worker_i.h b/applications/external/picopass/picopass_worker_i.h index f41cfce45..5e51b1cc6 100644 --- a/applications/external/picopass/picopass_worker_i.h +++ b/applications/external/picopass/picopass_worker_i.h @@ -1,6 +1,7 @@ #pragma once #include "picopass_worker.h" +#include "loclass_writer.h" #include "picopass_i.h" #include @@ -32,3 +33,4 @@ int32_t picopass_worker_task(void* context); void picopass_worker_detect(PicopassWorker* picopass_worker); void picopass_worker_write(PicopassWorker* picopass_worker); void picopass_worker_write_key(PicopassWorker* picopass_worker); +void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode); diff --git a/applications/external/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c index ac66cb92d..df10d97d6 100644 --- a/applications/external/picopass/rfal_picopass.c +++ b/applications/external/picopass/rfal_picopass.c @@ -29,8 +29,7 @@ static uint16_t rfalPicoPassUpdateCcitt(uint16_t crcSeed, uint8_t dataByte) { return crc; } -static uint16_t - rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length) { +uint16_t rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length) { uint16_t crc = preloadValue; uint16_t index; diff --git a/applications/external/picopass/rfal_picopass.h b/applications/external/picopass/rfal_picopass.h index 6926b2a79..194a03fb9 100644 --- a/applications/external/picopass/rfal_picopass.h +++ b/applications/external/picopass/rfal_picopass.h @@ -38,6 +38,8 @@ typedef struct { uint8_t crc[2]; } rfalPicoPassReadBlockRes; +uint16_t rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length); + FuriHalNfcReturn rfalPicoPassPollerInitialize(void); FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void); FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes); diff --git a/applications/external/picopass/scenes/picopass_scene_config.h b/applications/external/picopass/scenes/picopass_scene_config.h index 8ea970498..6156ed689 100644 --- a/applications/external/picopass/scenes/picopass_scene_config.h +++ b/applications/external/picopass/scenes/picopass_scene_config.h @@ -15,3 +15,5 @@ ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) ADD_SCENE(picopass, write_key, WriteKey) ADD_SCENE(picopass, key_menu, KeyMenu) ADD_SCENE(picopass, elite_dict_attack, EliteDictAttack) +ADD_SCENE(picopass, emulate, Emulate) +ADD_SCENE(picopass, loclass, Loclass) diff --git a/applications/external/picopass/scenes/picopass_scene_emulate.c b/applications/external/picopass/scenes/picopass_scene_emulate.c new file mode 100644 index 000000000..d772c28d5 --- /dev/null +++ b/applications/external/picopass/scenes/picopass_scene_emulate.c @@ -0,0 +1,58 @@ +#include "../picopass_i.h" +#include + +void picopass_emulate_worker_callback(PicopassWorkerEvent event, void* context) { + furi_assert(context); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, event); +} + +void picopass_scene_emulate_on_enter(void* context) { + Picopass* picopass = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + Widget* widget = picopass->widget; + widget_reset(widget); + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating"); + widget_add_string_element(widget, 89, 42, AlignCenter, AlignTop, FontPrimary, "PicoPass"); + + // Setup view + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); + + // Start worker + picopass_worker_start( + picopass->worker, + PicopassWorkerStateEmulate, + &picopass->dev->dev_data, + picopass_emulate_worker_callback, + picopass); + + picopass_blink_emulate_start(picopass); +} + +bool picopass_scene_emulate_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassCustomEventWorkerExit) { + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + return consumed; +} + +void picopass_scene_emulate_on_exit(void* context) { + Picopass* picopass = context; + + picopass_blink_stop(picopass); + + // Stop worker + picopass_worker_stop(picopass->worker); + + // Clear view + widget_reset(picopass->widget); +} diff --git a/applications/external/picopass/scenes/picopass_scene_loclass.c b/applications/external/picopass/scenes/picopass_scene_loclass.c new file mode 100644 index 000000000..06a17eefc --- /dev/null +++ b/applications/external/picopass/scenes/picopass_scene_loclass.c @@ -0,0 +1,80 @@ +#include "../picopass_i.h" +#include + +void picopass_loclass_worker_callback(PicopassWorkerEvent event, void* context) { + furi_assert(context); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, event); +} + +void picopass_loclass_result_callback(void* context) { + furi_assert(context); + Picopass* picopass = context; + view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventViewExit); +} + +void picopass_scene_loclass_on_enter(void* context) { + Picopass* picopass = context; + DOLPHIN_DEED(DolphinDeedNfcEmulate); + + scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneLoclass, 0); + + loclass_set_callback(picopass->loclass, picopass_loclass_result_callback, picopass); + + // Start worker + picopass_worker_start( + picopass->worker, + PicopassWorkerStateLoclass, + &picopass->dev->dev_data, + picopass_loclass_worker_callback, + picopass); + + picopass_blink_emulate_start(picopass); + + loclass_set_header(picopass->loclass, "Loclass"); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewLoclass); +} + +bool picopass_scene_loclass_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + uint32_t loclass_macs_collected = + scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneLoclass); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassWorkerEventLoclassGotMac) { + loclass_macs_collected++; + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneLoclass, loclass_macs_collected); + loclass_set_num_macs(picopass->loclass, loclass_macs_collected); + if(loclass_macs_collected >= LOCLASS_MACS_TO_COLLECT) { + scene_manager_previous_scene(picopass->scene_manager); + } + consumed = true; + } else if(event.event == PicopassWorkerEventLoclassGotStandardKey) { + loclass_set_header(picopass->loclass, "Loclass (Got Std Key)"); + consumed = true; + } else if(event.event == PicopassCustomEventViewExit) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } + return consumed; +} + +void picopass_scene_loclass_on_exit(void* context) { + Picopass* picopass = context; + + picopass_blink_stop(picopass); + + // Stop worker + picopass_worker_stop(picopass->worker); + + loclass_reset(picopass->loclass); + + // Clear view + widget_reset(picopass->widget); +} diff --git a/applications/external/picopass/scenes/picopass_scene_saved_menu.c b/applications/external/picopass/scenes/picopass_scene_saved_menu.c index 90a27ee81..401f43f9b 100644 --- a/applications/external/picopass/scenes/picopass_scene_saved_menu.c +++ b/applications/external/picopass/scenes/picopass_scene_saved_menu.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexDelete, SubmenuIndexInfo, SubmenuIndexWrite, + SubmenuIndexEmulate, }; void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -26,6 +27,12 @@ void picopass_scene_saved_menu_on_enter(void* context) { submenu, "Info", SubmenuIndexInfo, picopass_scene_saved_menu_submenu_callback, picopass); submenu_add_item( submenu, "Write", SubmenuIndexWrite, picopass_scene_saved_menu_submenu_callback, picopass); + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexEmulate, + picopass_scene_saved_menu_submenu_callback, + picopass); submenu_set_selected_item( picopass->submenu, @@ -51,6 +58,9 @@ bool picopass_scene_saved_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCard); consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneEmulate); + consumed = true; } } diff --git a/applications/external/picopass/scenes/picopass_scene_start.c b/applications/external/picopass/scenes/picopass_scene_start.c index 8f7b627aa..cfd758ed5 100644 --- a/applications/external/picopass/scenes/picopass_scene_start.c +++ b/applications/external/picopass/scenes/picopass_scene_start.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexRead, SubmenuIndexEliteDictAttack, SubmenuIndexSaved, + SubmenuIndexLoclass, }; void picopass_scene_start_submenu_callback(void* context, uint32_t index) { @@ -24,6 +25,9 @@ void picopass_scene_start_on_enter(void* context) { submenu_add_item( submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass); + submenu_add_item( + submenu, "Loclass", SubmenuIndexLoclass, picopass_scene_start_submenu_callback, picopass); + submenu_set_selected_item( submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart)); picopass_device_clear(picopass->dev); @@ -52,6 +56,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { picopass->scene_manager, PicopassSceneStart, SubmenuIndexEliteDictAttack); scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteDictAttack); consumed = true; + } else if(event.event == SubmenuIndexLoclass) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneLoclass, PicopassSceneLoclass); + scene_manager_next_scene(picopass->scene_manager, PicopassSceneLoclass); + consumed = true; } } diff --git a/applications/external/picopass/views/loclass.c b/applications/external/picopass/views/loclass.c new file mode 100644 index 000000000..4158019a8 --- /dev/null +++ b/applications/external/picopass/views/loclass.c @@ -0,0 +1,106 @@ +#include "loclass.h" +#include "../picopass_worker_i.h" + +#include + +struct Loclass { + View* view; + LoclassCallback callback; + void* context; +}; + +typedef struct { + FuriString* header; + uint8_t num_macs; +} LoclassViewModel; + +static void loclass_draw_callback(Canvas* canvas, void* model) { + LoclassViewModel* m = model; + + char draw_str[32] = {}; + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); + + float progress = m->num_macs == 0 ? 0 : + (float)(m->num_macs) / (float)(LOCLASS_MACS_TO_COLLECT); + + if(progress > 1.0) { + progress = 1.0; + } + + snprintf(draw_str, sizeof(draw_str), "%d/%d", m->num_macs, LOCLASS_MACS_TO_COLLECT); + + elements_progress_bar_with_text(canvas, 0, 20, 128, progress, draw_str); + + elements_button_center(canvas, "Skip"); +} + +static bool loclass_input_callback(InputEvent* event, void* context) { + Loclass* loclass = context; + bool consumed = false; + if(event->type == InputTypeShort && event->key == InputKeyOk) { + if(loclass->callback) { + loclass->callback(loclass->context); + } + consumed = true; + } + return consumed; +} + +Loclass* loclass_alloc() { + Loclass* loclass = malloc(sizeof(Loclass)); + loclass->view = view_alloc(); + view_allocate_model(loclass->view, ViewModelTypeLocking, sizeof(LoclassViewModel)); + view_set_draw_callback(loclass->view, loclass_draw_callback); + view_set_input_callback(loclass->view, loclass_input_callback); + view_set_context(loclass->view, loclass); + with_view_model( + loclass->view, LoclassViewModel * model, { model->header = furi_string_alloc(); }, false); + return loclass; +} + +void loclass_free(Loclass* loclass) { + furi_assert(loclass); + with_view_model( + loclass->view, LoclassViewModel * model, { furi_string_free(model->header); }, false); + view_free(loclass->view); + free(loclass); +} + +void loclass_reset(Loclass* loclass) { + furi_assert(loclass); + with_view_model( + loclass->view, + LoclassViewModel * model, + { + model->num_macs = 0; + furi_string_reset(model->header); + }, + false); +} + +View* loclass_get_view(Loclass* loclass) { + furi_assert(loclass); + return loclass->view; +} + +void loclass_set_callback(Loclass* loclass, LoclassCallback callback, void* context) { + furi_assert(loclass); + furi_assert(callback); + loclass->callback = callback; + loclass->context = context; +} + +void loclass_set_header(Loclass* loclass, const char* header) { + furi_assert(loclass); + furi_assert(header); + + with_view_model( + loclass->view, LoclassViewModel * model, { furi_string_set(model->header, header); }, true); +} + +void loclass_set_num_macs(Loclass* loclass, uint16_t num_macs) { + furi_assert(loclass); + with_view_model( + loclass->view, LoclassViewModel * model, { model->num_macs = num_macs; }, true); +} diff --git a/applications/external/picopass/views/loclass.h b/applications/external/picopass/views/loclass.h new file mode 100644 index 000000000..0e39b6083 --- /dev/null +++ b/applications/external/picopass/views/loclass.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include + +typedef struct Loclass Loclass; + +typedef void (*LoclassCallback)(void* context); + +Loclass* loclass_alloc(); + +void loclass_free(Loclass* loclass); + +void loclass_reset(Loclass* loclass); + +View* loclass_get_view(Loclass* loclass); + +void loclass_set_callback(Loclass* loclass, LoclassCallback callback, void* context); + +void loclass_set_header(Loclass* loclass, const char* header); + +void loclass_set_num_macs(Loclass* loclass, uint16_t num_macs); From 7b22576db9311c6e436b5157f617973a51010ec7 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Sat, 10 Jun 2023 23:15:31 +1000 Subject: [PATCH 133/370] Update for flipper api change --- applications/external/picopass/scenes/picopass_scene_emulate.c | 2 +- applications/external/picopass/scenes/picopass_scene_loclass.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/picopass/scenes/picopass_scene_emulate.c b/applications/external/picopass/scenes/picopass_scene_emulate.c index d772c28d5..4e0ed073b 100644 --- a/applications/external/picopass/scenes/picopass_scene_emulate.c +++ b/applications/external/picopass/scenes/picopass_scene_emulate.c @@ -9,7 +9,7 @@ void picopass_emulate_worker_callback(PicopassWorkerEvent event, void* context) void picopass_scene_emulate_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); Widget* widget = picopass->widget; widget_reset(widget); diff --git a/applications/external/picopass/scenes/picopass_scene_loclass.c b/applications/external/picopass/scenes/picopass_scene_loclass.c index 06a17eefc..01e245557 100644 --- a/applications/external/picopass/scenes/picopass_scene_loclass.c +++ b/applications/external/picopass/scenes/picopass_scene_loclass.c @@ -15,7 +15,7 @@ void picopass_loclass_result_callback(void* context) { void picopass_scene_loclass_on_enter(void* context) { Picopass* picopass = context; - DOLPHIN_DEED(DolphinDeedNfcEmulate); + dolphin_deed(DolphinDeedNfcEmulate); scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneLoclass, 0); From 36b328da2782ef0cceeabce1b0973fb286abf6ac Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 10 Jun 2023 17:38:58 +0100 Subject: [PATCH 134/370] BT fix empty MACs --- firmware/targets/f7/furi_hal/furi_hal_bt.c | 7 +++---- firmware/targets/furi_hal_include/furi_hal_version.h | 6 ++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 91cc1e7ea..1a57b07c8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -13,9 +13,6 @@ #define TAG "FuriHalBt" -#define FURI_HAL_BT_DEFAULT_MAC_ADDR \ - { 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 } - /* Time, in ms, to wait for mode transition before crashing */ #define C2_MODE_SWITCH_TIMEOUT 10000 @@ -220,9 +217,11 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, config->adv_service_uuid |= furi_hal_version_get_hw_color(); } else if(profile == FuriHalBtProfileHidKeyboard) { // Change MAC address for HID profile + uint8_t empty_mac[sizeof(config->mac_address)] = FURI_HAL_BT_EMPTY_MAC_ADDR; uint8_t default_mac[sizeof(config->mac_address)] = FURI_HAL_BT_DEFAULT_MAC_ADDR; const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); - if(memcmp(config->mac_address, default_mac, sizeof(config->mac_address)) == 0) { + if(memcmp(config->mac_address, empty_mac, sizeof(config->mac_address)) == 0 || + memcmp(config->mac_address, default_mac, sizeof(config->mac_address)) == 0) { memcpy(config->mac_address, normal_mac, sizeof(config->mac_address)); } if(memcmp(config->mac_address, normal_mac, sizeof(config->mac_address)) == 0) { diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index 351a4da10..92f2ec9ca 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -20,6 +20,12 @@ extern "C" { #define FURI_HAL_VERSION_DEVICE_NAME_LENGTH \ (1 + FURI_HAL_BT_ADV_NAME_LENGTH) // Used for custom BT name, BLE symbol + name +#define FURI_HAL_BT_EMPTY_MAC_ADDR \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } + +#define FURI_HAL_BT_DEFAULT_MAC_ADDR \ + { 0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72 } + /** OTP Versions enum */ typedef enum { FuriHalVersionOtpVersion0 = 0x00, From 5e810dae881fe9a0b10688724ce7188f79349733 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 10 Jun 2023 18:14:51 +0100 Subject: [PATCH 135/370] Fap loader optimizations --- applications/main/fap_loader/fap_loader_app.c | 137 ++++++++---------- 1 file changed, 61 insertions(+), 76 deletions(-) diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index 7f1383ae5..75f5d3867 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -32,45 +32,46 @@ bool fap_loader_load_name_and_icon( Storage* storage, uint8_t** icon_ptr, FuriString* item_name) { + bool load_success = true; + StorageData* storage_data; if(storage_get_data(storage, path, &storage_data) == FSE_OK && storage_path_already_open(path, storage_data)) { - size_t offset = furi_string_search_rchar(path, '/'); - if(offset != FURI_STRING_FAILURE) { - furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); - } else { - furi_string_set(item_name, path); - } - return false; - } - - FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); - - FlipperApplicationPreloadStatus preload_res = - flipper_application_preload_manifest(app, furi_string_get_cstr(path)); - - bool load_success = false; - - if(preload_res == FlipperApplicationPreloadStatusSuccess || - preload_res == FlipperApplicationPreloadStatusApiMismatch) { - const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); - if(manifest->has_icon && icon_ptr != NULL && *icon_ptr != NULL) { - memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); - } - furi_string_set(item_name, manifest->name); - load_success = true; - } else { - FURI_LOG_E(TAG, "FAP Loader failed to preload %s", furi_string_get_cstr(path)); - size_t offset = furi_string_search_rchar(path, '/'); - if(offset != FURI_STRING_FAILURE) { - furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); - } else { - furi_string_set(item_name, path); - } load_success = false; } - flipper_application_free(app); + if(load_success) { + load_success = false; + + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload_manifest(app, furi_string_get_cstr(path)); + + if(preload_res == FlipperApplicationPreloadStatusSuccess || + preload_res == FlipperApplicationPreloadStatusApiMismatch) { + const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); + if(manifest->has_icon && icon_ptr != NULL && *icon_ptr != NULL) { + memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); + } + furi_string_set(item_name, manifest->name); + load_success = true; + } else { + FURI_LOG_E(TAG, "FAP Loader failed to preload %s", furi_string_get_cstr(path)); + } + + flipper_application_free(app); + } + + if(!load_success) { + size_t offset = furi_string_search_rchar(path, '/'); + if(offset != FURI_STRING_FAILURE) { + furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); + } else { + furi_string_set(item_name, path); + } + } + return load_success; } @@ -84,46 +85,23 @@ static bool fap_loader_item_callback( return fap_loader_load_name_and_icon(path, fap_loader->storage, icon_ptr, item_name); } -static bool fap_loader_run_selected_app(FapLoader* loader, bool ignore_mismatch) { +static void fap_loader_run_selected_app(FapLoader* loader) { furi_assert(loader); - FuriString* error_message; + FuriString* error_message = furi_string_alloc_set("unknown error"); + loader->app = flipper_application_alloc(loader->storage, firmware_api_interface); - error_message = furi_string_alloc_set("unknown error"); - - bool file_selected = false; bool show_error = true; - bool retry = false; do { - file_selected = true; - loader->app = flipper_application_alloc(loader->storage, firmware_api_interface); size_t start = furi_get_tick(); - FURI_LOG_I(TAG, "FAP Loader is loading %s", furi_string_get_cstr(loader->fap_path)); FlipperApplicationPreloadStatus preload_res = flipper_application_preload(loader->app, furi_string_get_cstr(loader->fap_path)); + bool api_mismatch = false; if(preload_res != FlipperApplicationPreloadStatusSuccess) { if(preload_res == FlipperApplicationPreloadStatusApiMismatch) { - if(!ignore_mismatch) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header( - message, "API Mismatch", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(message, "Cancel", NULL, "Continue"); - dialog_message_set_text( - message, - "This app might not\nwork correctly\nContinue anyways?", - 64, - 32, - AlignCenter, - AlignCenter); - if(dialog_message_show(loader->dialogs, message) == DialogMessageButtonRight) { - retry = true; - } - dialog_message_free(message); - show_error = false; - break; - } + api_mismatch = true; } else { const char* err_msg = flipper_application_preload_status_to_string(preload_res); furi_string_printf(error_message, "Preload failed: %s", err_msg); @@ -147,6 +125,24 @@ static bool fap_loader_run_selected_app(FapLoader* loader, bool ignore_mismatch) furi_string_get_cstr(loader->fap_path), err_msg); break; + } else if(api_mismatch) { + // Successful map, but found api mismatch -> warn user + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "API Mismatch", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, "Cancel", NULL, "Continue"); + dialog_message_set_text( + message, + "This app might not\nwork correctly\nContinue anyways?", + 64, + 32, + AlignCenter, + AlignCenter); + DialogMessageButton res = dialog_message_show(loader->dialogs, message); + dialog_message_free(message); + if(res != DialogMessageButtonRight) { + show_error = false; + break; + } } FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); @@ -194,12 +190,9 @@ static bool fap_loader_run_selected_app(FapLoader* loader, bool ignore_mismatch) } furi_string_free(error_message); + flipper_application_free(loader->app); - if(file_selected) { - flipper_application_free(loader->app); - } - - return retry; + return; } static bool fap_loader_select_app(FapLoader* loader) { @@ -244,31 +237,23 @@ static void fap_loader_free(FapLoader* loader) { } int32_t fap_loader_app(char* p) { - size_t start = furi_get_tick(); XTREME_ASSETS_FREE(); - FURI_LOG_I("Assets", "Freed in %ums", (size_t)(furi_get_tick() - start)); FapLoader* loader; process_favorite_launch(&p); if(p) { loader = fap_loader_alloc((const char*)p); view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - if(fap_loader_run_selected_app(loader, false)) { - fap_loader_run_selected_app(loader, true); - } + fap_loader_run_selected_app(loader); } else { loader = fap_loader_alloc(EXT_PATH("apps")); while(fap_loader_select_app(loader)) { view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - if(fap_loader_run_selected_app(loader, false)) { - fap_loader_run_selected_app(loader, true); - } + fap_loader_run_selected_app(loader); } } fap_loader_free(loader); - start = furi_get_tick(); XTREME_ASSETS_LOAD(); - FURI_LOG_I("Assets", "Loaded in %ums", (size_t)(furi_get_tick() - start)); return 0; } From 451aa86c91cd109bfc448d9e6a2271e98c0025be Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 10 Jun 2023 21:02:52 +0300 Subject: [PATCH 136/370] Infrared RCA Protocol + Update docs Thanks to anonymous contributor for protocol implementation and testing --- ReadMe.md | 3 ++ .../file_formats/InfraredFileFormats.md | 17 +++++++ lib/infrared/encoder_decoder/infrared.c | 15 +++++++ lib/infrared/encoder_decoder/infrared.h | 1 + .../rca/infrared_decoder_rca.c | 45 +++++++++++++++++++ .../rca/infrared_encoder_rca.c | 37 +++++++++++++++ .../rca/infrared_protocol_rca.c | 40 +++++++++++++++++ .../rca/infrared_protocol_rca.h | 30 +++++++++++++ .../rca/infrared_protocol_rca_i.h | 30 +++++++++++++ 9 files changed, 218 insertions(+) create mode 100644 lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c create mode 100644 lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c create mode 100644 lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c create mode 100644 lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h create mode 100644 lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h diff --git a/ReadMe.md b/ReadMe.md index 6256d5905..19d74fed8 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -65,6 +65,7 @@ Our Discord Community: * SubGHz -> BFT Mitto / Somfy Telis / Nice Flor S / CAME Atomo, etc.. manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis) * SubGHz -> Debug mode counter increase settings (+1 -> +5, +10, default: +1) * SubGHz -> Debug PIN output settings for protocol development +* Infrared -> `RCA` Protocol * Infrared -> Debug TX PIN output settings * Other small fixes and changes throughout * See other changes in readme below @@ -198,6 +199,8 @@ Games: ## [- How to use Mifare Nested plugin to recover keys](https://github.com/AloneLiberty/FlipperNested#how-to-use-it) +## [- How to make captures to add them into Universal IR remotes](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/InfraredCaptures.md) + ### **Sub-GHz** ## [- Transmission is blocked? - How to extend Sub-GHz frequency range](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/DangerousSettings.md) diff --git a/documentation/file_formats/InfraredFileFormats.md b/documentation/file_formats/InfraredFileFormats.md index 3c0acdcb7..c9b6a9536 100644 --- a/documentation/file_formats/InfraredFileFormats.md +++ b/documentation/file_formats/InfraredFileFormats.md @@ -1,5 +1,22 @@ # Infrared Flipper File Formats + +## Supported protocols list for `type: parsed` +``` + NEC + NECext + NEC42 + NEC42ext + Samsung32 + RC6 + RC5 + RC5X + SIRC + SIRC15 + SIRC20 + Kaseikyo + RCA +``` ## Infrared Remote File Format ### Example diff --git a/lib/infrared/encoder_decoder/infrared.c b/lib/infrared/encoder_decoder/infrared.c index fcfc5da2b..56f2c3f9e 100644 --- a/lib/infrared/encoder_decoder/infrared.c +++ b/lib/infrared/encoder_decoder/infrared.c @@ -11,6 +11,7 @@ #include "rc6/infrared_protocol_rc6.h" #include "sirc/infrared_protocol_sirc.h" #include "kaseikyo/infrared_protocol_kaseikyo.h" +#include "rca/infrared_protocol_rca.h" typedef struct { InfraredAlloc alloc; @@ -127,6 +128,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = { .free = infrared_encoder_kaseikyo_free}, .get_protocol_variant = infrared_protocol_kaseikyo_get_variant, }, + { + .decoder = + {.alloc = infrared_decoder_rca_alloc, + .decode = infrared_decoder_rca_decode, + .reset = infrared_decoder_rca_reset, + .check_ready = infrared_decoder_rca_check_ready, + .free = infrared_decoder_rca_free}, + .encoder = + {.alloc = infrared_encoder_rca_alloc, + .encode = infrared_encoder_rca_encode, + .reset = infrared_encoder_rca_reset, + .free = infrared_encoder_rca_free}, + .get_protocol_variant = infrared_protocol_rca_get_variant, + }, }; static int infrared_find_index_by_protocol(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/infrared.h b/lib/infrared/encoder_decoder/infrared.h index 3ab46cbbf..ada449b98 100644 --- a/lib/infrared/encoder_decoder/infrared.h +++ b/lib/infrared/encoder_decoder/infrared.h @@ -33,6 +33,7 @@ typedef enum { InfraredProtocolSIRC15, InfraredProtocolSIRC20, InfraredProtocolKaseikyo, + InfraredProtocolRCA, InfraredProtocolMAX, } InfraredProtocol; diff --git a/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c b/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c new file mode 100644 index 000000000..b6d02a38c --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_decoder_rca.c @@ -0,0 +1,45 @@ +#include "infrared_protocol_rca_i.h" +#include + +InfraredMessage* infrared_decoder_rca_check_ready(void* ctx) { + return infrared_common_decoder_check_ready(ctx); +} + +bool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder) { + furi_assert(decoder); + + uint32_t* data = (void*)&decoder->data; + + uint8_t address = (*data & 0xF); + uint8_t command = (*data >> 4) & 0xFF; + uint8_t address_inverse = (*data >> 12) & 0xF; + uint8_t command_inverse = (*data >> 16) & 0xFF; + uint8_t inverse_address_inverse = (uint8_t)~address_inverse & 0xF; + uint8_t inverse_command_inverse = (uint8_t)~command_inverse; + + if((command == inverse_command_inverse) && (address == inverse_address_inverse)) { + decoder->message.protocol = InfraredProtocolRCA; + decoder->message.address = address; + decoder->message.command = command; + decoder->message.repeat = false; + return true; + } + + return false; +} + +void* infrared_decoder_rca_alloc(void) { + return infrared_common_decoder_alloc(&infrared_protocol_rca); +} + +InfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration) { + return infrared_common_decode(decoder, level, duration); +} + +void infrared_decoder_rca_free(void* decoder) { + infrared_common_decoder_free(decoder); +} + +void infrared_decoder_rca_reset(void* decoder) { + infrared_common_decoder_reset(decoder); +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c b/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c new file mode 100644 index 000000000..f0be4a6a9 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_encoder_rca.c @@ -0,0 +1,37 @@ +#include "infrared_protocol_rca_i.h" + +#include + +void infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message) { + furi_assert(encoder_ptr); + furi_assert(message); + + InfraredCommonEncoder* encoder = encoder_ptr; + infrared_common_encoder_reset(encoder); + + uint32_t* data = (void*)encoder->data; + + uint8_t address = message->address; + uint8_t address_inverse = ~address; + uint8_t command = message->command; + uint8_t command_inverse = ~command; + + *data = address & 0xF; + *data |= command << 4; + *data |= (address_inverse & 0xF) << 12; + *data |= command_inverse << 16; + + encoder->bits_to_encode = encoder->protocol->databit_len[0]; +} + +void* infrared_encoder_rca_alloc(void) { + return infrared_common_encoder_alloc(&infrared_protocol_rca); +} + +void infrared_encoder_rca_free(void* encoder_ptr) { + infrared_common_encoder_free(encoder_ptr); +} + +InfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return infrared_common_encode(encoder_ptr, duration, level); +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c new file mode 100644 index 000000000..8e1e76dbd --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.c @@ -0,0 +1,40 @@ +#include "infrared_protocol_rca_i.h" + +const InfraredCommonProtocolSpec infrared_protocol_rca = { + .timings = + { + .preamble_mark = INFRARED_RCA_PREAMBLE_MARK, + .preamble_space = INFRARED_RCA_PREAMBLE_SPACE, + .bit1_mark = INFRARED_RCA_BIT1_MARK, + .bit1_space = INFRARED_RCA_BIT1_SPACE, + .bit0_mark = INFRARED_RCA_BIT0_MARK, + .bit0_space = INFRARED_RCA_BIT0_SPACE, + .preamble_tolerance = INFRARED_RCA_PREAMBLE_TOLERANCE, + .bit_tolerance = INFRARED_RCA_BIT_TOLERANCE, + .silence_time = INFRARED_RCA_SILENCE, + .min_split_time = INFRARED_RCA_MIN_SPLIT_TIME, + }, + .databit_len[0] = 24, + .no_stop_bit = false, + .decode = infrared_common_decode_pdwm, + .encode = infrared_common_encode_pdwm, + .interpret = infrared_decoder_rca_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; + +static const InfraredProtocolVariant infrared_protocol_variant_rca = { + .name = "RCA", + .address_length = 4, + .command_length = 8, + .frequency = INFRARED_COMMON_CARRIER_FREQUENCY, + .duty_cycle = INFRARED_COMMON_DUTY_CYCLE, + .repeat_count = INFRARED_RCA_REPEAT_COUNT_MIN, +}; + +const InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol) { + if(protocol == InfraredProtocolRCA) + return &infrared_protocol_variant_rca; + else + return NULL; +} diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h new file mode 100644 index 000000000..d9cae48e4 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../infrared_i.h" + +/*************************************************************************************************** +* RCA protocol description +* https://www.sbprojects.net/knowledge/ir/rca.php +**************************************************************************************************** +* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble +* mark space Modulation up to period repeat repeat +* mark space +* +* 4000 4000 24 bit ...8000 4000 4000 +* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________ +* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________ +* +***************************************************************************************************/ + +void* infrared_decoder_rca_alloc(void); +void infrared_decoder_rca_reset(void* decoder); +void infrared_decoder_rca_free(void* decoder); +InfraredMessage* infrared_decoder_rca_check_ready(void* decoder); +InfraredMessage* infrared_decoder_rca_decode(void* decoder, bool level, uint32_t duration); + +void* infrared_encoder_rca_alloc(void); +InfraredStatus infrared_encoder_rca_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void infrared_encoder_rca_reset(void* encoder_ptr, const InfraredMessage* message); +void infrared_encoder_rca_free(void* encoder_ptr); + +const InfraredProtocolVariant* infrared_protocol_rca_get_variant(InfraredProtocol protocol); diff --git a/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h new file mode 100644 index 000000000..9ec4fe3b1 --- /dev/null +++ b/lib/infrared/encoder_decoder/rca/infrared_protocol_rca_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "../common/infrared_common_i.h" + +#define INFRARED_RCA_PREAMBLE_MARK 4000 +#define INFRARED_RCA_PREAMBLE_SPACE 4000 +#define INFRARED_RCA_BIT1_MARK 500 +#define INFRARED_RCA_BIT1_SPACE 2000 +#define INFRARED_RCA_BIT0_MARK 500 +#define INFRARED_RCA_BIT0_SPACE 1000 +#define INFRARED_RCA_REPEAT_PERIOD 8000 +#define INFRARED_RCA_SILENCE INFRARED_RCA_REPEAT_PERIOD + +#define INFRARED_RCA_MIN_SPLIT_TIME INFRARED_RCA_REPEAT_PAUSE_MIN +#define INFRARED_RCA_REPEAT_PAUSE_MIN 4000 +#define INFRARED_RCA_REPEAT_PAUSE_MAX 150000 +#define INFRARED_RCA_REPEAT_COUNT_MIN 1 +#define INFRARED_RCA_REPEAT_MARK INFRARED_RCA_PREAMBLE_MARK +#define INFRARED_RCA_REPEAT_SPACE INFRARED_RCA_PREAMBLE_SPACE +#define INFRARED_RCA_PREAMBLE_TOLERANCE 200 // us +#define INFRARED_RCA_BIT_TOLERANCE 120 // us + +extern const InfraredCommonProtocolSpec infrared_protocol_rca; + +bool infrared_decoder_rca_interpret(InfraredCommonDecoder* decoder); +InfraredStatus infrared_decoder_rca_decode_repeat(InfraredCommonDecoder* decoder); +InfraredStatus infrared_encoder_rca_encode_repeat( + InfraredCommonEncoder* encoder, + uint32_t* duration, + bool* level); From 77cec31cc98b28e51e05297b77edf1fb36112c3f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 10 Jun 2023 23:43:17 +0100 Subject: [PATCH 137/370] Some badkb cleanup --- applications/main/bad_kb/bad_kb_app.h | 1 - applications/main/bad_kb/helpers/ducky_script.h | 2 +- applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 662e05181..19d08000b 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -1,6 +1,5 @@ #pragma once -#include "bad_kb_app.h" #include "scenes/bad_kb_scene.h" #include "helpers/ducky_script.h" diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index 9e4ed77f4..cbfc6c8c8 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -121,7 +121,7 @@ typedef enum { } BadKbAppError; typedef struct { - char bt_name[BAD_KB_ADV_NAME_MAX_LEN + 1]; + char bt_name[BAD_KB_ADV_NAME_MAX_LEN]; uint8_t bt_mac[BAD_KB_MAC_ADDRESS_LEN]; FuriHalUsbInterface* usb_mode; GapPairing bt_mode; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c index 17477b2c2..31d2b49ec 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c @@ -40,11 +40,9 @@ void bad_kb_scene_config_layout_on_enter(void* context) { bool bad_kb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); - // BadKbApp* bad_kb = context; return false; } void bad_kb_scene_config_layout_on_exit(void* context) { UNUSED(context); - // BadKbApp* bad_kb = context; } From 196748f97785fd7f40002ac7b3a77d7cd797bde3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:30:06 +0100 Subject: [PATCH 138/370] Add furi_hal_usb_get_config_context() --- firmware/targets/f7/api_symbols.csv | 1 + firmware/targets/f7/furi_hal/furi_hal_usb.c | 19 +++++++++++++++++++ .../targets/furi_hal_include/furi_hal_usb.h | 6 ++++++ 3 files changed, 26 insertions(+) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ca15ff430..23fe7ac3c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1499,6 +1499,7 @@ Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" Function,+,furi_hal_usb_disable,void, Function,+,furi_hal_usb_enable,void, Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, +Function,+,furi_hal_usb_get_config_context,void*, Function,-,furi_hal_usb_init,void, Function,+,furi_hal_usb_is_locked,_Bool, Function,+,furi_hal_usb_lock,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index b88168d5d..3f9f7cbee 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -17,6 +17,7 @@ typedef enum { UsbApiEventTypeSetConfig, UsbApiEventTypeGetConfig, + UsbApiEventTypeGetConfigContext, UsbApiEventTypeLock, UsbApiEventTypeUnlock, UsbApiEventTypeIsLocked, @@ -168,6 +169,21 @@ FuriHalUsbInterface* furi_hal_usb_get_config() { return return_data.void_value; } +void* furi_hal_usb_get_config_context() { + UsbApiEventReturnData return_data = { + .void_value = NULL, + }; + + UsbApiEventMessage msg = { + .lock = api_lock_alloc_locked(), + .type = UsbApiEventTypeGetConfigContext, + .return_data = &return_data, + }; + + furi_hal_usb_send_message(&msg); + return return_data.void_value; +} + void furi_hal_usb_lock() { UsbApiEventMessage msg = { .lock = api_lock_alloc_locked(), @@ -411,6 +427,9 @@ static void usb_process_message(UsbApiEventMessage* message) { case UsbApiEventTypeGetConfig: message->return_data->void_value = usb.interface; break; + case UsbApiEventTypeGetConfigContext: + message->return_data->void_value = usb.interface_context; + break; case UsbApiEventTypeLock: FURI_LOG_I(TAG, "Mode lock"); usb.mode_lock = true; diff --git a/firmware/targets/furi_hal_include/furi_hal_usb.h b/firmware/targets/furi_hal_include/furi_hal_usb.h index 8b49f6c65..2affb3d6d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb.h @@ -56,6 +56,12 @@ bool furi_hal_usb_set_config(FuriHalUsbInterface* new_if, void* ctx); */ FuriHalUsbInterface* furi_hal_usb_get_config(); +/** Get USB device configuration context + * + * @return current USB device context + */ +void* furi_hal_usb_get_config_context(); + /** Lock USB device mode switch */ void furi_hal_usb_lock(); From 6090ef705e27ee7f014f22f479e4b8c2116b2d02 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:30:52 +0100 Subject: [PATCH 139/370] Simplify furi_hal_bt MAC address logic --- firmware/targets/f7/furi_hal/furi_hal_bt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 1a57b07c8..5fe13732f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -217,14 +217,13 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, config->adv_service_uuid |= furi_hal_version_get_hw_color(); } else if(profile == FuriHalBtProfileHidKeyboard) { // Change MAC address for HID profile + const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); uint8_t empty_mac[sizeof(config->mac_address)] = FURI_HAL_BT_EMPTY_MAC_ADDR; uint8_t default_mac[sizeof(config->mac_address)] = FURI_HAL_BT_DEFAULT_MAC_ADDR; - const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); if(memcmp(config->mac_address, empty_mac, sizeof(config->mac_address)) == 0 || + memcmp(config->mac_address, normal_mac, sizeof(config->mac_address)) == 0 || memcmp(config->mac_address, default_mac, sizeof(config->mac_address)) == 0) { memcpy(config->mac_address, normal_mac, sizeof(config->mac_address)); - } - if(memcmp(config->mac_address, normal_mac, sizeof(config->mac_address)) == 0) { config->mac_address[2]++; } // Change name Flipper -> Control From 4ceed8791e280a032b3ecfcfbbe8a9c22f04b518 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:32:23 +0100 Subject: [PATCH 140/370] BadKB redraw UI twice as often --- applications/main/bad_kb/bad_kb_app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 1bcfc57f8..abbf8f304 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -155,7 +155,7 @@ BadKbApp* bad_kb_app_alloc(char* arg) { view_dispatcher_set_event_callback_context(app->view_dispatcher, app); view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_kb_app_tick_event_callback, 500); + app->view_dispatcher, bad_kb_app_tick_event_callback, 250); view_dispatcher_set_custom_event_callback( app->view_dispatcher, bad_kb_app_custom_event_callback); view_dispatcher_set_navigation_event_callback( From b0515d986ee9f67fd8126f6234f415e63e9b87be Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:43:38 +0100 Subject: [PATCH 141/370] More BadKB cleanup --- applications/main/bad_kb/bad_kb_app.c | 21 +++++++++++-------- applications/main/bad_kb/bad_kb_app.h | 10 ++++----- .../main/bad_kb/scenes/bad_kb_scene_config.c | 2 +- .../bad_kb/scenes/bad_kb_scene_config_mac.c | 2 +- .../bad_kb/scenes/bad_kb_scene_config_name.c | 6 +++--- .../main/bad_kb/scenes/bad_kb_scene_error.c | 2 +- .../bad_kb/scenes/bad_kb_scene_file_select.c | 2 -- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index abbf8f304..6dda97805 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -170,11 +170,13 @@ BadKbApp* bad_kb_app_alloc(char* arg) { // Custom Widget app->widget = widget_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewError, widget_get_view(app->widget)); + app->view_dispatcher, BadKbAppViewWidget, widget_get_view(app->widget)); app->var_item_list = variable_item_list_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewConfig, variable_item_list_get_view(app->var_item_list)); + app->view_dispatcher, + BadKbAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); app->bad_kb_view = bad_kb_alloc(); view_dispatcher_add_view( @@ -182,11 +184,11 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->text_input = text_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewConfigName, text_input_get_view(app->text_input)); + app->view_dispatcher, BadKbAppViewTextInput, text_input_get_view(app->text_input)); app->byte_input = byte_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewConfigMac, byte_input_get_view(app->byte_input)); + app->view_dispatcher, BadKbAppViewByteInput, byte_input_get_view(app->byte_input)); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); @@ -219,30 +221,31 @@ void bad_kb_app_free(BadKbApp* app) { bad_kb_free(app->bad_kb_view); // Custom Widget - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewError); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWidget); widget_free(app->widget); // Variable item list - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfig); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewVarItemList); variable_item_list_free(app->var_item_list); // Text Input - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigName); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewTextInput); text_input_free(app->text_input); // Byte Input - view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigMac); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewByteInput); byte_input_free(app->byte_input); // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); - // Restore bt config + // Restore connection config app->bt->suppress_pin_screen = false; if(app->conn_init_thread) { furi_thread_join(app->conn_init_thread); furi_thread_free(app->conn_init_thread); + app->conn_init_thread = NULL; } bad_kb_conn_reset(app); diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 19d08000b..dec18e6ae 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -13,17 +13,17 @@ #define BAD_KB_APP_LAYOUT_EXTENSION ".kl" typedef enum BadKbCustomEvent { - BadKbAppCustomEventTextEditResult, + BadKbAppCustomEventTextInputDone, BadKbAppCustomEventByteInputDone, BadKbCustomEventErrorBack } BadKbCustomEvent; typedef enum { - BadKbAppViewError, + BadKbAppViewWidget, BadKbAppViewWork, - BadKbAppViewConfig, - BadKbAppViewConfigMac, - BadKbAppViewConfigName + BadKbAppViewVarItemList, + BadKbAppViewByteInput, + BadKbAppViewTextInput } BadKbAppView; void bad_kb_config_switch_remember_mode(BadKbApp* app); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index e029d052e..e8a8e3f51 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -83,7 +83,7 @@ void bad_kb_scene_config_on_enter(void* context) { variable_item_list_set_selected_item( var_item_list, scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfig)); - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfig); + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewVarItemList); } bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c index 2b30c90ac..e49fdbb56 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c @@ -21,7 +21,7 @@ void bad_kb_scene_config_mac_on_enter(void* context) { bad_kb, bad_kb->config.bt_mac, GAP_MAC_ADDR_SIZE); - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigMac); + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); } bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c index 40bfc4be8..fc8a8e22a 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c @@ -3,7 +3,7 @@ static void bad_kb_scene_config_name_text_input_callback(void* context) { BadKbApp* bad_kb = context; - view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextEditResult); + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); } void bad_kb_scene_config_name_on_enter(void* context) { @@ -20,7 +20,7 @@ void bad_kb_scene_config_name_on_enter(void* context) { BAD_KB_ADV_NAME_MAX_LEN, true); - view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigName); + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); } bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { @@ -29,7 +29,7 @@ bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; - if(event.event == BadKbAppCustomEventTextEditResult) { + if(event.event == BadKbAppCustomEventTextInputDone) { bt_set_profile_adv_name(bad_kb->bt, bad_kb->config.bt_name); } scene_manager_previous_scene(bad_kb->scene_manager); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_error.c b/applications/main/bad_kb/scenes/bad_kb_scene_error.c index eac04c448..e27329b9a 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_error.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_error.c @@ -27,7 +27,7 @@ void bad_kb_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", bad_kb_scene_error_event_callback, app); } - view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewError); + view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWidget); } bool bad_kb_scene_error_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c index 3a8c0748d..635b4f318 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c @@ -41,11 +41,9 @@ void bad_kb_scene_file_select_on_enter(void* context) { bool bad_kb_scene_file_select_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); - // BadKbApp* bad_kb = context; return false; } void bad_kb_scene_file_select_on_exit(void* context) { UNUSED(context); - // BadKbApp* bad_kb = context; } From c588cb67ec1d44d87586ca5a376b81de387204cd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:45:11 +0100 Subject: [PATCH 142/370] BadKB hide USB/BT label while loading --- applications/main/bad_kb/views/bad_kb_view.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index af7dd2709..8b2ba222a 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -18,9 +18,12 @@ typedef struct { static void bad_kb_draw_callback(Canvas* canvas, void* _model) { BadKbModel* model = _model; + BadKbWorkerState state = model->state.state; - FuriString* disp_str; - disp_str = furi_string_alloc_set(model->state.is_bt ? "(BT) " : "(USB) "); + FuriString* disp_str = furi_string_alloc_set( + state == BadKbStateInit ? "( . . . )" : + model->state.is_bt ? "(BT) " : + "(USB) "); furi_string_cat_str(disp_str, model->file_name); elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); @@ -42,7 +45,6 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); - BadKbWorkerState state = model->state.state; if((state == BadKbStateIdle) || (state == BadKbStateDone) || (state == BadKbStateNotConnected)) { if(XTREME_SETTINGS()->is_nsfw) { From 0e438f16959adf9458d8fc3d2699fde2e7a6f018 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:47:36 +0100 Subject: [PATCH 143/370] BadKB increase BT delays --- applications/main/bad_kb/helpers/ducky_script.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 9ada3ce1e..06f92e1d0 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -23,15 +23,13 @@ const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = #define BADKB_ASCII_TO_KEY(script, x) \ (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) -/** - * Delays for waiting between HID key press and key release -*/ +// Delays for waiting between HID key press and key release const uint8_t bt_hid_delays[LevelRssiNum] = { - 30, // LevelRssi122_100 - 25, // LevelRssi99_80 - 20, // LevelRssi79_60 - 17, // LevelRssi59_40 - 14, // LevelRssi39_0 + 45, // LevelRssi122_100 + 38, // LevelRssi99_80 + 30, // LevelRssi79_60 + 26, // LevelRssi59_40 + 21, // LevelRssi39_0 }; uint8_t bt_timeout = 0; @@ -767,7 +765,7 @@ static int32_t bad_kb_worker(void* context) { flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, FuriFlagWaitAny | FuriFlagNoClear, - 1500); + bad_kb->bt ? 3000 : 1500); if(flags == (unsigned)FuriFlagErrorTimeout) { // If nothing happened - start script execution worker_state = BadKbStateRunning; From 42d0b8a5e54d979e69a64fcfa6089646019cb063 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:49:48 +0100 Subject: [PATCH 144/370] BadKB add debug logging to worker thread --- .../main/bad_kb/helpers/ducky_script.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 06f92e1d0..29bc5b3d4 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -680,6 +680,7 @@ static int32_t bad_kb_worker(void* context) { while(1) { if(worker_state == BadKbStateInit) { // State: initialization + FURI_LOG_D(WORKER_TAG, "init start"); if(storage_file_open( script_file, furi_string_get_cstr(bad_kb->file_path), @@ -707,10 +708,13 @@ static int32_t bad_kb_worker(void* context) { worker_state = BadKbStateFileError; // File open error } bad_kb->st.state = worker_state; + FURI_LOG_D(WORKER_TAG, "init done"); } else if(worker_state == BadKbStateNotConnected) { // State: Not connected + FURI_LOG_D(WORKER_TAG, "not connected wait"); uint32_t flags = bad_kb_flags_get( WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "not connected flags: %lu", flags); if(flags & WorkerEvtEnd) { break; @@ -722,8 +726,10 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateIdle) { // State: ready to start + FURI_LOG_D(WORKER_TAG, "idle wait"); uint32_t flags = bad_kb_flags_get( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "idle flags: %lu", flags); if(flags & WorkerEvtEnd) { break; @@ -746,8 +752,10 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateWillRun) { // State: start on connection + FURI_LOG_D(WORKER_TAG, "will run wait"); uint32_t flags = bad_kb_flags_get( WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "will run flags: %lu", flags); if(flags & WorkerEvtEnd) { break; @@ -783,11 +791,13 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateRunning) { // State: running + FURI_LOG_D(WORKER_TAG, "running"); uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + FURI_LOG_D(WORKER_TAG, "running flags: %lu", flags); delay_val -= delay_cur; if(!(flags & FuriFlagError)) { @@ -856,9 +866,11 @@ static int32_t bad_kb_worker(void* context) { furi_check((flags & FuriFlagError) == 0); } } else if(worker_state == BadKbStateWaitForBtn) { // State: Wait for button Press + FURI_LOG_D(WORKER_TAG, "button wait"); uint32_t flags = bad_kb_flags_get( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "button flags: %lu", flags); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; @@ -877,9 +889,11 @@ static int32_t bad_kb_worker(void* context) { continue; } } else if(worker_state == BadKbStatePaused) { // State: Paused + FURI_LOG_D(WORKER_TAG, "paused wait"); uint32_t flags = bad_kb_flags_get( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, FuriWaitForever); + FURI_LOG_D(WORKER_TAG, "paused flags: %lu", flags); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { break; @@ -917,9 +931,11 @@ static int32_t bad_kb_worker(void* context) { continue; } } else if(worker_state == BadKbStateStringDelay) { // State: print string with delays + FURI_LOG_D(WORKER_TAG, "delay wait"); uint32_t flags = bad_kb_flags_get( WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, bad_kb->stringdelay); + FURI_LOG_D(WORKER_TAG, "delay flags: %lu", flags); if(!(flags & FuriFlagError)) { if(flags & WorkerEvtEnd) { @@ -958,8 +974,10 @@ static int32_t bad_kb_worker(void* context) { } else if( (worker_state == BadKbStateFileError) || (worker_state == BadKbStateScriptError)) { // State: error + FURI_LOG_D(WORKER_TAG, "error wait"); uint32_t flags = bad_kb_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command + FURI_LOG_D(WORKER_TAG, "error flags: %lu", flags); if(flags & WorkerEvtEnd) { break; From 15845602bf19a3fd500f06a9db2bdda0238513d4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 20:55:29 +0100 Subject: [PATCH 145/370] BadKB edit name and mac in temp buffers + cleanup --- .../main/bad_kb/helpers/ducky_script.h | 2 ++ .../bad_kb/scenes/bad_kb_scene_config_mac.c | 20 +++++++++++-------- .../bad_kb/scenes/bad_kb_scene_config_name.c | 5 ++++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index cbfc6c8c8..5f3e372ed 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -143,6 +143,8 @@ struct BadKbApp { VariableItemList* var_item_list; TextInput* text_input; ByteInput* byte_input; + char bt_name_buf[BAD_KB_NAME_LEN]; + uint8_t bt_mac_buf[BAD_KB_MAC_LEN]; BadKbAppError error; FuriString* file_path; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c index e49fdbb56..bb0935285 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c @@ -10,17 +10,20 @@ void bad_kb_scene_config_mac_byte_input_callback(void* context) { void bad_kb_scene_config_mac_on_enter(void* context) { BadKbApp* bad_kb = context; - - // Setup view ByteInput* byte_input = bad_kb->byte_input; + + memmove(bad_kb->bt_mac_buf, bad_kb->config.bt_mac, GAP_MAC_ADDR_SIZE); + byte_input_set_header_text(byte_input, "Set BT MAC address"); + byte_input_set_result_callback( byte_input, bad_kb_scene_config_mac_byte_input_callback, NULL, bad_kb, - bad_kb->config.bt_mac, + bad_kb->bt_mac_buf, GAP_MAC_ADDR_SIZE); + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); } @@ -29,19 +32,20 @@ bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { + consumed = true; if(event.event == BadKbAppCustomEventByteInputDone) { + memmove(bad_kb->config.bt_mac, bad_kb->bt_mac_buf, GAP_MAC_ADDR_SIZE); bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); - scene_manager_previous_scene(bad_kb->scene_manager); - consumed = true; } + scene_manager_previous_scene(bad_kb->scene_manager); } return consumed; } void bad_kb_scene_config_mac_on_exit(void* context) { BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; - // Clear view - byte_input_set_result_callback(bad_kb->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(bad_kb->byte_input, ""); + byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(byte_input, ""); } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c index fc8a8e22a..ea92e74cc 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c @@ -10,13 +10,15 @@ void bad_kb_scene_config_name_on_enter(void* context) { BadKbApp* bad_kb = context; TextInput* text_input = bad_kb->text_input; + strlcpy(bad_kb->bt_name_buf, bad_kb->config.bt_name, BAD_KB_ADV_NAME_MAX_LEN); + text_input_set_header_text(text_input, "Set BT device name"); text_input_set_result_callback( text_input, bad_kb_scene_config_name_text_input_callback, bad_kb, - bad_kb->config.bt_name, + bad_kb->bt_name_buf, BAD_KB_ADV_NAME_MAX_LEN, true); @@ -30,6 +32,7 @@ bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == BadKbAppCustomEventTextInputDone) { + strlcpy(bad_kb->config.bt_name, bad_kb->bt_name_buf, BAD_KB_ADV_NAME_MAX_LEN); bt_set_profile_adv_name(bad_kb->bt, bad_kb->config.bt_name); } scene_manager_previous_scene(bad_kb->scene_manager); From 481631a8584dc3c3e85d76302784ad0c62d3631d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 21:00:28 +0100 Subject: [PATCH 146/370] BadKB shorter mac and name length constants --- applications/main/bad_kb/bad_kb_app.c | 9 +++--- .../main/bad_kb/helpers/ducky_script.c | 30 ++++++++----------- .../main/bad_kb/helpers/ducky_script.h | 12 ++++---- .../main/bad_kb/scenes/bad_kb_scene_config.c | 2 +- .../bad_kb/scenes/bad_kb_scene_config_mac.c | 6 ++-- .../bad_kb/scenes/bad_kb_scene_config_name.c | 6 ++-- 6 files changed, 29 insertions(+), 36 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 6dda97805..0ec3e0fc5 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -34,7 +34,7 @@ static void bad_kb_load_settings(BadKbApp* app) { memcpy( app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); + BAD_KB_MAC_LEN); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -49,11 +49,11 @@ static void bad_kb_load_settings(BadKbApp* app) { strcpy(app->config.bt_name, ""); } if(!flipper_format_read_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN)) { + file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_LEN)) { memcpy( app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); + BAD_KB_MAC_LEN); } furi_string_free(tmp_str); flipper_format_file_close(file); @@ -82,8 +82,7 @@ static void bad_kb_save_settings(BadKbApp* app) { if(flipper_format_file_open_always(file, BAD_KB_SETTINGS_PATH)) { flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name); - flipper_format_write_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN); + flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_LEN); flipper_format_file_close(file); } flipper_format_free(file); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 29bc5b3d4..92fd5de9a 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -12,10 +12,8 @@ #include #include -const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = - {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; -const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN] = - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_LEN] = {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; +const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_LEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; #define TAG "BadKB" #define WORKER_TAG TAG "Worker" @@ -333,11 +331,11 @@ static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) { static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { size_t line_len = strlen(line); - size_t mac_len = BAD_KB_MAC_ADDRESS_LEN * 3; + size_t mac_len = BAD_KB_MAC_LEN * 3; if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name - uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; - for(size_t i = 0; i < BAD_KB_MAC_ADDRESS_LEN; i++) { + uint8_t mac[BAD_KB_MAC_LEN]; + for(size_t i = 0; i < BAD_KB_MAC_LEN; i++) { char a = line[i * 3]; char b = line[i * 3 + 1]; if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || @@ -427,11 +425,11 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { bool reset_name = strncmp( bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), - BAD_KB_ADV_NAME_MAX_LEN); + BAD_KB_NAME_LEN); bool reset_mac = memcmp( bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); + BAD_KB_MAC_LEN); if(reset_name && reset_mac) { furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bt_name); } else if(reset_name) { @@ -576,7 +574,7 @@ int32_t bad_kb_conn_refresh(BadKbApp* app) { memcpy( app->prev_config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); + BAD_KB_MAC_LEN); app->prev_config.bt_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); bt_timeout = bt_hid_delays[LevelRssi39_0]; @@ -591,10 +589,8 @@ int32_t bad_kb_conn_refresh(BadKbApp* app) { furi_hal_bt_set_profile_pairing_method( FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); } else { - if(memcmp( - app->config.bt_mac, - (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, - BAD_KB_MAC_ADDRESS_LEN) != 0) { + if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_LEN) != + 0) { furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); } furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); @@ -605,13 +601,11 @@ int32_t bad_kb_conn_refresh(BadKbApp* app) { app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); } - if(memcmp( - app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == - 0) { + if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_LEN) == 0) { memcpy( app->config.bt_mac, furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); + BAD_KB_MAC_LEN); } if(app->bt_remember) { bt_enable_peer_key_update(app->bt); diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index 5f3e372ed..f9c82de96 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -109,20 +109,20 @@ void bad_kb_script_pause_resume(BadKbScript* bad_kb); BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); -#define BAD_KB_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define BAD_KB_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE +#define BAD_KB_NAME_LEN FURI_HAL_BT_ADV_NAME_LENGTH +#define BAD_KB_MAC_LEN GAP_MAC_ADDR_SIZE // this is the MAC address used when we do not forget paired device (BOUND STATE) -extern const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN]; -extern const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN]; +extern const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_LEN]; +extern const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_LEN]; typedef enum { BadKbAppErrorNoFiles, } BadKbAppError; typedef struct { - char bt_name[BAD_KB_ADV_NAME_MAX_LEN]; - uint8_t bt_mac[BAD_KB_MAC_ADDRESS_LEN]; + char bt_name[BAD_KB_NAME_LEN]; + uint8_t bt_mac[BAD_KB_MAC_LEN]; FuriHalUsbInterface* usb_mode; GapPairing bt_mode; } BadKbConfig; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index e8a8e3f51..292fcfb28 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -112,7 +112,7 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac); break; case VarItemListIndexRandomizeBtMac: - furi_hal_random_fill_buf(bad_kb->config.bt_mac, BAD_KB_MAC_ADDRESS_LEN); + furi_hal_random_fill_buf(bad_kb->config.bt_mac, BAD_KB_MAC_LEN); bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); break; default: diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c index bb0935285..eb273b36f 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c @@ -12,7 +12,7 @@ void bad_kb_scene_config_mac_on_enter(void* context) { BadKbApp* bad_kb = context; ByteInput* byte_input = bad_kb->byte_input; - memmove(bad_kb->bt_mac_buf, bad_kb->config.bt_mac, GAP_MAC_ADDR_SIZE); + memmove(bad_kb->bt_mac_buf, bad_kb->config.bt_mac, BAD_KB_MAC_LEN); byte_input_set_header_text(byte_input, "Set BT MAC address"); @@ -22,7 +22,7 @@ void bad_kb_scene_config_mac_on_enter(void* context) { NULL, bad_kb, bad_kb->bt_mac_buf, - GAP_MAC_ADDR_SIZE); + BAD_KB_MAC_LEN); view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); } @@ -34,7 +34,7 @@ bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == BadKbAppCustomEventByteInputDone) { - memmove(bad_kb->config.bt_mac, bad_kb->bt_mac_buf, GAP_MAC_ADDR_SIZE); + memmove(bad_kb->config.bt_mac, bad_kb->bt_mac_buf, BAD_KB_MAC_LEN); bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); } scene_manager_previous_scene(bad_kb->scene_manager); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c index ea92e74cc..b080033eb 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c @@ -10,7 +10,7 @@ void bad_kb_scene_config_name_on_enter(void* context) { BadKbApp* bad_kb = context; TextInput* text_input = bad_kb->text_input; - strlcpy(bad_kb->bt_name_buf, bad_kb->config.bt_name, BAD_KB_ADV_NAME_MAX_LEN); + strlcpy(bad_kb->bt_name_buf, bad_kb->config.bt_name, BAD_KB_NAME_LEN); text_input_set_header_text(text_input, "Set BT device name"); @@ -19,7 +19,7 @@ void bad_kb_scene_config_name_on_enter(void* context) { bad_kb_scene_config_name_text_input_callback, bad_kb, bad_kb->bt_name_buf, - BAD_KB_ADV_NAME_MAX_LEN, + BAD_KB_NAME_LEN, true); view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); @@ -32,7 +32,7 @@ bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == BadKbAppCustomEventTextInputDone) { - strlcpy(bad_kb->config.bt_name, bad_kb->bt_name_buf, BAD_KB_ADV_NAME_MAX_LEN); + strlcpy(bad_kb->config.bt_name, bad_kb->bt_name_buf, BAD_KB_NAME_LEN); bt_set_profile_adv_name(bad_kb->bt, bad_kb->config.bt_name); } scene_manager_previous_scene(bad_kb->scene_manager); From 6b886858c1783954317d115fdfea873bfaaed151 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 21:08:03 +0100 Subject: [PATCH 147/370] BadKB fix connect/disconnect detection edge cases --- .../main/bad_kb/helpers/ducky_script.c | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 92fd5de9a..6e87f2252 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -707,7 +707,8 @@ static int32_t bad_kb_worker(void* context) { } else if(worker_state == BadKbStateNotConnected) { // State: Not connected FURI_LOG_D(WORKER_TAG, "not connected wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriWaitForever); FURI_LOG_D(WORKER_TAG, "not connected flags: %lu", flags); if(flags & WorkerEvtEnd) { @@ -722,7 +723,8 @@ static int32_t bad_kb_worker(void* context) { } else if(worker_state == BadKbStateIdle) { // State: ready to start FURI_LOG_D(WORKER_TAG, "idle wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtDisconnect, FuriWaitForever); + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect, + FuriWaitForever); FURI_LOG_D(WORKER_TAG, "idle flags: %lu", flags); if(flags & WorkerEvtEnd) { @@ -748,7 +750,8 @@ static int32_t bad_kb_worker(void* context) { } else if(worker_state == BadKbStateWillRun) { // State: start on connection FURI_LOG_D(WORKER_TAG, "will run wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtStartStop, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, + FuriWaitForever); FURI_LOG_D(WORKER_TAG, "will run flags: %lu", flags); if(flags & WorkerEvtEnd) { @@ -788,7 +791,8 @@ static int32_t bad_kb_worker(void* context) { FURI_LOG_D(WORKER_TAG, "running"); uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); FURI_LOG_D(WORKER_TAG, "running flags: %lu", flags); @@ -862,7 +866,8 @@ static int32_t bad_kb_worker(void* context) { } else if(worker_state == BadKbStateWaitForBtn) { // State: Wait for button Press FURI_LOG_D(WORKER_TAG, "button wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, FuriWaitForever); FURI_LOG_D(WORKER_TAG, "button flags: %lu", flags); if(!(flags & FuriFlagError)) { @@ -885,7 +890,8 @@ static int32_t bad_kb_worker(void* context) { } else if(worker_state == BadKbStatePaused) { // State: Paused FURI_LOG_D(WORKER_TAG, "paused wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, FuriWaitForever); FURI_LOG_D(WORKER_TAG, "paused flags: %lu", flags); if(!(flags & FuriFlagError)) { @@ -927,7 +933,8 @@ static int32_t bad_kb_worker(void* context) { } else if(worker_state == BadKbStateStringDelay) { // State: print string with delays FURI_LOG_D(WORKER_TAG, "delay wait"); uint32_t flags = bad_kb_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtDisconnect, + WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | + WorkerEvtDisconnect, bad_kb->stringdelay); FURI_LOG_D(WORKER_TAG, "delay flags: %lu", flags); From e1c12282cc5fc95e73968017b659482f8aad71a7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 21:26:43 +0100 Subject: [PATCH 148/370] BadKB completely rework connection handling Proper distinction between user and ID/BT_ID configs All init and deinit handled centrally in a cleaner way Moved USB HID config to be handled this way too Changing config doesn't reload the script file, less error/crash prone Remember MAC is now serial MAC +2, like bt remote has serial MAC +1 --- applications/main/bad_kb/bad_kb_app.c | 40 +-- applications/main/bad_kb/bad_kb_app.h | 2 - .../main/bad_kb/helpers/ducky_script.c | 337 +++++++++--------- .../main/bad_kb/helpers/ducky_script.h | 34 +- .../main/bad_kb/scenes/bad_kb_scene_config.c | 26 +- .../bad_kb/scenes/bad_kb_scene_config_mac.c | 2 +- .../bad_kb/scenes/bad_kb_scene_config_name.c | 2 +- 7 files changed, 210 insertions(+), 233 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 0ec3e0fc5..1c41618de 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -89,41 +89,6 @@ static void bad_kb_save_settings(BadKbApp* app) { furi_record_close(RECORD_STORAGE); } -void bad_kb_reload_worker(BadKbApp* app) { - bad_kb_script_close(app->bad_kb_script); - app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); - bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); -} - -int32_t bad_kb_config_switch_mode(BadKbApp* app) { - if(!app->is_bt) furi_hal_bt_stop_advertising(); - XTREME_SETTINGS()->bad_bt = app->is_bt; - XTREME_SETTINGS_SAVE(); - bad_kb_reload_worker(app); - if(app->is_bt) furi_hal_bt_start_advertising(); - bad_kb_config_refresh_menu(app); - return 0; -} - -void bad_kb_config_refresh_menu(BadKbApp* app) { - scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); - scene_manager_previous_scene(app->scene_manager); -} - -void bad_kb_config_switch_remember_mode(BadKbApp* app) { - if(app->bt_remember) { - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); - bt_enable_peer_key_update(app->bt); - } else { - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - bt_set_profile_mac_address(app->bt, app->config.bt_mac); - bt_disable_peer_key_update(app->bt); - } - bad_kb_reload_worker(app); -} - BadKbApp* bad_kb_app_alloc(char* arg) { BadKbApp* app = malloc(sizeof(BadKbApp)); @@ -165,6 +130,8 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->bt->suppress_pin_screen = true; app->is_bt = XTREME_SETTINGS()->bad_bt; app->bt_remember = XTREME_SETTINGS()->bad_bt_remember; + memcpy(BAD_KB_BOUND_MAC, furi_hal_version_get_ble_mac(), BAD_KB_MAC_LEN); + BAD_KB_BOUND_MAC[2] += 2; // Custom Widget app->widget = widget_alloc(); @@ -193,7 +160,7 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->conn_mode = BadKbConnModeNone; app->conn_init_thread = - furi_thread_alloc_ex("BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_conn_refresh, app); + furi_thread_alloc_ex("BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_conn_apply, app); furi_thread_start(app->conn_init_thread); if(!furi_string_empty(app->file_path)) { app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); @@ -247,6 +214,7 @@ void bad_kb_app_free(BadKbApp* app) { app->conn_init_thread = NULL; } bad_kb_conn_reset(app); + if(app->hid_cfg) free(app->hid_cfg); // Close records furi_record_close(RECORD_GUI); diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index dec18e6ae..e2786d301 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -25,5 +25,3 @@ typedef enum { BadKbAppViewByteInput, BadKbAppViewTextInput } BadKbAppView; - -void bad_kb_config_switch_remember_mode(BadKbApp* app); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 6e87f2252..c95d47fa6 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -11,9 +11,13 @@ #include "ducky_script_i.h" #include #include +#include +#include "../scenes/bad_kb_scene.h" -const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_LEN] = {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; -const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_LEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t BAD_KB_EMPTY_MAC[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; + +// Adjusts to serial MAC +2 in app init +uint8_t BAD_KB_BOUND_MAC[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; #define TAG "BadKB" #define WORKER_TAG TAG "Worker" @@ -305,51 +309,51 @@ static int32_t ducky_parse_line(BadKbScript* bad_kb, FuriString* line) { } static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) { - if(sscanf(line, "%lX:%lX", &bad_kb->hid_cfg.vid, &bad_kb->hid_cfg.pid) == 2) { - bad_kb->hid_cfg.manuf[0] = '\0'; - bad_kb->hid_cfg.product[0] = '\0'; + FuriHalUsbHidConfig* cfg = &bad_kb->app->id_config.usb_cfg; + + if(sscanf(line, "%lX:%lX", &cfg->vid, &cfg->pid) == 2) { + cfg->manuf[0] = '\0'; + cfg->product[0] = '\0'; uint8_t id_len = ducky_get_command_len(line); if(!ducky_is_line_end(line[id_len + 1])) { - sscanf( - &line[id_len + 1], - "%31[^\r\n:]:%31[^\r\n]", - bad_kb->hid_cfg.manuf, - bad_kb->hid_cfg.product); + sscanf(&line[id_len + 1], "%31[^\r\n:]:%31[^\r\n]", cfg->manuf, cfg->product); } FURI_LOG_D( WORKER_TAG, - "set id: %04lX:%04lX mfr:%s product:%s", - bad_kb->hid_cfg.vid, - bad_kb->hid_cfg.pid, - bad_kb->hid_cfg.manuf, - bad_kb->hid_cfg.product); + "set usb id: %04lX:%04lX mfr:%s product:%s", + cfg->vid, + cfg->pid, + cfg->manuf, + cfg->product); return true; } return false; } static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { + BadKbConfig* cfg = &bad_kb->app->id_config; + size_t line_len = strlen(line); - size_t mac_len = BAD_KB_MAC_LEN * 3; + size_t mac_len = BAD_KB_MAC_LEN * 3; // 2 text chars + separator per byte if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name - uint8_t mac[BAD_KB_MAC_LEN]; for(size_t i = 0; i < BAD_KB_MAC_LEN; i++) { char a = line[i * 3]; char b = line[i * 3 + 1]; if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || - (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) { + (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &cfg->bt_mac[i])) { return false; } } - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len); - bt_set_profile_mac_address(bad_kb->bt, mac); + strlcpy(cfg->bt_name, line + mac_len, BAD_KB_NAME_LEN); + FURI_LOG_D(WORKER_TAG, "set bt id: %s", line); return true; } -static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { +static void ducky_script_preload(BadKbScript* bad_kb, File* script_file) { + BadKbApp* app = bad_kb->app; uint8_t ret = 0; uint32_t line_len = 0; @@ -376,81 +380,23 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { } } while(ret > 0); - const char* line_tmp = furi_string_get_cstr(bad_kb->line); - if(bad_kb->app->switch_mode_thread) { - furi_thread_join(bad_kb->app->switch_mode_thread); - furi_thread_free(bad_kb->app->switch_mode_thread); - bad_kb->app->switch_mode_thread = NULL; - } // Looking for ID or BT_ID command at first line - bad_kb->set_usb_id = false; - bad_kb->set_bt_id = false; - bad_kb->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; - bad_kb->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; - if(bad_kb->has_usb_id) { - if(bad_kb->bt) { - bad_kb->app->is_bt = false; - bad_kb->app->switch_mode_thread = furi_thread_alloc_ex( - "BadKbSwitchMode", - 1024, - (FuriThreadCallback)bad_kb_config_switch_mode, - bad_kb->app); - furi_thread_start(bad_kb->app->switch_mode_thread); - return false; - } - bad_kb->set_usb_id = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); - } else if(bad_kb->has_bt_id) { - if(!bad_kb->bt) { - bad_kb->app->is_bt = true; - bad_kb->app->switch_mode_thread = furi_thread_alloc_ex( - "BadKbSwitchMode", - 1024, - (FuriThreadCallback)bad_kb_config_switch_mode, - bad_kb->app); - furi_thread_start(bad_kb->app->switch_mode_thread); - return false; - } - if(!bad_kb->app->bt_remember) { - bad_kb->set_bt_id = ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); - } - } - bad_kb_config_refresh_menu(bad_kb->app); + const char* line_tmp = furi_string_get_cstr(bad_kb->line); + app->set_usb_id = false; + app->set_bt_id = false; + app->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; + app->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; - if(bad_kb->bt) { - if(!bad_kb->set_bt_id) { - const char* bt_name = bad_kb->app->config.bt_name; - const uint8_t* bt_mac = bad_kb->app->bt_remember ? - (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS : - bad_kb->app->config.bt_mac; - bool reset_name = strncmp( - bt_name, - furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), - BAD_KB_NAME_LEN); - bool reset_mac = memcmp( - bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_LEN); - if(reset_name && reset_mac) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bt_name); - } else if(reset_name) { - bt_set_profile_adv_name(bad_kb->bt, bt_name); - } - if(reset_mac) { - bt_set_profile_mac_address(bad_kb->bt, bt_mac); - } - } - } else { - if(bad_kb->set_usb_id) { - furi_check(furi_hal_usb_set_config(&usb_hid, &bad_kb->hid_cfg)); - } else { - furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); - } + if(app->has_usb_id) { + app->is_bt = false; + app->set_usb_id = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); + } else if(app->has_bt_id) { + app->is_bt = true; + app->set_bt_id = ducky_set_bt_id(bad_kb, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); } storage_file_seek(script_file, 0, true); furi_string_reset(bad_kb->line); - - return true; } static int32_t ducky_script_execute_next(BadKbScript* bad_kb, File* script_file) { @@ -562,64 +508,68 @@ static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) { return flags; } -int32_t bad_kb_conn_refresh(BadKbApp* app) { - bool bt = app->is_bt; +int32_t bad_kb_conn_apply(BadKbApp* app) { + if(app->is_bt) { + // Shorthands so this bs is readable + BadKbConfig* prev = &app->prev_config; + BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; + FuriHalBtProfile kbd = FuriHalBtProfileHidKeyboard; - if(app->conn_mode != BadKbConnModeNone) bad_kb_conn_reset(app); + // Save prev config + strcpy(prev->bt_name, furi_hal_bt_get_profile_adv_name(kbd)); + memcpy(prev->bt_mac, furi_hal_bt_get_profile_mac_addr(kbd), BAD_KB_MAC_LEN); + prev->bt_mode = furi_hal_bt_get_profile_pairing_method(kbd); - if(bt) { - strcpy( - app->prev_config.bt_name, - furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - memcpy( - app->prev_config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_LEN); - app->prev_config.bt_mode = - furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); + // Setup new config bt_timeout = bt_hid_delays[LevelRssi39_0]; bt_disconnect(app->bt); + furi_delay_ms(200); bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); - if(strcmp(app->config.bt_name, "") != 0) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); - } + furi_hal_bt_set_profile_adv_name(kbd, cfg->bt_name); if(app->bt_remember) { - furi_hal_bt_set_profile_mac_addr( - FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + furi_hal_bt_set_profile_mac_addr(kbd, BAD_KB_BOUND_MAC); + furi_hal_bt_set_profile_pairing_method(kbd, GapPairingPinCodeVerifyYesNo); } else { - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_LEN) != - 0) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); - } - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); + furi_hal_bt_set_profile_mac_addr(kbd, cfg->bt_mac); + furi_hal_bt_set_profile_pairing_method(kbd, GapPairingNone); } + + // Set profile, restart BT, adjust defaults furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); + + // What was empty is now adjusted by furi_hal_bt so save the new defaults if(strcmp(app->config.bt_name, "") == 0) { - strcpy( - app->config.bt_name, - furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - } - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_LEN) == 0) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_LEN); + strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(kbd)); } + + // Advertise even if BT is off in settings + furi_hal_bt_start_advertising(); + + // Toggle key callback after since BT restart resets it if(app->bt_remember) { bt_enable_peer_key_update(app->bt); } else { bt_disable_peer_key_update(app->bt); } - furi_hal_bt_start_advertising(); app->conn_mode = BadKbConnModeBt; } else { + // Save prev config (TODO: maybe also restore config context?) app->prev_config.usb_mode = furi_hal_usb_get_config(); + + // Unlock RPC connections furi_hal_usb_unlock(); - furi_check(furi_hal_usb_set_config(NULL, NULL)); + + // Context will apply with set_config only if pointer address is different, so we use a copy + FuriHalUsbHidConfig* hid_cfg = malloc(sizeof(FuriHalUsbHidConfig)); + memcpy( + hid_cfg, + app->set_usb_id ? &app->id_config.usb_cfg : &app->config.usb_cfg, + sizeof(FuriHalUsbHidConfig)); + furi_check(furi_hal_usb_set_config(&usb_hid, hid_cfg)); + if(app->hid_cfg) free(app->hid_cfg); + app->hid_cfg = hid_cfg; app->conn_mode = BadKbConnModeUsb; } @@ -630,6 +580,7 @@ int32_t bad_kb_conn_refresh(BadKbApp* app) { void bad_kb_conn_reset(BadKbApp* app) { if(app->conn_mode == BadKbConnModeBt) { bt_disconnect(app->bt); + furi_delay_ms(200); bt_keys_storage_set_default_path(app->bt); furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); @@ -645,6 +596,101 @@ void bad_kb_conn_reset(BadKbApp* app) { app->conn_mode = BadKbConnModeNone; } +void bad_kb_config_refresh(BadKbApp* app) { + bt_set_status_changed_callback(app->bt, NULL, NULL); + furi_hal_hid_set_state_callback(NULL, NULL); + if(app->bad_kb_script) { + furi_thread_flags_set(furi_thread_get_id(app->bad_kb_script->thread), WorkerEvtDisconnect); + } + if(app->conn_init_thread) { + furi_thread_join(app->conn_init_thread); + } + + bool apply = false; + if(app->is_bt) { + BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; + + // MAC is adjusted by furi_hal_bt, adjust here too so it matches after applying + const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); + uint8_t empty_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; + uint8_t default_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_DEFAULT_MAC_ADDR; + if(memcmp(cfg->bt_mac, empty_mac, BAD_KB_MAC_LEN) == 0 || + memcmp(cfg->bt_mac, normal_mac, BAD_KB_MAC_LEN) == 0 || + memcmp(cfg->bt_mac, default_mac, BAD_KB_MAC_LEN) == 0) { + memcpy(cfg->bt_mac, normal_mac, BAD_KB_MAC_LEN); + cfg->bt_mac[2]++; + } + + if(app->conn_mode != BadKbConnModeBt) { + apply = true; + bad_kb_conn_reset(app); + } else { + apply = apply || strncmp( + cfg->bt_name, + furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), + BAD_KB_NAME_LEN); + apply = apply || memcmp( + app->bt_remember ? BAD_KB_BOUND_MAC : cfg->bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_LEN); + } + } else { + FuriHalUsbHidConfig* cfg = app->set_usb_id ? &app->id_config.usb_cfg : + &app->config.usb_cfg; + + if(app->conn_mode != BadKbConnModeUsb) { + apply = true; + bad_kb_conn_reset(app); + } else { + void* ctx; + if(furi_hal_usb_get_config() == &usb_hid && + (ctx = furi_hal_usb_get_config_context()) != NULL) { + FuriHalUsbHidConfig* cur = ctx; + apply = apply || cfg->vid != cur->vid; + apply = apply || cfg->pid != cur->pid; + apply = apply || strncmp(cfg->manuf, cur->manuf, sizeof(cur->manuf)); + apply = apply || strncmp(cfg->product, cur->product, sizeof(cur->product)); + } else { + apply = true; + } + } + } + + if(apply) { + bad_kb_conn_apply(app); + } + + if(app->bad_kb_script) { + BadKbScript* script = app->bad_kb_script; + script->st.is_bt = app->is_bt; + script->bt = app->is_bt ? app->bt : NULL; + bool connected; + if(app->is_bt) { + bt_set_status_changed_callback(app->bt, bad_kb_bt_hid_state_callback, script); + connected = furi_hal_bt_is_connected(); + } else { + furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, script); + connected = furi_hal_hid_is_connected(); + } + if(connected) { + furi_thread_flags_set(furi_thread_get_id(script->thread), WorkerEvtConnect); + } + } + + // Reload config page + scene_manager_next_scene(app->scene_manager, BadKbSceneConfig); + scene_manager_previous_scene(app->scene_manager); + + // Update settings + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + if(xtreme_settings->bad_bt != app->is_bt || + xtreme_settings->bad_bt_remember != app->bt_remember) { + xtreme_settings->bad_bt = app->is_bt; + xtreme_settings->bad_bt_remember = app->bt_remember; + XTREME_SETTINGS_SAVE(); + } +} + static int32_t bad_kb_worker(void* context) { BadKbScript* bad_kb = context; @@ -658,20 +704,6 @@ static int32_t bad_kb_worker(void* context) { bad_kb->line_prev = furi_string_alloc(); bad_kb->string_print = furi_string_alloc(); - if(bad_kb->app->conn_init_thread) { - furi_thread_join(bad_kb->app->conn_init_thread); - furi_thread_free(bad_kb->app->conn_init_thread); - bad_kb->app->conn_init_thread = NULL; - } - - if(bad_kb->bt) { - if(bad_kb->app->conn_mode != BadKbConnModeBt) bad_kb_conn_refresh(bad_kb->app); - bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb); - } else { - if(bad_kb->app->conn_mode != BadKbConnModeUsb) bad_kb_conn_refresh(bad_kb->app); - furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, bad_kb); - } - while(1) { if(worker_state == BadKbStateInit) { // State: initialization FURI_LOG_D(WORKER_TAG, "init start"); @@ -680,20 +712,10 @@ static int32_t bad_kb_worker(void* context) { furi_string_get_cstr(bad_kb->file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) { - if(bad_kb->bt) { - if(furi_hal_bt_is_connected()) { - worker_state = BadKbStateIdle; // Ready to run - } else { - worker_state = BadKbStateNotConnected; // Not connected - } - } else { - if(furi_hal_hid_is_connected()) { - worker_state = BadKbStateIdle; // Ready to run - } else { - worker_state = BadKbStateNotConnected; // Not connected - } - } + ducky_script_preload(bad_kb, script_file); + if(bad_kb->st.line_nb > 0) { + bad_kb_config_refresh(bad_kb->app); + worker_state = BadKbStateNotConnected; // Refresh will set connected flag } else { worker_state = BadKbStateScriptError; // Script preload error } @@ -989,11 +1011,8 @@ static int32_t bad_kb_worker(void* context) { } } - if(bad_kb->bt) { - bt_set_status_changed_callback(bad_kb->bt, NULL, NULL); - } else { - furi_hal_hid_set_state_callback(NULL, NULL); - } + bt_set_status_changed_callback(bad_kb->app->bt, NULL, NULL); + furi_hal_hid_set_state_callback(NULL, NULL); storage_file_close(script_file); storage_file_free(script_file); diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index f9c82de96..fa4f1df86 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -61,7 +61,6 @@ struct BadKbState { typedef struct BadKbApp BadKbApp; typedef struct { - FuriHalUsbHidConfig hid_cfg; FuriThread* thread; BadKbState st; @@ -84,11 +83,6 @@ typedef struct { FuriString* string_print; size_t string_print_pos; - bool set_usb_id; - bool set_bt_id; - bool has_usb_id; - bool has_bt_id; - Bt* bt; BadKbApp* app; } BadKbScript; @@ -112,19 +106,19 @@ BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); #define BAD_KB_NAME_LEN FURI_HAL_BT_ADV_NAME_LENGTH #define BAD_KB_MAC_LEN GAP_MAC_ADDR_SIZE -// this is the MAC address used when we do not forget paired device (BOUND STATE) -extern const uint8_t BAD_KB_BOUND_MAC_ADDRESS[BAD_KB_MAC_LEN]; -extern const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_LEN]; +extern const uint8_t BAD_KB_EMPTY_MAC[BAD_KB_MAC_LEN]; +extern uint8_t BAD_KB_BOUND_MAC[BAD_KB_MAC_LEN]; // For remember mode typedef enum { BadKbAppErrorNoFiles, } BadKbAppError; typedef struct { + GapPairing bt_mode; char bt_name[BAD_KB_NAME_LEN]; uint8_t bt_mac[BAD_KB_MAC_LEN]; FuriHalUsbInterface* usb_mode; - GapPairing bt_mode; + FuriHalUsbHidConfig usb_cfg; } BadKbConfig; typedef enum { @@ -155,22 +149,26 @@ struct BadKbApp { Bt* bt; bool is_bt; bool bt_remember; - BadKbConfig config; - BadKbConfig prev_config; + BadKbConfig config; // User options (TODO: allow users to change usb cfg) + BadKbConfig id_config; // ID and BT_ID values + BadKbConfig prev_config; // State to restore at exit + bool set_usb_id; + bool set_bt_id; + bool has_usb_id; + bool has_bt_id; + + FuriHalUsbHidConfig* hid_cfg; BadKbConnMode conn_mode; FuriThread* conn_init_thread; - FuriThread* switch_mode_thread; }; -int32_t bad_kb_config_switch_mode(BadKbApp* app); - -void bad_kb_config_refresh_menu(BadKbApp* app); - -int32_t bad_kb_conn_refresh(BadKbApp* app); +int32_t bad_kb_conn_apply(BadKbApp* app); void bad_kb_conn_reset(BadKbApp* app); +void bad_kb_config_refresh(BadKbApp* app); + #ifdef __cplusplus } #endif diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index 292fcfb28..7d8d4dca3 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -23,8 +23,6 @@ void bad_kb_scene_config_connection_callback(VariableItem* item) { void bad_kb_scene_config_bt_remember_callback(VariableItem* item) { BadKbApp* bad_kb = variable_item_get_context(item); bad_kb->bt_remember = variable_item_get_current_value_index(item); - XTREME_SETTINGS()->bad_bt_remember = bad_kb->bt_remember; - XTREME_SETTINGS_SAVE(); variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexBtRemember); } @@ -45,9 +43,9 @@ void bad_kb_scene_config_on_enter(void* context) { var_item_list, "Connection", 2, bad_kb_scene_config_connection_callback, bad_kb); variable_item_set_current_value_index(item, bad_kb->is_bt); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); - if(bad_kb->bad_kb_script->has_usb_id) { + if(bad_kb->has_usb_id) { variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nUSB Mode!"); - } else if(bad_kb->bad_kb_script->has_bt_id) { + } else if(bad_kb->has_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nBT Mode!"); } @@ -58,21 +56,21 @@ void bad_kb_scene_config_on_enter(void* context) { variable_item_set_current_value_text(item, bad_kb->bt_remember ? "ON" : "OFF"); item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_kb); - if(bad_kb->bad_kb_script->set_bt_id) { + if(bad_kb->set_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!"); } item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_kb); if(bad_kb->bt_remember) { variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } else if(bad_kb->bad_kb_script->set_bt_id) { + } else if(bad_kb->set_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); } item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_kb); if(bad_kb->bt_remember) { variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } else if(bad_kb->bad_kb_script->set_bt_id) { + } else if(bad_kb->set_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); } } @@ -97,14 +95,6 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { case VarItemListIndexKeyboardLayout: scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); break; - case VarItemListIndexConnection: - bad_kb_config_switch_mode(bad_kb); - break; - case VarItemListIndexBtRemember: - bad_kb_config_switch_remember_mode(bad_kb); - scene_manager_previous_scene(bad_kb->scene_manager); - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfig); - break; case VarItemListIndexBtDeviceName: scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName); break; @@ -113,7 +103,11 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { break; case VarItemListIndexRandomizeBtMac: furi_hal_random_fill_buf(bad_kb->config.bt_mac, BAD_KB_MAC_LEN); - bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); + /* fall through */ + case VarItemListIndexBtRemember: + /* fall through */ + case VarItemListIndexConnection: + bad_kb_config_refresh(bad_kb); break; default: break; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c index eb273b36f..7d7c5e57f 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c @@ -35,7 +35,7 @@ bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == BadKbAppCustomEventByteInputDone) { memmove(bad_kb->config.bt_mac, bad_kb->bt_mac_buf, BAD_KB_MAC_LEN); - bt_set_profile_mac_address(bad_kb->bt, bad_kb->config.bt_mac); + bad_kb_config_refresh(bad_kb); } scene_manager_previous_scene(bad_kb->scene_manager); } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c index b080033eb..fc541490b 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c @@ -33,7 +33,7 @@ bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == BadKbAppCustomEventTextInputDone) { strlcpy(bad_kb->config.bt_name, bad_kb->bt_name_buf, BAD_KB_NAME_LEN); - bt_set_profile_adv_name(bad_kb->bt, bad_kb->config.bt_name); + bad_kb_config_refresh(bad_kb); } scene_manager_previous_scene(bad_kb->scene_manager); } From b80fc6caf154f3aae4c2b6e28ae19c32210d29d9 Mon Sep 17 00:00:00 2001 From: AloneLiberty <111039319+AloneLiberty@users.noreply.github.com> Date: Sat, 10 Jun 2023 14:52:42 +0300 Subject: [PATCH 149/370] NFC: Improvements to NFC Magic app Ability to write gen1b tags (ignore 0x43) Ability to write gen1 7 byte UID tags Fix detection of non magic cards --- applications/external/nfc_magic/nfc_magic_i.h | 1 + .../external/nfc_magic/nfc_magic_worker.c | 66 ++++++++++--------- .../scenes/nfc_magic_scene_file_select.c | 2 +- .../scenes/nfc_magic_scene_not_magic.c | 3 +- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/applications/external/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h index 4d6b89103..88bc5706f 100644 --- a/applications/external/nfc_magic/nfc_magic_i.h +++ b/applications/external/nfc_magic/nfc_magic_i.h @@ -46,6 +46,7 @@ enum NfcMagicCustomEvent { struct NfcMagicDevice { MagicType type; uint32_t cuid; + uint8_t uid_len; uint32_t password; }; diff --git a/applications/external/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c index c55f4593a..86f521b2b 100644 --- a/applications/external/nfc_magic/nfc_magic_worker.c +++ b/applications/external/nfc_magic/nfc_magic_worker.c @@ -110,14 +110,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { } magic_activate(); if(magic_gen1_wupa()) { - if(!magic_gen1_data_access_cmd()) { - FURI_LOG_E( - TAG, "No card response to data access command (not a magic card)"); - nfc_magic_worker->callback( - NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); - done = true; - break; - } + magic_gen1_data_access_cmd(); MfClassicData* mfc_data = &dev_data->mf_classic_data; for(size_t i = 0; i < 64; i++) { @@ -299,6 +292,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { } void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { + FuriHalNfcDevData nfc_data = {}; NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; bool card_found_notified = false; uint8_t gen4_config[MAGIC_GEN4_CONFIG_LEN]; @@ -313,32 +307,44 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { card_found_notified = true; } - furi_hal_nfc_activate_nfca(200, &magic_dev->cuid); - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - break; - } - - magic_deactivate(); - furi_delay_ms(300); - magic_activate(); - - furi_hal_nfc_activate_nfca(200, &magic_dev->cuid); - if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) { - magic_dev->type = MagicTypeGen4; - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_dev->cuid = nfc_data.cuid; + magic_dev->uid_len = nfc_data.uid_len; + } else { + // wrong BCC + magic_dev->uid_len = 4; } - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); break; - } + } else { + magic_deactivate(); + magic_activate(); + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_dev->cuid = nfc_data.cuid; + magic_dev->uid_len = nfc_data.uid_len; + if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) { + magic_dev->type = MagicTypeGen4; + if(!card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + } - if(card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; + nfc_magic_worker->callback( + NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + } else { + nfc_magic_worker->callback( + NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); + card_found_notified = true; + } + break; + } else { + if(card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; + } + } } magic_deactivate(); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c index baa6bcccc..04b7024ff 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c @@ -8,7 +8,7 @@ static bool nfc_magic_scene_file_select_is_file_suitable(NfcMagic* nfc_magic) { case MagicTypeClassicDirectWrite: case MagicTypeClassicAPDU: if((nfc_dev->dev_data.mf_classic_data.type != MfClassicType1k) || - (nfc_dev->dev_data.nfc_data.uid_len != 4)) { + (nfc_dev->dev_data.nfc_data.uid_len != nfc_magic->dev->uid_len)) { return false; } return true; diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c index b87f7f383..b4f579f44 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c @@ -13,11 +13,10 @@ void nfc_magic_scene_not_magic_on_enter(void* context) { notification_message(nfc_magic->notifications, &sequence_error); - // widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); widget_add_string_multiline_element( - widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not a magic\ncard"); + widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not magic or unsupported\ncard"); widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_not_magic_widget_callback, nfc_magic); From f46640998513854faeba317d300e7485178344d7 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Tue, 6 Jun 2023 22:34:06 +0200 Subject: [PATCH 150/370] Fix M*LIB usage: * Fix oplist definition of SubGhzFrequencyAnalyzerLogItem * Fix oplist definition of M_CSTR_DUP_OPLIST * Remove dependency of furi_string_utf8_decode to the internal definition of string_unicode_t * Replace obsolete macro M_IF_DEFAULT1 to M_DEFAULT_ARGS --- ...subghz_frequency_analyzer_log_item_array.h | 2 +- furi/core/string.c | 4 ++- furi/core/string.h | 33 +++++++++---------- lib/toolbox/m_cstr_dup.h | 5 +-- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h index b94ebe380..e9d4e407c 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -27,7 +27,7 @@ TUPLE_DEF2( (rssi_max, uint8_t)) /* Register globally the oplist */ #define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ - TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) + TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) /* Define the array, register the oplist and define further algorithms on it */ ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) diff --git a/furi/core/string.c b/furi/core/string.c index 4384fe06a..682c8d409 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -296,7 +296,9 @@ static FuriStringUTF8State state_to_furi_state(m_str1ng_utf8_state_e state) { } void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode) { + string_unicode_t m_u = *unicode; m_str1ng_utf8_state_e m_state = furi_state_to_state(*state); - m_str1ng_utf8_decode(c, &m_state, unicode); + m_str1ng_utf8_decode(c, &m_state, &m_u); *state = state_to_furi_state(m_state); + *unicode = m_u; } diff --git a/furi/core/string.h b/furi/core/string.h index 0523d3ba0..521aa9993 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -633,20 +633,18 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Search for a string (or C string) in a string * (string, [c]string[, start=0]) */ -#define furi_string_search(v, ...) \ +#define furi_string_search(...) \ M_APPLY( \ FURI_STRING_SELECT3, \ furi_string_search, \ furi_string_search_str, \ - v, \ - M_IF_DEFAULT1(0, __VA_ARGS__)) - + M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Search for a C string in a string * (string, cstring[, start=0]) */ -#define furi_string_search_str(v, ...) \ - M_APPLY(furi_string_search_str, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_str( ...) \ + furi_string_search_str( M_DEFAULT_ARGS(3, (0), __VA_ARGS__) ) /** * @brief Test if the string starts with the given string (or C string). @@ -672,41 +670,40 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Trim a string from the given set of characters (default is " \n\r\t"). * (string[, set=" \n\r\t"]) */ -#define furi_string_trim(...) M_APPLY(furi_string_trim, M_IF_DEFAULT1(" \n\r\t", __VA_ARGS__)) +#define furi_string_trim(...) \ + furi_string_trim( M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__) ) /** * @brief Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_char(v, ...) \ - M_APPLY(furi_string_search_char, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_char(...) \ + furi_string_search_char( M_DEFAULT_ARGS(3, (0), __VA_ARGS__) ) /** * @brief Reverse Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_rchar(v, ...) \ - M_APPLY(furi_string_search_rchar, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_rchar( ...) \ + furi_string_search_rchar( M_DEFAULT_ARGS(3, (0), __VA_ARGS__) ) /** * @brief Replace a string to another string (or C string to another C string) in a string. * (string, [c]string, [c]string[, start=0]) */ -#define furi_string_replace(a, b, ...) \ +#define furi_string_replace(...) \ M_APPLY( \ FURI_STRING_SELECT4, \ furi_string_replace, \ furi_string_replace_str, \ - a, \ - b, \ - M_IF_DEFAULT1(0, __VA_ARGS__)) + M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief Replace a C string to another C string in a string. * (string, cstring, cstring[, start=0]) */ -#define furi_string_replace_str(a, b, ...) \ - M_APPLY(furi_string_replace_str, a, b, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_replace_str(...) \ + furi_string_replace_str( M_DEFAULT_ARGS(4, (0), __VA_ARGS__) ) /** * @brief INIT OPLIST for FuriString. @@ -743,4 +740,4 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/toolbox/m_cstr_dup.h b/lib/toolbox/m_cstr_dup.h index 0555f72c6..b25e8aa77 100644 --- a/lib/toolbox/m_cstr_dup.h +++ b/lib/toolbox/m_cstr_dup.h @@ -2,12 +2,13 @@ #include #define M_INIT_DUP(a) ((a) = strdup("")) -#define M_SET_DUP(a, b) (M_CHECK_DEFAULT_TYPE(a), free((void*)a), (a) = strdup(b)) +#define M_INIT_SET_DUP(a, b) ( (a) = strdup(b)) +#define M_SET_DUP(a, b) (free((void*)a), (a) = strdup(b)) #define M_CLEAR_DUP(a) (free((void*)a)) #define M_CSTR_DUP_OPLIST \ (INIT(M_INIT_DUP), \ - INIT_SET(M_SET_DUP), \ + INIT_SET(M_INIT_SET_DUP), \ SET(M_SET_DUP), \ CLEAR(M_CLEAR_DUP), \ HASH(m_core_cstr_hash), \ From 576aecbb8a7ec98829c7d9a300a09993d28f6198 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 23:27:15 +0100 Subject: [PATCH 151/370] BadKB only save prev config at startup --- applications/main/bad_kb/bad_kb_app.c | 10 ++++++++++ applications/main/bad_kb/helpers/ducky_script.c | 10 +--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 1c41618de..f4c771691 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -130,6 +130,16 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->bt->suppress_pin_screen = true; app->is_bt = XTREME_SETTINGS()->bad_bt; app->bt_remember = XTREME_SETTINGS()->bad_bt_remember; + + // Save prev config + BadKbConfig* prev = &app->prev_config; + prev->usb_mode = furi_hal_usb_get_config(); + FuriHalBtProfile kbd = FuriHalBtProfileHidKeyboard; + prev->bt_mode = furi_hal_bt_get_profile_pairing_method(kbd); + strcpy(prev->bt_name, furi_hal_bt_get_profile_adv_name(kbd)); + memcpy(prev->bt_mac, furi_hal_bt_get_profile_mac_addr(kbd), BAD_KB_MAC_LEN); + + // Adjust BT remember MAC to be serial MAC +2 memcpy(BAD_KB_BOUND_MAC, furi_hal_version_get_ble_mac(), BAD_KB_MAC_LEN); BAD_KB_BOUND_MAC[2] += 2; diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index c95d47fa6..16e7d04fd 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -511,15 +511,9 @@ static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) { int32_t bad_kb_conn_apply(BadKbApp* app) { if(app->is_bt) { // Shorthands so this bs is readable - BadKbConfig* prev = &app->prev_config; BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; FuriHalBtProfile kbd = FuriHalBtProfileHidKeyboard; - // Save prev config - strcpy(prev->bt_name, furi_hal_bt_get_profile_adv_name(kbd)); - memcpy(prev->bt_mac, furi_hal_bt_get_profile_mac_addr(kbd), BAD_KB_MAC_LEN); - prev->bt_mode = furi_hal_bt_get_profile_pairing_method(kbd); - // Setup new config bt_timeout = bt_hid_delays[LevelRssi39_0]; bt_disconnect(app->bt); @@ -555,9 +549,6 @@ int32_t bad_kb_conn_apply(BadKbApp* app) { app->conn_mode = BadKbConnModeBt; } else { - // Save prev config (TODO: maybe also restore config context?) - app->prev_config.usb_mode = furi_hal_usb_get_config(); - // Unlock RPC connections furi_hal_usb_unlock(); @@ -590,6 +581,7 @@ void bad_kb_conn_reset(BadKbApp* app) { bt_enable_peer_key_update(app->bt); } else if(app->conn_mode == BadKbConnModeUsb) { + // TODO: maybe also restore USB config context? furi_check(furi_hal_usb_set_config(app->prev_config.usb_mode, NULL)); } From 004ca027737e0008826b9e014b4323dfdbb856a2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 11 Jun 2023 23:48:14 +0100 Subject: [PATCH 152/370] BadKB fix VID/PID handling + adjust cfg at start --- applications/main/bad_kb/bad_kb_app.c | 1 + .../main/bad_kb/helpers/ducky_script.c | 41 +++++++++++-------- .../main/bad_kb/helpers/ducky_script.h | 2 + .../targets/f7/furi_hal/furi_hal_usb_hid.c | 3 -- .../furi_hal_include/furi_hal_usb_hid.h | 3 ++ 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index f4c771691..987c35a93 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -130,6 +130,7 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->bt->suppress_pin_screen = true; app->is_bt = XTREME_SETTINGS()->bad_bt; app->bt_remember = XTREME_SETTINGS()->bad_bt_remember; + bad_kb_config_adjust(&app->config); // Save prev config BadKbConfig* prev = &app->prev_config; diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 16e7d04fd..7881be52d 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -588,6 +588,23 @@ void bad_kb_conn_reset(BadKbApp* app) { app->conn_mode = BadKbConnModeNone; } +void bad_kb_config_adjust(BadKbConfig* cfg) { + // MAC is adjusted by furi_hal_bt, adjust here too so it matches after applying + const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); + uint8_t empty_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; + uint8_t default_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_DEFAULT_MAC_ADDR; + if(memcmp(cfg->bt_mac, empty_mac, BAD_KB_MAC_LEN) == 0 || + memcmp(cfg->bt_mac, normal_mac, BAD_KB_MAC_LEN) == 0 || + memcmp(cfg->bt_mac, default_mac, BAD_KB_MAC_LEN) == 0) { + memcpy(cfg->bt_mac, normal_mac, BAD_KB_MAC_LEN); + cfg->bt_mac[2]++; + } + + // Use defaults if vid or pid are unset + if(cfg->usb_cfg.vid == 0) cfg->usb_cfg.vid = HID_VID_DEFAULT; + if(cfg->usb_cfg.pid == 0) cfg->usb_cfg.pid = HID_PID_DEFAULT; +} + void bad_kb_config_refresh(BadKbApp* app) { bt_set_status_changed_callback(app->bt, NULL, NULL); furi_hal_hid_set_state_callback(NULL, NULL); @@ -601,17 +618,7 @@ void bad_kb_config_refresh(BadKbApp* app) { bool apply = false; if(app->is_bt) { BadKbConfig* cfg = app->set_bt_id ? &app->id_config : &app->config; - - // MAC is adjusted by furi_hal_bt, adjust here too so it matches after applying - const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); - uint8_t empty_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; - uint8_t default_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_DEFAULT_MAC_ADDR; - if(memcmp(cfg->bt_mac, empty_mac, BAD_KB_MAC_LEN) == 0 || - memcmp(cfg->bt_mac, normal_mac, BAD_KB_MAC_LEN) == 0 || - memcmp(cfg->bt_mac, default_mac, BAD_KB_MAC_LEN) == 0) { - memcpy(cfg->bt_mac, normal_mac, BAD_KB_MAC_LEN); - cfg->bt_mac[2]++; - } + bad_kb_config_adjust(cfg); if(app->conn_mode != BadKbConnModeBt) { apply = true; @@ -627,8 +634,8 @@ void bad_kb_config_refresh(BadKbApp* app) { BAD_KB_MAC_LEN); } } else { - FuriHalUsbHidConfig* cfg = app->set_usb_id ? &app->id_config.usb_cfg : - &app->config.usb_cfg; + BadKbConfig* cfg = app->set_usb_id ? &app->id_config : &app->config; + bad_kb_config_adjust(cfg); if(app->conn_mode != BadKbConnModeUsb) { apply = true; @@ -638,10 +645,10 @@ void bad_kb_config_refresh(BadKbApp* app) { if(furi_hal_usb_get_config() == &usb_hid && (ctx = furi_hal_usb_get_config_context()) != NULL) { FuriHalUsbHidConfig* cur = ctx; - apply = apply || cfg->vid != cur->vid; - apply = apply || cfg->pid != cur->pid; - apply = apply || strncmp(cfg->manuf, cur->manuf, sizeof(cur->manuf)); - apply = apply || strncmp(cfg->product, cur->product, sizeof(cur->product)); + apply = apply || cfg->usb_cfg.vid != cur->vid; + apply = apply || cfg->usb_cfg.pid != cur->pid; + apply = apply || strncmp(cfg->usb_cfg.manuf, cur->manuf, sizeof(cur->manuf)); + apply = apply || strncmp(cfg->usb_cfg.product, cur->product, sizeof(cur->product)); } else { apply = true; } diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index fa4f1df86..b027c3f56 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -169,6 +169,8 @@ void bad_kb_conn_reset(BadKbApp* app); void bad_kb_config_refresh(BadKbApp* app); +void bad_kb_config_adjust(BadKbConfig* cfg); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index 334aa0102..a7a5742ef 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -12,9 +12,6 @@ #define HID_INTERVAL 2 -#define HID_VID_DEFAULT 0x046D -#define HID_PID_DEFAULT 0xC529 - struct HidIntfDescriptor { struct usb_interface_descriptor hid; struct usb_hid_descriptor hid_desc; diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h index 13e83ef67..b1e6da31b 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h @@ -9,6 +9,9 @@ extern "C" { #endif +#define HID_VID_DEFAULT 0x046D +#define HID_PID_DEFAULT 0xC529 + /** Max number of simultaneously pressed keys (keyboard) */ #define HID_KB_MAX_KEYS 6 /** Max number of simultaneously pressed keys (consumer control) */ From 1b2e37518782c10103eda04c3331d033163d67b1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 00:25:29 +0100 Subject: [PATCH 153/370] ... --- firmware/targets/furi_hal_include/furi_hal_usb_hid.h | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h index b1e6da31b..fe2486ceb 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h @@ -164,6 +164,7 @@ static const uint16_t hid_asciimap[] = { }; typedef struct { + // Good job knobheads, these should be uint16_t uint32_t vid; uint32_t pid; char manuf[32]; From c1e21783b21ff503df49e65dad04ca811b18aa9f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 01:43:46 +0100 Subject: [PATCH 154/370] BadKB better name and settings handling --- applications/main/bad_kb/bad_kb_app.c | 25 ++++++++----------- .../main/bad_kb/helpers/ducky_script.c | 10 ++++---- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 987c35a93..8d31f4da4 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -30,11 +30,9 @@ static void bad_kb_app_tick_event_callback(void* context) { static void bad_kb_load_settings(BadKbApp* app) { furi_string_reset(app->keyboard_layout); - strcpy(app->config.bt_name, ""); - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_LEN); + BadKbConfig* cfg = &app->config; + strcpy(cfg->bt_name, ""); + memcpy(cfg->bt_mac, BAD_KB_EMPTY_MAC, BAD_KB_MAC_LEN); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -44,16 +42,12 @@ static void bad_kb_load_settings(BadKbApp* app) { furi_string_reset(app->keyboard_layout); } if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) { - strcpy(app->config.bt_name, furi_string_get_cstr(tmp_str)); + strcpy(cfg->bt_name, furi_string_get_cstr(tmp_str)); } else { - strcpy(app->config.bt_name, ""); + strcpy(cfg->bt_name, ""); } - if(!flipper_format_read_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_LEN)) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_LEN); + if(!flipper_format_read_hex(file, "Bt_Mac", (uint8_t*)&cfg->bt_mac, BAD_KB_MAC_LEN)) { + memcpy(cfg->bt_mac, BAD_KB_EMPTY_MAC, BAD_KB_MAC_LEN); } furi_string_free(tmp_str); flipper_format_file_close(file); @@ -77,12 +71,13 @@ static void bad_kb_load_settings(BadKbApp* app) { } static void bad_kb_save_settings(BadKbApp* app) { + BadKbConfig* cfg = &app->config; Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); if(flipper_format_file_open_always(file, BAD_KB_SETTINGS_PATH)) { flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); - flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name); - flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_KB_MAC_LEN); + flipper_format_write_string_cstr(file, "Bt_Name", cfg->bt_name); + flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&cfg->bt_mac, BAD_KB_MAC_LEN); flipper_format_file_close(file); } flipper_format_free(file); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 7881be52d..04000771d 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -531,11 +531,6 @@ int32_t bad_kb_conn_apply(BadKbApp* app) { // Set profile, restart BT, adjust defaults furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); - // What was empty is now adjusted by furi_hal_bt so save the new defaults - if(strcmp(app->config.bt_name, "") == 0) { - strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(kbd)); - } - // Advertise even if BT is off in settings furi_hal_bt_start_advertising(); @@ -589,6 +584,11 @@ void bad_kb_conn_reset(BadKbApp* app) { } void bad_kb_config_adjust(BadKbConfig* cfg) { + // Avoid empty name + if(strcmp(cfg->bt_name, "") == 0) { + snprintf(cfg->bt_name, BAD_KB_NAME_LEN, "Control %s", furi_hal_version_get_name_ptr()); + } + // MAC is adjusted by furi_hal_bt, adjust here too so it matches after applying const uint8_t* normal_mac = furi_hal_version_get_ble_mac(); uint8_t empty_mac[BAD_KB_MAC_LEN] = FURI_HAL_BT_EMPTY_MAC_ADDR; From 70e040300ffd92475f720fb99a58381af5fa5351 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 01:44:44 +0100 Subject: [PATCH 155/370] Cleaner furi_hal_bt default hid name logic --- firmware/targets/f7/furi_hal/furi_hal_bt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 5fe13732f..22d286abd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -233,8 +233,8 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, config->adv_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "%cControl %s", - *furi_hal_version_get_ble_local_device_name_ptr(), - furi_hal_version_get_ble_local_device_name_ptr() + 1); + AD_TYPE_COMPLETE_LOCAL_NAME, + furi_hal_version_get_name_ptr()); } } if(!gap_init(config, event_cb, context)) { From 0348aff9b8b1d83a5d9c28ac3a656ae2fc38fb90 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 01:46:13 +0100 Subject: [PATCH 156/370] BadKB usb go brrr --- applications/main/bad_kb/bad_kb_app.c | 25 ++++++ .../main/bad_kb/helpers/ducky_script.h | 4 +- .../main/bad_kb/scenes/bad_kb_scene_config.c | 89 ++++++++++++++++--- .../main/bad_kb/scenes/bad_kb_scene_config.h | 6 +- ...fig_mac.c => bad_kb_scene_config_bt_mac.c} | 13 ++- ...g_name.c => bad_kb_scene_config_bt_name.c} | 11 ++- .../scenes/bad_kb_scene_config_usb_name.c | 56 ++++++++++++ .../scenes/bad_kb_scene_config_usb_vidpid.c | 50 +++++++++++ 8 files changed, 224 insertions(+), 30 deletions(-) rename applications/main/bad_kb/scenes/{bad_kb_scene_config_mac.c => bad_kb_scene_config_bt_mac.c} (78%) rename applications/main/bad_kb/scenes/{bad_kb_scene_config_name.c => bad_kb_scene_config_bt_name.c} (77%) create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 8d31f4da4..5459fef10 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -33,6 +33,10 @@ static void bad_kb_load_settings(BadKbApp* app) { BadKbConfig* cfg = &app->config; strcpy(cfg->bt_name, ""); memcpy(cfg->bt_mac, BAD_KB_EMPTY_MAC, BAD_KB_MAC_LEN); + strcpy(cfg->usb_cfg.manuf, ""); + strcpy(cfg->usb_cfg.product, ""); + cfg->usb_cfg.vid = 0; + cfg->usb_cfg.pid = 0; Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -49,6 +53,23 @@ static void bad_kb_load_settings(BadKbApp* app) { if(!flipper_format_read_hex(file, "Bt_Mac", (uint8_t*)&cfg->bt_mac, BAD_KB_MAC_LEN)) { memcpy(cfg->bt_mac, BAD_KB_EMPTY_MAC, BAD_KB_MAC_LEN); } + if(flipper_format_read_string(file, "Usb_Manuf", tmp_str) && !furi_string_empty(tmp_str)) { + strcpy(cfg->usb_cfg.manuf, furi_string_get_cstr(tmp_str)); + } else { + strcpy(cfg->usb_cfg.manuf, ""); + } + if(flipper_format_read_string(file, "Usb_Product", tmp_str) && + !furi_string_empty(tmp_str)) { + strcpy(cfg->usb_cfg.product, furi_string_get_cstr(tmp_str)); + } else { + strcpy(cfg->usb_cfg.product, ""); + } + if(!flipper_format_read_uint32(file, "Usb_Vid", &cfg->usb_cfg.vid, 1)) { + cfg->usb_cfg.vid = 0; + } + if(!flipper_format_read_uint32(file, "Usb_Pid", &cfg->usb_cfg.pid, 1)) { + cfg->usb_cfg.pid = 0; + } furi_string_free(tmp_str); flipper_format_file_close(file); } @@ -78,6 +99,10 @@ static void bad_kb_save_settings(BadKbApp* app) { flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); flipper_format_write_string_cstr(file, "Bt_Name", cfg->bt_name); flipper_format_write_hex(file, "Bt_Mac", (uint8_t*)&cfg->bt_mac, BAD_KB_MAC_LEN); + flipper_format_write_string_cstr(file, "Usb_Manuf", cfg->usb_cfg.manuf); + flipper_format_write_string_cstr(file, "Usb_Product", cfg->usb_cfg.product); + flipper_format_write_uint32(file, "Usb_Vid", &cfg->usb_cfg.vid, 1); + flipper_format_write_uint32(file, "Usb_Pid", &cfg->usb_cfg.pid, 1); flipper_format_file_close(file); } flipper_format_free(file); diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index b027c3f56..23f225ab7 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -137,6 +137,8 @@ struct BadKbApp { VariableItemList* var_item_list; TextInput* text_input; ByteInput* byte_input; + char usb_name_buf[32]; + uint16_t usb_vidpid_buf[2]; char bt_name_buf[BAD_KB_NAME_LEN]; uint8_t bt_mac_buf[BAD_KB_MAC_LEN]; @@ -149,7 +151,7 @@ struct BadKbApp { Bt* bt; bool is_bt; bool bt_remember; - BadKbConfig config; // User options (TODO: allow users to change usb cfg) + BadKbConfig config; // User options BadKbConfig id_config; // ID and BT_ID values BadKbConfig prev_config; // State to restore at exit diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index 7d8d4dca3..f17a52b97 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -7,10 +7,20 @@ enum VarItemListIndex { VarItemListIndexKeyboardLayout, VarItemListIndexConnection, - VarItemListIndexBtRemember, +}; + +enum VarItemListIndexBt { + VarItemListIndexBtRemember = VarItemListIndexConnection + 1, VarItemListIndexBtDeviceName, VarItemListIndexBtMacAddress, - VarItemListIndexRandomizeBtMac, + VarItemListIndexBtRandomizeMac, +}; + +enum VarItemListIndexUsb { + VarItemListIndexUsbManufacturerName = VarItemListIndexConnection + 1, + VarItemListIndexUsbProductName, + VarItemListIndexUsbVidPid, + VarItemListIndexUsbRandomizeVidPid, }; void bad_kb_scene_config_connection_callback(VariableItem* item) { @@ -73,6 +83,26 @@ void bad_kb_scene_config_on_enter(void* context) { } else if(bad_kb->set_bt_id) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); } + } else { + item = variable_item_list_add(var_item_list, "USB Manufacturer Name", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Mname!"); + } + + item = variable_item_list_add(var_item_list, "USB Product Name", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Pname!"); + } + + item = variable_item_list_add(var_item_list, "USB VID and PID", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset IDs!"); + } + + item = variable_item_list_add(var_item_list, "Randomize USB VID:PID", 0, NULL, bad_kb); + if(bad_kb->set_usb_id) { + variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset IDs!"); + } } variable_item_list_set_enter_callback( @@ -95,23 +125,56 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { case VarItemListIndexKeyboardLayout: scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); break; - case VarItemListIndexBtDeviceName: - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName); - break; - case VarItemListIndexBtMacAddress: - scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac); - break; - case VarItemListIndexRandomizeBtMac: - furi_hal_random_fill_buf(bad_kb->config.bt_mac, BAD_KB_MAC_LEN); - /* fall through */ - case VarItemListIndexBtRemember: - /* fall through */ case VarItemListIndexConnection: bad_kb_config_refresh(bad_kb); break; default: break; } + if(bad_kb->is_bt) { + switch(event.event) { + case VarItemListIndexBtRemember: + bad_kb_config_refresh(bad_kb); + break; + case VarItemListIndexBtDeviceName: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtName); + break; + case VarItemListIndexBtMacAddress: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBtMac); + break; + case VarItemListIndexBtRandomizeMac: + furi_hal_random_fill_buf(bad_kb->config.bt_mac, BAD_KB_MAC_LEN); + bad_kb_config_refresh(bad_kb); + break; + default: + break; + } + } else { + switch(event.event) { + case VarItemListIndexUsbManufacturerName: + scene_manager_set_scene_state( + bad_kb->scene_manager, BadKbSceneConfigUsbName, true); + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); + break; + case VarItemListIndexUsbProductName: + scene_manager_set_scene_state( + bad_kb->scene_manager, BadKbSceneConfigUsbName, false); + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); + break; + case VarItemListIndexUsbVidPid: + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbVidPid); + break; + case VarItemListIndexUsbRandomizeVidPid: + furi_hal_random_fill_buf( + (void*)bad_kb->usb_vidpid_buf, sizeof(bad_kb->usb_vidpid_buf)); + bad_kb->config.usb_cfg.vid = bad_kb->usb_vidpid_buf[0]; + bad_kb->config.usb_cfg.pid = bad_kb->usb_vidpid_buf[1]; + bad_kb_config_refresh(bad_kb); + break; + default: + break; + } + } } return consumed; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.h b/applications/main/bad_kb/scenes/bad_kb_scene_config.h index ac445c673..034a898a4 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.h +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.h @@ -3,5 +3,7 @@ ADD_SCENE(bad_kb, work, Work) ADD_SCENE(bad_kb, error, Error) ADD_SCENE(bad_kb, config, Config) ADD_SCENE(bad_kb, config_layout, ConfigLayout) -ADD_SCENE(bad_kb, config_name, ConfigName) -ADD_SCENE(bad_kb, config_mac, ConfigMac) +ADD_SCENE(bad_kb, config_bt_name, ConfigBtName) +ADD_SCENE(bad_kb, config_bt_mac, ConfigBtMac) +ADD_SCENE(bad_kb, config_usb_name, ConfigUsbName) +ADD_SCENE(bad_kb, config_usb_vidpid, ConfigUsbVidPid) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c similarity index 78% rename from applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c rename to applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c index 7d7c5e57f..8005fdff3 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c @@ -1,24 +1,21 @@ #include "../bad_kb_app.h" -#define TAG "BadKbConfigMac" - -void bad_kb_scene_config_mac_byte_input_callback(void* context) { +void bad_kb_scene_config_bt_mac_byte_input_callback(void* context) { BadKbApp* bad_kb = context; view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); } -void bad_kb_scene_config_mac_on_enter(void* context) { +void bad_kb_scene_config_bt_mac_on_enter(void* context) { BadKbApp* bad_kb = context; ByteInput* byte_input = bad_kb->byte_input; memmove(bad_kb->bt_mac_buf, bad_kb->config.bt_mac, BAD_KB_MAC_LEN); - byte_input_set_header_text(byte_input, "Set BT MAC address"); byte_input_set_result_callback( byte_input, - bad_kb_scene_config_mac_byte_input_callback, + bad_kb_scene_config_bt_mac_byte_input_callback, NULL, bad_kb, bad_kb->bt_mac_buf, @@ -27,7 +24,7 @@ void bad_kb_scene_config_mac_on_enter(void* context) { view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); } -bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { +bool bad_kb_scene_config_bt_mac_on_event(void* context, SceneManagerEvent event) { BadKbApp* bad_kb = context; bool consumed = false; @@ -42,7 +39,7 @@ bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { return consumed; } -void bad_kb_scene_config_mac_on_exit(void* context) { +void bad_kb_scene_config_bt_mac_on_exit(void* context) { BadKbApp* bad_kb = context; ByteInput* byte_input = bad_kb->byte_input; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c similarity index 77% rename from applications/main/bad_kb/scenes/bad_kb_scene_config_name.c rename to applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c index fc541490b..3ce2c99b4 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_name.c @@ -1,22 +1,21 @@ #include "../bad_kb_app.h" -static void bad_kb_scene_config_name_text_input_callback(void* context) { +static void bad_kb_scene_config_bt_name_text_input_callback(void* context) { BadKbApp* bad_kb = context; view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); } -void bad_kb_scene_config_name_on_enter(void* context) { +void bad_kb_scene_config_bt_name_on_enter(void* context) { BadKbApp* bad_kb = context; TextInput* text_input = bad_kb->text_input; strlcpy(bad_kb->bt_name_buf, bad_kb->config.bt_name, BAD_KB_NAME_LEN); - text_input_set_header_text(text_input, "Set BT device name"); text_input_set_result_callback( text_input, - bad_kb_scene_config_name_text_input_callback, + bad_kb_scene_config_bt_name_text_input_callback, bad_kb, bad_kb->bt_name_buf, BAD_KB_NAME_LEN, @@ -25,7 +24,7 @@ void bad_kb_scene_config_name_on_enter(void* context) { view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); } -bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { +bool bad_kb_scene_config_bt_name_on_event(void* context, SceneManagerEvent event) { BadKbApp* bad_kb = context; bool consumed = false; @@ -40,7 +39,7 @@ bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { return consumed; } -void bad_kb_scene_config_name_on_exit(void* context) { +void bad_kb_scene_config_bt_name_on_exit(void* context) { BadKbApp* bad_kb = context; TextInput* text_input = bad_kb->text_input; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c new file mode 100644 index 000000000..250a3fde7 --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c @@ -0,0 +1,56 @@ +#include "../bad_kb_app.h" + +static void bad_kb_scene_config_usb_name_text_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextInputDone); +} + +void bad_kb_scene_config_usb_name_on_enter(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { + strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb_cfg.manuf, 32); + text_input_set_header_text(text_input, "Set USB manufacturer name"); + } else { + strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb_cfg.product, 32); + text_input_set_header_text(text_input, "Set USB product name"); + } + + text_input_set_result_callback( + text_input, + bad_kb_scene_config_usb_name_text_input_callback, + bad_kb, + bad_kb->usb_name_buf, + 32, + true); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); +} + +bool bad_kb_scene_config_usb_name_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventTextInputDone) { + if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { + strlcpy(bad_kb->config.usb_cfg.manuf, bad_kb->usb_name_buf, 32); + } else { + strlcpy(bad_kb->config.usb_cfg.product, bad_kb->usb_name_buf, 32); + } + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_usb_name_on_exit(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + text_input_reset(text_input); +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c new file mode 100644 index 000000000..ef1a4700f --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_vidpid.c @@ -0,0 +1,50 @@ +#include "../bad_kb_app.h" + +void bad_kb_scene_config_usb_vidpid_byte_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); +} + +void bad_kb_scene_config_usb_vidpid_on_enter(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + bad_kb->usb_vidpid_buf[0] = __REVSH(bad_kb->config.usb_cfg.vid); + bad_kb->usb_vidpid_buf[1] = __REVSH(bad_kb->config.usb_cfg.pid); + byte_input_set_header_text(byte_input, "Set USB VID:PID"); + + byte_input_set_result_callback( + byte_input, + bad_kb_scene_config_usb_vidpid_byte_input_callback, + NULL, + bad_kb, + (void*)bad_kb->usb_vidpid_buf, + sizeof(bad_kb->usb_vidpid_buf)); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewByteInput); +} + +bool bad_kb_scene_config_usb_vidpid_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventByteInputDone) { + bad_kb->config.usb_cfg.vid = __REVSH(bad_kb->usb_vidpid_buf[0]); + bad_kb->config.usb_cfg.pid = __REVSH(bad_kb->usb_vidpid_buf[1]); + bad_kb_config_refresh(bad_kb); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_usb_vidpid_on_exit(void* context) { + BadKbApp* bad_kb = context; + ByteInput* byte_input = bad_kb->byte_input; + + byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(byte_input, ""); +} From 1f8baba7f0e492c68e7f576b7d2ec04265e2591c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 01:48:51 +0100 Subject: [PATCH 157/370] BadKB same sysrq logic for bt and usb --- applications/main/bad_kb/helpers/ducky_script_commands.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script_commands.c b/applications/main/bad_kb/helpers/ducky_script_commands.c index 42ed1b9e8..5bae8fc24 100644 --- a/applications/main/bad_kb/helpers/ducky_script_commands.c +++ b/applications/main/bad_kb/helpers/ducky_script_commands.c @@ -86,8 +86,7 @@ static int32_t ducky_fnc_sysrq(BadKbScript* bad_kb, const char* line, int32_t pa furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + furi_hal_bt_hid_kb_release_all(); } else { furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_hid_kb_press(key); From b1af5bb6e5b87797ced7f1319867a020e51cda46 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 12 Jun 2023 04:24:20 +0300 Subject: [PATCH 158/370] Update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc5af4149..c9e169af8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,13 +10,15 @@ * SubGHz: Remove broken modulation that was causing buffer overrun (fixes issue #506) * SubGHz: Notifications fixes (by @wosk | PR #464) * GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509) +* Infrared: `RCA` protocol support * Infrared: Update universal remote assets - add new ACs and TCL TV * API: Add furi_hal_version_uid_default (+ Fix TOTP) (by @ClaraCrazy | PR #502) +* OFW PR 2760: NFC: Improvements to NFC Magic app (by AloneLiberty) * OFW PR 2756: fix: make dialog_file_browser_set_basic_options initialize all fields (by JarvisCraft) * OFW: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys * OFW: fbt: stable build dates * OFW: weather_station: add oregon3 with THGR221 -* OFW: Services: simplify api (DOLPHIN_DEED->dolphin_deed - function instead of macros + remake all apps in extra pack and main fw to use new API) +* OFW: Services: simplify api (DOLPHIN_DEED->dolphin_deed - function instead of macros + remake all apps in extra pack and main fw to use new API) -> **Breaking API change, API version was changed from 29.x to 30.x** * OFW: Core2, SRAM2: provide safety gap * OFW: FuriHal: always clock SMPS from HSI * OFW: ble: refactored bt gatt characteristics setup (+ remake of BT HID Led descriptor in new way to work with this changes) From 6256f0c46e98e47e8210b5e0b9092b14129b3f23 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 12 Jun 2023 04:33:16 +0300 Subject: [PATCH 159/370] Add more freqs to default list --- CHANGELOG.md | 1 + applications/main/subghz/views/subghz_frequency_analyzer.c | 7 ++++--- documentation/SubGHzSettings.md | 3 +++ lib/subghz/subghz_setting.c | 3 +++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9e169af8..957ce3f9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Plugins: Update TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) * Plugins: Unitemp SCD30 support (PR in unitemp repo by @divinebird / fixed by @xMasterX) * Plugins: Fix ProtoView issue #503 -> (Broken saved files with custom modulation) +* SubGHz: Added 430, 431 MHz to default list * SubGHz: Remove broken modulation that was causing buffer overrun (fixes issue #506) * SubGHz: Notifications fixes (by @wosk | PR #464) * GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509) diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 23c05e4c5..30545d4b7 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -24,9 +24,10 @@ static const uint32_t subghz_frequency_list[] = { 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000, 315000000, 318000000, 330000000, 345000000, 348000000, 350000000, 387000000, 390000000, - 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, - 434176948, 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, - 868350000, 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; + 418000000, 430000000, 431000000, 431500000, 433075000, 433220000, 433420000, 433657070, + 433889000, 433920000, 434075000, 434176948, 434390000, 434420000, 434775000, 438900000, + 440175000, 464000000, 779000000, 868350000, 868400000, 868800000, 868950000, 906400000, + 915000000, 925000000, 928000000}; typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index adf72067b..a15f72b98 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -38,6 +38,9 @@ if you need your custom one, make sure it doesn't listed here 387000000, 390000000, 418000000, + 430000000, + 431000000, + 431500000, 433075000, /* LPD433 first */ 433220000, 433420000, diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index b72cf99eb..cd9d0466e 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -45,6 +45,9 @@ static const uint32_t subghz_frequency_list[] = { 387000000, 390000000, 418000000, + 430000000, + 431000000, + 431500000, 433075000, /* LPD433 first */ 433220000, 433420000, From 7417e1e1cfd5cc58d32f9012cce3747044d43203 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 02:35:33 +0100 Subject: [PATCH 160/370] Add gatt char refactor revert patch for testing --- revert_gatt_char_refactor.patch | 2181 +++++++++++++++++++++++++++++++ 1 file changed, 2181 insertions(+) create mode 100644 revert_gatt_char_refactor.patch diff --git a/revert_gatt_char_refactor.patch b/revert_gatt_char_refactor.patch new file mode 100644 index 000000000..161830135 --- /dev/null +++ b/revert_gatt_char_refactor.patch @@ -0,0 +1,2181 @@ +diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c +index 882fd11e85..b8cb09734d 100644 +--- a/applications/services/bt/bt_service/bt.c ++++ b/applications/services/bt/bt_service/bt.c +@@ -1,7 +1,7 @@ + #include "bt_i.h" ++#include "battery_service.h" + #include "bt_keys_storage.h" + +-#include + #include + #include + #include +diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c +index 35f53ae22c..78e789ac30 100644 +--- a/firmware/targets/f7/ble_glue/app_debug.c ++++ b/firmware/targets/f7/ble_glue/app_debug.c +@@ -195,14 +195,14 @@ static void APPD_SetCPU2GpioConfig(void) { + gpio_config.Pin = gpiob_pin_list; + LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); + LL_GPIO_Init(GPIOB, &gpio_config); +- LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); ++ LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list); + } + + if(gpioc_pin_list != 0) { + gpio_config.Pin = gpioc_pin_list; + LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); + LL_GPIO_Init(GPIOC, &gpio_config); +- LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); ++ LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); + } + } + +diff --git a/firmware/targets/f7/ble_glue/services/battery_service.c b/firmware/targets/f7/ble_glue/battery_service.c +similarity index 53% +rename from firmware/targets/f7/ble_glue/services/battery_service.c +rename to firmware/targets/f7/ble_glue/battery_service.c +index 63f736b3b7..8c371efadb 100644 +--- a/firmware/targets/f7/ble_glue/services/battery_service.c ++++ b/firmware/targets/f7/ble_glue/battery_service.c +@@ -1,7 +1,5 @@ + #include "battery_service.h" + #include "app_common.h" +-#include "gatt_char.h" +- + #include + + #include +@@ -9,6 +7,12 @@ + + #define TAG "BtBatterySvc" + ++typedef struct { ++ uint16_t svc_handle; ++ uint16_t battery_level_char_handle; ++ uint16_t power_state_char_handle; ++} BatterySvc; ++ + enum { + // Common states + BatterySvcPowerStateUnknown = 0b00, +@@ -36,44 +40,13 @@ typedef struct { + + _Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); + ++static BatterySvc* battery_svc = NULL; ++ + #define BATTERY_POWER_STATE (0x2A1A) + + static const uint16_t service_uuid = BATTERY_SERVICE_UUID; +- +-typedef enum { +- BatterySvcGattCharacteristicBatteryLevel = 0, +- BatterySvcGattCharacteristicPowerState, +- BatterySvcGattCharacteristicCount, +-} BatterySvcGattCharacteristicId; +- +-static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = +- {[BatterySvcGattCharacteristicBatteryLevel] = +- {.name = "Battery Level", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = 1, +- .uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [BatterySvcGattCharacteristicPowerState] = { +- .name = "Power State", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = 1, +- .uuid.Char_UUID_16 = BATTERY_POWER_STATE, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}}; +- +-typedef struct { +- uint16_t svc_handle; +- FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount]; +-} BatterySvc; +- +-static BatterySvc* battery_svc = NULL; ++static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; ++static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; + + void battery_svc_start() { + battery_svc = malloc(sizeof(BatterySvc)); +@@ -85,19 +58,53 @@ void battery_svc_start() { + if(status) { + FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); + } +- for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_init( +- battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); ++ // Add Battery level characteristic ++ status = aci_gatt_add_char( ++ battery_svc->svc_handle, ++ UUID_TYPE_16, ++ (Char_UUID_t*)&battery_level_char_uuid, ++ 1, ++ CHAR_PROP_READ | CHAR_PROP_NOTIFY, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &battery_svc->battery_level_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); + } +- ++ // Add Power state characteristic ++ status = aci_gatt_add_char( ++ battery_svc->svc_handle, ++ UUID_TYPE_16, ++ (Char_UUID_t*)&power_state_char_uuid, ++ 1, ++ CHAR_PROP_READ | CHAR_PROP_NOTIFY, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &battery_svc->power_state_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); ++ } ++ // Update power state charachteristic + battery_svc_update_power_state(); + } + + void battery_svc_stop() { + tBleStatus status; + if(battery_svc) { +- for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); ++ // Delete Battery level characteristic ++ status = ++ aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); ++ } ++ // Delete Power state characteristic ++ status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); + } + // Delete Battery service + status = aci_gatt_del_service(battery_svc->svc_handle); +@@ -119,10 +126,13 @@ bool battery_svc_update_level(uint8_t battery_charge) { + return false; + } + // Update battery level characteristic +- return flipper_gatt_characteristic_update( +- battery_svc->svc_handle, +- &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], +- &battery_charge); ++ FURI_LOG_D(TAG, "Updating battery level characteristic"); ++ tBleStatus result = aci_gatt_update_char_value( ++ battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); ++ if(result) { ++ FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); ++ } ++ return result != BLE_STATUS_SUCCESS; + } + + bool battery_svc_update_power_state() { +@@ -142,9 +152,15 @@ bool battery_svc_update_power_state() { + power_state.charging = BatterySvcPowerStateNotCharging; + power_state.discharging = BatterySvcPowerStateDischarging; + } +- +- return flipper_gatt_characteristic_update( ++ FURI_LOG_D(TAG, "Updating power state characteristic"); ++ tBleStatus result = aci_gatt_update_char_value( + battery_svc->svc_handle, +- &battery_svc->chars[BatterySvcGattCharacteristicPowerState], +- &power_state); ++ battery_svc->power_state_char_handle, ++ 0, ++ 1, ++ (uint8_t*)&power_state); ++ if(result) { ++ FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); ++ } ++ return result != BLE_STATUS_SUCCESS; + } +diff --git a/firmware/targets/f7/ble_glue/services/battery_service.h b/firmware/targets/f7/ble_glue/battery_service.h +similarity index 100% +rename from firmware/targets/f7/ble_glue/services/battery_service.h +rename to firmware/targets/f7/ble_glue/battery_service.h +diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c +index 7a2148b6b8..4fc4d521be 100644 +--- a/firmware/targets/f7/ble_glue/ble_app.c ++++ b/firmware/targets/f7/ble_glue/ble_app.c +@@ -33,45 +33,6 @@ static int32_t ble_app_hci_thread(void* context); + static void ble_app_hci_event_handler(void* pPayload); + static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); + +-static const HCI_TL_HciInitConf_t hci_tl_config = { +- .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, +- .StatusNotCallBack = ble_app_hci_status_not_handler, +-}; +- +-static const SHCI_C2_CONFIG_Cmd_Param_t config_param = { +- .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, +- .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, +- .BleNvmRamAddress = (uint32_t)ble_app_nvm, +- .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, +-}; +- +-static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { +- .Header = {{0, 0, 0}}, // Header unused +- .Param = { +- .pBleBufferAddress = 0, // pBleBufferAddress not used +- .BleBufferSize = 0, // BleBufferSize not used +- .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, +- .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, +- .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, +- .NumOfLinks = CFG_BLE_NUM_LINK, +- .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, +- .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, +- .MblockCount = CFG_BLE_MBLOCK_COUNT, +- .AttMtu = CFG_BLE_MAX_ATT_MTU, +- .SlaveSca = CFG_BLE_SLAVE_SCA, +- .MasterSca = CFG_BLE_MASTER_SCA, +- .LsSource = CFG_BLE_LSE_SOURCE, +- .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, +- .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, +- .ViterbiEnable = CFG_BLE_VITERBI_MODE, +- .Options = CFG_BLE_OPTIONS, +- .HwVersion = 0, +- .max_coc_initiator_nbr = 32, +- .min_tx_power = 0, +- .max_tx_power = 0, +- .rx_model_config = 1, +- }}; +- + bool ble_app_init() { + SHCI_CmdStatus_t status; + ble_app = malloc(sizeof(BleApp)); +@@ -83,16 +44,52 @@ bool ble_app_init() { + furi_thread_start(ble_app->thread); + + // Initialize Ble Transport Layer ++ HCI_TL_HciInitConf_t hci_tl_config = { ++ .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, ++ .StatusNotCallBack = ble_app_hci_status_not_handler, ++ }; + hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); + + // Configure NVM store for pairing data +- status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); ++ SHCI_C2_CONFIG_Cmd_Param_t config_param = { ++ .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, ++ .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, ++ .BleNvmRamAddress = (uint32_t)ble_app_nvm, ++ .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, ++ }; ++ status = SHCI_C2_Config(&config_param); + if(status) { + FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); + } + + // Start ble stack on 2nd core +- status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); ++ SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { ++ .Header = {{0, 0, 0}}, // Header unused ++ .Param = { ++ .pBleBufferAddress = 0, // pBleBufferAddress not used ++ .BleBufferSize = 0, // BleBufferSize not used ++ .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, ++ .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, ++ .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, ++ .NumOfLinks = CFG_BLE_NUM_LINK, ++ .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, ++ .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, ++ .MblockCount = CFG_BLE_MBLOCK_COUNT, ++ .AttMtu = CFG_BLE_MAX_ATT_MTU, ++ .SlaveSca = CFG_BLE_SLAVE_SCA, ++ .MasterSca = CFG_BLE_MASTER_SCA, ++ .LsSource = CFG_BLE_LSE_SOURCE, ++ .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, ++ .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, ++ .ViterbiEnable = CFG_BLE_VITERBI_MODE, ++ .Options = CFG_BLE_OPTIONS, ++ .HwVersion = 0, ++ .max_coc_initiator_nbr = 32, ++ .min_tx_power = 0, ++ .max_tx_power = 0, ++ .rx_model_config = 1, ++ }}; ++ status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); + if(status) { + FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); + } +diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c +new file mode 100644 +index 0000000000..d24058632f +--- /dev/null ++++ b/firmware/targets/f7/ble_glue/dev_info_service.c +@@ -0,0 +1,220 @@ ++#include "dev_info_service.h" ++#include "app_common.h" ++#include ++ ++#include ++#include ++#include ++ ++#define TAG "BtDevInfoSvc" ++ ++typedef struct { ++ uint16_t service_handle; ++ uint16_t man_name_char_handle; ++ uint16_t serial_num_char_handle; ++ uint16_t firmware_rev_char_handle; ++ uint16_t software_rev_char_handle; ++ uint16_t rpc_version_char_handle; ++ FuriString* version_string; ++ char hardware_revision[4]; ++} DevInfoSvc; ++ ++static DevInfoSvc* dev_info_svc = NULL; ++ ++static const char dev_info_man_name[] = "Flipper Devices Inc."; ++static const char dev_info_serial_num[] = "1.0"; ++static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); ++ ++static const uint8_t dev_info_rpc_version_uuid[] = ++ {0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03}; ++ ++void dev_info_svc_start() { ++ dev_info_svc = malloc(sizeof(DevInfoSvc)); ++ dev_info_svc->version_string = furi_string_alloc_printf( ++ "%s %s %s %s", ++ version_get_githash(NULL), ++ version_get_version(NULL), ++ version_get_gitbranchnum(NULL), ++ version_get_builddate(NULL)); ++ snprintf( ++ dev_info_svc->hardware_revision, ++ sizeof(dev_info_svc->hardware_revision), ++ "%d", ++ version_get_target(NULL)); ++ tBleStatus status; ++ ++ // Add Device Information Service ++ uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; ++ status = aci_gatt_add_service( ++ UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 11, &dev_info_svc->service_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); ++ } ++ ++ // Add characteristics ++ uuid = MANUFACTURER_NAME_UUID; ++ status = aci_gatt_add_char( ++ dev_info_svc->service_handle, ++ UUID_TYPE_16, ++ (Char_UUID_t*)&uuid, ++ strlen(dev_info_man_name), ++ CHAR_PROP_READ, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &dev_info_svc->man_name_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status); ++ } ++ uuid = SERIAL_NUMBER_UUID; ++ status = aci_gatt_add_char( ++ dev_info_svc->service_handle, ++ UUID_TYPE_16, ++ (Char_UUID_t*)&uuid, ++ strlen(dev_info_serial_num), ++ CHAR_PROP_READ, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &dev_info_svc->serial_num_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add serial number char: %d", status); ++ } ++ uuid = FIRMWARE_REVISION_UUID; ++ status = aci_gatt_add_char( ++ dev_info_svc->service_handle, ++ UUID_TYPE_16, ++ (Char_UUID_t*)&uuid, ++ strlen(dev_info_svc->hardware_revision), ++ CHAR_PROP_READ, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &dev_info_svc->firmware_rev_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status); ++ } ++ uuid = SOFTWARE_REVISION_UUID; ++ status = aci_gatt_add_char( ++ dev_info_svc->service_handle, ++ UUID_TYPE_16, ++ (Char_UUID_t*)&uuid, ++ furi_string_size(dev_info_svc->version_string), ++ CHAR_PROP_READ, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &dev_info_svc->software_rev_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add software revision char: %d", status); ++ } ++ status = aci_gatt_add_char( ++ dev_info_svc->service_handle, ++ UUID_TYPE_128, ++ (const Char_UUID_t*)dev_info_rpc_version_uuid, ++ strlen(dev_info_rpc_version), ++ CHAR_PROP_READ, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &dev_info_svc->rpc_version_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status); ++ } ++ ++ // Update characteristics ++ status = aci_gatt_update_char_value( ++ dev_info_svc->service_handle, ++ dev_info_svc->man_name_char_handle, ++ 0, ++ strlen(dev_info_man_name), ++ (uint8_t*)dev_info_man_name); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status); ++ } ++ status = aci_gatt_update_char_value( ++ dev_info_svc->service_handle, ++ dev_info_svc->serial_num_char_handle, ++ 0, ++ strlen(dev_info_serial_num), ++ (uint8_t*)dev_info_serial_num); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to update serial number char: %d", status); ++ } ++ status = aci_gatt_update_char_value( ++ dev_info_svc->service_handle, ++ dev_info_svc->firmware_rev_char_handle, ++ 0, ++ strlen(dev_info_svc->hardware_revision), ++ (uint8_t*)dev_info_svc->hardware_revision); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status); ++ } ++ status = aci_gatt_update_char_value( ++ dev_info_svc->service_handle, ++ dev_info_svc->software_rev_char_handle, ++ 0, ++ furi_string_size(dev_info_svc->version_string), ++ (uint8_t*)furi_string_get_cstr(dev_info_svc->version_string)); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to update software revision char: %d", status); ++ } ++ status = aci_gatt_update_char_value( ++ dev_info_svc->service_handle, ++ dev_info_svc->rpc_version_char_handle, ++ 0, ++ strlen(dev_info_rpc_version), ++ (uint8_t*)dev_info_rpc_version); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status); ++ } ++} ++ ++void dev_info_svc_stop() { ++ tBleStatus status; ++ if(dev_info_svc) { ++ furi_string_free(dev_info_svc->version_string); ++ // Delete service characteristics ++ status = ++ aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status); ++ } ++ status = ++ aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status); ++ } ++ status = aci_gatt_del_char( ++ dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status); ++ } ++ status = aci_gatt_del_char( ++ dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status); ++ } ++ status = ++ aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status); ++ } ++ // Delete service ++ status = aci_gatt_del_service(dev_info_svc->service_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); ++ } ++ free(dev_info_svc); ++ dev_info_svc = NULL; ++ } ++} ++ ++bool dev_info_svc_is_started() { ++ return dev_info_svc != NULL; ++} +diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.h b/firmware/targets/f7/ble_glue/dev_info_service.h +similarity index 100% +rename from firmware/targets/f7/ble_glue/services/dev_info_service.h +rename to firmware/targets/f7/ble_glue/dev_info_service.h +diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c +new file mode 100644 +index 0000000000..a31d6015f5 +--- /dev/null ++++ b/firmware/targets/f7/ble_glue/hid_service.c +@@ -0,0 +1,416 @@ ++#include "hid_service.h" ++#include "app_common.h" ++#include ++ ++#include ++ ++#define TAG "BtHid" ++ ++typedef struct { ++ uint16_t svc_handle; ++ uint16_t protocol_mode_char_handle; ++ uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; ++ uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; ++ uint16_t report_map_char_handle; ++ uint16_t info_char_handle; ++ uint16_t ctrl_point_char_handle; ++ // led state ++ uint16_t led_state_char_handle; ++ uint16_t led_state_desc_handle; ++ HidLedStateEventCallback led_state_event_callback; ++ void* led_state_ctx; ++} HIDSvc; ++ ++static HIDSvc* hid_svc = NULL; ++ ++static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { ++ SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; ++ hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); ++ evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; ++ // aci_gatt_attribute_modified_event_rp0* attribute_modified; ++ if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { ++ if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { ++ // Process modification events ++ ret = SVCCTL_EvtAckFlowEnable; ++ } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { ++ // Process notification confirmation ++ ret = SVCCTL_EvtAckFlowEnable; ++ } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { ++ // Process write request ++ aci_gatt_write_permit_req_event_rp0* req = ++ (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; ++ ++ furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); ++ ++ // this check is likely to be incorrect, it will actually work in our case ++ // but we need to investigate gatt api to see what is the rules ++ // that specify attibute handle value from char handle (or the reverse) ++ if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) { ++ hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); ++ aci_gatt_write_resp( ++ req->Connection_Handle, ++ req->Attribute_Handle, ++ 0x00, /* write_status = 0 (no error))*/ ++ 0x00, /* err_code */ ++ req->Data_Length, ++ req->Data); ++ aci_gatt_write_char_value( ++ req->Connection_Handle, ++ hid_svc->led_state_char_handle, ++ req->Data_Length, ++ req->Data); ++ ret = SVCCTL_EvtAckFlowEnable; ++ } ++ } ++ } ++ return ret; ++} ++ ++void hid_svc_start() { ++ tBleStatus status; ++ hid_svc = malloc(sizeof(HIDSvc)); ++ Service_UUID_t svc_uuid = {}; ++ Char_Desc_Uuid_t desc_uuid = {}; ++ Char_UUID_t char_uuid = {}; ++ ++ // Register event handler ++ SVCCTL_RegisterSvcHandler(hid_svc_event_handler); ++ // Add service ++ svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; ++ /** ++ * Add Human Interface Device Service ++ */ ++ status = aci_gatt_add_service( ++ UUID_TYPE_16, ++ &svc_uuid, ++ PRIMARY_SERVICE, ++ 2 + /* protocol mode */ ++ (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + ++ (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + ++ 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ ++ &hid_svc->svc_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add HID service: %d", status); ++ } ++ // Add Protocol mode characteristics ++ char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; ++ status = aci_gatt_add_char( ++ hid_svc->svc_handle, ++ UUID_TYPE_16, ++ &char_uuid, ++ 1, ++ CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, ++ ATTR_PERMISSION_NONE, ++ GATT_NOTIFY_ATTRIBUTE_WRITE, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &hid_svc->protocol_mode_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status); ++ } ++ // Update Protocol mode characteristic ++ uint8_t protocol_mode = 1; ++ status = aci_gatt_update_char_value( ++ hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); ++ } ++ ++#if(HID_SVC_REPORT_COUNT != 0) ++ for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { ++ if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547 ++ uint8_t buf[2] = {i + 1, 1}; // 1 input ++ char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; ++ status = aci_gatt_add_char( ++ hid_svc->svc_handle, ++ UUID_TYPE_16, ++ &char_uuid, ++ HID_SVC_REPORT_MAX_LEN, ++ CHAR_PROP_READ | CHAR_PROP_NOTIFY, ++ ATTR_PERMISSION_NONE, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_VARIABLE, ++ &(hid_svc->report_char_handle[i])); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); ++ } ++ ++ desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; ++ status = aci_gatt_add_char_desc( ++ hid_svc->svc_handle, ++ hid_svc->report_char_handle[i], ++ UUID_TYPE_16, ++ &desc_uuid, ++ HID_SVC_REPORT_REF_LEN, ++ HID_SVC_REPORT_REF_LEN, ++ buf, ++ ATTR_PERMISSION_NONE, ++ ATTR_ACCESS_READ_WRITE, ++ GATT_DONT_NOTIFY_EVENTS, ++ MIN_ENCRY_KEY_SIZE, ++ CHAR_VALUE_LEN_CONSTANT, ++ &(hid_svc->report_ref_desc_handle[i])); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); ++ } ++ } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { ++ uint8_t buf[2] = {i + 1, 2}; // 2 output ++ char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; ++ status = aci_gatt_add_char( ++ hid_svc->svc_handle, ++ UUID_TYPE_16, ++ &char_uuid, ++ HID_SVC_REPORT_MAX_LEN, ++ CHAR_PROP_READ | CHAR_PROP_NOTIFY, ++ ATTR_PERMISSION_NONE, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_VARIABLE, ++ &(hid_svc->report_char_handle[i])); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); ++ } ++ ++ desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; ++ status = aci_gatt_add_char_desc( ++ hid_svc->svc_handle, ++ hid_svc->report_char_handle[i], ++ UUID_TYPE_16, ++ &desc_uuid, ++ HID_SVC_REPORT_REF_LEN, ++ HID_SVC_REPORT_REF_LEN, ++ buf, ++ ATTR_PERMISSION_NONE, ++ ATTR_ACCESS_READ_WRITE, ++ GATT_DONT_NOTIFY_EVENTS, ++ MIN_ENCRY_KEY_SIZE, ++ CHAR_VALUE_LEN_CONSTANT, ++ &(hid_svc->report_ref_desc_handle[i])); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); ++ } ++ } else { ++ uint8_t buf[2] = {i + 1, 3}; // 3 feature ++ char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; ++ status = aci_gatt_add_char( ++ hid_svc->svc_handle, ++ UUID_TYPE_16, ++ &char_uuid, ++ HID_SVC_REPORT_MAX_LEN, ++ CHAR_PROP_READ | CHAR_PROP_NOTIFY, ++ ATTR_PERMISSION_NONE, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_VARIABLE, ++ &(hid_svc->report_char_handle[i])); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); ++ } ++ ++ desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; ++ status = aci_gatt_add_char_desc( ++ hid_svc->svc_handle, ++ hid_svc->report_char_handle[i], ++ UUID_TYPE_16, ++ &desc_uuid, ++ HID_SVC_REPORT_REF_LEN, ++ HID_SVC_REPORT_REF_LEN, ++ buf, ++ ATTR_PERMISSION_NONE, ++ ATTR_ACCESS_READ_WRITE, ++ GATT_DONT_NOTIFY_EVENTS, ++ MIN_ENCRY_KEY_SIZE, ++ CHAR_VALUE_LEN_CONSTANT, ++ &(hid_svc->report_ref_desc_handle[i])); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); ++ } ++ } ++ } ++#endif ++ // Add led state output report ++ char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; ++ status = aci_gatt_add_char( ++ hid_svc->svc_handle, ++ UUID_TYPE_16, ++ &char_uuid, ++ 1, ++ CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, ++ ATTR_PERMISSION_NONE, ++ GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &(hid_svc->led_state_char_handle)); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status); ++ } ++ ++ // Add led state char descriptor specifying it is an output report ++ uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; ++ desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; ++ status = aci_gatt_add_char_desc( ++ hid_svc->svc_handle, ++ hid_svc->led_state_char_handle, ++ UUID_TYPE_16, ++ &desc_uuid, ++ HID_SVC_REPORT_REF_LEN, ++ HID_SVC_REPORT_REF_LEN, ++ buf, ++ ATTR_PERMISSION_NONE, ++ ATTR_ACCESS_READ_WRITE, ++ GATT_DONT_NOTIFY_EVENTS, ++ MIN_ENCRY_KEY_SIZE, ++ CHAR_VALUE_LEN_CONSTANT, ++ &(hid_svc->led_state_desc_handle)); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status); ++ } ++ // Add Report Map characteristic ++ char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; ++ status = aci_gatt_add_char( ++ hid_svc->svc_handle, ++ UUID_TYPE_16, ++ &char_uuid, ++ HID_SVC_REPORT_MAP_MAX_LEN, ++ CHAR_PROP_READ, ++ ATTR_PERMISSION_NONE, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_VARIABLE, ++ &hid_svc->report_map_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); ++ } ++ ++ // Add Information characteristic ++ char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; ++ status = aci_gatt_add_char( ++ hid_svc->svc_handle, ++ UUID_TYPE_16, ++ &char_uuid, ++ HID_SVC_INFO_LEN, ++ CHAR_PROP_READ, ++ ATTR_PERMISSION_NONE, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &hid_svc->info_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status); ++ } ++ // Add Control Point characteristic ++ char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID; ++ status = aci_gatt_add_char( ++ hid_svc->svc_handle, ++ UUID_TYPE_16, ++ &char_uuid, ++ HID_SVC_CONTROL_POINT_LEN, ++ CHAR_PROP_WRITE_WITHOUT_RESP, ++ ATTR_PERMISSION_NONE, ++ GATT_NOTIFY_ATTRIBUTE_WRITE, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &hid_svc->ctrl_point_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); ++ } ++ ++ hid_svc->led_state_event_callback = NULL; ++ hid_svc->led_state_ctx = NULL; ++} ++ ++bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { ++ furi_assert(data); ++ furi_assert(hid_svc); ++ ++ tBleStatus status = aci_gatt_update_char_value( ++ hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); ++ return false; ++ } ++ return true; ++} ++ ++bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { ++ furi_assert(data); ++ furi_assert(hid_svc); ++ ++ tBleStatus status = aci_gatt_update_char_value( ++ hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); ++ return false; ++ } ++ return true; ++} ++ ++bool hid_svc_update_info(uint8_t* data, uint16_t len) { ++ furi_assert(data); ++ furi_assert(hid_svc); ++ ++ tBleStatus status = ++ aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); ++ return false; ++ } ++ return true; ++} ++ ++void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { ++ furi_assert(hid_svc); ++ furi_assert(callback); ++ furi_assert(context); ++ ++ hid_svc->led_state_event_callback = callback; ++ hid_svc->led_state_ctx = context; ++} ++ ++bool hid_svc_is_started() { ++ return hid_svc != NULL; ++} ++ ++void hid_svc_stop() { ++ tBleStatus status; ++ if(hid_svc) { ++ // Delete characteristics ++ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); ++ } ++#if(HID_SVC_INPUT_REPORT_COUNT != 0) ++ for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { ++ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); ++ } ++ } ++#endif ++ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); ++ } ++ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); ++ } ++ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); ++ } ++ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status); ++ } ++ // Delete service ++ status = aci_gatt_del_service(hid_svc->svc_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); ++ } ++ // Delete buffer size mutex ++ free(hid_svc); ++ hid_svc = NULL; ++ } ++} +diff --git a/firmware/targets/f7/ble_glue/services/hid_service.h b/firmware/targets/f7/ble_glue/hid_service.h +similarity index 89% +rename from firmware/targets/f7/ble_glue/services/hid_service.h +rename to firmware/targets/f7/ble_glue/hid_service.h +index 4d0ed4c4f9..b8f6b244d2 100644 +--- a/firmware/targets/f7/ble_glue/services/hid_service.h ++++ b/firmware/targets/f7/ble_glue/hid_service.h +@@ -27,7 +27,6 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); + + bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); + +-// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) +-bool hid_svc_update_info(uint8_t* data); ++bool hid_svc_update_info(uint8_t* data, uint16_t len); + + void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); +diff --git a/firmware/targets/f7/ble_glue/services/serial_service.c b/firmware/targets/f7/ble_glue/serial_service.c +similarity index 57% +rename from firmware/targets/f7/ble_glue/services/serial_service.c +rename to firmware/targets/f7/ble_glue/serial_service.c +index ab009bbfcb..c6421dc28f 100644 +--- a/firmware/targets/f7/ble_glue/services/serial_service.c ++++ b/firmware/targets/f7/ble_glue/serial_service.c +@@ -1,67 +1,17 @@ + #include "serial_service.h" + #include "app_common.h" + #include +-#include "gatt_char.h" + + #include + +-#include "serial_service_uuid.inc" +- + #define TAG "BtSerialSvc" + +-typedef enum { +- SerialSvcGattCharacteristicTx = 0, +- SerialSvcGattCharacteristicRx, +- SerialSvcGattCharacteristicFlowCtrl, +- SerialSvcGattCharacteristicStatus, +- SerialSvcGattCharacteristicCount, +-} SerialSvcGattCharacteristicId; +- +-static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { +- [SerialSvcGattCharacteristicTx] = +- {.name = "TX", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, +- .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, +- .uuid_type = UUID_TYPE_128, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_VARIABLE}, +- [SerialSvcGattCharacteristicRx] = +- {.name = "RX", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, +- .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, +- .uuid_type = UUID_TYPE_128, +- .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, +- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, +- .is_variable = CHAR_VALUE_LEN_VARIABLE}, +- [SerialSvcGattCharacteristicFlowCtrl] = +- {.name = "Flow control", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = sizeof(uint32_t), +- .uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID, +- .uuid_type = UUID_TYPE_128, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [SerialSvcGattCharacteristicStatus] = { +- .name = "RPC status", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = sizeof(SerialServiceRpcStatus), +- .uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID, +- .uuid_type = UUID_TYPE_128, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, +- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}}; +- + typedef struct { + uint16_t svc_handle; +- FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; ++ uint16_t rx_char_handle; ++ uint16_t tx_char_handle; ++ uint16_t flow_ctrl_char_handle; ++ uint16_t rpc_status_char_handle; + FuriMutex* buff_size_mtx; + uint32_t buff_size; + uint16_t bytes_ready_to_receive; +@@ -71,6 +21,17 @@ typedef struct { + + static SerialSvc* serial_svc = NULL; + ++static const uint8_t service_uuid[] = ++ {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; ++static const uint8_t char_tx_uuid[] = ++ {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; ++static const uint8_t char_rx_uuid[] = ++ {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; ++static const uint8_t flow_ctrl_uuid[] = ++ {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; ++static const uint8_t rpc_status_uuid[] = ++ {0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; ++ + static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { + SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); +@@ -79,14 +40,11 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; +- if(attribute_modified->Attr_Handle == +- serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) { ++ if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { + // Descriptor handle + ret = SVCCTL_EvtAckFlowEnable; + FURI_LOG_D(TAG, "RX descriptor event"); +- } else if( +- attribute_modified->Attr_Handle == +- serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { ++ } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { + FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); + if(serial_svc->callback) { + furi_check( +@@ -112,9 +70,7 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { + furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); + } + ret = SVCCTL_EvtAckFlowEnable; +- } else if( +- attribute_modified->Attr_Handle == +- serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { ++ } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { + SerialServiceRpcStatus* rpc_status = + (SerialServiceRpcStatus*)attribute_modified->Attr_Data; + if(*rpc_status == SerialServiceRpcStatusNotActive) { +@@ -141,12 +97,18 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { + } + + static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { +- flipper_gatt_characteristic_update( +- serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); ++ tBleStatus ble_status = aci_gatt_update_char_value( ++ serial_svc->svc_handle, ++ serial_svc->rpc_status_char_handle, ++ 0, ++ sizeof(SerialServiceRpcStatus), ++ (uint8_t*)&status); ++ if(ble_status) { ++ FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status); ++ } + } + + void serial_svc_start() { +- UNUSED(serial_svc_chars); + tBleStatus status; + serial_svc = malloc(sizeof(SerialSvc)); + // Register event handler +@@ -154,17 +116,72 @@ void serial_svc_start() { + + // Add service + status = aci_gatt_add_service( +- UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); ++ UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); + } + +- // Add characteristics +- for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_init( +- serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); ++ // Add RX characteristics ++ status = aci_gatt_add_char( ++ serial_svc->svc_handle, ++ UUID_TYPE_128, ++ (const Char_UUID_t*)char_rx_uuid, ++ SERIAL_SVC_DATA_LEN_MAX, ++ CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, ++ ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, ++ GATT_NOTIFY_ATTRIBUTE_WRITE, ++ 10, ++ CHAR_VALUE_LEN_VARIABLE, ++ &serial_svc->rx_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status); + } + ++ // Add TX characteristic ++ status = aci_gatt_add_char( ++ serial_svc->svc_handle, ++ UUID_TYPE_128, ++ (const Char_UUID_t*)char_tx_uuid, ++ SERIAL_SVC_DATA_LEN_MAX, ++ CHAR_PROP_READ | CHAR_PROP_INDICATE, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_VARIABLE, ++ &serial_svc->tx_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status); ++ } ++ // Add Flow Control characteristic ++ status = aci_gatt_add_char( ++ serial_svc->svc_handle, ++ UUID_TYPE_128, ++ (const Char_UUID_t*)flow_ctrl_uuid, ++ sizeof(uint32_t), ++ CHAR_PROP_READ | CHAR_PROP_NOTIFY, ++ ATTR_PERMISSION_AUTHEN_READ, ++ GATT_DONT_NOTIFY_EVENTS, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &serial_svc->flow_ctrl_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); ++ } ++ // Add RPC status characteristic ++ status = aci_gatt_add_char( ++ serial_svc->svc_handle, ++ UUID_TYPE_128, ++ (const Char_UUID_t*)rpc_status_uuid, ++ sizeof(SerialServiceRpcStatus), ++ CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, ++ ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, ++ GATT_NOTIFY_ATTRIBUTE_WRITE, ++ 10, ++ CHAR_VALUE_LEN_CONSTANT, ++ &serial_svc->rpc_status_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status); ++ } + serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); + // Allocate buffer size mutex + serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); +@@ -179,12 +196,13 @@ void serial_svc_set_callbacks( + serial_svc->context = context; + serial_svc->buff_size = buff_size; + serial_svc->bytes_ready_to_receive = buff_size; +- + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); +- flipper_gatt_characteristic_update( ++ aci_gatt_update_char_value( + serial_svc->svc_handle, +- &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], +- &buff_size_reversed); ++ serial_svc->flow_ctrl_char_handle, ++ 0, ++ sizeof(uint32_t), ++ (uint8_t*)&buff_size_reversed); + } + + void serial_svc_notify_buffer_is_empty() { +@@ -195,12 +213,13 @@ void serial_svc_notify_buffer_is_empty() { + if(serial_svc->bytes_ready_to_receive == 0) { + FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); + serial_svc->bytes_ready_to_receive = serial_svc->buff_size; +- + uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); +- flipper_gatt_characteristic_update( ++ aci_gatt_update_char_value( + serial_svc->svc_handle, +- &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], +- &buff_size_reversed); ++ serial_svc->flow_ctrl_char_handle, ++ 0, ++ sizeof(uint32_t), ++ (uint8_t*)&buff_size_reversed); + } + furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); + } +@@ -208,8 +227,22 @@ void serial_svc_notify_buffer_is_empty() { + void serial_svc_stop() { + tBleStatus status; + if(serial_svc) { +- for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); ++ // Delete characteristics ++ status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status); ++ } ++ status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status); ++ } ++ status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); ++ } ++ status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle); ++ if(status) { ++ FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status); + } + // Delete service + status = aci_gatt_del_service(serial_svc->svc_handle); +@@ -240,7 +273,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { + tBleStatus result = aci_gatt_update_char_value_ext( + 0, + serial_svc->svc_handle, +- serial_svc->chars[SerialSvcGattCharacteristicTx].handle, ++ serial_svc->tx_char_handle, + remained ? 0x00 : 0x02, + data_len, + value_offset, +diff --git a/firmware/targets/f7/ble_glue/services/serial_service.h b/firmware/targets/f7/ble_glue/serial_service.h +similarity index 100% +rename from firmware/targets/f7/ble_glue/services/serial_service.h +rename to firmware/targets/f7/ble_glue/serial_service.h +diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.c b/firmware/targets/f7/ble_glue/services/dev_info_service.c +deleted file mode 100644 +index 5bee97b416..0000000000 +--- a/firmware/targets/f7/ble_glue/services/dev_info_service.c ++++ /dev/null +@@ -1,176 +0,0 @@ +-#include "dev_info_service.h" +-#include "app_common.h" +-#include "gatt_char.h" +-#include +- +-#include +-#include +-#include +- +-#include "dev_info_service_uuid.inc" +- +-#define TAG "BtDevInfoSvc" +- +-typedef enum { +- DevInfoSvcGattCharacteristicMfgName = 0, +- DevInfoSvcGattCharacteristicSerial, +- DevInfoSvcGattCharacteristicFirmwareRev, +- DevInfoSvcGattCharacteristicSoftwareRev, +- DevInfoSvcGattCharacteristicRpcVersion, +- DevInfoSvcGattCharacteristicCount, +-} DevInfoSvcGattCharacteristicId; +- +-#define DEVICE_INFO_HARDWARE_REV_SIZE 4 +-typedef struct { +- uint16_t service_handle; +- FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount]; +- FuriString* version_string; +- char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE]; +-} DevInfoSvc; +- +-static DevInfoSvc* dev_info_svc = NULL; +- +-static const char dev_info_man_name[] = "Flipper Devices Inc."; +-static const char dev_info_serial_num[] = "1.0"; +-static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); +- +-static bool dev_info_char_firmware_rev_callback( +- const void* context, +- const uint8_t** data, +- uint16_t* data_len) { +- const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; +- *data_len = sizeof(dev_info_svc->hardware_revision); +- if(data) { +- *data = (const uint8_t*)&dev_info_svc->hardware_revision; +- } +- return false; +-} +- +-static bool dev_info_char_software_rev_callback( +- const void* context, +- const uint8_t** data, +- uint16_t* data_len) { +- const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; +- *data_len = furi_string_size(dev_info_svc->version_string); +- if(data) { +- *data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string); +- } +- return false; +-} +- +-static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] = +- {[DevInfoSvcGattCharacteristicMfgName] = +- {.name = "Manufacturer Name", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = sizeof(dev_info_man_name) - 1, +- .data.fixed.ptr = (const uint8_t*)&dev_info_man_name, +- .uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [DevInfoSvcGattCharacteristicSerial] = +- {.name = "Serial Number", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = sizeof(dev_info_serial_num) - 1, +- .data.fixed.ptr = (const uint8_t*)&dev_info_serial_num, +- .uuid.Char_UUID_16 = SERIAL_NUMBER_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [DevInfoSvcGattCharacteristicFirmwareRev] = +- {.name = "Firmware Revision", +- .data_prop_type = FlipperGattCharacteristicDataCallback, +- .data.callback.context = &dev_info_svc, +- .data.callback.fn = dev_info_char_firmware_rev_callback, +- .uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [DevInfoSvcGattCharacteristicSoftwareRev] = +- {.name = "Software Revision", +- .data_prop_type = FlipperGattCharacteristicDataCallback, +- .data.callback.context = &dev_info_svc, +- .data.callback.fn = dev_info_char_software_rev_callback, +- .uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [DevInfoSvcGattCharacteristicRpcVersion] = { +- .name = "RPC Version", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = sizeof(dev_info_rpc_version) - 1, +- .data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version, +- .uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID, +- .uuid_type = UUID_TYPE_128, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}}; +- +-void dev_info_svc_start() { +- dev_info_svc = malloc(sizeof(DevInfoSvc)); +- dev_info_svc->version_string = furi_string_alloc_printf( +- "%s %s %s %s", +- version_get_githash(NULL), +- version_get_version(NULL), +- version_get_gitbranchnum(NULL), +- version_get_builddate(NULL)); +- snprintf( +- dev_info_svc->hardware_revision, +- sizeof(dev_info_svc->hardware_revision), +- "%d", +- version_get_target(NULL)); +- tBleStatus status; +- +- // Add Device Information Service +- uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; +- status = aci_gatt_add_service( +- UUID_TYPE_16, +- (Service_UUID_t*)&uuid, +- PRIMARY_SERVICE, +- 1 + 2 * DevInfoSvcGattCharacteristicCount, +- &dev_info_svc->service_handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); +- } +- +- for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_init( +- dev_info_svc->service_handle, +- &dev_info_svc_chars[i], +- &dev_info_svc->characteristics[i]); +- flipper_gatt_characteristic_update( +- dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL); +- } +-} +- +-void dev_info_svc_stop() { +- tBleStatus status; +- if(dev_info_svc) { +- furi_string_free(dev_info_svc->version_string); +- // Delete service characteristics +- for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_delete( +- dev_info_svc->service_handle, &dev_info_svc->characteristics[i]); +- } +- // Delete service +- status = aci_gatt_del_service(dev_info_svc->service_handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); +- } +- free(dev_info_svc); +- dev_info_svc = NULL; +- } +-} +- +-bool dev_info_svc_is_started() { +- return dev_info_svc != NULL; +-} +diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc +deleted file mode 100644 +index ad520f62e5..0000000000 +--- a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc ++++ /dev/null +@@ -1,3 +0,0 @@ +-#define DEV_INVO_RPC_VERSION_UID \ +- { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } +- +diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c +deleted file mode 100644 +index 9b6a44f61b..0000000000 +--- a/firmware/targets/f7/ble_glue/services/gatt_char.c ++++ /dev/null +@@ -1,123 +0,0 @@ +-#include "gatt_char.h" +- +-#include +- +-#define TAG "GattChar" +- +-#define GATT_MIN_READ_KEY_SIZE (10) +- +-void flipper_gatt_characteristic_init( +- uint16_t svc_handle, +- const FlipperGattCharacteristicParams* char_descriptor, +- FlipperGattCharacteristicInstance* char_instance) { +- furi_assert(char_descriptor); +- furi_assert(char_instance); +- +- // Copy the descriptor to the instance, since it may point to stack memory +- // TODO: only copy if really comes from stack +- char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); +- memcpy( +- (void*)char_instance->characteristic, +- char_descriptor, +- sizeof(FlipperGattCharacteristicParams)); +- +- uint16_t char_data_size = 0; +- if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { +- char_data_size = char_descriptor->data.fixed.length; +- } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { +- char_descriptor->data.callback.fn( +- char_descriptor->data.callback.context, NULL, &char_data_size); +- } +- +- tBleStatus status = aci_gatt_add_char( +- svc_handle, +- char_descriptor->uuid_type, +- &char_descriptor->uuid, +- char_data_size, +- char_descriptor->char_properties, +- char_descriptor->security_permissions, +- char_descriptor->gatt_evt_mask, +- GATT_MIN_READ_KEY_SIZE, +- char_descriptor->is_variable, +- &char_instance->handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); +- } +- +- char_instance->descriptor_handle = 0; +- if((status == 0) && char_descriptor->descriptor_params) { +- uint8_t const* char_data = NULL; +- const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = +- char_descriptor->descriptor_params; +- bool release_data = char_data_descriptor->data_callback.fn( +- char_data_descriptor->data_callback.context, &char_data, &char_data_size); +- +- status = aci_gatt_add_char_desc( +- svc_handle, +- char_instance->handle, +- char_data_descriptor->uuid_type, +- &char_data_descriptor->uuid, +- char_data_descriptor->max_length, +- char_data_size, +- char_data, +- char_data_descriptor->security_permissions, +- char_data_descriptor->access_permissions, +- char_data_descriptor->gatt_evt_mask, +- GATT_MIN_READ_KEY_SIZE, +- char_data_descriptor->is_variable, +- &char_instance->descriptor_handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); +- } +- if(release_data) { +- free((void*)char_data); +- } +- } +-} +- +-void flipper_gatt_characteristic_delete( +- uint16_t svc_handle, +- FlipperGattCharacteristicInstance* char_instance) { +- tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); +- if(status) { +- FURI_LOG_E( +- TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); +- } +- free((void*)char_instance->characteristic); +-} +- +-bool flipper_gatt_characteristic_update( +- uint16_t svc_handle, +- FlipperGattCharacteristicInstance* char_instance, +- const void* source) { +- furi_assert(char_instance); +- const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; +- FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); +- +- const uint8_t* char_data = NULL; +- uint16_t char_data_size = 0; +- bool release_data = false; +- if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { +- char_data = char_descriptor->data.fixed.ptr; +- if(source) { +- char_data = (uint8_t*)source; +- } +- char_data_size = char_descriptor->data.fixed.length; +- } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { +- const void* context = char_descriptor->data.callback.context; +- if(source) { +- context = source; +- } +- release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); +- } +- +- tBleStatus result = aci_gatt_update_char_value( +- svc_handle, char_instance->handle, 0, char_data_size, char_data); +- if(result) { +- FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); +- } +- if(release_data) { +- free((void*)char_data); +- } +- return result != BLE_STATUS_SUCCESS; +-} +\ No newline at end of file +diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.h b/firmware/targets/f7/ble_glue/services/gatt_char.h +deleted file mode 100644 +index 959ab67a49..0000000000 +--- a/firmware/targets/f7/ble_glue/services/gatt_char.h ++++ /dev/null +@@ -1,96 +0,0 @@ +-#pragma once +- +-#include +-#include +-#include +- +-#include +- +-#ifdef __cplusplus +-extern "C" { +-#endif +- +-// Callback signature for getting characteristic data +-// Is called when characteristic is created to get max data length. Data ptr is NULL in this case +-// The result is passed to aci_gatt_add_char as "Char_Value_Length" +-// For updates, called with a context - see flipper_gatt_characteristic_update +-// Returns true if *data ownership is transferred to the caller and will be freed +-typedef bool (*cbFlipperGattCharacteristicData)( +- const void* context, +- const uint8_t** data, +- uint16_t* data_len); +- +-typedef enum { +- FlipperGattCharacteristicDataFixed, +- FlipperGattCharacteristicDataCallback, +-} FlipperGattCharacteristicDataType; +- +-typedef struct { +- Char_Desc_Uuid_t uuid; +- struct { +- cbFlipperGattCharacteristicData fn; +- const void* context; +- } data_callback; +- uint8_t uuid_type; +- uint8_t max_length; +- uint8_t security_permissions; +- uint8_t access_permissions; +- uint8_t gatt_evt_mask; +- uint8_t is_variable; +-} FlipperGattCharacteristicDescriptorParams; +- +-typedef struct { +- const char* name; +- FlipperGattCharacteristicDescriptorParams* descriptor_params; +- union { +- struct { +- const uint8_t* ptr; +- uint16_t length; +- } fixed; +- struct { +- cbFlipperGattCharacteristicData fn; +- const void* context; +- } callback; +- } data; +- Char_UUID_t uuid; +- // Some packed bitfields to save space +- FlipperGattCharacteristicDataType data_prop_type : 2; +- uint8_t is_variable : 2; +- uint8_t uuid_type : 2; +- uint8_t char_properties; +- uint8_t security_permissions; +- uint8_t gatt_evt_mask; +-} FlipperGattCharacteristicParams; +- +-_Static_assert( +- sizeof(FlipperGattCharacteristicParams) == 36, +- "FlipperGattCharacteristicParams size must be 36 bytes"); +- +-typedef struct { +- const FlipperGattCharacteristicParams* characteristic; +- uint16_t handle; +- uint16_t descriptor_handle; +-} FlipperGattCharacteristicInstance; +- +-// Initialize a characteristic instance; copies the characteristic descriptor into the instance +-void flipper_gatt_characteristic_init( +- uint16_t svc_handle, +- const FlipperGattCharacteristicParams* char_descriptor, +- FlipperGattCharacteristicInstance* char_instance); +- +-// Delete a characteristic instance; frees the copied characteristic descriptor from the instance +-void flipper_gatt_characteristic_delete( +- uint16_t svc_handle, +- FlipperGattCharacteristicInstance* char_instance); +- +-// Update a characteristic instance; if source==NULL, uses the data from the characteristic +-// - For fixed data, fixed.ptr is used as the source if source==NULL +-// - For callback-based data, collback.context is passed as the context if source==NULL +-bool flipper_gatt_characteristic_update( +- uint16_t svc_handle, +- FlipperGattCharacteristicInstance* char_instance, +- const void* source); +- +-#ifdef __cplusplus +-} +-#endif +\ No newline at end of file +diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c +deleted file mode 100644 +index 11f10b7b38..0000000000 +--- a/firmware/targets/f7/ble_glue/services/hid_service.c ++++ /dev/null +@@ -1,365 +0,0 @@ +-#include "hid_service.h" +-#include "app_common.h" +-#include +-#include "gatt_char.h" +- +-#include +- +-#define TAG "BtHid" +- +-typedef enum { +- HidSvcGattCharacteristicProtocolMode = 0, +- HidSvcGattCharacteristicReportMap, +- HidSvcGattCharacteristicInfo, +- HidSvcGattCharacteristicCtrlPoint, +- HidSvcGattCharacteristicLed, +- HidSvcGattCharacteristicCount, +-} HidSvcGattCharacteristicId; +- +-typedef struct { +- uint8_t report_idx; +- uint8_t report_type; +-} HidSvcReportId; +- +-static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); +- +-static bool +- hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { +- const HidSvcReportId* report_id = context; +- *data_len = sizeof(HidSvcReportId); +- if(data) { +- *data = (const uint8_t*)report_id; +- } +- return false; +-} +- +-typedef struct { +- const void* data_ptr; +- uint16_t data_len; +-} HidSvcDataWrapper; +- +-static bool +- hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { +- const HidSvcDataWrapper* report_data = context; +- if(data) { +- *data = report_data->data_ptr; +- *data_len = report_data->data_len; +- } else { +- *data_len = HID_SVC_REPORT_MAP_MAX_LEN; +- } +- return false; +-} +- +-// LED Descriptor params for BadBT +- +-static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; +- +-static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { +- .uuid_type = UUID_TYPE_16, +- .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, +- .max_length = HID_SVC_REPORT_REF_LEN, +- .data_callback.fn = hid_svc_char_desc_data_callback, +- .data_callback.context = led_desc_context_buf, +- .security_permissions = ATTR_PERMISSION_NONE, +- .access_permissions = ATTR_ACCESS_READ_WRITE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT, +-}; +- +-static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { +- [HidSvcGattCharacteristicProtocolMode] = +- {.name = "Protocol Mode", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = 1, +- .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [HidSvcGattCharacteristicReportMap] = +- {.name = "Report Map", +- .data_prop_type = FlipperGattCharacteristicDataCallback, +- .data.callback.fn = hid_svc_report_data_callback, +- .data.callback.context = NULL, +- .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_VARIABLE}, +- [HidSvcGattCharacteristicInfo] = +- {.name = "HID Information", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = HID_SVC_INFO_LEN, +- .data.fixed.ptr = NULL, +- .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [HidSvcGattCharacteristicCtrlPoint] = +- {.name = "HID Control Point", +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, +- .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, +- .is_variable = CHAR_VALUE_LEN_CONSTANT}, +- [HidSvcGattCharacteristicLed] = +- { +- .name = +- "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars +- .data_prop_type = FlipperGattCharacteristicDataFixed, +- .data.fixed.length = 1, +- .uuid.Char_UUID_16 = REPORT_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | +- GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, +- .is_variable = CHAR_VALUE_LEN_CONSTANT, +- .descriptor_params = &hid_svc_char_descr_led, +- }, +-}; +- +-static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { +- .uuid_type = UUID_TYPE_16, +- .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, +- .max_length = HID_SVC_REPORT_REF_LEN, +- .data_callback.fn = hid_svc_char_desc_data_callback, +- .security_permissions = ATTR_PERMISSION_NONE, +- .access_permissions = ATTR_ACCESS_READ_WRITE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_CONSTANT, +-}; +- +-static const FlipperGattCharacteristicParams hid_svc_report_template = { +- .name = "Report", +- .data_prop_type = FlipperGattCharacteristicDataCallback, +- .data.callback.fn = hid_svc_report_data_callback, +- .data.callback.context = NULL, +- .uuid.Char_UUID_16 = REPORT_CHAR_UUID, +- .uuid_type = UUID_TYPE_16, +- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, +- .security_permissions = ATTR_PERMISSION_NONE, +- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, +- .is_variable = CHAR_VALUE_LEN_VARIABLE, +-}; +- +-typedef struct { +- uint16_t svc_handle; +- FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; +- FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; +- FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; +- FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; +- // led state +- HidLedStateEventCallback led_state_event_callback; +- void* led_state_ctx; +-} HIDSvc; +- +-static HIDSvc* hid_svc = NULL; +- +-static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { +- SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; +- hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); +- evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; +- // aci_gatt_attribute_modified_event_rp0* attribute_modified; +- if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { +- if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { +- // Process modification events +- ret = SVCCTL_EvtAckFlowEnable; +- } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { +- // Process notification confirmation +- ret = SVCCTL_EvtAckFlowEnable; +- } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { +- // LED Characteristic and descriptor for BadBT to get numlock state for altchars +- // +- // Process write request +- aci_gatt_write_permit_req_event_rp0* req = +- (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; +- +- furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); +- +- // this check is likely to be incorrect, it will actually work in our case +- // but we need to investigate gatt api to see what is the rules +- // that specify attibute handle value from char handle (or the reverse) +- if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { +- hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); +- aci_gatt_write_resp( +- req->Connection_Handle, +- req->Attribute_Handle, +- 0x00, /* write_status = 0 (no error))*/ +- 0x00, /* err_code */ +- req->Data_Length, +- req->Data); +- aci_gatt_write_char_value( +- req->Connection_Handle, +- hid_svc->chars[HidSvcGattCharacteristicLed].handle, +- req->Data_Length, +- req->Data); +- ret = SVCCTL_EvtAckFlowEnable; +- } +- } +- } +- return ret; +-} +- +-void hid_svc_start() { +- tBleStatus status; +- hid_svc = malloc(sizeof(HIDSvc)); +- Service_UUID_t svc_uuid = {}; +- +- // Register event handler +- SVCCTL_RegisterSvcHandler(hid_svc_event_handler); +- // Add service +- svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; +- /** +- * Add Human Interface Device Service +- */ +- status = aci_gatt_add_service( +- UUID_TYPE_16, +- &svc_uuid, +- PRIMARY_SERVICE, +- 2 + /* protocol mode */ +- (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + +- (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + +- 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ +- &hid_svc->svc_handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to add HID service: %d", status); +- } +- +- for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_init( +- hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); +- } +- uint8_t protocol_mode = 1; +- flipper_gatt_characteristic_update( +- hid_svc->svc_handle, +- &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], +- &protocol_mode); +- +- // reports +- FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; +- FlipperGattCharacteristicParams report_char; +- HidSvcReportId report_id; +- +- memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); +- memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); +- +- hid_svc_char_descr.data_callback.context = &report_id; +- report_char.descriptor_params = &hid_svc_char_descr; +- +- typedef struct { +- uint8_t report_type; +- uint8_t report_count; +- FlipperGattCharacteristicInstance* chars; +- } HidSvcReportCharProps; +- +- HidSvcReportCharProps hid_report_chars[] = { +- {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, +- {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, +- {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, +- }; +- +- for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); +- report_type_idx++) { +- report_id.report_type = hid_report_chars[report_type_idx].report_type; +- for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; +- report_idx++) { +- report_id.report_idx = report_idx + 1; +- flipper_gatt_characteristic_init( +- hid_svc->svc_handle, +- &report_char, +- &hid_report_chars[report_type_idx].chars[report_idx]); +- } +- } +-} +- +-bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { +- furi_assert(data); +- furi_assert(hid_svc); +- +- HidSvcDataWrapper report_data = { +- .data_ptr = data, +- .data_len = len, +- }; +- return flipper_gatt_characteristic_update( +- hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); +-} +- +-bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { +- furi_assert(data); +- furi_assert(hid_svc); +- furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); +- +- HidSvcDataWrapper report_data = { +- .data_ptr = data, +- .data_len = len, +- }; +- return flipper_gatt_characteristic_update( +- hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); +-} +- +-bool hid_svc_update_info(uint8_t* data) { +- furi_assert(data); +- furi_assert(hid_svc); +- +- return flipper_gatt_characteristic_update( +- hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); +-} +- +-void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { +- furi_assert(hid_svc); +- furi_assert(callback); +- furi_assert(context); +- +- hid_svc->led_state_event_callback = callback; +- hid_svc->led_state_ctx = context; +-} +- +-bool hid_svc_is_started() { +- return hid_svc != NULL; +-} +- +-void hid_svc_stop() { +- tBleStatus status; +- if(hid_svc) { +- // Delete characteristics +- for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { +- flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); +- } +- +- typedef struct { +- uint8_t report_count; +- FlipperGattCharacteristicInstance* chars; +- } HidSvcReportCharProps; +- +- HidSvcReportCharProps hid_report_chars[] = { +- {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, +- {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, +- {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, +- }; +- +- for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); +- report_type_idx++) { +- for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; +- report_idx++) { +- flipper_gatt_characteristic_delete( +- hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); +- } +- } +- +- // Delete service +- status = aci_gatt_del_service(hid_svc->svc_handle); +- if(status) { +- FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); +- } +- free(hid_svc); +- hid_svc = NULL; +- } +-} +diff --git a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc +deleted file mode 100644 +index a297d9ad60..0000000000 +--- a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc ++++ /dev/null +@@ -1,12 +0,0 @@ +- +-static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ +- { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; +- +-#define SERIAL_SVC_TX_CHAR_UUID \ +- { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +-#define SERIAL_SVC_RX_CHAR_UUID \ +- { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +-#define SERIAL_SVC_FLOW_CONTROL_UUID \ +- { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +-#define SERIAL_SVC_RPC_STATUS_UUID \ +- { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } +diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c +index 22d286abd0..f71cd37762 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_bt.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c +@@ -8,7 +8,8 @@ + #include + #include + #include +-#include ++#include "battery_service.h" ++ + #include + + #define TAG "FuriHalBt" +diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +index 2bbfc15231..8e05a99049 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +@@ -1,11 +1,11 @@ + #include + #include +-#include +-#include +-#include ++#include "usb_hid.h" ++#include "dev_info_service.h" ++#include "battery_service.h" ++#include "hid_service.h" + + #include +-#include + + #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) + #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) +@@ -221,7 +221,7 @@ void furi_hal_bt_hid_start() { + FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | + FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, + }; +- hid_svc_update_info(hid_info_val); ++ hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); + } + + void furi_hal_bt_hid_stop() { +diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +index 2927d946f9..2539e6bd0e 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +@@ -1,7 +1,7 @@ + #include +-#include +-#include +-#include ++#include "dev_info_service.h" ++#include "battery_service.h" ++#include "serial_service.h" + + #include + +diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h +index d6a6e8c42b..bfe4a67c3c 100644 +--- a/firmware/targets/furi_hal_include/furi_hal_bt.h ++++ b/firmware/targets/furi_hal_include/furi_hal_bt.h +@@ -8,7 +8,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + +diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +index 0472d31d18..1b6e79ab07 100644 +--- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h ++++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +@@ -1,6 +1,6 @@ + #pragma once + +-#include ++#include "serial_service.h" + + #ifdef __cplusplus + extern "C" { From ef66a62a88bc13e1c08ab8d9f6a9ff1ac1cc47a6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 12 Jun 2023 04:56:22 +0300 Subject: [PATCH 161/370] Update changelog --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 957ce3f9e..7e6f0cab6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### New changes * If you have copied any apps manually into `apps` folder - remove `apps` folder or that specific apps you copied on your microSD before installing this release to avoid issues due to OFW API version update! If you using regular builds or extra pack builds (e) without your manually added apps, all included apps will be installed automatically, no extra actions needed! * Settings->LCD and Notifications will be resetted to default due to new Contrast setting from OFW +* Core2 (Crash in idle) issues was reduced to current possible minimum, you can try using DeepSleep again (Sleep Method = Default) (+ more checks was added, if you get `Slow HSE/PLL startup` message more than one time, create issue with steps what to do to reproduce it again) ----- * Plugins: **New RFID 125KHz and iButton Fuzzers (remake from scratch + new features)** (by @gid9798 | PR #507) * Plugins: SubGHz Bruteforcer -> Time delay (between signals) setting (hold Up in main screen(says Up to Save)) + allow more repeats (by @gid9798 & @xMasterX) @@ -65,9 +66,11 @@ and all other great people who supported our project and me (xMasterX), thanks t **Recommended option - Web Updater** +### What `n`, `e`, `r` means? What I need to download if I don't want to use Web updater? What means `n` or `e` in - `flipper-z-f7-update-(version)(n / e).tgz` ? - `n` means this build comes without our custom animations, only official flipper animations, -`e` means build has extra apps pack preinstalled +`e` means build has extra apps pack preinstalled, +`r` means RGB patch for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) -Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for mobile app / qFlipper +Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for mobile app / qFlipper / web From 98579acf2dc014a5b6a03eb6aec4fdf90509207a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 03:21:58 +0100 Subject: [PATCH 162/370] BadKB setting string size fixes --- applications/main/bad_kb/bad_kb_app.c | 6 +++--- applications/main/bad_kb/helpers/ducky_script.h | 3 ++- .../main/bad_kb/scenes/bad_kb_scene_config_usb_name.c | 10 +++++----- firmware/targets/furi_hal_include/furi_hal_usb_hid.h | 6 ++++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 5459fef10..084683468 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -46,7 +46,7 @@ static void bad_kb_load_settings(BadKbApp* app) { furi_string_reset(app->keyboard_layout); } if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) { - strcpy(cfg->bt_name, furi_string_get_cstr(tmp_str)); + strlcpy(cfg->bt_name, furi_string_get_cstr(tmp_str), BAD_KB_NAME_LEN); } else { strcpy(cfg->bt_name, ""); } @@ -54,13 +54,13 @@ static void bad_kb_load_settings(BadKbApp* app) { memcpy(cfg->bt_mac, BAD_KB_EMPTY_MAC, BAD_KB_MAC_LEN); } if(flipper_format_read_string(file, "Usb_Manuf", tmp_str) && !furi_string_empty(tmp_str)) { - strcpy(cfg->usb_cfg.manuf, furi_string_get_cstr(tmp_str)); + strlcpy(cfg->usb_cfg.manuf, furi_string_get_cstr(tmp_str), BAD_KB_USB_LEN); } else { strcpy(cfg->usb_cfg.manuf, ""); } if(flipper_format_read_string(file, "Usb_Product", tmp_str) && !furi_string_empty(tmp_str)) { - strcpy(cfg->usb_cfg.product, furi_string_get_cstr(tmp_str)); + strlcpy(cfg->usb_cfg.product, furi_string_get_cstr(tmp_str), BAD_KB_USB_LEN); } else { strcpy(cfg->usb_cfg.product, ""); } diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index 23f225ab7..89b72bf99 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -105,6 +105,7 @@ BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); #define BAD_KB_NAME_LEN FURI_HAL_BT_ADV_NAME_LENGTH #define BAD_KB_MAC_LEN GAP_MAC_ADDR_SIZE +#define BAD_KB_USB_LEN HID_MANUF_PRODUCT_NAME_LEN extern const uint8_t BAD_KB_EMPTY_MAC[BAD_KB_MAC_LEN]; extern uint8_t BAD_KB_BOUND_MAC[BAD_KB_MAC_LEN]; // For remember mode @@ -137,7 +138,7 @@ struct BadKbApp { VariableItemList* var_item_list; TextInput* text_input; ByteInput* byte_input; - char usb_name_buf[32]; + char usb_name_buf[BAD_KB_USB_LEN]; uint16_t usb_vidpid_buf[2]; char bt_name_buf[BAD_KB_NAME_LEN]; uint8_t bt_mac_buf[BAD_KB_MAC_LEN]; diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c index 250a3fde7..381c2cdf0 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb_name.c @@ -11,10 +11,10 @@ void bad_kb_scene_config_usb_name_on_enter(void* context) { TextInput* text_input = bad_kb->text_input; if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { - strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb_cfg.manuf, 32); + strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb_cfg.manuf, BAD_KB_USB_LEN); text_input_set_header_text(text_input, "Set USB manufacturer name"); } else { - strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb_cfg.product, 32); + strlcpy(bad_kb->usb_name_buf, bad_kb->config.usb_cfg.product, BAD_KB_USB_LEN); text_input_set_header_text(text_input, "Set USB product name"); } @@ -23,7 +23,7 @@ void bad_kb_scene_config_usb_name_on_enter(void* context) { bad_kb_scene_config_usb_name_text_input_callback, bad_kb, bad_kb->usb_name_buf, - 32, + BAD_KB_USB_LEN, true); view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewTextInput); @@ -37,9 +37,9 @@ bool bad_kb_scene_config_usb_name_on_event(void* context, SceneManagerEvent even consumed = true; if(event.event == BadKbAppCustomEventTextInputDone) { if(scene_manager_get_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsbName)) { - strlcpy(bad_kb->config.usb_cfg.manuf, bad_kb->usb_name_buf, 32); + strlcpy(bad_kb->config.usb_cfg.manuf, bad_kb->usb_name_buf, BAD_KB_USB_LEN); } else { - strlcpy(bad_kb->config.usb_cfg.product, bad_kb->usb_name_buf, 32); + strlcpy(bad_kb->config.usb_cfg.product, bad_kb->usb_name_buf, BAD_KB_USB_LEN); } bad_kb_config_refresh(bad_kb); } diff --git a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h index fe2486ceb..887f81617 100644 --- a/firmware/targets/furi_hal_include/furi_hal_usb_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_usb_hid.h @@ -9,6 +9,8 @@ extern "C" { #endif +#define HID_MANUF_PRODUCT_NAME_LEN 32 + #define HID_VID_DEFAULT 0x046D #define HID_PID_DEFAULT 0xC529 @@ -167,8 +169,8 @@ typedef struct { // Good job knobheads, these should be uint16_t uint32_t vid; uint32_t pid; - char manuf[32]; - char product[32]; + char manuf[HID_MANUF_PRODUCT_NAME_LEN]; + char product[HID_MANUF_PRODUCT_NAME_LEN]; } FuriHalUsbHidConfig; typedef void (*HidStateCallback)(bool state, void* context); From 63b350aedc0e2719727d60f3065fa87d9cab847b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 12 Jun 2023 05:22:12 +0300 Subject: [PATCH 163/370] Fix noanims build --- .gitignore | 4 ++++ CHANGELOG.md | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bae5d6dbd..35e5cf2b5 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,10 @@ null.d # bundle output /dist +/artifacts-default +/artifacts-ofw-anims +/artifacts-rgb-patch +/artifacts-extra-apps # SCons build dir /build diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e6f0cab6..f77894a48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * SubGHz: Remove broken modulation that was causing buffer overrun (fixes issue #506) * SubGHz: Notifications fixes (by @wosk | PR #464) * GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509) +* CI/CD: Provide builds with RGB patch for modded flippers (with special led board installed) * Infrared: `RCA` protocol support * Infrared: Update universal remote assets - add new ACs and TCL TV * API: Add furi_hal_version_uid_default (+ Fix TOTP) (by @ClaraCrazy | PR #502) @@ -69,7 +70,7 @@ and all other great people who supported our project and me (xMasterX), thanks t ### What `n`, `e`, `r` means? What I need to download if I don't want to use Web updater? What means `n` or `e` in - `flipper-z-f7-update-(version)(n / e).tgz` ? - `n` means this build comes without our custom animations, only official flipper animations, `e` means build has extra apps pack preinstalled, -`r` means RGB patch for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) +`r` means RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for mobile app / qFlipper / web From 29bc041c89119baf83a7f31ae94942bf705c057f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 03:22:36 +0100 Subject: [PATCH 164/370] Shorter setting name to fit menu --- applications/main/bad_kb/scenes/bad_kb_scene_config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.c b/applications/main/bad_kb/scenes/bad_kb_scene_config.c index f17a52b97..be285a467 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.c @@ -17,7 +17,7 @@ enum VarItemListIndexBt { }; enum VarItemListIndexUsb { - VarItemListIndexUsbManufacturerName = VarItemListIndexConnection + 1, + VarItemListIndexUsbManufacturer = VarItemListIndexConnection + 1, VarItemListIndexUsbProductName, VarItemListIndexUsbVidPid, VarItemListIndexUsbRandomizeVidPid, @@ -84,7 +84,7 @@ void bad_kb_scene_config_on_enter(void* context) { variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); } } else { - item = variable_item_list_add(var_item_list, "USB Manufacturer Name", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "USB Manufacturer", 0, NULL, bad_kb); if(bad_kb->set_usb_id) { variable_item_set_locked(item, true, "Script has\nID cmd!\nLocked to\nset Mname!"); } @@ -151,7 +151,7 @@ bool bad_kb_scene_config_on_event(void* context, SceneManagerEvent event) { } } else { switch(event.event) { - case VarItemListIndexUsbManufacturerName: + case VarItemListIndexUsbManufacturer: scene_manager_set_scene_state( bad_kb->scene_manager, BadKbSceneConfigUsbName, true); scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsbName); From 4f0d2012691c3f9e8e3baf23cf730301c208547c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 12 Jun 2023 05:40:00 +0300 Subject: [PATCH 165/370] Fix changelog updater links --- .drone.yml | 16 ++++++++-------- CHANGELOG.md | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.drone.yml b/.drone.yml index f7c6d07de..b626de5c4 100644 --- a/.drone.yml +++ b/.drone.yml @@ -26,14 +26,6 @@ steps: - mv dist/f7-C/* artifacts-default/ - ls -laS artifacts-default - ls -laS artifacts-default/f7-update-${DRONE_TAG} - - sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md - - echo '# [Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md - - echo '' >> CHANGELOG.md - - echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md - - echo '' >> CHANGELOG.md - - echo '### [Version with RGB patch - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md - - echo '' >> CHANGELOG.md - - echo '## [Version with Extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md environment: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link @@ -94,6 +86,14 @@ steps: - mv dist/f7-C/* artifacts-ofw-anims/ - ls -laS artifacts-ofw-anims - ls -laS artifacts-ofw-anims/f7-update-${DRONE_TAG}n + - sed -i 's/(version)/'${DRONE_TAG}'/g' CHANGELOG.md + - echo '# [Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw/${DRONE_TAG}/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')' >> CHANGELOG.md + - echo '' >> CHANGELOG.md + - echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md + - echo '' >> CHANGELOG.md + - echo '### [Version with RGB patch - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md + - echo '' >> CHANGELOG.md + - echo '## [Version with Extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md environment: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link diff --git a/CHANGELOG.md b/CHANGELOG.md index f77894a48..3dbc1db79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,8 +67,8 @@ and all other great people who supported our project and me (xMasterX), thanks t **Recommended option - Web Updater** -### What `n`, `e`, `r` means? What I need to download if I don't want to use Web updater? -What means `n` or `e` in - `flipper-z-f7-update-(version)(n / e).tgz` ? - `n` means this build comes without our custom animations, only official flipper animations, +### What `n`, `r`, `e` means? What I need to download if I don't want to use Web updater? +What means `n` or `e` in - `flipper-z-f7-update-(version)(n / r / e).tgz` ? - `n` means this build comes without our custom animations, only official flipper animations, `e` means build has extra apps pack preinstalled, `r` means RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) From aa747cd8bf1b94f54d2f67637b4a9efa7910b726 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Mon, 12 Jun 2023 19:13:47 +1000 Subject: [PATCH 166/370] Clean up duplicated picopass command definitions. --- .../external/picopass/picopass_device.c | 22 +- .../external/picopass/picopass_device.h | 49 +---- .../external/picopass/picopass_keys.h | 12 +- .../external/picopass/picopass_worker.c | 193 +++++++++--------- .../external/picopass/rfal_picopass.c | 10 +- .../external/picopass/rfal_picopass.h | 41 +++- .../scenes/picopass_scene_device_info.c | 8 +- .../picopass/scenes/picopass_scene_key_menu.c | 8 +- .../scenes/picopass_scene_read_card.c | 2 +- .../scenes/picopass_scene_read_card_success.c | 20 +- .../picopass_scene_read_factory_success.c | 2 +- 11 files changed, 186 insertions(+), 181 deletions(-) diff --git a/applications/external/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c index 53778cfb3..92fc86c73 100644 --- a/applications/external/picopass/picopass_device.c +++ b/applications/external/picopass/picopass_device.c @@ -67,13 +67,14 @@ static bool picopass_device_save_file( if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break; if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break; if(!flipper_format_write_hex( - file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN)) + file, "Credential", pacs->credential, RFAL_PICOPASS_BLOCK_LEN)) break; if(pacs->pin_length > 0) { - if(!flipper_format_write_hex(file, "PIN\t\t", pacs->pin0, PICOPASS_BLOCK_LEN)) + if(!flipper_format_write_hex( + file, "PIN\t\t", pacs->pin0, RFAL_PICOPASS_BLOCK_LEN)) break; if(!flipper_format_write_hex( - file, "PIN(cont.)\t", pacs->pin1, PICOPASS_BLOCK_LEN)) + file, "PIN(cont.)\t", pacs->pin1, RFAL_PICOPASS_BLOCK_LEN)) break; } } @@ -86,7 +87,10 @@ static bool picopass_device_save_file( for(size_t i = 0; i < app_limit; i++) { furi_string_printf(temp_str, "Block %d", i); if(!flipper_format_write_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + file, + furi_string_get_cstr(temp_str), + AA1[i].data, + RFAL_PICOPASS_BLOCK_LEN)) { block_saved = false; break; } @@ -160,7 +164,7 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo for(size_t i = 0; i < 6; i++) { furi_string_printf(temp_str, "Block %d", i); if(!flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + file, furi_string_get_cstr(temp_str), AA1[i].data, RFAL_PICOPASS_BLOCK_LEN)) { block_read = false; break; } @@ -172,7 +176,7 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo for(size_t i = 6; i < app_limit; i++) { furi_string_printf(temp_str, "Block %d", i); if(!flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { + file, furi_string_get_cstr(temp_str), AA1[i].data, RFAL_PICOPASS_BLOCK_LEN)) { block_read = false; break; } @@ -335,9 +339,9 @@ ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pa } } else if(pacs->encryption == PicopassDeviceEncryptionNone) { FURI_LOG_D(TAG, "No Encryption"); - memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN); + memcpy(pacs->credential, AA1[7].data, RFAL_PICOPASS_BLOCK_LEN); + memcpy(pacs->pin0, AA1[8].data, RFAL_PICOPASS_BLOCK_LEN); + memcpy(pacs->pin1, AA1[9].data, RFAL_PICOPASS_BLOCK_LEN); } else if(pacs->encryption == PicopassDeviceEncryptionDES) { FURI_LOG_D(TAG, "DES Encrypted"); } else { diff --git a/applications/external/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h index 04c0c6aab..a07032f4a 100644 --- a/applications/external/picopass/picopass_device.h +++ b/applications/external/picopass/picopass_device.h @@ -14,16 +14,19 @@ #define PICOPASS_DEV_NAME_MAX_LEN 22 #define PICOPASS_READER_DATA_MAX_SIZE 64 -#define PICOPASS_BLOCK_LEN 8 #define PICOPASS_MAX_APP_LIMIT 32 #define PICOPASS_CSN_BLOCK_INDEX 0 #define PICOPASS_CONFIG_BLOCK_INDEX 1 -#define PICOPASS_EPURSE_BLOCK_INDEX 2 -#define PICOPASS_KD_BLOCK_INDEX 3 -#define PICOPASS_KC_BLOCK_INDEX 4 -#define PICOPASS_AIA_BLOCK_INDEX 5 -#define PICOPASS_PACS_CFG_BLOCK_INDEX 6 +// These definitions for blocks above 2 only hold for secure cards. +#define PICOPASS_SECURE_EPURSE_BLOCK_INDEX 2 +#define PICOPASS_SECURE_KD_BLOCK_INDEX 3 +#define PICOPASS_SECURE_KC_BLOCK_INDEX 4 +#define PICOPASS_SECURE_AIA_BLOCK_INDEX 5 +// Non-secure cards instead have an AIA at block 2 +#define PICOPASS_NONSECURE_AIA_BLOCK_INDEX 2 +// Only iClass cards +#define PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX 6 // Personalization Mode #define PICOPASS_FUSE_PERS 0x80 @@ -35,38 +38,6 @@ // Read Access, 1 meanns anonymous read enabled, 0 means must auth to read applicaion #define PICOPASS_FUSE_RA 0x01 -// PicoPass command bytes: -// Low nibble used for command -// High nibble used for options and checksum (MSB) -// The only option we care about in 15693 mode is the key -// which is only used by READCHECK, so for simplicity we -// don't bother breaking down the command and flags into parts -// READ: ADDRESS(1) CRC16(2) -> DATA(8) CRC16(2) -// IDENTIFY: No args -> ASNB(8) CRC16(2) -#define PICOPASS_CMD_READ_OR_IDENTIFY 0x0C -// ADDRESS(1) CRC16(2) -> DATA(32) CRC16(2) -#define PICOPASS_CMD_READ4 0x06 -// ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) -> DATA(8) CRC16(2) -#define PICOPASS_CMD_UPDATE 0x87 -// ADDRESS(1) -> DATA(8) -#define PICOPASS_CMD_READCHECK_KD 0x88 -// ADDRESS(1) -> DATA(8) -#define PICOPASS_CMD_READCHECK_KC 0x18 -// CHALLENGE(4) READERSIGNATURE(4) -> CHIPRESPONSE(4) -#define PICOPASS_CMD_CHECK 0x05 -// No args -> SOF -#define PICOPASS_CMD_ACTALL 0x0A -// No args -> SOF -#define PICOPASS_CMD_ACT 0x8E -// ASNB(8)|SERIALNB(8) -> SERIALNB(8) CRC16(2) -#define PICOPASS_CMD_SELECT 0x81 -// No args -> SERIALNB(8) CRC16(2) -#define PICOPASS_CMD_DETECT 0x0F -// No args -> SOF -#define PICOPASS_CMD_HALT 0x00 -// PAGE(1) CRC16(2) -> BLOCK1(8) CRC16(2) -#define PICOPASS_CMD_PAGESEL 0x84 - #define PICOPASS_APP_FOLDER ANY_PATH("picopass") #define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_SHADOW_EXTENSION ".pas" @@ -122,7 +93,7 @@ typedef struct { } PicopassPacs; typedef struct { - uint8_t data[PICOPASS_BLOCK_LEN]; + uint8_t data[RFAL_PICOPASS_BLOCK_LEN]; } PicopassBlock; typedef struct { diff --git a/applications/external/picopass/picopass_keys.h b/applications/external/picopass/picopass_keys.h index 2b5dba661..dc43fc68b 100644 --- a/applications/external/picopass/picopass_keys.h +++ b/applications/external/picopass/picopass_keys.h @@ -2,9 +2,9 @@ #include "picopass_device.h" -extern const uint8_t picopass_iclass_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_factory_credit_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_factory_debit_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xice_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xicl_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xics_key[PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_iclass_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_factory_credit_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_factory_debit_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xice_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xicl_key[RFAL_PICOPASS_BLOCK_LEN]; +extern const uint8_t picopass_xics_key[RFAL_PICOPASS_BLOCK_LEN]; diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index 024ed41fc..2108358dd 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -8,7 +8,7 @@ #define HAS_MASK(x, b) ((x & b) == b) // CSNs from Proxmark3 repo -static const uint8_t loclass_csns[LOCLASS_NUM_CSNS][PICOPASS_BLOCK_LEN] = { +static const uint8_t loclass_csns[LOCLASS_NUM_CSNS][RFAL_PICOPASS_BLOCK_LEN] = { {0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0}, {0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0}, {0x10, 0x97, 0x83, 0x7B, 0xF7, 0xFF, 0x12, 0xE0}, @@ -184,19 +184,19 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7]); rfalPicoPassReadBlockRes aia; - rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia); - memcpy(AA1[PICOPASS_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data)); + rfalPicoPassPollerReadBlock(PICOPASS_SECURE_AIA_BLOCK_INDEX, &aia); + memcpy(AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data)); FURI_LOG_D( TAG, "aia %02x%02x%02x%02x%02x%02x%02x%02x", - AA1[PICOPASS_AIA_BLOCK_INDEX].data[0], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[1], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[2], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[3], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[4], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[5], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[6], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[7]); + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[0], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[1], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[2], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[3], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[4], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[5], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[6], + AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[7]); return ERR_NONE; } @@ -212,7 +212,7 @@ static ReturnCode PicopassPacs* pacs = &dev_data->pacs; uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; - uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; + uint8_t* div_key = AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data; ReturnCode err = ERR_PARAM; @@ -220,7 +220,7 @@ static ReturnCode uint8_t ccnr[12] = {0}; size_t index = 0; - uint8_t key[PICOPASS_BLOCK_LEN] = {0}; + uint8_t key[RFAL_PICOPASS_BLOCK_LEN] = {0}; if(!iclass_elite_dict_check_presence(dict_type)) { FURI_LOG_E(TAG, "Dictionary not found"); @@ -261,7 +261,7 @@ static ReturnCode err = rfalPicoPassPollerCheck(mac, &chkRes); if(err == ERR_NONE) { - memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + memcpy(pacs->key, key, RFAL_PICOPASS_BLOCK_LEN); break; } @@ -305,7 +305,7 @@ ReturnCode picopass_read_card(PicopassBlock* AA1) { PICOPASS_MAX_APP_LIMIT; for(size_t i = 2; i < app_limit; i++) { - if(i == PICOPASS_KD_BLOCK_INDEX) { + if(i == PICOPASS_SECURE_KD_BLOCK_INDEX) { // Skip over Kd block which is populated earlier (READ of Kd returns all FF's) continue; } @@ -380,7 +380,7 @@ ReturnCode picopass_write_card(PicopassBlock* AA1) { FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", i); uint8_t data[9] = {0}; data[0] = i; - memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_MAX_BLOCK_LEN); + memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_BLOCK_LEN); loclass_doMAC_N(data, sizeof(data), div_key, mac); FURI_LOG_D( TAG, @@ -439,12 +439,12 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne } memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN) != 0) { + if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN) != 0) { FURI_LOG_E(TAG, "Wrong CSN for write"); return ERR_REQUEST; } - loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_KD_BLOCK_INDEX].data, mac); + loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, mac); err = rfalPicoPassPollerCheck(mac, &chkRes); if(err != ERR_NONE) { FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); @@ -462,7 +462,7 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne newBlock[5], newBlock[6], newBlock[7]}; - loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_KD_BLOCK_INDEX].data, mac); + loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, mac); FURI_LOG_D( TAG, "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x", @@ -515,7 +515,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { uint8_t ccnr[12] = {0}; size_t index = 0; - uint8_t key[PICOPASS_BLOCK_LEN] = {0}; + uint8_t key[RFAL_PICOPASS_BLOCK_LEN] = {0}; // Load dictionary IclassEliteDict* dict = dict_attack_data->dict; @@ -572,7 +572,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; - uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; + uint8_t* div_key = AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data; loclass_iclass_calc_div_key(csn, key, div_key, elite); loclass_opt_doReaderMAC(ccnr, div_key, mac); @@ -580,7 +580,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { err = rfalPicoPassPollerCheck(mac, &chkRes); if(err == ERR_NONE) { FURI_LOG_I(TAG, "Found key"); - memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); + memcpy(pacs->key, key, RFAL_PICOPASS_BLOCK_LEN); err = picopass_read_card(AA1); if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_read_card error %d", err); @@ -755,9 +755,9 @@ void picopass_worker_write_key(PicopassWorker* picopass_worker) { uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; uint8_t fuses = configBlock[7]; - uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data; + uint8_t* oldKey = AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data; - uint8_t newKey[PICOPASS_BLOCK_LEN] = {0}; + uint8_t newKey[RFAL_PICOPASS_BLOCK_LEN] = {0}; loclass_iclass_calc_div_key(csn, pacs->key, newKey, false); if((fuses & 0x80) == 0x80) { @@ -765,14 +765,14 @@ void picopass_worker_write_key(PicopassWorker* picopass_worker) { } else { FURI_LOG_D(TAG, "XOR write for application mode key change"); // XOR when in application mode - for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + for(size_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) { newKey[i] ^= oldKey[i]; } } while(picopass_worker->state == PicopassWorkerStateWriteKey) { if(picopass_detect_card(1000) == ERR_NONE) { - err = picopass_write_block(AA1, PICOPASS_KD_BLOCK_INDEX, newKey); + err = picopass_write_block(AA1, PICOPASS_SECURE_KD_BLOCK_INDEX, newKey); if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_write_block error %d", err); nextState = PicopassWorkerEventFail; @@ -808,7 +808,9 @@ static inline void picopass_emu_read_blocks( uint8_t block_num, uint8_t block_count) { memcpy( - buf, nfcv_data->data + (block_num * PICOPASS_BLOCK_LEN), block_count * PICOPASS_BLOCK_LEN); + buf, + nfcv_data->data + (block_num * RFAL_PICOPASS_BLOCK_LEN), + block_count * RFAL_PICOPASS_BLOCK_LEN); } static inline void picopass_emu_write_blocks( @@ -817,14 +819,16 @@ static inline void picopass_emu_write_blocks( uint8_t block_num, uint8_t block_count) { memcpy( - nfcv_data->data + (block_num * PICOPASS_BLOCK_LEN), buf, block_count * PICOPASS_BLOCK_LEN); + nfcv_data->data + (block_num * RFAL_PICOPASS_BLOCK_LEN), + buf, + block_count * RFAL_PICOPASS_BLOCK_LEN); } static void picopass_init_cipher_state(NfcVData* nfcv_data, PicopassEmulatorCtx* ctx) { - uint8_t cc[PICOPASS_BLOCK_LEN]; - uint8_t key[PICOPASS_BLOCK_LEN]; + uint8_t cc[RFAL_PICOPASS_BLOCK_LEN]; + uint8_t key[RFAL_PICOPASS_BLOCK_LEN]; - picopass_emu_read_blocks(nfcv_data, cc, PICOPASS_EPURSE_BLOCK_INDEX, 1); + picopass_emu_read_blocks(nfcv_data, cc, PICOPASS_SECURE_EPURSE_BLOCK_INDEX, 1); picopass_emu_read_blocks(nfcv_data, key, ctx->key_block_num, 1); ctx->cipher_state = loclass_opt_doTagMAC_1(cc, key); @@ -834,7 +838,7 @@ static void loclass_update_csn(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data, PicopassEmulatorCtx* ctx) { // collect two nonces in a row for each CSN uint8_t csn_num = (ctx->key_block_num / 2) % LOCLASS_NUM_CSNS; - memcpy(nfc_data->uid, loclass_csns[csn_num], PICOPASS_BLOCK_LEN); + memcpy(nfc_data->uid, loclass_csns[csn_num], RFAL_PICOPASS_BLOCK_LEN); picopass_emu_write_blocks(nfcv_data, loclass_csns[csn_num], PICOPASS_CSN_BLOCK_INDEX, 1); } @@ -846,7 +850,7 @@ static void picopass_emu_handle_packet( PicopassEmulatorCtx* ctx = nfcv_data->emu_protocol_ctx; uint8_t response[34]; uint8_t response_length = 0; - uint8_t key_block_num = PICOPASS_KD_BLOCK_INDEX; + uint8_t key_block_num = PICOPASS_SECURE_KD_BLOCK_INDEX; const uint8_t block_ff[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; @@ -855,7 +859,7 @@ static void picopass_emu_handle_packet( } switch(nfcv_data->frame[0]) { - case PICOPASS_CMD_ACTALL: // No args + case RFAL_PICOPASS_CMD_ACTALL: // No args if(nfcv_data->frame_length != 1) { return; } @@ -866,14 +870,14 @@ static void picopass_emu_handle_packet( // Send SOF only break; - case PICOPASS_CMD_ACT: // No args + case RFAL_PICOPASS_CMD_ACT: // No args if(nfcv_data->frame_length != 1 || ctx->state != PicopassEmulatorStateActive) { return; } // Send SOF only break; - case PICOPASS_CMD_HALT: // No args + case RFAL_PICOPASS_CMD_HALT: // No args if(nfcv_data->frame_length != 1 || ctx->state != PicopassEmulatorStateSelected) { return; } @@ -883,13 +887,13 @@ static void picopass_emu_handle_packet( // Send SOF only break; - case PICOPASS_CMD_READ_OR_IDENTIFY: + case RFAL_PICOPASS_CMD_READ_OR_IDENTIFY: if(nfcv_data->frame_length == 1 && ctx->state == PicopassEmulatorStateActive) { // PICOPASS_CMD_IDENTIFY // ASNB(8) CRC16(2) picopass_anticoll_csn(response, nfc_data->uid); - picopass_append_crc(response, PICOPASS_BLOCK_LEN); - response_length = PICOPASS_BLOCK_LEN + 2; + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN); + response_length = RFAL_PICOPASS_BLOCK_LEN + 2; break; } else if( nfcv_data->frame_length == 4 && @@ -902,20 +906,20 @@ static void picopass_emu_handle_packet( // TODO: Check auth? // DATA(8) CRC16(2) - if(nfcv_data->frame[1] == PICOPASS_KD_BLOCK_INDEX || - nfcv_data->frame[1] == PICOPASS_KC_BLOCK_INDEX) { + if(nfcv_data->frame[1] == PICOPASS_SECURE_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_SECURE_KC_BLOCK_INDEX) { // Reading Kd or Kc blocks always returns FF's - memcpy(response, block_ff, PICOPASS_BLOCK_LEN); + memcpy(response, block_ff, RFAL_PICOPASS_BLOCK_LEN); } else { picopass_emu_read_blocks(nfcv_data, response, nfcv_data->frame[1], 1); } - picopass_append_crc(response, PICOPASS_BLOCK_LEN); - response_length = PICOPASS_BLOCK_LEN + 2; + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN); + response_length = RFAL_PICOPASS_BLOCK_LEN + 2; break; } return; - case PICOPASS_CMD_READ4: // ADDRESS(1) CRC16(2) + case RFAL_PICOPASS_CMD_READ4: // ADDRESS(1) CRC16(2) if(nfcv_data->frame_length != 4 || ctx->state != PicopassEmulatorStateSelected || nfcv_data->frame[1] + 4 >= PICOPASS_MAX_APP_LIMIT) { return; @@ -930,32 +934,32 @@ static void picopass_emu_handle_packet( picopass_emu_read_blocks(nfcv_data, response, blockNum, 4); if(blockNum == 4) { // Kc is block 4, so just redact first block of response - memcpy(response, block_ff, PICOPASS_BLOCK_LEN); + memcpy(response, block_ff, RFAL_PICOPASS_BLOCK_LEN); } else if(blockNum < 4) { // Kd is block 3 - uint8_t* kdOffset = response + ((3 - blockNum) * PICOPASS_BLOCK_LEN); - memcpy(kdOffset, block_ff, PICOPASS_BLOCK_LEN); + uint8_t* kdOffset = response + ((3 - blockNum) * RFAL_PICOPASS_BLOCK_LEN); + memcpy(kdOffset, block_ff, RFAL_PICOPASS_BLOCK_LEN); if(blockNum != 0) { // Redact Kc - memcpy(kdOffset + PICOPASS_BLOCK_LEN, block_ff, PICOPASS_BLOCK_LEN); + memcpy(kdOffset + RFAL_PICOPASS_BLOCK_LEN, block_ff, RFAL_PICOPASS_BLOCK_LEN); } } - picopass_append_crc(response, PICOPASS_BLOCK_LEN * 4); - response_length = (PICOPASS_BLOCK_LEN * 4) + 2; + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN * 4); + response_length = (RFAL_PICOPASS_BLOCK_LEN * 4) + 2; break; - case PICOPASS_CMD_SELECT: // ASNB(8)|SERIALNB(8) + case RFAL_PICOPASS_CMD_SELECT: // ASNB(8)|SERIALNB(8) if(nfcv_data->frame_length != 9) { return; } - uint8_t select_csn[PICOPASS_BLOCK_LEN]; + uint8_t select_csn[RFAL_PICOPASS_BLOCK_LEN]; if(ctx->state == PicopassEmulatorStateHalt || ctx->state == PicopassEmulatorStateIdle) { - memcpy(select_csn, nfc_data->uid, PICOPASS_BLOCK_LEN); + memcpy(select_csn, nfc_data->uid, RFAL_PICOPASS_BLOCK_LEN); } else { picopass_anticoll_csn(select_csn, nfc_data->uid); } - if(memcmp(nfcv_data->frame + 1, select_csn, PICOPASS_BLOCK_LEN)) { + if(memcmp(nfcv_data->frame + 1, select_csn, RFAL_PICOPASS_BLOCK_LEN)) { if(ctx->state == PicopassEmulatorStateActive) { ctx->state = PicopassEmulatorStateIdle; } else if(ctx->state == PicopassEmulatorStateSelected) { @@ -969,16 +973,17 @@ static void picopass_emu_handle_packet( ctx->state = PicopassEmulatorStateSelected; // SERIALNB(8) CRC16(2) - memcpy(response, nfc_data->uid, PICOPASS_BLOCK_LEN); - picopass_append_crc(response, PICOPASS_BLOCK_LEN); + memcpy(response, nfc_data->uid, RFAL_PICOPASS_BLOCK_LEN); + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN); - response_length = PICOPASS_BLOCK_LEN + 2; + response_length = RFAL_PICOPASS_BLOCK_LEN + 2; break; - case PICOPASS_CMD_READCHECK_KC: // ADDRESS(1) - key_block_num = PICOPASS_KC_BLOCK_INDEX; + case RFAL_PICOPASS_CMD_READCHECK_KC: // ADDRESS(1) + key_block_num = PICOPASS_SECURE_KC_BLOCK_INDEX; // fallthrough - case PICOPASS_CMD_READCHECK_KD: // ADDRESS(1) - if(nfcv_data->frame_length != 2 || nfcv_data->frame[1] != PICOPASS_EPURSE_BLOCK_INDEX || + case RFAL_PICOPASS_CMD_READCHECK_KD: // ADDRESS(1) + if(nfcv_data->frame_length != 2 || + nfcv_data->frame[1] != PICOPASS_SECURE_EPURSE_BLOCK_INDEX || ctx->state != PicopassEmulatorStateSelected) { return; } @@ -990,9 +995,9 @@ static void picopass_emu_handle_packet( // DATA(8) picopass_emu_read_blocks(nfcv_data, response, nfcv_data->frame[1], 1); - response_length = PICOPASS_BLOCK_LEN; + response_length = RFAL_PICOPASS_BLOCK_LEN; break; - case PICOPASS_CMD_CHECK: // CHALLENGE(4) READERSIGNATURE(4) + case RFAL_PICOPASS_CMD_CHECK: // CHALLENGE(4) READERSIGNATURE(4) if(nfcv_data->frame_length != 9 || ctx->state != PicopassEmulatorStateSelected) { return; } @@ -1001,11 +1006,11 @@ static void picopass_emu_handle_packet( // LOCLASS Reader attack mode // Copy EPURSE - uint8_t cc[PICOPASS_BLOCK_LEN]; - picopass_emu_read_blocks(nfcv_data, cc, PICOPASS_EPURSE_BLOCK_INDEX, 1); + uint8_t cc[RFAL_PICOPASS_BLOCK_LEN]; + picopass_emu_read_blocks(nfcv_data, cc, PICOPASS_SECURE_EPURSE_BLOCK_INDEX, 1); // Check if the nonce is from a standard key - uint8_t key[PICOPASS_BLOCK_LEN]; + uint8_t key[RFAL_PICOPASS_BLOCK_LEN]; loclass_iclass_calc_div_key(nfc_data->uid, picopass_iclass_key, key, false); ctx->cipher_state = loclass_opt_doTagMAC_1(cc, key); @@ -1042,7 +1047,7 @@ static void picopass_emu_handle_packet( return; } - uint8_t key[PICOPASS_BLOCK_LEN]; + uint8_t key[RFAL_PICOPASS_BLOCK_LEN]; picopass_emu_read_blocks(nfcv_data, key, ctx->key_block_num, 1); uint8_t rmac[4]; @@ -1057,7 +1062,7 @@ static void picopass_emu_handle_packet( // CHIPRESPONSE(4) response_length = 4; break; - case PICOPASS_CMD_UPDATE: // ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) + case RFAL_PICOPASS_CMD_UPDATE: // ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) if((nfcv_data->frame_length != 12 && nfcv_data->frame_length != 14) || ctx->state != PicopassEmulatorStateSelected) { return; @@ -1067,7 +1072,7 @@ static void picopass_emu_handle_packet( return; } - uint8_t cfgBlock[PICOPASS_BLOCK_LEN]; + uint8_t cfgBlock[RFAL_PICOPASS_BLOCK_LEN]; picopass_emu_read_blocks(nfcv_data, cfgBlock, PICOPASS_CONFIG_BLOCK_INDEX, 1); bool persMode = HAS_MASK(cfgBlock[7], PICOPASS_FUSE_PERS); @@ -1077,10 +1082,10 @@ static void picopass_emu_handle_packet( !HAS_MASK(cfgBlock[3], 0x80)) // Chip is in RO mode, no updated possible (even ePurse) || (!persMode && nfcv_data->frame[1] == - PICOPASS_AIA_BLOCK_INDEX) // AIA can only be set in personalisation mode + PICOPASS_SECURE_AIA_BLOCK_INDEX) // AIA can only be set in personalisation mode || (!persMode && - (nfcv_data->frame[1] == PICOPASS_KD_BLOCK_INDEX || - nfcv_data->frame[1] == PICOPASS_KC_BLOCK_INDEX) && + (nfcv_data->frame[1] == PICOPASS_SECURE_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_SECURE_KC_BLOCK_INDEX) && (!HAS_MASK(cfgBlock[7], PICOPASS_FUSE_CRYPT10)))) { return; // TODO: Is this the right response? } @@ -1102,7 +1107,7 @@ static void picopass_emu_handle_packet( // -> must auth with that key to change it uint8_t blockOffset = nfcv_data->frame[1]; - uint8_t block[PICOPASS_BLOCK_LEN]; + uint8_t block[RFAL_PICOPASS_BLOCK_LEN]; switch(nfcv_data->frame[1]) { case PICOPASS_CONFIG_BLOCK_INDEX: block[0] = cfgBlock[0]; // Applications Limit @@ -1125,50 +1130,50 @@ static void picopass_emu_handle_packet( block[7] &= nfcv_data->frame[9] | ~PICOPASS_FUSE_CRYPT10; } break; - case PICOPASS_EPURSE_BLOCK_INDEX: + case PICOPASS_SECURE_EPURSE_BLOCK_INDEX: // ePurse updates swap first and second half of the block each update memcpy(block + 4, nfcv_data->frame + 2, 4); memcpy(block, nfcv_data->frame + 6, 4); break; - case PICOPASS_KD_BLOCK_INDEX: + case PICOPASS_SECURE_KD_BLOCK_INDEX: // fallthrough - case PICOPASS_KC_BLOCK_INDEX: + case PICOPASS_SECURE_KC_BLOCK_INDEX: if(!persMode) { picopass_emu_read_blocks(nfcv_data, block, blockOffset, 1); - for(uint8_t i = 0; i < sizeof(PICOPASS_BLOCK_LEN); i++) + for(uint8_t i = 0; i < sizeof(RFAL_PICOPASS_BLOCK_LEN); i++) block[i] ^= nfcv_data->frame[i + 2]; break; } // Use default case when in personalisation mode // fallthrough default: - memcpy(block, nfcv_data->frame + 2, PICOPASS_BLOCK_LEN); + memcpy(block, nfcv_data->frame + 2, RFAL_PICOPASS_BLOCK_LEN); break; } picopass_emu_write_blocks(nfcv_data, block, blockOffset, 1); if((nfcv_data->frame[1] == ctx->key_block_num || - nfcv_data->frame[1] == PICOPASS_EPURSE_BLOCK_INDEX) && + nfcv_data->frame[1] == PICOPASS_SECURE_EPURSE_BLOCK_INDEX) && !ctx->loclass_mode) picopass_init_cipher_state(nfcv_data, ctx); // DATA(8) CRC16(2) - if(nfcv_data->frame[1] == PICOPASS_KD_BLOCK_INDEX || - nfcv_data->frame[1] == PICOPASS_KD_BLOCK_INDEX) { + if(nfcv_data->frame[1] == PICOPASS_SECURE_KD_BLOCK_INDEX || + nfcv_data->frame[1] == PICOPASS_SECURE_KD_BLOCK_INDEX) { // Key updates always return FF's - memcpy(response, block_ff, PICOPASS_BLOCK_LEN); + memcpy(response, block_ff, RFAL_PICOPASS_BLOCK_LEN); } else { - memcpy(response, block, PICOPASS_BLOCK_LEN); + memcpy(response, block, RFAL_PICOPASS_BLOCK_LEN); } - picopass_append_crc(response, PICOPASS_BLOCK_LEN); - response_length = PICOPASS_BLOCK_LEN + 2; + picopass_append_crc(response, RFAL_PICOPASS_BLOCK_LEN); + response_length = RFAL_PICOPASS_BLOCK_LEN + 2; break; - case PICOPASS_CMD_PAGESEL: // PAGE(1) CRC16(2) + case RFAL_PICOPASS_CMD_PAGESEL: // PAGE(1) CRC16(2) // Chips with a single page do not answer to this command // BLOCK1(8) CRC16(2) return; - case PICOPASS_CMD_DETECT: + case RFAL_PICOPASS_CMD_DETECT: // TODO - not used by iClass though return; default: @@ -1193,16 +1198,16 @@ void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode) FuriHalNfcTxRxContext tx_rx = {}; PicopassEmulatorCtx emu_ctx = { .state = PicopassEmulatorStateIdle, - .key_block_num = PICOPASS_KD_BLOCK_INDEX, + .key_block_num = PICOPASS_SECURE_KD_BLOCK_INDEX, .loclass_mode = loclass_mode, .loclass_got_std_key = false, .loclass_writer = NULL, }; FuriHalNfcDevData nfc_data = { - .uid_len = PICOPASS_BLOCK_LEN, + .uid_len = RFAL_PICOPASS_UID_LEN, }; NfcVData* nfcv_data = malloc(sizeof(NfcVData)); - nfcv_data->block_size = PICOPASS_BLOCK_LEN; + nfcv_data->block_size = RFAL_PICOPASS_BLOCK_LEN; nfcv_data->emu_protocol_ctx = &emu_ctx; nfcv_data->emu_protocol_handler = &picopass_emu_handle_packet; @@ -1218,15 +1223,15 @@ void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode) picopass_emu_write_blocks(nfcv_data, conf, PICOPASS_CONFIG_BLOCK_INDEX, 1); uint8_t epurse[8] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - picopass_emu_write_blocks(nfcv_data, epurse, PICOPASS_EPURSE_BLOCK_INDEX, 1); + picopass_emu_write_blocks(nfcv_data, epurse, PICOPASS_SECURE_EPURSE_BLOCK_INDEX, 1); uint8_t aia[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - picopass_emu_write_blocks(nfcv_data, aia, PICOPASS_AIA_BLOCK_INDEX, 1); + picopass_emu_write_blocks(nfcv_data, aia, PICOPASS_SECURE_AIA_BLOCK_INDEX, 1); emu_ctx.loclass_writer = loclass_writer_alloc(); loclass_writer_write_start_stop(emu_ctx.loclass_writer, true); } else { - memcpy(nfc_data.uid, blocks[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); + memcpy(nfc_data.uid, blocks[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN); memcpy(nfcv_data->data, blocks, sizeof(dev_data->AA1)); picopass_init_cipher_state(nfcv_data, &emu_ctx); } diff --git a/applications/external/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c index df10d97d6..1d45a48dc 100644 --- a/applications/external/picopass/rfal_picopass.c +++ b/applications/external/picopass/rfal_picopass.c @@ -72,7 +72,7 @@ FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void) { FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes) { FuriHalNfcReturn ret; - uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_IDENTIFY}; + uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_READ_OR_IDENTIFY}; uint16_t recvLen = 0; uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); @@ -118,7 +118,7 @@ FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* s FuriHalNfcReturn rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes) { FuriHalNfcReturn ret; - uint8_t txBuf[2] = {RFAL_PICOPASS_CMD_READCHECK, 0x02}; + uint8_t txBuf[2] = {RFAL_PICOPASS_CMD_READCHECK_KD, 0x02}; uint16_t recvLen = 0; uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); @@ -170,7 +170,7 @@ FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chk FuriHalNfcReturn rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes) { FuriHalNfcReturn ret; - uint8_t txBuf[4] = {RFAL_PICOPASS_CMD_READ, 0, 0, 0}; + uint8_t txBuf[4] = {RFAL_PICOPASS_CMD_READ_OR_IDENTIFY, 0, 0, 0}; txBuf[1] = blockNum; uint16_t crc = rfalPicoPassCalculateCcitt(0xE012, txBuf + 1, 1); memcpy(txBuf + 2, &crc, sizeof(uint16_t)); @@ -193,8 +193,8 @@ FuriHalNfcReturn rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadB FuriHalNfcReturn rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]) { FuriHalNfcReturn ret; - uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_WRITE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - memcpy(txBuf + 2, data, RFAL_PICOPASS_MAX_BLOCK_LEN); + uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_UPDATE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(txBuf + 2, data, RFAL_PICOPASS_BLOCK_LEN); memcpy(txBuf + 10, mac, 4); uint16_t recvLen = 0; diff --git a/applications/external/picopass/rfal_picopass.h b/applications/external/picopass/rfal_picopass.h index 194a03fb9..6265884d6 100644 --- a/applications/external/picopass/rfal_picopass.h +++ b/applications/external/picopass/rfal_picopass.h @@ -3,16 +3,41 @@ #include #define RFAL_PICOPASS_UID_LEN 8 -#define RFAL_PICOPASS_MAX_BLOCK_LEN 8 +#define RFAL_PICOPASS_BLOCK_LEN 8 enum { - RFAL_PICOPASS_CMD_ACTALL = 0x0A, - RFAL_PICOPASS_CMD_IDENTIFY = 0x0C, - RFAL_PICOPASS_CMD_SELECT = 0x81, - RFAL_PICOPASS_CMD_READCHECK = 0x88, + // PicoPass command bytes: + // Low nibble used for command + // High nibble used for options and checksum (MSB) + // The only option we care about in 15693 mode is the key + // which is only used by READCHECK, so for simplicity we + // don't bother breaking down the command and flags into parts + + // READ: ADDRESS(1) CRC16(2) -> DATA(8) CRC16(2) + // IDENTIFY: No args -> ASNB(8) CRC16(2) + RFAL_PICOPASS_CMD_READ_OR_IDENTIFY = 0x0C, + // ADDRESS(1) CRC16(2) -> DATA(32) CRC16(2) + RFAL_PICOPASS_CMD_READ4 = 0x06, + // ADDRESS(1) DATA(8) SIGN(4)|CRC16(2) -> DATA(8) CRC16(2) + RFAL_PICOPASS_CMD_UPDATE = 0x87, + // ADDRESS(1) -> DATA(8) + RFAL_PICOPASS_CMD_READCHECK_KD = 0x88, + // ADDRESS(1) -> DATA(8) + RFAL_PICOPASS_CMD_READCHECK_KC = 0x18, + // CHALLENGE(4) READERSIGNATURE(4) -> CHIPRESPONSE(4) RFAL_PICOPASS_CMD_CHECK = 0x05, - RFAL_PICOPASS_CMD_READ = 0x0C, - RFAL_PICOPASS_CMD_WRITE = 0x87, + // No args -> SOF + RFAL_PICOPASS_CMD_ACTALL = 0x0A, + // No args -> SOF + RFAL_PICOPASS_CMD_ACT = 0x8E, + // ASNB(8)|SERIALNB(8) -> SERIALNB(8) CRC16(2) + RFAL_PICOPASS_CMD_SELECT = 0x81, + // No args -> SERIALNB(8) CRC16(2) + RFAL_PICOPASS_CMD_DETECT = 0x0F, + // No args -> SOF + RFAL_PICOPASS_CMD_HALT = 0x00, + // PAGE(1) CRC16(2) -> BLOCK1(8) CRC16(2) + RFAL_PICOPASS_CMD_PAGESEL = 0x84, }; typedef struct { @@ -34,7 +59,7 @@ typedef struct { } rfalPicoPassCheckRes; typedef struct { - uint8_t data[RFAL_PICOPASS_MAX_BLOCK_LEN]; + uint8_t data[RFAL_PICOPASS_BLOCK_LEN]; uint8_t crc[2]; } rfalPicoPassReadBlockRes; diff --git a/applications/external/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c index bb149aa6b..41d0bad81 100644 --- a/applications/external/picopass/scenes/picopass_scene_device_info.c +++ b/applications/external/picopass/scenes/picopass_scene_device_info.c @@ -26,9 +26,9 @@ void picopass_scene_device_info_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; - memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + uint8_t csn[RFAL_PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(csn_str, "%02X ", csn[i]); } @@ -42,7 +42,7 @@ void picopass_scene_device_info_on_enter(void* context) { bytesLength++; } furi_string_set(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { + for(uint8_t i = RFAL_PICOPASS_BLOCK_LEN - bytesLength; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); } diff --git a/applications/external/picopass/scenes/picopass_scene_key_menu.c b/applications/external/picopass/scenes/picopass_scene_key_menu.c index 8aac6cb24..c9456468b 100644 --- a/applications/external/picopass/scenes/picopass_scene_key_menu.c +++ b/applications/external/picopass/scenes/picopass_scene_key_menu.c @@ -59,25 +59,25 @@ bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexWriteStandard) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteStandard); - memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCE) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCL) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } else if(event.event == SubmenuIndexWriteiCS) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } diff --git a/applications/external/picopass/scenes/picopass_scene_read_card.c b/applications/external/picopass/scenes/picopass_scene_read_card.c index c1cc7249c..fabce52b6 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card.c @@ -38,7 +38,7 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) { if(memcmp( picopass->dev->dev_data.pacs.key, picopass_factory_debit_key, - PICOPASS_BLOCK_LEN) == 0) { + RFAL_PICOPASS_BLOCK_LEN) == 0) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess); } else { scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); diff --git a/applications/external/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c index ffe7195b7..2f80cd7b9 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -31,15 +31,15 @@ void picopass_scene_read_card_success_on_enter(void* context) { PicopassPacs* pacs = &picopass->dev->dev_data.pacs; Widget* widget = picopass->widget; - uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; - memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + uint8_t csn[RFAL_PICOPASS_BLOCK_LEN] = {0}; + memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(csn_str, "%02X", csn[i]); } - bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); - bool empty = - picopass_is_memset(AA1[PICOPASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN); + bool no_key = picopass_is_memset(pacs->key, 0x00, RFAL_PICOPASS_BLOCK_LEN); + bool empty = picopass_is_memset( + AA1[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, RFAL_PICOPASS_BLOCK_LEN); if(no_key) { furi_string_cat_printf(wiegand_str, "Read Failed"); @@ -78,7 +78,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { } else { size_t bytesLength = 1 + pacs->record.bitLength / 8; furi_string_set(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { + for(uint8_t i = RFAL_PICOPASS_BLOCK_LEN - bytesLength; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); } @@ -99,9 +99,9 @@ void picopass_scene_read_card_success_on_enter(void* context) { } furi_string_cat_printf(sio_str, "Key: "); - uint8_t key[PICOPASS_BLOCK_LEN]; - memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + uint8_t key[RFAL_PICOPASS_BLOCK_LEN]; + memcpy(key, &pacs->key, RFAL_PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(sio_str, "%02X", key[i]); } } diff --git a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c index f5fcd10fd..2ee6b253a 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c @@ -64,7 +64,7 @@ bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEve if(event.event == GuiButtonTypeLeft) { consumed = scene_manager_previous_scene(picopass->scene_manager); } else if(event.event == GuiButtonTypeCenter) { - memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); + memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, RFAL_PICOPASS_BLOCK_LEN); scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; } From 6aed650bcc933b1eb85f5b79ebcab1c720cb777c Mon Sep 17 00:00:00 2001 From: Sil333033 <94360907+Sil333033@users.noreply.github.com> Date: Mon, 12 Jun 2023 14:15:22 +0200 Subject: [PATCH 167/370] added external cc1101 module at cli --- applications/main/subghz/subghz_cli.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 526567e60..22a3ed3c7 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -779,6 +779,20 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { FuriString* cmd = furi_string_alloc(); + if(!furi_hal_power_is_otg_enabled()) { + furi_hal_power_enable_otg(); + } + + furi_delay_ms(15); + + furi_hal_subghz_select_radio_type(SubGhzRadioExternal); + furi_hal_subghz_init_radio_type(SubGhzRadioExternal); + + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_select_radio_type(SubGhzRadioInternal); + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); + } + do { if(!args_read_string_and_trim(args, cmd)) { subghz_cli_command_print_usage(); From 19038dce6149e2869203fc262a99f34461d83390 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 15:55:23 +0100 Subject: [PATCH 168/370] BadKB store prev config separately --- applications/main/bad_kb/bad_kb_app.c | 9 ++++----- applications/main/bad_kb/helpers/ducky_script.c | 13 +++++++------ applications/main/bad_kb/helpers/ducky_script.h | 8 +++++--- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 084683468..d2044afe4 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -153,12 +153,11 @@ BadKbApp* bad_kb_app_alloc(char* arg) { bad_kb_config_adjust(&app->config); // Save prev config - BadKbConfig* prev = &app->prev_config; - prev->usb_mode = furi_hal_usb_get_config(); + app->prev_usb_mode = furi_hal_usb_get_config(); FuriHalBtProfile kbd = FuriHalBtProfileHidKeyboard; - prev->bt_mode = furi_hal_bt_get_profile_pairing_method(kbd); - strcpy(prev->bt_name, furi_hal_bt_get_profile_adv_name(kbd)); - memcpy(prev->bt_mac, furi_hal_bt_get_profile_mac_addr(kbd), BAD_KB_MAC_LEN); + app->prev_bt_mode = furi_hal_bt_get_profile_pairing_method(kbd); + memcpy(app->prev_bt_mac, furi_hal_bt_get_profile_mac_addr(kbd), BAD_KB_MAC_LEN); + strlcpy(app->prev_bt_name, furi_hal_bt_get_profile_adv_name(kbd), BAD_KB_NAME_LEN); // Adjust BT remember MAC to be serial MAC +2 memcpy(BAD_KB_BOUND_MAC, furi_hal_version_get_ble_mac(), BAD_KB_MAC_LEN); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 04000771d..75040d98d 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -565,19 +565,20 @@ int32_t bad_kb_conn_apply(BadKbApp* app) { void bad_kb_conn_reset(BadKbApp* app) { if(app->conn_mode == BadKbConnModeBt) { + // TODO: maybe also restore BT profile? bt_disconnect(app->bt); furi_delay_ms(200); bt_keys_storage_set_default_path(app->bt); - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, app->prev_config.bt_mode); + FuriHalBtProfile kbd = FuriHalBtProfileHidKeyboard; + furi_hal_bt_set_profile_mac_addr(kbd, app->prev_bt_mac); + furi_hal_bt_set_profile_adv_name(kbd, app->prev_bt_name); + furi_hal_bt_set_profile_pairing_method(kbd, app->prev_bt_mode); furi_check(bt_set_profile(app->bt, BtProfileSerial)); bt_enable_peer_key_update(app->bt); } else if(app->conn_mode == BadKbConnModeUsb) { - // TODO: maybe also restore USB config context? - furi_check(furi_hal_usb_set_config(app->prev_config.usb_mode, NULL)); + // TODO: maybe also restore USB context? + furi_check(furi_hal_usb_set_config(app->prev_usb_mode, NULL)); } app->conn_mode = BadKbConnModeNone; diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index 89b72bf99..358617903 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -115,10 +115,8 @@ typedef enum { } BadKbAppError; typedef struct { - GapPairing bt_mode; char bt_name[BAD_KB_NAME_LEN]; uint8_t bt_mac[BAD_KB_MAC_LEN]; - FuriHalUsbInterface* usb_mode; FuriHalUsbHidConfig usb_cfg; } BadKbConfig; @@ -154,13 +152,17 @@ struct BadKbApp { bool bt_remember; BadKbConfig config; // User options BadKbConfig id_config; // ID and BT_ID values - BadKbConfig prev_config; // State to restore at exit bool set_usb_id; bool set_bt_id; bool has_usb_id; bool has_bt_id; + GapPairing prev_bt_mode; + char prev_bt_name[BAD_KB_NAME_LEN]; + uint8_t prev_bt_mac[BAD_KB_MAC_LEN]; + FuriHalUsbInterface* prev_usb_mode; + FuriHalUsbHidConfig* hid_cfg; BadKbConnMode conn_mode; FuriThread* conn_init_thread; From 5a75ed903d25a0b57956efd6bf42ee686cacc135 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Mon, 12 Jun 2023 19:15:35 +0200 Subject: [PATCH 169/370] Fix format --- ...subghz_frequency_analyzer_log_item_array.h | 7 +++- furi/core/string.h | 35 ++++++++----------- lib/toolbox/m_cstr_dup.h | 18 +++++----- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h index e9d4e407c..12c6bef6a 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -27,7 +27,12 @@ TUPLE_DEF2( (rssi_max, uint8_t)) /* Register globally the oplist */ #define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ - TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) + TUPLE_OPLIST( \ + SubGhzFrequencyAnalyzerLogItem, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST) /* Define the array, register the oplist and define further algorithms on it */ ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) diff --git a/furi/core/string.h b/furi/core/string.h index 521aa9993..7529deacd 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -633,18 +633,17 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Search for a string (or C string) in a string * (string, [c]string[, start=0]) */ -#define furi_string_search(...) \ - M_APPLY( \ - FURI_STRING_SELECT3, \ - furi_string_search, \ - furi_string_search_str, \ +#define furi_string_search(...) \ + M_APPLY( \ + FURI_STRING_SELECT3, \ + furi_string_search, \ + furi_string_search_str, \ M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Search for a C string in a string * (string, cstring[, start=0]) */ -#define furi_string_search_str( ...) \ - furi_string_search_str( M_DEFAULT_ARGS(3, (0), __VA_ARGS__) ) +#define furi_string_search_str(...) furi_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Test if the string starts with the given string (or C string). @@ -670,40 +669,36 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Trim a string from the given set of characters (default is " \n\r\t"). * (string[, set=" \n\r\t"]) */ -#define furi_string_trim(...) \ - furi_string_trim( M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__) ) +#define furi_string_trim(...) furi_string_trim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__)) /** * @brief Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_char(...) \ - furi_string_search_char( M_DEFAULT_ARGS(3, (0), __VA_ARGS__) ) +#define furi_string_search_char(...) furi_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Reverse Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_rchar( ...) \ - furi_string_search_rchar( M_DEFAULT_ARGS(3, (0), __VA_ARGS__) ) +#define furi_string_search_rchar(...) furi_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Replace a string to another string (or C string to another C string) in a string. * (string, [c]string, [c]string[, start=0]) */ -#define furi_string_replace(...) \ - M_APPLY( \ - FURI_STRING_SELECT4, \ - furi_string_replace, \ - furi_string_replace_str, \ +#define furi_string_replace(...) \ + M_APPLY( \ + FURI_STRING_SELECT4, \ + furi_string_replace, \ + furi_string_replace_str, \ M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief Replace a C string to another C string in a string. * (string, cstring, cstring[, start=0]) */ -#define furi_string_replace_str(...) \ - furi_string_replace_str( M_DEFAULT_ARGS(4, (0), __VA_ARGS__) ) +#define furi_string_replace_str(...) furi_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief INIT OPLIST for FuriString. diff --git a/lib/toolbox/m_cstr_dup.h b/lib/toolbox/m_cstr_dup.h index b25e8aa77..11b7fe35a 100644 --- a/lib/toolbox/m_cstr_dup.h +++ b/lib/toolbox/m_cstr_dup.h @@ -2,16 +2,16 @@ #include #define M_INIT_DUP(a) ((a) = strdup("")) -#define M_INIT_SET_DUP(a, b) ( (a) = strdup(b)) +#define M_INIT_SET_DUP(a, b) ((a) = strdup(b)) #define M_SET_DUP(a, b) (free((void*)a), (a) = strdup(b)) #define M_CLEAR_DUP(a) (free((void*)a)) -#define M_CSTR_DUP_OPLIST \ - (INIT(M_INIT_DUP), \ - INIT_SET(M_INIT_SET_DUP), \ - SET(M_SET_DUP), \ - CLEAR(M_CLEAR_DUP), \ - HASH(m_core_cstr_hash), \ - EQUAL(M_CSTR_EQUAL), \ - CMP(strcmp), \ +#define M_CSTR_DUP_OPLIST \ + (INIT(M_INIT_DUP), \ + INIT_SET(M_INIT_SET_DUP), \ + SET(M_SET_DUP), \ + CLEAR(M_CLEAR_DUP), \ + HASH(m_core_cstr_hash), \ + EQUAL(M_CSTR_EQUAL), \ + CMP(strcmp), \ TYPE(const char*)) From d1c970b0194f02087a8a977165124174a813ccd2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 12 Jun 2023 23:20:56 +0300 Subject: [PATCH 170/370] Fix protoview patable adding in custom modulations --- applications/external/protoview/signal_file.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/external/protoview/signal_file.c b/applications/external/protoview/signal_file.c index 886573a06..e7934d04b 100644 --- a/applications/external/protoview/signal_file.c +++ b/applications/external/protoview/signal_file.c @@ -48,6 +48,8 @@ bool save_signal(ProtoViewApp* app, const char* filename) { for(int j = 0; regs[j]; j += 2) { furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]); } + // Add patable + furi_string_cat(custom, "00 00 C0 00 00 00 00 00 00 00 "); //size_t len = furi_string_size(file_content); //furi_string_set_char(custom, len - 1, '\n'); furi_string_cat(custom, "\n"); From d535afc4c9167ab12f88ff981e1415cfad5dfbad Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 22:47:20 +0100 Subject: [PATCH 171/370] This isnt needed anymore --- applications/main/archive/helpers/archive_files.c | 5 ----- applications/main/archive/helpers/archive_files.h | 4 ---- 2 files changed, 9 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 11a30d956..8865262fb 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -32,11 +32,6 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder if(is_folder) { file->type = ArchiveFileTypeFolder; } else { - char tmp_extension[MAX_EXT_LEN]; - path_extract_extension(file->path, tmp_extension, MAX_EXT_LEN); - if((strcmp(tmp_extension, ".txt") == 0) || (strcmp(tmp_extension, ".md") == 0)) { - file->is_text_file = true; - } file->type = ArchiveFileTypeUnknown; } } diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index d54b99994..0d2569b09 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -31,7 +31,6 @@ typedef struct { FuriString* custom_name; bool fav; bool is_app; - bool is_text_file; } ArchiveFile_t; static void ArchiveFile_t_init(ArchiveFile_t* obj) { @@ -41,7 +40,6 @@ static void ArchiveFile_t_init(ArchiveFile_t* obj) { obj->custom_name = furi_string_alloc(); obj->fav = false; obj->is_app = false; - obj->is_text_file = false; } static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { @@ -56,7 +54,6 @@ static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) obj->custom_name = furi_string_alloc_set(src->custom_name); obj->fav = src->fav; obj->is_app = src->is_app; - obj->is_text_file = src->is_text_file; } static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { @@ -71,7 +68,6 @@ static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { furi_string_set(obj->custom_name, src->custom_name); obj->fav = src->fav; obj->is_app = src->is_app; - obj->is_text_file = src->is_text_file; } static void ArchiveFile_t_clear(ArchiveFile_t* obj) { From cb1d05d761ce413761394c9b759fd06c3541b8f8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 23:32:36 +0100 Subject: [PATCH 172/370] Who tf made extensions have limited length??? --- applications/main/archive/archive.c | 2 + applications/main/archive/archive_i.h | 4 +- .../main/archive/helpers/archive_files.c | 12 ++-- .../archive/scenes/archive_scene_rename.c | 55 +++++++------------ .../main/archive/views/archive_browser_view.h | 1 - .../services/storage/storage_external_api.c | 20 +++---- firmware/targets/f7/api_symbols.csv | 1 + lib/toolbox/path.c | 12 ++++ lib/toolbox/path.h | 8 +++ 9 files changed, 63 insertions(+), 52 deletions(-) diff --git a/applications/main/archive/archive.c b/applications/main/archive/archive.c index 588087fd2..5b7638152 100644 --- a/applications/main/archive/archive.c +++ b/applications/main/archive/archive.c @@ -22,6 +22,7 @@ static ArchiveApp* archive_alloc() { ArchiveApp* archive = malloc(sizeof(ArchiveApp)); archive->fav_move_str = furi_string_alloc(); + archive->file_extension = furi_string_alloc(); archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive); archive->view_dispatcher = view_dispatcher_alloc(); @@ -82,6 +83,7 @@ void archive_free(ArchiveApp* archive) { browser_free(archive->browser); furi_string_free(archive->fav_move_str); + furi_string_free(archive->file_extension); furi_record_close(RECORD_DIALOGS); archive->dialogs = NULL; diff --git a/applications/main/archive/archive_i.h b/applications/main/archive/archive_i.h index b96145827..bd683c22d 100644 --- a/applications/main/archive/archive_i.h +++ b/applications/main/archive/archive_i.h @@ -38,7 +38,7 @@ struct ArchiveApp { FuriString* fav_move_str; char text_store[MAX_NAME_LEN]; - char file_extension[MAX_EXT_LEN + 1]; + FuriString* file_extension; }; -void archive_show_loading_popup(ArchiveApp* context, bool show); \ No newline at end of file +void archive_show_loading_popup(ArchiveApp* context, bool show); diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 8865262fb..e1083ecd4 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -138,24 +138,26 @@ FS_Error archive_copy_rename_file_or_dir( if(find_name && storage_common_exists(fs_api, dst_path)) { FuriString* dir_path = furi_string_alloc(); FuriString* filename = furi_string_alloc(); - char extension[MAX_EXT_LEN] = {0}; + FuriString* file_ext = furi_string_alloc(); - path_extract_filename(dst_str, filename, true); path_extract_dirname(furi_string_get_cstr(dst_str), dir_path); - path_extract_extension(dst_str, extension, MAX_EXT_LEN); + path_extract_filename(dst_str, filename, true); + path_extract_ext_str(dst_str, file_ext); storage_get_next_filename( fs_api, furi_string_get_cstr(dir_path), furi_string_get_cstr(filename), - extension, + furi_string_get_cstr(file_ext), dst_str, 255); - furi_string_cat_printf(dir_path, "/%s%s", furi_string_get_cstr(dst_str), extension); + furi_string_cat_printf( + dir_path, "/%s%s", furi_string_get_cstr(dst_str), furi_string_get_cstr(file_ext)); furi_string_set(dst_str, dir_path); furi_string_free(dir_path); furi_string_free(filename); + furi_string_free(file_ext); } if(copy) { diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index 850cde349..8401ce1c2 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -25,25 +25,19 @@ void archive_scene_rename_on_enter(void* context) { FuriString* path_name = furi_string_alloc(); FuriString* path_folder = furi_string_alloc(); - if(current->type == ArchiveFileTypeFolder) { - // Set file ext to empty since we need to see folder name here - strcpy(archive->file_extension, ""); - // Extract folder name and copy into text_store + ArchiveTabEnum tab = archive_get_tab(archive->browser); + bool hide_ext = tab != ArchiveTabBrowser && tab != ArchiveTabInternal; + + if(current->type == ArchiveFileTypeFolder || !hide_ext) { + furi_string_reset(archive->file_extension); path_extract_basename(furi_string_get_cstr(current->path), path_name); - strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN); - text_input_set_header_text(text_input, "Rename directory:"); - } else /*if(current->type != ArchiveFileTypeUnknown) */ { - // Extract file name and copy into text_store + } else { + path_extract_ext_str(current->path, archive->file_extension); path_extract_filename(current->path, path_name, true); - strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN); - // Extract file extension for validator and rename func - path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN); - text_input_set_header_text(text_input, "Rename file:"); - } /*else { - path_extract_filename(current->path, path_name, false); - strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN); - text_input_set_header_text(text_input, "Rename unknown file:"); - }*/ + } + strlcpy(archive->text_store, furi_string_get_cstr(path_name), MAX_NAME_LEN); + text_input_set_header_text( + text_input, current->type == ArchiveFileTypeFolder ? "Rename directory:" : "Rename file:"); // Get current folder (for file) or previous folder (for folder) for validator path_extract_dirname(furi_string_get_cstr(current->path), path_folder); @@ -58,7 +52,9 @@ void archive_scene_rename_on_enter(void* context) { // Init validator to show message to user that name already exist ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - furi_string_get_cstr(path_folder), archive->file_extension, archive->text_store); + furi_string_get_cstr(path_folder), + furi_string_get_cstr(archive->file_extension), + archive->text_store); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); furi_string_free(path_name); @@ -74,27 +70,18 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SCENE_RENAME_CUSTOM_EVENT) { const char* path_src = archive_get_name(archive->browser); - ArchiveFile_t* file = archive_get_current_file(archive->browser); FuriString* path_dst; path_dst = furi_string_alloc(); - if(file->type == ArchiveFileTypeFolder) { - // Rename folder/dir - path_extract_dirname(path_src, path_dst); - furi_string_cat_printf(path_dst, "/%s", archive->text_store); - } else if(file->type != ArchiveFileTypeUnknown) { - // Rename known type - path_extract_dirname(path_src, path_dst); - furi_string_cat_printf( - path_dst, "/%s%s", archive->text_store, known_ext[file->type]); - } else { - // Rename unknown type - path_extract_dirname(path_src, path_dst); - furi_string_cat_printf( - path_dst, "/%s%s", archive->text_store, archive->file_extension); - } + path_extract_dirname(path_src, path_dst); + furi_string_cat_printf( + path_dst, + "/%s%s", + archive->text_store, + furi_string_get_cstr(archive->file_extension)); + // Long time process if this is directory view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewStack); archive_show_loading_popup(archive, true); diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index ed18df387..7d8eef65c 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -16,7 +16,6 @@ #define MAX_LEN_PX 110 #define MAX_NAME_LEN 255 -#define MAX_EXT_LEN 6 #define FRAME_HEIGHT 12 #define MENU_ITEMS 5u #define MOVE_OFFSET 5u diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index bfedb3e1e..38ff80252 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -8,7 +8,6 @@ #include "toolbox/path.h" #define MAX_NAME_LENGTH 256 -#define MAX_EXT_LEN 16 #define FILE_BUFFER_SIZE 512 #define TAG "StorageAPI" @@ -666,30 +665,31 @@ FS_Error storage_common_merge(Storage* storage, const char* old_path, const char error = storage_common_stat(storage, new_path, &fileinfo); if(error == FSE_OK) { furi_string_set(new_path_next, new_path); - FuriString* dir_path; - FuriString* filename; - char extension[MAX_EXT_LEN] = {0}; - - dir_path = furi_string_alloc(); - filename = furi_string_alloc(); + FuriString* dir_path = furi_string_alloc(); + FuriString* filename = furi_string_alloc(); + FuriString* file_ext = furi_string_alloc(); path_extract_filename(new_path_next, filename, true); path_extract_dirname(new_path, dir_path); - path_extract_extension(new_path_next, extension, MAX_EXT_LEN); + path_extract_ext_str(new_path_next, file_ext); storage_get_next_filename( storage, furi_string_get_cstr(dir_path), furi_string_get_cstr(filename), - extension, + furi_string_get_cstr(file_ext), new_path_next, 255); furi_string_cat_printf( - dir_path, "/%s%s", furi_string_get_cstr(new_path_next), extension); + dir_path, + "/%s%s", + furi_string_get_cstr(new_path_next), + furi_string_get_cstr(file_ext)); furi_string_set(new_path_next, dir_path); furi_string_free(dir_path); furi_string_free(filename); + furi_string_free(file_ext); new_path_tmp = furi_string_get_cstr(new_path_next); } else { new_path_tmp = new_path; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 23fe7ac3c..d44f72d8e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2233,6 +2233,7 @@ Function,+,path_concat,void,"const char*, const char*, FuriString*" Function,+,path_contains_only_ascii,_Bool,const char* Function,+,path_extract_basename,void,"const char*, FuriString*" Function,+,path_extract_dirname,void,"const char*, FuriString*" +Function,+,path_extract_ext_str,void,"FuriString*, FuriString*" Function,+,path_extract_extension,void,"FuriString*, char*, size_t" Function,+,path_extract_filename,void,"FuriString*, FuriString*, _Bool" Function,+,path_extract_filename_no_ext,void,"const char*, FuriString*" diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index 3d161a196..78a9ea8e9 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -34,6 +34,18 @@ void path_extract_filename(FuriString* path, FuriString* name, bool trim_ext) { } } +void path_extract_ext_str(FuriString* path, FuriString* ext) { + size_t dot = furi_string_search_rchar(path, '.'); + size_t filename_start = furi_string_search_rchar(path, '/'); + + if(dot != FURI_STRING_FAILURE && filename_start != FURI_STRING_FAILURE && + filename_start < dot) { + furi_string_set_n(ext, path, dot, furi_string_size(path) - dot); + } else { + furi_string_reset(ext); + } +} + void path_extract_extension(FuriString* path, char* ext, size_t ext_len_max) { size_t dot = furi_string_search_rchar(path, '.'); size_t filename_start = furi_string_search_rchar(path, '/'); diff --git a/lib/toolbox/path.h b/lib/toolbox/path.h index e6145b715..6296cfb3f 100644 --- a/lib/toolbox/path.h +++ b/lib/toolbox/path.h @@ -22,6 +22,14 @@ void path_extract_filename_no_ext(const char* path, FuriString* filename); */ void path_extract_filename(FuriString* path, FuriString* filename, bool trim_ext); +/** + * @brief Extract file extension string from path. + * + * @param path path string + * @param ext output extension furi string + */ +void path_extract_ext_str(FuriString* path, FuriString* ext); + /** * @brief Extract file extension from path. * From 2ea3c837a26d9f2a505c19140c9f14e46ff5ed6b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 23:43:11 +0100 Subject: [PATCH 173/370] And why tf are these buffers arbitrarily smaller?? --- .../avr_isp_programmer/scenes/avr_isp_scene_input_name.c | 4 +--- applications/main/archive/scenes/archive_scene_delete.c | 1 - applications/main/archive/scenes/archive_scene_new_dir.c | 3 +-- applications/main/archive/scenes/archive_scene_rename.c | 3 +-- applications/main/subghz/scenes/subghz_scene_save_name.c | 6 ++---- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c index 3394f4362..950d564ca 100644 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c +++ b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c @@ -1,8 +1,6 @@ #include "../avr_isp_app_i.h" #include -#define MAX_TEXT_INPUT_LEN 22 - void avr_isp_scene_input_name_text_callback(void* context) { furi_assert(context); @@ -46,7 +44,7 @@ void avr_isp_scene_input_name_on_enter(void* context) { avr_isp_scene_input_name_text_callback, app, app->file_name_tmp, - MAX_TEXT_INPUT_LEN, // buffer size + AVR_ISP_MAX_LEN_NAME, // buffer size dev_name_empty); ValidatorIsFile* validator_is_file = diff --git a/applications/main/archive/scenes/archive_scene_delete.c b/applications/main/archive/scenes/archive_scene_delete.c index 4a84e1c38..7dac0a9d3 100644 --- a/applications/main/archive/scenes/archive_scene_delete.c +++ b/applications/main/archive/scenes/archive_scene_delete.c @@ -3,7 +3,6 @@ #include "../helpers/archive_browser.h" #define SCENE_DELETE_CUSTOM_EVENT (0UL) -#define MAX_TEXT_INPUT_LEN 22 void archive_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); diff --git a/applications/main/archive/scenes/archive_scene_new_dir.c b/applications/main/archive/scenes/archive_scene_new_dir.c index 4d64d0ee9..235b349d2 100644 --- a/applications/main/archive/scenes/archive_scene_new_dir.c +++ b/applications/main/archive/scenes/archive_scene_new_dir.c @@ -9,7 +9,6 @@ #define TAG "Archive" #define SCENE_NEW_DIR_CUSTOM_EVENT (0UL) -#define MAX_TEXT_INPUT_LEN 22 void archive_scene_new_dir_text_input_callback(void* context) { ArchiveApp* archive = (ArchiveApp*)context; @@ -29,7 +28,7 @@ void archive_scene_new_dir_on_enter(void* context) { archive_scene_new_dir_text_input_callback, context, archive->text_store, - MAX_TEXT_INPUT_LEN, + MAX_NAME_LEN, false); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index 8401ce1c2..eb2f95489 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -9,7 +9,6 @@ #define TAG "Archive" #define SCENE_RENAME_CUSTOM_EVENT (0UL) -#define MAX_TEXT_INPUT_LEN 22 void archive_scene_rename_text_input_callback(void* context) { ArchiveApp* archive = (ArchiveApp*)context; @@ -47,7 +46,7 @@ void archive_scene_rename_on_enter(void* context) { archive_scene_rename_text_input_callback, context, archive->text_store, - MAX_TEXT_INPUT_LEN, + MAX_NAME_LEN, false); // Init validator to show message to user that name already exist diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index c05d0d962..73e7b74c7 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -6,8 +6,6 @@ #include #include -#define MAX_TEXT_INPUT_LEN 23 - void subghz_scene_save_name_text_input_callback(void* context) { furi_assert(context); SubGhz* subghz = context; @@ -108,7 +106,7 @@ void subghz_scene_save_name_on_enter(void* context) { subghz_scene_save_name_text_input_callback, subghz, subghz->file_name_tmp, - MAX_TEXT_INPUT_LEN, + SUBGHZ_MAX_LEN_NAME, dev_name_empty); ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( @@ -205,4 +203,4 @@ void subghz_scene_save_name_on_exit(void* context) { // Clear view text_input_reset(subghz->text_input); -} \ No newline at end of file +} From 45bdbca501b5d929f7a298da406ca4073f09913e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 12 Jun 2023 23:43:45 +0100 Subject: [PATCH 174/370] Archive hide/show ext based on tab not filetype --- applications/main/archive/views/archive_browser_view.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 6495ad76f..bd17f1925 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -228,16 +228,17 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveFile_t* file = files_array_get( model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); file_type = file->type; + bool ext = model->tab_idx == ArchiveTabBrowser || model->tab_idx == ArchiveTabInternal; if(file_type == ArchiveFileTypeApplication) { if(file->custom_icon_data) { custom_icon_data = file->custom_icon_data; furi_string_set(str_buf, file->custom_name); } else { file_type = ArchiveFileTypeUnknown; - path_extract_filename(file->path, str_buf, archive_is_known_app(file->type)); + path_extract_filename(file->path, str_buf, !ext); } } else { - path_extract_filename(file->path, str_buf, archive_is_known_app(file->type)); + path_extract_filename(file->path, str_buf, !ext); } } else { furi_string_set(str_buf, "---"); From 4ecab40fdeb1795edeaf4180ce131c68d95bd485 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 13 Jun 2023 03:10:10 +0100 Subject: [PATCH 175/370] Max file name length is actually 253 + null term More than that and fatfs will truncate like name~1 Also dir_read already accounts for null term in buf size, dont malloc +1 --- applications/debug/unit_tests/rpc/rpc_test.c | 6 +++--- .../scenes/wifi_marauder_scene_script_select.c | 8 +++++--- applications/main/archive/views/archive_browser_view.h | 2 +- applications/services/gui/modules/file_browser_worker.c | 2 +- applications/services/rpc/rpc_storage.c | 4 ++-- applications/services/storage/storage_cli.c | 2 +- applications/services/storage/storage_external_api.c | 4 ++-- lib/flipper_application/plugins/plugin_manager.c | 4 +++- lib/toolbox/dir_walk.c | 8 +++++--- lib/toolbox/tar/tar_archive.c | 2 +- lib/update_util/update_operation.h | 2 +- 11 files changed, 25 insertions(+), 19 deletions(-) diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 167266a84..e01f56be1 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -43,7 +43,7 @@ static RpcSessionContext rpc_session[TEST_RPC_SESSIONS]; #define TAG "UnitTestsRpc" #define MAX_RECEIVE_OUTPUT_TIMEOUT 3000 -#define MAX_NAME_LENGTH 255 +#define MAX_NAME_LENGTH 254 #define MAX_DATA_SIZE 512u // have to be exact as in rpc_storage.c #define TEST_DIR TEST_DIR_NAME "/" #define TEST_DIR_NAME EXT_PATH("unit_tests_tmp") @@ -186,7 +186,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) { File* dir = storage_file_alloc(fs_api); if(storage_dir_open(dir, clean_dir)) { FileInfo fileinfo; - char* name = malloc(MAX_NAME_LENGTH + 1); + char* name = malloc(MAX_NAME_LENGTH); while(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { size_t size = strlen(clean_dir) + strlen(name) + 1 + 1; char* fullname = malloc(size); @@ -598,7 +598,7 @@ static void test_rpc_storage_list_create_expected_list( while(!finish) { FileInfo fileinfo; - char* name = malloc(MAX_NAME_LENGTH + 1); + char* name = malloc(MAX_NAME_LENGTH); if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { if(i == COUNT_OF(list->file)) { list->file_count = i; diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c index bc0746858..ffdc2ded0 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c @@ -1,5 +1,7 @@ #include "../wifi_marauder_app_i.h" +#define MAX_NAME_LEN 254 + static void wifi_marauder_scene_script_select_callback(void* context, uint32_t index) { WifiMarauderApp* app = context; @@ -35,10 +37,10 @@ void wifi_marauder_scene_script_select_on_enter(void* context) { File* dir_scripts = storage_file_alloc(app->storage); if(storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS)) { FileInfo file_info; - char file_path[255]; + char file_path[MAX_NAME_LEN]; app->script_list_count = 0; // Goes through the files in the folder counting the ones that end with the json extension - while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + while(storage_dir_read(dir_scripts, &file_info, file_path, MAX_NAME_LEN)) { app->script_list_count++; } if(app->script_list_count > 0) { @@ -48,7 +50,7 @@ void wifi_marauder_scene_script_select_on_enter(void* context) { storage_dir_open(dir_scripts, MARAUDER_APP_FOLDER_SCRIPTS); // Read the files again from the beginning, adding the scripts in the list int script_index = 0; - while(storage_dir_read(dir_scripts, &file_info, file_path, 255)) { + while(storage_dir_read(dir_scripts, &file_info, file_path, MAX_NAME_LEN)) { app->script_list[script_index] = furi_string_alloc(); path_extract_filename_no_ext(file_path, app->script_list[script_index]); submenu_add_item( diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 7d8eef65c..383da4769 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -15,7 +15,7 @@ #include "gui/modules/file_browser_worker.h" #define MAX_LEN_PX 110 -#define MAX_NAME_LEN 255 +#define MAX_NAME_LEN 254 #define FRAME_HEIGHT 12 #define MENU_ITEMS 5u #define MOVE_OFFSET 5u diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 326040020..bde6c02a7 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -16,7 +16,7 @@ #define ASSETS_DIR "assets" #define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX -#define FILE_NAME_LEN_MAX 256 +#define FILE_NAME_LEN_MAX 254 #define LONG_LOAD_THRESHOLD 100 typedef enum { diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index c3a4a0470..96f43e1d7 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -15,7 +15,7 @@ #define TAG "RpcStorage" -#define MAX_NAME_LENGTH 255 +#define MAX_NAME_LENGTH 254 static const size_t MAX_DATA_SIZE = 512; @@ -282,7 +282,7 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex while(!finish) { FileInfo fileinfo; - char* name = malloc(MAX_NAME_LENGTH + 1); + char* name = malloc(MAX_NAME_LENGTH); if(storage_dir_read(dir, &fileinfo, name, MAX_NAME_LENGTH)) { if(path_contains_only_ascii(name)) { if(i == COUNT_OF(list->file)) { diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index d720de9d1..096060d82 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -9,7 +9,7 @@ #include #include -#define MAX_NAME_LENGTH 255 +#define MAX_NAME_LENGTH 254 static void storage_cli_print_usage() { printf("Usage:\r\n"); diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 38ff80252..d2c98f290 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -7,7 +7,7 @@ #include #include "toolbox/path.h" -#define MAX_NAME_LENGTH 256 +#define MAX_NAME_LENGTH 254 #define FILE_BUFFER_SIZE 512 #define TAG "StorageAPI" @@ -893,7 +893,7 @@ bool storage_simply_remove_recursive(Storage* storage, const char* path) { return true; } - char* name = malloc(MAX_NAME_LENGTH + 1); //-V799 + char* name = malloc(MAX_NAME_LENGTH); //-V799 File* dir = storage_file_alloc(storage); cur_dir = furi_string_alloc_set(path); bool go_deeper = false; diff --git a/lib/flipper_application/plugins/plugin_manager.c b/lib/flipper_application/plugins/plugin_manager.c index 101471dc5..ca4d99dcb 100644 --- a/lib/flipper_application/plugins/plugin_manager.c +++ b/lib/flipper_application/plugins/plugin_manager.c @@ -11,6 +11,8 @@ #define TAG "libmgr" +#define MAX_NAME_LEN 254 + ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) #define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST) @@ -103,7 +105,7 @@ PluginManagerError plugin_manager_load_single(PluginManager* manager, const char PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path) { File* directory = storage_file_alloc(manager->storage); - char file_name_buffer[256]; + char file_name_buffer[MAX_NAME_LEN]; FuriString* file_name = furi_string_alloc(); do { if(!storage_dir_open(directory, path)) { diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index 509ceb5b4..adfeeaea5 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -1,6 +1,8 @@ #include "dir_walk.h" #include +#define MAX_NAME_LEN 254 + LIST_DEF(DirIndexList, uint32_t); struct DirWalk { @@ -56,12 +58,12 @@ static bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* filei static DirWalkResult dir_walk_iter(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) { DirWalkResult result = DirWalkError; - char* name = malloc(256); // FIXME: remove magic number + char* name = malloc(MAX_NAME_LEN); // FIXME: remove magic number FileInfo info; bool end = false; while(!end) { - storage_dir_read(dir_walk->file, &info, name, 255); + storage_dir_read(dir_walk->file, &info, name, MAX_NAME_LEN); if(storage_file_get_error(dir_walk->file) == FSE_OK) { result = DirWalkOK; @@ -119,7 +121,7 @@ static DirWalkResult break; } - if(!storage_dir_read(dir_walk->file, &info, name, 255)) { + if(!storage_dir_read(dir_walk->file, &info, name, MAX_NAME_LEN)) { result = DirWalkError; end = true; break; diff --git a/lib/toolbox/tar/tar_archive.c b/lib/toolbox/tar/tar_archive.c index fcfc22a70..c8abbd6d4 100644 --- a/lib/toolbox/tar/tar_archive.c +++ b/lib/toolbox/tar/tar_archive.c @@ -6,7 +6,7 @@ #include #define TAG "TarArch" -#define MAX_NAME_LEN 255 +#define MAX_NAME_LEN 254 #define FILE_BLOCK_SIZE 512 #define FILE_OPEN_NTRIES 10 diff --git a/lib/update_util/update_operation.h b/lib/update_util/update_operation.h index 65abf8e15..270d60760 100644 --- a/lib/update_util/update_operation.h +++ b/lib/update_util/update_operation.h @@ -8,7 +8,7 @@ extern "C" { #endif #define UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC 0 -#define UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN 255u +#define UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN 254u #define UPDATE_OPERATION_MIN_MANIFEST_VERSION 2 /* From 392bd3cde09e17ab40fb55616d556ea3f77e9dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Tue, 13 Jun 2023 21:02:12 +0900 Subject: [PATCH 176/370] FuriHal: remove clock startup time tracking from clean builds (#2764) --- firmware/targets/f7/furi_hal/furi_hal_clock.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_clock.c b/firmware/targets/f7/furi_hal/furi_hal_clock.c index 770b74296..736ad9f7c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_clock.c +++ b/firmware/targets/f7/furi_hal/furi_hal_clock.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -143,7 +142,10 @@ void furi_hal_clock_switch_to_hsi() { } void furi_hal_clock_switch_to_pll() { +#ifdef FURI_HAL_CLOCK_TRACK_STARTUP uint32_t clock_start_time = DWT->CYCCNT; +#endif + LL_RCC_HSE_Enable(); LL_RCC_PLL_Enable(); LL_RCC_PLLSAI1_Enable(); @@ -166,11 +168,12 @@ void furi_hal_clock_switch_to_pll() { while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) ; +#ifdef FURI_HAL_CLOCK_TRACK_STARTUP uint32_t total = DWT->CYCCNT - clock_start_time; if(total > (20 * 0x148)) { - furi_hal_rtc_set_flag(FuriHalRtcFlagLegacySleep); furi_crash("Slow HSE/PLL startup"); } +#endif } void furi_hal_clock_suspend_tick() { From c4850ba89a9f712419d9048ec82e16e9eff84240 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 13 Jun 2023 18:36:55 +0100 Subject: [PATCH 177/370] Revert gatt refactor for now (breaks iOS BT HID) --- applications/services/bt/bt_service/bt.c | 2 +- firmware/targets/f7/ble_glue/app_debug.c | 4 +- .../ble_glue/{services => }/battery_service.c | 118 +- .../ble_glue/{services => }/battery_service.h | 0 firmware/targets/f7/ble_glue/ble_app.c | 79 +- .../targets/f7/ble_glue/dev_info_service.c | 220 ++ .../{services => }/dev_info_service.h | 0 firmware/targets/f7/ble_glue/hid_service.c | 416 ++++ .../f7/ble_glue/{services => }/hid_service.h | 3 +- .../ble_glue/{services => }/serial_service.c | 195 +- .../ble_glue/{services => }/serial_service.h | 0 .../f7/ble_glue/services/dev_info_service.c | 176 -- .../services/dev_info_service_uuid.inc | 3 - .../targets/f7/ble_glue/services/gatt_char.c | 123 - .../targets/f7/ble_glue/services/gatt_char.h | 96 - .../f7/ble_glue/services/hid_service.c | 365 --- .../ble_glue/services/serial_service_uuid.inc | 12 - firmware/targets/f7/furi_hal/furi_hal_bt.c | 3 +- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 10 +- .../targets/f7/furi_hal/furi_hal_bt_serial.c | 6 +- .../targets/furi_hal_include/furi_hal_bt.h | 2 +- .../furi_hal_include/furi_hal_bt_serial.h | 2 +- revert_gatt_char_refactor.patch | 2181 ----------------- 23 files changed, 871 insertions(+), 3145 deletions(-) rename firmware/targets/f7/ble_glue/{services => }/battery_service.c (53%) rename firmware/targets/f7/ble_glue/{services => }/battery_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/dev_info_service.c rename firmware/targets/f7/ble_glue/{services => }/dev_info_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/hid_service.c rename firmware/targets/f7/ble_glue/{services => }/hid_service.h (89%) rename firmware/targets/f7/ble_glue/{services => }/serial_service.c (57%) rename firmware/targets/f7/ble_glue/{services => }/serial_service.h (100%) delete mode 100644 firmware/targets/f7/ble_glue/services/dev_info_service.c delete mode 100644 firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc delete mode 100644 firmware/targets/f7/ble_glue/services/gatt_char.c delete mode 100644 firmware/targets/f7/ble_glue/services/gatt_char.h delete mode 100644 firmware/targets/f7/ble_glue/services/hid_service.c delete mode 100644 firmware/targets/f7/ble_glue/services/serial_service_uuid.inc delete mode 100644 revert_gatt_char_refactor.patch diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 882fd11e8..b8cb09734 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -1,7 +1,7 @@ #include "bt_i.h" +#include "battery_service.h" #include "bt_keys_storage.h" -#include #include #include #include diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index 35f53ae22..78e789ac3 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -195,14 +195,14 @@ static void APPD_SetCPU2GpioConfig(void) { gpio_config.Pin = gpiob_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); LL_GPIO_Init(GPIOB, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); + LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list); } if(gpioc_pin_list != 0) { gpio_config.Pin = gpioc_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); LL_GPIO_Init(GPIOC, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); + LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); } } diff --git a/firmware/targets/f7/ble_glue/services/battery_service.c b/firmware/targets/f7/ble_glue/battery_service.c similarity index 53% rename from firmware/targets/f7/ble_glue/services/battery_service.c rename to firmware/targets/f7/ble_glue/battery_service.c index 63f736b3b..8c371efad 100644 --- a/firmware/targets/f7/ble_glue/services/battery_service.c +++ b/firmware/targets/f7/ble_glue/battery_service.c @@ -1,7 +1,5 @@ #include "battery_service.h" #include "app_common.h" -#include "gatt_char.h" - #include #include @@ -9,6 +7,12 @@ #define TAG "BtBatterySvc" +typedef struct { + uint16_t svc_handle; + uint16_t battery_level_char_handle; + uint16_t power_state_char_handle; +} BatterySvc; + enum { // Common states BatterySvcPowerStateUnknown = 0b00, @@ -36,44 +40,13 @@ typedef struct { _Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); +static BatterySvc* battery_svc = NULL; + #define BATTERY_POWER_STATE (0x2A1A) static const uint16_t service_uuid = BATTERY_SERVICE_UUID; - -typedef enum { - BatterySvcGattCharacteristicBatteryLevel = 0, - BatterySvcGattCharacteristicPowerState, - BatterySvcGattCharacteristicCount, -} BatterySvcGattCharacteristicId; - -static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = - {[BatterySvcGattCharacteristicBatteryLevel] = - {.name = "Battery Level", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [BatterySvcGattCharacteristicPowerState] = { - .name = "Power State", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = BATTERY_POWER_STATE, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}}; - -typedef struct { - uint16_t svc_handle; - FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount]; -} BatterySvc; - -static BatterySvc* battery_svc = NULL; +static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; +static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; void battery_svc_start() { battery_svc = malloc(sizeof(BatterySvc)); @@ -85,19 +58,53 @@ void battery_svc_start() { if(status) { FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); } - for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); + // Add Battery level characteristic + status = aci_gatt_add_char( + battery_svc->svc_handle, + UUID_TYPE_16, + (Char_UUID_t*)&battery_level_char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &battery_svc->battery_level_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); } - + // Add Power state characteristic + status = aci_gatt_add_char( + battery_svc->svc_handle, + UUID_TYPE_16, + (Char_UUID_t*)&power_state_char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &battery_svc->power_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); + } + // Update power state charachteristic battery_svc_update_power_state(); } void battery_svc_stop() { tBleStatus status; if(battery_svc) { - for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); + // Delete Battery level characteristic + status = + aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); + } + // Delete Power state characteristic + status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); } // Delete Battery service status = aci_gatt_del_service(battery_svc->svc_handle); @@ -119,10 +126,13 @@ bool battery_svc_update_level(uint8_t battery_charge) { return false; } // Update battery level characteristic - return flipper_gatt_characteristic_update( - battery_svc->svc_handle, - &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], - &battery_charge); + FURI_LOG_D(TAG, "Updating battery level characteristic"); + tBleStatus result = aci_gatt_update_char_value( + battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); + if(result) { + FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); + } + return result != BLE_STATUS_SUCCESS; } bool battery_svc_update_power_state() { @@ -142,9 +152,15 @@ bool battery_svc_update_power_state() { power_state.charging = BatterySvcPowerStateNotCharging; power_state.discharging = BatterySvcPowerStateDischarging; } - - return flipper_gatt_characteristic_update( + FURI_LOG_D(TAG, "Updating power state characteristic"); + tBleStatus result = aci_gatt_update_char_value( battery_svc->svc_handle, - &battery_svc->chars[BatterySvcGattCharacteristicPowerState], - &power_state); + battery_svc->power_state_char_handle, + 0, + 1, + (uint8_t*)&power_state); + if(result) { + FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); + } + return result != BLE_STATUS_SUCCESS; } diff --git a/firmware/targets/f7/ble_glue/services/battery_service.h b/firmware/targets/f7/ble_glue/battery_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/services/battery_service.h rename to firmware/targets/f7/ble_glue/battery_service.h diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 7a2148b6b..4fc4d521b 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -33,45 +33,6 @@ static int32_t ble_app_hci_thread(void* context); static void ble_app_hci_event_handler(void* pPayload); static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); -static const HCI_TL_HciInitConf_t hci_tl_config = { - .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, - .StatusNotCallBack = ble_app_hci_status_not_handler, -}; - -static const SHCI_C2_CONFIG_Cmd_Param_t config_param = { - .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, - .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, - .BleNvmRamAddress = (uint32_t)ble_app_nvm, - .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, -}; - -static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { - .Header = {{0, 0, 0}}, // Header unused - .Param = { - .pBleBufferAddress = 0, // pBleBufferAddress not used - .BleBufferSize = 0, // BleBufferSize not used - .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, - .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, - .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, - .NumOfLinks = CFG_BLE_NUM_LINK, - .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, - .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, - .MblockCount = CFG_BLE_MBLOCK_COUNT, - .AttMtu = CFG_BLE_MAX_ATT_MTU, - .SlaveSca = CFG_BLE_SLAVE_SCA, - .MasterSca = CFG_BLE_MASTER_SCA, - .LsSource = CFG_BLE_LSE_SOURCE, - .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, - .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, - .ViterbiEnable = CFG_BLE_VITERBI_MODE, - .Options = CFG_BLE_OPTIONS, - .HwVersion = 0, - .max_coc_initiator_nbr = 32, - .min_tx_power = 0, - .max_tx_power = 0, - .rx_model_config = 1, - }}; - bool ble_app_init() { SHCI_CmdStatus_t status; ble_app = malloc(sizeof(BleApp)); @@ -83,16 +44,52 @@ bool ble_app_init() { furi_thread_start(ble_app->thread); // Initialize Ble Transport Layer + HCI_TL_HciInitConf_t hci_tl_config = { + .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, + .StatusNotCallBack = ble_app_hci_status_not_handler, + }; hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); // Configure NVM store for pairing data - status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); + SHCI_C2_CONFIG_Cmd_Param_t config_param = { + .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, + .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, + .BleNvmRamAddress = (uint32_t)ble_app_nvm, + .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, + }; + status = SHCI_C2_Config(&config_param); if(status) { FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); } // Start ble stack on 2nd core - status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); + SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { + .Header = {{0, 0, 0}}, // Header unused + .Param = { + .pBleBufferAddress = 0, // pBleBufferAddress not used + .BleBufferSize = 0, // BleBufferSize not used + .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, + .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, + .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, + .NumOfLinks = CFG_BLE_NUM_LINK, + .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, + .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, + .MblockCount = CFG_BLE_MBLOCK_COUNT, + .AttMtu = CFG_BLE_MAX_ATT_MTU, + .SlaveSca = CFG_BLE_SLAVE_SCA, + .MasterSca = CFG_BLE_MASTER_SCA, + .LsSource = CFG_BLE_LSE_SOURCE, + .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, + .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, + .ViterbiEnable = CFG_BLE_VITERBI_MODE, + .Options = CFG_BLE_OPTIONS, + .HwVersion = 0, + .max_coc_initiator_nbr = 32, + .min_tx_power = 0, + .max_tx_power = 0, + .rx_model_config = 1, + }}; + status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); if(status) { FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); } diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c new file mode 100644 index 000000000..d24058632 --- /dev/null +++ b/firmware/targets/f7/ble_glue/dev_info_service.c @@ -0,0 +1,220 @@ +#include "dev_info_service.h" +#include "app_common.h" +#include + +#include +#include +#include + +#define TAG "BtDevInfoSvc" + +typedef struct { + uint16_t service_handle; + uint16_t man_name_char_handle; + uint16_t serial_num_char_handle; + uint16_t firmware_rev_char_handle; + uint16_t software_rev_char_handle; + uint16_t rpc_version_char_handle; + FuriString* version_string; + char hardware_revision[4]; +} DevInfoSvc; + +static DevInfoSvc* dev_info_svc = NULL; + +static const char dev_info_man_name[] = "Flipper Devices Inc."; +static const char dev_info_serial_num[] = "1.0"; +static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); + +static const uint8_t dev_info_rpc_version_uuid[] = + {0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03}; + +void dev_info_svc_start() { + dev_info_svc = malloc(sizeof(DevInfoSvc)); + dev_info_svc->version_string = furi_string_alloc_printf( + "%s %s %s %s", + version_get_githash(NULL), + version_get_version(NULL), + version_get_gitbranchnum(NULL), + version_get_builddate(NULL)); + snprintf( + dev_info_svc->hardware_revision, + sizeof(dev_info_svc->hardware_revision), + "%d", + version_get_target(NULL)); + tBleStatus status; + + // Add Device Information Service + uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; + status = aci_gatt_add_service( + UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 11, &dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); + } + + // Add characteristics + uuid = MANUFACTURER_NAME_UUID; + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_16, + (Char_UUID_t*)&uuid, + strlen(dev_info_man_name), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->man_name_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status); + } + uuid = SERIAL_NUMBER_UUID; + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_16, + (Char_UUID_t*)&uuid, + strlen(dev_info_serial_num), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->serial_num_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add serial number char: %d", status); + } + uuid = FIRMWARE_REVISION_UUID; + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_16, + (Char_UUID_t*)&uuid, + strlen(dev_info_svc->hardware_revision), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->firmware_rev_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status); + } + uuid = SOFTWARE_REVISION_UUID; + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_16, + (Char_UUID_t*)&uuid, + furi_string_size(dev_info_svc->version_string), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->software_rev_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add software revision char: %d", status); + } + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_128, + (const Char_UUID_t*)dev_info_rpc_version_uuid, + strlen(dev_info_rpc_version), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->rpc_version_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status); + } + + // Update characteristics + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->man_name_char_handle, + 0, + strlen(dev_info_man_name), + (uint8_t*)dev_info_man_name); + if(status) { + FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status); + } + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->serial_num_char_handle, + 0, + strlen(dev_info_serial_num), + (uint8_t*)dev_info_serial_num); + if(status) { + FURI_LOG_E(TAG, "Failed to update serial number char: %d", status); + } + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->firmware_rev_char_handle, + 0, + strlen(dev_info_svc->hardware_revision), + (uint8_t*)dev_info_svc->hardware_revision); + if(status) { + FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status); + } + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->software_rev_char_handle, + 0, + furi_string_size(dev_info_svc->version_string), + (uint8_t*)furi_string_get_cstr(dev_info_svc->version_string)); + if(status) { + FURI_LOG_E(TAG, "Failed to update software revision char: %d", status); + } + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->rpc_version_char_handle, + 0, + strlen(dev_info_rpc_version), + (uint8_t*)dev_info_rpc_version); + if(status) { + FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status); + } +} + +void dev_info_svc_stop() { + tBleStatus status; + if(dev_info_svc) { + furi_string_free(dev_info_svc->version_string); + // Delete service characteristics + status = + aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status); + } + status = + aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status); + } + status = aci_gatt_del_char( + dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status); + } + status = aci_gatt_del_char( + dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status); + } + status = + aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status); + } + // Delete service + status = aci_gatt_del_service(dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); + } + free(dev_info_svc); + dev_info_svc = NULL; + } +} + +bool dev_info_svc_is_started() { + return dev_info_svc != NULL; +} diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.h b/firmware/targets/f7/ble_glue/dev_info_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/services/dev_info_service.h rename to firmware/targets/f7/ble_glue/dev_info_service.h diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c new file mode 100644 index 000000000..a31d6015f --- /dev/null +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -0,0 +1,416 @@ +#include "hid_service.h" +#include "app_common.h" +#include + +#include + +#define TAG "BtHid" + +typedef struct { + uint16_t svc_handle; + uint16_t protocol_mode_char_handle; + uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; + uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; + uint16_t report_map_char_handle; + uint16_t info_char_handle; + uint16_t ctrl_point_char_handle; + // led state + uint16_t led_state_char_handle; + uint16_t led_state_desc_handle; + HidLedStateEventCallback led_state_event_callback; + void* led_state_ctx; +} HIDSvc; + +static HIDSvc* hid_svc = NULL; + +static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { + SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); + evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; + // aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + // Process modification events + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { + // Process notification confirmation + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { + // Process write request + aci_gatt_write_permit_req_event_rp0* req = + (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; + + furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); + + // this check is likely to be incorrect, it will actually work in our case + // but we need to investigate gatt api to see what is the rules + // that specify attibute handle value from char handle (or the reverse) + if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) { + hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); + aci_gatt_write_resp( + req->Connection_Handle, + req->Attribute_Handle, + 0x00, /* write_status = 0 (no error))*/ + 0x00, /* err_code */ + req->Data_Length, + req->Data); + aci_gatt_write_char_value( + req->Connection_Handle, + hid_svc->led_state_char_handle, + req->Data_Length, + req->Data); + ret = SVCCTL_EvtAckFlowEnable; + } + } + } + return ret; +} + +void hid_svc_start() { + tBleStatus status; + hid_svc = malloc(sizeof(HIDSvc)); + Service_UUID_t svc_uuid = {}; + Char_Desc_Uuid_t desc_uuid = {}; + Char_UUID_t char_uuid = {}; + + // Register event handler + SVCCTL_RegisterSvcHandler(hid_svc_event_handler); + // Add service + svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; + /** + * Add Human Interface Device Service + */ + status = aci_gatt_add_service( + UUID_TYPE_16, + &svc_uuid, + PRIMARY_SERVICE, + 2 + /* protocol mode */ + (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + + 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ + &hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add HID service: %d", status); + } + // Add Protocol mode characteristics + char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + ATTR_PERMISSION_NONE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_CONSTANT, + &hid_svc->protocol_mode_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status); + } + // Update Protocol mode characteristic + uint8_t protocol_mode = 1; + status = aci_gatt_update_char_value( + hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode); + if(status) { + FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); + } + +#if(HID_SVC_REPORT_COUNT != 0) + for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { + if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547 + uint8_t buf[2] = {i + 1, 1}; // 1 input + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_REPORT_MAX_LEN, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &(hid_svc->report_char_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); + } + + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->report_char_handle[i], + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->report_ref_desc_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); + } + } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { + uint8_t buf[2] = {i + 1, 2}; // 2 output + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_REPORT_MAX_LEN, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &(hid_svc->report_char_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); + } + + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->report_char_handle[i], + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->report_ref_desc_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); + } + } else { + uint8_t buf[2] = {i + 1, 3}; // 3 feature + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_REPORT_MAX_LEN, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &(hid_svc->report_char_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); + } + + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->report_char_handle[i], + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->report_ref_desc_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); + } + } + } +#endif + // Add led state output report + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, + ATTR_PERMISSION_NONE, + GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, + 10, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->led_state_char_handle)); + if(status) { + FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status); + } + + // Add led state char descriptor specifying it is an output report + uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->led_state_char_handle, + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->led_state_desc_handle)); + if(status) { + FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status); + } + // Add Report Map characteristic + char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_REPORT_MAP_MAX_LEN, + CHAR_PROP_READ, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &hid_svc->report_map_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); + } + + // Add Information characteristic + char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_INFO_LEN, + CHAR_PROP_READ, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &hid_svc->info_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status); + } + // Add Control Point characteristic + char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_CONTROL_POINT_LEN, + CHAR_PROP_WRITE_WITHOUT_RESP, + ATTR_PERMISSION_NONE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_CONSTANT, + &hid_svc->ctrl_point_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); + } + + hid_svc->led_state_event_callback = NULL; + hid_svc->led_state_ctx = NULL; +} + +bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + tBleStatus status = aci_gatt_update_char_value( + hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); + if(status) { + FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); + return false; + } + return true; +} + +bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + tBleStatus status = aci_gatt_update_char_value( + hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); + if(status) { + FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); + return false; + } + return true; +} + +bool hid_svc_update_info(uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + tBleStatus status = + aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); + if(status) { + FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); + return false; + } + return true; +} + +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { + furi_assert(hid_svc); + furi_assert(callback); + furi_assert(context); + + hid_svc->led_state_event_callback = callback; + hid_svc->led_state_ctx = context; +} + +bool hid_svc_is_started() { + return hid_svc != NULL; +} + +void hid_svc_stop() { + tBleStatus status; + if(hid_svc) { + // Delete characteristics + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); + } +#if(HID_SVC_INPUT_REPORT_COUNT != 0) + for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); + } + } +#endif + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); + } + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); + } + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); + } + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status); + } + // Delete service + status = aci_gatt_del_service(hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); + } + // Delete buffer size mutex + free(hid_svc); + hid_svc = NULL; + } +} diff --git a/firmware/targets/f7/ble_glue/services/hid_service.h b/firmware/targets/f7/ble_glue/hid_service.h similarity index 89% rename from firmware/targets/f7/ble_glue/services/hid_service.h rename to firmware/targets/f7/ble_glue/hid_service.h index 4d0ed4c4f..b8f6b244d 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.h +++ b/firmware/targets/f7/ble_glue/hid_service.h @@ -27,7 +27,6 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); -// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) -bool hid_svc_update_info(uint8_t* data); +bool hid_svc_update_info(uint8_t* data, uint16_t len); void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); diff --git a/firmware/targets/f7/ble_glue/services/serial_service.c b/firmware/targets/f7/ble_glue/serial_service.c similarity index 57% rename from firmware/targets/f7/ble_glue/services/serial_service.c rename to firmware/targets/f7/ble_glue/serial_service.c index ab009bbfc..c6421dc28 100644 --- a/firmware/targets/f7/ble_glue/services/serial_service.c +++ b/firmware/targets/f7/ble_glue/serial_service.c @@ -1,67 +1,17 @@ #include "serial_service.h" #include "app_common.h" #include -#include "gatt_char.h" #include -#include "serial_service_uuid.inc" - #define TAG "BtSerialSvc" -typedef enum { - SerialSvcGattCharacteristicTx = 0, - SerialSvcGattCharacteristicRx, - SerialSvcGattCharacteristicFlowCtrl, - SerialSvcGattCharacteristicStatus, - SerialSvcGattCharacteristicCount, -} SerialSvcGattCharacteristicId; - -static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { - [SerialSvcGattCharacteristicTx] = - {.name = "TX", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, - .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_VARIABLE}, - [SerialSvcGattCharacteristicRx] = - {.name = "RX", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, - .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, - .is_variable = CHAR_VALUE_LEN_VARIABLE}, - [SerialSvcGattCharacteristicFlowCtrl] = - {.name = "Flow control", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(uint32_t), - .uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [SerialSvcGattCharacteristicStatus] = { - .name = "RPC status", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(SerialServiceRpcStatus), - .uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, - .is_variable = CHAR_VALUE_LEN_CONSTANT}}; - typedef struct { uint16_t svc_handle; - FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; + uint16_t rx_char_handle; + uint16_t tx_char_handle; + uint16_t flow_ctrl_char_handle; + uint16_t rpc_status_char_handle; FuriMutex* buff_size_mtx; uint32_t buff_size; uint16_t bytes_ready_to_receive; @@ -71,6 +21,17 @@ typedef struct { static SerialSvc* serial_svc = NULL; +static const uint8_t service_uuid[] = + {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; +static const uint8_t char_tx_uuid[] = + {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t char_rx_uuid[] = + {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t flow_ctrl_uuid[] = + {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t rpc_status_uuid[] = + {0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; + static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); @@ -79,14 +40,11 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; - if(attribute_modified->Attr_Handle == - serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) { + if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { // Descriptor handle ret = SVCCTL_EvtAckFlowEnable; FURI_LOG_D(TAG, "RX descriptor event"); - } else if( - attribute_modified->Attr_Handle == - serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { + } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); if(serial_svc->callback) { furi_check( @@ -112,9 +70,7 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } ret = SVCCTL_EvtAckFlowEnable; - } else if( - attribute_modified->Attr_Handle == - serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { + } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { SerialServiceRpcStatus* rpc_status = (SerialServiceRpcStatus*)attribute_modified->Attr_Data; if(*rpc_status == SerialServiceRpcStatusNotActive) { @@ -141,12 +97,18 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { } static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { - flipper_gatt_characteristic_update( - serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); + tBleStatus ble_status = aci_gatt_update_char_value( + serial_svc->svc_handle, + serial_svc->rpc_status_char_handle, + 0, + sizeof(SerialServiceRpcStatus), + (uint8_t*)&status); + if(ble_status) { + FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status); + } } void serial_svc_start() { - UNUSED(serial_svc_chars); tBleStatus status; serial_svc = malloc(sizeof(SerialSvc)); // Register event handler @@ -154,17 +116,72 @@ void serial_svc_start() { // Add service status = aci_gatt_add_service( - UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); + UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); } - // Add characteristics - for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); + // Add RX characteristics + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)char_rx_uuid, + SERIAL_SVC_DATA_LEN_MAX, + CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_VARIABLE, + &serial_svc->rx_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status); } + // Add TX characteristic + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)char_tx_uuid, + SERIAL_SVC_DATA_LEN_MAX, + CHAR_PROP_READ | CHAR_PROP_INDICATE, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &serial_svc->tx_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status); + } + // Add Flow Control characteristic + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)flow_ctrl_uuid, + sizeof(uint32_t), + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &serial_svc->flow_ctrl_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); + } + // Add RPC status characteristic + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)rpc_status_uuid, + sizeof(SerialServiceRpcStatus), + CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_CONSTANT, + &serial_svc->rpc_status_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status); + } serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); // Allocate buffer size mutex serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); @@ -179,12 +196,13 @@ void serial_svc_set_callbacks( serial_svc->context = context; serial_svc->buff_size = buff_size; serial_svc->bytes_ready_to_receive = buff_size; - uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - flipper_gatt_characteristic_update( + aci_gatt_update_char_value( serial_svc->svc_handle, - &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], - &buff_size_reversed); + serial_svc->flow_ctrl_char_handle, + 0, + sizeof(uint32_t), + (uint8_t*)&buff_size_reversed); } void serial_svc_notify_buffer_is_empty() { @@ -195,12 +213,13 @@ void serial_svc_notify_buffer_is_empty() { if(serial_svc->bytes_ready_to_receive == 0) { FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); serial_svc->bytes_ready_to_receive = serial_svc->buff_size; - uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - flipper_gatt_characteristic_update( + aci_gatt_update_char_value( serial_svc->svc_handle, - &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], - &buff_size_reversed); + serial_svc->flow_ctrl_char_handle, + 0, + sizeof(uint32_t), + (uint8_t*)&buff_size_reversed); } furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } @@ -208,8 +227,22 @@ void serial_svc_notify_buffer_is_empty() { void serial_svc_stop() { tBleStatus status; if(serial_svc) { - for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); + // Delete characteristics + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status); + } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status); + } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); + } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status); } // Delete service status = aci_gatt_del_service(serial_svc->svc_handle); @@ -240,7 +273,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { tBleStatus result = aci_gatt_update_char_value_ext( 0, serial_svc->svc_handle, - serial_svc->chars[SerialSvcGattCharacteristicTx].handle, + serial_svc->tx_char_handle, remained ? 0x00 : 0x02, data_len, value_offset, diff --git a/firmware/targets/f7/ble_glue/services/serial_service.h b/firmware/targets/f7/ble_glue/serial_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/services/serial_service.h rename to firmware/targets/f7/ble_glue/serial_service.h diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.c b/firmware/targets/f7/ble_glue/services/dev_info_service.c deleted file mode 100644 index 5bee97b41..000000000 --- a/firmware/targets/f7/ble_glue/services/dev_info_service.c +++ /dev/null @@ -1,176 +0,0 @@ -#include "dev_info_service.h" -#include "app_common.h" -#include "gatt_char.h" -#include - -#include -#include -#include - -#include "dev_info_service_uuid.inc" - -#define TAG "BtDevInfoSvc" - -typedef enum { - DevInfoSvcGattCharacteristicMfgName = 0, - DevInfoSvcGattCharacteristicSerial, - DevInfoSvcGattCharacteristicFirmwareRev, - DevInfoSvcGattCharacteristicSoftwareRev, - DevInfoSvcGattCharacteristicRpcVersion, - DevInfoSvcGattCharacteristicCount, -} DevInfoSvcGattCharacteristicId; - -#define DEVICE_INFO_HARDWARE_REV_SIZE 4 -typedef struct { - uint16_t service_handle; - FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount]; - FuriString* version_string; - char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE]; -} DevInfoSvc; - -static DevInfoSvc* dev_info_svc = NULL; - -static const char dev_info_man_name[] = "Flipper Devices Inc."; -static const char dev_info_serial_num[] = "1.0"; -static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); - -static bool dev_info_char_firmware_rev_callback( - const void* context, - const uint8_t** data, - uint16_t* data_len) { - const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; - *data_len = sizeof(dev_info_svc->hardware_revision); - if(data) { - *data = (const uint8_t*)&dev_info_svc->hardware_revision; - } - return false; -} - -static bool dev_info_char_software_rev_callback( - const void* context, - const uint8_t** data, - uint16_t* data_len) { - const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; - *data_len = furi_string_size(dev_info_svc->version_string); - if(data) { - *data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string); - } - return false; -} - -static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] = - {[DevInfoSvcGattCharacteristicMfgName] = - {.name = "Manufacturer Name", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(dev_info_man_name) - 1, - .data.fixed.ptr = (const uint8_t*)&dev_info_man_name, - .uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [DevInfoSvcGattCharacteristicSerial] = - {.name = "Serial Number", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(dev_info_serial_num) - 1, - .data.fixed.ptr = (const uint8_t*)&dev_info_serial_num, - .uuid.Char_UUID_16 = SERIAL_NUMBER_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [DevInfoSvcGattCharacteristicFirmwareRev] = - {.name = "Firmware Revision", - .data_prop_type = FlipperGattCharacteristicDataCallback, - .data.callback.context = &dev_info_svc, - .data.callback.fn = dev_info_char_firmware_rev_callback, - .uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [DevInfoSvcGattCharacteristicSoftwareRev] = - {.name = "Software Revision", - .data_prop_type = FlipperGattCharacteristicDataCallback, - .data.callback.context = &dev_info_svc, - .data.callback.fn = dev_info_char_software_rev_callback, - .uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [DevInfoSvcGattCharacteristicRpcVersion] = { - .name = "RPC Version", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(dev_info_rpc_version) - 1, - .data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version, - .uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}}; - -void dev_info_svc_start() { - dev_info_svc = malloc(sizeof(DevInfoSvc)); - dev_info_svc->version_string = furi_string_alloc_printf( - "%s %s %s %s", - version_get_githash(NULL), - version_get_version(NULL), - version_get_gitbranchnum(NULL), - version_get_builddate(NULL)); - snprintf( - dev_info_svc->hardware_revision, - sizeof(dev_info_svc->hardware_revision), - "%d", - version_get_target(NULL)); - tBleStatus status; - - // Add Device Information Service - uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; - status = aci_gatt_add_service( - UUID_TYPE_16, - (Service_UUID_t*)&uuid, - PRIMARY_SERVICE, - 1 + 2 * DevInfoSvcGattCharacteristicCount, - &dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); - } - - for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - dev_info_svc->service_handle, - &dev_info_svc_chars[i], - &dev_info_svc->characteristics[i]); - flipper_gatt_characteristic_update( - dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL); - } -} - -void dev_info_svc_stop() { - tBleStatus status; - if(dev_info_svc) { - furi_string_free(dev_info_svc->version_string); - // Delete service characteristics - for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_delete( - dev_info_svc->service_handle, &dev_info_svc->characteristics[i]); - } - // Delete service - status = aci_gatt_del_service(dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); - } - free(dev_info_svc); - dev_info_svc = NULL; - } -} - -bool dev_info_svc_is_started() { - return dev_info_svc != NULL; -} diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc deleted file mode 100644 index ad520f62e..000000000 --- a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc +++ /dev/null @@ -1,3 +0,0 @@ -#define DEV_INVO_RPC_VERSION_UID \ - { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } - diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c deleted file mode 100644 index 9b6a44f61..000000000 --- a/firmware/targets/f7/ble_glue/services/gatt_char.c +++ /dev/null @@ -1,123 +0,0 @@ -#include "gatt_char.h" - -#include - -#define TAG "GattChar" - -#define GATT_MIN_READ_KEY_SIZE (10) - -void flipper_gatt_characteristic_init( - uint16_t svc_handle, - const FlipperGattCharacteristicParams* char_descriptor, - FlipperGattCharacteristicInstance* char_instance) { - furi_assert(char_descriptor); - furi_assert(char_instance); - - // Copy the descriptor to the instance, since it may point to stack memory - // TODO: only copy if really comes from stack - char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); - memcpy( - (void*)char_instance->characteristic, - char_descriptor, - sizeof(FlipperGattCharacteristicParams)); - - uint16_t char_data_size = 0; - if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { - char_data_size = char_descriptor->data.fixed.length; - } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { - char_descriptor->data.callback.fn( - char_descriptor->data.callback.context, NULL, &char_data_size); - } - - tBleStatus status = aci_gatt_add_char( - svc_handle, - char_descriptor->uuid_type, - &char_descriptor->uuid, - char_data_size, - char_descriptor->char_properties, - char_descriptor->security_permissions, - char_descriptor->gatt_evt_mask, - GATT_MIN_READ_KEY_SIZE, - char_descriptor->is_variable, - &char_instance->handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); - } - - char_instance->descriptor_handle = 0; - if((status == 0) && char_descriptor->descriptor_params) { - uint8_t const* char_data = NULL; - const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = - char_descriptor->descriptor_params; - bool release_data = char_data_descriptor->data_callback.fn( - char_data_descriptor->data_callback.context, &char_data, &char_data_size); - - status = aci_gatt_add_char_desc( - svc_handle, - char_instance->handle, - char_data_descriptor->uuid_type, - &char_data_descriptor->uuid, - char_data_descriptor->max_length, - char_data_size, - char_data, - char_data_descriptor->security_permissions, - char_data_descriptor->access_permissions, - char_data_descriptor->gatt_evt_mask, - GATT_MIN_READ_KEY_SIZE, - char_data_descriptor->is_variable, - &char_instance->descriptor_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); - } - if(release_data) { - free((void*)char_data); - } - } -} - -void flipper_gatt_characteristic_delete( - uint16_t svc_handle, - FlipperGattCharacteristicInstance* char_instance) { - tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); - if(status) { - FURI_LOG_E( - TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); - } - free((void*)char_instance->characteristic); -} - -bool flipper_gatt_characteristic_update( - uint16_t svc_handle, - FlipperGattCharacteristicInstance* char_instance, - const void* source) { - furi_assert(char_instance); - const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; - FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); - - const uint8_t* char_data = NULL; - uint16_t char_data_size = 0; - bool release_data = false; - if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { - char_data = char_descriptor->data.fixed.ptr; - if(source) { - char_data = (uint8_t*)source; - } - char_data_size = char_descriptor->data.fixed.length; - } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { - const void* context = char_descriptor->data.callback.context; - if(source) { - context = source; - } - release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); - } - - tBleStatus result = aci_gatt_update_char_value( - svc_handle, char_instance->handle, 0, char_data_size, char_data); - if(result) { - FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); - } - if(release_data) { - free((void*)char_data); - } - return result != BLE_STATUS_SUCCESS; -} \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.h b/firmware/targets/f7/ble_glue/services/gatt_char.h deleted file mode 100644 index 959ab67a4..000000000 --- a/firmware/targets/f7/ble_glue/services/gatt_char.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// Callback signature for getting characteristic data -// Is called when characteristic is created to get max data length. Data ptr is NULL in this case -// The result is passed to aci_gatt_add_char as "Char_Value_Length" -// For updates, called with a context - see flipper_gatt_characteristic_update -// Returns true if *data ownership is transferred to the caller and will be freed -typedef bool (*cbFlipperGattCharacteristicData)( - const void* context, - const uint8_t** data, - uint16_t* data_len); - -typedef enum { - FlipperGattCharacteristicDataFixed, - FlipperGattCharacteristicDataCallback, -} FlipperGattCharacteristicDataType; - -typedef struct { - Char_Desc_Uuid_t uuid; - struct { - cbFlipperGattCharacteristicData fn; - const void* context; - } data_callback; - uint8_t uuid_type; - uint8_t max_length; - uint8_t security_permissions; - uint8_t access_permissions; - uint8_t gatt_evt_mask; - uint8_t is_variable; -} FlipperGattCharacteristicDescriptorParams; - -typedef struct { - const char* name; - FlipperGattCharacteristicDescriptorParams* descriptor_params; - union { - struct { - const uint8_t* ptr; - uint16_t length; - } fixed; - struct { - cbFlipperGattCharacteristicData fn; - const void* context; - } callback; - } data; - Char_UUID_t uuid; - // Some packed bitfields to save space - FlipperGattCharacteristicDataType data_prop_type : 2; - uint8_t is_variable : 2; - uint8_t uuid_type : 2; - uint8_t char_properties; - uint8_t security_permissions; - uint8_t gatt_evt_mask; -} FlipperGattCharacteristicParams; - -_Static_assert( - sizeof(FlipperGattCharacteristicParams) == 36, - "FlipperGattCharacteristicParams size must be 36 bytes"); - -typedef struct { - const FlipperGattCharacteristicParams* characteristic; - uint16_t handle; - uint16_t descriptor_handle; -} FlipperGattCharacteristicInstance; - -// Initialize a characteristic instance; copies the characteristic descriptor into the instance -void flipper_gatt_characteristic_init( - uint16_t svc_handle, - const FlipperGattCharacteristicParams* char_descriptor, - FlipperGattCharacteristicInstance* char_instance); - -// Delete a characteristic instance; frees the copied characteristic descriptor from the instance -void flipper_gatt_characteristic_delete( - uint16_t svc_handle, - FlipperGattCharacteristicInstance* char_instance); - -// Update a characteristic instance; if source==NULL, uses the data from the characteristic -// - For fixed data, fixed.ptr is used as the source if source==NULL -// - For callback-based data, collback.context is passed as the context if source==NULL -bool flipper_gatt_characteristic_update( - uint16_t svc_handle, - FlipperGattCharacteristicInstance* char_instance, - const void* source); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c deleted file mode 100644 index 11f10b7b3..000000000 --- a/firmware/targets/f7/ble_glue/services/hid_service.c +++ /dev/null @@ -1,365 +0,0 @@ -#include "hid_service.h" -#include "app_common.h" -#include -#include "gatt_char.h" - -#include - -#define TAG "BtHid" - -typedef enum { - HidSvcGattCharacteristicProtocolMode = 0, - HidSvcGattCharacteristicReportMap, - HidSvcGattCharacteristicInfo, - HidSvcGattCharacteristicCtrlPoint, - HidSvcGattCharacteristicLed, - HidSvcGattCharacteristicCount, -} HidSvcGattCharacteristicId; - -typedef struct { - uint8_t report_idx; - uint8_t report_type; -} HidSvcReportId; - -static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); - -static bool - hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { - const HidSvcReportId* report_id = context; - *data_len = sizeof(HidSvcReportId); - if(data) { - *data = (const uint8_t*)report_id; - } - return false; -} - -typedef struct { - const void* data_ptr; - uint16_t data_len; -} HidSvcDataWrapper; - -static bool - hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { - const HidSvcDataWrapper* report_data = context; - if(data) { - *data = report_data->data_ptr; - *data_len = report_data->data_len; - } else { - *data_len = HID_SVC_REPORT_MAP_MAX_LEN; - } - return false; -} - -// LED Descriptor params for BadBT - -static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; - -static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { - .uuid_type = UUID_TYPE_16, - .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, - .max_length = HID_SVC_REPORT_REF_LEN, - .data_callback.fn = hid_svc_char_desc_data_callback, - .data_callback.context = led_desc_context_buf, - .security_permissions = ATTR_PERMISSION_NONE, - .access_permissions = ATTR_ACCESS_READ_WRITE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT, -}; - -static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { - [HidSvcGattCharacteristicProtocolMode] = - {.name = "Protocol Mode", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [HidSvcGattCharacteristicReportMap] = - {.name = "Report Map", - .data_prop_type = FlipperGattCharacteristicDataCallback, - .data.callback.fn = hid_svc_report_data_callback, - .data.callback.context = NULL, - .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_VARIABLE}, - [HidSvcGattCharacteristicInfo] = - {.name = "HID Information", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = HID_SVC_INFO_LEN, - .data.fixed.ptr = NULL, - .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [HidSvcGattCharacteristicCtrlPoint] = - {.name = "HID Control Point", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, - .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [HidSvcGattCharacteristicLed] = - { - .name = - "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = REPORT_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | - GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, - .is_variable = CHAR_VALUE_LEN_CONSTANT, - .descriptor_params = &hid_svc_char_descr_led, - }, -}; - -static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { - .uuid_type = UUID_TYPE_16, - .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, - .max_length = HID_SVC_REPORT_REF_LEN, - .data_callback.fn = hid_svc_char_desc_data_callback, - .security_permissions = ATTR_PERMISSION_NONE, - .access_permissions = ATTR_ACCESS_READ_WRITE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT, -}; - -static const FlipperGattCharacteristicParams hid_svc_report_template = { - .name = "Report", - .data_prop_type = FlipperGattCharacteristicDataCallback, - .data.callback.fn = hid_svc_report_data_callback, - .data.callback.context = NULL, - .uuid.Char_UUID_16 = REPORT_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_VARIABLE, -}; - -typedef struct { - uint16_t svc_handle; - FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; - FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; - FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; - FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; - // led state - HidLedStateEventCallback led_state_event_callback; - void* led_state_ctx; -} HIDSvc; - -static HIDSvc* hid_svc = NULL; - -static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { - SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; - hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); - evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; - // aci_gatt_attribute_modified_event_rp0* attribute_modified; - if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { - if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { - // Process modification events - ret = SVCCTL_EvtAckFlowEnable; - } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { - // Process notification confirmation - ret = SVCCTL_EvtAckFlowEnable; - } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { - // LED Characteristic and descriptor for BadBT to get numlock state for altchars - // - // Process write request - aci_gatt_write_permit_req_event_rp0* req = - (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; - - furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); - - // this check is likely to be incorrect, it will actually work in our case - // but we need to investigate gatt api to see what is the rules - // that specify attibute handle value from char handle (or the reverse) - if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { - hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); - aci_gatt_write_resp( - req->Connection_Handle, - req->Attribute_Handle, - 0x00, /* write_status = 0 (no error))*/ - 0x00, /* err_code */ - req->Data_Length, - req->Data); - aci_gatt_write_char_value( - req->Connection_Handle, - hid_svc->chars[HidSvcGattCharacteristicLed].handle, - req->Data_Length, - req->Data); - ret = SVCCTL_EvtAckFlowEnable; - } - } - } - return ret; -} - -void hid_svc_start() { - tBleStatus status; - hid_svc = malloc(sizeof(HIDSvc)); - Service_UUID_t svc_uuid = {}; - - // Register event handler - SVCCTL_RegisterSvcHandler(hid_svc_event_handler); - // Add service - svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; - /** - * Add Human Interface Device Service - */ - status = aci_gatt_add_service( - UUID_TYPE_16, - &svc_uuid, - PRIMARY_SERVICE, - 2 + /* protocol mode */ - (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + - (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + - 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ - &hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add HID service: %d", status); - } - - for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); - } - uint8_t protocol_mode = 1; - flipper_gatt_characteristic_update( - hid_svc->svc_handle, - &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], - &protocol_mode); - - // reports - FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; - FlipperGattCharacteristicParams report_char; - HidSvcReportId report_id; - - memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); - memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); - - hid_svc_char_descr.data_callback.context = &report_id; - report_char.descriptor_params = &hid_svc_char_descr; - - typedef struct { - uint8_t report_type; - uint8_t report_count; - FlipperGattCharacteristicInstance* chars; - } HidSvcReportCharProps; - - HidSvcReportCharProps hid_report_chars[] = { - {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, - {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, - {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, - }; - - for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); - report_type_idx++) { - report_id.report_type = hid_report_chars[report_type_idx].report_type; - for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; - report_idx++) { - report_id.report_idx = report_idx + 1; - flipper_gatt_characteristic_init( - hid_svc->svc_handle, - &report_char, - &hid_report_chars[report_type_idx].chars[report_idx]); - } - } -} - -bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - HidSvcDataWrapper report_data = { - .data_ptr = data, - .data_len = len, - }; - return flipper_gatt_characteristic_update( - hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); -} - -bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); - - HidSvcDataWrapper report_data = { - .data_ptr = data, - .data_len = len, - }; - return flipper_gatt_characteristic_update( - hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); -} - -bool hid_svc_update_info(uint8_t* data) { - furi_assert(data); - furi_assert(hid_svc); - - return flipper_gatt_characteristic_update( - hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); -} - -void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { - furi_assert(hid_svc); - furi_assert(callback); - furi_assert(context); - - hid_svc->led_state_event_callback = callback; - hid_svc->led_state_ctx = context; -} - -bool hid_svc_is_started() { - return hid_svc != NULL; -} - -void hid_svc_stop() { - tBleStatus status; - if(hid_svc) { - // Delete characteristics - for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); - } - - typedef struct { - uint8_t report_count; - FlipperGattCharacteristicInstance* chars; - } HidSvcReportCharProps; - - HidSvcReportCharProps hid_report_chars[] = { - {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, - {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, - {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, - }; - - for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); - report_type_idx++) { - for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; - report_idx++) { - flipper_gatt_characteristic_delete( - hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); - } - } - - // Delete service - status = aci_gatt_del_service(hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); - } - free(hid_svc); - hid_svc = NULL; - } -} diff --git a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc deleted file mode 100644 index a297d9ad6..000000000 --- a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc +++ /dev/null @@ -1,12 +0,0 @@ - -static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ - { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; - -#define SERIAL_SVC_TX_CHAR_UUID \ - { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } -#define SERIAL_SVC_RX_CHAR_UUID \ - { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } -#define SERIAL_SVC_FLOW_CONTROL_UUID \ - { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } -#define SERIAL_SVC_RPC_STATUS_UUID \ - { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 22d286abd..f71cd3776 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -8,7 +8,8 @@ #include #include #include -#include +#include "battery_service.h" + #include #define TAG "FuriHalBt" diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 2bbfc1523..8e05a9904 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -1,11 +1,11 @@ #include #include -#include -#include -#include +#include "usb_hid.h" +#include "dev_info_service.h" +#include "battery_service.h" +#include "hid_service.h" #include -#include #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) @@ -221,7 +221,7 @@ void furi_hal_bt_hid_start() { FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, }; - hid_svc_update_info(hid_info_val); + hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); } void furi_hal_bt_hid_stop() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c index 2927d946f..2539e6bd0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -1,7 +1,7 @@ #include -#include -#include -#include +#include "dev_info_service.h" +#include "battery_service.h" +#include "serial_service.h" #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index d6a6e8c42..bfe4a67c3 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h index 0472d31d1..1b6e79ab0 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h @@ -1,6 +1,6 @@ #pragma once -#include +#include "serial_service.h" #ifdef __cplusplus extern "C" { diff --git a/revert_gatt_char_refactor.patch b/revert_gatt_char_refactor.patch deleted file mode 100644 index 161830135..000000000 --- a/revert_gatt_char_refactor.patch +++ /dev/null @@ -1,2181 +0,0 @@ -diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c -index 882fd11e85..b8cb09734d 100644 ---- a/applications/services/bt/bt_service/bt.c -+++ b/applications/services/bt/bt_service/bt.c -@@ -1,7 +1,7 @@ - #include "bt_i.h" -+#include "battery_service.h" - #include "bt_keys_storage.h" - --#include - #include - #include - #include -diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c -index 35f53ae22c..78e789ac30 100644 ---- a/firmware/targets/f7/ble_glue/app_debug.c -+++ b/firmware/targets/f7/ble_glue/app_debug.c -@@ -195,14 +195,14 @@ static void APPD_SetCPU2GpioConfig(void) { - gpio_config.Pin = gpiob_pin_list; - LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); - LL_GPIO_Init(GPIOB, &gpio_config); -- LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); -+ LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list); - } - - if(gpioc_pin_list != 0) { - gpio_config.Pin = gpioc_pin_list; - LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); - LL_GPIO_Init(GPIOC, &gpio_config); -- LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); -+ LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); - } - } - -diff --git a/firmware/targets/f7/ble_glue/services/battery_service.c b/firmware/targets/f7/ble_glue/battery_service.c -similarity index 53% -rename from firmware/targets/f7/ble_glue/services/battery_service.c -rename to firmware/targets/f7/ble_glue/battery_service.c -index 63f736b3b7..8c371efadb 100644 ---- a/firmware/targets/f7/ble_glue/services/battery_service.c -+++ b/firmware/targets/f7/ble_glue/battery_service.c -@@ -1,7 +1,5 @@ - #include "battery_service.h" - #include "app_common.h" --#include "gatt_char.h" -- - #include - - #include -@@ -9,6 +7,12 @@ - - #define TAG "BtBatterySvc" - -+typedef struct { -+ uint16_t svc_handle; -+ uint16_t battery_level_char_handle; -+ uint16_t power_state_char_handle; -+} BatterySvc; -+ - enum { - // Common states - BatterySvcPowerStateUnknown = 0b00, -@@ -36,44 +40,13 @@ typedef struct { - - _Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); - -+static BatterySvc* battery_svc = NULL; -+ - #define BATTERY_POWER_STATE (0x2A1A) - - static const uint16_t service_uuid = BATTERY_SERVICE_UUID; -- --typedef enum { -- BatterySvcGattCharacteristicBatteryLevel = 0, -- BatterySvcGattCharacteristicPowerState, -- BatterySvcGattCharacteristicCount, --} BatterySvcGattCharacteristicId; -- --static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = -- {[BatterySvcGattCharacteristicBatteryLevel] = -- {.name = "Battery Level", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = 1, -- .uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [BatterySvcGattCharacteristicPowerState] = { -- .name = "Power State", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = 1, -- .uuid.Char_UUID_16 = BATTERY_POWER_STATE, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}}; -- --typedef struct { -- uint16_t svc_handle; -- FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount]; --} BatterySvc; -- --static BatterySvc* battery_svc = NULL; -+static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; -+static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; - - void battery_svc_start() { - battery_svc = malloc(sizeof(BatterySvc)); -@@ -85,19 +58,53 @@ void battery_svc_start() { - if(status) { - FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); - } -- for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { -- flipper_gatt_characteristic_init( -- battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); -+ // Add Battery level characteristic -+ status = aci_gatt_add_char( -+ battery_svc->svc_handle, -+ UUID_TYPE_16, -+ (Char_UUID_t*)&battery_level_char_uuid, -+ 1, -+ CHAR_PROP_READ | CHAR_PROP_NOTIFY, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &battery_svc->battery_level_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); - } -- -+ // Add Power state characteristic -+ status = aci_gatt_add_char( -+ battery_svc->svc_handle, -+ UUID_TYPE_16, -+ (Char_UUID_t*)&power_state_char_uuid, -+ 1, -+ CHAR_PROP_READ | CHAR_PROP_NOTIFY, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &battery_svc->power_state_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); -+ } -+ // Update power state charachteristic - battery_svc_update_power_state(); - } - - void battery_svc_stop() { - tBleStatus status; - if(battery_svc) { -- for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { -- flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); -+ // Delete Battery level characteristic -+ status = -+ aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); -+ } -+ // Delete Power state characteristic -+ status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); - } - // Delete Battery service - status = aci_gatt_del_service(battery_svc->svc_handle); -@@ -119,10 +126,13 @@ bool battery_svc_update_level(uint8_t battery_charge) { - return false; - } - // Update battery level characteristic -- return flipper_gatt_characteristic_update( -- battery_svc->svc_handle, -- &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], -- &battery_charge); -+ FURI_LOG_D(TAG, "Updating battery level characteristic"); -+ tBleStatus result = aci_gatt_update_char_value( -+ battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); -+ if(result) { -+ FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); -+ } -+ return result != BLE_STATUS_SUCCESS; - } - - bool battery_svc_update_power_state() { -@@ -142,9 +152,15 @@ bool battery_svc_update_power_state() { - power_state.charging = BatterySvcPowerStateNotCharging; - power_state.discharging = BatterySvcPowerStateDischarging; - } -- -- return flipper_gatt_characteristic_update( -+ FURI_LOG_D(TAG, "Updating power state characteristic"); -+ tBleStatus result = aci_gatt_update_char_value( - battery_svc->svc_handle, -- &battery_svc->chars[BatterySvcGattCharacteristicPowerState], -- &power_state); -+ battery_svc->power_state_char_handle, -+ 0, -+ 1, -+ (uint8_t*)&power_state); -+ if(result) { -+ FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); -+ } -+ return result != BLE_STATUS_SUCCESS; - } -diff --git a/firmware/targets/f7/ble_glue/services/battery_service.h b/firmware/targets/f7/ble_glue/battery_service.h -similarity index 100% -rename from firmware/targets/f7/ble_glue/services/battery_service.h -rename to firmware/targets/f7/ble_glue/battery_service.h -diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c -index 7a2148b6b8..4fc4d521be 100644 ---- a/firmware/targets/f7/ble_glue/ble_app.c -+++ b/firmware/targets/f7/ble_glue/ble_app.c -@@ -33,45 +33,6 @@ static int32_t ble_app_hci_thread(void* context); - static void ble_app_hci_event_handler(void* pPayload); - static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); - --static const HCI_TL_HciInitConf_t hci_tl_config = { -- .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, -- .StatusNotCallBack = ble_app_hci_status_not_handler, --}; -- --static const SHCI_C2_CONFIG_Cmd_Param_t config_param = { -- .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, -- .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, -- .BleNvmRamAddress = (uint32_t)ble_app_nvm, -- .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, --}; -- --static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { -- .Header = {{0, 0, 0}}, // Header unused -- .Param = { -- .pBleBufferAddress = 0, // pBleBufferAddress not used -- .BleBufferSize = 0, // BleBufferSize not used -- .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, -- .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, -- .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, -- .NumOfLinks = CFG_BLE_NUM_LINK, -- .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, -- .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, -- .MblockCount = CFG_BLE_MBLOCK_COUNT, -- .AttMtu = CFG_BLE_MAX_ATT_MTU, -- .SlaveSca = CFG_BLE_SLAVE_SCA, -- .MasterSca = CFG_BLE_MASTER_SCA, -- .LsSource = CFG_BLE_LSE_SOURCE, -- .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, -- .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, -- .ViterbiEnable = CFG_BLE_VITERBI_MODE, -- .Options = CFG_BLE_OPTIONS, -- .HwVersion = 0, -- .max_coc_initiator_nbr = 32, -- .min_tx_power = 0, -- .max_tx_power = 0, -- .rx_model_config = 1, -- }}; -- - bool ble_app_init() { - SHCI_CmdStatus_t status; - ble_app = malloc(sizeof(BleApp)); -@@ -83,16 +44,52 @@ bool ble_app_init() { - furi_thread_start(ble_app->thread); - - // Initialize Ble Transport Layer -+ HCI_TL_HciInitConf_t hci_tl_config = { -+ .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, -+ .StatusNotCallBack = ble_app_hci_status_not_handler, -+ }; - hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); - - // Configure NVM store for pairing data -- status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); -+ SHCI_C2_CONFIG_Cmd_Param_t config_param = { -+ .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, -+ .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, -+ .BleNvmRamAddress = (uint32_t)ble_app_nvm, -+ .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, -+ }; -+ status = SHCI_C2_Config(&config_param); - if(status) { - FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); - } - - // Start ble stack on 2nd core -- status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); -+ SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { -+ .Header = {{0, 0, 0}}, // Header unused -+ .Param = { -+ .pBleBufferAddress = 0, // pBleBufferAddress not used -+ .BleBufferSize = 0, // BleBufferSize not used -+ .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, -+ .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, -+ .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, -+ .NumOfLinks = CFG_BLE_NUM_LINK, -+ .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, -+ .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, -+ .MblockCount = CFG_BLE_MBLOCK_COUNT, -+ .AttMtu = CFG_BLE_MAX_ATT_MTU, -+ .SlaveSca = CFG_BLE_SLAVE_SCA, -+ .MasterSca = CFG_BLE_MASTER_SCA, -+ .LsSource = CFG_BLE_LSE_SOURCE, -+ .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, -+ .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, -+ .ViterbiEnable = CFG_BLE_VITERBI_MODE, -+ .Options = CFG_BLE_OPTIONS, -+ .HwVersion = 0, -+ .max_coc_initiator_nbr = 32, -+ .min_tx_power = 0, -+ .max_tx_power = 0, -+ .rx_model_config = 1, -+ }}; -+ status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); - if(status) { - FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); - } -diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c -new file mode 100644 -index 0000000000..d24058632f ---- /dev/null -+++ b/firmware/targets/f7/ble_glue/dev_info_service.c -@@ -0,0 +1,220 @@ -+#include "dev_info_service.h" -+#include "app_common.h" -+#include -+ -+#include -+#include -+#include -+ -+#define TAG "BtDevInfoSvc" -+ -+typedef struct { -+ uint16_t service_handle; -+ uint16_t man_name_char_handle; -+ uint16_t serial_num_char_handle; -+ uint16_t firmware_rev_char_handle; -+ uint16_t software_rev_char_handle; -+ uint16_t rpc_version_char_handle; -+ FuriString* version_string; -+ char hardware_revision[4]; -+} DevInfoSvc; -+ -+static DevInfoSvc* dev_info_svc = NULL; -+ -+static const char dev_info_man_name[] = "Flipper Devices Inc."; -+static const char dev_info_serial_num[] = "1.0"; -+static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); -+ -+static const uint8_t dev_info_rpc_version_uuid[] = -+ {0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03}; -+ -+void dev_info_svc_start() { -+ dev_info_svc = malloc(sizeof(DevInfoSvc)); -+ dev_info_svc->version_string = furi_string_alloc_printf( -+ "%s %s %s %s", -+ version_get_githash(NULL), -+ version_get_version(NULL), -+ version_get_gitbranchnum(NULL), -+ version_get_builddate(NULL)); -+ snprintf( -+ dev_info_svc->hardware_revision, -+ sizeof(dev_info_svc->hardware_revision), -+ "%d", -+ version_get_target(NULL)); -+ tBleStatus status; -+ -+ // Add Device Information Service -+ uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; -+ status = aci_gatt_add_service( -+ UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 11, &dev_info_svc->service_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); -+ } -+ -+ // Add characteristics -+ uuid = MANUFACTURER_NAME_UUID; -+ status = aci_gatt_add_char( -+ dev_info_svc->service_handle, -+ UUID_TYPE_16, -+ (Char_UUID_t*)&uuid, -+ strlen(dev_info_man_name), -+ CHAR_PROP_READ, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &dev_info_svc->man_name_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status); -+ } -+ uuid = SERIAL_NUMBER_UUID; -+ status = aci_gatt_add_char( -+ dev_info_svc->service_handle, -+ UUID_TYPE_16, -+ (Char_UUID_t*)&uuid, -+ strlen(dev_info_serial_num), -+ CHAR_PROP_READ, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &dev_info_svc->serial_num_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add serial number char: %d", status); -+ } -+ uuid = FIRMWARE_REVISION_UUID; -+ status = aci_gatt_add_char( -+ dev_info_svc->service_handle, -+ UUID_TYPE_16, -+ (Char_UUID_t*)&uuid, -+ strlen(dev_info_svc->hardware_revision), -+ CHAR_PROP_READ, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &dev_info_svc->firmware_rev_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status); -+ } -+ uuid = SOFTWARE_REVISION_UUID; -+ status = aci_gatt_add_char( -+ dev_info_svc->service_handle, -+ UUID_TYPE_16, -+ (Char_UUID_t*)&uuid, -+ furi_string_size(dev_info_svc->version_string), -+ CHAR_PROP_READ, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &dev_info_svc->software_rev_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add software revision char: %d", status); -+ } -+ status = aci_gatt_add_char( -+ dev_info_svc->service_handle, -+ UUID_TYPE_128, -+ (const Char_UUID_t*)dev_info_rpc_version_uuid, -+ strlen(dev_info_rpc_version), -+ CHAR_PROP_READ, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &dev_info_svc->rpc_version_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status); -+ } -+ -+ // Update characteristics -+ status = aci_gatt_update_char_value( -+ dev_info_svc->service_handle, -+ dev_info_svc->man_name_char_handle, -+ 0, -+ strlen(dev_info_man_name), -+ (uint8_t*)dev_info_man_name); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status); -+ } -+ status = aci_gatt_update_char_value( -+ dev_info_svc->service_handle, -+ dev_info_svc->serial_num_char_handle, -+ 0, -+ strlen(dev_info_serial_num), -+ (uint8_t*)dev_info_serial_num); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to update serial number char: %d", status); -+ } -+ status = aci_gatt_update_char_value( -+ dev_info_svc->service_handle, -+ dev_info_svc->firmware_rev_char_handle, -+ 0, -+ strlen(dev_info_svc->hardware_revision), -+ (uint8_t*)dev_info_svc->hardware_revision); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status); -+ } -+ status = aci_gatt_update_char_value( -+ dev_info_svc->service_handle, -+ dev_info_svc->software_rev_char_handle, -+ 0, -+ furi_string_size(dev_info_svc->version_string), -+ (uint8_t*)furi_string_get_cstr(dev_info_svc->version_string)); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to update software revision char: %d", status); -+ } -+ status = aci_gatt_update_char_value( -+ dev_info_svc->service_handle, -+ dev_info_svc->rpc_version_char_handle, -+ 0, -+ strlen(dev_info_rpc_version), -+ (uint8_t*)dev_info_rpc_version); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status); -+ } -+} -+ -+void dev_info_svc_stop() { -+ tBleStatus status; -+ if(dev_info_svc) { -+ furi_string_free(dev_info_svc->version_string); -+ // Delete service characteristics -+ status = -+ aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status); -+ } -+ status = -+ aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status); -+ } -+ status = aci_gatt_del_char( -+ dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status); -+ } -+ status = aci_gatt_del_char( -+ dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status); -+ } -+ status = -+ aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status); -+ } -+ // Delete service -+ status = aci_gatt_del_service(dev_info_svc->service_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); -+ } -+ free(dev_info_svc); -+ dev_info_svc = NULL; -+ } -+} -+ -+bool dev_info_svc_is_started() { -+ return dev_info_svc != NULL; -+} -diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.h b/firmware/targets/f7/ble_glue/dev_info_service.h -similarity index 100% -rename from firmware/targets/f7/ble_glue/services/dev_info_service.h -rename to firmware/targets/f7/ble_glue/dev_info_service.h -diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c -new file mode 100644 -index 0000000000..a31d6015f5 ---- /dev/null -+++ b/firmware/targets/f7/ble_glue/hid_service.c -@@ -0,0 +1,416 @@ -+#include "hid_service.h" -+#include "app_common.h" -+#include -+ -+#include -+ -+#define TAG "BtHid" -+ -+typedef struct { -+ uint16_t svc_handle; -+ uint16_t protocol_mode_char_handle; -+ uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; -+ uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; -+ uint16_t report_map_char_handle; -+ uint16_t info_char_handle; -+ uint16_t ctrl_point_char_handle; -+ // led state -+ uint16_t led_state_char_handle; -+ uint16_t led_state_desc_handle; -+ HidLedStateEventCallback led_state_event_callback; -+ void* led_state_ctx; -+} HIDSvc; -+ -+static HIDSvc* hid_svc = NULL; -+ -+static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { -+ SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; -+ hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); -+ evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; -+ // aci_gatt_attribute_modified_event_rp0* attribute_modified; -+ if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { -+ if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { -+ // Process modification events -+ ret = SVCCTL_EvtAckFlowEnable; -+ } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { -+ // Process notification confirmation -+ ret = SVCCTL_EvtAckFlowEnable; -+ } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { -+ // Process write request -+ aci_gatt_write_permit_req_event_rp0* req = -+ (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; -+ -+ furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); -+ -+ // this check is likely to be incorrect, it will actually work in our case -+ // but we need to investigate gatt api to see what is the rules -+ // that specify attibute handle value from char handle (or the reverse) -+ if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) { -+ hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); -+ aci_gatt_write_resp( -+ req->Connection_Handle, -+ req->Attribute_Handle, -+ 0x00, /* write_status = 0 (no error))*/ -+ 0x00, /* err_code */ -+ req->Data_Length, -+ req->Data); -+ aci_gatt_write_char_value( -+ req->Connection_Handle, -+ hid_svc->led_state_char_handle, -+ req->Data_Length, -+ req->Data); -+ ret = SVCCTL_EvtAckFlowEnable; -+ } -+ } -+ } -+ return ret; -+} -+ -+void hid_svc_start() { -+ tBleStatus status; -+ hid_svc = malloc(sizeof(HIDSvc)); -+ Service_UUID_t svc_uuid = {}; -+ Char_Desc_Uuid_t desc_uuid = {}; -+ Char_UUID_t char_uuid = {}; -+ -+ // Register event handler -+ SVCCTL_RegisterSvcHandler(hid_svc_event_handler); -+ // Add service -+ svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; -+ /** -+ * Add Human Interface Device Service -+ */ -+ status = aci_gatt_add_service( -+ UUID_TYPE_16, -+ &svc_uuid, -+ PRIMARY_SERVICE, -+ 2 + /* protocol mode */ -+ (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + -+ (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + -+ 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ -+ &hid_svc->svc_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add HID service: %d", status); -+ } -+ // Add Protocol mode characteristics -+ char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; -+ status = aci_gatt_add_char( -+ hid_svc->svc_handle, -+ UUID_TYPE_16, -+ &char_uuid, -+ 1, -+ CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, -+ ATTR_PERMISSION_NONE, -+ GATT_NOTIFY_ATTRIBUTE_WRITE, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &hid_svc->protocol_mode_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status); -+ } -+ // Update Protocol mode characteristic -+ uint8_t protocol_mode = 1; -+ status = aci_gatt_update_char_value( -+ hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); -+ } -+ -+#if(HID_SVC_REPORT_COUNT != 0) -+ for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { -+ if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547 -+ uint8_t buf[2] = {i + 1, 1}; // 1 input -+ char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; -+ status = aci_gatt_add_char( -+ hid_svc->svc_handle, -+ UUID_TYPE_16, -+ &char_uuid, -+ HID_SVC_REPORT_MAX_LEN, -+ CHAR_PROP_READ | CHAR_PROP_NOTIFY, -+ ATTR_PERMISSION_NONE, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_VARIABLE, -+ &(hid_svc->report_char_handle[i])); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); -+ } -+ -+ desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; -+ status = aci_gatt_add_char_desc( -+ hid_svc->svc_handle, -+ hid_svc->report_char_handle[i], -+ UUID_TYPE_16, -+ &desc_uuid, -+ HID_SVC_REPORT_REF_LEN, -+ HID_SVC_REPORT_REF_LEN, -+ buf, -+ ATTR_PERMISSION_NONE, -+ ATTR_ACCESS_READ_WRITE, -+ GATT_DONT_NOTIFY_EVENTS, -+ MIN_ENCRY_KEY_SIZE, -+ CHAR_VALUE_LEN_CONSTANT, -+ &(hid_svc->report_ref_desc_handle[i])); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); -+ } -+ } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { -+ uint8_t buf[2] = {i + 1, 2}; // 2 output -+ char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; -+ status = aci_gatt_add_char( -+ hid_svc->svc_handle, -+ UUID_TYPE_16, -+ &char_uuid, -+ HID_SVC_REPORT_MAX_LEN, -+ CHAR_PROP_READ | CHAR_PROP_NOTIFY, -+ ATTR_PERMISSION_NONE, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_VARIABLE, -+ &(hid_svc->report_char_handle[i])); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); -+ } -+ -+ desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; -+ status = aci_gatt_add_char_desc( -+ hid_svc->svc_handle, -+ hid_svc->report_char_handle[i], -+ UUID_TYPE_16, -+ &desc_uuid, -+ HID_SVC_REPORT_REF_LEN, -+ HID_SVC_REPORT_REF_LEN, -+ buf, -+ ATTR_PERMISSION_NONE, -+ ATTR_ACCESS_READ_WRITE, -+ GATT_DONT_NOTIFY_EVENTS, -+ MIN_ENCRY_KEY_SIZE, -+ CHAR_VALUE_LEN_CONSTANT, -+ &(hid_svc->report_ref_desc_handle[i])); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); -+ } -+ } else { -+ uint8_t buf[2] = {i + 1, 3}; // 3 feature -+ char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; -+ status = aci_gatt_add_char( -+ hid_svc->svc_handle, -+ UUID_TYPE_16, -+ &char_uuid, -+ HID_SVC_REPORT_MAX_LEN, -+ CHAR_PROP_READ | CHAR_PROP_NOTIFY, -+ ATTR_PERMISSION_NONE, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_VARIABLE, -+ &(hid_svc->report_char_handle[i])); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); -+ } -+ -+ desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; -+ status = aci_gatt_add_char_desc( -+ hid_svc->svc_handle, -+ hid_svc->report_char_handle[i], -+ UUID_TYPE_16, -+ &desc_uuid, -+ HID_SVC_REPORT_REF_LEN, -+ HID_SVC_REPORT_REF_LEN, -+ buf, -+ ATTR_PERMISSION_NONE, -+ ATTR_ACCESS_READ_WRITE, -+ GATT_DONT_NOTIFY_EVENTS, -+ MIN_ENCRY_KEY_SIZE, -+ CHAR_VALUE_LEN_CONSTANT, -+ &(hid_svc->report_ref_desc_handle[i])); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); -+ } -+ } -+ } -+#endif -+ // Add led state output report -+ char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; -+ status = aci_gatt_add_char( -+ hid_svc->svc_handle, -+ UUID_TYPE_16, -+ &char_uuid, -+ 1, -+ CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, -+ ATTR_PERMISSION_NONE, -+ GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &(hid_svc->led_state_char_handle)); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status); -+ } -+ -+ // Add led state char descriptor specifying it is an output report -+ uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; -+ desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; -+ status = aci_gatt_add_char_desc( -+ hid_svc->svc_handle, -+ hid_svc->led_state_char_handle, -+ UUID_TYPE_16, -+ &desc_uuid, -+ HID_SVC_REPORT_REF_LEN, -+ HID_SVC_REPORT_REF_LEN, -+ buf, -+ ATTR_PERMISSION_NONE, -+ ATTR_ACCESS_READ_WRITE, -+ GATT_DONT_NOTIFY_EVENTS, -+ MIN_ENCRY_KEY_SIZE, -+ CHAR_VALUE_LEN_CONSTANT, -+ &(hid_svc->led_state_desc_handle)); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status); -+ } -+ // Add Report Map characteristic -+ char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; -+ status = aci_gatt_add_char( -+ hid_svc->svc_handle, -+ UUID_TYPE_16, -+ &char_uuid, -+ HID_SVC_REPORT_MAP_MAX_LEN, -+ CHAR_PROP_READ, -+ ATTR_PERMISSION_NONE, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_VARIABLE, -+ &hid_svc->report_map_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); -+ } -+ -+ // Add Information characteristic -+ char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; -+ status = aci_gatt_add_char( -+ hid_svc->svc_handle, -+ UUID_TYPE_16, -+ &char_uuid, -+ HID_SVC_INFO_LEN, -+ CHAR_PROP_READ, -+ ATTR_PERMISSION_NONE, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &hid_svc->info_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status); -+ } -+ // Add Control Point characteristic -+ char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID; -+ status = aci_gatt_add_char( -+ hid_svc->svc_handle, -+ UUID_TYPE_16, -+ &char_uuid, -+ HID_SVC_CONTROL_POINT_LEN, -+ CHAR_PROP_WRITE_WITHOUT_RESP, -+ ATTR_PERMISSION_NONE, -+ GATT_NOTIFY_ATTRIBUTE_WRITE, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &hid_svc->ctrl_point_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); -+ } -+ -+ hid_svc->led_state_event_callback = NULL; -+ hid_svc->led_state_ctx = NULL; -+} -+ -+bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { -+ furi_assert(data); -+ furi_assert(hid_svc); -+ -+ tBleStatus status = aci_gatt_update_char_value( -+ hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); -+ return false; -+ } -+ return true; -+} -+ -+bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { -+ furi_assert(data); -+ furi_assert(hid_svc); -+ -+ tBleStatus status = aci_gatt_update_char_value( -+ hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); -+ return false; -+ } -+ return true; -+} -+ -+bool hid_svc_update_info(uint8_t* data, uint16_t len) { -+ furi_assert(data); -+ furi_assert(hid_svc); -+ -+ tBleStatus status = -+ aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); -+ return false; -+ } -+ return true; -+} -+ -+void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { -+ furi_assert(hid_svc); -+ furi_assert(callback); -+ furi_assert(context); -+ -+ hid_svc->led_state_event_callback = callback; -+ hid_svc->led_state_ctx = context; -+} -+ -+bool hid_svc_is_started() { -+ return hid_svc != NULL; -+} -+ -+void hid_svc_stop() { -+ tBleStatus status; -+ if(hid_svc) { -+ // Delete characteristics -+ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); -+ } -+#if(HID_SVC_INPUT_REPORT_COUNT != 0) -+ for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { -+ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); -+ } -+ } -+#endif -+ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); -+ } -+ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); -+ } -+ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); -+ } -+ status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status); -+ } -+ // Delete service -+ status = aci_gatt_del_service(hid_svc->svc_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); -+ } -+ // Delete buffer size mutex -+ free(hid_svc); -+ hid_svc = NULL; -+ } -+} -diff --git a/firmware/targets/f7/ble_glue/services/hid_service.h b/firmware/targets/f7/ble_glue/hid_service.h -similarity index 89% -rename from firmware/targets/f7/ble_glue/services/hid_service.h -rename to firmware/targets/f7/ble_glue/hid_service.h -index 4d0ed4c4f9..b8f6b244d2 100644 ---- a/firmware/targets/f7/ble_glue/services/hid_service.h -+++ b/firmware/targets/f7/ble_glue/hid_service.h -@@ -27,7 +27,6 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); - - bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); - --// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) --bool hid_svc_update_info(uint8_t* data); -+bool hid_svc_update_info(uint8_t* data, uint16_t len); - - void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); -diff --git a/firmware/targets/f7/ble_glue/services/serial_service.c b/firmware/targets/f7/ble_glue/serial_service.c -similarity index 57% -rename from firmware/targets/f7/ble_glue/services/serial_service.c -rename to firmware/targets/f7/ble_glue/serial_service.c -index ab009bbfcb..c6421dc28f 100644 ---- a/firmware/targets/f7/ble_glue/services/serial_service.c -+++ b/firmware/targets/f7/ble_glue/serial_service.c -@@ -1,67 +1,17 @@ - #include "serial_service.h" - #include "app_common.h" - #include --#include "gatt_char.h" - - #include - --#include "serial_service_uuid.inc" -- - #define TAG "BtSerialSvc" - --typedef enum { -- SerialSvcGattCharacteristicTx = 0, -- SerialSvcGattCharacteristicRx, -- SerialSvcGattCharacteristicFlowCtrl, -- SerialSvcGattCharacteristicStatus, -- SerialSvcGattCharacteristicCount, --} SerialSvcGattCharacteristicId; -- --static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { -- [SerialSvcGattCharacteristicTx] = -- {.name = "TX", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, -- .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, -- .uuid_type = UUID_TYPE_128, -- .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_VARIABLE}, -- [SerialSvcGattCharacteristicRx] = -- {.name = "RX", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, -- .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, -- .uuid_type = UUID_TYPE_128, -- .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, -- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, -- .is_variable = CHAR_VALUE_LEN_VARIABLE}, -- [SerialSvcGattCharacteristicFlowCtrl] = -- {.name = "Flow control", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = sizeof(uint32_t), -- .uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID, -- .uuid_type = UUID_TYPE_128, -- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [SerialSvcGattCharacteristicStatus] = { -- .name = "RPC status", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = sizeof(SerialServiceRpcStatus), -- .uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID, -- .uuid_type = UUID_TYPE_128, -- .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, -- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}}; -- - typedef struct { - uint16_t svc_handle; -- FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; -+ uint16_t rx_char_handle; -+ uint16_t tx_char_handle; -+ uint16_t flow_ctrl_char_handle; -+ uint16_t rpc_status_char_handle; - FuriMutex* buff_size_mtx; - uint32_t buff_size; - uint16_t bytes_ready_to_receive; -@@ -71,6 +21,17 @@ typedef struct { - - static SerialSvc* serial_svc = NULL; - -+static const uint8_t service_uuid[] = -+ {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; -+static const uint8_t char_tx_uuid[] = -+ {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -+static const uint8_t char_rx_uuid[] = -+ {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -+static const uint8_t flow_ctrl_uuid[] = -+ {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -+static const uint8_t rpc_status_uuid[] = -+ {0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; -+ - static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { - SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; - hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); -@@ -79,14 +40,11 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { - if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { - if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { - attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; -- if(attribute_modified->Attr_Handle == -- serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) { -+ if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { - // Descriptor handle - ret = SVCCTL_EvtAckFlowEnable; - FURI_LOG_D(TAG, "RX descriptor event"); -- } else if( -- attribute_modified->Attr_Handle == -- serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { -+ } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { - FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); - if(serial_svc->callback) { - furi_check( -@@ -112,9 +70,7 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { - furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); - } - ret = SVCCTL_EvtAckFlowEnable; -- } else if( -- attribute_modified->Attr_Handle == -- serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { -+ } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { - SerialServiceRpcStatus* rpc_status = - (SerialServiceRpcStatus*)attribute_modified->Attr_Data; - if(*rpc_status == SerialServiceRpcStatusNotActive) { -@@ -141,12 +97,18 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { - } - - static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { -- flipper_gatt_characteristic_update( -- serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); -+ tBleStatus ble_status = aci_gatt_update_char_value( -+ serial_svc->svc_handle, -+ serial_svc->rpc_status_char_handle, -+ 0, -+ sizeof(SerialServiceRpcStatus), -+ (uint8_t*)&status); -+ if(ble_status) { -+ FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status); -+ } - } - - void serial_svc_start() { -- UNUSED(serial_svc_chars); - tBleStatus status; - serial_svc = malloc(sizeof(SerialSvc)); - // Register event handler -@@ -154,17 +116,72 @@ void serial_svc_start() { - - // Add service - status = aci_gatt_add_service( -- UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); -+ UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); - } - -- // Add characteristics -- for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { -- flipper_gatt_characteristic_init( -- serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); -+ // Add RX characteristics -+ status = aci_gatt_add_char( -+ serial_svc->svc_handle, -+ UUID_TYPE_128, -+ (const Char_UUID_t*)char_rx_uuid, -+ SERIAL_SVC_DATA_LEN_MAX, -+ CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, -+ ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, -+ GATT_NOTIFY_ATTRIBUTE_WRITE, -+ 10, -+ CHAR_VALUE_LEN_VARIABLE, -+ &serial_svc->rx_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status); - } - -+ // Add TX characteristic -+ status = aci_gatt_add_char( -+ serial_svc->svc_handle, -+ UUID_TYPE_128, -+ (const Char_UUID_t*)char_tx_uuid, -+ SERIAL_SVC_DATA_LEN_MAX, -+ CHAR_PROP_READ | CHAR_PROP_INDICATE, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_VARIABLE, -+ &serial_svc->tx_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status); -+ } -+ // Add Flow Control characteristic -+ status = aci_gatt_add_char( -+ serial_svc->svc_handle, -+ UUID_TYPE_128, -+ (const Char_UUID_t*)flow_ctrl_uuid, -+ sizeof(uint32_t), -+ CHAR_PROP_READ | CHAR_PROP_NOTIFY, -+ ATTR_PERMISSION_AUTHEN_READ, -+ GATT_DONT_NOTIFY_EVENTS, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &serial_svc->flow_ctrl_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); -+ } -+ // Add RPC status characteristic -+ status = aci_gatt_add_char( -+ serial_svc->svc_handle, -+ UUID_TYPE_128, -+ (const Char_UUID_t*)rpc_status_uuid, -+ sizeof(SerialServiceRpcStatus), -+ CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, -+ ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, -+ GATT_NOTIFY_ATTRIBUTE_WRITE, -+ 10, -+ CHAR_VALUE_LEN_CONSTANT, -+ &serial_svc->rpc_status_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status); -+ } - serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); - // Allocate buffer size mutex - serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); -@@ -179,12 +196,13 @@ void serial_svc_set_callbacks( - serial_svc->context = context; - serial_svc->buff_size = buff_size; - serial_svc->bytes_ready_to_receive = buff_size; -- - uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); -- flipper_gatt_characteristic_update( -+ aci_gatt_update_char_value( - serial_svc->svc_handle, -- &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], -- &buff_size_reversed); -+ serial_svc->flow_ctrl_char_handle, -+ 0, -+ sizeof(uint32_t), -+ (uint8_t*)&buff_size_reversed); - } - - void serial_svc_notify_buffer_is_empty() { -@@ -195,12 +213,13 @@ void serial_svc_notify_buffer_is_empty() { - if(serial_svc->bytes_ready_to_receive == 0) { - FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); - serial_svc->bytes_ready_to_receive = serial_svc->buff_size; -- - uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); -- flipper_gatt_characteristic_update( -+ aci_gatt_update_char_value( - serial_svc->svc_handle, -- &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], -- &buff_size_reversed); -+ serial_svc->flow_ctrl_char_handle, -+ 0, -+ sizeof(uint32_t), -+ (uint8_t*)&buff_size_reversed); - } - furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); - } -@@ -208,8 +227,22 @@ void serial_svc_notify_buffer_is_empty() { - void serial_svc_stop() { - tBleStatus status; - if(serial_svc) { -- for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { -- flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); -+ // Delete characteristics -+ status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status); -+ } -+ status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status); -+ } -+ status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); -+ } -+ status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle); -+ if(status) { -+ FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status); - } - // Delete service - status = aci_gatt_del_service(serial_svc->svc_handle); -@@ -240,7 +273,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { - tBleStatus result = aci_gatt_update_char_value_ext( - 0, - serial_svc->svc_handle, -- serial_svc->chars[SerialSvcGattCharacteristicTx].handle, -+ serial_svc->tx_char_handle, - remained ? 0x00 : 0x02, - data_len, - value_offset, -diff --git a/firmware/targets/f7/ble_glue/services/serial_service.h b/firmware/targets/f7/ble_glue/serial_service.h -similarity index 100% -rename from firmware/targets/f7/ble_glue/services/serial_service.h -rename to firmware/targets/f7/ble_glue/serial_service.h -diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.c b/firmware/targets/f7/ble_glue/services/dev_info_service.c -deleted file mode 100644 -index 5bee97b416..0000000000 ---- a/firmware/targets/f7/ble_glue/services/dev_info_service.c -+++ /dev/null -@@ -1,176 +0,0 @@ --#include "dev_info_service.h" --#include "app_common.h" --#include "gatt_char.h" --#include -- --#include --#include --#include -- --#include "dev_info_service_uuid.inc" -- --#define TAG "BtDevInfoSvc" -- --typedef enum { -- DevInfoSvcGattCharacteristicMfgName = 0, -- DevInfoSvcGattCharacteristicSerial, -- DevInfoSvcGattCharacteristicFirmwareRev, -- DevInfoSvcGattCharacteristicSoftwareRev, -- DevInfoSvcGattCharacteristicRpcVersion, -- DevInfoSvcGattCharacteristicCount, --} DevInfoSvcGattCharacteristicId; -- --#define DEVICE_INFO_HARDWARE_REV_SIZE 4 --typedef struct { -- uint16_t service_handle; -- FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount]; -- FuriString* version_string; -- char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE]; --} DevInfoSvc; -- --static DevInfoSvc* dev_info_svc = NULL; -- --static const char dev_info_man_name[] = "Flipper Devices Inc."; --static const char dev_info_serial_num[] = "1.0"; --static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); -- --static bool dev_info_char_firmware_rev_callback( -- const void* context, -- const uint8_t** data, -- uint16_t* data_len) { -- const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; -- *data_len = sizeof(dev_info_svc->hardware_revision); -- if(data) { -- *data = (const uint8_t*)&dev_info_svc->hardware_revision; -- } -- return false; --} -- --static bool dev_info_char_software_rev_callback( -- const void* context, -- const uint8_t** data, -- uint16_t* data_len) { -- const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; -- *data_len = furi_string_size(dev_info_svc->version_string); -- if(data) { -- *data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string); -- } -- return false; --} -- --static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] = -- {[DevInfoSvcGattCharacteristicMfgName] = -- {.name = "Manufacturer Name", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = sizeof(dev_info_man_name) - 1, -- .data.fixed.ptr = (const uint8_t*)&dev_info_man_name, -- .uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [DevInfoSvcGattCharacteristicSerial] = -- {.name = "Serial Number", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = sizeof(dev_info_serial_num) - 1, -- .data.fixed.ptr = (const uint8_t*)&dev_info_serial_num, -- .uuid.Char_UUID_16 = SERIAL_NUMBER_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [DevInfoSvcGattCharacteristicFirmwareRev] = -- {.name = "Firmware Revision", -- .data_prop_type = FlipperGattCharacteristicDataCallback, -- .data.callback.context = &dev_info_svc, -- .data.callback.fn = dev_info_char_firmware_rev_callback, -- .uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [DevInfoSvcGattCharacteristicSoftwareRev] = -- {.name = "Software Revision", -- .data_prop_type = FlipperGattCharacteristicDataCallback, -- .data.callback.context = &dev_info_svc, -- .data.callback.fn = dev_info_char_software_rev_callback, -- .uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [DevInfoSvcGattCharacteristicRpcVersion] = { -- .name = "RPC Version", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = sizeof(dev_info_rpc_version) - 1, -- .data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version, -- .uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID, -- .uuid_type = UUID_TYPE_128, -- .char_properties = CHAR_PROP_READ, -- .security_permissions = ATTR_PERMISSION_AUTHEN_READ, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}}; -- --void dev_info_svc_start() { -- dev_info_svc = malloc(sizeof(DevInfoSvc)); -- dev_info_svc->version_string = furi_string_alloc_printf( -- "%s %s %s %s", -- version_get_githash(NULL), -- version_get_version(NULL), -- version_get_gitbranchnum(NULL), -- version_get_builddate(NULL)); -- snprintf( -- dev_info_svc->hardware_revision, -- sizeof(dev_info_svc->hardware_revision), -- "%d", -- version_get_target(NULL)); -- tBleStatus status; -- -- // Add Device Information Service -- uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; -- status = aci_gatt_add_service( -- UUID_TYPE_16, -- (Service_UUID_t*)&uuid, -- PRIMARY_SERVICE, -- 1 + 2 * DevInfoSvcGattCharacteristicCount, -- &dev_info_svc->service_handle); -- if(status) { -- FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); -- } -- -- for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { -- flipper_gatt_characteristic_init( -- dev_info_svc->service_handle, -- &dev_info_svc_chars[i], -- &dev_info_svc->characteristics[i]); -- flipper_gatt_characteristic_update( -- dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL); -- } --} -- --void dev_info_svc_stop() { -- tBleStatus status; -- if(dev_info_svc) { -- furi_string_free(dev_info_svc->version_string); -- // Delete service characteristics -- for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { -- flipper_gatt_characteristic_delete( -- dev_info_svc->service_handle, &dev_info_svc->characteristics[i]); -- } -- // Delete service -- status = aci_gatt_del_service(dev_info_svc->service_handle); -- if(status) { -- FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); -- } -- free(dev_info_svc); -- dev_info_svc = NULL; -- } --} -- --bool dev_info_svc_is_started() { -- return dev_info_svc != NULL; --} -diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc -deleted file mode 100644 -index ad520f62e5..0000000000 ---- a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc -+++ /dev/null -@@ -1,3 +0,0 @@ --#define DEV_INVO_RPC_VERSION_UID \ -- { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } -- -diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c -deleted file mode 100644 -index 9b6a44f61b..0000000000 ---- a/firmware/targets/f7/ble_glue/services/gatt_char.c -+++ /dev/null -@@ -1,123 +0,0 @@ --#include "gatt_char.h" -- --#include -- --#define TAG "GattChar" -- --#define GATT_MIN_READ_KEY_SIZE (10) -- --void flipper_gatt_characteristic_init( -- uint16_t svc_handle, -- const FlipperGattCharacteristicParams* char_descriptor, -- FlipperGattCharacteristicInstance* char_instance) { -- furi_assert(char_descriptor); -- furi_assert(char_instance); -- -- // Copy the descriptor to the instance, since it may point to stack memory -- // TODO: only copy if really comes from stack -- char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); -- memcpy( -- (void*)char_instance->characteristic, -- char_descriptor, -- sizeof(FlipperGattCharacteristicParams)); -- -- uint16_t char_data_size = 0; -- if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { -- char_data_size = char_descriptor->data.fixed.length; -- } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { -- char_descriptor->data.callback.fn( -- char_descriptor->data.callback.context, NULL, &char_data_size); -- } -- -- tBleStatus status = aci_gatt_add_char( -- svc_handle, -- char_descriptor->uuid_type, -- &char_descriptor->uuid, -- char_data_size, -- char_descriptor->char_properties, -- char_descriptor->security_permissions, -- char_descriptor->gatt_evt_mask, -- GATT_MIN_READ_KEY_SIZE, -- char_descriptor->is_variable, -- &char_instance->handle); -- if(status) { -- FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); -- } -- -- char_instance->descriptor_handle = 0; -- if((status == 0) && char_descriptor->descriptor_params) { -- uint8_t const* char_data = NULL; -- const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = -- char_descriptor->descriptor_params; -- bool release_data = char_data_descriptor->data_callback.fn( -- char_data_descriptor->data_callback.context, &char_data, &char_data_size); -- -- status = aci_gatt_add_char_desc( -- svc_handle, -- char_instance->handle, -- char_data_descriptor->uuid_type, -- &char_data_descriptor->uuid, -- char_data_descriptor->max_length, -- char_data_size, -- char_data, -- char_data_descriptor->security_permissions, -- char_data_descriptor->access_permissions, -- char_data_descriptor->gatt_evt_mask, -- GATT_MIN_READ_KEY_SIZE, -- char_data_descriptor->is_variable, -- &char_instance->descriptor_handle); -- if(status) { -- FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); -- } -- if(release_data) { -- free((void*)char_data); -- } -- } --} -- --void flipper_gatt_characteristic_delete( -- uint16_t svc_handle, -- FlipperGattCharacteristicInstance* char_instance) { -- tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); -- if(status) { -- FURI_LOG_E( -- TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); -- } -- free((void*)char_instance->characteristic); --} -- --bool flipper_gatt_characteristic_update( -- uint16_t svc_handle, -- FlipperGattCharacteristicInstance* char_instance, -- const void* source) { -- furi_assert(char_instance); -- const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; -- FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); -- -- const uint8_t* char_data = NULL; -- uint16_t char_data_size = 0; -- bool release_data = false; -- if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { -- char_data = char_descriptor->data.fixed.ptr; -- if(source) { -- char_data = (uint8_t*)source; -- } -- char_data_size = char_descriptor->data.fixed.length; -- } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { -- const void* context = char_descriptor->data.callback.context; -- if(source) { -- context = source; -- } -- release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); -- } -- -- tBleStatus result = aci_gatt_update_char_value( -- svc_handle, char_instance->handle, 0, char_data_size, char_data); -- if(result) { -- FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); -- } -- if(release_data) { -- free((void*)char_data); -- } -- return result != BLE_STATUS_SUCCESS; --} -\ No newline at end of file -diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.h b/firmware/targets/f7/ble_glue/services/gatt_char.h -deleted file mode 100644 -index 959ab67a49..0000000000 ---- a/firmware/targets/f7/ble_glue/services/gatt_char.h -+++ /dev/null -@@ -1,96 +0,0 @@ --#pragma once -- --#include --#include --#include -- --#include -- --#ifdef __cplusplus --extern "C" { --#endif -- --// Callback signature for getting characteristic data --// Is called when characteristic is created to get max data length. Data ptr is NULL in this case --// The result is passed to aci_gatt_add_char as "Char_Value_Length" --// For updates, called with a context - see flipper_gatt_characteristic_update --// Returns true if *data ownership is transferred to the caller and will be freed --typedef bool (*cbFlipperGattCharacteristicData)( -- const void* context, -- const uint8_t** data, -- uint16_t* data_len); -- --typedef enum { -- FlipperGattCharacteristicDataFixed, -- FlipperGattCharacteristicDataCallback, --} FlipperGattCharacteristicDataType; -- --typedef struct { -- Char_Desc_Uuid_t uuid; -- struct { -- cbFlipperGattCharacteristicData fn; -- const void* context; -- } data_callback; -- uint8_t uuid_type; -- uint8_t max_length; -- uint8_t security_permissions; -- uint8_t access_permissions; -- uint8_t gatt_evt_mask; -- uint8_t is_variable; --} FlipperGattCharacteristicDescriptorParams; -- --typedef struct { -- const char* name; -- FlipperGattCharacteristicDescriptorParams* descriptor_params; -- union { -- struct { -- const uint8_t* ptr; -- uint16_t length; -- } fixed; -- struct { -- cbFlipperGattCharacteristicData fn; -- const void* context; -- } callback; -- } data; -- Char_UUID_t uuid; -- // Some packed bitfields to save space -- FlipperGattCharacteristicDataType data_prop_type : 2; -- uint8_t is_variable : 2; -- uint8_t uuid_type : 2; -- uint8_t char_properties; -- uint8_t security_permissions; -- uint8_t gatt_evt_mask; --} FlipperGattCharacteristicParams; -- --_Static_assert( -- sizeof(FlipperGattCharacteristicParams) == 36, -- "FlipperGattCharacteristicParams size must be 36 bytes"); -- --typedef struct { -- const FlipperGattCharacteristicParams* characteristic; -- uint16_t handle; -- uint16_t descriptor_handle; --} FlipperGattCharacteristicInstance; -- --// Initialize a characteristic instance; copies the characteristic descriptor into the instance --void flipper_gatt_characteristic_init( -- uint16_t svc_handle, -- const FlipperGattCharacteristicParams* char_descriptor, -- FlipperGattCharacteristicInstance* char_instance); -- --// Delete a characteristic instance; frees the copied characteristic descriptor from the instance --void flipper_gatt_characteristic_delete( -- uint16_t svc_handle, -- FlipperGattCharacteristicInstance* char_instance); -- --// Update a characteristic instance; if source==NULL, uses the data from the characteristic --// - For fixed data, fixed.ptr is used as the source if source==NULL --// - For callback-based data, collback.context is passed as the context if source==NULL --bool flipper_gatt_characteristic_update( -- uint16_t svc_handle, -- FlipperGattCharacteristicInstance* char_instance, -- const void* source); -- --#ifdef __cplusplus --} --#endif -\ No newline at end of file -diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c -deleted file mode 100644 -index 11f10b7b38..0000000000 ---- a/firmware/targets/f7/ble_glue/services/hid_service.c -+++ /dev/null -@@ -1,365 +0,0 @@ --#include "hid_service.h" --#include "app_common.h" --#include --#include "gatt_char.h" -- --#include -- --#define TAG "BtHid" -- --typedef enum { -- HidSvcGattCharacteristicProtocolMode = 0, -- HidSvcGattCharacteristicReportMap, -- HidSvcGattCharacteristicInfo, -- HidSvcGattCharacteristicCtrlPoint, -- HidSvcGattCharacteristicLed, -- HidSvcGattCharacteristicCount, --} HidSvcGattCharacteristicId; -- --typedef struct { -- uint8_t report_idx; -- uint8_t report_type; --} HidSvcReportId; -- --static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); -- --static bool -- hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { -- const HidSvcReportId* report_id = context; -- *data_len = sizeof(HidSvcReportId); -- if(data) { -- *data = (const uint8_t*)report_id; -- } -- return false; --} -- --typedef struct { -- const void* data_ptr; -- uint16_t data_len; --} HidSvcDataWrapper; -- --static bool -- hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { -- const HidSvcDataWrapper* report_data = context; -- if(data) { -- *data = report_data->data_ptr; -- *data_len = report_data->data_len; -- } else { -- *data_len = HID_SVC_REPORT_MAP_MAX_LEN; -- } -- return false; --} -- --// LED Descriptor params for BadBT -- --static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; -- --static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { -- .uuid_type = UUID_TYPE_16, -- .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, -- .max_length = HID_SVC_REPORT_REF_LEN, -- .data_callback.fn = hid_svc_char_desc_data_callback, -- .data_callback.context = led_desc_context_buf, -- .security_permissions = ATTR_PERMISSION_NONE, -- .access_permissions = ATTR_ACCESS_READ_WRITE, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT, --}; -- --static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { -- [HidSvcGattCharacteristicProtocolMode] = -- {.name = "Protocol Mode", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = 1, -- .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, -- .security_permissions = ATTR_PERMISSION_NONE, -- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [HidSvcGattCharacteristicReportMap] = -- {.name = "Report Map", -- .data_prop_type = FlipperGattCharacteristicDataCallback, -- .data.callback.fn = hid_svc_report_data_callback, -- .data.callback.context = NULL, -- .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ, -- .security_permissions = ATTR_PERMISSION_NONE, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_VARIABLE}, -- [HidSvcGattCharacteristicInfo] = -- {.name = "HID Information", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = HID_SVC_INFO_LEN, -- .data.fixed.ptr = NULL, -- .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ, -- .security_permissions = ATTR_PERMISSION_NONE, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [HidSvcGattCharacteristicCtrlPoint] = -- {.name = "HID Control Point", -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, -- .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, -- .security_permissions = ATTR_PERMISSION_NONE, -- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, -- .is_variable = CHAR_VALUE_LEN_CONSTANT}, -- [HidSvcGattCharacteristicLed] = -- { -- .name = -- "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars -- .data_prop_type = FlipperGattCharacteristicDataFixed, -- .data.fixed.length = 1, -- .uuid.Char_UUID_16 = REPORT_CHAR_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, -- .security_permissions = ATTR_PERMISSION_NONE, -- .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | -- GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, -- .is_variable = CHAR_VALUE_LEN_CONSTANT, -- .descriptor_params = &hid_svc_char_descr_led, -- }, --}; -- --static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { -- .uuid_type = UUID_TYPE_16, -- .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, -- .max_length = HID_SVC_REPORT_REF_LEN, -- .data_callback.fn = hid_svc_char_desc_data_callback, -- .security_permissions = ATTR_PERMISSION_NONE, -- .access_permissions = ATTR_ACCESS_READ_WRITE, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_CONSTANT, --}; -- --static const FlipperGattCharacteristicParams hid_svc_report_template = { -- .name = "Report", -- .data_prop_type = FlipperGattCharacteristicDataCallback, -- .data.callback.fn = hid_svc_report_data_callback, -- .data.callback.context = NULL, -- .uuid.Char_UUID_16 = REPORT_CHAR_UUID, -- .uuid_type = UUID_TYPE_16, -- .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, -- .security_permissions = ATTR_PERMISSION_NONE, -- .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, -- .is_variable = CHAR_VALUE_LEN_VARIABLE, --}; -- --typedef struct { -- uint16_t svc_handle; -- FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; -- FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; -- FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; -- FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; -- // led state -- HidLedStateEventCallback led_state_event_callback; -- void* led_state_ctx; --} HIDSvc; -- --static HIDSvc* hid_svc = NULL; -- --static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { -- SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; -- hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); -- evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; -- // aci_gatt_attribute_modified_event_rp0* attribute_modified; -- if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { -- if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { -- // Process modification events -- ret = SVCCTL_EvtAckFlowEnable; -- } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { -- // Process notification confirmation -- ret = SVCCTL_EvtAckFlowEnable; -- } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { -- // LED Characteristic and descriptor for BadBT to get numlock state for altchars -- // -- // Process write request -- aci_gatt_write_permit_req_event_rp0* req = -- (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; -- -- furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); -- -- // this check is likely to be incorrect, it will actually work in our case -- // but we need to investigate gatt api to see what is the rules -- // that specify attibute handle value from char handle (or the reverse) -- if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { -- hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); -- aci_gatt_write_resp( -- req->Connection_Handle, -- req->Attribute_Handle, -- 0x00, /* write_status = 0 (no error))*/ -- 0x00, /* err_code */ -- req->Data_Length, -- req->Data); -- aci_gatt_write_char_value( -- req->Connection_Handle, -- hid_svc->chars[HidSvcGattCharacteristicLed].handle, -- req->Data_Length, -- req->Data); -- ret = SVCCTL_EvtAckFlowEnable; -- } -- } -- } -- return ret; --} -- --void hid_svc_start() { -- tBleStatus status; -- hid_svc = malloc(sizeof(HIDSvc)); -- Service_UUID_t svc_uuid = {}; -- -- // Register event handler -- SVCCTL_RegisterSvcHandler(hid_svc_event_handler); -- // Add service -- svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; -- /** -- * Add Human Interface Device Service -- */ -- status = aci_gatt_add_service( -- UUID_TYPE_16, -- &svc_uuid, -- PRIMARY_SERVICE, -- 2 + /* protocol mode */ -- (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + -- (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + -- 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ -- &hid_svc->svc_handle); -- if(status) { -- FURI_LOG_E(TAG, "Failed to add HID service: %d", status); -- } -- -- for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { -- flipper_gatt_characteristic_init( -- hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); -- } -- uint8_t protocol_mode = 1; -- flipper_gatt_characteristic_update( -- hid_svc->svc_handle, -- &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], -- &protocol_mode); -- -- // reports -- FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; -- FlipperGattCharacteristicParams report_char; -- HidSvcReportId report_id; -- -- memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); -- memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); -- -- hid_svc_char_descr.data_callback.context = &report_id; -- report_char.descriptor_params = &hid_svc_char_descr; -- -- typedef struct { -- uint8_t report_type; -- uint8_t report_count; -- FlipperGattCharacteristicInstance* chars; -- } HidSvcReportCharProps; -- -- HidSvcReportCharProps hid_report_chars[] = { -- {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, -- {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, -- {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, -- }; -- -- for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); -- report_type_idx++) { -- report_id.report_type = hid_report_chars[report_type_idx].report_type; -- for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; -- report_idx++) { -- report_id.report_idx = report_idx + 1; -- flipper_gatt_characteristic_init( -- hid_svc->svc_handle, -- &report_char, -- &hid_report_chars[report_type_idx].chars[report_idx]); -- } -- } --} -- --bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { -- furi_assert(data); -- furi_assert(hid_svc); -- -- HidSvcDataWrapper report_data = { -- .data_ptr = data, -- .data_len = len, -- }; -- return flipper_gatt_characteristic_update( -- hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); --} -- --bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { -- furi_assert(data); -- furi_assert(hid_svc); -- furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); -- -- HidSvcDataWrapper report_data = { -- .data_ptr = data, -- .data_len = len, -- }; -- return flipper_gatt_characteristic_update( -- hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); --} -- --bool hid_svc_update_info(uint8_t* data) { -- furi_assert(data); -- furi_assert(hid_svc); -- -- return flipper_gatt_characteristic_update( -- hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); --} -- --void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { -- furi_assert(hid_svc); -- furi_assert(callback); -- furi_assert(context); -- -- hid_svc->led_state_event_callback = callback; -- hid_svc->led_state_ctx = context; --} -- --bool hid_svc_is_started() { -- return hid_svc != NULL; --} -- --void hid_svc_stop() { -- tBleStatus status; -- if(hid_svc) { -- // Delete characteristics -- for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { -- flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); -- } -- -- typedef struct { -- uint8_t report_count; -- FlipperGattCharacteristicInstance* chars; -- } HidSvcReportCharProps; -- -- HidSvcReportCharProps hid_report_chars[] = { -- {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, -- {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, -- {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, -- }; -- -- for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); -- report_type_idx++) { -- for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; -- report_idx++) { -- flipper_gatt_characteristic_delete( -- hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); -- } -- } -- -- // Delete service -- status = aci_gatt_del_service(hid_svc->svc_handle); -- if(status) { -- FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); -- } -- free(hid_svc); -- hid_svc = NULL; -- } --} -diff --git a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc -deleted file mode 100644 -index a297d9ad60..0000000000 ---- a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc -+++ /dev/null -@@ -1,12 +0,0 @@ -- --static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ -- { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; -- --#define SERIAL_SVC_TX_CHAR_UUID \ -- { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } --#define SERIAL_SVC_RX_CHAR_UUID \ -- { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } --#define SERIAL_SVC_FLOW_CONTROL_UUID \ -- { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } --#define SERIAL_SVC_RPC_STATUS_UUID \ -- { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } -diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c -index 22d286abd0..f71cd37762 100644 ---- a/firmware/targets/f7/furi_hal/furi_hal_bt.c -+++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c -@@ -8,7 +8,8 @@ - #include - #include - #include --#include -+#include "battery_service.h" -+ - #include - - #define TAG "FuriHalBt" -diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c -index 2bbfc15231..8e05a99049 100644 ---- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c -+++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c -@@ -1,11 +1,11 @@ - #include - #include --#include --#include --#include -+#include "usb_hid.h" -+#include "dev_info_service.h" -+#include "battery_service.h" -+#include "hid_service.h" - - #include --#include - - #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) - #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) -@@ -221,7 +221,7 @@ void furi_hal_bt_hid_start() { - FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | - FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, - }; -- hid_svc_update_info(hid_info_val); -+ hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); - } - - void furi_hal_bt_hid_stop() { -diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c -index 2927d946f9..2539e6bd0e 100644 ---- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c -+++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c -@@ -1,7 +1,7 @@ - #include --#include --#include --#include -+#include "dev_info_service.h" -+#include "battery_service.h" -+#include "serial_service.h" - - #include - -diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h -index d6a6e8c42b..bfe4a67c3c 100644 ---- a/firmware/targets/furi_hal_include/furi_hal_bt.h -+++ b/firmware/targets/furi_hal_include/furi_hal_bt.h -@@ -8,7 +8,7 @@ - #include - #include - #include --#include -+#include - #include - #include - -diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h -index 0472d31d18..1b6e79ab07 100644 ---- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h -+++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h -@@ -1,6 +1,6 @@ - #pragma once - --#include -+#include "serial_service.h" - - #ifdef __cplusplus - extern "C" { From c4baf3194ef776b0b2fbbf46125ce4e790762311 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 13 Jun 2023 22:17:54 +0300 Subject: [PATCH 178/370] SubGhz App: change load custom presets --- applications/main/subghz/subghz.c | 83 +++++++++++++---------------- firmware/targets/f7/api_symbols.csv | 3 +- lib/subghz/subghz_setting.c | 32 +++++++++++ lib/subghz/subghz_setting.h | 2 + 4 files changed, 73 insertions(+), 47 deletions(-) diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 97e2e8c34..aab154f2d 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -51,6 +51,42 @@ static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) } } +static void subghz_load_custom_presets(SubGhzSetting* setting) { + furi_assert(setting); + + const char* presets[][2] = { + {"FM95", + "02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"}, + + // #2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping + {"FM15k", + "02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0"}, + + // Pagers + {"Pagers", + "02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"}, + + // # HND - FM preset + {"HND_1", + "02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"}, + }; + + FlipperFormat* fff_temp = flipper_format_string_alloc(); + + for(uint8_t i = 0; i < COUNT_OF(presets); i++) { + flipper_format_insert_or_update_string_cstr(fff_temp, "Custom_preset_data", presets[i][1]); + + flipper_format_rewind(fff_temp); + subghz_setting_load_custom_preset(setting, presets[i][0], fff_temp); + } + + flipper_format_free(fff_temp); + +#ifdef FURI_DEBUG + subghz_setting_customs_presets_to_log(setting); +#endif +} + SubGhz* subghz_alloc(bool alloc_for_tx_only) { SubGhz* subghz = malloc(sizeof(SubGhz)); @@ -177,52 +213,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); - // Custom Presets load without using config file - if(!alloc_for_tx_only) { - FlipperFormat* temp_fm_preset = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset, - (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset); - subghz_setting_load_custom_preset(setting, (const char*)"FM95", temp_fm_preset); - - flipper_format_free(temp_fm_preset); - - // #2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping - FlipperFormat* temp_fm_preset2 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset2, - (const char*)"Custom_preset_data", - (const char*)"02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0"); - flipper_format_rewind(temp_fm_preset2); - subghz_setting_load_custom_preset(setting, (const char*)"FM15k", temp_fm_preset2); - - flipper_format_free(temp_fm_preset2); - - // Pagers - FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset3, - (const char*)"Custom_preset_data", - (const char*)"02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset3); - subghz_setting_load_custom_preset(setting, (const char*)"Pagers", temp_fm_preset3); - - flipper_format_free(temp_fm_preset3); - - // # HND - FM preset - FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset4, - (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset4); - subghz_setting_load_custom_preset(setting, (const char*)"HND_1", temp_fm_preset4); - - flipper_format_free(temp_fm_preset4); - } - // custom presets loading - end + subghz_load_custom_presets(setting); // Load last used values for Read, Read RAW, etc. or default subghz->last_settings = subghz_last_settings_alloc(); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8a7f947e6..aed57c63c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.1,, +Version,+,30.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2784,6 +2784,7 @@ Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" Function,+,subghz_receiver_set_rx_callback,void,"SubGhzReceiver*, SubGhzReceiverCallback, void*" Function,+,subghz_setting_alloc,SubGhzSetting*, +Function,-,subghz_setting_customs_presets_to_log,uint8_t,SubGhzSetting* Function,+,subghz_setting_delete_custom_preset,_Bool,"SubGhzSetting*, const char*" Function,+,subghz_setting_free,void,SubGhzSetting* Function,+,subghz_setting_get_default_frequency,uint32_t,SubGhzSetting* diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index cd9d0466e..46e39b38b 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -482,3 +482,35 @@ uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance) { return subghz_setting_get_frequency( instance, subghz_setting_get_frequency_default_index(instance)); } + +uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance) { + furi_assert(instance); +#ifndef FURI_DEBUG + FURI_LOG_I(TAG, "Logging loaded presets allow only Debug build"); +#else + uint8_t count = 0; + FuriString* temp = furi_string_alloc(); + + FURI_LOG_I(TAG, "Loaded presets"); + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + furi_string_reset(temp); + + for(uint8_t i = 0; i < item->custom_preset_data_size; i++) { + furi_string_cat_printf(temp, "%02u ", item->custom_preset_data[i]); + } + + FURI_LOG_I( + TAG, "%u - %s", count + 1, furi_string_get_cstr(item->custom_preset_name)); + FURI_LOG_I(TAG, " Size: %u", item->custom_preset_data_size); + FURI_LOG_I(TAG, " Data: %s", furi_string_get_cstr(temp)); + + count++; + } + + furi_string_free(temp); + + return count; +#endif + return 0; +} diff --git a/lib/subghz/subghz_setting.h b/lib/subghz/subghz_setting.h index 3cb07ff6d..3e705312f 100644 --- a/lib/subghz/subghz_setting.h +++ b/lib/subghz/subghz_setting.h @@ -53,6 +53,8 @@ uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup); +uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance); + #ifdef __cplusplus } #endif From 7b44221d9a6101996c67ffe753b83f2215bb1d3e Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 13 Jun 2023 22:36:34 +0300 Subject: [PATCH 179/370] Drop timestamp_file_names in hal --- .../main/subghz/scenes/subghz_scene_radio_settings.c | 3 +-- applications/main/subghz/scenes/subghz_scene_save_name.c | 2 +- applications/main/subghz/subghz_last_settings.c | 3 --- firmware/targets/f7/api_symbols.csv | 4 +--- firmware/targets/f7/furi_hal/furi_hal_subghz.c | 9 --------- firmware/targets/f7/furi_hal/furi_hal_subghz.h | 8 -------- 6 files changed, 3 insertions(+), 26 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index b60ea0b2d..3020c1b23 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -111,7 +111,6 @@ static void subghz_scene_receiver_config_set_timestamp_file_names(VariableItem* variable_item_set_current_value_text(item, timestamp_names_text[index]); - furi_hal_subghz_set_timestamp_file_names((index == 1)); subghz->last_settings->timestamp_file_names = (index == 1); subghz_last_settings_save(subghz->last_settings); } @@ -148,7 +147,7 @@ void subghz_scene_radio_settings_on_enter(void* context) { TIMESTAMP_NAMES_COUNT, subghz_scene_receiver_config_set_timestamp_file_names, subghz); - value_index = furi_hal_subghz_get_timestamp_file_names(); + value_index = subghz->last_settings->timestamp_file_names; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, timestamp_names_text[value_index]); diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index c05d0d962..5c52ed957 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -56,7 +56,7 @@ void subghz_scene_save_name_on_enter(void* context) { if(!subghz_path_is_file(subghz->file_path)) { char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; - if(furi_hal_subghz_get_timestamp_file_names()) { + if(subghz->last_settings->timestamp_file_names) { SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); if(decoder_result != 0x0) { if(decoder_result != NULL) { diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 7ee2554b0..614e0baa6 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -119,9 +119,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->timestamp_file_names = temp_timestamp_file_names; - // Set globally - furi_hal_subghz_set_timestamp_file_names(instance->timestamp_file_names); - if(instance->external_module_power_5v_disable) { furi_hal_subghz_set_external_power_disable(true); furi_hal_subghz_disable_ext_power(); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index aed57c63c..1e1ced11a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.2,, +Version,+,31.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1405,7 +1405,6 @@ Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_radio_type,SubGhzRadioType, Function,+,furi_hal_subghz_get_rolling_counter_mult,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, -Function,+,furi_hal_subghz_get_timestamp_file_names,_Bool, Function,+,furi_hal_subghz_idle,void, Function,-,furi_hal_subghz_init,void, Function,-,furi_hal_subghz_init_check,_Bool, @@ -1429,7 +1428,6 @@ Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath Function,-,furi_hal_subghz_set_rolling_counter_mult,void,uint8_t -Function,-,furi_hal_subghz_set_timestamp_file_names,void,_Bool Function,-,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 8125971e9..aa7438b0b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -41,7 +41,6 @@ volatile FuriHalSubGhz furi_hal_subghz = { .cc1101_g0_pin = &gpio_cc1101_g0, .rolling_counter_mult = 1, .ext_module_power_disabled = false, - .timestamp_file_names = false, .dangerous_frequency_i = false, }; @@ -90,14 +89,6 @@ bool furi_hal_subghz_get_external_power_disable(void) { return furi_hal_subghz.ext_module_power_disabled; } -void furi_hal_subghz_set_timestamp_file_names(bool state) { - furi_hal_subghz.timestamp_file_names = state; -} - -bool furi_hal_subghz_get_timestamp_file_names(void) { - return furi_hal_subghz.timestamp_file_names; -} - void furi_hal_subghz_set_dangerous_frequency(bool state_i) { furi_hal_subghz.dangerous_frequency_i = state_i; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index 0ef7bd90a..ae6876d45 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -332,14 +332,6 @@ void furi_hal_subghz_set_external_power_disable(bool state); */ bool furi_hal_subghz_get_external_power_disable(void); -/** If true - disable generation of random name and add timestamp to filenames instead - */ -void furi_hal_subghz_set_timestamp_file_names(bool state); - -/** Get the current state of the timestamp instead of random name flag - */ -bool furi_hal_subghz_get_timestamp_file_names(void); - /** Set what radio module we will be using */ void furi_hal_subghz_select_radio_type(SubGhzRadioType state); From f22624399c1288638ec4df9bd00046db5ca27550 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 14 Jun 2023 01:07:07 +0300 Subject: [PATCH 180/370] remove unneeded global var --- .../main/subghz/scenes/subghz_scene_radio_settings.c | 3 +-- applications/main/subghz/scenes/subghz_scene_save_name.c | 2 +- applications/main/subghz/subghz_last_settings.c | 2 -- firmware/targets/f7/api_symbols.csv | 2 -- firmware/targets/f7/furi_hal/furi_hal_subghz.c | 9 --------- firmware/targets/f7/furi_hal/furi_hal_subghz.h | 8 -------- 6 files changed, 2 insertions(+), 24 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index b60ea0b2d..3020c1b23 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -111,7 +111,6 @@ static void subghz_scene_receiver_config_set_timestamp_file_names(VariableItem* variable_item_set_current_value_text(item, timestamp_names_text[index]); - furi_hal_subghz_set_timestamp_file_names((index == 1)); subghz->last_settings->timestamp_file_names = (index == 1); subghz_last_settings_save(subghz->last_settings); } @@ -148,7 +147,7 @@ void subghz_scene_radio_settings_on_enter(void* context) { TIMESTAMP_NAMES_COUNT, subghz_scene_receiver_config_set_timestamp_file_names, subghz); - value_index = furi_hal_subghz_get_timestamp_file_names(); + value_index = subghz->last_settings->timestamp_file_names; variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, timestamp_names_text[value_index]); diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index c05d0d962..5c52ed957 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -56,7 +56,7 @@ void subghz_scene_save_name_on_enter(void* context) { if(!subghz_path_is_file(subghz->file_path)) { char file_name_buf[SUBGHZ_MAX_LEN_NAME] = {0}; - if(furi_hal_subghz_get_timestamp_file_names()) { + if(subghz->last_settings->timestamp_file_names) { SubGhzProtocolDecoderBase* decoder_result = subghz_txrx_get_decoder(subghz->txrx); if(decoder_result != 0x0) { if(decoder_result != NULL) { diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 7ee2554b0..8cf0f063a 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -120,8 +120,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->timestamp_file_names = temp_timestamp_file_names; // Set globally - furi_hal_subghz_set_timestamp_file_names(instance->timestamp_file_names); - if(instance->external_module_power_5v_disable) { furi_hal_subghz_set_external_power_disable(true); furi_hal_subghz_disable_ext_power(); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8a7f947e6..26e4c0b3e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1405,7 +1405,6 @@ Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_radio_type,SubGhzRadioType, Function,+,furi_hal_subghz_get_rolling_counter_mult,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, -Function,+,furi_hal_subghz_get_timestamp_file_names,_Bool, Function,+,furi_hal_subghz_idle,void, Function,-,furi_hal_subghz_init,void, Function,-,furi_hal_subghz_init_check,_Bool, @@ -1429,7 +1428,6 @@ Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath Function,-,furi_hal_subghz_set_rolling_counter_mult,void,uint8_t -Function,-,furi_hal_subghz_set_timestamp_file_names,void,_Bool Function,-,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 8125971e9..aa7438b0b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -41,7 +41,6 @@ volatile FuriHalSubGhz furi_hal_subghz = { .cc1101_g0_pin = &gpio_cc1101_g0, .rolling_counter_mult = 1, .ext_module_power_disabled = false, - .timestamp_file_names = false, .dangerous_frequency_i = false, }; @@ -90,14 +89,6 @@ bool furi_hal_subghz_get_external_power_disable(void) { return furi_hal_subghz.ext_module_power_disabled; } -void furi_hal_subghz_set_timestamp_file_names(bool state) { - furi_hal_subghz.timestamp_file_names = state; -} - -bool furi_hal_subghz_get_timestamp_file_names(void) { - return furi_hal_subghz.timestamp_file_names; -} - void furi_hal_subghz_set_dangerous_frequency(bool state_i) { furi_hal_subghz.dangerous_frequency_i = state_i; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index 0ef7bd90a..ae6876d45 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -332,14 +332,6 @@ void furi_hal_subghz_set_external_power_disable(bool state); */ bool furi_hal_subghz_get_external_power_disable(void); -/** If true - disable generation of random name and add timestamp to filenames instead - */ -void furi_hal_subghz_set_timestamp_file_names(bool state); - -/** Get the current state of the timestamp instead of random name flag - */ -bool furi_hal_subghz_get_timestamp_file_names(void); - /** Set what radio module we will be using */ void furi_hal_subghz_select_radio_type(SubGhzRadioType state); From 417dedd3176605f9b921ba0f6d91f623d1adb0d5 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Wed, 14 Jun 2023 01:24:05 +0300 Subject: [PATCH 181/370] SubGhz App: remove kostily and velosipedy --- .../subghz/scenes/subghz_scene_receiver.c | 78 +++++++------------ .../scenes/subghz_scene_receiver_config.c | 70 ++++++++--------- applications/main/subghz/subghz.c | 1 + applications/main/subghz/subghz_i.h | 5 +- lib/subghz/protocols/kia.c | 3 +- lib/subghz/protocols/magellan.c | 3 +- lib/subghz/protocols/scher_khan.c | 2 +- lib/subghz/protocols/star_line.c | 3 +- lib/subghz/types.h | 3 + 9 files changed, 75 insertions(+), 93 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index d9fd38836..8bba21b2b 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -92,35 +92,41 @@ static void subghz_scene_add_to_history_callback( void* context) { furi_assert(context); SubGhz* subghz = context; - SubGhzHistory* history = subghz->history; - FuriString* item_name = furi_string_alloc(); - FuriString* item_time = furi_string_alloc(); - uint16_t idx = subghz_history_get_item(history); - SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); - if(subghz_history_add_to_history(history, decoder_base, &preset)) { - furi_string_reset(item_name); - furi_string_reset(item_time); + // The check can be moved to /lib/subghz/receiver.c, but may result in false positives + if((decoder_base->protocol->flag & subghz->ignore_filter) == 0) { + SubGhzHistory* history = subghz->history; + FuriString* item_name = furi_string_alloc(); + FuriString* item_time = furi_string_alloc(); + uint16_t idx = subghz_history_get_item(history); - subghz->state_notifications = SubGhzNotificationStateRxDone; + SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); + if(subghz_history_add_to_history(history, decoder_base, &preset)) { + furi_string_reset(item_name); + furi_string_reset(item_time); - subghz_history_get_text_item_menu(history, item_name, idx); - subghz_history_get_time_item_menu(history, item_time, idx); - subghz_view_receiver_add_item_to_menu( - subghz->subghz_receiver, - furi_string_get_cstr(item_name), - furi_string_get_cstr(item_time), - subghz_history_get_type_protocol(history, idx)); + subghz->state_notifications = SubGhzNotificationStateRxDone; - subghz_scene_receiver_update_statusbar(subghz); - if(subghz_history_get_text_space_left(subghz->history, NULL)) { - notification_message(subghz->notifications, &sequence_error); + subghz_history_get_text_item_menu(history, item_name, idx); + subghz_history_get_time_item_menu(history, item_time, idx); + subghz_view_receiver_add_item_to_menu( + subghz->subghz_receiver, + furi_string_get_cstr(item_name), + furi_string_get_cstr(item_time), + subghz_history_get_type_protocol(history, idx)); + + subghz_scene_receiver_update_statusbar(subghz); + if(subghz_history_get_text_space_left(subghz->history, NULL)) { + notification_message(subghz->notifications, &sequence_error); + } } + subghz_receiver_reset(receiver); + furi_string_free(item_name); + furi_string_free(item_time); + subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); + } else { + FURI_LOG_I(TAG, "%s protocol ignored", decoder_base->protocol->name); } - subghz_receiver_reset(receiver); - furi_string_free(item_name); - furi_string_free(item_time); - subghz_rx_key_state_set(subghz, SubGhzRxKeyStateAddKey); } void subghz_scene_receiver_on_enter(void* context) { @@ -161,32 +167,6 @@ void subghz_scene_receiver_on_enter(void* context) { subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz); - // TODO: Replace with proper solution based on protocol flags, remove kostily and velosipedy from here - // Needs to be done after subghz refactoring merge!!! - if(subghz->ignore_starline == true) { - if(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, "Star Line")) { - subghz_protocol_decoder_base_set_decoder_callback( - subghz_txrx_get_decoder(subghz->txrx), NULL, NULL); - } - } - if(subghz->ignore_auto_alarms == true) { - if(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, "KIA Seed")) { - subghz_protocol_decoder_base_set_decoder_callback( - subghz_txrx_get_decoder(subghz->txrx), NULL, NULL); - } - - if(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, "Scher-Khan")) { - subghz_protocol_decoder_base_set_decoder_callback( - subghz_txrx_get_decoder(subghz->txrx), NULL, NULL); - } - } - if(subghz->ignore_magellan == true) { - if(subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, "Magellan")) { - subghz_protocol_decoder_base_set_decoder_callback( - subghz_txrx_get_decoder(subghz->txrx), NULL, NULL); - } - } - if(!subghz_history_get_text_space_left(subghz->history, NULL)) { subghz->state_notifications = SubGhzNotificationStateRx; } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index 29d55c03a..62ee53871 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -71,20 +71,8 @@ const uint32_t bin_raw_value[BIN_RAW_COUNT] = { SubGhzProtocolFlag_Decodable, SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, }; -#define STARLINE_COUNT 2 -const char* const starline_text[STARLINE_COUNT] = { - "OFF", - "ON", -}; - -#define AUTO_ALARMS_COUNT 2 -const char* const auto_alarms_text[AUTO_ALARMS_COUNT] = { - "OFF", - "ON", -}; - -#define MAGELLAN_COUNT 2 -const char* const magellan_text[MAGELLAN_COUNT] = { +#define PROTOCOL_IGNORE_COUNT 2 +const char* const protocol_ignore_text[PROTOCOL_IGNORE_COUNT] = { "OFF", "ON", }; @@ -257,28 +245,35 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it subghz_threshold_rssi_set(subghz->threshold_rssi, raw_threshold_rssi_value[index]); } -static void subghz_scene_receiver_config_set_starline(VariableItem* item) { +static inline void + subghz_scene_receiver_config_set_ignore_filter(VariableItem* item, SubGhzProtocolFlag filter) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, starline_text[index]); - subghz->ignore_starline = (index == 1); + variable_item_set_current_value_text(item, protocol_ignore_text[index]); + + if(index == 0) { + CLEAR_BIT(subghz->ignore_filter, filter); + } else { + SET_BIT(subghz->ignore_filter, filter); + } +} +static inline bool subghz_scene_receiver_config_ignore_filter_get_index( + SubGhzProtocolFlag filter, + SubGhzProtocolFlag flag) { + return READ_BIT(filter, flag) > 0; +} + +static void subghz_scene_receiver_config_set_starline(VariableItem* item) { + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_StarLine); } static void subghz_scene_receiver_config_set_auto_alarms(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, auto_alarms_text[index]); - subghz->ignore_auto_alarms = (index == 1); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_AutoAlarms); } static void subghz_scene_receiver_config_set_magellan(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, magellan_text[index]); - subghz->ignore_magellan = (index == 1); + subghz_scene_receiver_config_set_ignore_filter(item, SubGhzProtocolFlag_Magelan); } static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { @@ -362,35 +357,38 @@ void subghz_scene_receiver_config_on_enter(void* context) { item = variable_item_list_add( subghz->variable_item_list, "Ignore Starline:", - STARLINE_COUNT, + PROTOCOL_IGNORE_COUNT, subghz_scene_receiver_config_set_starline, subghz); - value_index = subghz->ignore_starline; + value_index = subghz_scene_receiver_config_ignore_filter_get_index( + subghz->ignore_filter, SubGhzProtocolFlag_StarLine); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, starline_text[value_index]); + variable_item_set_current_value_text(item, protocol_ignore_text[value_index]); item = variable_item_list_add( subghz->variable_item_list, "Ignore Cars:", - AUTO_ALARMS_COUNT, + PROTOCOL_IGNORE_COUNT, subghz_scene_receiver_config_set_auto_alarms, subghz); - value_index = subghz->ignore_auto_alarms; + value_index = subghz_scene_receiver_config_ignore_filter_get_index( + subghz->ignore_filter, SubGhzProtocolFlag_AutoAlarms); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, auto_alarms_text[value_index]); + variable_item_set_current_value_text(item, protocol_ignore_text[value_index]); item = variable_item_list_add( subghz->variable_item_list, "Ignore Magellan:", - MAGELLAN_COUNT, + PROTOCOL_IGNORE_COUNT, subghz_scene_receiver_config_set_magellan, subghz); - value_index = subghz->ignore_magellan; + value_index = subghz_scene_receiver_config_ignore_filter_get_index( + subghz->ignore_filter, SubGhzProtocolFlag_Magelan); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, magellan_text[value_index]); + variable_item_set_current_value_text(item, protocol_ignore_text[value_index]); } // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index aab154f2d..db63ab1fe 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -242,6 +242,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->secure_data = malloc(sizeof(SecureData)); subghz->filter = SubGhzProtocolFlag_Decodable; + subghz->ignore_filter = 0x0; subghz_txrx_receiver_set_filter(subghz->txrx, subghz->filter); subghz_txrx_set_need_save_callback(subghz->txrx, subghz_save_to_file, subghz); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index 3d1c2db9c..08658e9b2 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -89,13 +89,10 @@ struct SubGhz { SubGhzLastSettings* last_settings; SubGhzProtocolFlag filter; + SubGhzProtocolFlag ignore_filter; FuriString* error_str; SubGhzLock lock; - bool ignore_starline; - bool ignore_auto_alarms; - bool ignore_magellan; - SecureData* secure_data; SubGhzFileEncoderWorker* decode_raw_file_worker_encoder; diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 1d134f7ba..2d2ceaa1d 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -63,7 +63,8 @@ const SubGhzProtocolEncoder subghz_protocol_kia_encoder = { const SubGhzProtocol subghz_protocol_kia = { .name = SUBGHZ_PROTOCOL_KIA_NAME, .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_AutoAlarms, .decoder = &subghz_protocol_kia_decoder, .encoder = &subghz_protocol_kia_encoder, diff --git a/lib/subghz/protocols/magellan.c b/lib/subghz/protocols/magellan.c index 2d02a866c..be819ff31 100644 --- a/lib/subghz/protocols/magellan.c +++ b/lib/subghz/protocols/magellan.c @@ -64,7 +64,8 @@ const SubGhzProtocol subghz_protocol_magellan = { .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, .type = SubGhzProtocolTypeStatic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | + SubGhzProtocolFlag_Magelan, .decoder = &subghz_protocol_magellan_decoder, .encoder = &subghz_protocol_magellan_encoder, diff --git a/lib/subghz/protocols/scher_khan.c b/lib/subghz/protocols/scher_khan.c index c2fa77b2f..53b7935d6 100644 --- a/lib/subghz/protocols/scher_khan.c +++ b/lib/subghz/protocols/scher_khan.c @@ -70,7 +70,7 @@ const SubGhzProtocol subghz_protocol_scher_khan = { .name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Save, + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_AutoAlarms, .decoder = &subghz_protocol_scher_khan_decoder, .encoder = &subghz_protocol_scher_khan_encoder, diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index 05afd80a6..bf338b35d 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -79,7 +79,8 @@ const SubGhzProtocol subghz_protocol_star_line = { .name = SUBGHZ_PROTOCOL_STAR_LINE_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | + SubGhzProtocolFlag_StarLine, .decoder = &subghz_protocol_star_line_decoder, .encoder = &subghz_protocol_star_line_encoder, diff --git a/lib/subghz/types.h b/lib/subghz/types.h index ce65789a9..70a57d3f9 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -117,6 +117,9 @@ typedef enum { SubGhzProtocolFlag_Load = (1 << 8), SubGhzProtocolFlag_Send = (1 << 9), SubGhzProtocolFlag_BinRAW = (1 << 10), + SubGhzProtocolFlag_StarLine = (1 << 11), + SubGhzProtocolFlag_AutoAlarms = (1 << 12), + SubGhzProtocolFlag_Magelan = (1 << 13), } SubGhzProtocolFlag; struct SubGhzProtocol { From 93bb30810bcba54750b132f96bc15af279733594 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 13 Jun 2023 23:30:37 +0100 Subject: [PATCH 182/370] Fix yanderedev code --- firmware/targets/f7/furi_hal/furi_hal_light.c | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index 76b401d95..2673b8a58 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -97,26 +97,39 @@ void furi_hal_light_blink_set_color(Light light) { void furi_hal_light_sequence(const char* sequence) { do { - if(*sequence == 'R') { + switch(*sequence) { + case 'R': furi_hal_light_set(LightRed, 0xFF); - } else if(*sequence == 'r') { + break; + case 'r': furi_hal_light_set(LightRed, 0x00); - } else if(*sequence == 'G') { + break; + case 'G': furi_hal_light_set(LightGreen, 0xFF); - } else if(*sequence == 'g') { + break; + case 'g': furi_hal_light_set(LightGreen, 0x00); - } else if(*sequence == 'B') { + break; + case 'B': furi_hal_light_set(LightBlue, 0xFF); - } else if(*sequence == 'b') { + break; + case 'b': furi_hal_light_set(LightBlue, 0x00); - } else if(*sequence == 'W') { + break; + case 'W': furi_hal_light_set(LightBacklight, 0xFF); - } else if(*sequence == 'w') { + break; + case 'w': furi_hal_light_set(LightBacklight, 0x00); - } else if(*sequence == '.') { + break; + case '.': furi_delay_ms(250); - } else if(*sequence == '-') { + break; + case '-': furi_delay_ms(500); + break; + default: + break; } sequence++; } while(*sequence != 0); From eb1e81621c646bdf0ad49935f4f4eee43115e344 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 14 Jun 2023 00:55:44 +0100 Subject: [PATCH 183/370] Add mass storage --- .../mass_storage/helpers/mass_storage_scsi.c | 222 +++++++++ .../mass_storage/helpers/mass_storage_scsi.h | 56 +++ .../mass_storage/helpers/mass_storage_usb.c | 468 ++++++++++++++++++ .../mass_storage/helpers/mass_storage_usb.h | 9 + .../external/mass_storage/mass_storage_app.c | 124 +++++ .../external/mass_storage/mass_storage_app.h | 11 + .../mass_storage/mass_storage_app_i.h | 44 ++ .../mass_storage/scenes/mass_storage_scene.c | 30 ++ .../mass_storage/scenes/mass_storage_scene.h | 29 ++ .../scenes/mass_storage_scene_config.h | 3 + .../scenes/mass_storage_scene_error.c | 53 ++ .../scenes/mass_storage_scene_file_select.c | 36 ++ .../scenes/mass_storage_scene_work.c | 105 ++++ .../mass_storage/views/mass_storage_view.c | 56 +++ .../mass_storage/views/mass_storage_view.h | 13 + 15 files changed, 1259 insertions(+) create mode 100644 applications/external/mass_storage/helpers/mass_storage_scsi.c create mode 100644 applications/external/mass_storage/helpers/mass_storage_scsi.h create mode 100644 applications/external/mass_storage/helpers/mass_storage_usb.c create mode 100644 applications/external/mass_storage/helpers/mass_storage_usb.h create mode 100644 applications/external/mass_storage/mass_storage_app.c create mode 100644 applications/external/mass_storage/mass_storage_app.h create mode 100644 applications/external/mass_storage/mass_storage_app_i.h create mode 100644 applications/external/mass_storage/scenes/mass_storage_scene.c create mode 100644 applications/external/mass_storage/scenes/mass_storage_scene.h create mode 100644 applications/external/mass_storage/scenes/mass_storage_scene_config.h create mode 100644 applications/external/mass_storage/scenes/mass_storage_scene_error.c create mode 100644 applications/external/mass_storage/scenes/mass_storage_scene_file_select.c create mode 100644 applications/external/mass_storage/scenes/mass_storage_scene_work.c create mode 100644 applications/external/mass_storage/views/mass_storage_view.c create mode 100644 applications/external/mass_storage/views/mass_storage_view.h diff --git a/applications/external/mass_storage/helpers/mass_storage_scsi.c b/applications/external/mass_storage/helpers/mass_storage_scsi.c new file mode 100644 index 000000000..d1114c889 --- /dev/null +++ b/applications/external/mass_storage/helpers/mass_storage_scsi.c @@ -0,0 +1,222 @@ +#include "mass_storage_scsi.h" + +#include + +#define TAG "MassStorageSCSI" + +#define SCSI_TEST_UNIT_READY (0x00) +#define SCSI_REQUEST_SENSE (0x03) +#define SCSI_INQUIRY (0x12) +#define SCSI_READ_CAPACITY_6 (0x25) +#define SCSI_MODE_SENSE_6 (0x1A) +#define SCSI_READ_10 (0x28) +#define SCSI_PREVENT_MEDIUM_REMOVAL (0x1E) +#define SCSI_START_STOP_UNIT (0x1B) +#define SCSI_WRITE_10 (0x2A) + +bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) { + if(!len) { + scsi->sk = SCSI_SK_ILLEGAL_REQUEST; + scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + return false; + } + // FURI_LOG_I(TAG, "START %02x", cmd[0]); + scsi->cmd = cmd; + scsi->cmd_len = len; + scsi->rx_done = false; + scsi->tx_done = false; + switch(cmd[0]) { + case SCSI_WRITE_10: { + if(len < 10) return false; + scsi->write_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5]; + scsi->write_10.count = cmd[7] << 8 | cmd[8]; + FURI_LOG_I(TAG, "SCSI_WRITE_10 %08lx %04x", scsi->write_10.lba, scsi->write_10.count); + return true; + }; break; + case SCSI_READ_10: { + if(len < 10) return false; + scsi->read_10.lba = cmd[2] << 24 | cmd[3] << 16 | cmd[4] << 8 | cmd[5]; + scsi->read_10.count = cmd[7] << 8 | cmd[8]; + FURI_LOG_I(TAG, "SCSI_READ_10 %08lx %04x", scsi->read_10.lba, scsi->read_10.count); + return true; + }; break; + } + return true; +} + +bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) { + // FURI_LOG_I(TAG, "RX %02x len %d", scsi->cmd[0], len); + if(scsi->rx_done) return false; + switch(scsi->cmd[0]) { + case SCSI_WRITE_10: { + uint32_t block_size = SCSI_BLOCK_SIZE; + uint16_t blocks = len / block_size; + bool result = + scsi->fn.write(scsi->fn.ctx, scsi->write_10.lba, blocks, data, blocks * block_size); + scsi->write_10.lba += blocks; + scsi->write_10.count -= blocks; + if(!scsi->write_10.count) { + scsi->rx_done = true; + } + return result; + }; break; + default: { + FURI_LOG_W(TAG, "unexpected scsi rx data cmd=%02x", scsi->cmd[0]); + scsi->sk = SCSI_SK_ILLEGAL_REQUEST; + scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + return false; + }; break; + } +} + +bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t cap) { + // FURI_LOG_I(TAG, "TX %02x cap %d", scsi->cmd[0], cap); + if(scsi->tx_done) return false; + switch(scsi->cmd[0]) { + case SCSI_REQUEST_SENSE: { + FURI_LOG_I(TAG, "SCSI_REQUEST_SENSE"); + if(cap < 18) return false; + memset(data, 0, cap); + data[0] = 0x70; // fixed format sense data + data[1] = 0; // obsolete + data[2] = scsi->sk; // sense key + data[3] = 0; // information + data[4] = 0; // information + data[5] = 0; // information + data[6] = 0; // information + data[7] = 10; // additional sense length (len-8) + data[8] = 0; // command specific information + data[9] = 0; // command specific information + data[10] = 0; // command specific information + data[11] = 0; // command specific information + data[12] = scsi->asc; // additional sense code + data[13] = 0; // additional sense code qualifier + data[14] = 0; // field replaceable unit code + data[15] = 0; // sense key specific information + data[16] = 0; // sense key specific information + data[17] = 0; // sense key specific information + *len = 18; + scsi->sk = 0; + scsi->asc = 0; + scsi->tx_done = true; + return true; + }; break; + case SCSI_INQUIRY: { + FURI_LOG_I(TAG, "SCSI_INQUIRY"); + if(scsi->cmd_len < 5) return false; + if(cap < 36) return false; + bool evpd = scsi->cmd[1] & 1; + uint8_t page_code = scsi->cmd[2]; + // uint16_t alloc_len = scsi->cmd[3] << 8 | scsi->cmd[4]; + if(evpd) return false; + if(page_code) return false; + data[0] = 0x00; // device type: direct access block device + data[1] = 0x80; // removable: true + data[2] = 0x04; // version + data[3] = 0x02; // response data format + data[4] = 31; // additional length (len - 5) + data[5] = 0; // flags + data[6] = 0; // flags + data[7] = 0; // flags + memcpy(data + 8, "Flipper ", 8); // vendor id + memcpy(data + 16, "Mass Storage ", 16); // product id + memcpy(data + 32, "0001", 4); // product revision level + *len = 36; + scsi->tx_done = true; + return true; + }; break; + case SCSI_READ_CAPACITY_6: { + FURI_LOG_I(TAG, "SCSI_READ_CAPACITY_6"); + if(cap < 8) return false; + uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx); + uint32_t block_size = SCSI_BLOCK_SIZE; + data[0] = (n_blocks - 1) >> 24; + data[1] = (n_blocks - 1) >> 16; + data[2] = (n_blocks - 1) >> 8; + data[3] = (n_blocks - 1); + data[4] = block_size >> 24; + data[5] = block_size >> 16; + data[6] = block_size >> 8; + data[7] = block_size; + *len = 8; + scsi->tx_done = true; + return true; + }; break; + case SCSI_MODE_SENSE_6: { + FURI_LOG_I(TAG, "SCSI_MODE_SENSE_6 %ld", cap); + if(cap < 4) return false; + data[0] = 3; // mode data length (len - 1) + data[1] = 0; // medium type + data[2] = 0; // device-specific parameter + data[3] = 0; // block descriptor length + *len = 4; + scsi->tx_done = true; + return true; + }; break; + case SCSI_READ_10: { + uint32_t block_size = SCSI_BLOCK_SIZE; + bool result = + scsi->fn.read(scsi->fn.ctx, scsi->read_10.lba, scsi->read_10.count, data, len, cap); + *len -= *len % block_size; + uint16_t blocks = *len / block_size; + scsi->read_10.lba += blocks; + scsi->read_10.count -= blocks; + if(!scsi->read_10.count) { + scsi->tx_done = true; + } + return result; + }; break; + default: { + FURI_LOG_W(TAG, "unexpected scsi tx data cmd=%02x", scsi->cmd[0]); + scsi->sk = SCSI_SK_ILLEGAL_REQUEST; + scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + return false; + }; break; + } +} + +bool scsi_cmd_end(SCSISession* scsi) { + // FURI_LOG_I(TAG, "END %02x", scsi->cmd[0]); + uint8_t* cmd = scsi->cmd; + uint8_t len = scsi->cmd_len; + scsi->cmd = NULL; + scsi->cmd_len = 0; + switch(cmd[0]) { + case SCSI_WRITE_10: + return scsi->rx_done; + + case SCSI_REQUEST_SENSE: + case SCSI_INQUIRY: + case SCSI_READ_CAPACITY_6: + case SCSI_MODE_SENSE_6: + case SCSI_READ_10: + return scsi->tx_done; + + case SCSI_TEST_UNIT_READY: { + FURI_LOG_I(TAG, "SCSI_TEST_UNIT_READY"); + return true; + }; break; + case SCSI_PREVENT_MEDIUM_REMOVAL: { + if(len < 6) return false; + bool prevent = cmd[5]; + FURI_LOG_I(TAG, "SCSI_PREVENT_MEDIUM_REMOVAL prevent=%d", prevent); + return !prevent; + }; break; + case SCSI_START_STOP_UNIT: { + if(len < 6) return false; + bool eject = (cmd[4] & 2) != 0; + bool start = (cmd[4] & 1) != 0; + FURI_LOG_I(TAG, "SCSI_START_STOP_UNIT eject=%d start=%d", eject, start); + if(eject) { + scsi->fn.eject(scsi->fn.ctx); + } + return true; + }; break; + default: { + FURI_LOG_W(TAG, "unexpected scsi cmd=%02x", cmd[0]); + scsi->sk = SCSI_SK_ILLEGAL_REQUEST; + scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + return false; + }; break; + } +} diff --git a/applications/external/mass_storage/helpers/mass_storage_scsi.h b/applications/external/mass_storage/helpers/mass_storage_scsi.h new file mode 100644 index 000000000..ec366ef3a --- /dev/null +++ b/applications/external/mass_storage/helpers/mass_storage_scsi.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#define SCSI_BLOCK_SIZE (0x200) + +#define SCSI_SK_ILLEGAL_REQUEST (5) + +#define SCSI_ASC_INVALID_COMMAND_OPERATION_CODE (0x20) +#define SCSI_ASC_LBA_OOB (0x21) +#define SCSI_ASC_INVALID_FIELD_IN_CDB (0x24) + +typedef struct { + void* ctx; + bool (*read)( + void* ctx, + uint32_t lba, + uint16_t count, + uint8_t* out, + uint32_t* out_len, + uint32_t out_cap); + bool (*write)(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, uint32_t len); + uint32_t (*num_blocks)(void* ctx); + void (*eject)(void* ctx); +} SCSIDeviceFunc; + +typedef struct { + SCSIDeviceFunc fn; + + uint8_t* cmd; + uint8_t cmd_len; + bool rx_done; + bool tx_done; + + uint8_t sk; // sense key + uint8_t asc; // additional sense code + + // command-specific data + // valid from cmd_start to cmd_end + union { + struct { + uint16_t count; + uint32_t lba; + } read_10; // SCSI_READ_10 + + struct { + uint16_t count; + uint32_t lba; + } write_10; // SCSI_WRITE_10 + }; +} SCSISession; + +bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len); +bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len); +bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t cap); +bool scsi_cmd_end(SCSISession* scsi); \ No newline at end of file diff --git a/applications/external/mass_storage/helpers/mass_storage_usb.c b/applications/external/mass_storage/helpers/mass_storage_usb.c new file mode 100644 index 000000000..6b8a7cf69 --- /dev/null +++ b/applications/external/mass_storage/helpers/mass_storage_usb.c @@ -0,0 +1,468 @@ +#include "mass_storage_usb.h" + +#include + +#define TAG "MassStorageUsb" + +#define USB_MSC_RX_EP (0x01) +#define USB_MSC_TX_EP (0x82) + +#define USB_MSC_RX_EP_SIZE (64) +#define USB_MSC_TX_EP_SIZE (64) + +#define USB_MSC_BOT_GET_MAX_LUN (0xFE) +#define USB_MSC_BOT_RESET (0xFF) + +#define CBW_SIG (0x43425355) +#define CBW_FLAGS_DEVICE_TO_HOST (0x80) + +#define CSW_SIG (0x53425355) +#define CSW_STATUS_OK (0) +#define CSW_STATUS_NOK (1) +#define CSW_STATUS_PHASE_ERROR (2) + +// must be SCSI_BLOCK_SIZE aligned +// larger than 0x10000 exceeds size_t, storage_file_* ops fail +#define USB_MSC_BUF_MAX (0x10000 - SCSI_BLOCK_SIZE) + +static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); + +typedef enum { + EventExit = 1 << 0, + EventReset = 1 << 1, + EventRxTx = 1 << 2, + + EventAll = EventExit | EventReset | EventRxTx, +} MassStorageEvent; + +typedef struct { + uint32_t sig; + uint32_t tag; + uint32_t len; + uint8_t flags; + uint8_t lun; + uint8_t cmd_len; + uint8_t cmd[16]; +} __attribute__((packed)) CBW; +typedef struct { + uint32_t sig; + uint32_t tag; + uint32_t residue; + uint8_t status; +} __attribute__((packed)) CSW; + +struct MassStorageUsb { + FuriHalUsbInterface usb; + FuriHalUsbInterface* usb_prev; + + FuriThread* thread; + usbd_device* dev; + SCSIDeviceFunc fn; +}; + +static int32_t mass_thread_worker(void* context) { + MassStorageUsb* mass = context; + usbd_device* dev = mass->dev; + SCSISession scsi = { + .fn = mass->fn, + }; + CBW cbw = {0}; + CSW csw = {0}; + uint8_t* buf = NULL; + uint32_t buf_len = 0, buf_cap = 0, buf_sent = 0; + enum { + StateReadCBW, + StateReadData, + StateWriteData, + StateBuildCSW, + StateWriteCSW, + } state = StateReadCBW; + while(true) { + uint32_t flags = osThreadFlagsWait(EventAll, osFlagsWaitAny, osWaitForever); + if(flags & EventExit) { + FURI_LOG_I(TAG, "exit"); + free(buf); + return 0; + } + if(flags & EventReset) { + FURI_LOG_I(TAG, "reset"); + scsi.sk = 0; + scsi.asc = 0; + memset(&cbw, 0, sizeof(cbw)); + memset(&csw, 0, sizeof(csw)); + free(buf); + buf = NULL; + buf_len = buf_cap = buf_sent = 0; + state = StateReadCBW; + } + if(flags & EventRxTx) do { + switch(state) { + case StateReadCBW: { + // FURI_LOG_I(TAG, "StateReadCBW"); + int32_t len = usbd_ep_read(dev, USB_MSC_RX_EP, &cbw, sizeof(cbw)); + if(len <= 0) { + // FURI_LOG_I(TAG, "cbw not ready"); + break; + } + if(len != sizeof(cbw) || cbw.sig != CBW_SIG) { + FURI_LOG_W(TAG, "bad cbw sig=%08lx", cbw.sig); + usbd_ep_stall(dev, USB_MSC_TX_EP); + usbd_ep_stall(dev, USB_MSC_RX_EP); + continue; + } + if(!scsi_cmd_start(&scsi, cbw.cmd, cbw.cmd_len)) { + FURI_LOG_W(TAG, "bad cmd"); + usbd_ep_stall(dev, USB_MSC_RX_EP); + csw.sig = CSW_SIG; + csw.tag = cbw.tag; + csw.status = CSW_STATUS_NOK; + state = StateWriteCSW; + continue; + } + if(cbw.flags & CBW_FLAGS_DEVICE_TO_HOST) { + buf_len = 0; + buf_sent = 0; + state = StateWriteData; + } else { + buf_len = 0; + state = StateReadData; + } + continue; + }; break; + case StateReadData: { + // FURI_LOG_I(TAG, "StateReadData %d/%d", buf_len, cbw.len); + if(!cbw.len) { + state = StateBuildCSW; + continue; + } + uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX); + if(buf_clamp > buf_cap) { + // FURI_LOG_I(TAG, "growing buf %d -> %d", buf_cap, buf_clamp); + free(buf); + buf_cap = buf_clamp; + buf = malloc(buf_cap); + } + if(buf_len < buf_clamp) { + int32_t len = + usbd_ep_read(dev, USB_MSC_RX_EP, buf + buf_len, buf_clamp - buf_len); + if(len < 0) { + // FURI_LOG_I(TAG, "rx not ready %d", len); + break; + } + // FURI_LOG_I(TAG, "clamp %ld len %d", buf_clamp, len); + buf_len += len; + } + if(buf_len == buf_clamp) { + if(!scsi_cmd_rx_data(&scsi, buf, buf_len)) { + FURI_LOG_W(TAG, "short rx"); + usbd_ep_stall(dev, USB_MSC_RX_EP); + csw.sig = CSW_SIG; + csw.tag = cbw.tag; + csw.status = CSW_STATUS_NOK; + csw.residue = cbw.len; + state = StateWriteCSW; + continue; + } + cbw.len -= buf_len; + buf_len = 0; + } + continue; + }; break; + case StateWriteData: { + // FURI_LOG_I(TAG, "StateWriteData %d", cbw.len); + if(!cbw.len) { + state = StateBuildCSW; + continue; + } + uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX); + if(buf_clamp > buf_cap) { + // FURI_LOG_I(TAG, "growing buf %d -> %d", buf_cap, buf_clamp); + free(buf); + buf_cap = buf_clamp; + buf = malloc(buf_cap); + } + if(!buf_len && !scsi_cmd_tx_data(&scsi, buf, &buf_len, buf_clamp)) { + FURI_LOG_W(TAG, "short tx"); + // usbd_ep_stall(dev, USB_MSC_TX_EP); + state = StateBuildCSW; + continue; + } + int32_t len = usbd_ep_write( + dev, + USB_MSC_TX_EP, + buf + buf_sent, + MIN(USB_MSC_TX_EP_SIZE, buf_len - buf_sent)); + if(len < 0) { + // FURI_LOG_I(TAG, "tx not ready %d", len); + break; + } + buf_sent += len; + if(buf_sent == buf_len) { + cbw.len -= buf_len; + buf_len = 0; + buf_sent = 0; + } + continue; + }; break; + case StateBuildCSW: { + // FURI_LOG_I(TAG, "StateBuildCSW"); + csw.sig = CSW_SIG; + csw.tag = cbw.tag; + if(scsi_cmd_end(&scsi)) { + csw.status = CSW_STATUS_OK; + } else { + csw.status = CSW_STATUS_NOK; + } + csw.residue = cbw.len; + state = StateWriteCSW; + continue; + }; break; + case StateWriteCSW: { + // FURI_LOG_I(TAG, "StateWriteCSW"); + if(csw.status) { + FURI_LOG_W( + TAG, + "csw sig=%08lx tag=%08lx residue=%08lx status=%02x", + csw.sig, + csw.tag, + csw.residue, + csw.status); + } + int32_t len = usbd_ep_write(dev, USB_MSC_TX_EP, &csw, sizeof(csw)); + if(len < 0) { + // FURI_LOG_I(TAG, "csw not ready"); + break; + } + if(len != sizeof(csw)) { + FURI_LOG_W(TAG, "bad csw write %d", len); + usbd_ep_stall(dev, USB_MSC_TX_EP); + break; + } + memset(&cbw, 0, sizeof(cbw)); + memset(&csw, 0, sizeof(csw)); + state = StateReadCBW; + continue; + }; break; + } + break; + } while(true); + } +} + +// needed in usb_deinit, usb_suspend, usb_rxtx_ep_callback, usb_control, +// where if_ctx isn't passed +static MassStorageUsb* mass_cur = NULL; + +static void usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + MassStorageUsb* mass = ctx; + mass_cur = mass; + mass->dev = dev; + + usbd_reg_config(dev, usb_ep_config); + usbd_reg_control(dev, usb_control); + usbd_connect(dev, true); + + mass->thread = furi_thread_alloc(); + furi_thread_set_name(mass->thread, "MassStorageUsb"); + furi_thread_set_stack_size(mass->thread, 1024); + furi_thread_set_context(mass->thread, ctx); + furi_thread_set_callback(mass->thread, mass_thread_worker); + furi_thread_start(mass->thread); +} + +static void usb_deinit(usbd_device* dev) { + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + MassStorageUsb* mass = mass_cur; + if(!mass || mass->dev != dev) { + FURI_LOG_E(TAG, "deinit mass_cur leak"); + return; + } + mass_cur = NULL; + + furi_assert(mass->thread); + osThreadFlagsSet(furi_thread_get_thread_id(mass->thread), EventExit); + furi_thread_join(mass->thread); + furi_thread_free(mass->thread); + mass->thread = NULL; + + free(mass->usb.str_prod_descr); + mass->usb.str_prod_descr = NULL; + free(mass->usb.str_serial_descr); + mass->usb.str_serial_descr = NULL; + free(mass); +} + +static void usb_wakeup(usbd_device* dev) { +} + +static void usb_suspend(usbd_device* dev) { + MassStorageUsb* mass = mass_cur; + if(!mass || mass->dev != dev) return; + osThreadFlagsSet(furi_thread_get_thread_id(mass->thread), EventReset); +} + +static void usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + MassStorageUsb* mass = mass_cur; + if(!mass || mass->dev != dev) return; + osThreadFlagsSet(furi_thread_get_thread_id(mass->thread), EventRxTx); +} + +static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case 0: // deconfig + usbd_ep_deconfig(dev, USB_MSC_RX_EP); + usbd_ep_deconfig(dev, USB_MSC_TX_EP); + usbd_reg_endpoint(dev, USB_MSC_RX_EP, NULL); + usbd_reg_endpoint(dev, USB_MSC_TX_EP, NULL); + return usbd_ack; + case 1: // config + usbd_ep_config( + dev, USB_MSC_RX_EP, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, USB_MSC_RX_EP_SIZE); + usbd_ep_config( + dev, USB_MSC_TX_EP, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, USB_MSC_TX_EP_SIZE); + usbd_reg_endpoint(dev, USB_MSC_RX_EP, usb_rxtx_ep_callback); + usbd_reg_endpoint(dev, USB_MSC_TX_EP, usb_rxtx_ep_callback); + return usbd_ack; + } + return usbd_fail; +} + +static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) != + (USB_REQ_INTERFACE | USB_REQ_CLASS)) { + return usbd_fail; + } + switch(req->bRequest) { + case USB_MSC_BOT_GET_MAX_LUN: { + static uint8_t max_lun = 0; + dev->status.data_ptr = &max_lun; + dev->status.data_count = 1; + return usbd_ack; + }; break; + case USB_MSC_BOT_RESET: { + MassStorageUsb* mass = mass_cur; + if(!mass || mass->dev != dev) return usbd_fail; + osThreadFlagsSet(furi_thread_get_thread_id(mass->thread), EventReset); + return usbd_ack; + }; break; + } + return usbd_fail; +} + +static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc."); + +struct MassStorageDescriptor { + struct usb_config_descriptor config; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor ep_rx; + struct usb_endpoint_descriptor ep_tx; +} __attribute__((packed)); + +static const struct usb_device_descriptor usb_mass_dev_descr = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = USB_SUBCLASS_NONE, + .bDeviceProtocol = USB_PROTO_NONE, + .bMaxPacketSize0 = 8, // USB_EP0_SIZE + .idVendor = 0x0483, + .idProduct = 0x5720, + .bcdDevice = VERSION_BCD(1, 0, 0), + .iManufacturer = 1, // UsbDevManuf + .iProduct = 2, // UsbDevProduct + .iSerialNumber = 3, // UsbDevSerial + .bNumConfigurations = 1, +}; + +static const struct MassStorageDescriptor usb_mass_cfg_descr = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct MassStorageDescriptor), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED, + .bMaxPower = USB_CFG_POWER_MA(100), + }, + .intf = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, // scsi transparent + .bInterfaceProtocol = 0x50, // bulk only + .iInterface = NO_DESCRIPTOR, + }, + .ep_rx = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MSC_RX_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MSC_RX_EP_SIZE, + .bInterval = 0, + }, + .ep_tx = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = USB_MSC_TX_EP, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = USB_MSC_TX_EP_SIZE, + .bInterval = 0, + }, +}; + +MassStorageUsb* mass_storage_usb_start(const char* filename, SCSIDeviceFunc fn) { + MassStorageUsb* mass = malloc(sizeof(MassStorageUsb)); + mass->usb_prev = furi_hal_usb_get_config(); + mass->usb.init = usb_init; + mass->usb.deinit = usb_deinit; + mass->usb.wakeup = usb_wakeup; + mass->usb.suspend = usb_suspend; + mass->usb.dev_descr = (struct usb_device_descriptor*)&usb_mass_dev_descr; + mass->usb.str_manuf_descr = (void*)&dev_manuf_desc; + mass->usb.str_prod_descr = NULL; + mass->usb.str_serial_descr = NULL; + mass->usb.cfg_descr = (void*)&usb_mass_cfg_descr; + + const char* name = furi_hal_version_get_device_name_ptr(); + if(!name) name = "Flipper Zero"; + size_t len = strlen(name); + struct usb_string_descriptor* str_prod_descr = malloc(len * 2 + 2); + str_prod_descr->bLength = len * 2 + 2; + str_prod_descr->bDescriptorType = USB_DTYPE_STRING; + for(uint8_t i = 0; i < len; i++) str_prod_descr->wString[i] = name[i]; + mass->usb.str_prod_descr = str_prod_descr; + + len = strlen(filename); + struct usb_string_descriptor* str_serial_descr = malloc(len * 2 + 2); + str_serial_descr->bLength = len * 2 + 2; + str_serial_descr->bDescriptorType = USB_DTYPE_STRING; + for(uint8_t i = 0; i < len; i++) str_serial_descr->wString[i] = filename[i]; + mass->usb.str_serial_descr = str_serial_descr; + + mass->fn = fn; + if(!furi_hal_usb_set_config(&mass->usb, mass)) { + FURI_LOG_E(TAG, "USB locked, cannot start Mass Storage"); + free(mass->usb.str_prod_descr); + free(mass->usb.str_serial_descr); + free(mass); + return NULL; + } + return mass; +} + +void mass_storage_usb_stop(MassStorageUsb* mass) { + furi_hal_usb_set_config(mass->usb_prev, NULL); + // freed by usb_deinit asynchronously from usb thread +} diff --git a/applications/external/mass_storage/helpers/mass_storage_usb.h b/applications/external/mass_storage/helpers/mass_storage_usb.h new file mode 100644 index 000000000..0f370f98e --- /dev/null +++ b/applications/external/mass_storage/helpers/mass_storage_usb.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include "mass_storage_scsi.h" + +typedef struct MassStorageUsb MassStorageUsb; + +MassStorageUsb* mass_storage_usb_start(const char* filename, SCSIDeviceFunc fn); +void mass_storage_usb_stop(MassStorageUsb* mass); diff --git a/applications/external/mass_storage/mass_storage_app.c b/applications/external/mass_storage/mass_storage_app.c new file mode 100644 index 000000000..48159730f --- /dev/null +++ b/applications/external/mass_storage/mass_storage_app.c @@ -0,0 +1,124 @@ +#include "mass_storage_app_i.h" +#include +#include +#include + +static bool mass_storage_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + MassStorageApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool mass_storage_app_back_event_callback(void* context) { + furi_assert(context); + MassStorageApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void mass_storage_app_tick_event_callback(void* context) { + furi_assert(context); + MassStorageApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static bool mass_storage_check_assets(Storage* fs_api) { + File* dir = storage_file_alloc(fs_api); + bool ret = false; + + if(storage_dir_open(dir, MASS_STORAGE_APP_PATH_FOLDER)) { + ret = true; + } + + storage_dir_close(dir); + storage_file_free(dir); + + return ret; +} + +MassStorageApp* mass_storage_app_alloc(char* arg) { + MassStorageApp* app = malloc(sizeof(MassStorageApp)); + memset(app, 0, sizeof(MassStorageApp)); + + if(arg != NULL) { + string_t filename; + string_init_set(filename, arg); + if(string_start_with_str_p(filename, MASS_STORAGE_APP_PATH_FOLDER)) { + string_right(filename, strlen(MASS_STORAGE_APP_PATH_FOLDER) + 1); + } + strncpy(app->file_name, string_get_cstr(filename), MASS_STORAGE_FILE_NAME_LEN); + string_clear(filename); + } + + app->gui = furi_record_open("gui"); + app->fs_api = furi_record_open("storage"); + app->notifications = furi_record_open("notification"); + app->dialogs = furi_record_open("dialogs"); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&mass_storage_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, mass_storage_app_tick_event_callback, 500); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, mass_storage_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, mass_storage_app_back_event_callback); + + // Custom Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, MassStorageAppViewError, widget_get_view(app->widget)); + + app->mass_storage_view = mass_storage_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + MassStorageAppViewWork, + mass_storage_get_view(app->mass_storage_view)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + if(*app->file_name != '\0') { + scene_manager_next_scene(app->scene_manager, MassStorageSceneWork); + } else if(mass_storage_check_assets(app->fs_api)) { + scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect); + } else { + scene_manager_next_scene(app->scene_manager, MassStorageSceneError); + } + + return app; +} + +void mass_storage_app_free(MassStorageApp* app) { + furi_assert(app); + + // Views + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewFileSelect); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork); + mass_storage_free(app->mass_storage_view); + + // Custom Widget + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewError); + widget_free(app->widget); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close("gui"); + furi_record_close("storage"); + furi_record_close("notification"); + furi_record_close("dialogs"); + + free(app); +} + +int32_t mass_storage_app(void* p) { + MassStorageApp* mass_storage_app = mass_storage_app_alloc((char*)p); + view_dispatcher_run(mass_storage_app->view_dispatcher); + mass_storage_app_free(mass_storage_app); + return 0; +} diff --git a/applications/external/mass_storage/mass_storage_app.h b/applications/external/mass_storage/mass_storage_app.h new file mode 100644 index 000000000..820ad2b6c --- /dev/null +++ b/applications/external/mass_storage/mass_storage_app.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MassStorageApp MassStorageApp; + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/mass_storage/mass_storage_app_i.h b/applications/external/mass_storage/mass_storage_app_i.h new file mode 100644 index 000000000..e30b7b628 --- /dev/null +++ b/applications/external/mass_storage/mass_storage_app_i.h @@ -0,0 +1,44 @@ +#pragma once + +#include "mass_storage_app.h" +#include "scenes/mass_storage_scene.h" +#include "helpers/mass_storage_usb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/mass_storage_view.h" + +#define MASS_STORAGE_APP_PATH_FOLDER "/any/mass_storage" +// #define MASS_STORAGE_APP_EXTENSION ".iso" +#define MASS_STORAGE_FILE_NAME_LEN 40 + +struct MassStorageApp { + Gui* gui; + Storage* fs_api; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + + char file_name[MASS_STORAGE_FILE_NAME_LEN + 1]; + File* file; + MassStorage* mass_storage_view; + + osMutexId_t usb_mutex; + MassStorageUsb* usb; +}; + +typedef enum { + MassStorageAppViewError, + MassStorageAppViewFileSelect, + MassStorageAppViewWork, +} MassStorageAppView; diff --git a/applications/external/mass_storage/scenes/mass_storage_scene.c b/applications/external/mass_storage/scenes/mass_storage_scene.c new file mode 100644 index 000000000..bab24ca40 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene.c @@ -0,0 +1,30 @@ +#include "mass_storage_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const mass_storage_scene_on_enter_handlers[])(void*) = { +#include "mass_storage_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const mass_storage_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "mass_storage_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const mass_storage_scene_on_exit_handlers[])(void* context) = { +#include "mass_storage_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers mass_storage_scene_handlers = { + .on_enter_handlers = mass_storage_scene_on_enter_handlers, + .on_event_handlers = mass_storage_scene_on_event_handlers, + .on_exit_handlers = mass_storage_scene_on_exit_handlers, + .scene_num = MassStorageSceneNum, +}; diff --git a/applications/external/mass_storage/scenes/mass_storage_scene.h b/applications/external/mass_storage/scenes/mass_storage_scene.h new file mode 100644 index 000000000..d43bb4c97 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) MassStorageScene##id, +typedef enum { +#include "mass_storage_scene_config.h" + MassStorageSceneNum, +} MassStorageScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers mass_storage_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "mass_storage_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "mass_storage_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "mass_storage_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_config.h b/applications/external/mass_storage/scenes/mass_storage_scene_config.h new file mode 100644 index 000000000..7104fd852 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(mass_storage, file_select, FileSelect) +ADD_SCENE(mass_storage, work, Work) +ADD_SCENE(mass_storage, error, Error) diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_error.c b/applications/external/mass_storage/scenes/mass_storage_scene_error.c new file mode 100644 index 000000000..998c7d160 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_error.c @@ -0,0 +1,53 @@ +#include "../mass_storage_app_i.h" + +typedef enum { + SubghzCustomEventErrorBack, +} MassStorageCustomEvent; + +static void + mass_storage_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + MassStorageApp* app = context; + + if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event(app->view_dispatcher, SubghzCustomEventErrorBack); + } +} + +void mass_storage_scene_error_on_enter(void* context) { + MassStorageApp* app = context; + + widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); + + widget_add_string_multiline_element( + app->widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card or\napp data found.\nThis app requires a\nmass_storage/\ndirectory."); + + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", mass_storage_scene_error_event_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewError); +} + +bool mass_storage_scene_error_on_event(void* context, SceneManagerEvent event) { + MassStorageApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubghzCustomEventErrorBack) { + view_dispatcher_stop(app->view_dispatcher); + consumed = true; + } + } + return consumed; +} + +void mass_storage_scene_error_on_exit(void* context) { + MassStorageApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_file_select.c b/applications/external/mass_storage/scenes/mass_storage_scene_file_select.c new file mode 100644 index 000000000..ea1177a67 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_file_select.c @@ -0,0 +1,36 @@ +#include "../mass_storage_app_i.h" +#include "furi_hal_power.h" + +static bool mass_storage_file_select(MassStorageApp* mass_storage) { + furi_assert(mass_storage); + + // Input events and views are managed by file_select + bool res = dialog_file_select_show( + mass_storage->dialogs, + MASS_STORAGE_APP_PATH_FOLDER, + "*", + mass_storage->file_name, + sizeof(mass_storage->file_name), + NULL); + return res; +} + +void mass_storage_scene_file_select_on_enter(void* context) { + MassStorageApp* mass_storage = context; + + if(mass_storage_file_select(mass_storage)) { + scene_manager_next_scene(mass_storage->scene_manager, MassStorageSceneWork); + } else { + scene_manager_previous_scene(mass_storage->scene_manager); + view_dispatcher_stop(mass_storage->view_dispatcher); + } +} + +bool mass_storage_scene_file_select_on_event(void* context, SceneManagerEvent event) { + // MassStorageApp* mass_storage = context; + return false; +} + +void mass_storage_scene_file_select_on_exit(void* context) { + // MassStorageApp* mass_storage = context; +} diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_work.c b/applications/external/mass_storage/scenes/mass_storage_scene_work.c new file mode 100644 index 000000000..c3e9104b0 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_work.c @@ -0,0 +1,105 @@ +#include "../mass_storage_app_i.h" +#include "../views/mass_storage_view.h" +#include "../helpers/mass_storage_usb.h" + +#define TAG "MassStorageSceneWork" + +static bool file_read( + void* ctx, + uint32_t lba, + uint16_t count, + uint8_t* out, + uint32_t* out_len, + uint32_t out_cap) { + MassStorageApp* app = ctx; + // FURI_LOG_I(TAG, "file_read lba=%08lx count=%04x out_cap=%04x", lba, count, out_cap); + if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) { + FURI_LOG_W(TAG, "seek failed"); + return false; + } + uint16_t clamp = MIN(out_cap, count * SCSI_BLOCK_SIZE); + *out_len = storage_file_read(app->file, out, clamp); + // FURI_LOG_I(TAG, "%d/%d", *out_len, count * SCSI_BLOCK_SIZE); + return *out_len == clamp; +} + +static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, uint32_t len) { + MassStorageApp* app = ctx; + // FURI_LOG_I(TAG, "file_write lba=%08lx count=%04x len=%04x", lba, count, len); + if(len != count * SCSI_BLOCK_SIZE) { + FURI_LOG_W(TAG, "bad write params count=%d len=%d", count, len); + return false; + } + if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) { + FURI_LOG_W(TAG, "seek failed"); + return false; + } + return storage_file_write(app->file, buf, len) == len; +} + +static uint32_t file_num_blocks(void* ctx) { + MassStorageApp* app = ctx; + return storage_file_size(app->file) / SCSI_BLOCK_SIZE; +} + +static void file_eject(void* ctx) { + MassStorageApp* app = ctx; + FURI_LOG_I(TAG, "EJECT"); + furi_check(osMutexAcquire(app->usb_mutex, osWaitForever) == osOK); + mass_storage_usb_stop(app->usb); + app->usb = NULL; + furi_check(osMutexRelease(app->usb_mutex) == osOK); +} + +bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) { + MassStorageApp* app = context; + if(event.type == SceneManagerEventTypeTick) { + bool ejected; + furi_check(osMutexAcquire(app->usb_mutex, osWaitForever) == osOK); + ejected = app->usb == NULL; + furi_check(osMutexRelease(app->usb_mutex) == osOK); + if(ejected) scene_manager_previous_scene(app->scene_manager); + } + return false; +} + +void mass_storage_scene_work_on_enter(void* context) { + MassStorageApp* app = context; + + app->usb_mutex = osMutexNew(NULL); + + string_t file_name; + string_init(file_name); + + mass_storage_set_file_name(app->mass_storage_view, app->file_name); + string_printf(file_name, "%s/%s", MASS_STORAGE_APP_PATH_FOLDER, app->file_name); + app->file = storage_file_alloc(app->fs_api); + furi_assert(storage_file_open( + app->file, string_get_cstr(file_name), FSAM_READ | FSAM_WRITE, FSOM_OPEN_EXISTING)); + string_clear(file_name); + + SCSIDeviceFunc fn = { + .ctx = app, + .read = file_read, + .write = file_write, + .num_blocks = file_num_blocks, + .eject = file_eject, + }; + app->usb = mass_storage_usb_start(app->file_name, fn); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork); +} + +void mass_storage_scene_work_on_exit(void* context) { + MassStorageApp* app = context; + + furi_assert(osMutexDelete(app->usb_mutex) == osOK); + if(app->usb) { + mass_storage_usb_stop(app->usb); + app->usb = NULL; + } + if(app->file) { + storage_file_free(app->file); + app->file = NULL; + } +} diff --git a/applications/external/mass_storage/views/mass_storage_view.c b/applications/external/mass_storage/views/mass_storage_view.c new file mode 100644 index 000000000..7dc838f4f --- /dev/null +++ b/applications/external/mass_storage/views/mass_storage_view.c @@ -0,0 +1,56 @@ +#include "mass_storage_view.h" +#include + +struct MassStorage { + View* view; +}; + +typedef struct { + char* file_name; +} MassStorageModel; + +static void mass_storage_draw_callback(Canvas* canvas, void* _model) { + MassStorageModel* model = _model; + + string_t disp_str; + string_init_set_str(disp_str, model->file_name); + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 8, string_get_cstr(disp_str)); + string_reset(disp_str); + + canvas_draw_icon(canvas, 40, 20, &I_UsbTree_48x22); + + string_clear(disp_str); +} + +MassStorage* mass_storage_alloc() { + MassStorage* mass_storage = malloc(sizeof(MassStorage)); + + mass_storage->view = view_alloc(); + view_allocate_model(mass_storage->view, ViewModelTypeLocking, sizeof(MassStorageModel)); + view_set_context(mass_storage->view, mass_storage); + view_set_draw_callback(mass_storage->view, mass_storage_draw_callback); + + return mass_storage; +} + +void mass_storage_free(MassStorage* mass_storage) { + furi_assert(mass_storage); + view_free(mass_storage->view); + free(mass_storage); +} + +View* mass_storage_get_view(MassStorage* mass_storage) { + furi_assert(mass_storage); + return mass_storage->view; +} + +void mass_storage_set_file_name(MassStorage* mass_storage, char* name) { + furi_assert(name); + with_view_model( + mass_storage->view, (MassStorageModel * model) { + model->file_name = name; + return true; + }); +} diff --git a/applications/external/mass_storage/views/mass_storage_view.h b/applications/external/mass_storage/views/mass_storage_view.h new file mode 100644 index 000000000..af7b1fb37 --- /dev/null +++ b/applications/external/mass_storage/views/mass_storage_view.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct MassStorage MassStorage; + +MassStorage* mass_storage_alloc(); + +void mass_storage_free(MassStorage* mass_storage); + +View* mass_storage_get_view(MassStorage* mass_storage); + +void mass_storage_set_file_name(MassStorage* mass_storage, char* name); From 40cff8a6030e7740145df7109540d526a6d09e57 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 14 Jun 2023 00:58:18 +0100 Subject: [PATCH 184/370] Refactor mass storage to current flipper api (WIP) --- .../external/mass_storage/application.fam | 13 ++++++++ .../mass_storage/helpers/mass_storage_scsi.c | 2 -- .../mass_storage/helpers/mass_storage_scsi.h | 4 +-- .../mass_storage/helpers/mass_storage_usb.c | 21 ++++++++----- .../external/mass_storage/mass_storage_app.c | 30 +++++++++---------- .../mass_storage/mass_storage_app_i.h | 4 +-- .../scenes/mass_storage_scene_error.c | 1 + .../scenes/mass_storage_scene_file_select.c | 27 ++++++++++++----- .../scenes/mass_storage_scene_work.c | 23 +++++++------- .../mass_storage/views/mass_storage_view.c | 15 ++++------ 10 files changed, 82 insertions(+), 58 deletions(-) create mode 100644 applications/external/mass_storage/application.fam diff --git a/applications/external/mass_storage/application.fam b/applications/external/mass_storage/application.fam new file mode 100644 index 000000000..251deed80 --- /dev/null +++ b/applications/external/mass_storage/application.fam @@ -0,0 +1,13 @@ +App( + appid="mass_storage", + name="USB Mass Storage", + apptype=FlipperAppType.EXTERNAL, + entry_point="mass_storage_app", + cdefines=["APP_MASS_STORAGE"], + requires=["gui"], + stack_size=1 * 1024, + order=2, + # fap_icon="", + fap_category="Misc", + fap_libs=["assets"], +) diff --git a/applications/external/mass_storage/helpers/mass_storage_scsi.c b/applications/external/mass_storage/helpers/mass_storage_scsi.c index d1114c889..995f05076 100644 --- a/applications/external/mass_storage/helpers/mass_storage_scsi.c +++ b/applications/external/mass_storage/helpers/mass_storage_scsi.c @@ -1,7 +1,5 @@ #include "mass_storage_scsi.h" -#include - #define TAG "MassStorageSCSI" #define SCSI_TEST_UNIT_READY (0x00) diff --git a/applications/external/mass_storage/helpers/mass_storage_scsi.h b/applications/external/mass_storage/helpers/mass_storage_scsi.h index ec366ef3a..27a6f6ebb 100644 --- a/applications/external/mass_storage/helpers/mass_storage_scsi.h +++ b/applications/external/mass_storage/helpers/mass_storage_scsi.h @@ -2,7 +2,7 @@ #include -#define SCSI_BLOCK_SIZE (0x200) +#define SCSI_BLOCK_SIZE (0x200u) #define SCSI_SK_ILLEGAL_REQUEST (5) @@ -53,4 +53,4 @@ typedef struct { bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len); bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len); bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t cap); -bool scsi_cmd_end(SCSISession* scsi); \ No newline at end of file +bool scsi_cmd_end(SCSISession* scsi); diff --git a/applications/external/mass_storage/helpers/mass_storage_usb.c b/applications/external/mass_storage/helpers/mass_storage_usb.c index 6b8a7cf69..5a24b5898 100644 --- a/applications/external/mass_storage/helpers/mass_storage_usb.c +++ b/applications/external/mass_storage/helpers/mass_storage_usb.c @@ -8,7 +8,7 @@ #define USB_MSC_TX_EP (0x82) #define USB_MSC_RX_EP_SIZE (64) -#define USB_MSC_TX_EP_SIZE (64) +#define USB_MSC_TX_EP_SIZE (64u) #define USB_MSC_BOT_GET_MAX_LUN (0xFE) #define USB_MSC_BOT_RESET (0xFF) @@ -23,7 +23,7 @@ // must be SCSI_BLOCK_SIZE aligned // larger than 0x10000 exceeds size_t, storage_file_* ops fail -#define USB_MSC_BUF_MAX (0x10000 - SCSI_BLOCK_SIZE) +#define USB_MSC_BUF_MAX (0x10000u - SCSI_BLOCK_SIZE) static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg); static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); @@ -79,7 +79,7 @@ static int32_t mass_thread_worker(void* context) { StateWriteCSW, } state = StateReadCBW; while(true) { - uint32_t flags = osThreadFlagsWait(EventAll, osFlagsWaitAny, osWaitForever); + uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriFlagWaitAny); if(flags & EventExit) { FURI_LOG_I(TAG, "exit"); free(buf); @@ -235,7 +235,7 @@ static int32_t mass_thread_worker(void* context) { break; } if(len != sizeof(csw)) { - FURI_LOG_W(TAG, "bad csw write %d", len); + FURI_LOG_W(TAG, "bad csw write %ld", len); usbd_ep_stall(dev, USB_MSC_TX_EP); break; } @@ -255,6 +255,7 @@ static int32_t mass_thread_worker(void* context) { static MassStorageUsb* mass_cur = NULL; static void usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); MassStorageUsb* mass = ctx; mass_cur = mass; mass->dev = dev; @@ -283,7 +284,7 @@ static void usb_deinit(usbd_device* dev) { mass_cur = NULL; furi_assert(mass->thread); - osThreadFlagsSet(furi_thread_get_thread_id(mass->thread), EventExit); + furi_thread_flags_set(furi_thread_get_id(mass->thread), EventExit); furi_thread_join(mass->thread); furi_thread_free(mass->thread); mass->thread = NULL; @@ -296,18 +297,21 @@ static void usb_deinit(usbd_device* dev) { } static void usb_wakeup(usbd_device* dev) { + UNUSED(dev); } static void usb_suspend(usbd_device* dev) { MassStorageUsb* mass = mass_cur; if(!mass || mass->dev != dev) return; - osThreadFlagsSet(furi_thread_get_thread_id(mass->thread), EventReset); + furi_thread_flags_set(furi_thread_get_id(mass->thread), EventReset); } static void usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(event); + UNUSED(ep); MassStorageUsb* mass = mass_cur; if(!mass || mass->dev != dev) return; - osThreadFlagsSet(furi_thread_get_thread_id(mass->thread), EventRxTx); + furi_thread_flags_set(furi_thread_get_id(mass->thread), EventRxTx); } static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg) { @@ -331,6 +335,7 @@ static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg) { } static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) != (USB_REQ_INTERFACE | USB_REQ_CLASS)) { return usbd_fail; @@ -345,7 +350,7 @@ static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal case USB_MSC_BOT_RESET: { MassStorageUsb* mass = mass_cur; if(!mass || mass->dev != dev) return usbd_fail; - osThreadFlagsSet(furi_thread_get_thread_id(mass->thread), EventReset); + furi_thread_flags_set(furi_thread_get_id(mass->thread), EventReset); return usbd_ack; }; break; } diff --git a/applications/external/mass_storage/mass_storage_app.c b/applications/external/mass_storage/mass_storage_app.c index 48159730f..f07c8aa6c 100644 --- a/applications/external/mass_storage/mass_storage_app.c +++ b/applications/external/mass_storage/mass_storage_app.c @@ -40,19 +40,20 @@ MassStorageApp* mass_storage_app_alloc(char* arg) { memset(app, 0, sizeof(MassStorageApp)); if(arg != NULL) { - string_t filename; - string_init_set(filename, arg); - if(string_start_with_str_p(filename, MASS_STORAGE_APP_PATH_FOLDER)) { - string_right(filename, strlen(MASS_STORAGE_APP_PATH_FOLDER) + 1); + FuriString* filename = furi_string_alloc_set(arg); + if(furi_string_start_with_str(filename, MASS_STORAGE_APP_PATH_FOLDER)) { + furi_string_right(filename, strlen(MASS_STORAGE_APP_PATH_FOLDER) + 1); } - strncpy(app->file_name, string_get_cstr(filename), MASS_STORAGE_FILE_NAME_LEN); - string_clear(filename); + strlcpy(app->file_name, furi_string_get_cstr(filename), MASS_STORAGE_FILE_NAME_LEN); + furi_string_free(filename); } - app->gui = furi_record_open("gui"); - app->fs_api = furi_record_open("storage"); - app->notifications = furi_record_open("notification"); - app->dialogs = furi_record_open("dialogs"); + app->gui = furi_record_open(RECORD_GUI); + app->fs_api = furi_record_open(RECORD_STORAGE); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->dialogs = furi_record_open(RECORD_DIALOGS); + + storage_simply_mkdir(app->fs_api, MASS_STORAGE_APP_PATH_FOLDER); app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -95,7 +96,6 @@ void mass_storage_app_free(MassStorageApp* app) { furi_assert(app); // Views - view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewFileSelect); view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork); mass_storage_free(app->mass_storage_view); @@ -108,10 +108,10 @@ void mass_storage_app_free(MassStorageApp* app) { scene_manager_free(app->scene_manager); // Close records - furi_record_close("gui"); - furi_record_close("storage"); - furi_record_close("notification"); - furi_record_close("dialogs"); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); free(app); } diff --git a/applications/external/mass_storage/mass_storage_app_i.h b/applications/external/mass_storage/mass_storage_app_i.h index e30b7b628..7fc829aba 100644 --- a/applications/external/mass_storage/mass_storage_app_i.h +++ b/applications/external/mass_storage/mass_storage_app_i.h @@ -29,11 +29,11 @@ struct MassStorageApp { DialogsApp* dialogs; Widget* widget; - char file_name[MASS_STORAGE_FILE_NAME_LEN + 1]; + char file_name[MASS_STORAGE_FILE_NAME_LEN]; File* file; MassStorage* mass_storage_view; - osMutexId_t usb_mutex; + FuriMutex* usb_mutex; MassStorageUsb* usb; }; diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_error.c b/applications/external/mass_storage/scenes/mass_storage_scene_error.c index 998c7d160..daef7ccd1 100644 --- a/applications/external/mass_storage/scenes/mass_storage_scene_error.c +++ b/applications/external/mass_storage/scenes/mass_storage_scene_error.c @@ -1,4 +1,5 @@ #include "../mass_storage_app_i.h" +#include typedef enum { SubghzCustomEventErrorBack, diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_file_select.c b/applications/external/mass_storage/scenes/mass_storage_scene_file_select.c index ea1177a67..36c8c4692 100644 --- a/applications/external/mass_storage/scenes/mass_storage_scene_file_select.c +++ b/applications/external/mass_storage/scenes/mass_storage_scene_file_select.c @@ -4,14 +4,22 @@ static bool mass_storage_file_select(MassStorageApp* mass_storage) { furi_assert(mass_storage); - // Input events and views are managed by file_select - bool res = dialog_file_select_show( - mass_storage->dialogs, - MASS_STORAGE_APP_PATH_FOLDER, - "*", - mass_storage->file_name, - sizeof(mass_storage->file_name), - NULL); + FuriString* file_path = furi_string_alloc(); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, "*", NULL); + browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER; + furi_string_set(file_path, MASS_STORAGE_APP_PATH_FOLDER); + + bool res = + dialog_file_browser_show(mass_storage->dialogs, file_path, file_path, &browser_options); + + if(res) { + strlcpy( + mass_storage->file_name, + furi_string_get_cstr(file_path), + sizeof(mass_storage->file_name)); + } + furi_string_free(file_path); return res; } @@ -27,10 +35,13 @@ void mass_storage_scene_file_select_on_enter(void* context) { } bool mass_storage_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); // MassStorageApp* mass_storage = context; return false; } void mass_storage_scene_file_select_on_exit(void* context) { + UNUSED(context); // MassStorageApp* mass_storage = context; } diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_work.c b/applications/external/mass_storage/scenes/mass_storage_scene_work.c index c3e9104b0..4112931ea 100644 --- a/applications/external/mass_storage/scenes/mass_storage_scene_work.c +++ b/applications/external/mass_storage/scenes/mass_storage_scene_work.c @@ -27,7 +27,7 @@ static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, ui MassStorageApp* app = ctx; // FURI_LOG_I(TAG, "file_write lba=%08lx count=%04x len=%04x", lba, count, len); if(len != count * SCSI_BLOCK_SIZE) { - FURI_LOG_W(TAG, "bad write params count=%d len=%d", count, len); + FURI_LOG_W(TAG, "bad write params count=%d len=%ld", count, len); return false; } if(!storage_file_seek(app->file, lba * SCSI_BLOCK_SIZE, true)) { @@ -45,19 +45,19 @@ static uint32_t file_num_blocks(void* ctx) { static void file_eject(void* ctx) { MassStorageApp* app = ctx; FURI_LOG_I(TAG, "EJECT"); - furi_check(osMutexAcquire(app->usb_mutex, osWaitForever) == osOK); + furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk); mass_storage_usb_stop(app->usb); app->usb = NULL; - furi_check(osMutexRelease(app->usb_mutex) == osOK); + furi_check(furi_mutex_release(app->usb_mutex) == FuriStatusOk); } bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) { MassStorageApp* app = context; if(event.type == SceneManagerEventTypeTick) { bool ejected; - furi_check(osMutexAcquire(app->usb_mutex, osWaitForever) == osOK); + furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk); ejected = app->usb == NULL; - furi_check(osMutexRelease(app->usb_mutex) == osOK); + furi_check(furi_mutex_release(app->usb_mutex) == FuriStatusOk); if(ejected) scene_manager_previous_scene(app->scene_manager); } return false; @@ -66,17 +66,16 @@ bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) { void mass_storage_scene_work_on_enter(void* context) { MassStorageApp* app = context; - app->usb_mutex = osMutexNew(NULL); + app->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - string_t file_name; - string_init(file_name); + FuriString* file_name = furi_string_alloc(); mass_storage_set_file_name(app->mass_storage_view, app->file_name); - string_printf(file_name, "%s/%s", MASS_STORAGE_APP_PATH_FOLDER, app->file_name); + furi_string_printf(file_name, "%s/%s", MASS_STORAGE_APP_PATH_FOLDER, app->file_name); app->file = storage_file_alloc(app->fs_api); furi_assert(storage_file_open( - app->file, string_get_cstr(file_name), FSAM_READ | FSAM_WRITE, FSOM_OPEN_EXISTING)); - string_clear(file_name); + app->file, furi_string_get_cstr(file_name), FSAM_READ | FSAM_WRITE, FSOM_OPEN_EXISTING)); + furi_string_free(file_name); SCSIDeviceFunc fn = { .ctx = app, @@ -93,7 +92,7 @@ void mass_storage_scene_work_on_enter(void* context) { void mass_storage_scene_work_on_exit(void* context) { MassStorageApp* app = context; - furi_assert(osMutexDelete(app->usb_mutex) == osOK); + furi_mutex_free(app->usb_mutex); if(app->usb) { mass_storage_usb_stop(app->usb); app->usb = NULL; diff --git a/applications/external/mass_storage/views/mass_storage_view.c b/applications/external/mass_storage/views/mass_storage_view.c index 7dc838f4f..7cf0fa66c 100644 --- a/applications/external/mass_storage/views/mass_storage_view.c +++ b/applications/external/mass_storage/views/mass_storage_view.c @@ -1,5 +1,6 @@ #include "mass_storage_view.h" #include +#include struct MassStorage { View* view; @@ -12,16 +13,15 @@ typedef struct { static void mass_storage_draw_callback(Canvas* canvas, void* _model) { MassStorageModel* model = _model; - string_t disp_str; - string_init_set_str(disp_str, model->file_name); + FuriString* disp_str = furi_string_alloc_set(model->file_name); elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 8, string_get_cstr(disp_str)); - string_reset(disp_str); + canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); canvas_draw_icon(canvas, 40, 20, &I_UsbTree_48x22); - string_clear(disp_str); + furi_string_free(disp_str); } MassStorage* mass_storage_alloc() { @@ -49,8 +49,5 @@ View* mass_storage_get_view(MassStorage* mass_storage) { void mass_storage_set_file_name(MassStorage* mass_storage, char* name) { furi_assert(name); with_view_model( - mass_storage->view, (MassStorageModel * model) { - model->file_name = name; - return true; - }); + mass_storage->view, MassStorageModel * model, { model->file_name = name; }, true); } From 44023851f7349b6ae9ca9f9bd9228d795a7e04c0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 14 Jun 2023 00:59:34 +0100 Subject: [PATCH 185/370] Fix string handling in tuning fork --- .../external/tuning_fork/tuning_fork.c | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/applications/external/tuning_fork/tuning_fork.c b/applications/external/tuning_fork/tuning_fork.c index 6912d1780..5547fb670 100644 --- a/applications/external/tuning_fork/tuning_fork.c +++ b/applications/external/tuning_fork/tuning_fork.c @@ -138,8 +138,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { TuningForkState* tuning_fork_state = ctx; furi_mutex_acquire(tuning_fork_state->mutex, FuriWaitForever); - string_t tempStr; - string_init(tempStr); + FuriString* tempStr = furi_string_alloc(); canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -148,23 +147,24 @@ static void render_callback(Canvas* const canvas, void* ctx) { if(tuning_fork_state->page == Tunings) { char tuningLabel[20]; current_tuning_label(tuning_fork_state, tuningLabel); - string_printf(tempStr, "< %s >", tuningLabel); + furi_string_printf(tempStr, "< %s >", tuningLabel); canvas_draw_str_aligned( - canvas, 64, 28, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + canvas, 64, 28, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); } else { char tuningLabel[20]; current_tuning_label(tuning_fork_state, tuningLabel); - string_printf(tempStr, "%s", tuningLabel); - canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + furi_string_printf(tempStr, "%s", tuningLabel); + canvas_draw_str_aligned( + canvas, 64, 8, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); char tuningNoteLabel[20]; current_tuning_note_label(tuning_fork_state, tuningNoteLabel); - string_printf(tempStr, "< %s >", tuningNoteLabel); + furi_string_printf(tempStr, "< %s >", tuningNoteLabel); canvas_draw_str_aligned( - canvas, 64, 24, AlignCenter, AlignCenter, string_get_cstr(tempStr)); - string_reset(tempStr); + canvas, 64, 24, AlignCenter, AlignCenter, furi_string_get_cstr(tempStr)); + furi_string_reset(tempStr); } canvas_set_font(canvas, FontSecondary); @@ -184,7 +184,7 @@ static void render_callback(Canvas* const canvas, void* ctx) { elements_progress_bar(canvas, 8, 36, 112, tuning_fork_state->volume); } - string_clear(tempStr); + furi_string_free(tempStr); furi_mutex_release(tuning_fork_state->mutex); } From c7b8ca276f877476430da810894ec9ddade93bec Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 14 Jun 2023 01:04:42 +0100 Subject: [PATCH 186/370] Add flipbip app --- applications/external/flipbip/LICENSE | 21 + applications/external/flipbip/application.fam | 22 + applications/external/flipbip/flipbip.c | 205 ++ applications/external/flipbip/flipbip.h | 99 + .../external/flipbip/flipbip_10px.png | Bin 0 -> 183 bytes .../flipbip/helpers/flipbip_custom_event.h | 16 + .../external/flipbip/helpers/flipbip_file.c | 309 +++ .../external/flipbip/helpers/flipbip_file.h | 23 + .../external/flipbip/helpers/flipbip_haptic.c | 35 + .../external/flipbip/helpers/flipbip_haptic.h | 7 + .../external/flipbip/helpers/flipbip_led.c | 39 + .../external/flipbip/helpers/flipbip_led.h | 2 + .../flipbip/helpers/flipbip_speaker.c | 27 + .../flipbip/helpers/flipbip_speaker.h | 4 + .../external/flipbip/helpers/flipbip_string.c | 129 ++ .../external/flipbip/helpers/flipbip_string.h | 12 + .../external/flipbip/icons/Auth_62x31.png | Bin 0 -> 3761 bytes .../flipbip/icons/ButtonCenter_7x7.png | Bin 0 -> 1440 bytes .../flipbip/icons/ButtonDown_10x5.png | Bin 0 -> 6223 bytes .../flipbip/icons/ButtonLeftSmall_3x5.png | Bin 0 -> 1741 bytes .../external/flipbip/icons/ButtonLeft_4x7.png | Bin 0 -> 1415 bytes .../flipbip/icons/ButtonRightSmall_3x5.png | Bin 0 -> 1738 bytes .../flipbip/icons/ButtonRight_4x7.png | Bin 0 -> 1839 bytes .../external/flipbip/icons/ButtonUp_10x5.png | Bin 0 -> 6233 bytes .../external/flipbip/icons/Keychain_39x36.png | Bin 0 -> 3775 bytes .../external/flipbip/lib/crypto/AUTHORS | 2 + .../external/flipbip/lib/crypto/CONTRIBUTORS | 16 + .../external/flipbip/lib/crypto/LICENSE | 22 + .../external/flipbip/lib/crypto/Makefile | 187 ++ .../external/flipbip/lib/crypto/address.c | 90 + .../external/flipbip/lib/crypto/address.h | 39 + .../external/flipbip/lib/crypto/aes/aes.h | 256 +++ .../flipbip/lib/crypto/aes/aes_modes.c | 932 +++++++++ .../flipbip/lib/crypto/aes/aescrypt.c | 349 ++++ .../external/flipbip/lib/crypto/aes/aeskey.c | 662 ++++++ .../external/flipbip/lib/crypto/aes/aesopt.h | 793 +++++++ .../external/flipbip/lib/crypto/aes/aestab.c | 403 ++++ .../external/flipbip/lib/crypto/aes/aestab.h | 173 ++ .../external/flipbip/lib/crypto/aes/aestst.c | 189 ++ .../external/flipbip/lib/crypto/aes/aestst.h | 85 + .../external/flipbip/lib/crypto/base32.c | 241 +++ .../external/flipbip/lib/crypto/base32.h | 42 + .../external/flipbip/lib/crypto/base58.c | 219 ++ .../external/flipbip/lib/crypto/base58.h | 48 + .../external/flipbip/lib/crypto/bignum.c | 1834 +++++++++++++++++ .../external/flipbip/lib/crypto/bignum.h | 195 ++ .../external/flipbip/lib/crypto/bip32.c | 885 ++++++++ .../external/flipbip/lib/crypto/bip32.h | 202 ++ .../external/flipbip/lib/crypto/bip39.c | 288 +++ .../external/flipbip/lib/crypto/bip39.h | 61 + .../flipbip/lib/crypto/bip39_english.c | 283 +++ .../external/flipbip/lib/crypto/blake256.c | 222 ++ .../external/flipbip/lib/crypto/blake256.h | 53 + .../flipbip/lib/crypto/blake2_common.h | 46 + .../external/flipbip/lib/crypto/blake2b.c | 313 +++ .../external/flipbip/lib/crypto/blake2b.h | 49 + .../external/flipbip/lib/crypto/blake2s.c | 310 +++ .../external/flipbip/lib/crypto/blake2s.h | 49 + .../external/flipbip/lib/crypto/byte_order.h | 57 + .../external/flipbip/lib/crypto/cardano.c | 307 +++ .../external/flipbip/lib/crypto/cardano.h | 60 + .../external/flipbip/lib/crypto/cash_addr.c | 188 ++ .../external/flipbip/lib/crypto/cash_addr.h | 74 + .../lib/crypto/chacha20poly1305/LICENSE | 21 + .../chacha20poly1305/chacha20poly1305.c | 63 + .../chacha20poly1305/chacha20poly1305.h | 22 + .../crypto/chacha20poly1305/chacha_merged.c | 251 +++ .../crypto/chacha20poly1305/ecrypt_config.h | 316 +++ .../crypto/chacha20poly1305/ecrypt_machine.h | 49 + .../crypto/chacha20poly1305/ecrypt_portable.h | 246 +++ .../lib/crypto/chacha20poly1305/ecrypt_sync.h | 282 +++ .../crypto/chacha20poly1305/ecrypt_types.h | 53 + .../crypto/chacha20poly1305/poly1305_donna.c | 208 ++ .../crypto/chacha20poly1305/poly1305_donna.h | 23 + .../chacha20poly1305/poly1305_donna_32.h | 252 +++ .../lib/crypto/chacha20poly1305/rfc7539.c | 46 + .../lib/crypto/chacha20poly1305/rfc7539.h | 10 + .../external/flipbip/lib/crypto/chacha_drbg.c | 131 ++ .../external/flipbip/lib/crypto/chacha_drbg.h | 59 + .../external/flipbip/lib/crypto/check_mem.h | 34 + .../external/flipbip/lib/crypto/curves.c | 39 + .../external/flipbip/lib/crypto/curves.h | 42 + .../external/flipbip/lib/crypto/ecdsa.c | 1284 ++++++++++++ .../external/flipbip/lib/crypto/ecdsa.h | 167 ++ .../ed25519_donna/curve25519_donna_32bit.c | 953 +++++++++ .../ed25519_donna/curve25519_donna_32bit.h | 79 + .../ed25519_donna/curve25519_donna_helpers.c | 66 + .../ed25519_donna/curve25519_donna_helpers.h | 22 + .../curve25519_donna_scalarmult_base.c | 70 + .../curve25519_donna_scalarmult_base.h | 11 + .../lib/crypto/ed25519_donna/ed25519.c | 336 +++ .../lib/crypto/ed25519_donna/ed25519.h | 78 + .../lib/crypto/ed25519_donna/ed25519_donna.h | 52 + .../ed25519_donna_32bit_tables.c | 1049 ++++++++++ .../ed25519_donna_32bit_tables.h | 17 + .../ed25519_donna_basepoint_table.c | 1796 ++++++++++++++++ .../ed25519_donna_basepoint_table.h | 2 + .../ed25519_donna/ed25519_donna_impl_base.c | 829 ++++++++ .../ed25519_donna/ed25519_donna_impl_base.h | 127 ++ .../ed25519_donna/ed25519_donna_portable.h | 22 + .../ed25519_donna/ed25519_hash_custom.h | 23 + .../ed25519_hash_custom_keccak.h | 29 + .../ed25519_donna/ed25519_hash_custom_sha3.h | 23 + .../lib/crypto/ed25519_donna/ed25519_keccak.c | 14 + .../lib/crypto/ed25519_donna/ed25519_keccak.h | 38 + .../lib/crypto/ed25519_donna/ed25519_sha3.c | 8 + .../lib/crypto/ed25519_donna/ed25519_sha3.h | 32 + .../crypto/ed25519_donna/modm_donna_32bit.c | 800 +++++++ .../crypto/ed25519_donna/modm_donna_32bit.h | 87 + .../external/flipbip/lib/crypto/groestl.c | 755 +++++++ .../external/flipbip/lib/crypto/groestl.h | 95 + .../flipbip/lib/crypto/groestl_internal.h | 461 +++++ .../external/flipbip/lib/crypto/hasher.c | 164 ++ .../external/flipbip/lib/crypto/hasher.h | 85 + .../external/flipbip/lib/crypto/hmac.c | 186 ++ .../external/flipbip/lib/crypto/hmac.h | 70 + .../external/flipbip/lib/crypto/hmac_drbg.c | 141 ++ .../external/flipbip/lib/crypto/hmac_drbg.h | 51 + .../external/flipbip/lib/crypto/memzero.c | 74 + .../external/flipbip/lib/crypto/memzero.h | 8 + .../flipbip/lib/crypto/monero/base58.c | 297 +++ .../flipbip/lib/crypto/monero/base58.h | 60 + .../flipbip/lib/crypto/monero/int_util.h | 81 + .../flipbip/lib/crypto/monero/monero.h | 24 + .../flipbip/lib/crypto/monero/serialize.c | 57 + .../flipbip/lib/crypto/monero/serialize.h | 19 + .../external/flipbip/lib/crypto/monero/xmr.c | 202 ++ .../external/flipbip/lib/crypto/monero/xmr.h | 93 + .../external/flipbip/lib/crypto/nem.c | 603 ++++++ .../external/flipbip/lib/crypto/nem.h | 216 ++ .../external/flipbip/lib/crypto/nist256p1.c | 114 + .../external/flipbip/lib/crypto/nist256p1.h | 35 + .../flipbip/lib/crypto/nist256p1.table | 1664 +++++++++++++++ .../external/flipbip/lib/crypto/options.h | 94 + .../external/flipbip/lib/crypto/pbkdf2.c | 195 ++ .../external/flipbip/lib/crypto/pbkdf2.h | 82 + .../external/flipbip/lib/crypto/rand.c | 102 + .../external/flipbip/lib/crypto/rand.h | 37 + .../external/flipbip/lib/crypto/rc4.c | 56 + .../external/flipbip/lib/crypto/rc4.h | 37 + .../external/flipbip/lib/crypto/rfc6979.c | 65 + .../external/flipbip/lib/crypto/rfc6979.h | 44 + .../external/flipbip/lib/crypto/ripemd160.c | 330 +++ .../external/flipbip/lib/crypto/ripemd160.h | 20 + .../external/flipbip/lib/crypto/script.c | 64 + .../external/flipbip/lib/crypto/script.h | 30 + .../external/flipbip/lib/crypto/secp256k1.c | 135 ++ .../external/flipbip/lib/crypto/secp256k1.h | 38 + .../flipbip/lib/crypto/secp256k1.table | 1664 +++++++++++++++ .../external/flipbip/lib/crypto/segwit_addr.c | 225 ++ .../external/flipbip/lib/crypto/segwit_addr.h | 97 + .../external/flipbip/lib/crypto/setup.py | 39 + .../external/flipbip/lib/crypto/sha2.c | 1252 +++++++++++ .../external/flipbip/lib/crypto/sha2.h | 92 + .../external/flipbip/lib/crypto/sha3.c | 392 ++++ .../external/flipbip/lib/crypto/sha3.h | 88 + .../external/flipbip/lib/crypto/shamir.c | 338 +++ .../external/flipbip/lib/crypto/shamir.h | 73 + .../external/flipbip/lib/crypto/slip39.c | 150 ++ .../external/flipbip/lib/crypto/slip39.h | 39 + .../flipbip/lib/crypto/slip39_wordlist.h | 1203 +++++++++++ .../external/flipbip/scenes/flipbip_scene.c | 30 + .../external/flipbip/scenes/flipbip_scene.h | 29 + .../flipbip/scenes/flipbip_scene_config.h | 4 + .../flipbip/scenes/flipbip_scene_menu.c | 130 ++ .../flipbip/scenes/flipbip_scene_scene_1.c | 50 + .../flipbip/scenes/flipbip_scene_settings.c | 141 ++ .../scenes/flipbip_scene_startscreen.c | 55 + .../external/flipbip/views/flipbip_scene_1.c | 723 +++++++ .../external/flipbip/views/flipbip_scene_1.h | 19 + .../flipbip/views/flipbip_startscreen.c | 130 ++ .../flipbip/views/flipbip_startscreen.h | 19 + 172 files changed, 35201 insertions(+) create mode 100644 applications/external/flipbip/LICENSE create mode 100644 applications/external/flipbip/application.fam create mode 100644 applications/external/flipbip/flipbip.c create mode 100644 applications/external/flipbip/flipbip.h create mode 100644 applications/external/flipbip/flipbip_10px.png create mode 100644 applications/external/flipbip/helpers/flipbip_custom_event.h create mode 100644 applications/external/flipbip/helpers/flipbip_file.c create mode 100644 applications/external/flipbip/helpers/flipbip_file.h create mode 100644 applications/external/flipbip/helpers/flipbip_haptic.c create mode 100644 applications/external/flipbip/helpers/flipbip_haptic.h create mode 100644 applications/external/flipbip/helpers/flipbip_led.c create mode 100644 applications/external/flipbip/helpers/flipbip_led.h create mode 100644 applications/external/flipbip/helpers/flipbip_speaker.c create mode 100644 applications/external/flipbip/helpers/flipbip_speaker.h create mode 100644 applications/external/flipbip/helpers/flipbip_string.c create mode 100644 applications/external/flipbip/helpers/flipbip_string.h create mode 100644 applications/external/flipbip/icons/Auth_62x31.png create mode 100644 applications/external/flipbip/icons/ButtonCenter_7x7.png create mode 100644 applications/external/flipbip/icons/ButtonDown_10x5.png create mode 100644 applications/external/flipbip/icons/ButtonLeftSmall_3x5.png create mode 100644 applications/external/flipbip/icons/ButtonLeft_4x7.png create mode 100644 applications/external/flipbip/icons/ButtonRightSmall_3x5.png create mode 100644 applications/external/flipbip/icons/ButtonRight_4x7.png create mode 100644 applications/external/flipbip/icons/ButtonUp_10x5.png create mode 100644 applications/external/flipbip/icons/Keychain_39x36.png create mode 100644 applications/external/flipbip/lib/crypto/AUTHORS create mode 100644 applications/external/flipbip/lib/crypto/CONTRIBUTORS create mode 100644 applications/external/flipbip/lib/crypto/LICENSE create mode 100644 applications/external/flipbip/lib/crypto/Makefile create mode 100644 applications/external/flipbip/lib/crypto/address.c create mode 100644 applications/external/flipbip/lib/crypto/address.h create mode 100644 applications/external/flipbip/lib/crypto/aes/aes.h create mode 100644 applications/external/flipbip/lib/crypto/aes/aes_modes.c create mode 100644 applications/external/flipbip/lib/crypto/aes/aescrypt.c create mode 100644 applications/external/flipbip/lib/crypto/aes/aeskey.c create mode 100644 applications/external/flipbip/lib/crypto/aes/aesopt.h create mode 100644 applications/external/flipbip/lib/crypto/aes/aestab.c create mode 100644 applications/external/flipbip/lib/crypto/aes/aestab.h create mode 100644 applications/external/flipbip/lib/crypto/aes/aestst.c create mode 100644 applications/external/flipbip/lib/crypto/aes/aestst.h create mode 100644 applications/external/flipbip/lib/crypto/base32.c create mode 100644 applications/external/flipbip/lib/crypto/base32.h create mode 100644 applications/external/flipbip/lib/crypto/base58.c create mode 100644 applications/external/flipbip/lib/crypto/base58.h create mode 100644 applications/external/flipbip/lib/crypto/bignum.c create mode 100644 applications/external/flipbip/lib/crypto/bignum.h create mode 100644 applications/external/flipbip/lib/crypto/bip32.c create mode 100644 applications/external/flipbip/lib/crypto/bip32.h create mode 100644 applications/external/flipbip/lib/crypto/bip39.c create mode 100644 applications/external/flipbip/lib/crypto/bip39.h create mode 100644 applications/external/flipbip/lib/crypto/bip39_english.c create mode 100644 applications/external/flipbip/lib/crypto/blake256.c create mode 100644 applications/external/flipbip/lib/crypto/blake256.h create mode 100644 applications/external/flipbip/lib/crypto/blake2_common.h create mode 100644 applications/external/flipbip/lib/crypto/blake2b.c create mode 100644 applications/external/flipbip/lib/crypto/blake2b.h create mode 100644 applications/external/flipbip/lib/crypto/blake2s.c create mode 100644 applications/external/flipbip/lib/crypto/blake2s.h create mode 100644 applications/external/flipbip/lib/crypto/byte_order.h create mode 100644 applications/external/flipbip/lib/crypto/cardano.c create mode 100644 applications/external/flipbip/lib/crypto/cardano.h create mode 100644 applications/external/flipbip/lib/crypto/cash_addr.c create mode 100644 applications/external/flipbip/lib/crypto/cash_addr.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/LICENSE create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.c create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/chacha_merged.c create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_config.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_machine.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_portable.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_sync.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_types.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.c create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna_32.h create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.c create mode 100644 applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.h create mode 100644 applications/external/flipbip/lib/crypto/chacha_drbg.c create mode 100644 applications/external/flipbip/lib/crypto/chacha_drbg.h create mode 100644 applications/external/flipbip/lib/crypto/check_mem.h create mode 100644 applications/external/flipbip/lib/crypto/curves.c create mode 100644 applications/external/flipbip/lib/crypto/curves.h create mode 100644 applications/external/flipbip/lib/crypto/ecdsa.c create mode 100644 applications/external/flipbip/lib/crypto/ecdsa.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_portable.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_keccak.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_sha3.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.h create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.c create mode 100644 applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.h create mode 100644 applications/external/flipbip/lib/crypto/groestl.c create mode 100644 applications/external/flipbip/lib/crypto/groestl.h create mode 100644 applications/external/flipbip/lib/crypto/groestl_internal.h create mode 100644 applications/external/flipbip/lib/crypto/hasher.c create mode 100644 applications/external/flipbip/lib/crypto/hasher.h create mode 100644 applications/external/flipbip/lib/crypto/hmac.c create mode 100644 applications/external/flipbip/lib/crypto/hmac.h create mode 100644 applications/external/flipbip/lib/crypto/hmac_drbg.c create mode 100644 applications/external/flipbip/lib/crypto/hmac_drbg.h create mode 100644 applications/external/flipbip/lib/crypto/memzero.c create mode 100644 applications/external/flipbip/lib/crypto/memzero.h create mode 100644 applications/external/flipbip/lib/crypto/monero/base58.c create mode 100644 applications/external/flipbip/lib/crypto/monero/base58.h create mode 100644 applications/external/flipbip/lib/crypto/monero/int_util.h create mode 100644 applications/external/flipbip/lib/crypto/monero/monero.h create mode 100644 applications/external/flipbip/lib/crypto/monero/serialize.c create mode 100644 applications/external/flipbip/lib/crypto/monero/serialize.h create mode 100644 applications/external/flipbip/lib/crypto/monero/xmr.c create mode 100644 applications/external/flipbip/lib/crypto/monero/xmr.h create mode 100644 applications/external/flipbip/lib/crypto/nem.c create mode 100644 applications/external/flipbip/lib/crypto/nem.h create mode 100644 applications/external/flipbip/lib/crypto/nist256p1.c create mode 100644 applications/external/flipbip/lib/crypto/nist256p1.h create mode 100644 applications/external/flipbip/lib/crypto/nist256p1.table create mode 100644 applications/external/flipbip/lib/crypto/options.h create mode 100644 applications/external/flipbip/lib/crypto/pbkdf2.c create mode 100644 applications/external/flipbip/lib/crypto/pbkdf2.h create mode 100644 applications/external/flipbip/lib/crypto/rand.c create mode 100644 applications/external/flipbip/lib/crypto/rand.h create mode 100644 applications/external/flipbip/lib/crypto/rc4.c create mode 100644 applications/external/flipbip/lib/crypto/rc4.h create mode 100644 applications/external/flipbip/lib/crypto/rfc6979.c create mode 100644 applications/external/flipbip/lib/crypto/rfc6979.h create mode 100644 applications/external/flipbip/lib/crypto/ripemd160.c create mode 100644 applications/external/flipbip/lib/crypto/ripemd160.h create mode 100644 applications/external/flipbip/lib/crypto/script.c create mode 100644 applications/external/flipbip/lib/crypto/script.h create mode 100644 applications/external/flipbip/lib/crypto/secp256k1.c create mode 100644 applications/external/flipbip/lib/crypto/secp256k1.h create mode 100644 applications/external/flipbip/lib/crypto/secp256k1.table create mode 100644 applications/external/flipbip/lib/crypto/segwit_addr.c create mode 100644 applications/external/flipbip/lib/crypto/segwit_addr.h create mode 100644 applications/external/flipbip/lib/crypto/setup.py create mode 100644 applications/external/flipbip/lib/crypto/sha2.c create mode 100644 applications/external/flipbip/lib/crypto/sha2.h create mode 100644 applications/external/flipbip/lib/crypto/sha3.c create mode 100644 applications/external/flipbip/lib/crypto/sha3.h create mode 100644 applications/external/flipbip/lib/crypto/shamir.c create mode 100644 applications/external/flipbip/lib/crypto/shamir.h create mode 100644 applications/external/flipbip/lib/crypto/slip39.c create mode 100644 applications/external/flipbip/lib/crypto/slip39.h create mode 100644 applications/external/flipbip/lib/crypto/slip39_wordlist.h create mode 100644 applications/external/flipbip/scenes/flipbip_scene.c create mode 100644 applications/external/flipbip/scenes/flipbip_scene.h create mode 100644 applications/external/flipbip/scenes/flipbip_scene_config.h create mode 100644 applications/external/flipbip/scenes/flipbip_scene_menu.c create mode 100644 applications/external/flipbip/scenes/flipbip_scene_scene_1.c create mode 100644 applications/external/flipbip/scenes/flipbip_scene_settings.c create mode 100644 applications/external/flipbip/scenes/flipbip_scene_startscreen.c create mode 100644 applications/external/flipbip/views/flipbip_scene_1.c create mode 100644 applications/external/flipbip/views/flipbip_scene_1.h create mode 100644 applications/external/flipbip/views/flipbip_startscreen.c create mode 100644 applications/external/flipbip/views/flipbip_startscreen.h diff --git a/applications/external/flipbip/LICENSE b/applications/external/flipbip/LICENSE new file mode 100644 index 000000000..61361ecc7 --- /dev/null +++ b/applications/external/flipbip/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Struan Clark + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/flipbip/application.fam b/applications/external/flipbip/application.fam new file mode 100644 index 000000000..ca340b22b --- /dev/null +++ b/applications/external/flipbip/application.fam @@ -0,0 +1,22 @@ +App( + appid="flipbip", + name="FlipBIP Crypto Tool", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipbip_app", + requires=[ + "gui", + ], + stack_size=3 * 1024, + order=10, + fap_icon="flipbip_10px.png", + fap_icon_assets="icons", + fap_private_libs=[ + Lib( + name="crypto", + ), + ], + fap_category="Tools", + fap_description="Crypto toolkit for Flipper", + fap_author="Struan Clark (xtruan)", + fap_weburl="https://github.com/xtruan/FlipBIP", +) diff --git a/applications/external/flipbip/flipbip.c b/applications/external/flipbip/flipbip.c new file mode 100644 index 000000000..1a5e5a983 --- /dev/null +++ b/applications/external/flipbip/flipbip.c @@ -0,0 +1,205 @@ +#pragma GCC optimize("-Os") + +#include "flipbip.h" +#include "helpers/flipbip_file.h" +#include "helpers/flipbip_haptic.h" +// From: lib/crypto +#include +#include + +bool flipbip_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + FlipBip* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +void flipbip_tick_event_callback(void* context) { + furi_assert(context); + FlipBip* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +//leave app if back button pressed +bool flipbip_navigation_event_callback(void* context) { + furi_assert(context); + FlipBip* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void text_input_callback(void* context) { + furi_assert(context); + FlipBip* app = context; + bool handled = false; + + // check that there is text in the input + if(strlen(app->input_text) > 0) { + if(app->input_state == FlipBipTextInputPassphrase) { + if(app->passphrase == FlipBipPassphraseOn) { + strcpy(app->passphrase_text, app->input_text); + } + // clear input text + memzero(app->input_text, TEXT_BUFFER_SIZE); + // reset input state + app->input_state = FlipBipTextInputDefault; + handled = true; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdSettings); + } else if(app->input_state == FlipBipTextInputMnemonic) { + if(app->import_from_mnemonic == 1) { + strcpy(app->import_mnemonic_text, app->input_text); + + int status = FlipBipStatusSuccess; + // Check if the mnemonic is valid + if(mnemonic_check(app->import_mnemonic_text) == 0) + status = FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error + // Save the mnemonic to persistent storage + else if(!flipbip_save_file_secure(app->import_mnemonic_text)) + status = FlipBipStatusSaveError; // 12 = save error + + if(status == FlipBipStatusSuccess) { + //notification_message(app->notification, &sequence_blink_cyan_100); + flipbip_play_happy_bump(app); + } else { + //notification_message(app->notification, &sequence_blink_red_100); + flipbip_play_long_bump(app); + } + + memzero(app->import_mnemonic_text, TEXT_BUFFER_SIZE); + } + // clear input text + memzero(app->input_text, TEXT_BUFFER_SIZE); + // reset input state + app->input_state = FlipBipTextInputDefault; + handled = true; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); + } + } + + if(!handled) { + // clear input text + memzero(app->input_text, TEXT_BUFFER_SIZE); + // reset input state + app->input_state = FlipBipTextInputDefault; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); + } +} + +FlipBip* flipbip_app_alloc() { + FlipBip* app = malloc(sizeof(FlipBip)); + app->gui = furi_record_open(RECORD_GUI); + app->notification = furi_record_open(RECORD_NOTIFICATION); + + //Turn backlight on, believe me this makes testing your app easier + notification_message(app->notification, &sequence_display_backlight_on); + + //Scene additions + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&flipbip_scene_handlers, app); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, flipbip_navigation_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, flipbip_tick_event_callback, 100); + view_dispatcher_set_custom_event_callback(app->view_dispatcher, flipbip_custom_event_callback); + app->submenu = submenu_alloc(); + + // Settings + app->haptic = FlipBipHapticOn; + app->led = FlipBipLedOn; + app->bip39_strength = FlipBipStrength256; // 256 bits (24 words) + app->passphrase = FlipBipPassphraseOff; + + // Main menu + app->bip44_coin = FlipBipCoinBTC0; // 0 (BTC) + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + + // Text input + app->input_state = FlipBipTextInputDefault; + + view_dispatcher_add_view( + app->view_dispatcher, FlipBipViewIdMenu, submenu_get_view(app->submenu)); + app->flipbip_startscreen = flipbip_startscreen_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipBipViewIdStartscreen, + flipbip_startscreen_get_view(app->flipbip_startscreen)); + app->flipbip_scene_1 = flipbip_scene_1_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FlipBipViewIdScene1, flipbip_scene_1_get_view(app->flipbip_scene_1)); + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipBipViewIdSettings, + variable_item_list_get_view(app->variable_item_list)); + + app->text_input = text_input_alloc(); + text_input_set_result_callback( + app->text_input, + text_input_callback, + (void*)app, + app->input_text, + TEXT_BUFFER_SIZE, + //clear default text + true); + text_input_set_header_text(app->text_input, "Input"); + view_dispatcher_add_view( + app->view_dispatcher, FlipBipViewIdTextInput, text_input_get_view(app->text_input)); + + //End Scene Additions + + return app; +} + +void flipbip_app_free(FlipBip* app) { + furi_assert(app); + + // Scene manager + scene_manager_free(app->scene_manager); + + text_input_free(app->text_input); + + // View Dispatcher + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdMenu); + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdScene1); + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdSettings); + view_dispatcher_remove_view(app->view_dispatcher, FlipBipViewIdTextInput); + submenu_free(app->submenu); + + view_dispatcher_free(app->view_dispatcher); + furi_record_close(RECORD_GUI); + + app->gui = NULL; + app->notification = NULL; + + //Remove whatever is left + memzero(app, sizeof(FlipBip)); + free(app); +} + +int32_t flipbip_app(void* p) { + UNUSED(p); + FlipBip* app = flipbip_app_alloc(); + + // Disabled because causes exit on custom firmwares such as RM + /*if(!furi_hal_region_is_provisioned()) { + flipbip_app_free(app); + return 1; + }*/ + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene( + app->scene_manager, FlipBipSceneStartscreen); //Start with start screen + //scene_manager_next_scene(app->scene_manager, FlipBipSceneMenu); //if you want to directly start with Menu + + furi_hal_power_suppress_charge_enter(); + + view_dispatcher_run(app->view_dispatcher); + + furi_hal_power_suppress_charge_exit(); + flipbip_app_free(app); + + return 0; +} diff --git a/applications/external/flipbip/flipbip.h b/applications/external/flipbip/flipbip.h new file mode 100644 index 000000000..6f84a1736 --- /dev/null +++ b/applications/external/flipbip/flipbip.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/flipbip_scene.h" +#include "views/flipbip_startscreen.h" +#include "views/flipbip_scene_1.h" + +#define FLIPBIP_VERSION "v0.0.9" + +#define COIN_BTC 0 +#define COIN_DOGE 3 +#define COIN_ETH 60 + +#define TEXT_BUFFER_SIZE 256 + +typedef struct { + Gui* gui; + NotificationApp* notification; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + SceneManager* scene_manager; + VariableItemList* variable_item_list; + TextInput* text_input; + FlipBipStartscreen* flipbip_startscreen; + FlipBipScene1* flipbip_scene_1; + // Settings options + int haptic; + int led; + int bip39_strength; + int passphrase; + // Main menu options + int bip44_coin; + int overwrite_saved_seed; + int import_from_mnemonic; + // Text input + int input_state; + char passphrase_text[TEXT_BUFFER_SIZE]; + char import_mnemonic_text[TEXT_BUFFER_SIZE]; + char input_text[TEXT_BUFFER_SIZE]; +} FlipBip; + +typedef enum { + FlipBipViewIdStartscreen, + FlipBipViewIdMenu, + FlipBipViewIdScene1, + FlipBipViewIdSettings, + FlipBipViewIdTextInput, +} FlipBipViewId; + +typedef enum { + FlipBipHapticOff, + FlipBipHapticOn, +} FlipBipHapticState; + +typedef enum { + FlipBipLedOff, + FlipBipLedOn, +} FlipBipLedState; + +typedef enum { + FlipBipStrength128, + FlipBipStrength192, + FlipBipStrength256, +} FlipBipStrengthState; + +typedef enum { + FlipBipPassphraseOff, + FlipBipPassphraseOn, +} FlipBipPassphraseState; + +typedef enum { + FlipBipCoinBTC0, + FlipBipCoinETH60, + FlipBipCoinDOGE3, +} FlipBipCoin; + +typedef enum { + FlipBipTextInputDefault, + FlipBipTextInputPassphrase, + FlipBipTextInputMnemonic +} FlipBipTextInputState; + +typedef enum { + FlipBipStatusSuccess = 0, + FlipBipStatusReturn = 10, + FlipBipStatusLoadError = 11, + FlipBipStatusSaveError = 12, + FlipBipStatusMnemonicCheckError = 13, +} FlipBipStatus; diff --git a/applications/external/flipbip/flipbip_10px.png b/applications/external/flipbip/flipbip_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..173079046552b36f7fc8637351de9e12266ed23c GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>VM%xNb!1@J z*w6hZkrl}2EbxddW?&Fg1z|?dAe9e5K_5>S#}JL+sS_>=F$i$5oc{mc_?pih1>l-mCcd{ncAqmp>oA bKf`phgYkO@?}Ph+<}!G?`njxgN@xNA*DpTh literal 0 HcmV?d00001 diff --git a/applications/external/flipbip/helpers/flipbip_custom_event.h b/applications/external/flipbip/helpers/flipbip_custom_event.h new file mode 100644 index 000000000..2dbaf5112 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_custom_event.h @@ -0,0 +1,16 @@ +#pragma once + +typedef enum { + FlipBipCustomEventStartscreenUp, + FlipBipCustomEventStartscreenDown, + FlipBipCustomEventStartscreenLeft, + FlipBipCustomEventStartscreenRight, + FlipBipCustomEventStartscreenOk, + FlipBipCustomEventStartscreenBack, + FlipBipCustomEventScene1Up, + FlipBipCustomEventScene1Down, + FlipBipCustomEventScene1Left, + FlipBipCustomEventScene1Right, + FlipBipCustomEventScene1Ok, + FlipBipCustomEventScene1Back, +} FlipBipCustomEvent; \ No newline at end of file diff --git a/applications/external/flipbip/helpers/flipbip_file.c b/applications/external/flipbip/helpers/flipbip_file.c new file mode 100644 index 000000000..3b61b6b95 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_file.c @@ -0,0 +1,309 @@ +#include "flipbip_file.h" +#include +#include +#include +#include "../helpers/flipbip_string.h" +// From: lib/crypto +#include +#include + +// #define FLIPBIP_APP_BASE_FOLDER APP_DATA_PATH("flipbip") +#define FLIPBIP_APP_BASE_FOLDER EXT_PATH("apps_data/flipbip") +#define FLIPBIP_APP_BASE_FOLDER_PATH(path) FLIPBIP_APP_BASE_FOLDER "/" path +#define FLIPBIP_DAT_FILE_NAME ".flipbip.dat" +// #define FLIPBIP_DAT_FILE_NAME ".flipbip.dat.txt" +#define FLIPBIP_DAT_FILE_NAME_BAK ".flipbip.dat.bak" +#define FLIPBIP_KEY_FILE_NAME ".flipbip.key" +// #define FLIPBIP_KEY_FILE_NAME ".flipbip.key.txt" +#define FLIPBIP_KEY_FILE_NAME_BAK ".flipbip.key.bak" +#define FLIPBIP_DAT_PATH FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_DAT_FILE_NAME) +#define FLIPBIP_DAT_PATH_BAK FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_DAT_FILE_NAME_BAK) +#define FLIPBIP_KEY_PATH FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_KEY_FILE_NAME) +#define FLIPBIP_KEY_PATH_BAK FLIPBIP_APP_BASE_FOLDER_PATH(FLIPBIP_KEY_FILE_NAME_BAK) + +const char* TEXT_QRFILE = "Filetype: QRCode\n" + "Version: 0\n" + "Message: "; // 37 chars + 1 null +#define FILE_HLEN 4 +#define FILE_KLEN 256 +#define FILE_SLEN 512 +#define FILE_MAX_PATH_LEN 48 +#define FILE_MAX_QRFILE_CONTENT 90 +const char* FILE_HSTR = "fb01"; +const char* FILE_K1 = "fb0131d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a" + "baefe6d9ceb651842260e0d1e05e3b90d15e7d5ffaaabc0207bf200a117793a2"; + +bool flipbip_load_file(char* settings, const FlipBipFile file_type, const char* file_name) { + bool ret = false; + const char* path; + if(file_type == FlipBipFileKey) { + path = FLIPBIP_KEY_PATH; + } else if(file_type == FlipBipFileDat) { + path = FLIPBIP_DAT_PATH; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22 + strcpy(path_buf + strlen(path_buf), "/"); + strcpy(path_buf + strlen(path_buf), file_name); + path = path_buf; + } + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + + File* settings_file = storage_file_alloc(fs_api); + if(storage_file_open(settings_file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + char chr; + int i = 0; + while((storage_file_read(settings_file, &chr, 1) == 1) && + !storage_file_eof(settings_file) && !isspace(chr)) { + settings[i] = chr; + i++; + } + ret = true; + } else { + memzero(settings, strlen(settings)); + settings[0] = '\0'; + ret = false; + } + storage_file_close(settings_file); + storage_file_free(settings_file); + furi_record_close(RECORD_STORAGE); + + if(strlen(settings) > 0) { + Storage* fs_api = furi_record_open(RECORD_STORAGE); + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat(fs_api, path, &layout_file_info); + furi_record_close(RECORD_STORAGE); + if(file_check_err != FSE_OK) { + memzero(settings, strlen(settings)); + settings[0] = '\0'; + ret = false; + } + // if(layout_file_info.size != 256) { + // memzero(settings, strlen(settings)); + // settings[0] = '\0'; + // } + } + + return ret; +} + +bool flipbip_has_file(const FlipBipFile file_type, const char* file_name, const bool remove) { + bool ret = false; + const char* path; + if(file_type == FlipBipFileKey) { + path = FLIPBIP_KEY_PATH; + } else if(file_type == FlipBipFileDat) { + path = FLIPBIP_DAT_PATH; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22 + strcpy(path_buf + strlen(path_buf), "/"); + strcpy(path_buf + strlen(path_buf), file_name); + path = path_buf; + } + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + if(remove) { + ret = storage_simply_remove(fs_api, path); + } else { + ret = storage_file_exists(fs_api, path); + } + furi_record_close(RECORD_STORAGE); + + return ret; +} + +bool flipbip_save_file( + const char* settings, + const FlipBipFile file_type, + const char* file_name, + const bool append) { + bool ret = false; + const char* path; + const char* path_bak; + if(file_type == FlipBipFileKey) { + path = FLIPBIP_KEY_PATH; + path_bak = FLIPBIP_KEY_PATH_BAK; + } else if(file_type == FlipBipFileDat) { + path = FLIPBIP_DAT_PATH; + path_bak = FLIPBIP_DAT_PATH_BAK; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPBIP_APP_BASE_FOLDER); // 22 + strcpy(path_buf + strlen(path_buf), "/"); + strcpy(path_buf + strlen(path_buf), file_name); + path = path_buf; + path_bak = NULL; + } + int open_mode = FSOM_OPEN_ALWAYS; + if(append) { + open_mode = FSOM_OPEN_APPEND; + } + + Storage* fs_api = furi_record_open(RECORD_STORAGE); + // // if the key file exists, we don't want to overwrite it + // if (key_file && storage_file_exists(fs_api, path)) { + // furi_record_close(RECORD_STORAGE); + // ret = true; + // return ret; + // } + // try to create the folder + storage_simply_mkdir(fs_api, FLIPBIP_APP_BASE_FOLDER); + + File* settings_file = storage_file_alloc(fs_api); + if(storage_file_open(settings_file, path, FSAM_WRITE, open_mode)) { + storage_file_write(settings_file, settings, strlen(settings)); + storage_file_write(settings_file, "\n", 1); + ret = true; + } + storage_file_close(settings_file); + storage_file_free(settings_file); + + if(path_bak != NULL) { + File* settings_file_bak = storage_file_alloc(fs_api); + if(storage_file_open(settings_file_bak, path_bak, FSAM_WRITE, open_mode)) { + storage_file_write(settings_file_bak, settings, strlen(settings)); + storage_file_write(settings_file_bak, "\n", 1); + } + storage_file_close(settings_file_bak); + storage_file_free(settings_file_bak); + } + + furi_record_close(RECORD_STORAGE); + + return ret; +} + +bool flipbip_save_qrfile( + const char* qr_msg_prefix, + const char* qr_msg_content, + const char* file_name) { + char qr_buf[FILE_MAX_QRFILE_CONTENT] = {0}; + strcpy(qr_buf, TEXT_QRFILE); + strcpy(qr_buf + strlen(qr_buf), qr_msg_prefix); + strcpy(qr_buf + strlen(qr_buf), qr_msg_content); + return flipbip_save_file(qr_buf, FlipBipFileOther, file_name, false); +} + +bool flipbip_load_file_secure(char* settings) { + const size_t dlen = FILE_HLEN + FILE_SLEN + 1; + + // allocate memory for key/data + char* data = malloc(dlen); + memzero(data, dlen); + + // load k2 from file + if(!flipbip_load_file(data, FlipBipFileKey, NULL)) return false; + + // check header + if(data[0] != FILE_HSTR[0] || data[1] != FILE_HSTR[1] || data[2] != FILE_HSTR[2] || + data[3] != FILE_HSTR[3]) { + memzero(data, dlen); + free(data); + return false; + } + // seek --> header + data += FILE_HLEN; + + // prepare k1 + uint8_t k1[64]; + flipbip_xtob(FILE_K1, k1, strlen(FILE_K1) / 2); + + // load k2 from file buffer (secured by k1) + flipbip_cipher(k1, strlen(FILE_K1) / 2, data, data, FILE_KLEN); + uint8_t k2[128]; + flipbip_xtob(data, k2, FILE_KLEN / 2); + // zero k2 buffer + memzero(data, FILE_KLEN); + // seek <-- header + data -= FILE_HLEN; + + // load data from file + if(!flipbip_load_file(data, FlipBipFileDat, NULL)) return false; + + // check header + if(data[0] != FILE_HSTR[0] || data[1] != FILE_HSTR[1] || data[2] != FILE_HSTR[2] || + data[3] != FILE_HSTR[3]) { + memzero(data, dlen); + free(data); + memzero(k1, strlen(FILE_K1) / 2); + memzero(k2, FILE_KLEN / 2); + return false; + } + // seek --> header + data += FILE_HLEN; + + // load settings from file buffer (secured by k2) + flipbip_cipher(k2, FILE_KLEN / 2, data, data, FILE_SLEN); + flipbip_xtob(data, (unsigned char*)data, FILE_SLEN / 2); + + // copy to output + strcpy(settings, data); + + // seek <-- header + data -= FILE_HLEN; + + // clear memory + memzero(data, dlen); + free(data); + memzero(k1, strlen(FILE_K1) / 2); + memzero(k2, FILE_KLEN / 2); + + return true; +} + +bool flipbip_save_file_secure(const char* settings) { + const size_t dlen = FILE_HLEN + FILE_SLEN + 1; + + // cap settings to 256 bytes + size_t len = strlen(settings); + if(len > (FILE_SLEN / 2)) len = FILE_SLEN / 2; + + // allocate memory for key/data + char* data = malloc(dlen); + memzero(data, dlen); + + // write header + strncpy(data, FILE_HSTR, FILE_HLEN); + // seek --> header + data += FILE_HLEN; + + // prepare k1 + uint8_t k1[64]; + flipbip_xtob(FILE_K1, k1, strlen(FILE_K1) / 2); + + // generate k2 + uint8_t k2[128]; + random_buffer(k2, FILE_KLEN / 2); + + // write k2 to file buffer (secured by k1) + flipbip_btox(k2, FILE_KLEN / 2, data); + flipbip_cipher(k1, strlen(FILE_K1) / 2, data, data, FILE_KLEN); + + // seek <-- header + data -= FILE_HLEN; + // save k2 to file + flipbip_save_file(data, FlipBipFileKey, NULL, false); + // seek --> header + data += FILE_HLEN; + // zero k2 memory + memzero(data, FILE_KLEN); + + // write settings to file buffer (secured by k2) + flipbip_btox((uint8_t*)settings, len, data); + flipbip_cipher(k2, FILE_KLEN / 2, data, data, FILE_SLEN); + + // seek <-- header + data -= FILE_HLEN; + // save data to file + flipbip_save_file(data, FlipBipFileDat, NULL, false); + + // clear memory + memzero(data, dlen); + free(data); + memzero(k1, strlen(FILE_K1) / 2); + memzero(k2, FILE_KLEN / 2); + + return true; +} diff --git a/applications/external/flipbip/helpers/flipbip_file.h b/applications/external/flipbip/helpers/flipbip_file.h new file mode 100644 index 000000000..c19f70dc5 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_file.h @@ -0,0 +1,23 @@ +#include + +typedef enum { + FlipBipFileDat, + FlipBipFileKey, + FlipBipFileOther, +} FlipBipFile; + +bool flipbip_has_file(const FlipBipFile file_type, const char* file_name, const bool remove); +bool flipbip_load_file(char* settings, const FlipBipFile file_type, const char* file_name); +bool flipbip_save_file( + const char* settings, + const FlipBipFile file_type, + const char* file_name, + const bool append); + +bool flipbip_save_qrfile( + const char* qr_msg_prefix, + const char* qr_msg_content, + const char* file_name); + +bool flipbip_load_file_secure(char* settings); +bool flipbip_save_file_secure(const char* settings); diff --git a/applications/external/flipbip/helpers/flipbip_haptic.c b/applications/external/flipbip/helpers/flipbip_haptic.c new file mode 100644 index 000000000..c5608efa5 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_haptic.c @@ -0,0 +1,35 @@ +#include "flipbip_haptic.h" +#include "../flipbip.h" + +void flipbip_play_happy_bump(void* context) { + FlipBip* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 20); + notification_message(app->notification, &sequence_reset_vibro); +} + +void flipbip_play_bad_bump(void* context) { + FlipBip* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + notification_message(app->notification, &sequence_reset_vibro); +} + +void flipbip_play_long_bump(void* context) { + FlipBip* app = context; + if(app->haptic != 1) { + return; + } + for(int i = 0; i < 4; i++) { + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 50); + notification_message(app->notification, &sequence_reset_vibro); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + } +} diff --git a/applications/external/flipbip/helpers/flipbip_haptic.h b/applications/external/flipbip/helpers/flipbip_haptic.h new file mode 100644 index 000000000..cab1d3a63 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_haptic.h @@ -0,0 +1,7 @@ +#include + +void flipbip_play_happy_bump(void* context); + +void flipbip_play_bad_bump(void* context); + +void flipbip_play_long_bump(void* context); diff --git a/applications/external/flipbip/helpers/flipbip_led.c b/applications/external/flipbip/helpers/flipbip_led.c new file mode 100644 index 000000000..7a6fd1778 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_led.c @@ -0,0 +1,39 @@ +#include "flipbip_led.h" +#include "../flipbip.h" + +void flipbip_led_set_rgb(void* context, int red, int green, int blue) { + FlipBip* app = context; + if(app->led != 1) { + return; + } + NotificationMessage notification_led_message_1; + notification_led_message_1.type = NotificationMessageTypeLedRed; + NotificationMessage notification_led_message_2; + notification_led_message_2.type = NotificationMessageTypeLedGreen; + NotificationMessage notification_led_message_3; + notification_led_message_3.type = NotificationMessageTypeLedBlue; + + notification_led_message_1.data.led.value = red; + notification_led_message_2.data.led.value = green; + notification_led_message_3.data.led.value = blue; + const NotificationSequence notification_sequence = { + ¬ification_led_message_1, + ¬ification_led_message_2, + ¬ification_led_message_3, + &message_do_not_reset, + NULL, + }; + notification_message(app->notification, ¬ification_sequence); + furi_thread_flags_wait( + 0, FuriFlagWaitAny, 10); //Delay, prevent removal from RAM before LED value set +} + +void flipbip_led_reset(void* context) { + FlipBip* app = context; + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + + furi_thread_flags_wait( + 0, FuriFlagWaitAny, 300); //Delay, prevent removal from RAM before LED value set +} diff --git a/applications/external/flipbip/helpers/flipbip_led.h b/applications/external/flipbip/helpers/flipbip_led.h new file mode 100644 index 000000000..bbacc976b --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_led.h @@ -0,0 +1,2 @@ +void flipbip_led_set_rgb(void* context, int red, int green, int blue); +void flipbip_led_reset(void* context); diff --git a/applications/external/flipbip/helpers/flipbip_speaker.c b/applications/external/flipbip/helpers/flipbip_speaker.c new file mode 100644 index 000000000..f7ae2193b --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_speaker.c @@ -0,0 +1,27 @@ +// #include "flipbip_speaker.h" +// #include "../flipbip.h" + +// #define NOTE_INPUT 587.33f + +// void flipbip_play_input_sound(void* context) { +// FlipBip* app = context; +// if (app->speaker != 1) { +// return; +// } +// float volume = 1.0f; +// if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { +// furi_hal_speaker_start(NOTE_INPUT, volume); +// } + +// } + +// void flipbip_stop_all_sound(void* context) { +// FlipBip* app = context; +// if (app->speaker != 1) { +// return; +// } +// if(furi_hal_speaker_is_mine()) { +// furi_hal_speaker_stop(); +// furi_hal_speaker_release(); +// } +// } diff --git a/applications/external/flipbip/helpers/flipbip_speaker.h b/applications/external/flipbip/helpers/flipbip_speaker.h new file mode 100644 index 000000000..150ba9129 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_speaker.h @@ -0,0 +1,4 @@ +// #define NOTE_INPUT 587.33f + +// void flipbip_play_input_sound(void* context); +// void flipbip_stop_all_sound(void* context); diff --git a/applications/external/flipbip/helpers/flipbip_string.c b/applications/external/flipbip/helpers/flipbip_string.c new file mode 100644 index 000000000..999820646 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_string.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "flipbip_string.h" +#include +#include +#include +// From: lib/crypto +#include +#include + +char* flipbip_strtok(char* s, const char* delim) { + static char* last; + return flipbip_strtok_r(s, delim, &last); +} +char* flipbip_strtok_r(char* s, const char* delim, char** last) { + char* spanp; + int c, sc; + char* tok; + if(s == NULL && (s = *last) == NULL) return (NULL); + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for(spanp = (char*)delim; (sc = *spanp++) != 0;) { + if(c == sc) goto cont; + } + if(c == 0) { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for(;;) { + c = *s++; + spanp = (char*)delim; + do { + if((sc = *spanp++) == c) { + if(c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return (tok); + } + } while(sc != 0); + } + /* NOTREACHED */ +} + +void flipbip_btox(const unsigned char* in, int in_len, char* str) { + for(int i = 0; i < in_len; i++) { + unsigned char n; + unsigned char x = in[i]; + + str += 2; + *(str + (i * 2)) = '\0'; + + for(n = 2; n != 0; --n) { + *(--str + (i * 2)) = "0123456789abcdef"[x & 0x0F]; + x >>= 4; + } + } +} +void flipbip_xtob(const char* str, unsigned char* out, int out_len) { + int len = strlen(str) / 2; + if(len > out_len) len = out_len; + for(int i = 0; i < len; i++) { + char c = 0; + if(str[i * 2] >= '0' && str[i * 2] <= '9') c += (str[i * 2] - '0') << 4; + if((str[i * 2] & ~0x20) >= 'A' && (str[i * 2] & ~0x20) <= 'F') + c += (10 + (str[i * 2] & ~0x20) - 'A') << 4; + if(str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9') c += (str[i * 2 + 1] - '0'); + if((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F') + c += (10 + (str[i * 2 + 1] & ~0x20) - 'A'); + out[i] = c; + } +} + +void flipbip_cipher( + const unsigned char* key_in, + const unsigned int key_len, + const char* in, + char* out, + const unsigned int io_len) { + if(io_len > 512) return; + + RC4_CTX ctx; + uint8_t buf[256]; + memzero(buf, 256); + + flipbip_xtob(in, buf, io_len / 2); + + rc4_init(&ctx, key_in, key_len); + rc4_encrypt(&ctx, buf, 256); + + flipbip_btox(buf, io_len / 2, out); + + memzero(buf, 256); +} \ No newline at end of file diff --git a/applications/external/flipbip/helpers/flipbip_string.h b/applications/external/flipbip/helpers/flipbip_string.h new file mode 100644 index 000000000..a66bf7572 --- /dev/null +++ b/applications/external/flipbip/helpers/flipbip_string.h @@ -0,0 +1,12 @@ +char* flipbip_strtok(char* s, const char* delim); +char* flipbip_strtok_r(char* s, const char* delim, char** last); + +void flipbip_btox(const unsigned char* in, int in_len, char* str); +void flipbip_xtob(const char* str, unsigned char* out, int out_len); + +void flipbip_cipher( + const unsigned char* key_in, + const unsigned int key_len, + const char* in, + char* out, + const unsigned int io_len); \ No newline at end of file diff --git a/applications/external/flipbip/icons/Auth_62x31.png b/applications/external/flipbip/icons/Auth_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..40f094ac9ba758ea06838c2bb8376cf6f28f8050 GIT binary patch literal 3761 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 literal 0 HcmV?d00001 diff --git a/applications/external/flipbip/icons/ButtonCenter_7x7.png b/applications/external/flipbip/icons/ButtonCenter_7x7.png new file mode 100644 index 0000000000000000000000000000000000000000..a66461b227b321f11bc85a38c63afda0d982d78d GIT binary patch literal 1440 zcmbVMO>5LZ7>3B0UTd{6LeL$!>SMgR$1#+OC>f>WbBi&Q4}`hi)=4nY!B^ zRS++F@uGMUJb3aT=s^*^`ByxtKS0pQengRK)xc!Fo_XHy_wM4t+~~;u5yLP>tMip5 zJ#SCPj;;EC7QMf%r=8LK<-{;{ji+Oa@#yhB!`QZ0)RxtQGk2+A_%pkO15A>@E!$;O3zzJQJa2RR6$Nr)W_ z7YND|Tm*y9)Y4+VL6$0~2eS0eHCswW1j0_IV|Q}4jGGXbN+pOK=s1=}Sjk$bXx9pp z14D)iPgpD>C1eOvMp~Dv$~Cp7eG`HxqYaaRo3z7VmqrlVC^e}E^jU_BR^xV0xX&PO z^MHpc(O8^ewU<0EgKtP11Q|L}vp2Lx1KT!4C$+VR!zG@`)tK?w8(QLlp<+=>Dw(w8 zZ|umfGixGJjyY8_u1VP*25NE00vv2R;P538`m&Q8Nu>-P@CsGjHFeamg|L;wH8e#W zO!E1o7!ic}P*3N9PZj+;u=GV^ZBwYvNJGW})m{<-ZE_fe&7L&RBh@fbG-SM5aZRnN zErd@BC0xw0I=6BS)Uc*#9$8bxdfj3UYD_xCJf@Ru_`V|9Y8t`ed;cHbpO1yEAlm+a zo-9J7EZZ{nq_4f4Gr^DZYPq}^%Z6y{i)3l;6sXRY3%FD$SdNX;MDHaWnHPzU>e`@m zF7WGvRa<~wjuFbzGH^|n#-ID<;8^^(3;VR47T2VI$csKKkqyJgp_fv5X;ksj_%dA!DP1fvqs5@M&TiMq)XFS~oQMEEt+wm~CH#1^@v8^9I ze!KeW*2}SvVq)w;aB`3F^wYBk_vc38mDg{_-`&1AJ@Mku!7q1qAAXYd(CX|$<@u>= GH@^cCN4$jq literal 0 HcmV?d00001 diff --git a/applications/external/flipbip/icons/ButtonDown_10x5.png b/applications/external/flipbip/icons/ButtonDown_10x5.png new file mode 100644 index 0000000000000000000000000000000000000000..b492b926c45def68d88b03c380527e226740beb6 GIT binary patch literal 6223 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 literal 0 HcmV?d00001 diff --git a/applications/external/flipbip/icons/ButtonLeft_4x7.png b/applications/external/flipbip/icons/ButtonLeft_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4655d43247083aa705620e9836ac415b42ca46 GIT binary patch literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a literal 0 HcmV?d00001 diff --git a/applications/external/flipbip/icons/ButtonRightSmall_3x5.png b/applications/external/flipbip/icons/ButtonRightSmall_3x5.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d5f87db1ca55141449cfcc3bf054417eaa84e1 GIT binary patch literal 1738 zcmcIl%Wm676lEHu%>zLLWYHwZf?zfc+Tjd`l=wgx!?E02K7gZd!)A>b-AnNYDb z=UD-0O?$90FBm_PwI0iHnuo^kzrHc_RD{NpUPPi|OHR_A(^5V@-5v4MBkl`h`li))oId$h zr-Twrdf1}K>IcLLELU%T22?9W66_DYYiq$%XiVz52r!<_X6DQ`RXN6%@B5fgOeq2c zsup?8<|wc3bqoVp@iHyyRONcZ$YOO|hXyEJO(84Rw0YIq1cu=`E3jpfW=b6}iq3{+ z*&1Ed+b2+^)%#K6YP2XM-j|g+F1g%3k$HWuD^^TYt*VLogtqnH|4=CSx?pi!PM7uw zj^$Klz+C~>TIwr;tx~dDl_RC5T~K>nMV(qE)xUm{=0eS?`;DS@fE=(|h6bc&Ap((k zBdZr!eqejwSR^211&yE&1gqKkz)Gaa;ylnO3Wj-Avz*J}AT&UfnWiG(Hm6?C6^L<1 zqL@1bP64XW zqKrwxT^V~c?$~}TQ}}Y&^h4H0l>o-Xk=)_jK{NqDuJ0r$_IQFAaV$sJiQn_7p}()Y zrKYNklmK^aLl-wVr`&ul_BH)&R_4UgD(ZOFr}nOx%X#N!gVeJ4h)=Wyh? zQXrf4V!h1m}&`|!B-3a0&FC= whJ($~<(FI>9{%<2-NwbcKNmOR7sY+;Hox}g7dMW6Yj&IA_U_=9M~Bb;1{rl65&!@I literal 0 HcmV?d00001 diff --git a/applications/external/flipbip/icons/ButtonRight_4x7.png b/applications/external/flipbip/icons/ButtonRight_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1c74c1c0038ea55172f19ac875003fc80c2d06 GIT binary patch literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA literal 0 HcmV?d00001 diff --git a/applications/external/flipbip/icons/ButtonUp_10x5.png b/applications/external/flipbip/icons/ButtonUp_10x5.png new file mode 100644 index 0000000000000000000000000000000000000000..5da99d01ee968c974521c414e6f789237f888be2 GIT binary patch literal 6233 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 +Pavol Rusnak diff --git a/applications/external/flipbip/lib/crypto/CONTRIBUTORS b/applications/external/flipbip/lib/crypto/CONTRIBUTORS new file mode 100644 index 000000000..300b9788d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/CONTRIBUTORS @@ -0,0 +1,16 @@ +Tomas Dzetkulic +Pavol Rusnak +Jochen Hoenicke +Dustin Laurence +Ondrej Mikle +Roman Zeyde +Alex Beregszaszi +netanelkl +Jan Pochyla +Ondrej Mikle +Josh Billings +Adam Mackler +Oleg Andreev +mog +John Dvorak +Christian Reitter diff --git a/applications/external/flipbip/lib/crypto/LICENSE b/applications/external/flipbip/lib/crypto/LICENSE new file mode 100644 index 000000000..1ea1df703 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2013 Tomas Dzetkulic +Copyright (c) 2013 Pavol Rusnak + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/flipbip/lib/crypto/Makefile b/applications/external/flipbip/lib/crypto/Makefile new file mode 100644 index 000000000..2bdfc9417 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/Makefile @@ -0,0 +1,187 @@ +# CLANG_VERSION is empty if the compiler is not clang-based +CLANG_VERSION = $(shell $(CC) --version | sed -nr 's/^.*clang version ([0-9.]+).*$$/\1/p') +CLANG_VERSION_MAJOR = $(shell echo $(CLANG_VERSION) | cut -f1 -d.) + +# determine specific version ranges +ifneq ($(CLANG_VERSION),) +$(if $(shell [ $(CLANG_VERSION_MAJOR) -ge 13 ] && echo "OK"), \ + $(eval CLANG_AT_LEAST_13 := true), \ + $(eval CLANG_AT_LEAST_13 := false)) +endif + +ifeq ($(FUZZER),1) +CC ?= clang +LD ?= $(CC) +SANFLAGS += -fsanitize=fuzzer + +# only clang versions >= 13 support this feature +ifeq ($(CLANG_AT_LEAST_13),true) +$(info "info: using -fsanitize-ignorelist") +SANFLAGS += -fsanitize-ignorelist=fuzzer/sanitizer_ignorelist.txt +else +$(info "info: not using -fsanitize-ignorelist") +endif + +# TODO is there a better solution, for example by disabling a specific optimization technique? +# there is a clang optimization issue in relation with the blake2 code at -fsanitize=undefined +$(warning "warning: disabling optimization on blake2 code as workaround") +blake2b.o: OPTFLAGS += -O0 +blake2s.o: OPTFLAGS += -O0 + +else ifeq ($(ADDRESS_SANITIZER),1) +SANFLAGS += -fsanitize=address,undefined +endif + +CC ?= cc + +OPTFLAGS ?= -O3 -g + +CFLAGS += $(OPTFLAGS) \ + $(SANFLAGS) \ + -std=gnu99 \ + -W \ + -Wall \ + -Wextra \ + -Wimplicit-function-declaration \ + -Wredundant-decls \ + -Wstrict-prototypes \ + -Wundef \ + -Wshadow \ + -Wpointer-arith \ + -Wformat \ + -Wreturn-type \ + -Wsign-compare \ + -Wmultichar \ + -Wformat-nonliteral \ + -Winit-self \ + -Wuninitialized \ + -Wformat-security \ + -Wno-missing-braces \ + -Werror + +ZKP_CFLAGS = \ + -DECMULT_GEN_PREC_BITS=4 \ + -DECMULT_WINDOW_SIZE=8 \ + -DENABLE_MODULE_GENERATOR \ + -DENABLE_MODULE_RECOVERY \ + -DENABLE_MODULE_SCHNORRSIG \ + -DENABLE_MODULE_EXTRAKEYS +ZKP_PATH = ../vendor/secp256k1-zkp +# this is specific for 64-bit builds +CFLAGS += -DSECP256K1_CONTEXT_SIZE=208 + +VALGRIND ?= 1 +ifeq ($(VALGRIND),1) +CFLAGS += -DVALGRIND +endif + +CFLAGS += -I. +CFLAGS += -I.. +CFLAGS += -DUSE_ETHEREUM=1 +CFLAGS += -DUSE_KECCAK=1 +CFLAGS += -DUSE_MONERO=1 +CFLAGS += -DUSE_NEM=1 +CFLAGS += -DUSE_CARDANO=1 +CFLAGS += $(shell pkg-config --cflags openssl) + +# disable certain optimizations and features when small footprint is required +ifdef SMALL +CFLAGS += -DUSE_PRECOMPUTED_CP=0 +endif + +SRCS = bignum.c ecdsa.c curves.c secp256k1.c nist256p1.c rand.c hmac.c bip32.c bip39.c bip39_english.c pbkdf2.c base58.c base32.c +SRCS += address.c +SRCS += script.c +SRCS += ripemd160.c +SRCS += sha2.c +SRCS += sha3.c +SRCS += hasher.c +SRCS += aes/aescrypt.c aes/aeskey.c aes/aestab.c aes/aes_modes.c +SRCS += ed25519_donna/curve25519_donna_32bit.c ed25519_donna/curve25519_donna_helpers.c ed25519_donna/modm_donna_32bit.c +SRCS += ed25519_donna/ed25519_donna_basepoint_table.c ed25519_donna/ed25519_donna_32bit_tables.c ed25519_donna/ed25519_donna_impl_base.c +SRCS += ed25519_donna/ed25519.c ed25519_donna/curve25519_donna_scalarmult_base.c ed25519_donna/ed25519_sha3.c ed25519_donna/ed25519_keccak.c +SRCS += monero/base58.c +SRCS += monero/serialize.c +SRCS += monero/xmr.c +SRCS += blake256.c +SRCS += blake2b.c blake2s.c +SRCS += chacha_drbg.c +SRCS += groestl.c +SRCS += chacha20poly1305/chacha20poly1305.c chacha20poly1305/chacha_merged.c chacha20poly1305/poly1305_donna.c chacha20poly1305/rfc7539.c +SRCS += rc4.c +SRCS += nem.c +SRCS += segwit_addr.c cash_addr.c +SRCS += memzero.c +SRCS += shamir.c +SRCS += hmac_drbg.c +SRCS += rfc6979.c +SRCS += slip39.c +SRCS += zkp_context.c +SRCS += zkp_ecdsa.c +SRCS += zkp_bip340.c +SRCS += cardano.c + +OBJS = $(SRCS:.c=.o) +OBJS += secp256k1-zkp.o +OBJS += precomputed_ecmult.o +OBJS += precomputed_ecmult_gen.o + +TESTLIBS = $(shell pkg-config --libs check) -lpthread -lm +TESTSSLLIBS = $(shell pkg-config --libs openssl) + +all: tools tests + +%.o: %.c %.h options.h + $(CC) $(CFLAGS) -o $@ -c $< + +tests: tests/test_check tests/test_openssl tests/test_speed tests/libtrezor-crypto.so tests/aestst + +tests/aestst: aes/aestst.o aes/aescrypt.o aes/aeskey.o aes/aestab.o + $(CC) $(CFLAGS) $^ -o $@ + +tests/test_check.o: tests/test_check_cardano.h tests/test_check_monero.h tests/test_check_cashaddr.h tests/test_check_segwit.h + +tests/test_check: tests/test_check.o $(OBJS) + $(CC) $(CFLAGS) tests/test_check.o $(OBJS) $(TESTLIBS) -o tests/test_check + +tests/test_speed: tests/test_speed.o $(OBJS) + $(CC) $(CFLAGS) tests/test_speed.o $(OBJS) -o tests/test_speed + +tests/test_openssl: tests/test_openssl.o $(OBJS) + $(CC) $(CFLAGS) tests/test_openssl.o $(OBJS) $(TESTSSLLIBS) -o tests/test_openssl + +tests/libtrezor-crypto.so: $(SRCS) secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o + $(CC) $(CFLAGS) -DAES_128 -DAES_192 -fPIC -shared $(SRCS) secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o -o tests/libtrezor-crypto.so + +tools: tools/xpubaddrgen tools/mktable tools/bip39bruteforce + +tools/xpubaddrgen: tools/xpubaddrgen.o $(OBJS) + $(CC) $(CFLAGS) tools/xpubaddrgen.o $(OBJS) -o tools/xpubaddrgen + +tools/mktable: tools/mktable.o $(OBJS) + $(CC) $(CFLAGS) tools/mktable.o $(OBJS) -o tools/mktable + +tools/bip39bruteforce: tools/bip39bruteforce.o $(OBJS) + $(CC) $(CFLAGS) tools/bip39bruteforce.o $(OBJS) -o tools/bip39bruteforce + +fuzzer: fuzzer/fuzzer.o $(OBJS) + $(CC) $(CFLAGS) fuzzer/fuzzer.o $(OBJS) -o fuzzer/fuzzer + +precomputed_ecmult.o: + $(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -c $(ZKP_PATH)/src/precomputed_ecmult.c -o precomputed_ecmult.o + +precomputed_ecmult_gen.o: + $(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -c $(ZKP_PATH)/src/precomputed_ecmult_gen.c -o precomputed_ecmult_gen.o + +secp256k1-zkp.o: + $(CC) $(CFLAGS) -Wno-unused-function $(ZKP_CFLAGS) -fPIC -I$(ZKP_PATH) -I$(ZKP_PATH)/src -c $(ZKP_PATH)/src/secp256k1.c -o secp256k1-zkp.o + +clean: + rm -f *.o aes/*.o chacha20poly1305/*.o ed25519_donna/*.o monero/*.o + rm -f tests/*.o tests/test_check tests/test_speed tests/test_openssl tests/libtrezor-crypto.so tests/aestst + rm -f tools/*.o tools/xpubaddrgen tools/mktable tools/bip39bruteforce + rm -f fuzzer/*.o fuzzer/fuzzer + rm -f secp256k1-zkp.o precomputed_ecmult.o precomputed_ecmult_gen.o + +clean-fuzzer: clean + rm -f crash-* fuzz-*.log slow-unit-* timeout-* diff --git a/applications/external/flipbip/lib/crypto/address.c b/applications/external/flipbip/lib/crypto/address.c new file mode 100644 index 000000000..2e791d05f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/address.c @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2016 Daira Hopwood + * Copyright (c) 2016 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "address.h" +#include "bignum.h" + +size_t address_prefix_bytes_len(uint32_t address_type) { + if(address_type <= 0xFF) return 1; + if(address_type <= 0xFFFF) return 2; + if(address_type <= 0xFFFFFF) return 3; + return 4; +} + +void address_write_prefix_bytes(uint32_t address_type, uint8_t* out) { + if(address_type > 0xFFFFFF) *(out++) = address_type >> 24; + if(address_type > 0xFFFF) *(out++) = (address_type >> 16) & 0xFF; + if(address_type > 0xFF) *(out++) = (address_type >> 8) & 0xFF; + *(out++) = address_type & 0xFF; +} + +bool address_check_prefix(const uint8_t* addr, uint32_t address_type) { + if(address_type <= 0xFF) { + return address_type == (uint32_t)(addr[0]); + } + if(address_type <= 0xFFFF) { + return address_type == (((uint32_t)addr[0] << 8) | ((uint32_t)addr[1])); + } + if(address_type <= 0xFFFFFF) { + return address_type == + (((uint32_t)addr[0] << 16) | ((uint32_t)addr[1] << 8) | ((uint32_t)addr[2])); + } + return address_type == (((uint32_t)addr[0] << 24) | ((uint32_t)addr[1] << 16) | + ((uint32_t)addr[2] << 8) | ((uint32_t)addr[3])); +} + +#if USE_ETHEREUM +#include "sha3.h" + +void ethereum_address_checksum(const uint8_t* addr, char* address, bool rskip60, uint64_t chain_id) { + const char* hex = "0123456789abcdef"; + address[0] = '0'; + address[1] = 'x'; + for(int i = 0; i < 20; i++) { + address[2 + i * 2] = hex[(addr[i] >> 4) & 0xF]; + address[2 + i * 2 + 1] = hex[addr[i] & 0xF]; + } + address[42] = 0; + + SHA3_CTX ctx = {0}; + uint8_t hash[32] = {0}; + keccak_256_Init(&ctx); + if(rskip60) { + char prefix[16] = {0}; + int prefix_size = + bn_format_uint64(chain_id, NULL, "0x", 0, 0, false, 0, prefix, sizeof(prefix)); + keccak_Update(&ctx, (const uint8_t*)prefix, prefix_size); + } + keccak_Update(&ctx, (const uint8_t*)(address + 2), 40); + keccak_Final(&ctx, hash); + + for(int i = 0; i < 20; i++) { + if((hash[i] & 0x80) && address[2 + i * 2] >= 'a' && address[2 + i * 2] <= 'f') { + address[2 + i * 2] -= 0x20; + } + if((hash[i] & 0x08) && address[2 + i * 2 + 1] >= 'a' && address[2 + i * 2 + 1] <= 'f') { + address[2 + i * 2 + 1] -= 0x20; + } + } +} +#endif diff --git a/applications/external/flipbip/lib/crypto/address.h b/applications/external/flipbip/lib/crypto/address.h new file mode 100644 index 000000000..e2767d6b0 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/address.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2016 Daira Hopwood + * Copyright (c) 2016 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __ADDRESS_H__ +#define __ADDRESS_H__ + +#include +#include +#include +#include "options.h" + +size_t address_prefix_bytes_len(uint32_t address_type); +void address_write_prefix_bytes(uint32_t address_type, uint8_t* out); +bool address_check_prefix(const uint8_t* addr, uint32_t address_type); +#if USE_ETHEREUM +void ethereum_address_checksum(const uint8_t* addr, char* address, bool rskip60, uint64_t chain_id); +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aes.h b/applications/external/flipbip/lib/crypto/aes/aes.h new file mode 100644 index 000000000..b277c390d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aes.h @@ -0,0 +1,256 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 02/08/2018 + + This file contains the definitions required to use AES in C. See aesopt.h + for optimisation details. +*/ + +#ifndef _AES_H +#define _AES_H + +#include +#include + +#define VOID_RETURN void +#define INT_RETURN int +#define ALIGN_OFFSET(x, n) (((intptr_t)(x)) & ((n)-1)) +#define ALIGN_FLOOR(x, n) ((uint8_t*)(x) - (((intptr_t)(x)) & ((n)-1))) +#define ALIGN_CEIL(x, n) ((uint8_t*)(x) + (-((intptr_t)(x)) & ((n)-1))) + +#if defined(__cplusplus) +extern "C" { +#endif + +// #define AES_128 /* if a fast 128 bit key scheduler is needed */ +// #define AES_192 /* if a fast 192 bit key scheduler is needed */ +#define AES_256 /* if a fast 256 bit key scheduler is needed */ +// #define AES_VAR /* if variable key size scheduler is needed */ +#if 1 +#define AES_MODES /* if support is needed for modes in the C code */ +#endif /* (these will use AES_NI if it is present) */ +#if 0 /* add this to make direct calls to the AES_NI */ +#/* implemented CBC and CTR modes available */ +#define ADD_AESNI_MODE_CALLS +#endif + +/* The following must also be set in assembler files if being used */ + +#define AES_ENCRYPT /* if support for encryption is needed */ +#define AES_DECRYPT /* if support for decryption is needed */ + +#define AES_BLOCK_SIZE_P2 4 /* AES block size as a power of 2 */ +#define AES_BLOCK_SIZE (1 << AES_BLOCK_SIZE_P2) /* AES block size */ +#define N_COLS 4 /* the number of columns in the state */ + +/* The key schedule length is 11, 13 or 15 16-byte blocks for 128, */ +/* 192 or 256-bit keys respectively. That is 176, 208 or 240 bytes */ +/* or 44, 52 or 60 32-bit words. */ + +#if defined(AES_VAR) || defined(AES_256) +#define KS_LENGTH 60 +#elif defined(AES_192) +#define KS_LENGTH 52 +#else +#define KS_LENGTH 44 +#endif + +#define AES_RETURN INT_RETURN + +/* the character array 'inf' in the following structures is used */ +/* to hold AES context information. This AES code uses cx->inf.b[0] */ +/* to hold the number of rounds multiplied by 16. The other three */ +/* elements can be used by code that implements additional modes */ + +typedef union { + uint32_t l; + uint8_t b[4]; +} aes_inf; + +#ifdef _MSC_VER +#pragma warning(disable : 4324) +#endif + +#if defined(_MSC_VER) && defined(_WIN64) +#define ALIGNED_(x) __declspec(align(x)) +#elif defined(__GNUC__) && defined(__x86_64__) +#define ALIGNED_(x) __attribute__((aligned(x))) +#else +#define ALIGNED_(x) +#endif + +typedef struct ALIGNED_(16) { + uint32_t ks[KS_LENGTH]; + aes_inf inf; +} aes_encrypt_ctx; + +typedef struct ALIGNED_(16) { + uint32_t ks[KS_LENGTH]; + aes_inf inf; +} aes_decrypt_ctx; + +#ifdef _MSC_VER +#pragma warning(default : 4324) +#endif + +/* This routine must be called before first use if non-static */ +/* tables are being used */ + +AES_RETURN aes_init(void); + +/* Key lengths in the range 16 <= key_len <= 32 are given in bytes, */ +/* those in the range 128 <= key_len <= 256 are given in bits */ + +#if defined(AES_ENCRYPT) + +#if defined(AES_128) || defined(AES_VAR) +AES_RETURN aes_encrypt_key128(const unsigned char* key, aes_encrypt_ctx cx[1]); +#endif + +#if defined(AES_192) || defined(AES_VAR) +AES_RETURN aes_encrypt_key192(const unsigned char* key, aes_encrypt_ctx cx[1]); +#endif + +#if defined(AES_256) || defined(AES_VAR) +AES_RETURN aes_encrypt_key256(const unsigned char* key, aes_encrypt_ctx cx[1]); +#endif + +#if defined(AES_VAR) +AES_RETURN aes_encrypt_key(const unsigned char* key, int key_len, aes_encrypt_ctx cx[1]); +#endif + +AES_RETURN aes_encrypt(const unsigned char* in, unsigned char* out, const aes_encrypt_ctx cx[1]); + +#endif + +#if defined(AES_DECRYPT) + +#if defined(AES_128) || defined(AES_VAR) +AES_RETURN aes_decrypt_key128(const unsigned char* key, aes_decrypt_ctx cx[1]); +#endif + +#if defined(AES_192) || defined(AES_VAR) +AES_RETURN aes_decrypt_key192(const unsigned char* key, aes_decrypt_ctx cx[1]); +#endif + +#if defined(AES_256) || defined(AES_VAR) +AES_RETURN aes_decrypt_key256(const unsigned char* key, aes_decrypt_ctx cx[1]); +#endif + +#if defined(AES_VAR) +AES_RETURN aes_decrypt_key(const unsigned char* key, int key_len, aes_decrypt_ctx cx[1]); +#endif + +AES_RETURN aes_decrypt(const unsigned char* in, unsigned char* out, const aes_decrypt_ctx cx[1]); + +#endif + +#if defined(AES_MODES) + +/* Multiple calls to the following subroutines for multiple block */ +/* ECB, CBC, CFB, OFB and CTR mode encryption can be used to handle */ +/* long messages incrementally provided that the context AND the iv */ +/* are preserved between all such calls. For the ECB and CBC modes */ +/* each individual call within a series of incremental calls must */ +/* process only full blocks (i.e. len must be a multiple of 16) but */ +/* the CFB, OFB and CTR mode calls can handle multiple incremental */ +/* calls of any length. Each mode is reset when a new AES key is */ +/* set but ECB needs no reset and CBC can be reset without setting */ +/* a new key by setting a new IV value. To reset CFB, OFB and CTR */ +/* without setting the key, aes_mode_reset() must be called and the */ +/* IV must be set. NOTE: All these calls update the IV on exit so */ +/* this has to be reset if a new operation with the same IV as the */ +/* previous one is required (or decryption follows encryption with */ +/* the same IV array). */ + +AES_RETURN aes_test_alignment_detection(unsigned int n); + +AES_RETURN aes_ecb_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_ecb_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_cbc_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cbc_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_mode_reset(aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx cx[1]); + +#define aes_ofb_encrypt aes_ofb_crypt +#define aes_ofb_decrypt aes_ofb_crypt + +AES_RETURN aes_ofb_crypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx cx[1]); + +typedef void cbuf_inc(unsigned char* cbuf); + +#define aes_ctr_encrypt aes_ctr_crypt +#define aes_ctr_decrypt aes_ctr_crypt + +AES_RETURN aes_ctr_crypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* cbuf, + cbuf_inc ctr_inc, + aes_encrypt_ctx cx[1]); + +void aes_ctr_cbuf_inc(unsigned char* cbuf); + +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aes_modes.c b/applications/external/flipbip/lib/crypto/aes/aes_modes.c new file mode 100644 index 000000000..7e6b7eb34 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aes_modes.c @@ -0,0 +1,932 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + These subroutines implement multiple block AES modes for ECB, CBC, CFB, + OFB and CTR encryption, The code provides support for the VIA Advanced + Cryptography Engine (ACE). + + NOTE: In the following subroutines, the AES contexts (ctx) must be + 16 byte aligned if VIA ACE is being used +*/ + +#include +#include +#include + +#include "aesopt.h" + +#if defined(AES_MODES) +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(_MSC_VER) && (_MSC_VER > 800) +#pragma intrinsic(memcpy) +#endif + +#define BFR_BLOCKS 8 + +/* These values are used to detect long word alignment in order to */ +/* speed up some buffer operations. This facility may not work on */ +/* some machines so this define can be commented out if necessary */ + +#define FAST_BUFFER_OPERATIONS + +#define lp32(x) ((uint32_t*)(x)) + +#if defined(USE_VIA_ACE_IF_PRESENT) + +#include "aes_via_ace.h" + +#pragma pack(16) + +aligned_array(unsigned long, enc_gen_table, 12, 16) = NEH_ENC_GEN_DATA; +aligned_array(unsigned long, enc_load_table, 12, 16) = NEH_ENC_LOAD_DATA; +aligned_array(unsigned long, enc_hybrid_table, 12, 16) = NEH_ENC_HYBRID_DATA; +aligned_array(unsigned long, dec_gen_table, 12, 16) = NEH_DEC_GEN_DATA; +aligned_array(unsigned long, dec_load_table, 12, 16) = NEH_DEC_LOAD_DATA; +aligned_array(unsigned long, dec_hybrid_table, 12, 16) = NEH_DEC_HYBRID_DATA; + +/* NOTE: These control word macros must only be used after */ +/* a key has been set up because they depend on key size */ +/* See the VIA ACE documentation for key type information */ +/* and aes_via_ace.h for non-default NEH_KEY_TYPE values */ + +#ifndef NEH_KEY_TYPE +#define NEH_KEY_TYPE NEH_HYBRID +#endif + +#if NEH_KEY_TYPE == NEH_LOAD +#define kd_adr(c) ((uint8_t*)(c)->ks) +#elif NEH_KEY_TYPE == NEH_GENERATE +#define kd_adr(c) ((uint8_t*)(c)->ks + (c)->inf.b[0]) +#elif NEH_KEY_TYPE == NEH_HYBRID +#define kd_adr(c) ((uint8_t*)(c)->ks + ((c)->inf.b[0] == 160 ? 160 : 0)) +#else +#error no key type defined for VIA ACE +#endif + +#else + +#define aligned_array(type, name, no, stride) type name[no] +#define aligned_auto(type, name, no, stride) type name[no] + +#endif + +#if defined(_MSC_VER) && _MSC_VER > 1200 + +#define via_cwd(cwd, ty, dir, len) unsigned long* cwd = (dir##_##ty##_table + ((len - 128) >> 4)) + +#else + +#define via_cwd(cwd, ty, dir, len) \ + aligned_auto(unsigned long, cwd, 4, 16); \ + cwd[1] = cwd[2] = cwd[3] = 0; \ + cwd[0] = neh_##dir##_##ty##_key(len) + +#endif + +/* test the code for detecting and setting pointer alignment */ + +AES_RETURN aes_test_alignment_detection(unsigned int n) /* 4 <= n <= 16 */ +{ + uint8_t p[16]; + uint32_t i = 0, count_eq = 0, count_neq = 0; + + if(n < 4 || n > 16) return EXIT_FAILURE; + + for(i = 0; i < n; ++i) { + uint8_t *qf = ALIGN_FLOOR(p + i, n), *qh = ALIGN_CEIL(p + i, n); + + if(qh == qf) + ++count_eq; + else if(qh == qf + n) + ++count_neq; + else + return EXIT_FAILURE; + } + return (count_eq != 1 || count_neq != n - 1 ? EXIT_FAILURE : EXIT_SUCCESS); +} + +AES_RETURN aes_mode_reset(aes_encrypt_ctx ctx[1]) { + ctx->inf.b[2] = 0; + return EXIT_SUCCESS; +} + +AES_RETURN aes_ecb_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + const aes_encrypt_ctx ctx[1]) { + int nb = len >> AES_BLOCK_SIZE_P2; + + if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE; + +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + uint8_t* ksp = (uint8_t*)(ctx->ks); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_ecb_op5(ksp, cwd, ibuf, obuf, nb); + } else { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ecb_op5(ksp, cwd, ip, op, m); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + return EXIT_SUCCESS; + } + +#endif + +#if !defined(ASSUME_VIA_ACE_PRESENT) + while(nb--) { + if(aes_encrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_ecb_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + const aes_decrypt_ctx ctx[1]) { + int nb = len >> AES_BLOCK_SIZE_P2; + + if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE; + +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + uint8_t* ksp = kd_adr(ctx); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_ecb_op5(ksp, cwd, ibuf, obuf, nb); + } else { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ecb_op5(ksp, cwd, ip, op, m); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + return EXIT_SUCCESS; + } + +#endif + +#if !defined(ASSUME_VIA_ACE_PRESENT) + while(nb--) { + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cbc_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + const aes_encrypt_ctx ctx[1]) { + int nb = len >> AES_BLOCK_SIZE_P2; + + if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE; + +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16) && !ALIGN_OFFSET(iv, 16)) { + via_cbc_op7(ksp, cwd, ibuf, obuf, nb, ivp, ivp); + } else { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cbc_op7(ksp, cwd, ip, op, m, ivp, ivp); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + if(iv != ivp) memcpy(iv, ivp, AES_BLOCK_SIZE); + + return EXIT_SUCCESS; + } + +#endif + +#if !defined(ASSUME_VIA_ACE_PRESENT) +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(nb--) { + lp32(iv)[0] ^= lp32(ibuf)[0]; + lp32(iv)[1] ^= lp32(ibuf)[1]; + lp32(iv)[2] ^= lp32(ibuf)[2]; + lp32(iv)[3] ^= lp32(ibuf)[3]; + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + memcpy(obuf, iv, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +#endif + while(nb--) { + iv[0] ^= ibuf[0]; + iv[1] ^= ibuf[1]; + iv[2] ^= ibuf[2]; + iv[3] ^= ibuf[3]; + iv[4] ^= ibuf[4]; + iv[5] ^= ibuf[5]; + iv[6] ^= ibuf[6]; + iv[7] ^= ibuf[7]; + iv[8] ^= ibuf[8]; + iv[9] ^= ibuf[9]; + iv[10] ^= ibuf[10]; + iv[11] ^= ibuf[11]; + iv[12] ^= ibuf[12]; + iv[13] ^= ibuf[13]; + iv[14] ^= ibuf[14]; + iv[15] ^= ibuf[15]; + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + memcpy(obuf, iv, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cbc_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + const aes_decrypt_ctx ctx[1]) { + unsigned char tmp[AES_BLOCK_SIZE]; + int nb = len >> AES_BLOCK_SIZE_P2; + + if(len & (AES_BLOCK_SIZE - 1)) return EXIT_FAILURE; + +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + uint8_t *ksp = kd_adr(ctx), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16) && !ALIGN_OFFSET(iv, 16)) { + via_cbc_op6(ksp, cwd, ibuf, obuf, nb, ivp); + } else { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cbc_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + if(iv != ivp) memcpy(iv, ivp, AES_BLOCK_SIZE); + + return EXIT_SUCCESS; + } +#endif + +#if !defined(ASSUME_VIA_ACE_PRESENT) +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(nb--) { + memcpy(tmp, ibuf, AES_BLOCK_SIZE); + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + lp32(obuf)[0] ^= lp32(iv)[0]; + lp32(obuf)[1] ^= lp32(iv)[1]; + lp32(obuf)[2] ^= lp32(iv)[2]; + lp32(obuf)[3] ^= lp32(iv)[3]; + memcpy(iv, tmp, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +#endif + while(nb--) { + memcpy(tmp, ibuf, AES_BLOCK_SIZE); + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + obuf[0] ^= iv[0]; + obuf[1] ^= iv[1]; + obuf[2] ^= iv[2]; + obuf[3] ^= iv[3]; + obuf[4] ^= iv[4]; + obuf[5] ^= iv[5]; + obuf[6] ^= iv[6]; + obuf[7] ^= iv[7]; + obuf[8] ^= iv[8]; + obuf[9] ^= iv[9]; + obuf[10] ^= iv[10]; + obuf[11] ^= iv[11]; + obuf[12] ^= iv[12]; + obuf[13] ^= iv[13]; + obuf[14] ^= iv[14]; + obuf[15] ^= iv[15]; + memcpy(iv, tmp, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cfb_encrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx ctx[1]) { + int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { + while(b_pos < AES_BLOCK_SIZE && cnt < len) { + *obuf++ = (iv[b_pos++] ^= *ibuf++); + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */ + { +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + int m; + uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_cfb_op7(ksp, cwd, ibuf, obuf, nb, ivp, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } else /* input, output or both are unaligned */ + { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cfb_op7(ksp, cwd, ip, op, m, ivp, ivp); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(cnt + AES_BLOCK_SIZE <= len) { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + lp32(obuf)[0] = lp32(iv)[0] ^= lp32(ibuf)[0]; + lp32(obuf)[1] = lp32(iv)[1] ^= lp32(ibuf)[1]; + lp32(obuf)[2] = lp32(iv)[2] ^= lp32(ibuf)[2]; + lp32(obuf)[3] = lp32(iv)[3] ^= lp32(ibuf)[3]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +#endif + while(cnt + AES_BLOCK_SIZE <= len) { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + obuf[0] = iv[0] ^= ibuf[0]; + obuf[1] = iv[1] ^= ibuf[1]; + obuf[2] = iv[2] ^= ibuf[2]; + obuf[3] = iv[3] ^= ibuf[3]; + obuf[4] = iv[4] ^= ibuf[4]; + obuf[5] = iv[5] ^= ibuf[5]; + obuf[6] = iv[6] ^= ibuf[6]; + obuf[7] = iv[7] ^= ibuf[7]; + obuf[8] = iv[8] ^= ibuf[8]; + obuf[9] = iv[9] ^= ibuf[9]; + obuf[10] = iv[10] ^= ibuf[10]; + obuf[11] = iv[11] ^= ibuf[11]; + obuf[12] = iv[12] ^= ibuf[12]; + obuf[13] = iv[13] ^= ibuf[13]; + obuf[14] = iv[14] ^= ibuf[14]; + obuf[15] = iv[15] ^= ibuf[15]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) { + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) { + *obuf++ = (iv[b_pos++] ^= *ibuf++); + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint8_t)b_pos; + return EXIT_SUCCESS; +} + +AES_RETURN aes_cfb_decrypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx ctx[1]) { + int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { + uint8_t t; + + while(b_pos < AES_BLOCK_SIZE && cnt < len) { + t = *ibuf++; + *obuf++ = t ^ iv[b_pos]; + iv[b_pos++] = t; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */ + { +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + int m; + uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_cfb_op6(ksp, cwd, ibuf, obuf, nb, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } else /* input, output or both are unaligned */ + { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) /* input buffer is not aligned */ + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cfb_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) /* output buffer is not aligned */ + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(cnt + AES_BLOCK_SIZE <= len) { + uint32_t t; + + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + t = lp32(ibuf)[0], lp32(obuf)[0] = t ^ lp32(iv)[0], lp32(iv)[0] = t; + t = lp32(ibuf)[1], lp32(obuf)[1] = t ^ lp32(iv)[1], lp32(iv)[1] = t; + t = lp32(ibuf)[2], lp32(obuf)[2] = t ^ lp32(iv)[2], lp32(iv)[2] = t; + t = lp32(ibuf)[3], lp32(obuf)[3] = t ^ lp32(iv)[3], lp32(iv)[3] = t; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +#endif + while(cnt + AES_BLOCK_SIZE <= len) { + uint8_t t; + + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + t = ibuf[0], obuf[0] = t ^ iv[0], iv[0] = t; + t = ibuf[1], obuf[1] = t ^ iv[1], iv[1] = t; + t = ibuf[2], obuf[2] = t ^ iv[2], iv[2] = t; + t = ibuf[3], obuf[3] = t ^ iv[3], iv[3] = t; + t = ibuf[4], obuf[4] = t ^ iv[4], iv[4] = t; + t = ibuf[5], obuf[5] = t ^ iv[5], iv[5] = t; + t = ibuf[6], obuf[6] = t ^ iv[6], iv[6] = t; + t = ibuf[7], obuf[7] = t ^ iv[7], iv[7] = t; + t = ibuf[8], obuf[8] = t ^ iv[8], iv[8] = t; + t = ibuf[9], obuf[9] = t ^ iv[9], iv[9] = t; + t = ibuf[10], obuf[10] = t ^ iv[10], iv[10] = t; + t = ibuf[11], obuf[11] = t ^ iv[11], iv[11] = t; + t = ibuf[12], obuf[12] = t ^ iv[12], iv[12] = t; + t = ibuf[13], obuf[13] = t ^ iv[13], iv[13] = t; + t = ibuf[14], obuf[14] = t ^ iv[14], iv[14] = t; + t = ibuf[15], obuf[15] = t ^ iv[15], iv[15] = t; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) { + uint8_t t; + + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) { + t = *ibuf++; + *obuf++ = t ^ iv[b_pos]; + iv[b_pos++] = t; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint8_t)b_pos; + return EXIT_SUCCESS; +} + +AES_RETURN aes_ofb_crypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* iv, + aes_encrypt_ctx ctx[1]) { + int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { + while(b_pos < AES_BLOCK_SIZE && cnt < len) { + *obuf++ = iv[b_pos++] ^ *ibuf++; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> AES_BLOCK_SIZE_P2) != 0) /* process whole blocks */ + { +#if defined(USE_VIA_ACE_IF_PRESENT) + + if(ctx->inf.b[1] == 0xff) { + int m; + uint8_t *ksp = (uint8_t*)(ctx->ks), *ivp = iv; + aligned_auto(uint8_t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; + + if(ALIGN_OFFSET(iv, 16)) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET(ibuf, 16) && !ALIGN_OFFSET(obuf, 16)) { + via_ofb_op6(ksp, cwd, ibuf, obuf, nb, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } else /* input, output or both are unaligned */ + { + aligned_auto(uint8_t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint8_t *ip = NULL, *op = NULL; + + while(nb) { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET(ibuf, 16) ? buf : ibuf); + op = (ALIGN_OFFSET(obuf, 16) ? buf : obuf); + + if(ip != ibuf) memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ofb_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(iv, 4)) + while(cnt + AES_BLOCK_SIZE <= len) { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + lp32(obuf)[0] = lp32(iv)[0] ^ lp32(ibuf)[0]; + lp32(obuf)[1] = lp32(iv)[1] ^ lp32(ibuf)[1]; + lp32(obuf)[2] = lp32(iv)[2] ^ lp32(ibuf)[2]; + lp32(obuf)[3] = lp32(iv)[3] ^ lp32(ibuf)[3]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +#endif + while(cnt + AES_BLOCK_SIZE <= len) { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + obuf[0] = iv[0] ^ ibuf[0]; + obuf[1] = iv[1] ^ ibuf[1]; + obuf[2] = iv[2] ^ ibuf[2]; + obuf[3] = iv[3] ^ ibuf[3]; + obuf[4] = iv[4] ^ ibuf[4]; + obuf[5] = iv[5] ^ ibuf[5]; + obuf[6] = iv[6] ^ ibuf[6]; + obuf[7] = iv[7] ^ ibuf[7]; + obuf[8] = iv[8] ^ ibuf[8]; + obuf[9] = iv[9] ^ ibuf[9]; + obuf[10] = iv[10] ^ ibuf[10]; + obuf[11] = iv[11] ^ ibuf[11]; + obuf[12] = iv[12] ^ ibuf[12]; + obuf[13] = iv[13] ^ ibuf[13]; + obuf[14] = iv[14] ^ ibuf[14]; + obuf[15] = iv[15] ^ ibuf[15]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) { + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) { + *obuf++ = iv[b_pos++] ^ *ibuf++; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint8_t)b_pos; + return EXIT_SUCCESS; +} + +#define BFR_LENGTH (BFR_BLOCKS * AES_BLOCK_SIZE) + +AES_RETURN aes_ctr_crypt( + const unsigned char* ibuf, + unsigned char* obuf, + int len, + unsigned char* cbuf, + cbuf_inc ctr_inc, + aes_encrypt_ctx ctx[1]) { + unsigned char* ip; + int i = 0, blen = 0, b_pos = (int)(ctx->inf.b[2]); + +#if defined(USE_VIA_ACE_IF_PRESENT) + aligned_auto(uint8_t, buf, BFR_LENGTH, 16); + if(ctx->inf.b[1] == 0xff && ALIGN_OFFSET(ctx, 16)) return EXIT_FAILURE; +#else + uint8_t buf[BFR_LENGTH] = {0}; +#endif + + if(b_pos) { + memcpy(buf, cbuf, AES_BLOCK_SIZE); + if(aes_ecb_encrypt(buf, buf, AES_BLOCK_SIZE, ctx) != EXIT_SUCCESS) return EXIT_FAILURE; + + while(b_pos < AES_BLOCK_SIZE && len) { + *obuf++ = *ibuf++ ^ buf[b_pos++]; + --len; + } + + if(len) ctr_inc(cbuf), b_pos = 0; + } + + while(len) { + blen = (len > BFR_LENGTH ? BFR_LENGTH : len), len -= blen; + + for(i = 0, ip = buf; i < (blen >> AES_BLOCK_SIZE_P2); ++i) { + memcpy(ip, cbuf, AES_BLOCK_SIZE); + ctr_inc(cbuf); + ip += AES_BLOCK_SIZE; + } + + if(blen & (AES_BLOCK_SIZE - 1)) memcpy(ip, cbuf, AES_BLOCK_SIZE), i++; + +#if defined(USE_VIA_ACE_IF_PRESENT) + if(ctx->inf.b[1] == 0xff) { + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + via_ecb_op5((ctx->ks), cwd, buf, buf, i); + } else +#endif + if(aes_ecb_encrypt(buf, buf, i * AES_BLOCK_SIZE, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + + i = 0; + ip = buf; +#ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET(ibuf, 4) && !ALIGN_OFFSET(obuf, 4) && !ALIGN_OFFSET(ip, 4)) + while(i + AES_BLOCK_SIZE <= blen) { + lp32(obuf)[0] = lp32(ibuf)[0] ^ lp32(ip)[0]; + lp32(obuf)[1] = lp32(ibuf)[1] ^ lp32(ip)[1]; + lp32(obuf)[2] = lp32(ibuf)[2] ^ lp32(ip)[2]; + lp32(obuf)[3] = lp32(ibuf)[3] ^ lp32(ip)[3]; + i += AES_BLOCK_SIZE; + ip += AES_BLOCK_SIZE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +#endif + while(i + AES_BLOCK_SIZE <= blen) { + obuf[0] = ibuf[0] ^ ip[0]; + obuf[1] = ibuf[1] ^ ip[1]; + obuf[2] = ibuf[2] ^ ip[2]; + obuf[3] = ibuf[3] ^ ip[3]; + obuf[4] = ibuf[4] ^ ip[4]; + obuf[5] = ibuf[5] ^ ip[5]; + obuf[6] = ibuf[6] ^ ip[6]; + obuf[7] = ibuf[7] ^ ip[7]; + obuf[8] = ibuf[8] ^ ip[8]; + obuf[9] = ibuf[9] ^ ip[9]; + obuf[10] = ibuf[10] ^ ip[10]; + obuf[11] = ibuf[11] ^ ip[11]; + obuf[12] = ibuf[12] ^ ip[12]; + obuf[13] = ibuf[13] ^ ip[13]; + obuf[14] = ibuf[14] ^ ip[14]; + obuf[15] = ibuf[15] ^ ip[15]; + i += AES_BLOCK_SIZE; + ip += AES_BLOCK_SIZE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + + while(i++ < blen) *obuf++ = *ibuf++ ^ ip[b_pos++]; + } + + ctx->inf.b[2] = (uint8_t)b_pos; + return EXIT_SUCCESS; +} + +void aes_ctr_cbuf_inc(unsigned char* cbuf) { + int i = AES_BLOCK_SIZE - 1; + while(i >= 0) { + cbuf[i]++; + if(cbuf[i]) return; // if there was no overflow + i--; + } +} + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aescrypt.c b/applications/external/flipbip/lib/crypto/aes/aescrypt.c new file mode 100644 index 000000000..a160b6f95 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aescrypt.c @@ -0,0 +1,349 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#include "aesopt.h" +#include "aestab.h" + +#if defined(USE_INTEL_AES_IF_PRESENT) +#include "aes_ni.h" +#else +/* map names here to provide the external API ('name' -> 'aes_name') */ +#define aes_xi(x) aes_##x +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#define si(y, x, k, c) (s(y, c) = word_in(x, c) ^ (k)[c]) +#define so(y, x, c) word_out(y, c, s(x, c)) + +#if defined(ARRAYS) +#define locals(y, x) x[4], y[4] +#else +#define locals(y, x) x##0, x##1, x##2, x##3, y##0, y##1, y##2, y##3 +#endif + +#define l_copy(y, x) \ + s(y, 0) = s(x, 0); \ + s(y, 1) = s(x, 1); \ + s(y, 2) = s(x, 2); \ + s(y, 3) = s(x, 3); +#define state_in(y, x, k) \ + si(y, x, k, 0); \ + si(y, x, k, 1); \ + si(y, x, k, 2); \ + si(y, x, k, 3) +#define state_out(y, x) \ + so(y, x, 0); \ + so(y, x, 1); \ + so(y, x, 2); \ + so(y, x, 3) +#define round(rm, y, x, k) \ + rm(y, x, k, 0); \ + rm(y, x, k, 1); \ + rm(y, x, k, 2); \ + rm(y, x, k, 3) + +#if(FUNCS_IN_C & ENCRYPTION_IN_C) + +/* Visual C++ .Net v7.1 provides the fastest encryption code when using + Pentium optimiation with small code but this is poor for decryption + so we need to control this with the following VC++ pragmas +*/ + +#if defined(_MSC_VER) && !defined(_WIN64) && !defined(__clang__) +#pragma optimize("s", on) +#endif + +/* Given the column (c) of the output state variable, the following + macros give the input state variables which are needed in its + computation for each row (r) of the state. All the alternative + macros give the same end values but expand into different ways + of calculating these values. In particular the complex macro + used for dynamically variable block sizes is designed to expand + to a compile time constant whenever possible but will expand to + conditional clauses on some branches (I am grateful to Frank + Yellin for this construction) +*/ + +#define fwd_var(x, r, c) \ + (r == 0 ? (c == 0 ? s(x, 0) : \ + c == 1 ? s(x, 1) : \ + c == 2 ? s(x, 2) : \ + s(x, 3)) : \ + r == 1 ? (c == 0 ? s(x, 1) : \ + c == 1 ? s(x, 2) : \ + c == 2 ? s(x, 3) : \ + s(x, 0)) : \ + r == 2 ? (c == 0 ? s(x, 2) : \ + c == 1 ? s(x, 3) : \ + c == 2 ? s(x, 0) : \ + s(x, 1)) : \ + (c == 0 ? s(x, 3) : \ + c == 1 ? s(x, 0) : \ + c == 2 ? s(x, 1) : \ + s(x, 2))) + +#if defined(FT4_SET) +#undef dec_fmvars +#define fwd_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(f, n), fwd_var, rf1, c)) +#elif defined(FT1_SET) +#undef dec_fmvars +#define fwd_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, upr, t_use(f, n), fwd_var, rf1, c)) +#else +#define fwd_rnd(y, x, k, c) \ + (s(y, c) = (k)[c] ^ fwd_mcol(no_table(x, t_use(s, box), fwd_var, rf1, c))) +#endif + +#if defined(FL4_SET) +#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(f, l), fwd_var, rf1, c)) +#elif defined(FL1_SET) +#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, ups, t_use(f, l), fwd_var, rf1, c)) +#else +#define fwd_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ no_table(x, t_use(s, box), fwd_var, rf1, c)) +#endif + +AES_RETURN +aes_xi(encrypt)(const unsigned char* in, unsigned char* out, const aes_encrypt_ctx cx[1]) { + uint32_t locals(b0, b1); + const uint32_t* kp = NULL; +#if defined(dec_fmvars) + dec_fmvars; /* declare variables for fwd_mcol() if needed */ +#endif + + if(cx->inf.b[0] != 10 * AES_BLOCK_SIZE && cx->inf.b[0] != 12 * AES_BLOCK_SIZE && + cx->inf.b[0] != 14 * AES_BLOCK_SIZE) + return EXIT_FAILURE; + + kp = cx->ks; + state_in(b0, in, kp); + +#if(ENC_UNROLL == FULL) + + switch(cx->inf.b[0]) { + case 14 * AES_BLOCK_SIZE: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + kp += 2 * N_COLS; + //-fallthrough + case 12 * AES_BLOCK_SIZE: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + kp += 2 * N_COLS; + //-fallthrough + case 10 * AES_BLOCK_SIZE: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + round(fwd_rnd, b1, b0, kp + 3 * N_COLS); + round(fwd_rnd, b0, b1, kp + 4 * N_COLS); + round(fwd_rnd, b1, b0, kp + 5 * N_COLS); + round(fwd_rnd, b0, b1, kp + 6 * N_COLS); + round(fwd_rnd, b1, b0, kp + 7 * N_COLS); + round(fwd_rnd, b0, b1, kp + 8 * N_COLS); + round(fwd_rnd, b1, b0, kp + 9 * N_COLS); + round(fwd_lrnd, b0, b1, kp + 10 * N_COLS); + //-fallthrough + } + +#else + +#if(ENC_UNROLL == PARTIAL) + { + uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) { + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); + kp += N_COLS; + round(fwd_rnd, b0, b1, kp); + } + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); +#else + { + uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) { + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); + l_copy(b0, b1); + } +#endif + kp += N_COLS; + round(fwd_lrnd, b0, b1, kp); + } +#endif + + state_out(out, b0); + return EXIT_SUCCESS; +} + +#endif + +#if(FUNCS_IN_C & DECRYPTION_IN_C) + +/* Visual C++ .Net v7.1 provides the fastest encryption code when using + Pentium optimiation with small code but this is poor for decryption + so we need to control this with the following VC++ pragmas +*/ + +#if defined(_MSC_VER) && !defined(_WIN64) && !defined(__clang__) +#pragma optimize("t", on) +#endif + +/* Given the column (c) of the output state variable, the following + macros give the input state variables which are needed in its + computation for each row (r) of the state. All the alternative + macros give the same end values but expand into different ways + of calculating these values. In particular the complex macro + used for dynamically variable block sizes is designed to expand + to a compile time constant whenever possible but will expand to + conditional clauses on some branches (I am grateful to Frank + Yellin for this construction) +*/ + +#define inv_var(x, r, c) \ + (r == 0 ? (c == 0 ? s(x, 0) : \ + c == 1 ? s(x, 1) : \ + c == 2 ? s(x, 2) : \ + s(x, 3)) : \ + r == 1 ? (c == 0 ? s(x, 3) : \ + c == 1 ? s(x, 0) : \ + c == 2 ? s(x, 1) : \ + s(x, 2)) : \ + r == 2 ? (c == 0 ? s(x, 2) : \ + c == 1 ? s(x, 3) : \ + c == 2 ? s(x, 0) : \ + s(x, 1)) : \ + (c == 0 ? s(x, 1) : \ + c == 1 ? s(x, 2) : \ + c == 2 ? s(x, 3) : \ + s(x, 0))) + +#if defined(IT4_SET) +#undef dec_imvars +#define inv_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(i, n), inv_var, rf1, c)) +#elif defined(IT1_SET) +#undef dec_imvars +#define inv_rnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, upr, t_use(i, n), inv_var, rf1, c)) +#else +#define inv_rnd(y, x, k, c) \ + (s(y, c) = inv_mcol((k)[c] ^ no_table(x, t_use(i, box), inv_var, rf1, c))) +#endif + +#if defined(IL4_SET) +#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ four_tables(x, t_use(i, l), inv_var, rf1, c)) +#elif defined(IL1_SET) +#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ one_table(x, ups, t_use(i, l), inv_var, rf1, c)) +#else +#define inv_lrnd(y, x, k, c) (s(y, c) = (k)[c] ^ no_table(x, t_use(i, box), inv_var, rf1, c)) +#endif + +/* This code can work with the decryption key schedule in the */ +/* order that is used for encrytpion (where the 1st decryption */ +/* round key is at the high end ot the schedule) or with a key */ +/* schedule that has been reversed to put the 1st decryption */ +/* round key at the low end of the schedule in memory (when */ +/* AES_REV_DKS is defined) */ + +#ifdef AES_REV_DKS +#define key_ofs 0 +#define rnd_key(n) (kp + n * N_COLS) +#else +#define key_ofs 1 +#define rnd_key(n) (kp - n * N_COLS) +#endif + +AES_RETURN +aes_xi(decrypt)(const unsigned char* in, unsigned char* out, const aes_decrypt_ctx cx[1]) { + uint32_t locals(b0, b1); +#if defined(dec_imvars) + dec_imvars; /* declare variables for inv_mcol() if needed */ +#endif + const uint32_t* kp = NULL; + + if(cx->inf.b[0] != 10 * AES_BLOCK_SIZE && cx->inf.b[0] != 12 * AES_BLOCK_SIZE && + cx->inf.b[0] != 14 * AES_BLOCK_SIZE) + return EXIT_FAILURE; + + kp = cx->ks + (key_ofs ? (cx->inf.b[0] >> 2) : 0); + state_in(b0, in, kp); + +#if(DEC_UNROLL == FULL) + + kp = cx->ks + (key_ofs ? 0 : (cx->inf.b[0] >> 2)); + switch(cx->inf.b[0]) { + case 14 * AES_BLOCK_SIZE: + round(inv_rnd, b1, b0, rnd_key(-13)); + round(inv_rnd, b0, b1, rnd_key(-12)); + //-fallthrough + case 12 * AES_BLOCK_SIZE: + round(inv_rnd, b1, b0, rnd_key(-11)); + round(inv_rnd, b0, b1, rnd_key(-10)); + //-fallthrough + case 10 * AES_BLOCK_SIZE: + round(inv_rnd, b1, b0, rnd_key(-9)); + round(inv_rnd, b0, b1, rnd_key(-8)); + round(inv_rnd, b1, b0, rnd_key(-7)); + round(inv_rnd, b0, b1, rnd_key(-6)); + round(inv_rnd, b1, b0, rnd_key(-5)); + round(inv_rnd, b0, b1, rnd_key(-4)); + round(inv_rnd, b1, b0, rnd_key(-3)); + round(inv_rnd, b0, b1, rnd_key(-2)); + round(inv_rnd, b1, b0, rnd_key(-1)); + round(inv_lrnd, b0, b1, rnd_key(0)); + //-fallthrough + } + +#else + +#if(DEC_UNROLL == PARTIAL) + { + uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) { + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); + kp = rnd_key(1); + round(inv_rnd, b0, b1, kp); + } + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); +#else + { + uint32_t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) { + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); + l_copy(b0, b1); + } +#endif + kp = rnd_key(1); + round(inv_lrnd, b0, b1, kp); + } +#endif + + state_out(out, b0); + return EXIT_SUCCESS; +} + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aeskey.c b/applications/external/flipbip/lib/crypto/aes/aeskey.c new file mode 100644 index 000000000..0092be439 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aeskey.c @@ -0,0 +1,662 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#include "aesopt.h" +#include "aestab.h" + +#if defined(USE_INTEL_AES_IF_PRESENT) +#include "aes_ni.h" +#else +/* map names here to provide the external API ('name' -> 'aes_name') */ +#define aes_xi(x) aes_##x +#endif + +#ifdef USE_VIA_ACE_IF_PRESENT +#include "aes_via_ace.h" +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Initialise the key schedule from the user supplied key. The key + length can be specified in bytes, with legal values of 16, 24 + and 32, or in bits, with legal values of 128, 192 and 256. These + values correspond with Nk values of 4, 6 and 8 respectively. + + The following macros implement a single cycle in the key + schedule generation process. The number of cycles needed + for each cx->n_col and nk value is: + + nk = 4 5 6 7 8 + ------------------------------ + cx->n_col = 4 10 9 8 7 7 + cx->n_col = 5 14 11 10 9 9 + cx->n_col = 6 19 15 12 11 11 + cx->n_col = 7 21 19 16 13 14 + cx->n_col = 8 29 23 19 17 14 +*/ + +#if defined(REDUCE_CODE_SIZE) +#define ls_box ls_sub +uint32_t ls_sub(const uint32_t t, const uint32_t n); +#define inv_mcol im_sub +uint32_t im_sub(const uint32_t x); +#ifdef ENC_KS_UNROLL +#undef ENC_KS_UNROLL +#endif +#ifdef DEC_KS_UNROLL +#undef DEC_KS_UNROLL +#endif +#endif + +#if(FUNCS_IN_C & ENC_KEYING_IN_C) + +#if defined(AES_128) || defined(AES_VAR) + +#define ke4(k, i) \ + { \ + k[4 * (i) + 4] = ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + k[4 * (i) + 5] = ss[1] ^= ss[0]; \ + k[4 * (i) + 6] = ss[2] ^= ss[1]; \ + k[4 * (i) + 7] = ss[3] ^= ss[2]; \ + } + +AES_RETURN aes_xi(encrypt_key128)(const unsigned char* key, aes_encrypt_ctx cx[1]) { + uint32_t ss[4]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + +#ifdef ENC_KS_UNROLL + ke4(cx->ks, 0); + ke4(cx->ks, 1); + ke4(cx->ks, 2); + ke4(cx->ks, 3); + ke4(cx->ks, 4); + ke4(cx->ks, 5); + ke4(cx->ks, 6); + ke4(cx->ks, 7); + ke4(cx->ks, 8); +#else + { + uint32_t i; + for(i = 0; i < 9; ++i) ke4(cx->ks, i); + } +#endif + ke4(cx->ks, 9); + cx->inf.l = 0; + cx->inf.b[0] = 10 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_192) || defined(AES_VAR) + +#define kef6(k, i) \ + { \ + k[6 * (i) + 6] = ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + k[6 * (i) + 7] = ss[1] ^= ss[0]; \ + k[6 * (i) + 8] = ss[2] ^= ss[1]; \ + k[6 * (i) + 9] = ss[3] ^= ss[2]; \ + } + +#define ke6(k, i) \ + { \ + kef6(k, i); \ + k[6 * (i) + 10] = ss[4] ^= ss[3]; \ + k[6 * (i) + 11] = ss[5] ^= ss[4]; \ + } + +AES_RETURN aes_xi(encrypt_key192)(const unsigned char* key, aes_encrypt_ctx cx[1]) { + uint32_t ss[6]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + cx->ks[4] = ss[4] = word_in(key, 4); + cx->ks[5] = ss[5] = word_in(key, 5); + +#ifdef ENC_KS_UNROLL + ke6(cx->ks, 0); + ke6(cx->ks, 1); + ke6(cx->ks, 2); + ke6(cx->ks, 3); + ke6(cx->ks, 4); + ke6(cx->ks, 5); + ke6(cx->ks, 6); +#else + { + uint32_t i; + for(i = 0; i < 7; ++i) ke6(cx->ks, i); + } +#endif + kef6(cx->ks, 7); + cx->inf.l = 0; + cx->inf.b[0] = 12 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_256) || defined(AES_VAR) + +#define kef8(k, i) \ + { \ + k[8 * (i) + 8] = ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + k[8 * (i) + 9] = ss[1] ^= ss[0]; \ + k[8 * (i) + 10] = ss[2] ^= ss[1]; \ + k[8 * (i) + 11] = ss[3] ^= ss[2]; \ + } + +#define ke8(k, i) \ + { \ + kef8(k, i); \ + k[8 * (i) + 12] = ss[4] ^= ls_box(ss[3], 0); \ + k[8 * (i) + 13] = ss[5] ^= ss[4]; \ + k[8 * (i) + 14] = ss[6] ^= ss[5]; \ + k[8 * (i) + 15] = ss[7] ^= ss[6]; \ + } + +AES_RETURN aes_xi(encrypt_key256)(const unsigned char* key, aes_encrypt_ctx cx[1]) { + uint32_t ss[8]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + cx->ks[4] = ss[4] = word_in(key, 4); + cx->ks[5] = ss[5] = word_in(key, 5); + cx->ks[6] = ss[6] = word_in(key, 6); + cx->ks[7] = ss[7] = word_in(key, 7); + +#ifdef ENC_KS_UNROLL + ke8(cx->ks, 0); + ke8(cx->ks, 1); + ke8(cx->ks, 2); + ke8(cx->ks, 3); + ke8(cx->ks, 4); + ke8(cx->ks, 5); +#else + { + uint32_t i; + for(i = 0; i < 6; ++i) ke8(cx->ks, i); + } +#endif + kef8(cx->ks, 6); + cx->inf.l = 0; + cx->inf.b[0] = 14 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#endif + +#if(FUNCS_IN_C & DEC_KEYING_IN_C) + +/* this is used to store the decryption round keys */ +/* in forward or reverse order */ + +#ifdef AES_REV_DKS +#define v(n, i) ((n) - (i) + 2 * ((i)&3)) +#else +#define v(n, i) (i) +#endif + +#if DEC_ROUND == NO_TABLES +#define ff(x) (x) +#else +#define ff(x) inv_mcol(x) +#if defined(dec_imvars) +#define d_vars dec_imvars +#endif +#endif + +#if defined(AES_128) || defined(AES_VAR) + +#define k4e(k, i) \ + { \ + k[v(40, (4 * (i)) + 4)] = ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + k[v(40, (4 * (i)) + 5)] = ss[1] ^= ss[0]; \ + k[v(40, (4 * (i)) + 6)] = ss[2] ^= ss[1]; \ + k[v(40, (4 * (i)) + 7)] = ss[3] ^= ss[2]; \ + } + +#if 1 + +#define kdf4(k, i) \ + { \ + ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; \ + ss[1] = ss[1] ^ ss[3]; \ + ss[2] = ss[2] ^ ss[3]; \ + ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \ + ss[i % 4] ^= ss[4]; \ + ss[4] ^= k[v(40, (4 * (i)))]; \ + k[v(40, (4 * (i)) + 4)] = ff(ss[4]); \ + ss[4] ^= k[v(40, (4 * (i)) + 1)]; \ + k[v(40, (4 * (i)) + 5)] = ff(ss[4]); \ + ss[4] ^= k[v(40, (4 * (i)) + 2)]; \ + k[v(40, (4 * (i)) + 6)] = ff(ss[4]); \ + ss[4] ^= k[v(40, (4 * (i)) + 3)]; \ + k[v(40, (4 * (i)) + 7)] = ff(ss[4]); \ + } + +#define kd4(k, i) \ + { \ + ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \ + ss[i % 4] ^= ss[4]; \ + ss[4] = ff(ss[4]); \ + k[v(40, (4 * (i)) + 4)] = ss[4] ^= k[v(40, (4 * (i)))]; \ + k[v(40, (4 * (i)) + 5)] = ss[4] ^= k[v(40, (4 * (i)) + 1)]; \ + k[v(40, (4 * (i)) + 6)] = ss[4] ^= k[v(40, (4 * (i)) + 2)]; \ + k[v(40, (4 * (i)) + 7)] = ss[4] ^= k[v(40, (4 * (i)) + 3)]; \ + } + +#define kdl4(k, i) \ + { \ + ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \ + ss[i % 4] ^= ss[4]; \ + k[v(40, (4 * (i)) + 4)] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; \ + k[v(40, (4 * (i)) + 5)] = ss[1] ^ ss[3]; \ + k[v(40, (4 * (i)) + 6)] = ss[0]; \ + k[v(40, (4 * (i)) + 7)] = ss[1]; \ + } + +#else + +#define kdf4(k, i) \ + { \ + ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + k[v(40, (4 * (i)) + 4)] = ff(ss[0]); \ + ss[1] ^= ss[0]; \ + k[v(40, (4 * (i)) + 5)] = ff(ss[1]); \ + ss[2] ^= ss[1]; \ + k[v(40, (4 * (i)) + 6)] = ff(ss[2]); \ + ss[3] ^= ss[2]; \ + k[v(40, (4 * (i)) + 7)] = ff(ss[3]); \ + } + +#define kd4(k, i) \ + { \ + ss[4] = ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + ss[0] ^= ss[4]; \ + ss[4] = ff(ss[4]); \ + k[v(40, (4 * (i)) + 4)] = ss[4] ^= k[v(40, (4 * (i)))]; \ + ss[1] ^= ss[0]; \ + k[v(40, (4 * (i)) + 5)] = ss[4] ^= k[v(40, (4 * (i)) + 1)]; \ + ss[2] ^= ss[1]; \ + k[v(40, (4 * (i)) + 6)] = ss[4] ^= k[v(40, (4 * (i)) + 2)]; \ + ss[3] ^= ss[2]; \ + k[v(40, (4 * (i)) + 7)] = ss[4] ^= k[v(40, (4 * (i)) + 3)]; \ + } + +#define kdl4(k, i) \ + { \ + ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \ + k[v(40, (4 * (i)) + 4)] = ss[0]; \ + ss[1] ^= ss[0]; \ + k[v(40, (4 * (i)) + 5)] = ss[1]; \ + ss[2] ^= ss[1]; \ + k[v(40, (4 * (i)) + 6)] = ss[2]; \ + ss[3] ^= ss[2]; \ + k[v(40, (4 * (i)) + 7)] = ss[3]; \ + } + +#endif + +AES_RETURN aes_xi(decrypt_key128)(const unsigned char* key, aes_decrypt_ctx cx[1]) { + uint32_t ss[5]; +#if defined(d_vars) + d_vars; +#endif + + cx->ks[v(40, (0))] = ss[0] = word_in(key, 0); + cx->ks[v(40, (1))] = ss[1] = word_in(key, 1); + cx->ks[v(40, (2))] = ss[2] = word_in(key, 2); + cx->ks[v(40, (3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + kdf4(cx->ks, 0); + kd4(cx->ks, 1); + kd4(cx->ks, 2); + kd4(cx->ks, 3); + kd4(cx->ks, 4); + kd4(cx->ks, 5); + kd4(cx->ks, 6); + kd4(cx->ks, 7); + kd4(cx->ks, 8); + kdl4(cx->ks, 9); +#else + { + uint32_t i; + for(i = 0; i < 10; ++i) k4e(cx->ks, i); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 10 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 10 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_192) || defined(AES_VAR) + +#define k6ef(k, i) \ + { \ + k[v(48, (6 * (i)) + 6)] = ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + k[v(48, (6 * (i)) + 7)] = ss[1] ^= ss[0]; \ + k[v(48, (6 * (i)) + 8)] = ss[2] ^= ss[1]; \ + k[v(48, (6 * (i)) + 9)] = ss[3] ^= ss[2]; \ + } + +#define k6e(k, i) \ + { \ + k6ef(k, i); \ + k[v(48, (6 * (i)) + 10)] = ss[4] ^= ss[3]; \ + k[v(48, (6 * (i)) + 11)] = ss[5] ^= ss[4]; \ + } + +#define kdf6(k, i) \ + { \ + ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + k[v(48, (6 * (i)) + 6)] = ff(ss[0]); \ + ss[1] ^= ss[0]; \ + k[v(48, (6 * (i)) + 7)] = ff(ss[1]); \ + ss[2] ^= ss[1]; \ + k[v(48, (6 * (i)) + 8)] = ff(ss[2]); \ + ss[3] ^= ss[2]; \ + k[v(48, (6 * (i)) + 9)] = ff(ss[3]); \ + ss[4] ^= ss[3]; \ + k[v(48, (6 * (i)) + 10)] = ff(ss[4]); \ + ss[5] ^= ss[4]; \ + k[v(48, (6 * (i)) + 11)] = ff(ss[5]); \ + } + +#define kd6(k, i) \ + { \ + ss[6] = ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + ss[0] ^= ss[6]; \ + ss[6] = ff(ss[6]); \ + k[v(48, (6 * (i)) + 6)] = ss[6] ^= k[v(48, (6 * (i)))]; \ + ss[1] ^= ss[0]; \ + k[v(48, (6 * (i)) + 7)] = ss[6] ^= k[v(48, (6 * (i)) + 1)]; \ + ss[2] ^= ss[1]; \ + k[v(48, (6 * (i)) + 8)] = ss[6] ^= k[v(48, (6 * (i)) + 2)]; \ + ss[3] ^= ss[2]; \ + k[v(48, (6 * (i)) + 9)] = ss[6] ^= k[v(48, (6 * (i)) + 3)]; \ + ss[4] ^= ss[3]; \ + k[v(48, (6 * (i)) + 10)] = ss[6] ^= k[v(48, (6 * (i)) + 4)]; \ + ss[5] ^= ss[4]; \ + k[v(48, (6 * (i)) + 11)] = ss[6] ^= k[v(48, (6 * (i)) + 5)]; \ + } + +#define kdl6(k, i) \ + { \ + ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \ + k[v(48, (6 * (i)) + 6)] = ss[0]; \ + ss[1] ^= ss[0]; \ + k[v(48, (6 * (i)) + 7)] = ss[1]; \ + ss[2] ^= ss[1]; \ + k[v(48, (6 * (i)) + 8)] = ss[2]; \ + ss[3] ^= ss[2]; \ + k[v(48, (6 * (i)) + 9)] = ss[3]; \ + } + +AES_RETURN aes_xi(decrypt_key192)(const unsigned char* key, aes_decrypt_ctx cx[1]) { + uint32_t ss[7]; +#if defined(d_vars) + d_vars; +#endif + + cx->ks[v(48, (0))] = ss[0] = word_in(key, 0); + cx->ks[v(48, (1))] = ss[1] = word_in(key, 1); + cx->ks[v(48, (2))] = ss[2] = word_in(key, 2); + cx->ks[v(48, (3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + ss[4] = word_in(key, 4); + ss[5] = word_in(key, 5); + cx->ks[v(48, (4))] = ff(ss[4]); + cx->ks[v(48, (5))] = ff(ss[5]); + kdf6(cx->ks, 0); + kd6(cx->ks, 1); + kd6(cx->ks, 2); + kd6(cx->ks, 3); + kd6(cx->ks, 4); + kd6(cx->ks, 5); + kd6(cx->ks, 6); + kdl6(cx->ks, 7); +#else + cx->ks[v(48, (4))] = ss[4] = word_in(key, 4); + cx->ks[v(48, (5))] = ss[5] = word_in(key, 5); + { + uint32_t i; + + for(i = 0; i < 7; ++i) k6e(cx->ks, i); + k6ef(cx->ks, 7); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 12 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 12 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_256) || defined(AES_VAR) + +#define k8ef(k, i) \ + { \ + k[v(56, (8 * (i)) + 8)] = ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + k[v(56, (8 * (i)) + 9)] = ss[1] ^= ss[0]; \ + k[v(56, (8 * (i)) + 10)] = ss[2] ^= ss[1]; \ + k[v(56, (8 * (i)) + 11)] = ss[3] ^= ss[2]; \ + } + +#define k8e(k, i) \ + { \ + k8ef(k, i); \ + k[v(56, (8 * (i)) + 12)] = ss[4] ^= ls_box(ss[3], 0); \ + k[v(56, (8 * (i)) + 13)] = ss[5] ^= ss[4]; \ + k[v(56, (8 * (i)) + 14)] = ss[6] ^= ss[5]; \ + k[v(56, (8 * (i)) + 15)] = ss[7] ^= ss[6]; \ + } + +#define kdf8(k, i) \ + { \ + ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + k[v(56, (8 * (i)) + 8)] = ff(ss[0]); \ + ss[1] ^= ss[0]; \ + k[v(56, (8 * (i)) + 9)] = ff(ss[1]); \ + ss[2] ^= ss[1]; \ + k[v(56, (8 * (i)) + 10)] = ff(ss[2]); \ + ss[3] ^= ss[2]; \ + k[v(56, (8 * (i)) + 11)] = ff(ss[3]); \ + ss[4] ^= ls_box(ss[3], 0); \ + k[v(56, (8 * (i)) + 12)] = ff(ss[4]); \ + ss[5] ^= ss[4]; \ + k[v(56, (8 * (i)) + 13)] = ff(ss[5]); \ + ss[6] ^= ss[5]; \ + k[v(56, (8 * (i)) + 14)] = ff(ss[6]); \ + ss[7] ^= ss[6]; \ + k[v(56, (8 * (i)) + 15)] = ff(ss[7]); \ + } + +#define kd8(k, i) \ + { \ + ss[8] = ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + ss[0] ^= ss[8]; \ + ss[8] = ff(ss[8]); \ + k[v(56, (8 * (i)) + 8)] = ss[8] ^= k[v(56, (8 * (i)))]; \ + ss[1] ^= ss[0]; \ + k[v(56, (8 * (i)) + 9)] = ss[8] ^= k[v(56, (8 * (i)) + 1)]; \ + ss[2] ^= ss[1]; \ + k[v(56, (8 * (i)) + 10)] = ss[8] ^= k[v(56, (8 * (i)) + 2)]; \ + ss[3] ^= ss[2]; \ + k[v(56, (8 * (i)) + 11)] = ss[8] ^= k[v(56, (8 * (i)) + 3)]; \ + ss[8] = ls_box(ss[3], 0); \ + ss[4] ^= ss[8]; \ + ss[8] = ff(ss[8]); \ + k[v(56, (8 * (i)) + 12)] = ss[8] ^= k[v(56, (8 * (i)) + 4)]; \ + ss[5] ^= ss[4]; \ + k[v(56, (8 * (i)) + 13)] = ss[8] ^= k[v(56, (8 * (i)) + 5)]; \ + ss[6] ^= ss[5]; \ + k[v(56, (8 * (i)) + 14)] = ss[8] ^= k[v(56, (8 * (i)) + 6)]; \ + ss[7] ^= ss[6]; \ + k[v(56, (8 * (i)) + 15)] = ss[8] ^= k[v(56, (8 * (i)) + 7)]; \ + } + +#define kdl8(k, i) \ + { \ + ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \ + k[v(56, (8 * (i)) + 8)] = ss[0]; \ + ss[1] ^= ss[0]; \ + k[v(56, (8 * (i)) + 9)] = ss[1]; \ + ss[2] ^= ss[1]; \ + k[v(56, (8 * (i)) + 10)] = ss[2]; \ + ss[3] ^= ss[2]; \ + k[v(56, (8 * (i)) + 11)] = ss[3]; \ + } + +AES_RETURN aes_xi(decrypt_key256)(const unsigned char* key, aes_decrypt_ctx cx[1]) { + uint32_t ss[9]; +#if defined(d_vars) + d_vars; +#endif + + cx->ks[v(56, (0))] = ss[0] = word_in(key, 0); + cx->ks[v(56, (1))] = ss[1] = word_in(key, 1); + cx->ks[v(56, (2))] = ss[2] = word_in(key, 2); + cx->ks[v(56, (3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + ss[4] = word_in(key, 4); + ss[5] = word_in(key, 5); + ss[6] = word_in(key, 6); + ss[7] = word_in(key, 7); + cx->ks[v(56, (4))] = ff(ss[4]); + cx->ks[v(56, (5))] = ff(ss[5]); + cx->ks[v(56, (6))] = ff(ss[6]); + cx->ks[v(56, (7))] = ff(ss[7]); + kdf8(cx->ks, 0); + kd8(cx->ks, 1); + kd8(cx->ks, 2); + kd8(cx->ks, 3); + kd8(cx->ks, 4); + kd8(cx->ks, 5); + kdl8(cx->ks, 6); +#else + cx->ks[v(56, (4))] = ss[4] = word_in(key, 4); + cx->ks[v(56, (5))] = ss[5] = word_in(key, 5); + cx->ks[v(56, (6))] = ss[6] = word_in(key, 6); + cx->ks[v(56, (7))] = ss[7] = word_in(key, 7); + { + uint32_t i; + + for(i = 0; i < 6; ++i) k8e(cx->ks, i); + k8ef(cx->ks, 6); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 14 * N_COLS; ++i) cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 14 * AES_BLOCK_SIZE; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#endif + +#if defined(AES_VAR) + +AES_RETURN aes_encrypt_key(const unsigned char* key, int key_len, aes_encrypt_ctx cx[1]) { + switch(key_len) { + case 16: + case 128: + return aes_encrypt_key128(key, cx); + case 24: + case 192: + return aes_encrypt_key192(key, cx); + case 32: + case 256: + return aes_encrypt_key256(key, cx); + default: + return EXIT_FAILURE; + } +} + +AES_RETURN aes_decrypt_key(const unsigned char* key, int key_len, aes_decrypt_ctx cx[1]) { + switch(key_len) { + case 16: + case 128: + return aes_decrypt_key128(key, cx); + case 24: + case 192: + return aes_decrypt_key192(key, cx); + case 32: + case 256: + return aes_decrypt_key256(key, cx); + default: + return EXIT_FAILURE; + } +} + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aesopt.h b/applications/external/flipbip/lib/crypto/aes/aesopt.h new file mode 100644 index 000000000..6d4de004f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aesopt.h @@ -0,0 +1,793 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + This file contains the compilation options for AES (Rijndael) and code + that is common across encryption, key scheduling and table generation. + + OPERATION + + These source code files implement the AES algorithm Rijndael designed by + Joan Daemen and Vincent Rijmen. This version is designed for the standard + block size of 16 bytes and for key sizes of 128, 192 and 256 bits (16, 24 + and 32 bytes). + + This version is designed for flexibility and speed using operations on + 32-bit words rather than operations on bytes. It can be compiled with + either big or little endian internal byte order but is faster when the + native byte order for the processor is used. + + THE CIPHER INTERFACE + + The cipher interface is implemented as an array of bytes in which lower + AES bit sequence indexes map to higher numeric significance within bytes. + + uint8_t (an unsigned 8-bit type) + uint32_t (an unsigned 32-bit type) + struct aes_encrypt_ctx (structure for the cipher encryption context) + struct aes_decrypt_ctx (structure for the cipher decryption context) + AES_RETURN the function return type + + C subroutine calls: + + AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, + const aes_encrypt_ctx cx[1]); + + AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, + const aes_decrypt_ctx cx[1]); + + IMPORTANT NOTE: If you are using this C interface with dynamic tables make sure that + you call aes_init() before AES is used so that the tables are initialised. + + C++ aes class subroutines: + + Class AESencrypt for encryption + + Constructors: + AESencrypt(void) + AESencrypt(const unsigned char *key) - 128 bit key + Members: + AES_RETURN key128(const unsigned char *key) + AES_RETURN key192(const unsigned char *key) + AES_RETURN key256(const unsigned char *key) + AES_RETURN encrypt(const unsigned char *in, unsigned char *out) const + + Class AESdecrypt for encryption + Constructors: + AESdecrypt(void) + AESdecrypt(const unsigned char *key) - 128 bit key + Members: + AES_RETURN key128(const unsigned char *key) + AES_RETURN key192(const unsigned char *key) + AES_RETURN key256(const unsigned char *key) + AES_RETURN decrypt(const unsigned char *in, unsigned char *out) const +*/ + +#if !defined(_AESOPT_H) +#define _AESOPT_H + +#if defined(__cplusplus) +#include "aescpp.h" +#else +#include "aes.h" +#endif + +/* PLATFORM SPECIFIC INCLUDES */ + +#define IS_BIG_ENDIAN 4321 +#define IS_LITTLE_ENDIAN 1234 +#define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN + +/* CONFIGURATION - THE USE OF DEFINES + + Later in this section there are a number of defines that control the + operation of the code. In each section, the purpose of each define is + explained so that the relevant form can be included or excluded by + setting either 1's or 0's respectively on the branches of the related + #if clauses. The following local defines should not be changed. +*/ + +#define ENCRYPTION_IN_C 1 +#define DECRYPTION_IN_C 2 +#define ENC_KEYING_IN_C 4 +#define DEC_KEYING_IN_C 8 + +#define NO_TABLES 0 +#define ONE_TABLE 1 +#define FOUR_TABLES 4 +#define NONE 0 +#define PARTIAL 1 +#define FULL 2 + +/* --- START OF USER CONFIGURED OPTIONS --- */ + +/* 1. BYTE ORDER WITHIN 32 BIT WORDS + + The fundamental data processing units in Rijndael are 8-bit bytes. The + input, output and key input are all enumerated arrays of bytes in which + bytes are numbered starting at zero and increasing to one less than the + number of bytes in the array in question. This enumeration is only used + for naming bytes and does not imply any adjacency or order relationship + from one byte to another. When these inputs and outputs are considered + as bit sequences, bits 8*n to 8*n+7 of the bit sequence are mapped to + byte[n] with bit 8n+i in the sequence mapped to bit 7-i within the byte. + In this implementation bits are numbered from 0 to 7 starting at the + numerically least significant end of each byte (bit n represents 2^n). + + However, Rijndael can be implemented more efficiently using 32-bit + words by packing bytes into words so that bytes 4*n to 4*n+3 are placed + into word[n]. While in principle these bytes can be assembled into words + in any positions, this implementation only supports the two formats in + which bytes in adjacent positions within words also have adjacent byte + numbers. This order is called big-endian if the lowest numbered bytes + in words have the highest numeric significance and little-endian if the + opposite applies. + + This code can work in either order irrespective of the order used by the + machine on which it runs. Normally the internal byte order will be set + to the order of the processor on which the code is to be run but this + define can be used to reverse this in special situations + + WARNING: Assembler code versions rely on PLATFORM_BYTE_ORDER being set. + This define will hence be redefined later (in section 4) if necessary +*/ + +#if 1 +#define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER +#elif 0 +#define ALGORITHM_BYTE_ORDER IS_LITTLE_ENDIAN +#elif 0 +#define ALGORITHM_BYTE_ORDER IS_BIG_ENDIAN +#else +#error The algorithm byte order is not defined +#endif + +/* 2. Intel AES AND VIA ACE SUPPORT */ + +#if defined(__GNUC__) && defined(__i386__) && !defined(__BEOS__) || \ + defined(_WIN32) && defined(_M_IX86) && \ + !(defined(_WIN64) || defined(_WIN32_WCE) || defined(_MSC_VER) && (_MSC_VER <= 800)) +#define VIA_ACE_POSSIBLE +#endif + +/* AESNI is supported by all Windows x64 compilers, but for Linux/GCC + we have to test for SSE 2, SSE 3, and AES to before enabling it; */ +#if !defined(INTEL_AES_POSSIBLE) +#if defined(_WIN64) && defined(_MSC_VER) || defined(__GNUC__) && defined(__x86_64__) && \ + defined(__SSE2__) && defined(__SSE3__) && \ + defined(__AES__) +#define INTEL_AES_POSSIBLE +#endif +#endif + +/* Define this option if support for the Intel AESNI is required + If USE_INTEL_AES_IF_PRESENT is defined then AESNI will be used + if it is detected (both present and enabled). + + AESNI uses a decryption key schedule with the first decryption + round key at the high end of the key scedule with the following + round keys at lower positions in memory. So AES_REV_DKS must NOT + be defined when AESNI will be used. Although it is unlikely that + assembler code will be used with an AESNI build, if it is then + AES_REV_DKS must NOT be defined when the assembler files are + built (the definition of USE_INTEL_AES_IF_PRESENT in the assembler + code files must match that here if they are used). +*/ + +#if 0 && defined(INTEL_AES_POSSIBLE) && !defined(USE_INTEL_AES_IF_PRESENT) +#define USE_INTEL_AES_IF_PRESENT +#endif + +/* Define this option if support for the VIA ACE is required. This uses + inline assembler instructions and is only implemented for the Microsoft, + Intel and GCC compilers. If VIA ACE is known to be present, then defining + ASSUME_VIA_ACE_PRESENT will remove the ordinary encryption/decryption + code. If USE_VIA_ACE_IF_PRESENT is defined then VIA ACE will be used if + it is detected (both present and enabled) but the normal AES code will + also be present. + + When VIA ACE is to be used, all AES encryption contexts MUST be 16 byte + aligned; other input/output buffers do not need to be 16 byte aligned + but there are very large performance gains if this can be arranged. + VIA ACE also requires the decryption key schedule to be in reverse + order (which later checks below ensure). + + AES_REV_DKS must be set for assembler code used with a VIA ACE build +*/ + +#if 0 && defined(VIA_ACE_POSSIBLE) && !defined(USE_VIA_ACE_IF_PRESENT) +#define USE_VIA_ACE_IF_PRESENT +#endif + +#if 0 && defined(VIA_ACE_POSSIBLE) && !defined(ASSUME_VIA_ACE_PRESENT) +#define ASSUME_VIA_ACE_PRESENT +#endif + +/* 3. ASSEMBLER SUPPORT + + This define (which can be on the command line) enables the use of the + assembler code routines for encryption, decryption and key scheduling + as follows: + + ASM_X86_V1C uses the assembler (aes_x86_v1.asm) with large tables for + encryption and decryption and but with key scheduling in C + ASM_X86_V2 uses assembler (aes_x86_v2.asm) with compressed tables for + encryption, decryption and key scheduling + ASM_X86_V2C uses assembler (aes_x86_v2.asm) with compressed tables for + encryption and decryption and but with key scheduling in C + ASM_AMD64_C uses assembler (aes_amd64.asm) with compressed tables for + encryption and decryption and but with key scheduling in C + + Change one 'if 0' below to 'if 1' to select the version or define + as a compilation option. +*/ + +#if 0 && !defined(ASM_X86_V1C) +#define ASM_X86_V1C +#elif 0 && !defined(ASM_X86_V2) +#define ASM_X86_V2 +#elif 0 && !defined(ASM_X86_V2C) +#define ASM_X86_V2C +#elif 0 && !defined(ASM_AMD64_C) +#define ASM_AMD64_C +#endif + +#if defined(__i386) || defined(_M_IX86) +#define A32_ +#elif defined(__x86_64__) || defined(_M_X64) +#define A64_ +#endif + +#if(defined(ASM_X86_V1C) || defined(ASM_X86_V2) || defined(ASM_X86_V2C)) && !defined(A32_) || \ + defined(ASM_AMD64_C) && !defined(A64_) +#error Assembler code is only available for x86 and AMD64 systems +#endif + +/* 4. FAST INPUT/OUTPUT OPERATIONS. + + On some machines it is possible to improve speed by transferring the + bytes in the input and output arrays to and from the internal 32-bit + variables by addressing these arrays as if they are arrays of 32-bit + words. On some machines this will always be possible but there may + be a large performance penalty if the byte arrays are not aligned on + the normal word boundaries. On other machines this technique will + lead to memory access errors when such 32-bit word accesses are not + properly aligned. The option SAFE_IO avoids such problems but will + often be slower on those machines that support misaligned access + (especially so if care is taken to align the input and output byte + arrays on 32-bit word boundaries). If SAFE_IO is not defined it is + assumed that access to byte arrays as if they are arrays of 32-bit + words will not cause problems when such accesses are misaligned. +*/ +#if 1 && !defined(_MSC_VER) +#define SAFE_IO +#endif + +/* 5. LOOP UNROLLING + + The code for encryption and decrytpion cycles through a number of rounds + that can be implemented either in a loop or by expanding the code into a + long sequence of instructions, the latter producing a larger program but + one that will often be much faster. The latter is called loop unrolling. + There are also potential speed advantages in expanding two iterations in + a loop with half the number of iterations, which is called partial loop + unrolling. The following options allow partial or full loop unrolling + to be set independently for encryption and decryption +*/ +#if 1 +#define ENC_UNROLL FULL +#elif 0 +#define ENC_UNROLL PARTIAL +#else +#define ENC_UNROLL NONE +#endif + +#if 1 +#define DEC_UNROLL FULL +#elif 0 +#define DEC_UNROLL PARTIAL +#else +#define DEC_UNROLL NONE +#endif + +#if 1 +#define ENC_KS_UNROLL +#endif + +#if 1 +#define DEC_KS_UNROLL +#endif + +/* 6. FAST FINITE FIELD OPERATIONS + + If this section is included, tables are used to provide faster finite + field arithmetic (this has no effect if STATIC_TABLES is defined). +*/ +#if 1 +#define FF_TABLES +#endif + +/* 7. INTERNAL STATE VARIABLE FORMAT + + The internal state of Rijndael is stored in a number of local 32-bit + word varaibles which can be defined either as an array or as individual + names variables. Include this section if you want to store these local + varaibles in arrays. Otherwise individual local variables will be used. +*/ +#if 1 +#define ARRAYS +#endif + +/* 8. FIXED OR DYNAMIC TABLES + + When this section is included the tables used by the code are compiled + statically into the binary file. Otherwise the subroutine aes_init() + must be called to compute them before the code is first used. +*/ +#if 1 && !(defined(_MSC_VER) && (_MSC_VER <= 800)) +#define STATIC_TABLES +#endif + +/* 9. MASKING OR CASTING FROM LONGER VALUES TO BYTES + + In some systems it is better to mask longer values to extract bytes + rather than using a cast. This option allows this choice. +*/ +#if 0 +#define to_byte(x) ((uint8_t)(x)) +#else +#define to_byte(x) ((x)&0xff) +#endif + +/* 10. TABLE ALIGNMENT + + On some sytsems speed will be improved by aligning the AES large lookup + tables on particular boundaries. This define should be set to a power of + two giving the desired alignment. It can be left undefined if alignment + is not needed. This option is specific to the Microsft VC++ compiler - + it seems to sometimes cause trouble for the VC++ version 6 compiler. +*/ + +#if 1 && defined(_MSC_VER) && (_MSC_VER >= 1300) +#define TABLE_ALIGN 32 +#endif + +/* 11. REDUCE CODE AND TABLE SIZE + + This replaces some expanded macros with function calls if AES_ASM_V2 or + AES_ASM_V2C are defined +*/ + +#if 1 && (defined(ASM_X86_V2) || defined(ASM_X86_V2C)) +#define REDUCE_CODE_SIZE +#endif + +/* 12. TABLE OPTIONS + + This cipher proceeds by repeating in a number of cycles known as 'rounds' + which are implemented by a round function which can optionally be speeded + up using tables. The basic tables are each 256 32-bit words, with either + one or four tables being required for each round function depending on + how much speed is required. The encryption and decryption round functions + are different and the last encryption and decrytpion round functions are + different again making four different round functions in all. + + This means that: + 1. Normal encryption and decryption rounds can each use either 0, 1 + or 4 tables and table spaces of 0, 1024 or 4096 bytes each. + 2. The last encryption and decryption rounds can also use either 0, 1 + or 4 tables and table spaces of 0, 1024 or 4096 bytes each. + + Include or exclude the appropriate definitions below to set the number + of tables used by this implementation. +*/ + +#if 1 /* set tables for the normal encryption round */ +#define ENC_ROUND FOUR_TABLES +#elif 0 +#define ENC_ROUND ONE_TABLE +#else +#define ENC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the last encryption round */ +#define LAST_ENC_ROUND FOUR_TABLES +#elif 0 +#define LAST_ENC_ROUND ONE_TABLE +#else +#define LAST_ENC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the normal decryption round */ +#define DEC_ROUND FOUR_TABLES +#elif 0 +#define DEC_ROUND ONE_TABLE +#else +#define DEC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the last decryption round */ +#define LAST_DEC_ROUND FOUR_TABLES +#elif 0 +#define LAST_DEC_ROUND ONE_TABLE +#else +#define LAST_DEC_ROUND NO_TABLES +#endif + +/* The decryption key schedule can be speeded up with tables in the same + way that the round functions can. Include or exclude the following + defines to set this requirement. +*/ +#if 1 +#define KEY_SCHED FOUR_TABLES +#elif 0 +#define KEY_SCHED ONE_TABLE +#else +#define KEY_SCHED NO_TABLES +#endif + +/* ---- END OF USER CONFIGURED OPTIONS ---- */ + +/* VIA ACE support is only available for VC++ and GCC */ + +#if !defined(_MSC_VER) && !defined(__GNUC__) +#if defined(ASSUME_VIA_ACE_PRESENT) +#undef ASSUME_VIA_ACE_PRESENT +#endif +#if defined(USE_VIA_ACE_IF_PRESENT) +#undef USE_VIA_ACE_IF_PRESENT +#endif +#endif + +#if defined(ASSUME_VIA_ACE_PRESENT) && !defined(USE_VIA_ACE_IF_PRESENT) +#define USE_VIA_ACE_IF_PRESENT +#endif + +/* define to reverse decryption key schedule */ +#if 1 || defined(USE_VIA_ACE_IF_PRESENT) && !defined(AES_REV_DKS) +#define AES_REV_DKS +#endif + +/* Intel AESNI uses a decryption key schedule in the encryption order */ +#if defined(USE_INTEL_AES_IF_PRESENT) && defined(AES_REV_DKS) +#undef AES_REV_DKS +#endif + +/* Assembler support requires the use of platform byte order */ + +#if(defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || defined(ASM_AMD64_C)) && \ + (ALGORITHM_BYTE_ORDER != PLATFORM_BYTE_ORDER) +#undef ALGORITHM_BYTE_ORDER +#define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER +#endif + +/* In this implementation the columns of the state array are each held in + 32-bit words. The state array can be held in various ways: in an array + of words, in a number of individual word variables or in a number of + processor registers. The following define maps a variable name x and + a column number c to the way the state array variable is to be held. + The first define below maps the state into an array x[c] whereas the + second form maps the state into a number of individual variables x0, + x1, etc. Another form could map individual state colums to machine + register names. +*/ + +#if defined(ARRAYS) +#define s(x, c) x[c] +#else +#define s(x, c) x##c +#endif + +/* This implementation provides subroutines for encryption, decryption + and for setting the three key lengths (separately) for encryption + and decryption. Since not all functions are needed, masks are set + up here to determine which will be implemented in C +*/ + +#if !defined(AES_ENCRYPT) +#define EFUNCS_IN_C 0 +#elif defined(ASSUME_VIA_ACE_PRESENT) || defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || \ + defined(ASM_AMD64_C) +#define EFUNCS_IN_C ENC_KEYING_IN_C +#elif !defined(ASM_X86_V2) +#define EFUNCS_IN_C (ENCRYPTION_IN_C | ENC_KEYING_IN_C) +#else +#define EFUNCS_IN_C 0 +#endif + +#if !defined(AES_DECRYPT) +#define DFUNCS_IN_C 0 +#elif defined(ASSUME_VIA_ACE_PRESENT) || defined(ASM_X86_V1C) || defined(ASM_X86_V2C) || \ + defined(ASM_AMD64_C) +#define DFUNCS_IN_C DEC_KEYING_IN_C +#elif !defined(ASM_X86_V2) +#define DFUNCS_IN_C (DECRYPTION_IN_C | DEC_KEYING_IN_C) +#else +#define DFUNCS_IN_C 0 +#endif + +#define FUNCS_IN_C (EFUNCS_IN_C | DFUNCS_IN_C) + +/* END OF CONFIGURATION OPTIONS */ + +#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2)) + +/* Disable or report errors on some combinations of options */ + +#if ENC_ROUND == NO_TABLES && LAST_ENC_ROUND != NO_TABLES +#undef LAST_ENC_ROUND +#define LAST_ENC_ROUND NO_TABLES +#elif ENC_ROUND == ONE_TABLE && LAST_ENC_ROUND == FOUR_TABLES +#undef LAST_ENC_ROUND +#define LAST_ENC_ROUND ONE_TABLE +#endif + +#if ENC_ROUND == NO_TABLES && ENC_UNROLL != NONE +#undef ENC_UNROLL +#define ENC_UNROLL NONE +#endif + +#if DEC_ROUND == NO_TABLES && LAST_DEC_ROUND != NO_TABLES +#undef LAST_DEC_ROUND +#define LAST_DEC_ROUND NO_TABLES +#elif DEC_ROUND == ONE_TABLE && LAST_DEC_ROUND == FOUR_TABLES +#undef LAST_DEC_ROUND +#define LAST_DEC_ROUND ONE_TABLE +#endif + +#if DEC_ROUND == NO_TABLES && DEC_UNROLL != NONE +#undef DEC_UNROLL +#define DEC_UNROLL NONE +#endif + +#if defined(bswap32) +#define aes_sw32 bswap32 +#elif defined(bswap_32) +#define aes_sw32 bswap_32 +#else +#define brot(x, n) (((uint32_t)(x) << n) | ((uint32_t)(x) >> (32 - n))) +#define aes_sw32(x) ((brot((x), 8) & 0x00ff00ff) | (brot((x), 24) & 0xff00ff00)) +#endif + +/* upr(x,n): rotates bytes within words by n positions, moving bytes to + higher index positions with wrap around into low positions + ups(x,n): moves bytes by n positions to higher index positions in + words but without wrap around + bval(x,n): extracts a byte from a word + + WARNING: The definitions given here are intended only for use with + unsigned variables and with shift counts that are compile + time constants +*/ + +#if(ALGORITHM_BYTE_ORDER == IS_LITTLE_ENDIAN) +#define upr(x, n) (((uint32_t)(x) << (8 * (n))) | ((uint32_t)(x) >> (32 - 8 * (n)))) +#define ups(x, n) ((uint32_t)(x) << (8 * (n))) +#define bval(x, n) to_byte((x) >> (8 * (n))) +#define bytes2word(b0, b1, b2, b3) \ + (((uint32_t)(b3) << 24) | ((uint32_t)(b2) << 16) | ((uint32_t)(b1) << 8) | (b0)) +#endif + +#if(ALGORITHM_BYTE_ORDER == IS_BIG_ENDIAN) +#define upr(x, n) (((uint32_t)(x) >> (8 * (n))) | ((uint32_t)(x) << (32 - 8 * (n)))) +#define ups(x, n) ((uint32_t)(x) >> (8 * (n))) +#define bval(x, n) to_byte((x) >> (24 - 8 * (n))) +#define bytes2word(b0, b1, b2, b3) \ + (((uint32_t)(b0) << 24) | ((uint32_t)(b1) << 16) | ((uint32_t)(b2) << 8) | (b3)) +#endif + +#if defined(SAFE_IO) +#define word_in(x, c) \ + bytes2word( \ + ((const uint8_t*)(x) + 4 * c)[0], \ + ((const uint8_t*)(x) + 4 * c)[1], \ + ((const uint8_t*)(x) + 4 * c)[2], \ + ((const uint8_t*)(x) + 4 * c)[3]) +#define word_out(x, c, v) \ + { \ + ((uint8_t*)(x) + 4 * c)[0] = bval(v, 0); \ + ((uint8_t*)(x) + 4 * c)[1] = bval(v, 1); \ + ((uint8_t*)(x) + 4 * c)[2] = bval(v, 2); \ + ((uint8_t*)(x) + 4 * c)[3] = bval(v, 3); \ + } +#elif(ALGORITHM_BYTE_ORDER == PLATFORM_BYTE_ORDER) +#define word_in(x, c) (*((uint32_t*)(x) + (c))) +#define word_out(x, c, v) (*((uint32_t*)(x) + (c)) = (v)) +#else +#define word_in(x, c) aes_sw32(*((uint32_t*)(x) + (c))) +#define word_out(x, c, v) (*((uint32_t*)(x) + (c)) = aes_sw32(v)) +#endif + +/* the finite field modular polynomial and elements */ + +#define WPOLY 0x011b +#define BPOLY 0x1b + +/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + +#define gf_c1 0x80808080 +#define gf_c2 0x7f7f7f7f +#define gf_mulx(x) ((((x)&gf_c2) << 1) ^ ((((x)&gf_c1) >> 7) * BPOLY)) + +/* The following defines provide alternative definitions of gf_mulx that might + give improved performance if a fast 32-bit multiply is not available. Note + that a temporary variable u needs to be defined where gf_mulx is used. + +#define gf_mulx(x) (u = (x) & gf_c1, u |= (u >> 1), ((x) & gf_c2) << 1) ^ ((u >> 3) | (u >> 6)) +#define gf_c4 (0x01010101 * BPOLY) +#define gf_mulx(x) (u = (x) & gf_c1, ((x) & gf_c2) << 1) ^ ((u - (u >> 7)) & gf_c4) +*/ + +/* Work out which tables are needed for the different options */ + +#if defined(ASM_X86_V1C) +#if defined(ENC_ROUND) +#undef ENC_ROUND +#endif +#define ENC_ROUND FOUR_TABLES +#if defined(LAST_ENC_ROUND) +#undef LAST_ENC_ROUND +#endif +#define LAST_ENC_ROUND FOUR_TABLES +#if defined(DEC_ROUND) +#undef DEC_ROUND +#endif +#define DEC_ROUND FOUR_TABLES +#if defined(LAST_DEC_ROUND) +#undef LAST_DEC_ROUND +#endif +#define LAST_DEC_ROUND FOUR_TABLES +#if defined(KEY_SCHED) +#undef KEY_SCHED +#define KEY_SCHED FOUR_TABLES +#endif +#endif + +#if(FUNCS_IN_C & ENCRYPTION_IN_C) || defined(ASM_X86_V1C) +#if ENC_ROUND == ONE_TABLE +#define FT1_SET +#elif ENC_ROUND == FOUR_TABLES +#define FT4_SET +#else +#define SBX_SET +#endif +#if LAST_ENC_ROUND == ONE_TABLE +#define FL1_SET +#elif LAST_ENC_ROUND == FOUR_TABLES +#define FL4_SET +#elif !defined(SBX_SET) +#define SBX_SET +#endif +#endif + +#if(FUNCS_IN_C & DECRYPTION_IN_C) || defined(ASM_X86_V1C) +#if DEC_ROUND == ONE_TABLE +#define IT1_SET +#elif DEC_ROUND == FOUR_TABLES +#define IT4_SET +#else +#define ISB_SET +#endif +#if LAST_DEC_ROUND == ONE_TABLE +#define IL1_SET +#elif LAST_DEC_ROUND == FOUR_TABLES +#define IL4_SET +#elif !defined(ISB_SET) +#define ISB_SET +#endif +#endif + +#if !(defined(REDUCE_CODE_SIZE) && (defined(ASM_X86_V2) || defined(ASM_X86_V2C))) +#if((FUNCS_IN_C & ENC_KEYING_IN_C) || (FUNCS_IN_C & DEC_KEYING_IN_C)) +#if KEY_SCHED == ONE_TABLE +#if !defined(FL1_SET) && !defined(FL4_SET) +#define LS1_SET +#endif +#elif KEY_SCHED == FOUR_TABLES +#if !defined(FL4_SET) +#define LS4_SET +#endif +#elif !defined(SBX_SET) +#define SBX_SET +#endif +#endif +#if(FUNCS_IN_C & DEC_KEYING_IN_C) +#if KEY_SCHED == ONE_TABLE +#define IM1_SET +#elif KEY_SCHED == FOUR_TABLES +#define IM4_SET +#elif !defined(SBX_SET) +#define SBX_SET +#endif +#endif +#endif + +/* generic definitions of Rijndael macros that use tables */ + +#define no_table(x, box, vf, rf, c) \ + bytes2word( \ + box[bval(vf(x, 0, c), rf(0, c))], \ + box[bval(vf(x, 1, c), rf(1, c))], \ + box[bval(vf(x, 2, c), rf(2, c))], \ + box[bval(vf(x, 3, c), rf(3, c))]) + +#define one_table(x, op, tab, vf, rf, c) \ + (tab[bval(vf(x, 0, c), rf(0, c))] ^ op(tab[bval(vf(x, 1, c), rf(1, c))], 1) ^ \ + op(tab[bval(vf(x, 2, c), rf(2, c))], 2) ^ op(tab[bval(vf(x, 3, c), rf(3, c))], 3)) + +#define four_tables(x, tab, vf, rf, c) \ + (tab[0][bval(vf(x, 0, c), rf(0, c))] ^ tab[1][bval(vf(x, 1, c), rf(1, c))] ^ \ + tab[2][bval(vf(x, 2, c), rf(2, c))] ^ tab[3][bval(vf(x, 3, c), rf(3, c))]) + +#define vf1(x, r, c) (x) +#define rf1(r, c) (r) +#define rf2(r, c) ((8 + r - c) & 3) + +/* perform forward and inverse column mix operation on four bytes in long word x in */ +/* parallel. NOTE: x must be a simple variable, NOT an expression in these macros. */ + +#if !(defined(REDUCE_CODE_SIZE) && (defined(ASM_X86_V2) || defined(ASM_X86_V2C))) + +#if defined(FM4_SET) /* not currently used */ +#define fwd_mcol(x) four_tables(x, t_use(f, m), vf1, rf1, 0) +#elif defined(FM1_SET) /* not currently used */ +#define fwd_mcol(x) one_table(x, upr, t_use(f, m), vf1, rf1, 0) +#else +#define dec_fmvars uint32_t g2 +#define fwd_mcol(x) (g2 = gf_mulx(x), g2 ^ upr((x) ^ g2, 3) ^ upr((x), 2) ^ upr((x), 1)) +#endif + +#if defined(IM4_SET) +#define inv_mcol(x) four_tables(x, t_use(i, m), vf1, rf1, 0) +#elif defined(IM1_SET) +#define inv_mcol(x) one_table(x, upr, t_use(i, m), vf1, rf1, 0) +#else +#define dec_imvars uint32_t g2, g4, g9 +#define inv_mcol(x) \ + (g2 = gf_mulx(x), \ + g4 = gf_mulx(g2), \ + g9 = (x) ^ gf_mulx(g4), \ + g4 ^= g9, \ + (x) ^ g2 ^ g4 ^ upr(g2 ^ g9, 3) ^ upr(g4, 2) ^ upr(g9, 1)) +#endif + +#if defined(FL4_SET) +#define ls_box(x, c) four_tables(x, t_use(f, l), vf1, rf2, c) +#elif defined(LS4_SET) +#define ls_box(x, c) four_tables(x, t_use(l, s), vf1, rf2, c) +#elif defined(FL1_SET) +#define ls_box(x, c) one_table(x, upr, t_use(f, l), vf1, rf2, c) +#elif defined(LS1_SET) +#define ls_box(x, c) one_table(x, upr, t_use(l, s), vf1, rf2, c) +#else +#define ls_box(x, c) no_table(x, t_use(s, box), vf1, rf2, c) +#endif + +#endif + +#if defined(ASM_X86_V1C) && defined(AES_DECRYPT) && !defined(ISB_SET) +#define ISB_SET +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aestab.c b/applications/external/flipbip/lib/crypto/aes/aestab.c new file mode 100644 index 000000000..0bfb7808f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aestab.c @@ -0,0 +1,403 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +#define DO_TABLES + +#include "aes.h" +#include "aesopt.h" + +#if defined(STATIC_TABLES) + +#define sb_data(w) \ + { \ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5), w(0x30), w(0x01), \ + w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76), w(0xca), w(0x82), w(0xc9), \ + w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0), w(0xad), w(0xd4), w(0xa2), w(0xaf), \ + w(0x9c), w(0xa4), w(0x72), w(0xc0), w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), \ + w(0x3f), w(0xf7), w(0xcc), w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), \ + w(0x31), w(0x15), w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), \ + w(0x9a), w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75), \ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0), w(0x52), \ + w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84), w(0x53), w(0xd1), \ + w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b), w(0x6a), w(0xcb), w(0xbe), \ + w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf), w(0xd0), w(0xef), w(0xaa), w(0xfb), \ + w(0x43), w(0x4d), w(0x33), w(0x85), w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), \ + w(0x3c), w(0x9f), w(0xa8), w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), \ + w(0x38), w(0xf5), w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), \ + w(0xd2), w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17), \ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73), w(0x60), \ + w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88), w(0x46), w(0xee), \ + w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb), w(0xe0), w(0x32), w(0x3a), \ + w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c), w(0xc2), w(0xd3), w(0xac), w(0x62), \ + w(0x91), w(0x95), w(0xe4), w(0x79), w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), \ + w(0xd5), w(0x4e), w(0xa9), w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), \ + w(0xae), w(0x08), w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), \ + w(0xc6), w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a), \ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e), w(0x61), \ + w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e), w(0xe1), w(0xf8), \ + w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94), w(0x9b), w(0x1e), w(0x87), \ + w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf), w(0x8c), w(0xa1), w(0x89), w(0x0d), \ + w(0xbf), w(0xe6), w(0x42), w(0x68), w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), \ + w(0x54), w(0xbb), w(0x16) \ + } + +#define isb_data(w) \ + { \ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38), w(0xbf), w(0x40), \ + w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb), w(0x7c), w(0xe3), w(0x39), \ + w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87), w(0x34), w(0x8e), w(0x43), w(0x44), \ + w(0xc4), w(0xde), w(0xe9), w(0xcb), w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), \ + w(0xc2), w(0x23), w(0x3d), w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), \ + w(0xc3), w(0x4e), w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), \ + w(0xb2), w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25), \ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16), w(0xd4), \ + w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92), w(0x6c), w(0x70), \ + w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda), w(0x5e), w(0x15), w(0x46), \ + w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84), w(0x90), w(0xd8), w(0xab), w(0x00), \ + w(0x8c), w(0xbc), w(0xd3), w(0x0a), w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), \ + w(0xb3), w(0x45), w(0x06), w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), \ + w(0x0f), w(0x02), w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), \ + w(0x6b), w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea), \ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73), w(0x96), \ + w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85), w(0xe2), w(0xf9), \ + w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e), w(0x47), w(0xf1), w(0x1a), \ + w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89), w(0x6f), w(0xb7), w(0x62), w(0x0e), \ + w(0xaa), w(0x18), w(0xbe), w(0x1b), w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), \ + w(0xd2), w(0x79), w(0x20), w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), \ + w(0x5a), w(0xf4), w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), \ + w(0x31), w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f), \ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d), w(0x2d), \ + w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef), w(0xa0), w(0xe0), \ + w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0), w(0xc8), w(0xeb), w(0xbb), \ + w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61), w(0x17), w(0x2b), w(0x04), w(0x7e), \ + w(0xba), w(0x77), w(0xd6), w(0x26), w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), \ + w(0x21), w(0x0c), w(0x7d) \ + } + +#define mm_data(w) \ + { \ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07), w(0x08), w(0x09), \ + w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f), w(0x10), w(0x11), w(0x12), \ + w(0x13), w(0x14), w(0x15), w(0x16), w(0x17), w(0x18), w(0x19), w(0x1a), w(0x1b), \ + w(0x1c), w(0x1d), w(0x1e), w(0x1f), w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), \ + w(0x25), w(0x26), w(0x27), w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), \ + w(0x2e), w(0x2f), w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), \ + w(0x37), w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f), \ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47), w(0x48), \ + w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f), w(0x50), w(0x51), \ + w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57), w(0x58), w(0x59), w(0x5a), \ + w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f), w(0x60), w(0x61), w(0x62), w(0x63), \ + w(0x64), w(0x65), w(0x66), w(0x67), w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), \ + w(0x6d), w(0x6e), w(0x6f), w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), \ + w(0x76), w(0x77), w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), \ + w(0x7f), w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87), \ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f), w(0x90), \ + w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97), w(0x98), w(0x99), \ + w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f), w(0xa0), w(0xa1), w(0xa2), \ + w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7), w(0xa8), w(0xa9), w(0xaa), w(0xab), \ + w(0xac), w(0xad), w(0xae), w(0xaf), w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), \ + w(0xb5), w(0xb6), w(0xb7), w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), \ + w(0xbe), w(0xbf), w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), \ + w(0xc7), w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf), \ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7), w(0xd8), \ + w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf), w(0xe0), w(0xe1), \ + w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7), w(0xe8), w(0xe9), w(0xea), \ + w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef), w(0xf0), w(0xf1), w(0xf2), w(0xf3), \ + w(0xf4), w(0xf5), w(0xf6), w(0xf7), w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), \ + w(0xfd), w(0xfe), w(0xff) \ + } + +#define rc_data(w) \ + { w(0x01), w(0x02), w(0x04), w(0x08), w(0x10), w(0x20), w(0x40), w(0x80), w(0x1b), w(0x36) } + +#define h0(x) (x) + +#define w0(p) bytes2word(p, 0, 0, 0) +#define w1(p) bytes2word(0, p, 0, 0) +#define w2(p) bytes2word(0, 0, p, 0) +#define w3(p) bytes2word(0, 0, 0, p) + +#define u0(p) bytes2word(f2(p), p, p, f3(p)) +#define u1(p) bytes2word(f3(p), f2(p), p, p) +#define u2(p) bytes2word(p, f3(p), f2(p), p) +#define u3(p) bytes2word(p, p, f3(p), f2(p)) + +#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p)) +#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p)) +#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p)) +#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p)) + +#endif + +#if defined(STATIC_TABLES) || !defined(FF_TABLES) + +#define f2(x) ((x << 1) ^ (((x >> 7) & 1) * WPOLY)) +#define f4(x) ((x << 2) ^ (((x >> 6) & 1) * WPOLY) ^ (((x >> 6) & 2) * WPOLY)) +#define f8(x) \ + ((x << 3) ^ (((x >> 5) & 1) * WPOLY) ^ (((x >> 5) & 2) * WPOLY) ^ (((x >> 5) & 4) * WPOLY)) +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#else + +#define f2(x) ((x) ? pow[log[x] + 0x19] : 0) +#define f3(x) ((x) ? pow[log[x] + 0x01] : 0) +#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0) +#define fb(x) ((x) ? pow[log[x] + 0x68] : 0) +#define fd(x) ((x) ? pow[log[x] + 0xee] : 0) +#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0) + +#endif + +#include "aestab.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(STATIC_TABLES) + +/* implemented in case of wrong call for fixed tables */ + +AES_RETURN aes_init(void) { + return EXIT_SUCCESS; +} + +#else /* Generate the tables for the dynamic table option */ + +#if defined(FF_TABLES) + +#define gf_inv(x) ((x) ? pow[255 - log[x]] : 0) + +#else + +/* It will generally be sensible to use tables to compute finite + field multiplies and inverses but where memory is scarse this + code might sometimes be better. But it only has effect during + initialisation so its pretty unimportant in overall terms. +*/ + +/* return 2 ^ (n - 1) where n is the bit number of the highest bit + set in x with x in the range 1 < x < 0x00000200. This form is + used so that locals within fi can be bytes rather than words +*/ + +static uint8_t hibit(const uint32_t x) { + uint8_t r = (uint8_t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint8_t gf_inv(const uint8_t x) { + uint8_t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) return x; + + for(;;) { + if(n1) + while(n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= v1 * n2; /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if(n2) /* repeat with values swapped */ + while(n1 >= n2) { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +#endif + +/* The forward and inverse affine transformations used in the S-box */ +uint8_t fwd_affine(const uint8_t x) { + uint32_t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +} + +uint8_t inv_affine(const uint8_t x) { + uint32_t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +} + +static int init = 0; + +AES_RETURN aes_init(void) { + uint32_t i, w; + +#if defined(FF_TABLES) + + uint8_t pow[512] = {0}, log[256] = {0}; + + if(init) return EXIT_SUCCESS; + /* log and power tables for GF(2^8) finite field with + WPOLY as modular polynomial - the simplest primitive + root is 0x03, used here to generate the tables + */ + + i = 0; + w = 1; + do { + pow[i] = (uint8_t)w; + pow[i + 255] = (uint8_t)w; + log[w] = (uint8_t)i++; + w ^= (w << 1) ^ (w & 0x80 ? WPOLY : 0); + } while(w != 1); + +#else + if(init) return EXIT_SUCCESS; +#endif + + for(i = 0, w = 1; i < RC_LENGTH; ++i) { + t_set(r, c)[i] = bytes2word(w, 0, 0, 0); + w = f2(w); + } + + for(i = 0; i < 256; ++i) { + uint8_t b; + + b = fwd_affine(gf_inv((uint8_t)i)); + w = bytes2word(f2(b), b, b, f3(b)); + +#if defined(SBX_SET) + t_set(s, box)[i] = b; +#endif + +#if defined(FT1_SET) /* tables for a normal encryption round */ + t_set(f, n)[i] = w; +#endif +#if defined(FT4_SET) + t_set(f, n)[0][i] = w; + t_set(f, n)[1][i] = upr(w, 1); + t_set(f, n)[2][i] = upr(w, 2); + t_set(f, n)[3][i] = upr(w, 3); +#endif + w = bytes2word(b, 0, 0, 0); + +#if defined(FL1_SET) /* tables for last encryption round (may also */ + t_set(f, l)[i] = w; /* be used in the key schedule) */ +#endif +#if defined(FL4_SET) + t_set(f, l)[0][i] = w; + t_set(f, l)[1][i] = upr(w, 1); + t_set(f, l)[2][i] = upr(w, 2); + t_set(f, l)[3][i] = upr(w, 3); +#endif + +#if defined(LS1_SET) /* table for key schedule if t_set(f,l) above is*/ + t_set(l, s)[i] = w; /* not of the required form */ +#endif +#if defined(LS4_SET) + t_set(l, s)[0][i] = w; + t_set(l, s)[1][i] = upr(w, 1); + t_set(l, s)[2][i] = upr(w, 2); + t_set(l, s)[3][i] = upr(w, 3); +#endif + + b = gf_inv(inv_affine((uint8_t)i)); + w = bytes2word(fe(b), f9(b), fd(b), fb(b)); + +#if defined(IM1_SET) /* tables for the inverse mix column operation */ + t_set(i, m)[b] = w; +#endif +#if defined(IM4_SET) + t_set(i, m)[0][b] = w; + t_set(i, m)[1][b] = upr(w, 1); + t_set(i, m)[2][b] = upr(w, 2); + t_set(i, m)[3][b] = upr(w, 3); +#endif + +#if defined(ISB_SET) + t_set(i, box)[i] = b; +#endif +#if defined(IT1_SET) /* tables for a normal decryption round */ + t_set(i, n)[i] = w; +#endif +#if defined(IT4_SET) + t_set(i, n)[0][i] = w; + t_set(i, n)[1][i] = upr(w, 1); + t_set(i, n)[2][i] = upr(w, 2); + t_set(i, n)[3][i] = upr(w, 3); +#endif + w = bytes2word(b, 0, 0, 0); +#if defined(IL1_SET) /* tables for last decryption round */ + t_set(i, l)[i] = w; +#endif +#if defined(IL4_SET) + t_set(i, l)[0][i] = w; + t_set(i, l)[1][i] = upr(w, 1); + t_set(i, l)[2][i] = upr(w, 2); + t_set(i, l)[3][i] = upr(w, 3); +#endif + } + init = 1; + return EXIT_SUCCESS; +} + +/* + Automatic code initialisation (suggested by by Henrik S. Gaßmann) + based on code provided by Joe Lowe and placed in the public domain at: + http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +*/ + +#ifdef _MSC_VER + +#pragma section(".CRT$XCU", read) + +__declspec(allocate(".CRT$XCU")) void(__cdecl* aes_startup)(void) = aes_init; + +#elif defined(__GNUC__) + +static void aes_startup(void) __attribute__((constructor)); + +static void aes_startup(void) { + aes_init(); +} + +#else + +#pragma message("dynamic tables must be initialised manually on your system") + +#endif + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aestab.h b/applications/external/flipbip/lib/crypto/aes/aestab.h new file mode 100644 index 000000000..9dfcded1a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aestab.h @@ -0,0 +1,173 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 + + This file contains the code for declaring the tables needed to implement + AES. The file aesopt.h is assumed to be included before this header file. + If there are no global variables, the definitions here can be used to put + the AES tables in a structure so that a pointer can then be added to the + AES context to pass them to the AES routines that need them. If this + facility is used, the calling program has to ensure that this pointer is + managed appropriately. In particular, the value of the t_dec(in,it) item + in the table structure must be set to zero in order to ensure that the + tables are initialised. In practice the three code sequences in aeskey.c + that control the calls to aes_init() and the aes_init() routine itself will + have to be changed for a specific implementation. If global variables are + available it will generally be preferable to use them with the precomputed + STATIC_TABLES option that uses static global tables. + + The following defines can be used to control the way the tables + are defined, initialised and used in embedded environments that + require special features for these purposes + + the 't_dec' construction is used to declare fixed table arrays + the 't_set' construction is used to set fixed table values + the 't_use' construction is used to access fixed table values + + 256 byte tables: + + t_xxx(s,box) => forward S box + t_xxx(i,box) => inverse S box + + 256 32-bit word OR 4 x 256 32-bit word tables: + + t_xxx(f,n) => forward normal round + t_xxx(f,l) => forward last round + t_xxx(i,n) => inverse normal round + t_xxx(i,l) => inverse last round + t_xxx(l,s) => key schedule table + t_xxx(i,m) => key schedule table + + Other variables and tables: + + t_xxx(r,c) => the rcon table +*/ + +#if !defined(_AESTAB_H) +#define _AESTAB_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#define t_dec(m, n) t_##m##n +#define t_set(m, n) t_##m##n +#define t_use(m, n) t_##m##n + +#if defined(STATIC_TABLES) +#if !defined(__GNUC__) && (defined(__MSDOS__) || defined(__WIN16__)) +/* make tables far data to avoid using too much DGROUP space (PG) */ +#define CONST const far +#else +#define CONST const +#endif +#else +#define CONST +#endif + +#if defined(DO_TABLES) +#define EXTERN +#else +#define EXTERN extern +#endif + +#if defined(_MSC_VER) && defined(TABLE_ALIGN) +#define ALIGN __declspec(align(TABLE_ALIGN)) +#else +#define ALIGN +#endif + +#if defined(__WATCOMC__) && (__WATCOMC__ >= 1100) +#define XP_DIR __cdecl +#else +#define XP_DIR +#endif + +#if defined(DO_TABLES) && defined(STATIC_TABLES) +#define d_1(t, n, b, e) EXTERN ALIGN CONST XP_DIR t n[256] = b(e) +#define d_4(t, n, b, e, f, g, h) EXTERN ALIGN CONST XP_DIR t n[4][256] = {b(e), b(f), b(g), b(h)} +EXTERN ALIGN CONST uint32_t t_dec(r, c)[RC_LENGTH] = rc_data(w0); +#else +#define d_1(t, n, b, e) EXTERN ALIGN CONST XP_DIR t n[256] +#define d_4(t, n, b, e, f, g, h) EXTERN ALIGN CONST XP_DIR t n[4][256] +EXTERN ALIGN CONST uint32_t t_dec(r, c)[RC_LENGTH]; +#endif + +#if defined(SBX_SET) +d_1(uint8_t, t_dec(s, box), sb_data, h0); +#endif +#if defined(ISB_SET) +d_1(uint8_t, t_dec(i, box), isb_data, h0); +#endif + +#if defined(FT1_SET) +d_1(uint32_t, t_dec(f, n), sb_data, u0); +#endif +#if defined(FT4_SET) +d_4(uint32_t, t_dec(f, n), sb_data, u0, u1, u2, u3); +#endif + +#if defined(FL1_SET) +d_1(uint32_t, t_dec(f, l), sb_data, w0); +#endif +#if defined(FL4_SET) +d_4(uint32_t, t_dec(f, l), sb_data, w0, w1, w2, w3); +#endif + +#if defined(IT1_SET) +d_1(uint32_t, t_dec(i, n), isb_data, v0); +#endif +#if defined(IT4_SET) +d_4(uint32_t, t_dec(i, n), isb_data, v0, v1, v2, v3); +#endif + +#if defined(IL1_SET) +d_1(uint32_t, t_dec(i, l), isb_data, w0); +#endif +#if defined(IL4_SET) +d_4(uint32_t, t_dec(i, l), isb_data, w0, w1, w2, w3); +#endif + +#if defined(LS1_SET) +#if defined(FL1_SET) +#undef LS1_SET +#else +d_1(uint32_t, t_dec(l, s), sb_data, w0); +#endif +#endif + +#if defined(LS4_SET) +#if defined(FL4_SET) +#undef LS4_SET +#else +d_4(uint32_t, t_dec(l, s), sb_data, w0, w1, w2, w3); +#endif +#endif + +#if defined(IM1_SET) +d_1(uint32_t, t_dec(i, m), mm_data, v0); +#endif +#if defined(IM4_SET) +d_4(uint32_t, t_dec(i, m), mm_data, v0, v1, v2, v3); +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/aes/aestst.c b/applications/external/flipbip/lib/crypto/aes/aestst.c new file mode 100644 index 000000000..f9d6465dd --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aestst.c @@ -0,0 +1,189 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +// Correct Output (for variable block size - AES_BLOCK_SIZE undefined): + +// lengths: block = 16 bytes, key = 16 bytes +// key = 2b7e151628aed2a6abf7158809cf4f3c +// input = 3243f6a8885a308d313198a2e0370734 +// encrypt = 3925841d02dc09fbdc118597196a0b32 +// decrypt = 3243f6a8885a308d313198a2e0370734 + +// lengths: block = 16 bytes, key = 24 bytes +// key = 2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da5 +// input = 3243f6a8885a308d313198a2e0370734 +// encrypt = f9fb29aefc384a250340d833b87ebc00 +// decrypt = 3243f6a8885a308d313198a2e0370734 + +// lengths: block = 16 bytes, key = 32 bytes +// key = 2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfe +// input = 3243f6a8885a308d313198a2e0370734 +// encrypt = 1a6e6c2c662e7da6501ffb62bc9e93f3 +// decrypt = 3243f6a8885a308d313198a2e0370734 + +#include +#include + +#include "aes.h" +#include "aestst.h" + +void out_state(long s0, long s1, long s2, long s3) { + printf("\n%08lx%08lx%08lx%08lx", s0, s1, s2, s3); +} + +void oblk(char m[], unsigned char v[], unsigned long n) { + unsigned long i; + + printf("\n%s", m); + + for(i = 0; i < n; ++i) printf("%02x", v[i]); +} + +void message(const char* s) { + printf("%s", s); +} + +unsigned char pih[32] = // hex digits of pi + {0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, + 0xa2, 0xe0, 0x37, 0x07, 0x34, 0x4a, 0x40, 0x93, 0x82, 0x22, 0x99, + 0xf3, 0x1d, 0x00, 0x82, 0xef, 0xa9, 0x8e, 0xc4, 0xe6, 0xc8}; + +unsigned char exh[32] = // hex digits of e + {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, + 0x88, 0x09, 0xcf, 0x4f, 0x3c, 0x76, 0x2e, 0x71, 0x60, 0xf3, 0x8b, + 0x4d, 0xa5, 0x6a, 0x78, 0x4d, 0x90, 0x45, 0x19, 0x0c, 0xfe}; + +unsigned char res[3][32] = { + {0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32}, + {0xf9, 0xfb, 0x29, 0xae, 0xfc, 0x38, 0x4a, 0x25, 0x03, 0x40, 0xd8, 0x33, 0xb8, 0x7e, 0xbc, 0x00}, + {0x1a, 0x6e, 0x6c, 0x2c, 0x66, 0x2e, 0x7d, 0xa6, 0x50, 0x1f, 0xfb, 0x62, 0xbc, 0x9e, 0x93, 0xf3}}; + +// void cycles(volatile uint64_t *rtn) +// { +// #if defined( _MSCVER ) +// __asm // read the Pentium Time Stamp Counter +// { cpuid +// rdtsc +// mov ecx,rtn +// mov [ecx],eax +// mov [ecx+4],edx +// cpuid +// } +// #elif defined( __GNUC__ ) +// #if defined(__aarch64__) +// __asm__ __volatile__("mrs %0, cntvct_el0": "=r" (*rtn)); +// #else +// __asm__ __volatile__("rdtsc": "=A" (*rtn)); +// #endif +// #endif +// } + +int main(void) { + unsigned char out[32], ret[32], err = 0; + f_ectx alge[1]; + f_dctx algd[1]; + + aes_init(); + + message("\nRun tests for the AES algorithm"); + + memset(&alge, 0, sizeof(aes_encrypt_ctx)); + memset(&algd, 0, sizeof(aes_decrypt_ctx)); + +#if defined(AES_128) + memset(out, 0xcc, 16); + memset(ret, 0xcc, 16); + printf("\n\n// lengths: block = 16, bytes, key = 16 bytes"); + f_enc_key128(alge, exh); + oblk("// key = ", exh, 16); + oblk("// input = ", pih, 16); + do_enc(alge, pih, out, 1); + oblk("// encrypt = ", out, 16); + if(memcmp(out, res[0], 16)) { + message(" error"); + err += 1; + } + f_dec_key128(algd, exh); + do_dec(algd, out, ret, 1); + oblk("// decrypt = ", ret, 16); + if(memcmp(ret, pih, 16)) { + message(" error"); + err += 2; + } +#endif + +#if defined(AES_192) + memset(out, 0xcc, 16); + memset(ret, 0xcc, 16); + printf("\n\n// lengths: block = 16, bytes, key = 24 bytes"); + f_enc_key192(alge, exh); + oblk("// key = ", exh, 24); + oblk("// input = ", pih, 16); + do_enc(alge, pih, out, 1); + oblk("// encrypt = ", out, 16); + if(memcmp(out, res[1], 16)) { + message(" error"); + err += 4; + } + f_dec_key192(algd, exh); + do_dec(algd, out, ret, 1); + oblk("// decrypt = ", ret, 16); + if(memcmp(ret, pih, 16)) { + message(" error"); + err += 8; + } +#endif + +#if defined(AES_256) + memset(out, 0xcc, 16); + memset(ret, 0xcc, 16); + printf("\n\n// lengths: block = 16, bytes, key = 32 bytes"); + f_enc_key256(alge, exh); + oblk("// key = ", exh, 32); + oblk("// input = ", pih, 16); + do_enc(alge, pih, out, 1); + oblk("// encrypt = ", out, 16); + if(memcmp(out, res[2], 16)) { + message(" error"); + err += 16; + } + f_dec_key256(algd, exh); + do_dec(algd, out, ret, 1); + oblk("// decrypt = ", ret, 16); + if(memcmp(ret, pih, 16)) { + message(" error"); + err += 32; + } +#endif + + if(!err) + message("\n\nThese values are all correct\n\n"); + else + message("\n\nSome values are in error\n\n"); + + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/aes/aestst.h b/applications/external/flipbip/lib/crypto/aes/aestst.h new file mode 100644 index 000000000..bb38b0434 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/aes/aestst.h @@ -0,0 +1,85 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 20/12/2007 +*/ + +// The following definitions are required for testing only, They are not needed +// for AES (Rijndael) implementation. They are used to allow C, C++ and DLL +// data access and subroutine calls to be expressed in the same form in the +// testing code. + +#ifndef AESTST_H +#define AESTST_H + +#define f_info(x) (x)->inf.b[2] +#define f_ectx aes_encrypt_ctx +#define f_enc_key128(a, b) aes_encrypt_key128((b), (a)) +#define f_enc_key192(a, b) aes_encrypt_key192((b), (a)) +#define f_enc_key256(a, b) aes_encrypt_key256((b), (a)) +#define f_enc_key(a, b, c) aes_encrypt_key((b), (c), (a)) +#define f_enc_blk(a, b, c) aes_encrypt((b), (c), (a)) + +#define f_dctx aes_decrypt_ctx +#define f_dec_key128(a, b) aes_decrypt_key128((b), (a)) +#define f_dec_key192(a, b) aes_decrypt_key192((b), (a)) +#define f_dec_key256(a, b) aes_decrypt_key256((b), (a)) +#define f_dec_key(a, b, c) aes_decrypt_key((b), (c), (a)) +#define f_dec_blk(a, b, c) aes_decrypt((b), (c), (a)) + +#define f_talign(a, b) aes_test_alignment_detection(b) +#define f_mode_reset(a) aes_mode_reset(a) +#define f_ecb_enc(a, b, c, d) aes_ecb_encrypt((b), (c), (d), (a)) +#define f_ecb_dec(a, b, c, d) aes_ecb_decrypt((b), (c), (d), (a)) +#define f_cbc_enc(a, b, c, d, e) aes_cbc_encrypt((b), (c), (d), (e), (a)) +#define f_cbc_dec(a, b, c, d, e) aes_cbc_decrypt((b), (c), (d), (e), (a)) +#define f_cfb_enc(a, b, c, d, e) aes_cfb_encrypt((b), (c), (d), (e), (a)) +#define f_cfb_dec(a, b, c, d, e) aes_cfb_decrypt((b), (c), (d), (e), (a)) +#define f_ofb_cry(a, b, c, d, e) aes_ofb_crypt((b), (c), (d), (e), (a)) +#define f_ctr_cry(a, b, c, d, e, f) aes_ctr_crypt((b), (c), (d), (e), (f), (a)) + +#define ek_name128 "aes_encrypt_key128" +#define ek_name192 "aes_encrypt_key192" +#define ek_name256 "aes_encrypt_key256" +#define ek_name "aes_encrypt_key" +#define eb_name "aes_encrypt" + +#define dk_name128 "aes_decrypt_key128" +#define dk_name192 "aes_decrypt_key192" +#define dk_name256 "aes_decrypt_key256" +#define dk_name "aes_decrypt_key" +#define db_name "aes_decrypt" + +#define eres_name "aes_mode_reset" +#define ecbe_name "aes_ecb_encrypt" +#define ecbd_name "aes_ecb_decrypt" +#define cbce_name "aes_cbc_encrypt" +#define cbcd_name "aes_cbc_decrypt" +#define cfbe_name "aes_cfb_encrypt" +#define cfbd_name "aes_cfb_decrypt" +#define ofb_name "aes_ofb_crypt" +#define ctr_name "aes_ctr_crypt" + +#ifndef AES_N_BLOCK +#define do_enc(a, b, c, d) f_enc_blk(a, b, c) +#define do_dec(a, b, c, d) f_dec_blk(a, b, c) +#else +#define do_enc(a, b, c, d) f_ecb_enc(a, b, c, 1) +#define do_dec(a, b, c, d) f_ecb_dec(a, b, c, 1) +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/base32.c b/applications/external/flipbip/lib/crypto/base32.c new file mode 100644 index 000000000..e211bb308 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/base32.c @@ -0,0 +1,241 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E1PRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "base32.h" + +#include + +const char* BASE32_ALPHABET_RFC4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ23456789"; + +static inline void base32_5to8(const uint8_t* in, uint8_t length, uint8_t* out); +static inline bool + base32_8to5(const uint8_t* in, uint8_t length, uint8_t* out, const char* alphabet); +static inline void base32_8to5_raw(const uint8_t* in, uint8_t length, uint8_t* out); + +static inline int base32_encode_character(uint8_t decoded, const char* alphabet); +static inline int base32_decode_character(char encoded, const char* alphabet); + +char* base32_encode( + const uint8_t* in, + size_t inlen, + char* out, + size_t outlen, + const char* alphabet) { + size_t length = base32_encoded_length(inlen); + if(outlen <= length) { + return NULL; + } + + base32_encode_unsafe(in, inlen, (uint8_t*)out); + + for(size_t i = 0; i < length; i++) { + int ret = base32_encode_character(out[i], alphabet); + + if(ret == -1) { + return false; + } else { + out[i] = ret; + } + } + + out[length] = '\0'; + return &out[length]; +} + +uint8_t* + base32_decode(const char* in, size_t inlen, uint8_t* out, size_t outlen, const char* alphabet) { + size_t length = base32_decoded_length(inlen); + if(outlen < length) { + return NULL; + } + + if(!base32_decode_unsafe((uint8_t*)in, inlen, (uint8_t*)out, alphabet)) { + return NULL; + } + + return &out[length]; +} + +void base32_encode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out) { + uint8_t remainder = inlen % 5; + size_t limit = inlen - remainder; + + size_t i = 0, j = 0; + for(i = 0, j = 0; i < limit; i += 5, j += 8) { + base32_5to8(&in[i], 5, &out[j]); + } + + if(remainder) base32_5to8(&in[i], remainder, &out[j]); +} + +bool base32_decode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out, const char* alphabet) { + uint8_t remainder = inlen % 8; + size_t limit = inlen - remainder; + + size_t i = 0, j = 0; + for(i = 0, j = 0; i < limit; i += 8, j += 5) { + if(!base32_8to5(&in[i], 8, &out[j], alphabet)) { + return false; + } + } + + if(remainder && !base32_8to5(&in[i], remainder, &out[j], alphabet)) { + return false; + } + + return true; +} + +size_t base32_encoded_length(size_t inlen) { + uint8_t remainder = inlen % 5; + + return (inlen / 5) * 8 + (remainder * 8 + 4) / 5; +} + +size_t base32_decoded_length(size_t inlen) { + uint8_t remainder = inlen % 8; + + return (inlen / 8) * 5 + (remainder * 5) / 8; +} + +void base32_5to8(const uint8_t* in, uint8_t length, uint8_t* out) { + if(length >= 1) { + out[0] = (in[0] >> 3); + out[1] = (in[0] & 7) << 2; + } + + if(length >= 2) { + out[1] |= (in[1] >> 6); + out[2] = (in[1] >> 1) & 31; + out[3] = (in[1] & 1) << 4; + } + + if(length >= 3) { + out[3] |= (in[2] >> 4); + out[4] = (in[2] & 15) << 1; + } + + if(length >= 4) { + out[4] |= (in[3] >> 7); + out[5] = (in[3] >> 2) & 31; + out[6] = (in[3] & 3) << 3; + } + + if(length >= 5) { + out[6] |= (in[4] >> 5); + out[7] = (in[4] & 31); + } +} + +bool base32_8to5(const uint8_t* in, uint8_t length, uint8_t* out, const char* alphabet) { + if(length == 1 || length == 3 || length == 6 || length > 8) { + return false; + } + + if(alphabet) { + uint8_t decoded[length]; + memset(decoded, 0, sizeof(decoded)); + + for(size_t i = 0; i < length; i++) { + int ret = base32_decode_character(in[i], alphabet); + + if(ret == -1) { + return false; + } else { + decoded[i] = ret; + } + } + + base32_8to5_raw(decoded, length, out); + } else { + base32_8to5_raw(in, length, out); + } + + return true; +} + +void base32_8to5_raw(const uint8_t* in, uint8_t length, uint8_t* out) { + if(length >= 2) { + out[0] = (in[0] << 3); + out[0] |= (in[1] >> 2); + } + + if(length >= 4) { + out[1] = (in[1] & 3) << 6; + out[1] |= (in[2] << 1); + out[1] |= (in[3] >> 4); + } + + if(length >= 5) { + out[2] = (in[3] & 15) << 4; + out[2] |= (in[4] >> 1); + } + + if(length >= 7) { + out[3] = (in[4] & 1) << 7; + out[3] |= (in[5] << 2); + out[3] |= (in[6] >> 3); + } + + if(length >= 8) { + out[4] = (in[6] & 7) << 5; + out[4] |= (in[7] & 31); + } +} + +int base32_encode_character(uint8_t decoded, const char* alphabet) { + if(decoded >> 5) { + return -1; + } + + if(alphabet == BASE32_ALPHABET_RFC4648) { + if(decoded < 26) { + return 'A' + decoded; + } else { + return '2' - 26 + decoded; + } + } + + return alphabet[decoded]; +} + +int base32_decode_character(char encoded, const char* alphabet) { + if(alphabet == BASE32_ALPHABET_RFC4648) { + if(encoded >= 'A' && encoded <= 'Z') { + return encoded - 'A'; + } else if(encoded >= 'a' && encoded <= 'z') { + return encoded - 'a'; + } else if(encoded >= '2' && encoded <= '7') { + return encoded - '2' + 26; + } else { + return -1; + } + } + + const char* occurrence = strchr(alphabet, encoded); + + if(occurrence) { + return occurrence - alphabet; + } else { + return -1; + } +} diff --git a/applications/external/flipbip/lib/crypto/base32.h b/applications/external/flipbip/lib/crypto/base32.h new file mode 100644 index 000000000..5e214aabb --- /dev/null +++ b/applications/external/flipbip/lib/crypto/base32.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BASE32_H__ +#define __BASE32_H__ + +#include +#include +#include + +extern const char* BASE32_ALPHABET_RFC4648; + +char* base32_encode(const uint8_t* in, size_t inlen, char* out, size_t outlen, const char* alphabet); +void base32_encode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out); + +uint8_t* + base32_decode(const char* in, size_t inlen, uint8_t* out, size_t outlen, const char* alphabet); +bool base32_decode_unsafe(const uint8_t* in, size_t inlen, uint8_t* out, const char* alphabet); + +size_t base32_encoded_length(size_t inlen); +size_t base32_decoded_length(size_t inlen); + +#endif diff --git a/applications/external/flipbip/lib/crypto/base58.c b/applications/external/flipbip/lib/crypto/base58.c new file mode 100644 index 000000000..dbcd3832f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/base58.c @@ -0,0 +1,219 @@ +/** + * Copyright (c) 2012-2014 Luke Dashjr + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "base58.h" +#include +#include +#include "memzero.h" +#include "ripemd160.h" +#include "sha2.h" + +const char b58digits_ordered[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +const int8_t b58digits_map[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, 9, + 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, -1, -1, -1, -1, -1, -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1, +}; + +typedef uint64_t b58_maxint_t; +typedef uint32_t b58_almostmaxint_t; +#define b58_almostmaxint_bits (sizeof(b58_almostmaxint_t) * 8) +static const b58_almostmaxint_t b58_almostmaxint_mask = + ((((b58_maxint_t)1) << b58_almostmaxint_bits) - 1); + +// Decodes a null-terminated Base58 string `b58` to binary and writes the result +// at the end of the buffer `bin` of size `*binszp`. On success `*binszp` is set +// to the number of valid bytes at the end of the buffer. +bool b58tobin(void* bin, size_t* binszp, const char* b58) { + size_t binsz = *binszp; + + if(binsz == 0) { + return false; + } + + const unsigned char* b58u = (const unsigned char*)b58; + unsigned char* binu = bin; + size_t outisz = (binsz + sizeof(b58_almostmaxint_t) - 1) / sizeof(b58_almostmaxint_t); + b58_almostmaxint_t outi[outisz]; + b58_maxint_t t = 0; + b58_almostmaxint_t c = 0; + size_t i = 0, j = 0; + uint8_t bytesleft = binsz % sizeof(b58_almostmaxint_t); + b58_almostmaxint_t zeromask = bytesleft ? (b58_almostmaxint_mask << (bytesleft * 8)) : 0; + unsigned zerocount = 0; + + size_t b58sz = strlen(b58); + + memzero(outi, sizeof(outi)); + + // Leading zeros, just count + for(i = 0; i < b58sz && b58u[i] == '1'; ++i) ++zerocount; + + for(; i < b58sz; ++i) { + if(b58u[i] & 0x80) + // High-bit set on invalid digit + return false; + if(b58digits_map[b58u[i]] == -1) + // Invalid base58 digit + return false; + c = (unsigned)b58digits_map[b58u[i]]; + for(j = outisz; j--;) { + t = ((b58_maxint_t)outi[j]) * 58 + c; + c = t >> b58_almostmaxint_bits; + outi[j] = t & b58_almostmaxint_mask; + } + if(c) + // Output number too big (carry to the next int32) + return false; + if(outi[0] & zeromask) + // Output number too big (last int32 filled too far) + return false; + } + + j = 0; + if(bytesleft) { + for(i = bytesleft; i > 0; --i) { + *(binu++) = (outi[0] >> (8 * (i - 1))) & 0xff; + } + ++j; + } + + for(; j < outisz; ++j) { + for(i = sizeof(*outi); i > 0; --i) { + *(binu++) = (outi[j] >> (8 * (i - 1))) & 0xff; + } + } + + // locate the most significant byte + binu = bin; + for(i = 0; i < binsz; ++i) { + if(binu[i]) break; + } + + // prepend the correct number of null-bytes + if(zerocount > i) { + /* result too large */ + return false; + } + *binszp = binsz - i + zerocount; + + return true; +} + +int b58check(const void* bin, size_t binsz, HasherType hasher_type, const char* base58str) { + unsigned char buf[32] = {0}; + const uint8_t* binc = bin; + unsigned i = 0; + if(binsz < 4) return -4; + hasher_Raw(hasher_type, bin, binsz - 4, buf); + if(memcmp(&binc[binsz - 4], buf, 4)) return -1; + + // Check number of zeros is correct AFTER verifying checksum (to avoid + // possibility of accessing base58str beyond the end) + for(i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) { + } // Just finding the end of zeros, nothing to do in loop + if(binc[i] == '\0' || base58str[i] == '1') return -3; + + return binc[0]; +} + +bool b58enc(char* b58, size_t* b58sz, const void* data, size_t binsz) { + const uint8_t* bin = data; + int carry = 0; + size_t i = 0, j = 0, high = 0, zcount = 0; + size_t size = 0; + + while(zcount < binsz && !bin[zcount]) ++zcount; + + size = (binsz - zcount) * 138 / 100 + 1; + uint8_t buf[size]; + memzero(buf, size); + + for(i = zcount, high = size - 1; i < binsz; ++i, high = j) { + for(carry = bin[i], j = size - 1; (j > high) || carry; --j) { + carry += 256 * buf[j]; + buf[j] = carry % 58; + carry /= 58; + if(!j) { + // Otherwise j wraps to maxint which is > high + break; + } + } + } + + for(j = 0; j < size && !buf[j]; ++j) + ; + + if(*b58sz <= zcount + size - j) { + *b58sz = zcount + size - j + 1; + return false; + } + + if(zcount) memset(b58, '1', zcount); + for(i = zcount; j < size; ++i, ++j) b58[i] = b58digits_ordered[buf[j]]; + b58[i] = '\0'; + *b58sz = i + 1; + + return true; +} + +int base58_encode_check( + const uint8_t* data, + int datalen, + HasherType hasher_type, + char* str, + int strsize) { + if(datalen > 128) { + return 0; + } + uint8_t buf[datalen + 32]; + memset(buf, 0, sizeof(buf)); + uint8_t* hash = buf + datalen; + memcpy(buf, data, datalen); + hasher_Raw(hasher_type, data, datalen, hash); + size_t res = strsize; + bool success = b58enc(str, &res, buf, datalen + 4); + memzero(buf, sizeof(buf)); + return success ? res : 0; +} + +int base58_decode_check(const char* str, HasherType hasher_type, uint8_t* data, int datalen) { + if(datalen > 128) { + return 0; + } + uint8_t d[datalen + 4]; + memset(d, 0, sizeof(d)); + size_t res = datalen + 4; + if(b58tobin(d, &res, str) != true) { + return 0; + } + uint8_t* nd = d + datalen + 4 - res; + if(b58check(nd, res, hasher_type, str) < 0) { + return 0; + } + memcpy(data, nd, res - 4); + return res - 4; +} diff --git a/applications/external/flipbip/lib/crypto/base58.h b/applications/external/flipbip/lib/crypto/base58.h new file mode 100644 index 000000000..0f7fa66d3 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/base58.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BASE58_H__ +#define __BASE58_H__ + +#include +#include +#include "hasher.h" +#include "options.h" + +extern const char b58digits_ordered[]; +extern const int8_t b58digits_map[]; + +int base58_encode_check( + const uint8_t* data, + int len, + HasherType hasher_type, + char* str, + int strsize); +int base58_decode_check(const char* str, HasherType hasher_type, uint8_t* data, int datalen); + +// Private +bool b58tobin(void* bin, size_t* binszp, const char* b58); +int b58check(const void* bin, size_t binsz, HasherType hasher_type, const char* base58str); +bool b58enc(char* b58, size_t* b58sz, const void* data, size_t binsz); + +#endif diff --git a/applications/external/flipbip/lib/crypto/bignum.c b/applications/external/flipbip/lib/crypto/bignum.c new file mode 100644 index 000000000..f0c7d6e43 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bignum.c @@ -0,0 +1,1834 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015 Jochen Hoenicke + * Copyright (c) 2016 Alex Beregszaszi + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "bignum.h" + +#include +#include +#include +#include + +#include "memzero.h" +#include "script.h" + +/* + This library implements 256-bit numbers arithmetic. + + An unsigned 256-bit number is represented by a bignum256 structure, that is an + array of nine 32-bit values called limbs. Limbs are digits of the number in + the base 2**29 representation in the little endian order. This means that + bignum256 x; + represents the value + sum([x[i] * 2**(29*i) for i in range(9)). + + A limb of a bignum256 is *normalized* iff it's less than 2**29. + A bignum256 is *normalized* iff every its limb is normalized. + A number is *fully reduced modulo p* iff it is less than p. + A number is *partly reduced modulo p* iff is is less than 2*p. + The number p is usually a prime number such that 2^256 - 2^224 <= p <= 2^256. + + All functions except bn_fast_mod expect that all their bignum256 inputs are + normalized. (The function bn_fast_mod allows the input number to have the + most significant limb unnormalized). All bignum256 outputs of all functions + are guaranteed to be normalized. + + A number can be partly reduced with bn_fast_mod, a partly reduced number can + be fully reduced with bn_mod. + + A function has *constant control flow with regard to its argument* iff the + order in which instructions of the function are executed doesn't depend on the + value of the argument. + A function has *constant memory access flow with regard to its argument* iff + the memory addresses that are acessed and the order in which they are accessed + don't depend on the value of the argument. + A function *has contant control (memory access) flow* iff it has constant + control (memory access) flow with regard to all its arguments. + + The following function has contant control flow with regard to its arugment + n, however is doesn't have constant memory access flow with regard to it: + void (int n, int *a) } + a[0] = 0; + a[n] = 0; // memory address reveals the value of n + } + + Unless stated otherwise all functions are supposed to have both constant + control flow and constant memory access flow. + */ + +#define BN_MAX_DECIMAL_DIGITS 79 // floor(log(2**(LIMBS * BITS_PER_LIMB), 10)) + 1 + +// out_number = (bignum256) in_number +// Assumes in_number is a raw bigendian 256-bit number +// Guarantees out_number is normalized +void bn_read_be(const uint8_t* in_number, bignum256* out_number) { + uint32_t temp = 0; + + for(int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t limb = read_be(in_number + (BN_LIMBS - 2 - i) * 4); + + temp |= limb << (BN_EXTRA_BITS * i); + out_number->val[i] = temp & BN_LIMB_MASK; + + temp = limb >> (32 - BN_EXTRA_BITS * (i + 1)); + } + + out_number->val[BN_LIMBS - 1] = temp; +} + +// out_number = (256BE) in_number +// Assumes in_number < 2**256 +// Guarantess out_number is a raw bigendian 256-bit number +void bn_write_be(const bignum256* in_number, uint8_t* out_number) { + uint32_t temp = in_number->val[BN_LIMBS - 1]; + for(int i = BN_LIMBS - 2; i >= 0; i--) { + uint32_t limb = in_number->val[i]; + + temp = (temp << (BN_BITS_PER_LIMB - BN_EXTRA_BITS * i)) | (limb >> (BN_EXTRA_BITS * i)); + write_be(out_number + (BN_LIMBS - 2 - i) * 4, temp); + + temp = limb; + } +} + +// out_number = (bignum256) in_number +// Assumes in_number is a raw little endian 256-bit number +// Guarantees out_number is normalized +void bn_read_le(const uint8_t* in_number, bignum256* out_number) { + uint32_t temp = 0; + for(int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t limb = read_le(in_number + i * 4); + + temp |= limb << (BN_EXTRA_BITS * i); + out_number->val[i] = temp & BN_LIMB_MASK; + temp = limb >> (32 - BN_EXTRA_BITS * (i + 1)); + } + + out_number->val[BN_LIMBS - 1] = temp; +} + +// out_number = (256LE) in_number +// Assumes in_number < 2**256 +// Guarantess out_number is a raw little endian 256-bit number +void bn_write_le(const bignum256* in_number, uint8_t* out_number) { + uint32_t temp = in_number->val[BN_LIMBS - 1]; + + for(int i = BN_LIMBS - 2; i >= 0; i--) { + uint32_t limb = in_number->val[i]; + temp = (temp << (BN_BITS_PER_LIMB - BN_EXTRA_BITS * i)) | (limb >> (BN_EXTRA_BITS * i)); + write_le(out_number + i * 4, temp); + temp = limb; + } +} + +// out_number = (bignum256) in_number +// Guarantees out_number is normalized +void bn_read_uint32(uint32_t in_number, bignum256* out_number) { + out_number->val[0] = in_number & BN_LIMB_MASK; + out_number->val[1] = in_number >> BN_BITS_PER_LIMB; + for(uint32_t i = 2; i < BN_LIMBS; i++) out_number->val[i] = 0; +} + +// out_number = (bignum256) in_number +// Guarantees out_number is normalized +void bn_read_uint64(uint64_t in_number, bignum256* out_number) { + out_number->val[0] = in_number & BN_LIMB_MASK; + out_number->val[1] = (in_number >>= BN_BITS_PER_LIMB) & BN_LIMB_MASK; + out_number->val[2] = in_number >> BN_BITS_PER_LIMB; + for(uint32_t i = 3; i < BN_LIMBS; i++) out_number->val[i] = 0; +} + +// Returns the bitsize of x +// Assumes x is normalized +// The function doesn't have neither constant control flow nor constant memory +// access flow +int bn_bitcount(const bignum256* x) { + for(int i = BN_LIMBS - 1; i >= 0; i--) { + uint32_t limb = x->val[i]; + if(limb != 0) { + // __builtin_clz returns the number of leading zero bits starting at the + // most significant bit position + return i * BN_BITS_PER_LIMB + (32 - __builtin_clz(limb)); + } + } + return 0; +} + +// Returns the number of decimal digits of x; if x is 0, returns 1 +// Assumes x is normalized +// The function doesn't have neither constant control flow nor constant memory +// access flow +unsigned int bn_digitcount(const bignum256* x) { + bignum256 val = {0}; + bn_copy(x, &val); + + unsigned int digits = 1; + for(unsigned int i = 0; i < BN_MAX_DECIMAL_DIGITS; i += 3) { + uint32_t limb = 0; + + bn_divmod1000(&val, &limb); + + if(limb >= 100) { + digits = i + 3; + } else if(limb >= 10) { + digits = i + 2; + } else if(limb >= 1) { + digits = i + 1; + } + } + + memzero(&val, sizeof(val)); + + return digits; +} + +// x = 0 +// Guarantees x is normalized +void bn_zero(bignum256* x) { + for(int i = 0; i < BN_LIMBS; i++) { + x->val[i] = 0; + } +} + +// x = 1 +// Guarantees x is normalized +void bn_one(bignum256* x) { + x->val[0] = 1; + for(int i = 1; i < BN_LIMBS; i++) { + x->val[i] = 0; + } +} + +// Returns x == 0 +// Assumes x is normalized +int bn_is_zero(const bignum256* x) { + uint32_t result = 0; + for(int i = 0; i < BN_LIMBS; i++) { + result |= x->val[i]; + } + return !result; +} + +// Returns x == 1 +// Assumes x is normalized +int bn_is_one(const bignum256* x) { + uint32_t result = x->val[0] ^ 1; + for(int i = 1; i < BN_LIMBS; i++) { + result |= x->val[i]; + } + return !result; +} + +// Returns x < y +// Assumes x, y are normalized +int bn_is_less(const bignum256* x, const bignum256* y) { + uint32_t res1 = 0; + uint32_t res2 = 0; + for(int i = BN_LIMBS - 1; i >= 0; i--) { + res1 = (res1 << 1) | (x->val[i] < y->val[i]); + res2 = (res2 << 1) | (x->val[i] > y->val[i]); + } + return res1 > res2; +} + +// Returns x == y +// Assumes x, y are normalized +int bn_is_equal(const bignum256* x, const bignum256* y) { + uint32_t result = 0; + for(int i = 0; i < BN_LIMBS; i++) { + result |= x->val[i] ^ y->val[i]; + } + return !result; +} + +// res = cond if truecase else falsecase +// Assumes cond is either 0 or 1 +// Works properly even if &res == &truecase or &res == &falsecase or +// &truecase == &falsecase or &res == &truecase == &falsecase +void bn_cmov( + bignum256* res, + volatile uint32_t cond, + const bignum256* truecase, + const bignum256* falsecase) { + // Intentional use of bitwise OR operator to ensure constant-time + assert((int)(cond == 1) | (int)(cond == 0)); + + uint32_t tmask = -cond; // tmask = 0xFFFFFFFF if cond else 0x00000000 + uint32_t fmask = ~tmask; // fmask = 0x00000000 if cond else 0xFFFFFFFF + + for(int i = 0; i < BN_LIMBS; i++) { + res->val[i] = (truecase->val[i] & tmask) | (falsecase->val[i] & fmask); + } +} + +// x = -x % prime if cond else x, +// Explicitly x = (3 * prime - x if x > prime else 2 * prime - x) if cond else +// else (x if x > prime else x + prime) +// Assumes x is normalized and partly reduced +// Assumes cond is either 1 or 0 +// Guarantees x is normalized +// Assumes prime is normalized and +// 0 < prime < 2**260 == 2**(BITS_PER_LIMB * LIMBS - 1) +void bn_cnegate(volatile uint32_t cond, bignum256* x, const bignum256* prime) { + // Intentional use of bitwise OR operator to ensure constant time + assert((int)(cond == 1) | (int)(cond == 0)); + + uint32_t tmask = -cond; // tmask = 0xFFFFFFFF if cond else 0x00000000 + uint32_t fmask = ~tmask; // fmask = 0x00000000 if cond else 0xFFFFFFFF + + bn_mod(x, prime); + // x < prime + + uint32_t acc1 = 1; + uint32_t acc2 = 0; + + for(int i = 0; i < BN_LIMBS; i++) { + acc1 += (BN_BASE - 1) + 2 * prime->val[i] - x->val[i]; + // acc1 neither overflows 32 bits nor underflows 0 + // Proof: + // acc1 + (BASE - 1) + 2 * prime[i] - x[i] + // >= (BASE - 1) - x >= (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) + // == 0 + // acc1 + (BASE - 1) + 2 * prime[i] - x[i] + // <= acc1 + (BASE - 1) + 2 * prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + // == 7 + 3 * 2**29 < 2**32 + + acc2 += prime->val[i] + x->val[i]; + // acc2 doesn't overflow 32 bits + // Proof: + // acc2 + prime[i] + x[i] + // <= 2**(32 - BITS_PER_LIMB) - 1 + 2 * (2**BITS_PER_LIMB - 1) + // == 2**(32 - BITS_PER_LIMB) + 2**(BITS_PER_LIMB + 1) - 2 + // == 2**30 + 5 < 2**32 + + // x = acc1 & LIMB_MASK if cond else acc2 & LIMB_MASK + x->val[i] = ((acc1 & tmask) | (acc2 & fmask)) & BN_LIMB_MASK; + + acc1 >>= BN_BITS_PER_LIMB; + // acc1 <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + // acc1 == 2**(BITS_PER_LIMB * (i + 1)) + 2 * prime[:i + 1] - x[:i + 1] + // >> BITS_PER_LIMB * (i + 1) + + acc2 >>= BN_BITS_PER_LIMB; + // acc2 <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + // acc2 == prime[:i + 1] + x[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc1 == 1); // assert prime <= 2**260 + // assert(acc2 == 0); + + // clang-format off + // acc1 == 1 + // Proof: + // acc1 == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime[:LIMBS] - x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime - x >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2 * prime >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2 * (2**(BITS_PER_LIMB * LIMBS - 1) - 1) >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 2 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc1 == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime[:LIMBS] - x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime - x >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) + 0 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc2 == 0 + // Proof: + // acc2 == prime[:LIMBS] + x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == prime + x >> BITS_PER_LIMB * LIMBS + // <= 2 * prime - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * (2**(BITS_PER_LIMB * LIMBS - 1) - 1) - 1 >> 261 + // == 2**(BITS_PER_LIMB * LIMBS) - 3 >> BITS_PER_LIMB * LIMBS + // == 0 + // clang-format on +} + +// x <<= 1 +// Assumes x is normalized, x < 2**260 == 2**(LIMBS*BITS_PER_LIMB - 1) +// Guarantees x is normalized +void bn_lshift(bignum256* x) { + for(int i = BN_LIMBS - 1; i > 0; i--) { + x->val[i] = ((x->val[i] << 1) & BN_LIMB_MASK) | (x->val[i - 1] >> (BN_BITS_PER_LIMB - 1)); + } + x->val[0] = (x->val[0] << 1) & BN_LIMB_MASK; +} + +// x >>= 1, i.e. x = floor(x/2) +// Assumes x is normalized +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +void bn_rshift(bignum256* x) { + for(int i = 0; i < BN_LIMBS - 1; i++) { + x->val[i] = (x->val[i] >> 1) | ((x->val[i + 1] & 1) << (BN_BITS_PER_LIMB - 1)); + } + x->val[BN_LIMBS - 1] >>= 1; +} + +// Sets i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// Guarantees x is normalized +// The function has constant control flow but not constant memory access flow +// with regard to i +void bn_setbit(bignum256* x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + x->val[i / BN_BITS_PER_LIMB] |= (1u << (i % BN_BITS_PER_LIMB)); +} + +// clears i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// Guarantees x is normalized +// The function has constant control flow but not constant memory access flow +// with regard to i +void bn_clearbit(bignum256* x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + x->val[i / BN_BITS_PER_LIMB] &= ~(1u << (i % BN_BITS_PER_LIMB)); +} + +// returns i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// The function has constant control flow but not constant memory access flow +// with regard to i +uint32_t bn_testbit(const bignum256* x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + return (x->val[i / BN_BITS_PER_LIMB] >> (i % BN_BITS_PER_LIMB)) & 1; +} + +// res = x ^ y +// Assumes x, y are normalized +// Guarantees res is normalized +// Works properly even if &res == &x or &res == &y or &res == &x == &y +void bn_xor(bignum256* res, const bignum256* x, const bignum256* y) { + for(int i = 0; i < BN_LIMBS; i++) { + res->val[i] = x->val[i] ^ y->val[i]; + } +} + +// x = x / 2 % prime +// Explicitly x = x / 2 if is_even(x) else (x + prime) / 2 +// Assumes x is normalized, x + prime < 261 == LIMBS * BITS_PER_LIMB +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +// Assumes prime is an odd number and normalized +void bn_mult_half(bignum256* x, const bignum256* prime) { + // x = x / 2 if is_even(x) else (x + prime) / 2 + + uint32_t x_is_odd_mask = -(x->val[0] & 1); // x_is_odd_mask = 0xFFFFFFFF if is_odd(x) else 0 + + uint32_t acc = (x->val[0] + (prime->val[0] & x_is_odd_mask)) >> 1; + // acc < 2**BITS_PER_LIMB + // Proof: + // acc == x[0] + prime[0] & x_is_odd_mask >> 1 + // <= (2**(BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB) - 1) >> 1 + // == 2**(BITS_PER_LIMB + 1) - 2 >> 1 + // < 2**(BITS_PER_LIMB) + + for(int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t temp = (x->val[i + 1] + (prime->val[i + 1] & x_is_odd_mask)); + // temp < 2**(BITS_PER_LIMB + 1) + // Proof: + // temp == x[i + 1] + val[i + 1] & x_is_odd_mask + // <= (2**(BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB) - 1) + // < 2**(BITS_PER_LIMB + 1) + + acc += (temp & 1) << (BN_BITS_PER_LIMB - 1); + // acc doesn't overflow 32 bits + // Proof: + // acc + (temp & 1 << BITS_PER_LIMB - 1) + // <= 2**(BITS_PER_LIMB + 1) + 2**(BITS_PER_LIMB - 1) + // <= 2**30 + 2**28 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + acc += temp >> 1; + // acc < 2**(BITS_PER_LIMB + 1) + // Proof: + // acc + (temp >> 1) + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB + 1) - 1 >> 1) + // == 7 + 2**(BITS_PER_LIMB) - 1 < 2**(BITS_PER_LIMB + 1) + + // acc == x[:i+2]+(prime[:i+2] & x_is_odd_mask) >> BITS_PER_LIMB * (i+1) + } + x->val[BN_LIMBS - 1] = acc; + + // assert(acc >> BITS_PER_LIMB == 0); + // acc >> BITS_PER_LIMB == 0 + // Proof: + // acc + // == x[:LIMBS] + (prime[:LIMBS] & x_is_odd_mask) >> BITS_PER_LIMB*LIMBS + // == x + (prime & x_is_odd_mask) >> BITS_PER_LIMB * LIMBS + // <= x + prime >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 0 +} + +// x = x * k % prime +// Assumes x is normalized, 0 <= k <= 8 = 2**(32 - BITS_PER_LIMB) +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +// Guarantees x is normalized and partly reduced modulo prime +void bn_mult_k(bignum256* x, uint8_t k, const bignum256* prime) { + assert(k <= 8); + + for(int i = 0; i < BN_LIMBS; i++) { + x->val[i] = k * x->val[i]; + // x[i] doesn't overflow 32 bits + // k * x[i] <= 2**(32 - BITS_PER_LIMB) * (2**BITS_PER_LIMB - 1) + // < 2**(32 - BITS_PER_LIMB) * 2**BITS_PER_LIMB == 2**32 + } + + bn_fast_mod(x, prime); +} + +// Reduces partly reduced x modulo prime +// Explicitly x = x if x < prime else x - prime +// Assumes x is partly reduced modulo prime +// Guarantees x is fully reduced modulo prime +// Assumes prime is nonzero and normalized +void bn_mod(bignum256* x, const bignum256* prime) { + uint32_t x_less_prime = bn_is_less(x, prime); + + bignum256 temp = {0}; + bn_subtract(x, prime, &temp); + bn_cmov(x, x_less_prime, x, &temp); + + memzero(&temp, sizeof(temp)); +} + +// Auxiliary function for bn_multiply +// res = k * x +// Assumes k and x are normalized +// Guarantees res is normalized 18 digit little endian number in base 2**29 +void bn_multiply_long(const bignum256* k, const bignum256* x, uint32_t res[2 * BN_LIMBS]) { + // Uses long multiplication in base 2**29, see + // https://en.wikipedia.org/wiki/Multiplication_algorithm#Long_multiplication + + uint64_t acc = 0; + + // compute lower half + for(int i = 0; i < BN_LIMBS; i++) { + for(int j = 0; j <= i; j++) { + acc += k->val[j] * (uint64_t)x->val[i - j]; + // acc doesn't overflow 64 bits + // Proof: + // acc <= acc + sum([k[j] * x[i-j] for j in range(i)]) + // <= (2**(64 - BITS_PER_LIMB) - 1) + + // LIMBS * (2**BITS_PER_LIMB - 1) * (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + 9 * (2**29 - 1) * (2**29 - 1) + // <= 2**35 + 9 * 2**58 < 2**64 + } + + res[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**35 - 1 == 2**(64 - BITS_PER_LIMB) - 1 + } + + // compute upper half + for(int i = BN_LIMBS; i < 2 * BN_LIMBS - 1; i++) { + for(int j = i - BN_LIMBS + 1; j < BN_LIMBS; j++) { + acc += k->val[j] * (uint64_t)x->val[i - j]; + // acc doesn't overflow 64 bits + // Proof: + // acc <= acc + sum([k[j] * x[i-j] for j in range(i)]) + // <= (2**(64 - BITS_PER_LIMB) - 1) + // LIMBS * (2**BITS_PER_LIMB - 1) * (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + 9 * (2**29 - 1) * (2**29 - 1) + // <= 2**35 + 9 * 2**58 < 2**64 + } + + res[i] = acc & (BN_BASE - 1); + acc >>= BN_BITS_PER_LIMB; + // acc < 2**35 == 2**(64 - BITS_PER_LIMB) + } + + res[2 * BN_LIMBS - 1] = acc; +} + +// Auxiliary function for bn_multiply +// Assumes 0 <= d <= 8 == LIMBS - 1 +// Assumes res is normalized and res < 2**(256 + 29*d + 31) +// Guarantess res in normalized and res < 2 * prime * 2**(29*d) +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply_reduce_step(uint32_t res[2 * BN_LIMBS], const bignum256* prime, uint32_t d) { + // clang-format off + // Computes res = res - (res // 2**(256 + BITS_PER_LIMB * d)) * prime * 2**(BITS_PER_LIMB * d) + + // res - (res // 2**(256 + BITS_PER_LIMB * d)) * prime * 2**(BITS_PER_LIMB * d) < 2 * prime * 2**(BITS_PER_LIMB * d) + // Proof: + // res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * prime + // == res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * (2**256 - (2**256 - prime)) + // == res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * 2**256 + res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * (2**256 - prime) + // == (res % 2**(256 + BITS_PER_LIMB * d)) + res // (2**256 + BITS_PER_LIMB * d) * 2**(BITS_PER_LIMB * d) * (2**256 - prime) + // <= (2**(256 + 29*d + 31) % 2**(256 + 29*d)) + (2**(256 + 29*d + 31) - 1) / (2**256 + 29*d) * 2**(29*d) * (2**256 - prime) + // <= 2**(256 + 29*d) + 2**(256 + 29*d + 31) / (2**256 + 29*d) * 2**(29*d) * (2**256 - prime) + // == 2**(256 + 29*d) + 2**31 * 2**(29*d) * (2**256 - prime) + // == 2**(29*d) * (2**256 + 2**31 * (2*256 - prime)) + // <= 2**(29*d) * (2**256 + 2**31 * 2*224) + // <= 2**(29*d) * (2**256 + 2**255) + // <= 2**(29*d) * 2 * (2**256 - 2**224) + // <= 2 * prime * 2**(29*d) + // clang-format on + + uint32_t coef = (res[d + BN_LIMBS - 1] >> (256 - (BN_LIMBS - 1) * BN_BITS_PER_LIMB)) + + (res[d + BN_LIMBS] << ((BN_LIMBS * BN_BITS_PER_LIMB) - 256)); + + // coef == res // 2**(256 + BITS_PER_LIMB * d) + + // coef < 2**31 + // Proof: + // coef == res // 2**(256 + BITS_PER_LIMB * d) + // < 2**(256 + 29 * d + 31) // 2**(256 + 29 * d) + // == 2**31 + + const int shift = 31; + uint64_t acc = 1ull << shift; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += (((uint64_t)(BN_BASE - 1)) << shift) + res[d + i] - prime->val[i] * (uint64_t)coef; + // acc neither overflow 64 bits nor underflow zero + // Proof: + // acc + ((BASE - 1) << shift) + res[d + i] - prime[i] * coef + // >= ((BASE - 1) << shift) - prime[i] * coef + // == 2**shift * (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) * + // (2**31 - 1) + // == (2**shift - 2**31 + 1) * (2**BITS_PER_LIMB - 1) + // == (2**31 - 2**31 + 1) * (2**29 - 1) + // == 2**29 - 1 > 0 + // acc + ((BASE - 1) << shift) + res[d + i] - prime[i] * coef + // <= acc + ((BASE - 1) << shift) + res[d+i] + // <= (2**(64 - BITS_PER_LIMB) - 1) + 2**shift * (2**BITS_PER_LIMB - 1) + // + (2*BITS_PER_LIMB - 1) + // == (2**(64 - BITS_PER_LIMB) - 1) + (2**shift + 1) * + // (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + (2**31 + 1) * (2**29 - 1) + // <= 2**35 + 2**60 + 2**29 < 2**64 + + res[d + i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**(64 - BITS_PER_LIMB) - 1 == 2**35 - 1 + + // acc == (1 << BITS_PER_LIMB * (i + 1) + shift) + res[d : d + i + 1] + // - coef * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // acc += (((uint64_t)(BASE - 1)) << shift) + res[d + LIMBS]; + // acc >>= BITS_PER_LIMB; + // assert(acc <= 1ul << shift); + + // clang-format off + // acc == 1 << shift + // Proof: + // acc + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime[:LIMBS] >> BITS_PER_LIMB * (LIMBS + 1) + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime >> BITS_PER_LIMB * (LIMBS + 1) + + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res[d : d + LIMBS + 1] - coef * prime) >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res[:d] + BASE**d * res[d : d + LIMBS + 1] - BASE**d * coef * prime)//BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res - BASE**d * coef * prime) // BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (2 * prime * BASE**d) // BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << 321) + 2 * 2**256 >> 290 + // == 1 << 31 == 1 << shift + + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime[:LIMBS + 1] >> BITS_PER_LIMB * (LIMBS + 1) + // >= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + 0 >> BITS_PER_LIMB * (LIMBS + 1) + // == 1 << shift + // clang-format on + + res[d + BN_LIMBS] = 0; +} + +// Auxiliary function for bn_multiply +// Partly reduces res and stores both in x and res +// Assumes res in normalized and res < 2**519 +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply_reduce(bignum256* x, uint32_t res[2 * BN_LIMBS], const bignum256* prime) { + for(int i = BN_LIMBS - 1; i >= 0; i--) { + // res < 2**(256 + 29*i + 31) + // Proof: + // if i == LIMBS - 1: + // res < 2**519 + // == 2**(256 + 29 * 8 + 31) + // == 2**(256 + 29 * (LIMBS - 1) + 31) + // else: + // res < 2 * prime * 2**(29 * (i + 1)) + // <= 2**256 * 2**(29*i + 29) < 2**(256 + 29*i + 31) + bn_multiply_reduce_step(res, prime, i); + } + + for(int i = 0; i < BN_LIMBS; i++) { + x->val[i] = res[i]; + } +} + +// x = k * x % prime +// Assumes k, x are normalized, k * x < 2**519 +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply(const bignum256* k, bignum256* x, const bignum256* prime) { + uint32_t res[2 * BN_LIMBS] = {0}; + + bn_multiply_long(k, x, res); + bn_multiply_reduce(x, res, prime); + + memzero(res, sizeof(res)); +} + +// Partly reduces x modulo prime +// Assumes limbs of x except the last (the most significant) one are normalized +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +// Guarantees x is normalized and partly reduced modulo prime +void bn_fast_mod(bignum256* x, const bignum256* prime) { + // Computes x = x - (x // 2**256) * prime + + // x < 2**((LIMBS - 1) * BITS_PER_LIMB + 32) == 2**264 + + // x - (x // 2**256) * prime < 2 * prime + // Proof: + // x - (x // 2**256) * prime + // == x - (x // 2**256) * (2**256 - (2**256 - prime)) + // == x - ((x // 2**256) * 2**256) + (x // 2**256) * (2**256 - prime) + // == (x % prime) + (x // 2**256) * (2**256 - prime) + // <= prime - 1 + (2**264 // 2**256) * (2**256 - prime) + // <= 2**256 + 2**8 * 2**224 == 2**256 + 2**232 + // < 2 * (2**256 - 2**224) + // <= 2 * prime + + // x - (x // 2**256 - 1) * prime < 2 * prime + // Proof: + // x - (x // 2**256) * prime + prime + // == x - (x // 2**256) * (2**256 - (2**256 - prime)) + prime + // == x - ((x//2**256) * 2**256) + (x//2**256) * (2**256 - prime) + prime + // == (x % prime) + (x // 2**256) * (2**256 - prime) + prime + // <= 2 * prime - 1 + (2**264 // 2**256) * (2**256 - prime) + // <= 2 * prime + 2**8 * 2**224 == 2**256 + 2**232 + 2**256 - 2**224 + // < 2 * (2**256 - 2**224) + // <= 2 * prime + + uint32_t coef = x->val[BN_LIMBS - 1] >> (256 - ((BN_LIMBS - 1) * BN_BITS_PER_LIMB)); + + // clang-format off + // coef == x // 2**256 + // 0 <= coef < 2**((LIMBS - 1) * BITS_PER_LIMB + 32 - 256) == 256 + // Proof: + //* Let x[[a : b] be the number consisting of a-th to (b-1)-th bit of the number x. + // x[LIMBS - 1] >> (256 - ((LIMBS - 1) * BITS_PER_LIMB)) + // == x[[(LIMBS - 1) * BITS_PER_LIMB : (LIMBS - 1) * BITS_PER_LIMB + 32]] >> (256 - ((LIMBS - 1) * BITS_PER_LIMB)) + // == x[[256 - ((LIMBS - 1) * BITS_PER_LIMB) + (LIMBS - 1) * BITS_PER_LIMB : (LIMBS - 1) * BITS_PER_LIMB + 32]] + // == x[[256 : (LIMBS - 1) * BITS_PER_LIMB + 32]] + // == x[[256 : 264]] == x // 2**256 + // clang-format on + + const int shift = 8; + uint64_t acc = 1ull << shift; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += (((uint64_t)(BN_BASE - 1)) << shift) + x->val[i] - prime->val[i] * (uint64_t)coef; + // acc neither overflows 64 bits nor underflows 0 + // Proof: + // acc + (BASE - 1 << shift) + x[i] - prime[i] * coef + // >= (BASE - 1 << shift) - prime[i] * coef + // >= 2**shift * (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) * 255 + // == (2**shift - 255) * (2**BITS_PER_LIMB - 1) + // == (2**8 - 255) * (2**29 - 1) == 2**29 - 1 >= 0 + // acc + (BASE - 1 << shift) + x[i] - prime[i] * coef + // <= acc + ((BASE - 1) << shift) + x[i] + // <= (2**(64 - BITS_PER_LIMB) - 1) + 2**shift * (2**BITS_PER_LIMB - 1) + // + (2**32 - 1) + // == (2**35 - 1) + 2**8 * (2**29 - 1) + 2**32 + // < 2**35 + 2**37 + 2**32 < 2**64 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**(64 - BITS_PER_LIMB) - 1 == 2**35 - 1 + + // acc == (1 << BITS_PER_LIMB * (i + 1) + shift) + x[:i + 1] + // - coef * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 1 << shift); + + // clang-format off + // acc == 1 << shift + // Proof: + // acc + // == (1 << BITS_PER_LIMB * LIMBS + shift) + x[:LIMBS] - coef * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == (1 << BITS_PER_LIMB * LIMBS + shift) + (x - coef * prime) >> BITS_PER_LIMB * LIMBS + // <= (1 << BITS_PER_LIMB * LIMBS + shift) + (2 * prime) >> BITS_PER_LIMB * LIMBS + // <= (1 << BITS_PER_LIMB * LIMBS + shift) + 2 * 2**256 >> BITS_PER_LIMB * LIMBS + // <= 2**269 + 2**257 >> 2**261 + // <= 1 << 8 == 1 << shift + + // acc + // == (1 << BITS_PER_LIMB * LIMBS + shift) + x[:LIMBS] - coef * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // >= (1 << BITS_PER_LIMB * LIMBS + shift) + 0 >> BITS_PER_LIMB * LIMBS + // == (1 << BITS_PER_LIMB * LIMBS + shift) + 0 >> BITS_PER_LIMB * LIMBS + // <= 1 << 8 == 1 << shift + // clang-format on +} + +// res = x**e % prime +// Assumes both x and e are normalized, x < 2**259 +// Guarantees res is normalized and partly reduced modulo prime +// Works properly even if &x == &res +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to e +void bn_power_mod(const bignum256* x, const bignum256* e, const bignum256* prime, bignum256* res) { + // Uses iterative right-to-left exponentiation by squaring, see + // https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method + + bignum256 acc = {0}; + bn_copy(x, &acc); + + bn_one(res); + for(int i = 0; i < BN_LIMBS; i++) { + uint32_t limb = e->val[i]; + + for(int j = 0; j < BN_BITS_PER_LIMB; j++) { + // Break if the following bits of the last limb are zero + if(i == BN_LIMBS - 1 && limb == 0) break; + + if(limb & 1) + // acc * res < 2**519 + // Proof: + // acc * res <= max(2**259 - 1, 2 * prime) * (2 * prime) + // == max(2**259 - 1, 2**257) * 2**257 < 2**259 * 2**257 + // == 2**516 < 2**519 + bn_multiply(&acc, res, prime); + + limb >>= 1; + // acc * acc < 2**519 + // Proof: + // acc * acc <= max(2**259 - 1, 2 * prime)**2 + // <= (2**259)**2 == 2**518 < 2**519 + bn_multiply(&acc, &acc, prime); + } + // acc == x**(e[:i + 1]) % prime + } + + memzero(&acc, sizeof(acc)); +} + +// x = sqrt(x) % prime +// Explicitly x = x**((prime+1)/4) % prime +// The other root is -sqrt(x) +// Assumes x is normalized, x < 2**259 and quadratic residuum mod prime +// Assumes prime is a prime number, prime % 4 == 3, it is normalized and +// 2**256 - 2**224 <= prime <= 2**256 +// Guarantees x is normalized and fully reduced modulo prime +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime +void bn_sqrt(bignum256* x, const bignum256* prime) { + // Uses the Lagrange formula for the primes of the special form, see + // http://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus + // If prime % 4 == 3, then sqrt(x) % prime == x**((prime+1)//4) % prime + + assert(prime->val[BN_LIMBS - 1] % 4 == 3); + + // e = (prime + 1) // 4 + bignum256 e = {0}; + bn_copy(prime, &e); + bn_addi(&e, 1); + bn_rshift(&e); + bn_rshift(&e); + + bn_power_mod(x, &e, prime, x); + bn_mod(x, prime); + + memzero(&e, sizeof(e)); +} + +// a = 1/a % 2**n +// Assumes a is odd, 1 <= n <= 32 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to n +uint32_t inverse_mod_power_two(uint32_t a, uint32_t n) { + // Uses "Explicit Quadratic Modular inverse modulo 2" from section 3.3 of "On + // Newton-Raphson iteration for multiplicative inverses modulo prime powers" + // by Jean-Guillaume Dumas, see + // https://arxiv.org/pdf/1209.6626.pdf + + // 1/a % 2**n + // = (2-a) * product([1 + (a-1)**(2**i) for i in range(1, floor(log2(n)))]) + + uint32_t acc = 2 - a; + uint32_t f = a - 1; + + // mask = (1 << n) - 1 + uint32_t mask = n == 32 ? 0xFFFFFFFF : (1u << n) - 1; + + for(uint32_t i = 1; i < n; i <<= 1) { + f = (f * f) & mask; + acc = (acc * (1 + f)) & mask; + } + + return acc; +} + +// x = (x / 2**BITS_PER_LIMB) % prime +// Assumes both x and prime are normalized +// Assumes prime is an odd number and normalized +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +void bn_divide_base(bignum256* x, const bignum256* prime) { + // Uses an explicit formula for the modular inverse of power of two + // (x / 2**n) % prime == (x + ((-x / prime) % 2**n) * prime) // 2**n + // Proof: + // (x + ((-x / prime) % 2**n) * prime) % 2**n + // == (x - x / prime * prime) % 2**n + // == 0 + // (x + ((-1 / prime) % 2**n) * prime) % prime + // == x + // if x < prime: + // (x + ((-x / prime) % 2**n) * prime) // 2**n + // <= ((prime - 1) + (2**n - 1) * prime) / 2**n + // == (2**n * prime - 1) / 2**n == prime - 1 / 2**n < prime + // if x < 2 * prime: + // (x + ((-x / prime) % 2**n) * prime) // 2**n + // <= ((2 * prime - 1) + (2**n - 1) * prime) / 2**n + // == (2**n * prime + prime - 1) / 2**n + // == prime + (prime - 1) / 2**n < 2 * prime + + // m = (-x / prime) % 2**BITS_PER_LIMB + uint32_t m = (x->val[0] * (BN_BASE - inverse_mod_power_two(prime->val[0], BN_BITS_PER_LIMB))) & + BN_LIMB_MASK; + // m < 2**BITS_PER_LIMB + + uint64_t acc = x->val[0] + (uint64_t)m * prime->val[0]; + acc >>= BN_BITS_PER_LIMB; + + for(int i = 1; i < BN_LIMBS; i++) { + acc = acc + x->val[i] + (uint64_t)m * prime->val[i]; + // acc does not overflow 64 bits + // acc == acc + x + m * prime + // <= 2**(64 - BITS_PER_LIMB) + 2**(BITS_PER_LIMB) + // 2**(BITS_PER_LIMB) * 2**(BITS_PER_LIMB) + // <= 2**(2 * BITS_PER_LIMB) + 2**(64 - BITS_PER_LIMB) + + // 2**(BITS_PER_LIMB) + // <= 2**58 + 2**35 + 2**29 < 2**64 + + x->val[i - 1] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc < 2**35 == 2**(64 - BITS_PER_LIMB) + + // acc == x[:i + 1] + m * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + x->val[BN_LIMBS - 1] = acc; + + assert(acc >> BN_BITS_PER_LIMB == 0); + + // clang-format off + // acc >> BITS_PER_LIMB == 0 + // Proof: + // acc >> BITS_PER_LIMB + // == (x[:LIMB] + m * prime[:LIMB] >> BITS_PER_LIMB * LIMBS) >> BITS_PER_LIMB * (LIMBS + 1) + // == x + m * prime >> BITS_PER_LIMB * (LIMBS + 1) + // <= (2**(BITS_PER_LIMB * LIMBS) - 1) + (2**BITS_PER_LIMB - 1) * (2**(BITS_PER_LIMB * LIMBS) - 1) >> BITS_PER_LIMB * (LIMBS + 1) + // == 2**(BITS_PER_LIMB * LIMBS) - 1 + 2**(BITS_PER_LIMB * (LIMBS + 1)) - 2**(BITS_PER_LIMB * LIMBS) - 2**BITS_PER_LIMB + 1 >> BITS_PER_LIMB * (LIMBS + 1) + // == 2**(BITS_PER_LIMB * (LIMBS + 1)) - 2**BITS_PER_LIMB >> BITS_PER_LIMB * (LIMBS + 1) + // == 0 + // clang-format on +} + +#if !USE_INVERSE_FAST +// x = 1/x % prime if x != 0 else 0 +// Assumes x is normalized +// Assumes prime is a prime number +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime +static void bn_inverse_slow(bignum256* x, const bignum256* prime) { + // Uses formula 1/x % prime == x**(prime - 2) % prime + // See https://en.wikipedia.org/wiki/Fermat%27s_little_theorem + + bn_fast_mod(x, prime); + + // e = prime - 2 + bignum256 e = {0}; + bn_read_uint32(2, &e); + bn_subtract(prime, &e, &e); + + bn_power_mod(x, &e, prime, x); + bn_mod(x, prime); + + memzero(&e, sizeof(e)); +} +#endif + +#if false +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime and x +static void bn_inverse_fast(bignum256 *x, const bignum256 *prime) { + // "The Almost Montgomery Inverse" from the section 3 of "Constant Time + // Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x & prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): + u = u // 2 + s = 2 * s + elif is_even(v): + v = v // 2 + r = 2 * r + elif v < u: + u = (u - v) // 2 + r = r + s + s = 2 * s + else: + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + if (bn_is_zero(x)) return; + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + int k = 0; + while (!bn_is_one(&v)) { + if ((u.val[0] & 1) == 0) { + bn_rshift(&u); + bn_lshift(&s); + } else if ((v.val[0] & 1) == 0) { + bn_rshift(&v); + bn_lshift(&r); + } else if (bn_is_less(&v, &u)) { + bn_subtract(&u, &v, &u); + bn_rshift(&u); + bn_add(&r, &s); + bn_lshift(&s); + } else { + bn_subtract(&v, &u, &v); + bn_rshift(&v); + bn_add(&s, &r); + bn_lshift(&r); + } + k += 1; + assert(!bn_is_zero(&v)); // assert GCD(x, prime) == 1 + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for (int i = 0; i < k / BITS_PER_LIMB; i++) { + bn_divide_base(&s, prime); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for (int i = 0; i < k % BN_BITS_PER_LIMB; i++) { + bn_mult_half(&s, prime); + } + + bn_copy(&s, x); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(r)); + memzero(&s, sizeof(s)); +} +#endif + +#if USE_INVERSE_FAST +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function has constant control flow but not constant memory access flow +// with regard to prime and x +static void bn_inverse_fast(bignum256* x, const bignum256* prime) { + // Custom constant time version of "The Almost Montgomery Inverse" from the + // section 3 of "Constant Time Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x % prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): # b1 + u = u // 2 + s = 2 * s + elif is_even(v): # b2 + v = v // 2 + r = 2 * r + elif v < u: # b3 + u = (u - v) // 2 + r = r + s + s = 2 * s + else: # b4 + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + bignum256 zero = {0}; + bn_zero(&zero); + + int k = 0; + + int finished = 0, u_even = 0, v_even = 0, v_less_u = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0; + finished = 0; + + for(int i = 0; i < 2 * BN_LIMBS * BN_BITS_PER_LIMB; i++) { + finished = finished | -bn_is_one(&v); + u_even = -bn_is_even(&u); + v_even = -bn_is_even(&v); + v_less_u = -bn_is_less(&v, &u); + + b1 = ~finished & u_even; + b2 = ~finished & ~b1 & v_even; + b3 = ~finished & ~b1 & ~b2 & v_less_u; + b4 = ~finished & ~b1 & ~b2 & ~b3; + +// The ternary operator for pointers with constant control flow +// BN_INVERSE_FAST_TERNARY(c, t, f) = t if c else f +// Very nasty hack, sorry for that +#define BN_INVERSE_FAST_TERNARY(c, t, f) \ + ((void*)(((c) & (uintptr_t)(t)) | (~(c) & (uintptr_t)(f)))) + + bn_subtract( + BN_INVERSE_FAST_TERNARY(b3, &u, &v), + BN_INVERSE_FAST_TERNARY(b3 | b4, BN_INVERSE_FAST_TERNARY(b3, &v, &u), &zero), + BN_INVERSE_FAST_TERNARY(b3, &u, &v)); + + bn_add( + BN_INVERSE_FAST_TERNARY(b3, &r, &s), + BN_INVERSE_FAST_TERNARY(b3 | b4, BN_INVERSE_FAST_TERNARY(b3, &s, &r), &zero)); + bn_rshift(BN_INVERSE_FAST_TERNARY(b1 | b3, &u, &v)); + bn_lshift(BN_INVERSE_FAST_TERNARY(b1 | b3, &s, &r)); + + k = k - ~finished; + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for(int i = 0; i < 2 * BN_LIMBS; i++) { + // s = s / 2**BITS_PER_LIMB % prime if i < k // BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_divide_base(&r, prime); + bn_cmov(&s, i < k / BN_BITS_PER_LIMB, &r, &s); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for(int i = 0; i < BN_BITS_PER_LIMB; i++) { + // s = s / 2 % prime if i < k % BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_mult_half(&r, prime); + bn_cmov(&s, i < k % BN_BITS_PER_LIMB, &r, &s); + } + + bn_cmov(x, bn_is_zero(x), x, &s); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(s)); + memzero(&s, sizeof(s)); +} +#endif + +#if false +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +static void bn_inverse_fast(bignum256 *x, const bignum256 *prime) { + // Custom constant time version of "The Almost Montgomery Inverse" from the + // section 3 of "Constant Time Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x % prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): # b1 + u = u // 2 + s = 2 * s + elif is_even(v): # b2 + v = v // 2 + r = 2 * r + elif v < u: # b3 + u = (u - v) // 2 + r = r + s + s = 2 * s + else: # b4 + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + bignum256 zero = {0}; + bn_zero(&zero); + + int k = 0; + + uint32_t finished = 0, u_even = 0, v_even = 0, v_less_u = 0, b1 = 0, b2 = 0, + b3 = 0, b4 = 0; + finished = 0; + + bignum256 u_half = {0}, v_half = {0}, u_minus_v_half = {0}, v_minus_u_half = {0}, r_plus_s = {0}, r_twice = {0}, s_twice = {0}; + for (int i = 0; i < 2 * BN_LIMBS * BN_BITS_PER_LIMB; i++) { + finished = finished | bn_is_one(&v); + u_even = bn_is_even(&u); + v_even = bn_is_even(&v); + v_less_u = bn_is_less(&v, &u); + + b1 = (finished ^ 1) & u_even; + b2 = (finished ^ 1) & (b1 ^ 1) & v_even; + b3 = (finished ^ 1) & (b1 ^ 1) & (b2 ^ 1) & v_less_u; + b4 = (finished ^ 1) & (b1 ^ 1) & (b2 ^ 1) & (b3 ^ 1); + + // u_half = u // 2 + bn_copy(&u, &u_half); + bn_rshift(&u_half); + + // v_half = v // 2 + bn_copy(&v, &v_half); + bn_rshift(&v_half); + + // u_minus_v_half = (u - v) // 2 + bn_subtract(&u, &v, &u_minus_v_half); + bn_rshift(&u_minus_v_half); + + // v_minus_u_half = (v - u) // 2 + bn_subtract(&v, &u, &v_minus_u_half); + bn_rshift(&v_minus_u_half); + + // r_plus_s = r + s + bn_copy(&r, &r_plus_s); + bn_add(&r_plus_s, &s); + + // r_twice = 2 * r + bn_copy(&r, &r_twice); + bn_lshift(&r_twice); + + // s_twice = 2 * s + bn_copy(&s, &s_twice); + bn_lshift(&s_twice); + + bn_cmov(&u, b1, &u_half, &u); + bn_cmov(&u, b3, &u_minus_v_half, &u); + + bn_cmov(&v, b2, &v_half, &v); + bn_cmov(&v, b4, &v_minus_u_half, &v); + + bn_cmov(&r, b2 | b4, &r_twice, &r); + bn_cmov(&r, b3, &r_plus_s, &r); + + bn_cmov(&s, b1 | b3, &s_twice, &s); + bn_cmov(&s, b4, &r_plus_s, &s); + + k = k + (finished ^ 1); + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for (int i = 0; i < 2 * BN_LIMBS; i++) { + // s = s / 2**BITS_PER_LIMB % prime if i < k // BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_divide_base(&r, prime); + bn_cmov(&s, i < k / BITS_PER_LIMB, &r, &s); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for (int i = 0; i < BN_BITS_PER_LIMB; i++) { + // s = s / 2 % prime if i < k % BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_mult_half(&r, prime); + bn_cmov(&s, i < k % BN_BITS_PER_LIMB, &r, &s); + } + + bn_cmov(x, bn_is_zero(x), x, &s); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(r)); + memzero(&s, sizeof(s)); + memzero(&u_half, sizeof(u_half)); + memzero(&v_half, sizeof(v_half)); + memzero(&u_minus_v_half, sizeof(u_minus_v_half)); + memzero(&v_minus_u_half, sizeof(v_minus_u_half)); + memzero(&r_twice, sizeof(r_twice)); + memzero(&s_twice, sizeof(s_twice)); + memzero(&r_plus_s, sizeof(r_plus_s)); +} +#endif + +// Normalizes x +// Assumes x < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +void bn_normalize(bignum256* x) { + uint32_t acc = 0; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // acc + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + // == 7 + 2**29 - 1 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= (BN_BITS_PER_LIMB); + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + } +} + +// x = x + y +// Assumes x, y are normalized, x + y < 2**(LIMBS*BITS_PER_LIMB) == 2**261 +// Guarantees x is normalized +// Works properly even if &x == &y +void bn_add(bignum256* x, const bignum256* y) { + uint32_t acc = 0; + for(int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i] + y->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // acc + x[i] + y[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + // == (2**(32 - BITS_PER_LIMB) - 1) + 2**(BITS_PER_LIMB + 1) - 2 + // == 7 + 2**30 - 2 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + y[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + y[:LIMBS] >> LIMBS * BITS_PER_LIMB + // == x + y >> LIMBS * BITS_PER_LIMB + // <= 2**(LIMBS * BITS_PER_LIMB) - 1 >> LIMBS * BITS_PER_LIMB == 0 +} + +// x = x + y % prime +// Assumes x, y are normalized +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +void bn_addmod(bignum256* x, const bignum256* y, const bignum256* prime) { + for(int i = 0; i < BN_LIMBS; i++) { + x->val[i] += y->val[i]; + // x[i] doesn't overflow 32 bits + // Proof: + // x[i] + y[i] + // <= 2 * (2**BITS_PER_LIMB - 1) + // == 2**30 - 2 < 2**32 + } + + bn_fast_mod(x, prime); +} + +// x = x + y +// Assumes x is normalized +// Assumes y <= 2**32 - 2**29 == 2**32 - 2**BITS_PER_LIMB and +// x + y < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +void bn_addi(bignum256* x, uint32_t y) { + // assert(y <= 3758096384); // assert y <= 2**32 - 2**29 + uint32_t acc = y; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // if i == 0: + // acc + x[i] == y + x[0] + // <= (2**32 - 2**BITS_PER_LIMB) + (2**BITS_PER_LIMB - 1) + // == 2**32 - 1 < 2**32 + // else: + // acc + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + // == 7 + 2**29 - 1 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= (BN_BITS_PER_LIMB); + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + y >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + y << LIMBS * BITS_PER_LIMB + // == x + y << LIMBS * BITS_PER_LIMB + // <= 2**(LIMBS + BITS_PER_LIMB) - 1 << LIMBS * BITS_PER_LIMB + // == 0 +} + +// x = x - y % prime +// Explicitly x = x + prime - y +// Assumes x, y are normalized +// Assumes y < prime[0], x + prime - y < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +// If x is fully reduced modulo prime, +// guarantess x will be partly reduced modulo prime +// Assumes prime is nonzero and normalized +void bn_subi(bignum256* x, uint32_t y, const bignum256* prime) { + assert(y < prime->val[0]); + + // x = x + prime - y + + uint32_t acc = -y; + for(int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i] + prime->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + x[i] + prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + // <= 7 + 2**30 - 2 < 2**32 + // acc + x[i] + prime[i] + // >= -y + prime[0] >= 0 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + prime[:i + 1] - y >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + prime - y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + prime[:LIMBS] - y >> BITS_PER_LIMB * LIMBS + // == x + prime - y >> BITS_PER_LIMB * LIMBS + // <= 2**(LIMBS * BITS_PER_LIMB) - 1 >> BITS_PER_LIMB * LIMBS == 0 +} + +// res = x - y % prime +// Explicitly res = x + (2 * prime - y) +// Assumes x, y are normalized, y is partly reduced +// Assumes x + 2 * prime - y < 2**261 == 2**(BITS_PER_LIMB * LIMBS) +// Guarantees res is normalized +// Assumes prime is nonzero and normalized +void bn_subtractmod(const bignum256* x, const bignum256* y, bignum256* res, const bignum256* prime) { + // res = x + (2 * prime - y) + + uint32_t acc = 1; + + for(int i = 0; i < BN_LIMBS; i++) { + acc += (BN_BASE - 1) + x->val[i] + 2 * prime->val[i] - y->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + (BASE - 1) + x[i] + 2 * prime[i] - y[i] + // >= (BASE - 1) - y[i] + // == (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) == 0 + // acc + (BASE - 1) + x[i] + 2 * prime[i] - y[i] + // <= acc + (BASE - 1) + x[i] + 2 * prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + 2 * (2**BITS_PER_LIMB - 1) + // <= (2**(32 - BITS_PER_LIMB) - 1) + 4 * (2**BITS_PER_LIMB - 1) + // == 7 + 4 * 2**29 - 4 == 2**31 + 3 < 2**32 + + res->val[i] = acc & (BN_BASE - 1); + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == 2**(BITS_PER_LIMB * (i + 1)) + x[:i+1] - y[:i+1] + 2*prime[:i+1] + // >> BITS_PER_LIMB * (i+1) + } + + // assert(acc == 1); // assert x + 2 * prime - y < 2**261 + + // clang-format off + // acc == 1 + // Proof: + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] + 2 * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y + 2 * prime >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x + (2 * prime - y) >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] + 2 * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y + 2 * prime >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x + (2 * prime - y) >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) + 0 + 1 >> BITS_PER_LIMB * LIMBS + // == 1 + // clang-format on +} + +// res = x - y +// Assumes x, y are normalized and x >= y +// Guarantees res is normalized +// Works properly even if &x == &y or &x == &res or &y == &res or +// &x == &y == &res +void bn_subtract(const bignum256* x, const bignum256* y, bignum256* res) { + uint32_t acc = 1; + for(int i = 0; i < BN_LIMBS; i++) { + acc += (BN_BASE - 1) + x->val[i] - y->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + (BASE - 1) + x[i] - y[i] + // >= (BASE - 1) - y == (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) + // == 0 + // acc + (BASE - 1) + x[i] - y[i] + // <= acc + (BASE - 1) + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + // == 7 + 2 * 2**29 < 2 **32 + + res->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == 2**(BITS_PER_LIMB * (i + 1)) + x[:i + 1] - y[:i + 1] + // >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 1); // assert x >= y + + // clang-format off + // acc == 1 + // Proof: + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) >> BITS_PER_LIMB * LIMBS + // == 1 +} + +// q = x // d, r = x % d +// Assumes x is normalized, 1 <= d <= 61304 +// Guarantees q is normalized +void bn_long_division(bignum256 *x, uint32_t d, bignum256 *q, uint32_t *r) { + assert(1 <= d && d < 61304); + + uint32_t acc = 0; + + *r = x->val[BN_LIMBS - 1] % d; + q->val[BN_LIMBS - 1] = x->val[BN_LIMBS - 1] / d; + + for (int i = BN_LIMBS - 2; i >= 0; i--) { + acc = *r * (BN_BASE % d) + x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // r * (BASE % d) + x[i] + // <= (d - 1) * (d - 1) + (2**BITS_PER_LIMB - 1) + // == d**2 - 2*d + 2**BITS_PER_LIMB + // == 61304**2 - 2 * 61304 + 2**29 + // == 3758057808 + 2**29 < 2**32 + + q->val[i] = *r * (BN_BASE / d) + (acc / d); + // q[i] doesn't overflow 32 bits + // Proof: + // r * (BASE // d) + (acc // d) + // <= (d - 1) * (2**BITS_PER_LIMB / d) + + // ((d**2 - 2*d + 2**BITS_PER_LIMB) / d) + // <= (d - 1) * (2**BITS_PER_LIMB / d) + (d - 2 + 2**BITS_PER_LIMB / d) + // == (d - 1 + 1) * (2**BITS_PER_LIMB / d) + d - 2 + // == 2**BITS_PER_LIMB + d - 2 <= 2**29 + 61304 < 2**32 + + // q[i] == (r * BASE + x[i]) // d + // Proof: + // q[i] == r * (BASE // d) + (acc // d) + // == r * (BASE // d) + (r * (BASE % d) + x[i]) // d + // == (r * d * (BASE // d) + r * (BASE % d) + x[i]) // d + // == (r * (d * (BASE // d) + (BASE % d)) + x[i]) // d + // == (r * BASE + x[i]) // d + + // q[i] < 2**BITS_PER_LIMB + // Proof: + // q[i] == (r * BASE + x[i]) // d + // <= ((d - 1) * 2**BITS_PER_LIMB + (2**BITS_PER_LIMB - 1)) / d + // == (d * 2**BITS_PER_LIMB - 1) / d == 2**BITS_PER_LIMB - 1 / d + // < 2**BITS_PER_LIMB + + *r = acc % d; + // r == (r * BASE + x[i]) % d + // Proof: + // r == acc % d == (r * (BASE % d) + x[i]) % d + // == (r * BASE + x[i]) % d + + // x[:i] == q[:i] * d + r + } +} + +// x = x // 58, r = x % 58 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod58(bignum256 *x, uint32_t *r) { bn_long_division(x, 58, x, r); } + +// x = x // 1000, r = x % 1000 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod1000(bignum256 *x, uint32_t *r) { + bn_long_division(x, 1000, x, r); +} + +// x = x // 10, r = x % 10 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod10(bignum256 *x, uint32_t *r) { bn_long_division(x, 10, x, r); } + +// Formats amount +// Assumes amount is normalized +// Assumes prefix and suffix are null-terminated strings +// Assumes output is an array of length output_length +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to any its argument +size_t bn_format(const bignum256 *amount, const char *prefix, const char *suffix, unsigned int decimals, int exponent, bool trailing, char thousands, char *output, size_t output_length) { + +/* + Python prototype of the function: + + def format(amount, prefix, suffix, decimals, exponent, trailing, thousands): + if exponent >= 0: + amount *= 10**exponent + else: + amount //= 10 ** (-exponent) + + d = pow(10, decimals) + + integer_part = amount // d + integer_str = f"{integer_part:,}".replace(",", thousands or "") + + if decimals: + decimal_part = amount % d + decimal_str = f".{decimal_part:0{decimals}d}" + if not trailing: + decimal_str = decimal_str.rstrip("0").rstrip(".") + else: + decimal_str = "" + + return prefix + integer_str + decimal_str + suffix +*/ + +// Auxiliary macro for bn_format +// If enough space adds one character to output starting from the end +#define BN_FORMAT_ADD_OUTPUT_CHAR(c) \ + { \ + --position; \ + if (output <= position && position < output + output_length) { \ + *position = (c); \ + } else { \ + memset(output, '\0', output_length); \ + return 0; \ + } \ + } + + bignum256 temp = {0}; + bn_copy(amount, &temp); + uint32_t digit = 0; + + char *position = output + output_length; + + // Add string ending character + BN_FORMAT_ADD_OUTPUT_CHAR('\0'); + + // Add suffix + size_t suffix_length = suffix ? strlen(suffix) : 0; + for (int i = suffix_length - 1; i >= 0; --i) + BN_FORMAT_ADD_OUTPUT_CHAR(suffix[i]) + + // amount //= 10**exponent + for (; exponent < 0; ++exponent) { + // if temp == 0, there is no need to divide it by 10 anymore + if (bn_is_zero(&temp)) { + exponent = 0; + break; + } + bn_divmod10(&temp, &digit); + } + + // exponent >= 0 && decimals >= 0 + + bool fractional_part = false; // is fractional-part of amount present + + { // Add fractional-part digits of amount + // Add trailing zeroes + unsigned int trailing_zeros = decimals < (unsigned int) exponent ? decimals : (unsigned int) exponent; + // When casting a negative int to unsigned int, UINT_MAX is added to the int before + // Since exponent >= 0, the value remains unchanged + decimals -= trailing_zeros; + exponent -= trailing_zeros; + + if (trailing && trailing_zeros) { + fractional_part = true; + for (; trailing_zeros > 0; --trailing_zeros) + BN_FORMAT_ADD_OUTPUT_CHAR('0') + } + + // exponent == 0 || decimals == 0 + + // Add significant digits and leading zeroes + for (; decimals > 0; --decimals) { + bn_divmod10(&temp, &digit); + + if (fractional_part || digit || trailing) { + fractional_part = true; + BN_FORMAT_ADD_OUTPUT_CHAR('0' + digit) + } + else if (bn_is_zero(&temp)) { + // We break since the remaining digits are zeroes and fractional_part == trailing == false + decimals = 0; + break; + } + } + // decimals == 0 + } + + if (fractional_part) { + BN_FORMAT_ADD_OUTPUT_CHAR('.') + } + + { // Add integer-part digits of amount + // Add trailing zeroes + int digits = 0; + if (!bn_is_zero(&temp)) { + for (; exponent > 0; --exponent) { + ++digits; + BN_FORMAT_ADD_OUTPUT_CHAR('0') + if (thousands != 0 && digits % 3 == 0) { + BN_FORMAT_ADD_OUTPUT_CHAR(thousands) + } + } + } + // decimals == 0 && exponent == 0 + + // Add significant digits + bool is_zero = false; + do { + ++digits; + bn_divmod10(&temp, &digit); + is_zero = bn_is_zero(&temp); + BN_FORMAT_ADD_OUTPUT_CHAR('0' + digit) + if (thousands != 0 && !is_zero && digits % 3 == 0) { + BN_FORMAT_ADD_OUTPUT_CHAR(thousands) + } + } while (!is_zero); + } + + // Add prefix + size_t prefix_length = prefix ? strlen(prefix) : 0; + for (int i = prefix_length - 1; i >= 0; --i) + BN_FORMAT_ADD_OUTPUT_CHAR(prefix[i]) + + // Move formatted amount to the start of output + int length = output - position + output_length; + memmove(output, position, length); + return length - 1; +} + +#if USE_BN_PRINT +// Prints x in hexadecimal +// Assumes x is normalized and x < 2**256 +void bn_print(const bignum256 *x) { + printf("%06x", x->val[8]); + printf("%08x", ((x->val[7] << 3) | (x->val[6] >> 26))); + printf("%07x", ((x->val[6] << 2) | (x->val[5] >> 27)) & 0x0FFFFFFF); + printf("%07x", ((x->val[5] << 1) | (x->val[4] >> 28)) & 0x0FFFFFFF); + printf("%07x", x->val[4] & 0x0FFFFFFF); + printf("%08x", ((x->val[3] << 3) | (x->val[2] >> 26))); + printf("%07x", ((x->val[2] << 2) | (x->val[1] >> 27)) & 0x0FFFFFFF); + printf("%07x", ((x->val[1] << 1) | (x->val[0] >> 28)) & 0x0FFFFFFF); + printf("%07x", x->val[0] & 0x0FFFFFFF); +} + +// Prints comma separated list of limbs of x +void bn_print_raw(const bignum256 *x) { + for (int i = 0; i < BN_LIMBS - 1; i++) { + printf("0x%08x, ", x->val[i]); + } + printf("0x%08x", x->val[BN_LIMBS - 1]); +} +#endif + +#if USE_INVERSE_FAST +void bn_inverse(bignum256 *x, const bignum256 *prime) { + bn_inverse_fast(x, prime); +} +#else +void bn_inverse(bignum256 *x, const bignum256 *prime) { + bn_inverse_slow(x, prime); +} +#endif diff --git a/applications/external/flipbip/lib/crypto/bignum.h b/applications/external/flipbip/lib/crypto/bignum.h new file mode 100644 index 000000000..858613401 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bignum.h @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2016 Alex Beregszaszi + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BIGNUM_H__ +#define __BIGNUM_H__ + +#include +#include +#include + +#include "options.h" + +#define BN_LIMBS 9 +#define BN_BITS_PER_LIMB 29 +#define BN_BASE (1u << BN_BITS_PER_LIMB) +#define BN_LIMB_MASK ((1u << BN_BITS_PER_LIMB) - 1) +#define BN_EXTRA_BITS (32 - BN_BITS_PER_LIMB) +#define BN_BITS_LAST_LIMB (256 - (BN_LIMBS - 1) * BN_BITS_PER_LIMB) + +// Represents the number sum([val[i] * 2**(29*i) for i in range(9)) +typedef struct { + uint32_t val[BN_LIMBS]; +} bignum256; + +static inline uint32_t read_be(const uint8_t* data) { + return (((uint32_t)data[0]) << 24) | (((uint32_t)data[1]) << 16) | (((uint32_t)data[2]) << 8) | + (((uint32_t)data[3])); +} + +static inline void write_be(uint8_t* data, uint32_t x) { + data[0] = x >> 24; + data[1] = x >> 16; + data[2] = x >> 8; + data[3] = x; +} + +static inline uint32_t read_le(const uint8_t* data) { + return (((uint32_t)data[3]) << 24) | (((uint32_t)data[2]) << 16) | (((uint32_t)data[1]) << 8) | + (((uint32_t)data[0])); +} + +static inline void write_le(uint8_t* data, uint32_t x) { + data[3] = x >> 24; + data[2] = x >> 16; + data[1] = x >> 8; + data[0] = x; +} + +void bn_read_be(const uint8_t* in_number, bignum256* out_number); +void bn_write_be(const bignum256* in_number, uint8_t* out_number); +void bn_read_le(const uint8_t* in_number, bignum256* out_number); +void bn_write_le(const bignum256* in_number, uint8_t* out_number); +void bn_read_uint32(uint32_t in_number, bignum256* out_number); +void bn_read_uint64(uint64_t in_number, bignum256* out_number); +int bn_bitcount(const bignum256* x); +unsigned int bn_digitcount(const bignum256* x); +void bn_zero(bignum256* x); +void bn_one(bignum256* x); +int bn_is_zero(const bignum256* x); +int bn_is_one(const bignum256* x); +int bn_is_less(const bignum256* x, const bignum256* y); +int bn_is_equal(const bignum256* x, const bignum256* y); +void bn_cmov( + bignum256* res, + volatile uint32_t cond, + const bignum256* truecase, + const bignum256* falsecase); +void bn_cnegate(volatile uint32_t cond, bignum256* x, const bignum256* prime); +void bn_lshift(bignum256* x); +void bn_rshift(bignum256* x); +void bn_setbit(bignum256* x, uint16_t i); +void bn_clearbit(bignum256* x, uint16_t i); +uint32_t bn_testbit(const bignum256* x, uint16_t i); +void bn_xor(bignum256* res, const bignum256* x, const bignum256* y); +void bn_mult_half(bignum256* x, const bignum256* prime); +void bn_mult_k(bignum256* x, uint8_t k, const bignum256* prime); +void bn_mod(bignum256* x, const bignum256* prime); +void bn_multiply(const bignum256* k, bignum256* x, const bignum256* prime); +void bn_fast_mod(bignum256* x, const bignum256* prime); +void bn_power_mod(const bignum256* x, const bignum256* e, const bignum256* prime, bignum256* res); +void bn_sqrt(bignum256* x, const bignum256* prime); +uint32_t inverse_mod_power_two(uint32_t a, uint32_t n); +void bn_divide_base(bignum256* x, const bignum256* prime); +void bn_normalize(bignum256* x); +void bn_add(bignum256* x, const bignum256* y); +void bn_addmod(bignum256* x, const bignum256* y, const bignum256* prime); +void bn_addi(bignum256* x, uint32_t y); +void bn_subi(bignum256* x, uint32_t y, const bignum256* prime); +void bn_subtractmod(const bignum256* x, const bignum256* y, bignum256* res, const bignum256* prime); +void bn_subtract(const bignum256* x, const bignum256* y, bignum256* res); +void bn_long_division(bignum256* x, uint32_t d, bignum256* q, uint32_t* r); +void bn_divmod58(bignum256* x, uint32_t* r); +void bn_divmod1000(bignum256* x, uint32_t* r); +void bn_inverse(bignum256* x, const bignum256* prime); +size_t bn_format( + const bignum256* amount, + const char* prefix, + const char* suffix, + unsigned int decimals, + int exponent, + bool trailing, + char thousands, + char* output, + size_t output_length); + +// Returns (uint32_t) in_number +// Assumes in_number < 2**32 +// Assumes in_number is normalized +static inline uint32_t bn_write_uint32(const bignum256* in_number) { + return in_number->val[0] | (in_number->val[1] << BN_BITS_PER_LIMB); +} + +// Returns (uint64_t) in_number +// Assumes in_number < 2**64 +// Assumes in_number is normalized +static inline uint64_t bn_write_uint64(const bignum256* in_number) { + uint64_t acc; + acc = in_number->val[2]; + acc <<= BN_BITS_PER_LIMB; + acc |= in_number->val[1]; + acc <<= BN_BITS_PER_LIMB; + acc |= in_number->val[0]; + return acc; +} + +// y = x +static inline void bn_copy(const bignum256* x, bignum256* y) { + *y = *x; +} + +// Returns x % 2 == 0 +static inline int bn_is_even(const bignum256* x) { + return (x->val[0] & 1) == 0; +} + +// Returns x % 2 == 0 +static inline int bn_is_odd(const bignum256* x) { + return (x->val[0] & 1) == 1; +} + +static inline size_t bn_format_uint64( + uint64_t amount, + const char* prefix, + const char* suffix, + unsigned int decimals, + int exponent, + bool trailing, + char thousands, + char* output, + size_t output_length) { + bignum256 bn_amount; + bn_read_uint64(amount, &bn_amount); + + return bn_format( + &bn_amount, prefix, suffix, decimals, exponent, trailing, thousands, output, output_length); +} + +static inline size_t bn_format_amount( + uint64_t amount, + const char* prefix, + const char* suffix, + unsigned int decimals, + char* output, + size_t output_length) { + return bn_format_uint64( + amount, prefix, suffix, decimals, 0, false, ',', output, output_length); +} + +#if USE_BN_PRINT +void bn_print(const bignum256* x); +void bn_print_raw(const bignum256* x); +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/bip32.c b/applications/external/flipbip/lib/crypto/bip32.c new file mode 100644 index 000000000..09f00d60e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip32.c @@ -0,0 +1,885 @@ +/** + * Copyright (c) 2013-2016 Tomas Dzetkulic + * Copyright (c) 2013-2016 Pavol Rusnak + * Copyright (c) 2015-2016 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "address.h" +#include "aes/aes.h" +#include "base58.h" +#include "bignum.h" +#include "bip32.h" +#include "cardano.h" +#include "curves.h" +#include "ecdsa.h" +#include "ed25519_donna/ed25519_sha3.h" +#include "ed25519_donna/ed25519.h" +#include "hmac.h" +#include "nist256p1.h" +#include "secp256k1.h" +#include "sha2.h" +#include "sha3.h" +#if USE_KECCAK +#include "ed25519_donna/ed25519_keccak.h" +#endif +#if USE_NEM +#include "nem.h" +#endif +#include "memzero.h" + +const curve_info ed25519_info = { + .bip32_name = ED25519_SEED_NAME, + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +const curve_info ed25519_sha3_info = { + .bip32_name = "ed25519-sha3 seed", + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +#if USE_KECCAK +const curve_info ed25519_keccak_info = { + .bip32_name = "ed25519-keccak seed", + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; +#endif + +const curve_info curve25519_info = { + .bip32_name = "curve25519 seed", + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +int hdnode_from_xpub( + uint32_t depth, + uint32_t child_num, + const uint8_t* chain_code, + const uint8_t* public_key, + const char* curve, + HDNode* out) { + const curve_info* info = get_curve_by_name(curve); + if(info == 0) { + return 0; + } + if(public_key[0] != 0x02 && public_key[0] != 0x03) { // invalid pubkey + return 0; + } + out->curve = info; + out->depth = depth; + out->child_num = child_num; + memcpy(out->chain_code, chain_code, 32); + memzero(out->private_key, 32); + memzero(out->private_key_extension, 32); + memcpy(out->public_key, public_key, 33); + return 1; +} + +int hdnode_from_xprv( + uint32_t depth, + uint32_t child_num, + const uint8_t* chain_code, + const uint8_t* private_key, + const char* curve, + HDNode* out) { + bool failed = false; + const curve_info* info = get_curve_by_name(curve); + if(info == 0) { + failed = true; + } else if(info->params) { + bignum256 a = {0}; + bn_read_be(private_key, &a); + if(bn_is_zero(&a)) { // == 0 + failed = true; + } else { + if(!bn_is_less(&a, &info->params->order)) { // >= order + failed = true; + } + } + memzero(&a, sizeof(a)); + } + + if(failed) { + return 0; + } + + out->curve = info; + out->depth = depth; + out->child_num = child_num; + memcpy(out->chain_code, chain_code, 32); + memcpy(out->private_key, private_key, 32); + memzero(out->public_key, sizeof(out->public_key)); + memzero(out->private_key_extension, sizeof(out->private_key_extension)); + return 1; +} + +int hdnode_from_seed(const uint8_t* seed, int seed_len, const char* curve, HDNode* out) { + static CONFIDENTIAL uint8_t I[32 + 32]; + memzero(out, sizeof(HDNode)); + out->depth = 0; + out->child_num = 0; + out->curve = get_curve_by_name(curve); + if(out->curve == 0) { + return 0; + } + static CONFIDENTIAL HMAC_SHA512_CTX ctx; + hmac_sha512_Init(&ctx, (const uint8_t*)out->curve->bip32_name, strlen(out->curve->bip32_name)); + hmac_sha512_Update(&ctx, seed, seed_len); + hmac_sha512_Final(&ctx, I); + + if(out->curve->params) { + bignum256 a = {0}; + while(true) { + bn_read_be(I, &a); + if(!bn_is_zero(&a) // != 0 + && bn_is_less(&a, &out->curve->params->order)) { // < order + break; + } + hmac_sha512_Init( + &ctx, (const uint8_t*)out->curve->bip32_name, strlen(out->curve->bip32_name)); + hmac_sha512_Update(&ctx, I, sizeof(I)); + hmac_sha512_Final(&ctx, I); + } + memzero(&a, sizeof(a)); + } + memcpy(out->private_key, I, 32); + memcpy(out->chain_code, I + 32, 32); + memzero(out->public_key, sizeof(out->public_key)); + memzero(I, sizeof(I)); + return 1; +} + +uint32_t hdnode_fingerprint(HDNode* node) { + uint8_t digest[32] = {0}; + uint32_t fingerprint = 0; + + hdnode_fill_public_key(node); + hasher_Raw(node->curve->hasher_pubkey, node->public_key, 33, digest); + fingerprint = ((uint32_t)digest[0] << 24) + (digest[1] << 16) + (digest[2] << 8) + digest[3]; + memzero(digest, sizeof(digest)); + return fingerprint; +} + +int hdnode_private_ckd_bip32(HDNode* inout, uint32_t i) { + static CONFIDENTIAL uint8_t data[1 + 32 + 4]; + static CONFIDENTIAL uint8_t I[32 + 32]; + static CONFIDENTIAL bignum256 a, b; + +#if USE_CARDANO + if(inout->curve == &ed25519_cardano_info) { + return 0; + } +#endif + + if(i & 0x80000000) { // private derivation + data[0] = 0; + memcpy(data + 1, inout->private_key, 32); + } else { // public derivation + if(!inout->curve->params) { + return 0; + } + if(hdnode_fill_public_key(inout) != 0) { + return 0; + } + memcpy(data, inout->public_key, 33); + } + write_be(data + 33, i); + + bn_read_be(inout->private_key, &a); + + static CONFIDENTIAL HMAC_SHA512_CTX ctx; + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, sizeof(data)); + hmac_sha512_Final(&ctx, I); + + if(inout->curve->params) { + while(true) { + bool failed = false; + bn_read_be(I, &b); + if(!bn_is_less(&b, &inout->curve->params->order)) { // >= order + failed = true; + } else { + bn_add(&b, &a); + bn_mod(&b, &inout->curve->params->order); + if(bn_is_zero(&b)) { + failed = true; + } + } + + if(!failed) { + bn_write_be(&b, inout->private_key); + break; + } + + data[0] = 1; + memcpy(data + 1, I + 32, 32); + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, sizeof(data)); + hmac_sha512_Final(&ctx, I); + } + } else { + memcpy(inout->private_key, I, 32); + } + + memcpy(inout->chain_code, I + 32, 32); + inout->depth++; + inout->child_num = i; + memzero(inout->public_key, sizeof(inout->public_key)); + + // making sure to wipe our memory + memzero(&a, sizeof(a)); + memzero(&b, sizeof(b)); + memzero(I, sizeof(I)); + memzero(data, sizeof(data)); + return 1; +} + +int hdnode_private_ckd(HDNode* inout, uint32_t i) { +#if USE_CARDANO + if(inout->curve == &ed25519_cardano_info) { + return hdnode_private_ckd_cardano(inout, i); + } else +#endif + { + return hdnode_private_ckd_bip32(inout, i); + } +} + +int hdnode_public_ckd_cp( + const ecdsa_curve* curve, + const curve_point* parent, + const uint8_t* parent_chain_code, + uint32_t i, + curve_point* child, + uint8_t* child_chain_code) { + uint8_t data[(1 + 32) + 4] = {0}; + uint8_t I[32 + 32] = {0}; + bignum256 c = {0}; + + if(i & 0x80000000) { // private derivation + return 0; + } + + data[0] = 0x02 | (parent->y.val[0] & 0x01); + bn_write_be(&parent->x, data + 1); + write_be(data + 33, i); + + while(true) { + hmac_sha512(parent_chain_code, 32, data, sizeof(data), I); + bn_read_be(I, &c); + if(bn_is_less(&c, &curve->order)) { // < order + scalar_multiply(curve, &c, child); // b = c * G + point_add(curve, parent, child); // b = a + b + if(!point_is_infinity(child)) { + if(child_chain_code) { + memcpy(child_chain_code, I + 32, 32); + } + + // Wipe all stack data. + memzero(data, sizeof(data)); + memzero(I, sizeof(I)); + memzero(&c, sizeof(c)); + return 1; + } + } + + data[0] = 1; + memcpy(data + 1, I + 32, 32); + } +} + +int hdnode_public_ckd(HDNode* inout, uint32_t i) { + curve_point parent = {0}, child = {0}; + + if(!ecdsa_read_pubkey(inout->curve->params, inout->public_key, &parent)) { + return 0; + } + if(!hdnode_public_ckd_cp( + inout->curve->params, &parent, inout->chain_code, i, &child, inout->chain_code)) { + return 0; + } + memzero(inout->private_key, 32); + inout->depth++; + inout->child_num = i; + inout->public_key[0] = 0x02 | (child.y.val[0] & 0x01); + bn_write_be(&child.x, inout->public_key + 1); + + // Wipe all stack data. + memzero(&parent, sizeof(parent)); + memzero(&child, sizeof(child)); + + return 1; +} + +void hdnode_public_ckd_address_optimized( + const curve_point* pub, + const uint8_t* chain_code, + uint32_t i, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize, + int addrformat) { + uint8_t child_pubkey[33] = {0}; + curve_point b = {0}; + + hdnode_public_ckd_cp(&secp256k1, pub, chain_code, i, &b, NULL); + child_pubkey[0] = 0x02 | (b.y.val[0] & 0x01); + bn_write_be(&b.x, child_pubkey + 1); + + switch(addrformat) { + case 1: // Segwit-in-P2SH + ecdsa_get_address_segwit_p2sh( + child_pubkey, version, hasher_pubkey, hasher_base58, addr, addrsize); + break; + default: // normal address + ecdsa_get_address(child_pubkey, version, hasher_pubkey, hasher_base58, addr, addrsize); + break; + } +} + +#if USE_BIP32_CACHE +static bool private_ckd_cache_root_set = false; +static CONFIDENTIAL HDNode private_ckd_cache_root; +static int private_ckd_cache_index = 0; + +static CONFIDENTIAL struct { + bool set; + size_t depth; + uint32_t i[BIP32_CACHE_MAXDEPTH]; + HDNode node; +} private_ckd_cache[BIP32_CACHE_SIZE]; + +void bip32_cache_clear(void) { + private_ckd_cache_root_set = false; + private_ckd_cache_index = 0; + memzero(&private_ckd_cache_root, sizeof(private_ckd_cache_root)); + memzero(private_ckd_cache, sizeof(private_ckd_cache)); +} + +int hdnode_private_ckd_cached( + HDNode* inout, + const uint32_t* i, + size_t i_count, + uint32_t* fingerprint) { + if(i_count == 0) { + // no way how to compute parent fingerprint + return 1; + } + if(i_count == 1) { + if(fingerprint) { + *fingerprint = hdnode_fingerprint(inout); + } + if(hdnode_private_ckd(inout, i[0]) == 0) return 0; + return 1; + } + + bool found = false; + // if root is not set or not the same + if(!private_ckd_cache_root_set || + memcmp(&private_ckd_cache_root, inout, sizeof(HDNode)) != 0) { + // clear the cache + private_ckd_cache_index = 0; + memzero(private_ckd_cache, sizeof(private_ckd_cache)); + // setup new root + memcpy(&private_ckd_cache_root, inout, sizeof(HDNode)); + private_ckd_cache_root_set = true; + } else { + // try to find parent + int j = 0; + for(j = 0; j < BIP32_CACHE_SIZE; j++) { + if(private_ckd_cache[j].set && private_ckd_cache[j].depth == i_count - 1 && + memcmp(private_ckd_cache[j].i, i, (i_count - 1) * sizeof(uint32_t)) == 0 && + private_ckd_cache[j].node.curve == inout->curve) { + memcpy(inout, &(private_ckd_cache[j].node), sizeof(HDNode)); + found = true; + break; + } + } + } + + // else derive parent + if(!found) { + size_t k = 0; + for(k = 0; k < i_count - 1; k++) { + if(hdnode_private_ckd(inout, i[k]) == 0) return 0; + } + // and save it + memzero( + &(private_ckd_cache[private_ckd_cache_index]), + sizeof(private_ckd_cache[private_ckd_cache_index])); + private_ckd_cache[private_ckd_cache_index].set = true; + private_ckd_cache[private_ckd_cache_index].depth = i_count - 1; + memcpy(private_ckd_cache[private_ckd_cache_index].i, i, (i_count - 1) * sizeof(uint32_t)); + memcpy(&(private_ckd_cache[private_ckd_cache_index].node), inout, sizeof(HDNode)); + private_ckd_cache_index = (private_ckd_cache_index + 1) % BIP32_CACHE_SIZE; + } + + if(fingerprint) { + *fingerprint = hdnode_fingerprint(inout); + } + if(hdnode_private_ckd(inout, i[i_count - 1]) == 0) return 0; + + return 1; +} +#endif + +int hdnode_get_address_raw(HDNode* node, uint32_t version, uint8_t* addr_raw) { + if(hdnode_fill_public_key(node) != 0) { + return 1; + } + ecdsa_get_address_raw(node->public_key, version, node->curve->hasher_pubkey, addr_raw); + return 0; +} + +int hdnode_get_address(HDNode* node, uint32_t version, char* addr, int addrsize) { + if(hdnode_fill_public_key(node) != 0) { + return 1; + } + ecdsa_get_address( + node->public_key, + version, + node->curve->hasher_pubkey, + node->curve->hasher_base58, + addr, + addrsize); + return 0; +} + +int hdnode_fill_public_key(HDNode* node) { + if(node->public_key[0] != 0) return 0; + +#if USE_BIP32_25519_CURVES + if(node->curve->params) { + if(ecdsa_get_public_key33(node->curve->params, node->private_key, node->public_key) != 0) { + return 1; + } + } else { + node->public_key[0] = 1; + if(node->curve == &ed25519_info) { + ed25519_publickey(node->private_key, node->public_key + 1); + } else if(node->curve == &ed25519_sha3_info) { + ed25519_publickey_sha3(node->private_key, node->public_key + 1); +#if USE_KECCAK + } else if(node->curve == &ed25519_keccak_info) { + ed25519_publickey_keccak(node->private_key, node->public_key + 1); +#endif + } else if(node->curve == &curve25519_info) { + curve25519_scalarmult_basepoint(node->public_key + 1, node->private_key); +#if USE_CARDANO + } else if(node->curve == &ed25519_cardano_info) { + ed25519_publickey_ext(node->private_key, node->public_key + 1); +#endif + } + } +#else + + if(ecdsa_get_public_key33(node->curve->params, node->private_key, node->public_key) != 0) { + return 1; + } +#endif + return 0; +} + +#if USE_ETHEREUM +int hdnode_get_ethereum_pubkeyhash(const HDNode* node, uint8_t* pubkeyhash) { + uint8_t buf[65] = {0}; + //SHA3_CTX ctx = {0}; + SHA3_CTX* ctx = malloc(sizeof(SHA3_CTX)); + memzero(ctx, sizeof(SHA3_CTX)); + + /* get uncompressed public key */ + if(ecdsa_get_public_key65(node->curve->params, node->private_key, buf) != 0) { + memzero(ctx, sizeof(SHA3_CTX)); + free(ctx); + return 0; + } + + /* compute sha3 of x and y coordinate without 04 prefix */ + sha3_256_Init(ctx); + sha3_Update(ctx, buf + 1, 64); + keccak_Final(ctx, buf); + + memzero(ctx, sizeof(SHA3_CTX)); + free(ctx); + + /* result are the least significant 160 bits */ + memcpy(pubkeyhash, buf + 12, 20); + + return 1; +} +#endif + +#if USE_NEM +int hdnode_get_nem_address(HDNode* node, uint8_t version, char* address) { + if(node->curve != &ed25519_keccak_info) { + return 0; + } + + if(hdnode_fill_public_key(node) != 0) { + return 0; + } + + return nem_get_address(&node->public_key[1], version, address); +} + +int hdnode_get_nem_shared_key( + const HDNode* node, + const ed25519_public_key peer_public_key, + const uint8_t* salt, + ed25519_public_key mul, + uint8_t* shared_key) { + if(node->curve != &ed25519_keccak_info) { + return 0; + } + + // sizeof(ed25519_public_key) == SHA3_256_DIGEST_LENGTH + if(mul == NULL) mul = shared_key; + + if(ed25519_scalarmult_keccak(mul, node->private_key, peer_public_key)) { + return 0; + } + + for(size_t i = 0; i < 32; i++) { + shared_key[i] = mul[i] ^ salt[i]; + } + + keccak_256(shared_key, 32, shared_key); + return 1; +} + +int hdnode_nem_encrypt( + const HDNode* node, + const ed25519_public_key public_key, + const uint8_t* iv_immut, + const uint8_t* salt, + const uint8_t* payload, + size_t size, + uint8_t* buffer) { + uint8_t last_block[AES_BLOCK_SIZE] = {0}; + uint8_t remainder = size % AES_BLOCK_SIZE; + + // Round down to last whole block + size -= remainder; + // Copy old last block + memcpy(last_block, &payload[size], remainder); + // Pad new last block with number of missing bytes + memset(&last_block[remainder], AES_BLOCK_SIZE - remainder, AES_BLOCK_SIZE - remainder); + + // the IV gets mutated, so we make a copy not to touch the original + uint8_t iv[AES_BLOCK_SIZE] = {0}; + memcpy(iv, iv_immut, AES_BLOCK_SIZE); + + uint8_t shared_key[SHA3_256_DIGEST_LENGTH] = {0}; + if(!hdnode_get_nem_shared_key(node, public_key, salt, NULL, shared_key)) { + return 0; + } + + aes_encrypt_ctx ctx = {0}; + + int ret = aes_encrypt_key256(shared_key, &ctx); + memzero(shared_key, sizeof(shared_key)); + + if(ret != EXIT_SUCCESS) { + return 0; + } + + if(aes_cbc_encrypt(payload, buffer, size, iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + if(aes_cbc_encrypt(last_block, &buffer[size], sizeof(last_block), iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + return 1; +} + +int hdnode_nem_decrypt( + const HDNode* node, + const ed25519_public_key public_key, + uint8_t* iv, + const uint8_t* salt, + const uint8_t* payload, + size_t size, + uint8_t* buffer) { + uint8_t shared_key[SHA3_256_DIGEST_LENGTH] = {0}; + + if(!hdnode_get_nem_shared_key(node, public_key, salt, NULL, shared_key)) { + return 0; + } + + aes_decrypt_ctx ctx = {0}; + + int ret = aes_decrypt_key256(shared_key, &ctx); + memzero(shared_key, sizeof(shared_key)); + + if(ret != EXIT_SUCCESS) { + return 0; + } + + if(aes_cbc_decrypt(payload, buffer, size, iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + return 1; +} +#endif + +// msg is a data to be signed +// msg_len is the message length +int hdnode_sign( + HDNode* node, + const uint8_t* msg, + uint32_t msg_len, + HasherType hasher_sign, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])) { + if(node->curve->params) { + return ecdsa_sign( + node->curve->params, + hasher_sign, + node->private_key, + msg, + msg_len, + sig, + pby, + is_canonical); + } else if(node->curve == &curve25519_info) { + return 1; // signatures are not supported + } else { + if(node->curve == &ed25519_info) { + ed25519_sign(msg, msg_len, node->private_key, sig); + } else if(node->curve == &ed25519_sha3_info) { + ed25519_sign_sha3(msg, msg_len, node->private_key, sig); +#if USE_KECCAK + } else if(node->curve == &ed25519_keccak_info) { + ed25519_sign_keccak(msg, msg_len, node->private_key, sig); +#endif + } else { + return 1; // unknown or unsupported curve + } + return 0; + } +} + +int hdnode_sign_digest( + HDNode* node, + const uint8_t* digest, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])) { + if(node->curve->params) { + return ecdsa_sign_digest( + node->curve->params, node->private_key, digest, sig, pby, is_canonical); + } else if(node->curve == &curve25519_info) { + return 1; // signatures are not supported + } else { + return hdnode_sign(node, digest, 32, 0, sig, pby, is_canonical); + } +} + +int hdnode_get_shared_key( + const HDNode* node, + const uint8_t* peer_public_key, + uint8_t* session_key, + int* result_size) { + // Use elliptic curve Diffie-Helman to compute shared session key + if(node->curve->params) { + if(ecdh_multiply(node->curve->params, node->private_key, peer_public_key, session_key) != + 0) { + return 1; + } + *result_size = 65; + return 0; + } else if(node->curve == &curve25519_info) { + session_key[0] = 0x04; + if(peer_public_key[0] != 0x40) { + return 1; // Curve25519 public key should start with 0x40 byte. + } + curve25519_scalarmult(session_key + 1, node->private_key, peer_public_key + 1); + *result_size = 33; + return 0; + } else { + *result_size = 0; + return 1; // ECDH is not supported + } +} + +static int hdnode_serialize( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + bool use_private, + char* str, + int strsize) { + uint8_t node_data[78] = {0}; + write_be(node_data, version); + node_data[4] = node->depth; + write_be(node_data + 5, fingerprint); + write_be(node_data + 9, node->child_num); + memcpy(node_data + 13, node->chain_code, 32); + if(use_private) { + node_data[45] = 0; + memcpy(node_data + 46, node->private_key, 32); + } else { + memcpy(node_data + 45, node->public_key, 33); + } + int ret = base58_encode_check( + node_data, sizeof(node_data), node->curve->hasher_base58, str, strsize); + memzero(node_data, sizeof(node_data)); + return ret; +} + +int hdnode_serialize_public( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + char* str, + int strsize) { + return hdnode_serialize(node, fingerprint, version, false, str, strsize); +} + +int hdnode_serialize_private( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + char* str, + int strsize) { + return hdnode_serialize(node, fingerprint, version, true, str, strsize); +} + +// check for validity of curve point in case of public data not performed +static int hdnode_deserialize( + const char* str, + uint32_t version, + bool use_private, + const char* curve, + HDNode* node, + uint32_t* fingerprint) { + uint8_t node_data[78] = {0}; + memzero(node, sizeof(HDNode)); + node->curve = get_curve_by_name(curve); + if(base58_decode_check(str, node->curve->hasher_base58, node_data, sizeof(node_data)) != + sizeof(node_data)) { + return -1; + } + uint32_t ver = read_be(node_data); + if(ver != version) { + return -3; // invalid version + } + if(use_private) { + // invalid data + if(node_data[45]) { + return -2; + } + memcpy(node->private_key, node_data + 46, 32); + memzero(node->public_key, sizeof(node->public_key)); + } else { + memzero(node->private_key, sizeof(node->private_key)); + memcpy(node->public_key, node_data + 45, 33); + } + node->depth = node_data[4]; + if(fingerprint) { + *fingerprint = read_be(node_data + 5); + } + node->child_num = read_be(node_data + 9); + memcpy(node->chain_code, node_data + 13, 32); + return 0; +} + +int hdnode_deserialize_public( + const char* str, + uint32_t version, + const char* curve, + HDNode* node, + uint32_t* fingerprint) { + return hdnode_deserialize(str, version, false, curve, node, fingerprint); +} + +int hdnode_deserialize_private( + const char* str, + uint32_t version, + const char* curve, + HDNode* node, + uint32_t* fingerprint) { + return hdnode_deserialize(str, version, true, curve, node, fingerprint); +} + +const curve_info* get_curve_by_name(const char* curve_name) { + if(curve_name == 0) { + return 0; + } + if(strcmp(curve_name, SECP256K1_NAME) == 0) { + return &secp256k1_info; + } + if(strcmp(curve_name, SECP256K1_DECRED_NAME) == 0) { + return &secp256k1_decred_info; + } + if(strcmp(curve_name, SECP256K1_GROESTL_NAME) == 0) { + return &secp256k1_groestl_info; + } + if(strcmp(curve_name, SECP256K1_SMART_NAME) == 0) { + return &secp256k1_smart_info; + } + if(strcmp(curve_name, NIST256P1_NAME) == 0) { + return &nist256p1_info; + } + if(strcmp(curve_name, ED25519_NAME) == 0) { + return &ed25519_info; + } +#if USE_CARDANO + if(strcmp(curve_name, ED25519_CARDANO_NAME) == 0) { + return &ed25519_cardano_info; + } +#endif + if(strcmp(curve_name, ED25519_SHA3_NAME) == 0) { + return &ed25519_sha3_info; + } +#if USE_KECCAK + if(strcmp(curve_name, ED25519_KECCAK_NAME) == 0) { + return &ed25519_keccak_info; + } +#endif + if(strcmp(curve_name, CURVE25519_NAME) == 0) { + return &curve25519_info; + } + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/bip32.h b/applications/external/flipbip/lib/crypto/bip32.h new file mode 100644 index 000000000..3e3114848 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip32.h @@ -0,0 +1,202 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BIP32_H__ +#define __BIP32_H__ + +#include +#include +#include +#include "ecdsa.h" +#include "ed25519_donna/ed25519.h" +#include "options.h" + +// Maximum length of a Base58Check-encoded extended public or private key. +#define XPUB_MAXLEN 112 + +// Maximum length of a Base58Check-encoded address. +#define ADDRESS_MAXLEN 39 + +typedef struct { + const char* bip32_name; // string for generating BIP32 xprv from seed + const ecdsa_curve* params; // ecdsa curve parameters, null for ed25519 + + HasherType hasher_base58; + HasherType hasher_sign; + HasherType hasher_pubkey; + HasherType hasher_script; +} curve_info; + +typedef struct { + uint32_t depth; + uint32_t child_num; + uint8_t chain_code[32]; + + uint8_t private_key[32]; + uint8_t private_key_extension[32]; + + uint8_t public_key[33]; + const curve_info* curve; +} HDNode; + +int hdnode_from_xpub( + uint32_t depth, + uint32_t child_num, + const uint8_t* chain_code, + const uint8_t* public_key, + const char* curve, + HDNode* out); + +int hdnode_from_xprv( + uint32_t depth, + uint32_t child_num, + const uint8_t* chain_code, + const uint8_t* private_key, + const char* curve, + HDNode* out); + +int hdnode_from_seed(const uint8_t* seed, int seed_len, const char* curve, HDNode* out); + +#define hdnode_private_ckd_prime(X, I) hdnode_private_ckd((X), ((I) | 0x80000000)) + +int hdnode_private_ckd(HDNode* inout, uint32_t i); + +int hdnode_public_ckd_cp( + const ecdsa_curve* curve, + const curve_point* parent, + const uint8_t* parent_chain_code, + uint32_t i, + curve_point* child, + uint8_t* child_chain_code); + +int hdnode_public_ckd(HDNode* inout, uint32_t i); + +void hdnode_public_ckd_address_optimized( + const curve_point* pub, + const uint8_t* chain_code, + uint32_t i, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize, + int addrformat); + +#if USE_BIP32_CACHE +void bip32_cache_clear(void); +int hdnode_private_ckd_cached( + HDNode* inout, + const uint32_t* i, + size_t i_count, + uint32_t* fingerprint); +#endif + +uint32_t hdnode_fingerprint(HDNode* node); + +int hdnode_fill_public_key(HDNode* node); + +#if USE_ETHEREUM +int hdnode_get_ethereum_pubkeyhash(const HDNode* node, uint8_t* pubkeyhash); +#endif + +#if USE_NEM +int hdnode_get_nem_address(HDNode* node, uint8_t version, char* address); +int hdnode_get_nem_shared_key( + const HDNode* node, + const ed25519_public_key peer_public_key, + const uint8_t* salt, + ed25519_public_key mul, + uint8_t* shared_key); +int hdnode_nem_encrypt( + const HDNode* node, + const ed25519_public_key public_key, + const uint8_t* iv, + const uint8_t* salt, + const uint8_t* payload, + size_t size, + uint8_t* buffer); +int hdnode_nem_decrypt( + const HDNode* node, + const ed25519_public_key public_key, + uint8_t* iv, + const uint8_t* salt, + const uint8_t* payload, + size_t size, + uint8_t* buffer); +#endif + +int hdnode_sign( + HDNode* node, + const uint8_t* msg, + uint32_t msg_len, + HasherType hasher_sign, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])); +int hdnode_sign_digest( + HDNode* node, + const uint8_t* digest, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])); + +int hdnode_get_shared_key( + const HDNode* node, + const uint8_t* peer_public_key, + uint8_t* session_key, + int* result_size); + +int hdnode_serialize_public( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + char* str, + int strsize); + +int hdnode_serialize_private( + const HDNode* node, + uint32_t fingerprint, + uint32_t version, + char* str, + int strsize); + +int hdnode_deserialize_public( + const char* str, + uint32_t version, + const char* curve, + HDNode* node, + uint32_t* fingerprint); + +int hdnode_deserialize_private( + const char* str, + uint32_t version, + const char* curve, + HDNode* node, + uint32_t* fingerprint); + +int hdnode_get_address_raw(HDNode* node, uint32_t version, uint8_t* addr_raw); +int hdnode_get_address(HDNode* node, uint32_t version, char* addr, int addrsize); + +const curve_info* get_curve_by_name(const char* curve_name); + +#endif diff --git a/applications/external/flipbip/lib/crypto/bip39.c b/applications/external/flipbip/lib/crypto/bip39.c new file mode 100644 index 000000000..c99fe76c9 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip39.c @@ -0,0 +1,288 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "bip39.h" +#include "hmac.h" +#include "memzero.h" +#include "options.h" +#include "pbkdf2.h" +#include "rand.h" +#include "sha2.h" + +#if USE_BIP39_CACHE + +static int bip39_cache_index = 0; + +static CONFIDENTIAL struct { + bool set; + char mnemonic[256]; + char passphrase[64]; + uint8_t seed[512 / 8]; +} bip39_cache[BIP39_CACHE_SIZE]; + +void bip39_cache_clear(void) { + memzero(bip39_cache, sizeof(bip39_cache)); + bip39_cache_index = 0; +} + +#endif + +const char* mnemonic_generate(int strength) { + if(strength % 32 || strength < 128 || strength > 256) { + return 0; + } + uint8_t data[32] = {0}; + random_buffer(data, 32); + const char* r = mnemonic_from_data(data, strength / 8); + memzero(data, sizeof(data)); + return r; +} + +static CONFIDENTIAL char mnemo[24 * 10]; + +const char* mnemonic_from_data(const uint8_t* data, int len) { + if(len % 4 || len < 16 || len > 32) { + return 0; + } + + uint8_t bits[32 + 1] = {0}; + + sha256_Raw(data, len, bits); + // checksum + bits[len] = bits[0]; + // data + memcpy(bits, data, len); + + int mlen = len * 3 / 4; + + int i = 0, j = 0, idx = 0; + char* p = mnemo; + for(i = 0; i < mlen; i++) { + idx = 0; + for(j = 0; j < 11; j++) { + idx <<= 1; + idx += (bits[(i * 11 + j) / 8] & (1 << (7 - ((i * 11 + j) % 8)))) > 0; + } + strcpy(p, BIP39_WORDLIST_ENGLISH[idx]); + p += strlen(BIP39_WORDLIST_ENGLISH[idx]); + *p = (i < mlen - 1) ? ' ' : 0; + p++; + } + memzero(bits, sizeof(bits)); + + return mnemo; +} + +void mnemonic_clear(void) { + memzero(mnemo, sizeof(mnemo)); +} + +int mnemonic_to_bits(const char* mnemonic, uint8_t* bits) { + if(!mnemonic) { + return 0; + } + + uint32_t i = 0, n = 0; + + while(mnemonic[i]) { + if(mnemonic[i] == ' ') { + n++; + } + i++; + } + n++; + + // check that number of words is valid for BIP-39: + // (a) between 128 and 256 bits of initial entropy (12 - 24 words) + // (b) number of bits divisible by 33 (1 checksum bit per 32 input bits) + // - that is, (n * 11) % 33 == 0, so n % 3 == 0 + if(n < 12 || n > 24 || (n % 3)) { + return 0; + } + + char current_word[10] = {0}; + uint32_t j = 0, ki = 0, bi = 0; + uint8_t result[32 + 1] = {0}; + + memzero(result, sizeof(result)); + i = 0; + while(mnemonic[i]) { + j = 0; + while(mnemonic[i] != ' ' && mnemonic[i] != 0) { + if(j >= sizeof(current_word) - 1) { + return 0; + } + current_word[j] = mnemonic[i]; + i++; + j++; + } + current_word[j] = 0; + if(mnemonic[i] != 0) { + i++; + } + int k = mnemonic_find_word(current_word); + if(k < 0) { // word not found + return 0; + } + for(ki = 0; ki < 11; ki++) { + if(k & (1 << (10 - ki))) { + result[bi / 8] |= 1 << (7 - (bi % 8)); + } + bi++; + } + } + if(bi != n * 11) { + return 0; + } + memcpy(bits, result, sizeof(result)); + memzero(result, sizeof(result)); + + // returns amount of entropy + checksum BITS + return n * 11; +} + +int mnemonic_check(const char* mnemonic) { + uint8_t bits[32 + 1] = {0}; + int mnemonic_bits_len = mnemonic_to_bits(mnemonic, bits); + if(mnemonic_bits_len != (12 * 11) && mnemonic_bits_len != (18 * 11) && + mnemonic_bits_len != (24 * 11)) { + return 0; + } + int words = mnemonic_bits_len / 11; + + uint8_t checksum = bits[words * 4 / 3]; + sha256_Raw(bits, words * 4 / 3, bits); + if(words == 12) { + return (bits[0] & 0xF0) == (checksum & 0xF0); // compare first 4 bits + } else if(words == 18) { + return (bits[0] & 0xFC) == (checksum & 0xFC); // compare first 6 bits + } else if(words == 24) { + return bits[0] == checksum; // compare 8 bits + } + return 0; +} + +// passphrase must be at most 256 characters otherwise it would be truncated +void mnemonic_to_seed( + const char* mnemonic, + const char* passphrase, + uint8_t seed[512 / 8], + void (*progress_callback)(uint32_t current, uint32_t total)) { + int mnemoniclen = strlen(mnemonic); + int passphraselen = strlen(passphrase); + if(passphraselen > 256) passphraselen = 256; +#if USE_BIP39_CACHE + // check cache + if(mnemoniclen < 256 && passphraselen < 64) { + for(int i = 0; i < BIP39_CACHE_SIZE; i++) { + if(!bip39_cache[i].set) continue; + if(strcmp(bip39_cache[i].mnemonic, mnemonic) != 0) continue; + if(strcmp(bip39_cache[i].passphrase, passphrase) != 0) continue; + // found the correct entry + memcpy(seed, bip39_cache[i].seed, 512 / 8); + return; + } + } +#endif + uint8_t salt[8 + 256] = {0}; + memcpy(salt, "mnemonic", 8); + memcpy(salt + 8, passphrase, passphraselen); + static CONFIDENTIAL PBKDF2_HMAC_SHA512_CTX pctx; + pbkdf2_hmac_sha512_Init( + &pctx, (const uint8_t*)mnemonic, mnemoniclen, salt, passphraselen + 8, 1); + if(progress_callback) { + progress_callback(0, BIP39_PBKDF2_ROUNDS); + } + for(int i = 0; i < 16; i++) { + pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 16); + if(progress_callback) { + progress_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 16, BIP39_PBKDF2_ROUNDS); + } + } + pbkdf2_hmac_sha512_Final(&pctx, seed); + memzero(salt, sizeof(salt)); +#if USE_BIP39_CACHE + // store to cache + if(mnemoniclen < 256 && passphraselen < 64) { + bip39_cache[bip39_cache_index].set = true; + strcpy(bip39_cache[bip39_cache_index].mnemonic, mnemonic); + strcpy(bip39_cache[bip39_cache_index].passphrase, passphrase); + memcpy(bip39_cache[bip39_cache_index].seed, seed, 512 / 8); + bip39_cache_index = (bip39_cache_index + 1) % BIP39_CACHE_SIZE; + } +#endif +} + +// binary search for finding the word in the wordlist +int mnemonic_find_word(const char* word) { + int lo = 0, hi = BIP39_WORD_COUNT - 1; + while(lo <= hi) { + int mid = lo + (hi - lo) / 2; + int cmp = strcmp(word, BIP39_WORDLIST_ENGLISH[mid]); + if(cmp == 0) { + return mid; + } + if(cmp > 0) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return -1; +} + +const char* mnemonic_complete_word(const char* prefix, int len) { + // we need to perform linear search, + // because we want to return the first match + for(int i = 0; i < BIP39_WORD_COUNT; i++) { + if(strncmp(BIP39_WORDLIST_ENGLISH[i], prefix, len) == 0) { + return BIP39_WORDLIST_ENGLISH[i]; + } + } + return NULL; +} + +const char* mnemonic_get_word(int index) { + if(index >= 0 && index < BIP39_WORD_COUNT) { + return BIP39_WORDLIST_ENGLISH[index]; + } else { + return NULL; + } +} + +uint32_t mnemonic_word_completion_mask(const char* prefix, int len) { + if(len <= 0) { + return 0x3ffffff; // all letters (bits 1-26 set) + } + uint32_t res = 0; + for(int i = 0; i < BIP39_WORD_COUNT; i++) { + const char* word = BIP39_WORDLIST_ENGLISH[i]; + if(strncmp(word, prefix, len) == 0 && word[len] >= 'a' && word[len] <= 'z') { + res |= 1 << (word[len] - 'a'); + } + } + return res; +} diff --git a/applications/external/flipbip/lib/crypto/bip39.h b/applications/external/flipbip/lib/crypto/bip39.h new file mode 100644 index 000000000..5e29c8336 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip39.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __BIP39_H__ +#define __BIP39_H__ + +#include +#include + +#include "options.h" + +#define BIP39_WORD_COUNT 2048 +#define BIP39_PBKDF2_ROUNDS 2048 + +#if USE_BIP39_CACHE +void bip39_cache_clear(void); +#endif + +extern const char* const BIP39_WORDLIST_ENGLISH[BIP39_WORD_COUNT]; + +const char* mnemonic_generate(int strength); // strength in bits +const char* mnemonic_from_data(const uint8_t* data, int len); +void mnemonic_clear(void); + +int mnemonic_check(const char* mnemonic); + +int mnemonic_to_bits(const char* mnemonic, uint8_t* bits); + +// passphrase must be at most 256 characters otherwise it would be truncated +void mnemonic_to_seed( + const char* mnemonic, + const char* passphrase, + uint8_t seed[512 / 8], + void (*progress_callback)(uint32_t current, uint32_t total)); + +int mnemonic_find_word(const char* word); +const char* mnemonic_complete_word(const char* prefix, int len); +const char* mnemonic_get_word(int index); +uint32_t mnemonic_word_completion_mask(const char* prefix, int len); + +#endif diff --git a/applications/external/flipbip/lib/crypto/bip39_english.c b/applications/external/flipbip/lib/crypto/bip39_english.c new file mode 100644 index 000000000..4b92b4c7f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/bip39_english.c @@ -0,0 +1,283 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "bip39.h" + +const char* const BIP39_WORDLIST_ENGLISH[BIP39_WORD_COUNT] = { + "abandon", "ability", "able", "about", "above", "absent", "absorb", "abstract", + "absurd", "abuse", "access", "accident", "account", "accuse", "achieve", "acid", + "acoustic", "acquire", "across", "act", "action", "actor", "actress", "actual", + "adapt", "add", "addict", "address", "adjust", "admit", "adult", "advance", + "advice", "aerobic", "affair", "afford", "afraid", "again", "age", "agent", + "agree", "ahead", "aim", "air", "airport", "aisle", "alarm", "album", + "alcohol", "alert", "alien", "all", "alley", "allow", "almost", "alone", + "alpha", "already", "also", "alter", "always", "amateur", "amazing", "among", + "amount", "amused", "analyst", "anchor", "ancient", "anger", "angle", "angry", + "animal", "ankle", "announce", "annual", "another", "answer", "antenna", "antique", + "anxiety", "any", "apart", "apology", "appear", "apple", "approve", "april", + "arch", "arctic", "area", "arena", "argue", "arm", "armed", "armor", + "army", "around", "arrange", "arrest", "arrive", "arrow", "art", "artefact", + "artist", "artwork", "ask", "aspect", "assault", "asset", "assist", "assume", + "asthma", "athlete", "atom", "attack", "attend", "attitude", "attract", "auction", + "audit", "august", "aunt", "author", "auto", "autumn", "average", "avocado", + "avoid", "awake", "aware", "away", "awesome", "awful", "awkward", "axis", + "baby", "bachelor", "bacon", "badge", "bag", "balance", "balcony", "ball", + "bamboo", "banana", "banner", "bar", "barely", "bargain", "barrel", "base", + "basic", "basket", "battle", "beach", "bean", "beauty", "because", "become", + "beef", "before", "begin", "behave", "behind", "believe", "below", "belt", + "bench", "benefit", "best", "betray", "better", "between", "beyond", "bicycle", + "bid", "bike", "bind", "biology", "bird", "birth", "bitter", "black", + "blade", "blame", "blanket", "blast", "bleak", "bless", "blind", "blood", + "blossom", "blouse", "blue", "blur", "blush", "board", "boat", "body", + "boil", "bomb", "bone", "bonus", "book", "boost", "border", "boring", + "borrow", "boss", "bottom", "bounce", "box", "boy", "bracket", "brain", + "brand", "brass", "brave", "bread", "breeze", "brick", "bridge", "brief", + "bright", "bring", "brisk", "broccoli", "broken", "bronze", "broom", "brother", + "brown", "brush", "bubble", "buddy", "budget", "buffalo", "build", "bulb", + "bulk", "bullet", "bundle", "bunker", "burden", "burger", "burst", "bus", + "business", "busy", "butter", "buyer", "buzz", "cabbage", "cabin", "cable", + "cactus", "cage", "cake", "call", "calm", "camera", "camp", "can", + "canal", "cancel", "candy", "cannon", "canoe", "canvas", "canyon", "capable", + "capital", "captain", "car", "carbon", "card", "cargo", "carpet", "carry", + "cart", "case", "cash", "casino", "castle", "casual", "cat", "catalog", + "catch", "category", "cattle", "caught", "cause", "caution", "cave", "ceiling", + "celery", "cement", "census", "century", "cereal", "certain", "chair", "chalk", + "champion", "change", "chaos", "chapter", "charge", "chase", "chat", "cheap", + "check", "cheese", "chef", "cherry", "chest", "chicken", "chief", "child", + "chimney", "choice", "choose", "chronic", "chuckle", "chunk", "churn", "cigar", + "cinnamon", "circle", "citizen", "city", "civil", "claim", "clap", "clarify", + "claw", "clay", "clean", "clerk", "clever", "click", "client", "cliff", + "climb", "clinic", "clip", "clock", "clog", "close", "cloth", "cloud", + "clown", "club", "clump", "cluster", "clutch", "coach", "coast", "coconut", + "code", "coffee", "coil", "coin", "collect", "color", "column", "combine", + "come", "comfort", "comic", "common", "company", "concert", "conduct", "confirm", + "congress", "connect", "consider", "control", "convince", "cook", "cool", "copper", + "copy", "coral", "core", "corn", "correct", "cost", "cotton", "couch", + "country", "couple", "course", "cousin", "cover", "coyote", "crack", "cradle", + "craft", "cram", "crane", "crash", "crater", "crawl", "crazy", "cream", + "credit", "creek", "crew", "cricket", "crime", "crisp", "critic", "crop", + "cross", "crouch", "crowd", "crucial", "cruel", "cruise", "crumble", "crunch", + "crush", "cry", "crystal", "cube", "culture", "cup", "cupboard", "curious", + "current", "curtain", "curve", "cushion", "custom", "cute", "cycle", "dad", + "damage", "damp", "dance", "danger", "daring", "dash", "daughter", "dawn", + "day", "deal", "debate", "debris", "decade", "december", "decide", "decline", + "decorate", "decrease", "deer", "defense", "define", "defy", "degree", "delay", + "deliver", "demand", "demise", "denial", "dentist", "deny", "depart", "depend", + "deposit", "depth", "deputy", "derive", "describe", "desert", "design", "desk", + "despair", "destroy", "detail", "detect", "develop", "device", "devote", "diagram", + "dial", "diamond", "diary", "dice", "diesel", "diet", "differ", "digital", + "dignity", "dilemma", "dinner", "dinosaur", "direct", "dirt", "disagree", "discover", + "disease", "dish", "dismiss", "disorder", "display", "distance", "divert", "divide", + "divorce", "dizzy", "doctor", "document", "dog", "doll", "dolphin", "domain", + "donate", "donkey", "donor", "door", "dose", "double", "dove", "draft", + "dragon", "drama", "drastic", "draw", "dream", "dress", "drift", "drill", + "drink", "drip", "drive", "drop", "drum", "dry", "duck", "dumb", + "dune", "during", "dust", "dutch", "duty", "dwarf", "dynamic", "eager", + "eagle", "early", "earn", "earth", "easily", "east", "easy", "echo", + "ecology", "economy", "edge", "edit", "educate", "effort", "egg", "eight", + "either", "elbow", "elder", "electric", "elegant", "element", "elephant", "elevator", + "elite", "else", "embark", "embody", "embrace", "emerge", "emotion", "employ", + "empower", "empty", "enable", "enact", "end", "endless", "endorse", "enemy", + "energy", "enforce", "engage", "engine", "enhance", "enjoy", "enlist", "enough", + "enrich", "enroll", "ensure", "enter", "entire", "entry", "envelope", "episode", + "equal", "equip", "era", "erase", "erode", "erosion", "error", "erupt", + "escape", "essay", "essence", "estate", "eternal", "ethics", "evidence", "evil", + "evoke", "evolve", "exact", "example", "excess", "exchange", "excite", "exclude", + "excuse", "execute", "exercise", "exhaust", "exhibit", "exile", "exist", "exit", + "exotic", "expand", "expect", "expire", "explain", "expose", "express", "extend", + "extra", "eye", "eyebrow", "fabric", "face", "faculty", "fade", "faint", + "faith", "fall", "false", "fame", "family", "famous", "fan", "fancy", + "fantasy", "farm", "fashion", "fat", "fatal", "father", "fatigue", "fault", + "favorite", "feature", "february", "federal", "fee", "feed", "feel", "female", + "fence", "festival", "fetch", "fever", "few", "fiber", "fiction", "field", + "figure", "file", "film", "filter", "final", "find", "fine", "finger", + "finish", "fire", "firm", "first", "fiscal", "fish", "fit", "fitness", + "fix", "flag", "flame", "flash", "flat", "flavor", "flee", "flight", + "flip", "float", "flock", "floor", "flower", "fluid", "flush", "fly", + "foam", "focus", "fog", "foil", "fold", "follow", "food", "foot", + "force", "forest", "forget", "fork", "fortune", "forum", "forward", "fossil", + "foster", "found", "fox", "fragile", "frame", "frequent", "fresh", "friend", + "fringe", "frog", "front", "frost", "frown", "frozen", "fruit", "fuel", + "fun", "funny", "furnace", "fury", "future", "gadget", "gain", "galaxy", + "gallery", "game", "gap", "garage", "garbage", "garden", "garlic", "garment", + "gas", "gasp", "gate", "gather", "gauge", "gaze", "general", "genius", + "genre", "gentle", "genuine", "gesture", "ghost", "giant", "gift", "giggle", + "ginger", "giraffe", "girl", "give", "glad", "glance", "glare", "glass", + "glide", "glimpse", "globe", "gloom", "glory", "glove", "glow", "glue", + "goat", "goddess", "gold", "good", "goose", "gorilla", "gospel", "gossip", + "govern", "gown", "grab", "grace", "grain", "grant", "grape", "grass", + "gravity", "great", "green", "grid", "grief", "grit", "grocery", "group", + "grow", "grunt", "guard", "guess", "guide", "guilt", "guitar", "gun", + "gym", "habit", "hair", "half", "hammer", "hamster", "hand", "happy", + "harbor", "hard", "harsh", "harvest", "hat", "have", "hawk", "hazard", + "head", "health", "heart", "heavy", "hedgehog", "height", "hello", "helmet", + "help", "hen", "hero", "hidden", "high", "hill", "hint", "hip", + "hire", "history", "hobby", "hockey", "hold", "hole", "holiday", "hollow", + "home", "honey", "hood", "hope", "horn", "horror", "horse", "hospital", + "host", "hotel", "hour", "hover", "hub", "huge", "human", "humble", + "humor", "hundred", "hungry", "hunt", "hurdle", "hurry", "hurt", "husband", + "hybrid", "ice", "icon", "idea", "identify", "idle", "ignore", "ill", + "illegal", "illness", "image", "imitate", "immense", "immune", "impact", "impose", + "improve", "impulse", "inch", "include", "income", "increase", "index", "indicate", + "indoor", "industry", "infant", "inflict", "inform", "inhale", "inherit", "initial", + "inject", "injury", "inmate", "inner", "innocent", "input", "inquiry", "insane", + "insect", "inside", "inspire", "install", "intact", "interest", "into", "invest", + "invite", "involve", "iron", "island", "isolate", "issue", "item", "ivory", + "jacket", "jaguar", "jar", "jazz", "jealous", "jeans", "jelly", "jewel", + "job", "join", "joke", "journey", "joy", "judge", "juice", "jump", + "jungle", "junior", "junk", "just", "kangaroo", "keen", "keep", "ketchup", + "key", "kick", "kid", "kidney", "kind", "kingdom", "kiss", "kit", + "kitchen", "kite", "kitten", "kiwi", "knee", "knife", "knock", "know", + "lab", "label", "labor", "ladder", "lady", "lake", "lamp", "language", + "laptop", "large", "later", "latin", "laugh", "laundry", "lava", "law", + "lawn", "lawsuit", "layer", "lazy", "leader", "leaf", "learn", "leave", + "lecture", "left", "leg", "legal", "legend", "leisure", "lemon", "lend", + "length", "lens", "leopard", "lesson", "letter", "level", "liar", "liberty", + "library", "license", "life", "lift", "light", "like", "limb", "limit", + "link", "lion", "liquid", "list", "little", "live", "lizard", "load", + "loan", "lobster", "local", "lock", "logic", "lonely", "long", "loop", + "lottery", "loud", "lounge", "love", "loyal", "lucky", "luggage", "lumber", + "lunar", "lunch", "luxury", "lyrics", "machine", "mad", "magic", "magnet", + "maid", "mail", "main", "major", "make", "mammal", "man", "manage", + "mandate", "mango", "mansion", "manual", "maple", "marble", "march", "margin", + "marine", "market", "marriage", "mask", "mass", "master", "match", "material", + "math", "matrix", "matter", "maximum", "maze", "meadow", "mean", "measure", + "meat", "mechanic", "medal", "media", "melody", "melt", "member", "memory", + "mention", "menu", "mercy", "merge", "merit", "merry", "mesh", "message", + "metal", "method", "middle", "midnight", "milk", "million", "mimic", "mind", + "minimum", "minor", "minute", "miracle", "mirror", "misery", "miss", "mistake", + "mix", "mixed", "mixture", "mobile", "model", "modify", "mom", "moment", + "monitor", "monkey", "monster", "month", "moon", "moral", "more", "morning", + "mosquito", "mother", "motion", "motor", "mountain", "mouse", "move", "movie", + "much", "muffin", "mule", "multiply", "muscle", "museum", "mushroom", "music", + "must", "mutual", "myself", "mystery", "myth", "naive", "name", "napkin", + "narrow", "nasty", "nation", "nature", "near", "neck", "need", "negative", + "neglect", "neither", "nephew", "nerve", "nest", "net", "network", "neutral", + "never", "news", "next", "nice", "night", "noble", "noise", "nominee", + "noodle", "normal", "north", "nose", "notable", "note", "nothing", "notice", + "novel", "now", "nuclear", "number", "nurse", "nut", "oak", "obey", + "object", "oblige", "obscure", "observe", "obtain", "obvious", "occur", "ocean", + "october", "odor", "off", "offer", "office", "often", "oil", "okay", + "old", "olive", "olympic", "omit", "once", "one", "onion", "online", + "only", "open", "opera", "opinion", "oppose", "option", "orange", "orbit", + "orchard", "order", "ordinary", "organ", "orient", "original", "orphan", "ostrich", + "other", "outdoor", "outer", "output", "outside", "oval", "oven", "over", + "own", "owner", "oxygen", "oyster", "ozone", "pact", "paddle", "page", + "pair", "palace", "palm", "panda", "panel", "panic", "panther", "paper", + "parade", "parent", "park", "parrot", "party", "pass", "patch", "path", + "patient", "patrol", "pattern", "pause", "pave", "payment", "peace", "peanut", + "pear", "peasant", "pelican", "pen", "penalty", "pencil", "people", "pepper", + "perfect", "permit", "person", "pet", "phone", "photo", "phrase", "physical", + "piano", "picnic", "picture", "piece", "pig", "pigeon", "pill", "pilot", + "pink", "pioneer", "pipe", "pistol", "pitch", "pizza", "place", "planet", + "plastic", "plate", "play", "please", "pledge", "pluck", "plug", "plunge", + "poem", "poet", "point", "polar", "pole", "police", "pond", "pony", + "pool", "popular", "portion", "position", "possible", "post", "potato", "pottery", + "poverty", "powder", "power", "practice", "praise", "predict", "prefer", "prepare", + "present", "pretty", "prevent", "price", "pride", "primary", "print", "priority", + "prison", "private", "prize", "problem", "process", "produce", "profit", "program", + "project", "promote", "proof", "property", "prosper", "protect", "proud", "provide", + "public", "pudding", "pull", "pulp", "pulse", "pumpkin", "punch", "pupil", + "puppy", "purchase", "purity", "purpose", "purse", "push", "put", "puzzle", + "pyramid", "quality", "quantum", "quarter", "question", "quick", "quit", "quiz", + "quote", "rabbit", "raccoon", "race", "rack", "radar", "radio", "rail", + "rain", "raise", "rally", "ramp", "ranch", "random", "range", "rapid", + "rare", "rate", "rather", "raven", "raw", "razor", "ready", "real", + "reason", "rebel", "rebuild", "recall", "receive", "recipe", "record", "recycle", + "reduce", "reflect", "reform", "refuse", "region", "regret", "regular", "reject", + "relax", "release", "relief", "rely", "remain", "remember", "remind", "remove", + "render", "renew", "rent", "reopen", "repair", "repeat", "replace", "report", + "require", "rescue", "resemble", "resist", "resource", "response", "result", "retire", + "retreat", "return", "reunion", "reveal", "review", "reward", "rhythm", "rib", + "ribbon", "rice", "rich", "ride", "ridge", "rifle", "right", "rigid", + "ring", "riot", "ripple", "risk", "ritual", "rival", "river", "road", + "roast", "robot", "robust", "rocket", "romance", "roof", "rookie", "room", + "rose", "rotate", "rough", "round", "route", "royal", "rubber", "rude", + "rug", "rule", "run", "runway", "rural", "sad", "saddle", "sadness", + "safe", "sail", "salad", "salmon", "salon", "salt", "salute", "same", + "sample", "sand", "satisfy", "satoshi", "sauce", "sausage", "save", "say", + "scale", "scan", "scare", "scatter", "scene", "scheme", "school", "science", + "scissors", "scorpion", "scout", "scrap", "screen", "script", "scrub", "sea", + "search", "season", "seat", "second", "secret", "section", "security", "seed", + "seek", "segment", "select", "sell", "seminar", "senior", "sense", "sentence", + "series", "service", "session", "settle", "setup", "seven", "shadow", "shaft", + "shallow", "share", "shed", "shell", "sheriff", "shield", "shift", "shine", + "ship", "shiver", "shock", "shoe", "shoot", "shop", "short", "shoulder", + "shove", "shrimp", "shrug", "shuffle", "shy", "sibling", "sick", "side", + "siege", "sight", "sign", "silent", "silk", "silly", "silver", "similar", + "simple", "since", "sing", "siren", "sister", "situate", "six", "size", + "skate", "sketch", "ski", "skill", "skin", "skirt", "skull", "slab", + "slam", "sleep", "slender", "slice", "slide", "slight", "slim", "slogan", + "slot", "slow", "slush", "small", "smart", "smile", "smoke", "smooth", + "snack", "snake", "snap", "sniff", "snow", "soap", "soccer", "social", + "sock", "soda", "soft", "solar", "soldier", "solid", "solution", "solve", + "someone", "song", "soon", "sorry", "sort", "soul", "sound", "soup", + "source", "south", "space", "spare", "spatial", "spawn", "speak", "special", + "speed", "spell", "spend", "sphere", "spice", "spider", "spike", "spin", + "spirit", "split", "spoil", "sponsor", "spoon", "sport", "spot", "spray", + "spread", "spring", "spy", "square", "squeeze", "squirrel", "stable", "stadium", + "staff", "stage", "stairs", "stamp", "stand", "start", "state", "stay", + "steak", "steel", "stem", "step", "stereo", "stick", "still", "sting", + "stock", "stomach", "stone", "stool", "story", "stove", "strategy", "street", + "strike", "strong", "struggle", "student", "stuff", "stumble", "style", "subject", + "submit", "subway", "success", "such", "sudden", "suffer", "sugar", "suggest", + "suit", "summer", "sun", "sunny", "sunset", "super", "supply", "supreme", + "sure", "surface", "surge", "surprise", "surround", "survey", "suspect", "sustain", + "swallow", "swamp", "swap", "swarm", "swear", "sweet", "swift", "swim", + "swing", "switch", "sword", "symbol", "symptom", "syrup", "system", "table", + "tackle", "tag", "tail", "talent", "talk", "tank", "tape", "target", + "task", "taste", "tattoo", "taxi", "teach", "team", "tell", "ten", + "tenant", "tennis", "tent", "term", "test", "text", "thank", "that", + "theme", "then", "theory", "there", "they", "thing", "this", "thought", + "three", "thrive", "throw", "thumb", "thunder", "ticket", "tide", "tiger", + "tilt", "timber", "time", "tiny", "tip", "tired", "tissue", "title", + "toast", "tobacco", "today", "toddler", "toe", "together", "toilet", "token", + "tomato", "tomorrow", "tone", "tongue", "tonight", "tool", "tooth", "top", + "topic", "topple", "torch", "tornado", "tortoise", "toss", "total", "tourist", + "toward", "tower", "town", "toy", "track", "trade", "traffic", "tragic", + "train", "transfer", "trap", "trash", "travel", "tray", "treat", "tree", + "trend", "trial", "tribe", "trick", "trigger", "trim", "trip", "trophy", + "trouble", "truck", "true", "truly", "trumpet", "trust", "truth", "try", + "tube", "tuition", "tumble", "tuna", "tunnel", "turkey", "turn", "turtle", + "twelve", "twenty", "twice", "twin", "twist", "two", "type", "typical", + "ugly", "umbrella", "unable", "unaware", "uncle", "uncover", "under", "undo", + "unfair", "unfold", "unhappy", "uniform", "unique", "unit", "universe", "unknown", + "unlock", "until", "unusual", "unveil", "update", "upgrade", "uphold", "upon", + "upper", "upset", "urban", "urge", "usage", "use", "used", "useful", + "useless", "usual", "utility", "vacant", "vacuum", "vague", "valid", "valley", + "valve", "van", "vanish", "vapor", "various", "vast", "vault", "vehicle", + "velvet", "vendor", "venture", "venue", "verb", "verify", "version", "very", + "vessel", "veteran", "viable", "vibrant", "vicious", "victory", "video", "view", + "village", "vintage", "violin", "virtual", "virus", "visa", "visit", "visual", + "vital", "vivid", "vocal", "voice", "void", "volcano", "volume", "vote", + "voyage", "wage", "wagon", "wait", "walk", "wall", "walnut", "want", + "warfare", "warm", "warrior", "wash", "wasp", "waste", "water", "wave", + "way", "wealth", "weapon", "wear", "weasel", "weather", "web", "wedding", + "weekend", "weird", "welcome", "west", "wet", "whale", "what", "wheat", + "wheel", "when", "where", "whip", "whisper", "wide", "width", "wife", + "wild", "will", "win", "window", "wine", "wing", "wink", "winner", + "winter", "wire", "wisdom", "wise", "wish", "witness", "wolf", "woman", + "wonder", "wood", "wool", "word", "work", "world", "worry", "worth", + "wrap", "wreck", "wrestle", "wrist", "write", "wrong", "yard", "year", + "yellow", "you", "young", "youth", "zebra", "zero", "zone", "zoo", +}; diff --git a/applications/external/flipbip/lib/crypto/blake256.c b/applications/external/flipbip/lib/crypto/blake256.c new file mode 100644 index 000000000..dfe6213b6 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake256.c @@ -0,0 +1,222 @@ +/* + BLAKE reference C implementation + + Copyright (c) 2012 Jean-Philippe Aumasson + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along with + this software. If not, see . + */ +#include "blake256.h" + +#include + +#define U8TO32_BIG(p) \ + (((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | ((uint32_t)((p)[2]) << 8) | \ + ((uint32_t)((p)[3]))) + +#define U32TO8_BIG(p, v) \ + (p)[0] = (uint8_t)((v) >> 24); \ + (p)[1] = (uint8_t)((v) >> 16); \ + (p)[2] = (uint8_t)((v) >> 8); \ + (p)[3] = (uint8_t)((v)); + +static const uint8_t sigma[][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}}; + +static const uint32_t u256[16] = { + 0x243f6a88, + 0x85a308d3, + 0x13198a2e, + 0x03707344, + 0xa4093822, + 0x299f31d0, + 0x082efa98, + 0xec4e6c89, + 0x452821e6, + 0x38d01377, + 0xbe5466cf, + 0x34e90c6c, + 0xc0ac29b7, + 0xc97c50dd, + 0x3f84d5b5, + 0xb5470917}; + +static const uint8_t padding[129] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static void blake256_compress(BLAKE256_CTX* S, const uint8_t* block) { + uint32_t v[16] = {0}, m[16] = {0}, i = 0; +#define ROT(x, n) (((x) << (32 - n)) | ((x) >> (n))) +#define G(a, b, c, d, e) \ + v[a] += (m[sigma[i][e]] ^ u256[sigma[i][e + 1]]) + v[b]; \ + v[d] = ROT(v[d] ^ v[a], 16); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c], 12); \ + v[a] += (m[sigma[i][e + 1]] ^ u256[sigma[i][e]]) + v[b]; \ + v[d] = ROT(v[d] ^ v[a], 8); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c], 7); + + for(i = 0; i < 16; ++i) m[i] = U8TO32_BIG(block + i * 4); + + for(i = 0; i < 8; ++i) v[i] = S->h[i]; + + v[8] = S->s[0] ^ u256[0]; + v[9] = S->s[1] ^ u256[1]; + v[10] = S->s[2] ^ u256[2]; + v[11] = S->s[3] ^ u256[3]; + v[12] = u256[4]; + v[13] = u256[5]; + v[14] = u256[6]; + v[15] = u256[7]; + + /* don't xor t when the block is only padding */ + if(!S->nullt) { + v[12] ^= S->t[0]; + v[13] ^= S->t[0]; + v[14] ^= S->t[1]; + v[15] ^= S->t[1]; + } + + for(i = 0; i < 14; ++i) { + /* column step */ + G(0, 4, 8, 12, 0); + G(1, 5, 9, 13, 2); + G(2, 6, 10, 14, 4); + G(3, 7, 11, 15, 6); + /* diagonal step */ + G(0, 5, 10, 15, 8); + G(1, 6, 11, 12, 10); + G(2, 7, 8, 13, 12); + G(3, 4, 9, 14, 14); + } + + for(i = 0; i < 16; ++i) S->h[i % 8] ^= v[i]; + + for(i = 0; i < 8; ++i) S->h[i] ^= S->s[i % 4]; +} + +void blake256_Init(BLAKE256_CTX* S) { + S->h[0] = 0x6a09e667; + S->h[1] = 0xbb67ae85; + S->h[2] = 0x3c6ef372; + S->h[3] = 0xa54ff53a; + S->h[4] = 0x510e527f; + S->h[5] = 0x9b05688c; + S->h[6] = 0x1f83d9ab; + S->h[7] = 0x5be0cd19; + S->t[0] = S->t[1] = S->buflen = S->nullt = 0; + S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0; +} + +void blake256_Update(BLAKE256_CTX* S, const uint8_t* in, size_t inlen) { + size_t left = S->buflen; + size_t fill = 64 - left; + + /* data left and data received fill a block */ + if(left && (inlen >= fill)) { + memcpy((void*)(S->buf + left), (void*)in, fill); + S->t[0] += 512; + + if(S->t[0] == 0) S->t[1]++; + + blake256_compress(S, S->buf); + in += fill; + inlen -= fill; + left = 0; + } + + /* compress blocks of data received */ + while(inlen >= 64) { + S->t[0] += 512; + + if(S->t[0] == 0) S->t[1]++; + + blake256_compress(S, in); + in += 64; + inlen -= 64; + } + + /* store any data left */ + if(inlen > 0) { + memcpy((void*)(S->buf + left), (void*)in, (size_t)inlen); + } + S->buflen = left + inlen; +} + +void blake256_Final(BLAKE256_CTX* S, uint8_t* out) { + uint8_t msglen[8] = {0}, zo = 0x01, oo = 0x81; + uint32_t lo = S->t[0] + (S->buflen << 3), hi = S->t[1]; + + /* support for hashing more than 2^32 bits */ + if(lo < (S->buflen << 3)) hi++; + + U32TO8_BIG(msglen + 0, hi); + U32TO8_BIG(msglen + 4, lo); + + if(S->buflen == 55) /* one padding byte */ + { + S->t[0] -= 8; + blake256_Update(S, &oo, 1); + } else { + if(S->buflen < 55) /* enough space to fill the block */ + { + if(!S->buflen) S->nullt = 1; + + S->t[0] -= 440 - (S->buflen << 3); + blake256_Update(S, padding, 55 - S->buflen); + } else /* need 2 compressions */ + { + S->t[0] -= 512 - (S->buflen << 3); + blake256_Update(S, padding, 64 - S->buflen); + S->t[0] -= 440; + blake256_Update(S, padding + 1, 55); + S->nullt = 1; + } + + blake256_Update(S, &zo, 1); + S->t[0] -= 8; + } + + S->t[0] -= 64; + blake256_Update(S, msglen, 8); + U32TO8_BIG(out + 0, S->h[0]); + U32TO8_BIG(out + 4, S->h[1]); + U32TO8_BIG(out + 8, S->h[2]); + U32TO8_BIG(out + 12, S->h[3]); + U32TO8_BIG(out + 16, S->h[4]); + U32TO8_BIG(out + 20, S->h[5]); + U32TO8_BIG(out + 24, S->h[6]); + U32TO8_BIG(out + 28, S->h[7]); +} + +void blake256(const uint8_t* in, size_t inlen, uint8_t* out) { + BLAKE256_CTX S = {0}; + blake256_Init(&S); + blake256_Update(&S, in, inlen); + blake256_Final(&S, out); +} diff --git a/applications/external/flipbip/lib/crypto/blake256.h b/applications/external/flipbip/lib/crypto/blake256.h new file mode 100644 index 000000000..c02f7c004 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake256.h @@ -0,0 +1,53 @@ +// Copyright (c) 2014-2017, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#ifndef __BLAKE256_H__ +#define __BLAKE256_H__ + +#include +#include + +#define BLAKE256_DIGEST_LENGTH 32 +#define BLAKE256_BLOCK_LENGTH 64 + +typedef struct { + uint32_t h[8], s[4], t[2]; + size_t buflen; + uint8_t nullt; + uint8_t buf[64]; +} BLAKE256_CTX; + +void blake256_Init(BLAKE256_CTX*); +void blake256_Update(BLAKE256_CTX*, const uint8_t*, size_t); +void blake256_Final(BLAKE256_CTX*, uint8_t*); + +void blake256(const uint8_t*, size_t, uint8_t*); + +#endif /* __BLAKE256_H__ */ diff --git a/applications/external/flipbip/lib/crypto/blake2_common.h b/applications/external/flipbip/lib/crypto/blake2_common.h new file mode 100644 index 000000000..369c7cc0f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2_common.h @@ -0,0 +1,46 @@ + +#include "byte_order.h" + +static inline uint32_t load32(const void* src) { + uint32_t w; + memcpy(&w, src, sizeof w); +#if BYTE_ORDER == BIG_ENDIAN + REVERSE32(w, w); +#endif + return w; +} + +static inline uint64_t load64(const void* src) { + uint64_t w; + memcpy(&w, src, sizeof w); +#if BYTE_ORDER == BIG_ENDIAN + REVERSE64(w, w); +#endif + return w; +} + +static inline void store16(void* dst, uint16_t w) { + memcpy(dst, &w, sizeof w); +} + +static inline void store32(void* dst, uint32_t w) { +#if BYTE_ORDER == BIG_ENDIAN + REVERSE32(w, w); +#endif + memcpy(dst, &w, sizeof w); +} + +static inline void store64(void* dst, uint64_t w) { +#if BYTE_ORDER == BIG_ENDIAN + REVERSE64(w, w); +#endif + memcpy(dst, &w, sizeof w); +} + +static inline uint32_t rotr32(const uint32_t w, const unsigned c) { + return (w >> c) | (w << (32 - c)); +} + +static inline uint64_t rotr64(const uint64_t w, const unsigned c) { + return (w >> c) | (w << (64 - c)); +} diff --git a/applications/external/flipbip/lib/crypto/blake2b.c b/applications/external/flipbip/lib/crypto/blake2b.c new file mode 100644 index 000000000..1f92b66f2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2b.c @@ -0,0 +1,313 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include + +#include "blake2b.h" +#include "blake2_common.h" +#include "memzero.h" + +typedef struct blake2b_param__ { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +} __attribute__((packed)) blake2b_param; + +static const uint64_t blake2b_IV[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL}; + +static const uint8_t blake2b_sigma[12][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}}; + +static void blake2b_set_lastnode(blake2b_state* S) { + S->f[1] = (uint64_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2b_is_lastblock(const blake2b_state* S) { + return S->f[0] != 0; +} + +static void blake2b_set_lastblock(blake2b_state* S) { + if(S->last_node) blake2b_set_lastnode(S); + + S->f[0] = (uint64_t)-1; +} + +static void blake2b_increment_counter(blake2b_state* S, const uint64_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static void blake2b_init0(blake2b_state* S) { + size_t i = 0; + memzero(S, sizeof(blake2b_state)); + + for(i = 0; i < 8; ++i) S->h[i] = blake2b_IV[i]; +} + +/* init xors IV with input parameter block */ +int blake2b_init_param(blake2b_state* S, const blake2b_param* P) { + const uint8_t* p = (const uint8_t*)(P); + size_t i = 0; + + blake2b_init0(S); + + /* IV XOR ParamBlock */ + for(i = 0; i < 8; ++i) S->h[i] ^= load64(p + sizeof(S->h[i]) * i); + + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2b initialization */ +int blake2b_Init(blake2b_state* S, size_t outlen) { + blake2b_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2B_OUTBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memzero(P->reserved, sizeof(P->reserved)); + memzero(P->salt, sizeof(P->salt)); + memzero(P->personal, sizeof(P->personal)); + return blake2b_init_param(S, P); +} + +int blake2b_InitPersonal( + blake2b_state* S, + size_t outlen, + const void* personal, + size_t personal_len) { + blake2b_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2B_OUTBYTES)) return -1; + if((!personal) || (personal_len != BLAKE2B_PERSONALBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memzero(P->reserved, sizeof(P->reserved)); + memzero(P->salt, sizeof(P->salt)); + memcpy(P->personal, personal, BLAKE2B_PERSONALBYTES); + return blake2b_init_param(S, P); +} + +int blake2b_InitKey(blake2b_state* S, size_t outlen, const void* key, size_t keylen) { + blake2b_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2B_OUTBYTES)) return -1; + + if(!key || !keylen || keylen > BLAKE2B_KEYBYTES) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store32(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + memzero(P->reserved, sizeof(P->reserved)); + memzero(P->salt, sizeof(P->salt)); + memzero(P->personal, sizeof(P->personal)); + + if(blake2b_init_param(S, P) < 0) return -1; + + { + uint8_t block[BLAKE2B_BLOCKBYTES] = {0}; + memzero(block, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_Update(S, block, BLAKE2B_BLOCKBYTES); + memzero(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */ + } + return 0; +} + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while(0) + +static void blake2b_compress(blake2b_state* S, const uint8_t block[BLAKE2B_BLOCKBYTES]) { + uint64_t m[16] = {0}; + uint64_t v[16] = {0}; + size_t i = 0; + + for(i = 0; i < 16; ++i) { + m[i] = load64(block + i * sizeof(m[i])); + } + + for(i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); + ROUND(10); + ROUND(11); + + for(i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2b_Update(blake2b_state* S, const void* pin, size_t inlen) { + const unsigned char* in = (const unsigned char*)pin; + if(inlen > 0) { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if(inlen > fill) { + S->buflen = 0; + memcpy(S->buf + left, in, fill); /* Fill buffer */ + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, S->buf); /* Compress */ + in += fill; + inlen -= fill; + while(inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress(S, in); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy(S->buf + S->buflen, in, inlen); + S->buflen += inlen; + } + return 0; +} + +int blake2b_Final(blake2b_state* S, void* out, size_t outlen) { + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + size_t i = 0; + + if(out == NULL || outlen < S->outlen) return -1; + + if(blake2b_is_lastblock(S)) return -1; + + blake2b_increment_counter(S, S->buflen); + blake2b_set_lastblock(S); + memzero(S->buf + S->buflen, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ + blake2b_compress(S, S->buf); + + for(i = 0; i < 8; ++i) /* Output full hash to temp buffer */ + store64(buffer + sizeof(S->h[i]) * i, S->h[i]); + + memcpy(out, buffer, S->outlen); + memzero(buffer, sizeof(buffer)); + return 0; +} + +int blake2b(const uint8_t* msg, uint32_t msg_len, void* out, size_t outlen) { + BLAKE2B_CTX ctx; + if(0 != blake2b_Init(&ctx, outlen)) return -1; + if(0 != blake2b_Update(&ctx, msg, msg_len)) return -1; + if(0 != blake2b_Final(&ctx, out, outlen)) return -1; + return 0; +} + +int blake2b_Key( + const uint8_t* msg, + uint32_t msg_len, + const void* key, + size_t keylen, + void* out, + size_t outlen) { + BLAKE2B_CTX ctx; + if(0 != blake2b_InitKey(&ctx, outlen, key, keylen)) return -1; + if(0 != blake2b_Update(&ctx, msg, msg_len)) return -1; + if(0 != blake2b_Final(&ctx, out, outlen)) return -1; + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/blake2b.h b/applications/external/flipbip/lib/crypto/blake2b.h new file mode 100644 index 000000000..5a47a912c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2b.h @@ -0,0 +1,49 @@ +#ifndef __BLAKE2B_H__ +#define __BLAKE2B_H__ + +#include +#include + +enum blake2b_constant { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 +}; + +typedef struct __blake2b_state { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; +} blake2b_state; + +#define BLAKE2B_CTX blake2b_state +#define BLAKE2B_BLOCK_LENGTH BLAKE2B_BLOCKBYTES +#define BLAKE2B_DIGEST_LENGTH BLAKE2B_OUTBYTES +#define BLAKE2B_KEY_LENGTH BLAKE2B_KEYBYTES + +int blake2b_Init(blake2b_state* S, size_t outlen); +int blake2b_InitKey(blake2b_state* S, size_t outlen, const void* key, size_t keylen); +int blake2b_InitPersonal( + blake2b_state* S, + size_t outlen, + const void* personal, + size_t personal_len); +int blake2b_Update(blake2b_state* S, const void* pin, size_t inlen); +int blake2b_Final(blake2b_state* S, void* out, size_t outlen); + +int blake2b(const uint8_t* msg, uint32_t msg_len, void* out, size_t outlen); +int blake2b_Key( + const uint8_t* msg, + uint32_t msg_len, + const void* key, + size_t keylen, + void* out, + size_t outlen); + +#endif diff --git a/applications/external/flipbip/lib/crypto/blake2s.c b/applications/external/flipbip/lib/crypto/blake2s.c new file mode 100644 index 000000000..0e75ef7c6 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2s.c @@ -0,0 +1,310 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include + +#include "blake2s.h" +#include "blake2_common.h" +#include "memzero.h" + +typedef struct blake2s_param__ { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint16_t xof_length; /* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ + /* uint8_t reserved[0]; */ + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ +} __attribute__((packed)) blake2s_param; + +static const uint32_t blake2s_IV[8] = { + 0x6A09E667UL, + 0xBB67AE85UL, + 0x3C6EF372UL, + 0xA54FF53AUL, + 0x510E527FUL, + 0x9B05688CUL, + 0x1F83D9ABUL, + 0x5BE0CD19UL}; + +static const uint8_t blake2s_sigma[10][16] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, +}; + +static void blake2s_set_lastnode(blake2s_state* S) { + S->f[1] = (uint32_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2s_is_lastblock(const blake2s_state* S) { + return S->f[0] != 0; +} + +static void blake2s_set_lastblock(blake2s_state* S) { + if(S->last_node) blake2s_set_lastnode(S); + + S->f[0] = (uint32_t)-1; +} + +static void blake2s_increment_counter(blake2s_state* S, const uint32_t inc) { + S->t[0] += inc; + S->t[1] += (S->t[0] < inc); +} + +static void blake2s_init0(blake2s_state* S) { + size_t i = 0; + memzero(S, sizeof(blake2s_state)); + + for(i = 0; i < 8; ++i) S->h[i] = blake2s_IV[i]; +} + +/* init2 xors IV with input parameter block */ +int blake2s_init_param(blake2s_state* S, const blake2s_param* P) { + const unsigned char* p = (const unsigned char*)(P); + size_t i = 0; + + blake2s_init0(S); + + /* IV XOR ParamBlock */ + for(i = 0; i < 8; ++i) S->h[i] ^= load32(&p[i * 4]); + + S->outlen = P->digest_length; + return 0; +} + +/* Sequential blake2s initialization */ +int blake2s_Init(blake2s_state* S, size_t outlen) { + blake2s_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2S_OUTBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store16(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero(P->salt, sizeof(P->salt)); + memzero(P->personal, sizeof(P->personal)); + return blake2s_init_param(S, P); +} + +int blake2s_InitPersonal( + blake2s_state* S, + size_t outlen, + const void* personal, + size_t personal_len) { + blake2s_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2S_OUTBYTES)) return -1; + if((!personal) || (personal_len != BLAKE2S_PERSONALBYTES)) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store16(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero(P->salt, sizeof(P->salt)); + memcpy(P->personal, personal, BLAKE2S_PERSONALBYTES); + return blake2s_init_param(S, P); +} + +int blake2s_InitKey(blake2s_state* S, size_t outlen, const void* key, size_t keylen) { + blake2s_param P[1] = {0}; + + if((!outlen) || (outlen > BLAKE2S_OUTBYTES)) return -1; + + if(!key || !keylen || keylen > BLAKE2S_KEYBYTES) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32(&P->leaf_length, 0); + store32(&P->node_offset, 0); + store16(&P->xof_length, 0); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero(P->salt, sizeof(P->salt)); + memzero(P->personal, sizeof(P->personal)); + + if(blake2s_init_param(S, P) < 0) return -1; + + { + uint8_t block[BLAKE2S_BLOCKBYTES] = {0}; + memzero(block, BLAKE2S_BLOCKBYTES); + memcpy(block, key, keylen); + blake2s_Update(S, block, BLAKE2S_BLOCKBYTES); + memzero(block, BLAKE2S_BLOCKBYTES); /* Burn the key from stack */ + } + return 0; +} + +#define G(r, i, a, b, c, d) \ + do { \ + a = a + b + m[blake2s_sigma[r][2 * i + 0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2 * i + 1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r, 0, v[0], v[4], v[8], v[12]); \ + G(r, 1, v[1], v[5], v[9], v[13]); \ + G(r, 2, v[2], v[6], v[10], v[14]); \ + G(r, 3, v[3], v[7], v[11], v[15]); \ + G(r, 4, v[0], v[5], v[10], v[15]); \ + G(r, 5, v[1], v[6], v[11], v[12]); \ + G(r, 6, v[2], v[7], v[8], v[13]); \ + G(r, 7, v[3], v[4], v[9], v[14]); \ + } while(0) + +static void blake2s_compress(blake2s_state* S, const uint8_t in[BLAKE2S_BLOCKBYTES]) { + uint32_t m[16] = {0}; + uint32_t v[16] = {0}; + size_t i = 0; + + for(i = 0; i < 16; ++i) { + m[i] = load32(in + i * sizeof(m[i])); + } + + for(i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + v[8] = blake2s_IV[0]; + v[9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); + + for(i = 0; i < 8; ++i) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2s_Update(blake2s_state* S, const void* pin, size_t inlen) { + const unsigned char* in = (const unsigned char*)pin; + if(inlen > 0) { + size_t left = S->buflen; + size_t fill = BLAKE2S_BLOCKBYTES - left; + if(inlen > fill) { + S->buflen = 0; + memcpy(S->buf + left, in, fill); /* Fill buffer */ + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress(S, S->buf); /* Compress */ + in += fill; + inlen -= fill; + while(inlen > BLAKE2S_BLOCKBYTES) { + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress(S, in); + in += BLAKE2S_BLOCKBYTES; + inlen -= BLAKE2S_BLOCKBYTES; + } + } + memcpy(S->buf + S->buflen, in, inlen); + S->buflen += inlen; + } + return 0; +} + +int blake2s_Final(blake2s_state* S, void* out, size_t outlen) { + uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; + size_t i = 0; + + if(out == NULL || outlen < S->outlen) return -1; + + if(blake2s_is_lastblock(S)) return -1; + + blake2s_increment_counter(S, (uint32_t)S->buflen); + blake2s_set_lastblock(S); + memzero(S->buf + S->buflen, BLAKE2S_BLOCKBYTES - S->buflen); /* Padding */ + blake2s_compress(S, S->buf); + + for(i = 0; i < 8; ++i) /* Output full hash to temp buffer */ + store32(buffer + sizeof(S->h[i]) * i, S->h[i]); + + memcpy(out, buffer, outlen); + memzero(buffer, sizeof(buffer)); + return 0; +} + +int blake2s(const uint8_t* msg, uint32_t msg_len, void* out, size_t outlen) { + BLAKE2S_CTX ctx; + if(0 != blake2s_Init(&ctx, outlen)) return -1; + if(0 != blake2s_Update(&ctx, msg, msg_len)) return -1; + if(0 != blake2s_Final(&ctx, out, outlen)) return -1; + return 0; +} + +int blake2s_Key( + const uint8_t* msg, + uint32_t msg_len, + const void* key, + size_t keylen, + void* out, + size_t outlen) { + BLAKE2S_CTX ctx; + if(0 != blake2s_InitKey(&ctx, outlen, key, keylen)) return -1; + if(0 != blake2s_Update(&ctx, msg, msg_len)) return -1; + if(0 != blake2s_Final(&ctx, out, outlen)) return -1; + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/blake2s.h b/applications/external/flipbip/lib/crypto/blake2s.h new file mode 100644 index 000000000..452090693 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/blake2s.h @@ -0,0 +1,49 @@ +#ifndef __BLAKE2S_H__ +#define __BLAKE2S_H__ + +#include +#include + +enum blake2s_constant { + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32, + BLAKE2S_KEYBYTES = 32, + BLAKE2S_SALTBYTES = 8, + BLAKE2S_PERSONALBYTES = 8 +}; + +typedef struct __blake2s_state { + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[BLAKE2S_BLOCKBYTES]; + uint32_t buflen; + uint8_t outlen; + uint8_t last_node; +} blake2s_state; + +#define BLAKE2S_CTX blake2s_state +#define BLAKE2S_BLOCK_LENGTH BLAKE2S_BLOCKBYTES +#define BLAKE2S_DIGEST_LENGTH BLAKE2S_OUTBYTES +#define BLAKE2S_KEY_LENGTH BLAKE2S_KEYBYTES + +int blake2s_Init(blake2s_state* S, size_t outlen); +int blake2s_InitKey(blake2s_state* S, size_t outlen, const void* key, size_t keylen); +int blake2s_InitPersonal( + blake2s_state* S, + size_t outlen, + const void* personal, + size_t personal_len); +int blake2s_Update(blake2s_state* S, const void* pin, size_t inlen); +int blake2s_Final(blake2s_state* S, void* out, size_t outlen); + +int blake2s(const uint8_t* msg, uint32_t msg_len, void* out, size_t outlen); +int blake2s_Key( + const uint8_t* msg, + uint32_t msg_len, + const void* key, + size_t keylen, + void* out, + size_t outlen); + +#endif diff --git a/applications/external/flipbip/lib/crypto/byte_order.h b/applications/external/flipbip/lib/crypto/byte_order.h new file mode 100644 index 000000000..8484f4349 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/byte_order.h @@ -0,0 +1,57 @@ +#ifndef __BYTE_ORDER_H__ +#define __BYTE_ORDER_H__ + +// FROM sha2.h: +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivalent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including (which in turn includes + * where the appropriate definitions are actually + * made). + */ + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#endif + +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif + +#define REVERSE32(w, x) \ + { \ + uint32_t tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ + } + +#define REVERSE64(w, x) \ + { \ + uint64_t tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | ((tmp & 0x0000ffff0000ffffULL) << 16); \ + } + +#endif diff --git a/applications/external/flipbip/lib/crypto/cardano.c b/applications/external/flipbip/lib/crypto/cardano.c new file mode 100644 index 000000000..8542799a7 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/cardano.c @@ -0,0 +1,307 @@ +/** + * Copyright (c) 2013-2021 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "bignum.h" +#include "bip32.h" +#include "cardano.h" +#include "curves.h" +#include "hasher.h" +#include "hmac.h" +#include "memzero.h" +#include "options.h" +#include "pbkdf2.h" +#include "sha2.h" + +#if USE_CARDANO + +#define CARDANO_MAX_NODE_DEPTH 1048576 + +const curve_info ed25519_cardano_info = { + .bip32_name = ED25519_CARDANO_NAME, + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +static void scalar_multiply8(const uint8_t* src, int bytes, uint8_t* dst) { + uint8_t prev_acc = 0; + for(int i = 0; i < bytes; i++) { + dst[i] = (src[i] << 3) + (prev_acc & 0x7); + prev_acc = src[i] >> 5; + } + dst[bytes] = src[bytes - 1] >> 5; +} + +static void scalar_add_256bits(const uint8_t* src1, const uint8_t* src2, uint8_t* dst) { + uint16_t r = 0; + for(int i = 0; i < 32; i++) { + r = r + (uint16_t)src1[i] + (uint16_t)src2[i]; + dst[i] = r & 0xff; + r >>= 8; + } +} + +static void cardano_ed25519_tweak_bits(uint8_t private_key[32]) { + private_key[0] &= 0xf8; + private_key[31] &= 0x1f; + private_key[31] |= 0x40; +} + +int hdnode_private_ckd_cardano(HDNode* inout, uint32_t index) { + if(inout->curve != &ed25519_cardano_info) { + return 0; + } + + if(inout->depth >= CARDANO_MAX_NODE_DEPTH) { + return 0; + } + + // checks for hardened/non-hardened derivation, keysize 32 means we are + // dealing with public key and thus non-h, keysize 64 is for private key + int keysize = 32; + if(index & 0x80000000) { + keysize = 64; + } + + static CONFIDENTIAL uint8_t data[1 + 64 + 4]; + static CONFIDENTIAL uint8_t z[32 + 32]; + static CONFIDENTIAL uint8_t priv_key[64]; + static CONFIDENTIAL uint8_t res_key[64]; + + write_le(data + keysize + 1, index); + + memcpy(priv_key, inout->private_key, 32); + memcpy(priv_key + 32, inout->private_key_extension, 32); + + if(keysize == 64) { // private derivation + data[0] = 0; + memcpy(data + 1, inout->private_key, 32); + memcpy(data + 1 + 32, inout->private_key_extension, 32); + } else { // public derivation + if(hdnode_fill_public_key(inout) != 0) { + return 0; + } + data[0] = 2; + memcpy(data + 1, inout->public_key + 1, 32); + } + + static CONFIDENTIAL HMAC_SHA512_CTX ctx; + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, 1 + keysize + 4); + hmac_sha512_Final(&ctx, z); + + static CONFIDENTIAL uint8_t zl8[32]; + memzero(zl8, 32); + + /* get 8 * Zl */ + scalar_multiply8(z, 28, zl8); + /* Kl = 8*Zl + parent(K)l */ + scalar_add_256bits(zl8, priv_key, res_key); + + /* Kr = Zr + parent(K)r */ + scalar_add_256bits(z + 32, priv_key + 32, res_key + 32); + + memcpy(inout->private_key, res_key, 32); + memcpy(inout->private_key_extension, res_key + 32, 32); + + if(keysize == 64) { + data[0] = 1; + } else { + data[0] = 3; + } + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, 1 + keysize + 4); + hmac_sha512_Final(&ctx, z); + + memcpy(inout->chain_code, z + 32, 32); + inout->depth++; + inout->child_num = index; + memzero(inout->public_key, sizeof(inout->public_key)); + + // making sure to wipe our memory + memzero(z, sizeof(z)); + memzero(data, sizeof(data)); + memzero(priv_key, sizeof(priv_key)); + memzero(res_key, sizeof(res_key)); + return 1; +} + +int hdnode_from_secret_cardano(const uint8_t secret[CARDANO_SECRET_LENGTH], HDNode* out) { + memzero(out, sizeof(HDNode)); + out->depth = 0; + out->child_num = 0; + out->curve = &ed25519_cardano_info; + memcpy(out->private_key, secret, 32); + memcpy(out->private_key_extension, secret + 32, 32); + memcpy(out->chain_code, secret + 64, 32); + + cardano_ed25519_tweak_bits(out->private_key); + + out->public_key[0] = 0; + if(hdnode_fill_public_key(out) != 0) { + return 0; + } + + return 1; +} + +// Derives the root Cardano secret from a master secret, aka seed, as defined in +// SLIP-0023. +int secret_from_seed_cardano_slip23( + const uint8_t* seed, + int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]) { + static CONFIDENTIAL uint8_t I[SHA512_DIGEST_LENGTH]; + static CONFIDENTIAL HMAC_SHA512_CTX ctx; + + hmac_sha512_Init(&ctx, (const uint8_t*)ED25519_CARDANO_NAME, strlen(ED25519_CARDANO_NAME)); + hmac_sha512_Update(&ctx, seed, seed_len); + hmac_sha512_Final(&ctx, I); + + sha512_Raw(I, 32, secret_out); + + memcpy(secret_out + SHA512_DIGEST_LENGTH, I + 32, 32); + cardano_ed25519_tweak_bits(secret_out); + + memzero(I, sizeof(I)); + memzero(&ctx, sizeof(ctx)); + return 1; +} + +// Derives the root Cardano secret from a BIP-32 master secret via the Ledger +// derivation: +// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Ledger.md +int secret_from_seed_cardano_ledger( + const uint8_t* seed, + int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]) { + static CONFIDENTIAL uint8_t chain_code[SHA256_DIGEST_LENGTH]; + static CONFIDENTIAL uint8_t root_key[SHA512_DIGEST_LENGTH]; + static CONFIDENTIAL HMAC_SHA256_CTX ctx; + static CONFIDENTIAL HMAC_SHA512_CTX sctx; + + const uint8_t* intermediate_result = seed; + int intermediate_result_len = seed_len; + do { + // STEP 1: derive a master secret like in BIP-32/SLIP-10 + hmac_sha512_Init(&sctx, (const uint8_t*)ED25519_SEED_NAME, strlen(ED25519_SEED_NAME)); + hmac_sha512_Update(&sctx, intermediate_result, intermediate_result_len); + hmac_sha512_Final(&sctx, root_key); + + // STEP 2: check that the resulting key does not have a particular bit set, + // otherwise iterate like in SLIP-10 + intermediate_result = root_key; + intermediate_result_len = sizeof(root_key); + } while(root_key[31] & 0x20); + + // STEP 3: calculate the chain code as a HMAC-SHA256 of "\x01" + seed, + // key is "ed25519 seed" + hmac_sha256_Init(&ctx, (const unsigned char*)ED25519_SEED_NAME, strlen(ED25519_SEED_NAME)); + hmac_sha256_Update(&ctx, (const unsigned char*)"\x01", 1); + hmac_sha256_Update(&ctx, seed, seed_len); + hmac_sha256_Final(&ctx, chain_code); + + // STEP 4: extract information into output + _Static_assert( + SHA512_DIGEST_LENGTH + SHA256_DIGEST_LENGTH == CARDANO_SECRET_LENGTH, + "Invalid configuration of Cardano secret size"); + memcpy(secret_out, root_key, SHA512_DIGEST_LENGTH); + memcpy(secret_out + SHA512_DIGEST_LENGTH, chain_code, SHA256_DIGEST_LENGTH); + + // STEP 5: tweak bits of the private key + cardano_ed25519_tweak_bits(secret_out); + + memzero(&ctx, sizeof(ctx)); + memzero(&sctx, sizeof(sctx)); + memzero(root_key, sizeof(root_key)); + memzero(chain_code, sizeof(chain_code)); + return 1; +} + +#define CARDANO_ICARUS_STEPS 32 +_Static_assert( + CARDANO_ICARUS_PBKDF2_ROUNDS % CARDANO_ICARUS_STEPS == 0, + "CARDANO_ICARUS_STEPS does not divide CARDANO_ICARUS_PBKDF2_ROUNDS"); +#define CARDANO_ICARUS_ROUNDS_PER_STEP (CARDANO_ICARUS_PBKDF2_ROUNDS / CARDANO_ICARUS_STEPS) + +// Derives the root Cardano HDNode from a passphrase and the entropy encoded in +// a BIP-0039 mnemonic using the Icarus derivation scheme, aka V2 derivation +// scheme: +// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Icarus.md +int secret_from_entropy_cardano_icarus( + const uint8_t* pass, + int pass_len, + const uint8_t* entropy, + int entropy_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH], + void (*progress_callback)(uint32_t, uint32_t)) { + static CONFIDENTIAL PBKDF2_HMAC_SHA512_CTX pctx; + static CONFIDENTIAL uint8_t digest[SHA512_DIGEST_LENGTH]; + uint32_t progress = 0; + + // PASS 1: first 64 bytes + pbkdf2_hmac_sha512_Init(&pctx, pass, pass_len, entropy, entropy_len, 1); + if(progress_callback) { + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + for(int i = 0; i < CARDANO_ICARUS_STEPS; i++) { + pbkdf2_hmac_sha512_Update(&pctx, CARDANO_ICARUS_ROUNDS_PER_STEP); + if(progress_callback) { + progress += CARDANO_ICARUS_ROUNDS_PER_STEP; + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + } + pbkdf2_hmac_sha512_Final(&pctx, digest); + + memcpy(secret_out, digest, SHA512_DIGEST_LENGTH); + + // PASS 2: remaining 32 bytes + pbkdf2_hmac_sha512_Init(&pctx, pass, pass_len, entropy, entropy_len, 2); + if(progress_callback) { + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + for(int i = 0; i < CARDANO_ICARUS_STEPS; i++) { + pbkdf2_hmac_sha512_Update(&pctx, CARDANO_ICARUS_ROUNDS_PER_STEP); + if(progress_callback) { + progress += CARDANO_ICARUS_ROUNDS_PER_STEP; + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + } + pbkdf2_hmac_sha512_Final(&pctx, digest); + + memcpy( + secret_out + SHA512_DIGEST_LENGTH, digest, CARDANO_SECRET_LENGTH - SHA512_DIGEST_LENGTH); + + cardano_ed25519_tweak_bits(secret_out); + + memzero(&pctx, sizeof(pctx)); + memzero(digest, sizeof(digest)); + return 1; +} + +#endif // USE_CARDANO diff --git a/applications/external/flipbip/lib/crypto/cardano.h b/applications/external/flipbip/lib/crypto/cardano.h new file mode 100644 index 000000000..761ce978c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/cardano.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2013-2021 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CARDANO_H__ +#define __CARDANO_H__ + +#include +#include +#include "bip32.h" +#include "options.h" + +#if USE_CARDANO + +#define CARDANO_SECRET_LENGTH 96 +#define CARDANO_ICARUS_PBKDF2_ROUNDS 4096 + +extern const curve_info ed25519_cardano_info; + +int hdnode_private_ckd_cardano(HDNode* inout, uint32_t i); + +int secret_from_entropy_cardano_icarus( + const uint8_t* pass, + int pass_len, + const uint8_t* entropy, + int entropy_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH], + void (*progress_callback)(uint32_t current, uint32_t total)); +int secret_from_seed_cardano_ledger( + const uint8_t* seed, + int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]); +int secret_from_seed_cardano_slip23( + const uint8_t* seed, + int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]); + +int hdnode_from_secret_cardano(const uint8_t secret[CARDANO_SECRET_LENGTH], HDNode* out); + +#endif // USE_CARDANO + +#endif // __CARDANO_H__ diff --git a/applications/external/flipbip/lib/crypto/cash_addr.c b/applications/external/flipbip/lib/crypto/cash_addr.c new file mode 100644 index 000000000..31656c888 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/cash_addr.c @@ -0,0 +1,188 @@ +/* Copyright (c) 2017 Jochen Hoenicke + * based on code Copyright (c) 2017 Peter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + +#include "cash_addr.h" + +#define MAX_CASHADDR_SIZE 129 +#define MAX_BASE32_SIZE 104 +#define MAX_DATA_SIZE 65 +#define MAX_HRP_SIZE 20 +#define CHECKSUM_SIZE 8 + +uint64_t cashaddr_polymod_step(uint64_t pre) { + uint8_t b = pre >> 35; + return ((pre & 0x7FFFFFFFFULL) << 5) ^ (-((b >> 0) & 1) & 0x98f2bc8e61ULL) ^ + (-((b >> 1) & 1) & 0x79b76d99e2ULL) ^ (-((b >> 2) & 1) & 0xf33e5fb3c4ULL) ^ + (-((b >> 3) & 1) & 0xae2eabe2a8ULL) ^ (-((b >> 4) & 1) & 0x1e4f43e470ULL); +} + +static const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +static const int8_t charset_rev[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, + -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, + 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, + 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}; + +int cash_encode(char* output, const char* hrp, const uint8_t* data, size_t data_len) { + uint64_t chk = 1; + size_t i = 0; + while(hrp[i] != 0) { + int ch = hrp[i]; + if(ch < 33 || ch > 126) { + return 0; + } + *(output++) = ch; + chk = cashaddr_polymod_step(chk) ^ (ch & 0x1f); + ++i; + } + if(i + 1 + data_len + CHECKSUM_SIZE > MAX_CASHADDR_SIZE) { + return 0; + } + chk = cashaddr_polymod_step(chk); + *(output++) = ':'; + for(i = 0; i < data_len; ++i) { + if(*data >> 5) return 0; + chk = cashaddr_polymod_step(chk) ^ (*data); + *(output++) = charset[*(data++)]; + } + for(i = 0; i < CHECKSUM_SIZE; ++i) { + chk = cashaddr_polymod_step(chk); + } + chk ^= 1; + for(i = 0; i < CHECKSUM_SIZE; ++i) { + *(output++) = charset[(chk >> ((CHECKSUM_SIZE - 1 - i) * 5)) & 0x1f]; + } + *output = 0; + return 1; +} + +int cash_decode(char* hrp, uint8_t* data, size_t* data_len, const char* input) { + uint64_t chk = 1; + size_t i = 0; + size_t input_len = strlen(input); + size_t hrp_len = 0; + int have_lower = 0, have_upper = 0; + if(input_len < CHECKSUM_SIZE || input_len > MAX_CASHADDR_SIZE) { + return 0; + } + *data_len = 0; + while(*data_len < input_len && input[(input_len - 1) - *data_len] != ':') { + ++(*data_len); + } + hrp_len = input_len - (1 + *data_len); + if(1 + *data_len >= input_len || hrp_len > MAX_HRP_SIZE || *data_len < CHECKSUM_SIZE || + *data_len > CHECKSUM_SIZE + MAX_BASE32_SIZE) { + return 0; + } + // subtract checksum + *(data_len) -= CHECKSUM_SIZE; + for(i = 0; i < hrp_len; ++i) { + int ch = input[i]; + if(ch < 33 || ch > 126) { + return 0; + } + if(ch >= 'a' && ch <= 'z') { + have_lower = 1; + } else if(ch >= 'A' && ch <= 'Z') { + have_upper = 1; + ch = (ch - 'A') + 'a'; + } + hrp[i] = ch; + chk = cashaddr_polymod_step(chk) ^ (ch & 0x1f); + } + hrp[i] = 0; + chk = cashaddr_polymod_step(chk); + ++i; + while(i < input_len) { + int v = (input[i] & 0x80) ? -1 : charset_rev[(int)input[i]]; + if(input[i] >= 'a' && input[i] <= 'z') have_lower = 1; + if(input[i] >= 'A' && input[i] <= 'Z') have_upper = 1; + if(v == -1) { + return 0; + } + chk = cashaddr_polymod_step(chk) ^ v; + if(i + CHECKSUM_SIZE < input_len) { + data[i - (1 + hrp_len)] = v; + } + ++i; + } + if(have_lower && have_upper) { + return 0; + } + return chk == 1; +} + +static int convert_bits( + uint8_t* out, + size_t* outlen, + int outbits, + const uint8_t* in, + size_t inlen, + int inbits, + int pad) { + uint32_t val = 0; + int bits = 0; + uint32_t maxv = (((uint32_t)1) << outbits) - 1; + while(inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while(bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; + } + } + if(pad) { + if(bits) { + out[(*outlen)++] = (val << (outbits - bits)) & maxv; + } + } else if(((val << (outbits - bits)) & maxv) || bits >= inbits) { + return 0; + } + return 1; +} + +int cash_addr_encode(char* output, const char* hrp, const uint8_t* data, size_t data_len) { + uint8_t base32[MAX_BASE32_SIZE] = {0}; + size_t base32len = 0; + if(data_len < 2 || data_len > MAX_DATA_SIZE) return 0; + convert_bits(base32, &base32len, 5, data, data_len, 8, 1); + return cash_encode(output, hrp, base32, base32len); +} + +int cash_addr_decode(uint8_t* witdata, size_t* witdata_len, const char* hrp, const char* addr) { + uint8_t data[MAX_BASE32_SIZE] = {0}; + char hrp_actual[MAX_HRP_SIZE + 1] = {0}; + size_t data_len = 0; + if(!cash_decode(hrp_actual, data, &data_len, addr)) return 0; + if(data_len == 0 || data_len > MAX_BASE32_SIZE) return 0; + if(strncmp(hrp, hrp_actual, MAX_HRP_SIZE + 1) != 0) return 0; + *witdata_len = 0; + if(!convert_bits(witdata, witdata_len, 8, data, data_len, 5, 0)) return 0; + if(*witdata_len < 2 || *witdata_len > MAX_DATA_SIZE) return 0; + return 1; +} diff --git a/applications/external/flipbip/lib/crypto/cash_addr.h b/applications/external/flipbip/lib/crypto/cash_addr.h new file mode 100644 index 000000000..5590b5b87 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/cash_addr.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2017 Jochen Hoenicke, Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _CASH_ADDR_H_ +#define _CASH_ADDR_H_ 1 + +#include + +/** Encode a Cashaddr address + * + * Out: output: Pointer to a buffer of size 105 + strlen(hrp) that will be + * updated to contain the null-terminated address. + * In: hrp: Pointer to the null-terminated human readable part to use + * (chain/network specific). + * prog: Data bytes for the hash (between 21 and 65 bytes). + * prog_len: Number of data bytes in prog. + * Returns 1 if successful. + */ +int cash_addr_encode(char* output, const char* hrp, const uint8_t* prog, size_t prog_len); + +/** Decode a CashAddr address + * + * Out: prog: Pointer to a buffer of size 65 that will be updated to + * contain the witness program bytes. + * prog_len: Pointer to a size_t that will be updated to contain the + * length of bytes in prog. hrp: Pointer to the null-terminated human + * readable part that is expected (chain/network specific). addr: Pointer to + * the null-terminated address. Returns 1 if successful. + */ +int cash_addr_decode(uint8_t* prog, size_t* prog_len, const char* hrp, const char* addr); + +/** Encode a Cash string + * + * Out: output: Pointer to a buffer of size strlen(hrp) + data_len + 8 that + * will be updated to contain the null-terminated Cash string. + * In: hrp : Pointer to the null-terminated human readable part. + * data : Pointer to an array of 5-bit values. + * data_len: Length of the data array. + * Returns 1 if successful. + */ +int cash_encode(char* output, const char* hrp, const uint8_t* data, size_t data_len); + +/** Decode a Cash string + * + * Out: hrp: Pointer to a buffer of size strlen(input) - 6. Will be + * updated to contain the null-terminated human readable part. + * data: Pointer to a buffer of size strlen(input) - 8 that will + * hold the encoded 5-bit data values. + * data_len: Pointer to a size_t that will be updated to be the number + * of entries in data. + * In: input: Pointer to a null-terminated Cash string. + * Returns 1 if succesful. + */ +int cash_decode(char* hrp, uint8_t* data, size_t* data_len, const char* input); + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/LICENSE b/applications/external/flipbip/lib/crypto/chacha20poly1305/LICENSE new file mode 100644 index 000000000..95404966f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (C) 2016 Will Glozer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.c b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.c new file mode 100644 index 000000000..281dd465a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.c @@ -0,0 +1,63 @@ +// Implementations of the XChaCha20 + Poly1305 and ChaCha20 + Poly1305 +// AEAD constructions with a goal of simplicity and correctness rather +// than performance. + +#include "chacha20poly1305.h" +#include "ecrypt_portable.h" + +void hchacha20(ECRYPT_ctx* x, u8* c); + +// Initialize the XChaCha20 + Poly1305 context for encryption or decryption +// using a 32 byte key and 24 byte nonce. The key and the first 16 bytes of +// the nonce are used as input to HChaCha20 to derive the Chacha20 key. +void xchacha20poly1305_init( + chacha20poly1305_ctx* ctx, + const uint8_t key[32], + const uint8_t nonce[24]) { + unsigned char subkey[32] = {0}; + unsigned char block0[64] = {0}; + ECRYPT_ctx tmp = {0}; + + // Generate the Chacha20 key by applying HChaCha20 to the + // original key and the first 16 bytes of the nonce. + ECRYPT_keysetup(&tmp, key, 256, 16); + tmp.input[12] = U8TO32_LITTLE(nonce + 0); + tmp.input[13] = U8TO32_LITTLE(nonce + 4); + tmp.input[14] = U8TO32_LITTLE(nonce + 8); + tmp.input[15] = U8TO32_LITTLE(nonce + 12); + hchacha20(&tmp, subkey); + + // Initialize Chacha20 with the newly generated key and + // the last 8 bytes of the nonce. + ECRYPT_keysetup(&ctx->chacha20, subkey, 256, 16); + ECRYPT_ivsetup(&ctx->chacha20, nonce + 16); + + // Encrypt 64 bytes of zeros and use the first 32 bytes + // as the Poly1305 key. + ECRYPT_encrypt_bytes(&ctx->chacha20, block0, block0, 64); + poly1305_init(&ctx->poly1305, block0); +} + +// Encrypt n bytes of plaintext where n must be evenly divisible by the +// Chacha20 blocksize of 64, except for the final n bytes of plaintext. +void chacha20poly1305_encrypt(chacha20poly1305_ctx* ctx, const uint8_t* in, uint8_t* out, size_t n) { + ECRYPT_encrypt_bytes(&ctx->chacha20, in, out, n); + poly1305_update(&ctx->poly1305, out, n); +} + +// Decrypt n bytes of ciphertext where n must be evenly divisible by the +// Chacha20 blocksize of 64, except for the final n bytes of ciphertext. +void chacha20poly1305_decrypt(chacha20poly1305_ctx* ctx, const uint8_t* in, uint8_t* out, size_t n) { + poly1305_update(&ctx->poly1305, in, n); + ECRYPT_encrypt_bytes(&ctx->chacha20, in, out, n); +} + +// Include authenticated data in the Poly1305 MAC. +void chacha20poly1305_auth(chacha20poly1305_ctx* ctx, const uint8_t* in, size_t n) { + poly1305_update(&ctx->poly1305, in, n); +} + +// Compute NaCl secretbox-style Poly1305 MAC. +void chacha20poly1305_finish(chacha20poly1305_ctx* ctx, uint8_t mac[16]) { + poly1305_finish(&ctx->poly1305, mac); +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.h new file mode 100644 index 000000000..803772b7d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha20poly1305.h @@ -0,0 +1,22 @@ +#ifndef CHACHA20POLY1305_H +#define CHACHA20POLY1305_H + +#include +#include "ecrypt_sync.h" +#include "poly1305_donna.h" + +typedef struct { + ECRYPT_ctx chacha20; + poly1305_context poly1305; +} chacha20poly1305_ctx; + +void xchacha20poly1305_init( + chacha20poly1305_ctx* ctx, + const uint8_t key[32], + const uint8_t nonce[24]); +void chacha20poly1305_encrypt(chacha20poly1305_ctx* ctx, const uint8_t* in, uint8_t* out, size_t n); +void chacha20poly1305_decrypt(chacha20poly1305_ctx* ctx, const uint8_t* in, uint8_t* out, size_t n); +void chacha20poly1305_auth(chacha20poly1305_ctx* ctx, const uint8_t* in, size_t n); +void chacha20poly1305_finish(chacha20poly1305_ctx* ctx, uint8_t mac[16]); + +#endif // CHACHA20POLY1305_H diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha_merged.c b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha_merged.c new file mode 100644 index 000000000..ee228b471 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/chacha_merged.c @@ -0,0 +1,251 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#include "ecrypt_sync.h" +#include "ecrypt_portable.h" + +#define ROTATE(v, c) (ROTL32(v, c)) +#define XOR(v, w) ((v) ^ (w)) +#define PLUS(v, w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v), 1)) + +#define QUARTERROUND(a, b, c, d) \ + a = PLUS(a, b); \ + d = ROTATE(XOR(d, a), 16); \ + c = PLUS(c, d); \ + b = ROTATE(XOR(b, c), 12); \ + a = PLUS(a, b); \ + d = ROTATE(XOR(d, a), 8); \ + c = PLUS(c, d); \ + b = ROTATE(XOR(b, c), 7); + +void ECRYPT_init(void) { + return; +} + +static const char sigma[16] = "expand 32-byte k"; +static const char tau[16] = "expand 16-byte k"; + +void ECRYPT_keysetup(ECRYPT_ctx* x, const u8* k, u32 kbits, u32 ivbits) { + (void)ivbits; + const char* constants = (const char*)0; + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + if(kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +void ECRYPT_ivsetup(ECRYPT_ctx* x, const u8* iv) { + x->input[12] = 0; + x->input[13] = 0; + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +void ECRYPT_ctrsetup(ECRYPT_ctx* x, const u8* ctr) { + x->input[12] = U8TO32_LITTLE(ctr + 0); + x->input[13] = U8TO32_LITTLE(ctr + 4); +} + +void ECRYPT_encrypt_bytes(ECRYPT_ctx* x, const u8* m, u8* c, u32 bytes) { + u32 x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0, x8 = 0, x9 = 0, x10 = 0, + x11 = 0, x12 = 0, x13 = 0, x14 = 0, x15 = 0; + u32 j0 = 0, j1 = 0, j2 = 0, j3 = 0, j4 = 0, j5 = 0, j6 = 0, j7 = 0, j8 = 0, j9 = 0, j10 = 0, + j11 = 0, j12 = 0, j13 = 0, j14 = 0, j15 = 0; + u8* ctarget = 0; + u8 tmp[64] = {0}; + int i = 0; + + if(!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for(;;) { + if(bytes < 64) { + for(i = 0; i < (int)bytes; ++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for(i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + x0 = PLUS(x0, j0); + x1 = PLUS(x1, j1); + x2 = PLUS(x2, j2); + x3 = PLUS(x3, j3); + x4 = PLUS(x4, j4); + x5 = PLUS(x5, j5); + x6 = PLUS(x6, j6); + x7 = PLUS(x7, j7); + x8 = PLUS(x8, j8); + x9 = PLUS(x9, j9); + x10 = PLUS(x10, j10); + x11 = PLUS(x11, j11); + x12 = PLUS(x12, j12); + x13 = PLUS(x13, j13); + x14 = PLUS(x14, j14); + x15 = PLUS(x15, j15); + + x0 = XOR(x0, U8TO32_LITTLE(m + 0)); + x1 = XOR(x1, U8TO32_LITTLE(m + 4)); + x2 = XOR(x2, U8TO32_LITTLE(m + 8)); + x3 = XOR(x3, U8TO32_LITTLE(m + 12)); + x4 = XOR(x4, U8TO32_LITTLE(m + 16)); + x5 = XOR(x5, U8TO32_LITTLE(m + 20)); + x6 = XOR(x6, U8TO32_LITTLE(m + 24)); + x7 = XOR(x7, U8TO32_LITTLE(m + 28)); + x8 = XOR(x8, U8TO32_LITTLE(m + 32)); + x9 = XOR(x9, U8TO32_LITTLE(m + 36)); + x10 = XOR(x10, U8TO32_LITTLE(m + 40)); + x11 = XOR(x11, U8TO32_LITTLE(m + 44)); + x12 = XOR(x12, U8TO32_LITTLE(m + 48)); + x13 = XOR(x13, U8TO32_LITTLE(m + 52)); + x14 = XOR(x14, U8TO32_LITTLE(m + 56)); + x15 = XOR(x15, U8TO32_LITTLE(m + 60)); + + j12 = PLUSONE(j12); + if(!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0, x0); + U32TO8_LITTLE(c + 4, x1); + U32TO8_LITTLE(c + 8, x2); + U32TO8_LITTLE(c + 12, x3); + U32TO8_LITTLE(c + 16, x4); + U32TO8_LITTLE(c + 20, x5); + U32TO8_LITTLE(c + 24, x6); + U32TO8_LITTLE(c + 28, x7); + U32TO8_LITTLE(c + 32, x8); + U32TO8_LITTLE(c + 36, x9); + U32TO8_LITTLE(c + 40, x10); + U32TO8_LITTLE(c + 44, x11); + U32TO8_LITTLE(c + 48, x12); + U32TO8_LITTLE(c + 52, x13); + U32TO8_LITTLE(c + 56, x14); + U32TO8_LITTLE(c + 60, x15); + + if(bytes <= 64) { + if(bytes < 64) { + for(i = 0; i < (int)bytes; ++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; + m += 64; + } +} + +void ECRYPT_decrypt_bytes(ECRYPT_ctx* x, const u8* c, u8* m, u32 bytes) { + ECRYPT_encrypt_bytes(x, c, m, bytes); +} + +void ECRYPT_keystream_bytes(ECRYPT_ctx* x, u8* stream, u32 bytes) { + u32 i = 0; + for(i = 0; i < bytes; ++i) stream[i] = 0; + ECRYPT_encrypt_bytes(x, stream, stream, bytes); +} + +void hchacha20(ECRYPT_ctx* x, u8* c) { + u32 x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0, x8 = 0, x9 = 0, x10 = 0, + x11 = 0, x12 = 0, x13 = 0, x14 = 0, x15 = 0; + int i = 0; + + x0 = x->input[0]; + x1 = x->input[1]; + x2 = x->input[2]; + x3 = x->input[3]; + x4 = x->input[4]; + x5 = x->input[5]; + x6 = x->input[6]; + x7 = x->input[7]; + x8 = x->input[8]; + x9 = x->input[9]; + x10 = x->input[10]; + x11 = x->input[11]; + x12 = x->input[12]; + x13 = x->input[13]; + x14 = x->input[14]; + x15 = x->input[15]; + + for(i = 20; i > 0; i -= 2) { + QUARTERROUND(x0, x4, x8, x12) + QUARTERROUND(x1, x5, x9, x13) + QUARTERROUND(x2, x6, x10, x14) + QUARTERROUND(x3, x7, x11, x15) + QUARTERROUND(x0, x5, x10, x15) + QUARTERROUND(x1, x6, x11, x12) + QUARTERROUND(x2, x7, x8, x13) + QUARTERROUND(x3, x4, x9, x14) + } + + U32TO8_LITTLE(c + 0, x0); + U32TO8_LITTLE(c + 4, x1); + U32TO8_LITTLE(c + 8, x2); + U32TO8_LITTLE(c + 12, x3); + U32TO8_LITTLE(c + 16, x12); + U32TO8_LITTLE(c + 20, x13); + U32TO8_LITTLE(c + 24, x14); + U32TO8_LITTLE(c + 28, x15); +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_config.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_config.h new file mode 100644 index 000000000..40a3a484a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_config.h @@ -0,0 +1,316 @@ +/* ecrypt_config.h */ + +/* *** Normally, it should not be necessary to edit this file. *** */ + +#ifndef ECRYPT_CONFIG +#define ECRYPT_CONFIG + +/* ------------------------------------------------------------------------- */ + +/* Guess the endianness of the target architecture. */ + +/* + * The LITTLE endian machines: + */ +#if defined(__ultrix) /* Older MIPS */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__alpha) /* Alpha */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(i386) /* x86 (gcc) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__i386) /* x86 (gcc) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__x86_64) /* x86_64 (gcc) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(_M_IX86) /* x86 (MSC, Borland) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(_MSC_VER) /* x86 (surely MSC) */ +#define ECRYPT_LITTLE_ENDIAN +#elif defined(__INTEL_COMPILER) /* x86 (surely Intel compiler icl.exe) */ +#define ECRYPT_LITTLE_ENDIAN + +/* + * The BIG endian machines: + */ +#elif defined(__sparc) /* Newer Sparc's */ +#define ECRYPT_BIG_ENDIAN +#elif defined(__powerpc__) /* PowerPC */ +#define ECRYPT_BIG_ENDIAN +#elif defined(__ppc__) /* PowerPC */ +#define ECRYPT_BIG_ENDIAN +#elif defined(__hppa) /* HP-PA */ +#define ECRYPT_BIG_ENDIAN + +/* + * Finally machines with UNKNOWN endianness: + */ +#elif defined(_AIX) /* RS6000 */ +#define ECRYPT_UNKNOWN +#elif defined(__aux) /* 68K */ +#define ECRYPT_UNKNOWN +#elif defined(__dgux) /* 88K (but P6 in latest boxes) */ +#define ECRYPT_UNKNOWN +#elif defined(__sgi) /* Newer MIPS */ +#define ECRYPT_UNKNOWN +#else /* Any other processor */ +#define ECRYPT_UNKNOWN +#endif + +/* ------------------------------------------------------------------------- */ + +/* + * Find minimal-width types to store 8-bit, 16-bit, 32-bit, and 64-bit + * integers. + * + * Note: to enable 64-bit types on 32-bit compilers, it might be + * necessary to switch from ISO C90 mode to ISO C99 mode (e.g., gcc + * -std=c99), or to allow compiler-specific extensions. + */ + +#include + +/* --- check char --- */ + +#if(UCHAR_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T char +#define U8C(v) (v##U) + +#if(UCHAR_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(UCHAR_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T char +#define U16C(v) (v##U) +#endif + +#if(UCHAR_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T char +#define U32C(v) (v##U) +#endif + +#if(UCHAR_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T char +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check short --- */ + +#if(USHRT_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T short +#define U8C(v) (v##U) + +#if(USHRT_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(USHRT_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T short +#define U16C(v) (v##U) +#endif + +#if(USHRT_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T short +#define U32C(v) (v##U) +#endif + +#if(USHRT_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T short +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check int --- */ + +#if(UINT_MAX / 0xFU > 0xFU) +#ifndef I8T +#define I8T int +#define U8C(v) (v##U) + +#if(ULONG_MAX == 0xFFU) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(UINT_MAX / 0xFFU > 0xFFU) +#ifndef I16T +#define I16T int +#define U16C(v) (v##U) +#endif + +#if(UINT_MAX / 0xFFFFU > 0xFFFFU) +#ifndef I32T +#define I32T int +#define U32C(v) (v##U) +#endif + +#if(UINT_MAX / 0xFFFFFFFFU > 0xFFFFFFFFU) +#ifndef I64T +#define I64T int +#define U64C(v) (v##U) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check long --- */ + +#if(ULONG_MAX / 0xFUL > 0xFUL) +#ifndef I8T +#define I8T long +#define U8C(v) (v##UL) + +#if(ULONG_MAX == 0xFFUL) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(ULONG_MAX / 0xFFUL > 0xFFUL) +#ifndef I16T +#define I16T long +#define U16C(v) (v##UL) +#endif + +#if(ULONG_MAX / 0xFFFFUL > 0xFFFFUL) +#ifndef I32T +#define I32T long +#define U32C(v) (v##UL) +#endif + +#if(ULONG_MAX / 0xFFFFFFFFUL > 0xFFFFFFFFUL) +#ifndef I64T +#define I64T long +#define U64C(v) (v##UL) +#define ECRYPT_NATIVE64 +#endif + +#endif +#endif +#endif +#endif + +/* --- check long long --- */ + +#ifdef ULLONG_MAX + +#if(ULLONG_MAX / 0xFULL > 0xFULL) +#ifndef I8T +#define I8T long long +#define U8C(v) (v##ULL) + +#if(ULLONG_MAX == 0xFFULL) +#define ECRYPT_I8T_IS_BYTE +#endif + +#endif + +#if(ULLONG_MAX / 0xFFULL > 0xFFULL) +#ifndef I16T +#define I16T long long +#define U16C(v) (v##ULL) +#endif + +#if(ULLONG_MAX / 0xFFFFULL > 0xFFFFULL) +#ifndef I32T +#define I32T long long +#define U32C(v) (v##ULL) +#endif + +#if(ULLONG_MAX / 0xFFFFFFFFULL > 0xFFFFFFFFULL) +#ifndef I64T +#define I64T long long +#define U64C(v) (v##ULL) +#endif + +#endif +#endif +#endif +#endif + +#endif + +/* --- check __int64 --- */ + +#if !defined(__STDC__) && defined(_UI64_MAX) + +#ifndef I64T +#define I64T __int64 +#define U64C(v) (v##ui64) +#endif + +#endif + +/* --- if platform doesn't announce anything, use most common choices --- */ + +#ifndef I8T +#define I8T char +#define U8C(v) (v##U) +#endif +#ifndef I16T +#define I16T short +#define U16C(v) (v##U) +#endif +#ifndef I32T +#define I32T int +#define U32C(v) (v##U) +#endif +#ifndef I64T +#define I64T long long +#define U64C(v) (v##ULL) +#endif + +/* ------------------------------------------------------------------------- */ + +/* find the largest type on this platform (used for alignment) */ + +#if defined(__SSE__) || (defined(_MSC_VER) && (_MSC_VER >= 1300)) + +#include +#define MAXT __m128 + +#elif defined(__MMX__) + +#include +#define MAXT __m64 + +#elif defined(__ALTIVEC__) + +#define MAXT __vector int + +#else + +#define MAXT long + +#endif + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_machine.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_machine.h new file mode 100644 index 000000000..6773c2571 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_machine.h @@ -0,0 +1,49 @@ +/* ecrypt_machine.h */ + +/* + * This file is included by 'ecrypt_portable.h'. It allows to override + * the default macros for specific platforms. Please carefully check + * the machine code generated by your compiler (with optimisations + * turned on) before deciding to edit this file. + */ + +/* ------------------------------------------------------------------------- */ + +#if(defined(ECRYPT_DEFAULT_ROT) && !defined(ECRYPT_MACHINE_ROT)) + +#define ECRYPT_MACHINE_ROT + +#if(defined(WIN32) && defined(_MSC_VER)) + +#undef ROTL32 +#undef ROTR32 +#undef ROTL64 +#undef ROTR64 + +#include + +#pragma intrinsic(_lrotl) /* compile rotations "inline" */ +#pragma intrinsic(_lrotr) + +#define ROTL32(v, n) _lrotl(v, n) +#define ROTR32(v, n) _lrotr(v, n) +#define ROTL64(v, n) _rotl64(v, n) +#define ROTR64(v, n) _rotr64(v, n) + +#endif + +#endif + +/* ------------------------------------------------------------------------- */ + +#if(defined(ECRYPT_DEFAULT_SWAP) && !defined(ECRYPT_MACHINE_SWAP)) + +#define ECRYPT_MACHINE_SWAP + +/* + * If you want to overwrite the default swap macros, put it here. And so on. + */ + +#endif + +/* ------------------------------------------------------------------------- */ diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_portable.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_portable.h new file mode 100644 index 000000000..f5d500114 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_portable.h @@ -0,0 +1,246 @@ +/* ecrypt_portable.h */ + +/* + * WARNING: the conversions defined below are implemented as macros, + * and should be used carefully. They should NOT be used with + * parameters which perform some action. E.g., the following two lines + * are not equivalent: + * + * 1) ++x; y = ROTL32(x, n); + * 2) y = ROTL32(++x, n); + */ + +/* + * *** Please do not edit this file. *** + * + * The default macros can be overridden for specific architectures by + * editing 'ecrypt_machine.h'. + */ + +#ifndef ECRYPT_PORTABLE +#define ECRYPT_PORTABLE + +#include "ecrypt_config.h" +#include "ecrypt_types.h" + +/* ------------------------------------------------------------------------- */ + +/* + * The following macros are used to obtain exact-width results. + */ + +#define U8V(v) ((u8)(v)&U8C(0xFF)) +#define U16V(v) ((u16)(v)&U16C(0xFFFF)) +#define U32V(v) ((u32)(v)&U32C(0xFFFFFFFF)) +#define U64V(v) ((u64)(v)&U64C(0xFFFFFFFFFFFFFFFF)) + +/* ------------------------------------------------------------------------- */ + +/* + * The following macros return words with their bits rotated over n + * positions to the left/right. + */ + +#define ECRYPT_DEFAULT_ROT + +#define ROTL8(v, n) (U8V((v) << (n)) | ((v) >> (8 - (n)))) + +#define ROTL16(v, n) (U16V((v) << (n)) | ((v) >> (16 - (n)))) + +#define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define ROTL64(v, n) (U64V((v) << (n)) | ((v) >> (64 - (n)))) + +#define ROTR8(v, n) ROTL8(v, 8 - (n)) +#define ROTR16(v, n) ROTL16(v, 16 - (n)) +#define ROTR32(v, n) ROTL32(v, 32 - (n)) +#define ROTR64(v, n) ROTL64(v, 64 - (n)) + +#include "ecrypt_machine.h" + +/* ------------------------------------------------------------------------- */ + +/* + * The following macros return a word with bytes in reverse order. + */ + +#define ECRYPT_DEFAULT_SWAP + +#define SWAP16(v) ROTL16(v, 8) + +#define SWAP32(v) ((ROTL32(v, 8) & U32C(0x00FF00FF)) | (ROTL32(v, 24) & U32C(0xFF00FF00))) + +#ifdef ECRYPT_NATIVE64 +#define SWAP64(v) \ + ((ROTL64(v, 8) & U64C(0x000000FF000000FF)) | (ROTL64(v, 24) & U64C(0x0000FF000000FF00)) | \ + (ROTL64(v, 40) & U64C(0x00FF000000FF0000)) | (ROTL64(v, 56) & U64C(0xFF000000FF000000))) +#else +#define SWAP64(v) (((u64)SWAP32(U32V(v)) << 32) | (u64)SWAP32(U32V(v >> 32))) +#endif + +#include "ecrypt_machine.h" + +#define ECRYPT_DEFAULT_WTOW + +#ifdef ECRYPT_LITTLE_ENDIAN +#define U16TO16_LITTLE(v) (v) +#define U32TO32_LITTLE(v) (v) +#define U64TO64_LITTLE(v) (v) + +#define U16TO16_BIG(v) SWAP16(v) +#define U32TO32_BIG(v) SWAP32(v) +#define U64TO64_BIG(v) SWAP64(v) +#endif + +#ifdef ECRYPT_BIG_ENDIAN +#define U16TO16_LITTLE(v) SWAP16(v) +#define U32TO32_LITTLE(v) SWAP32(v) +#define U64TO64_LITTLE(v) SWAP64(v) + +#define U16TO16_BIG(v) (v) +#define U32TO32_BIG(v) (v) +#define U64TO64_BIG(v) (v) +#endif + +#include "ecrypt_machine.h" + +/* + * The following macros load words from an array of bytes with + * different types of endianness, and vice versa. + */ + +#define ECRYPT_DEFAULT_BTOW + +#if(!defined(ECRYPT_UNKNOWN) && defined(ECRYPT_I8T_IS_BYTE)) + +#define U8TO16_LITTLE(p) U16TO16_LITTLE(((u16*)(p))[0]) +#define U8TO32_LITTLE(p) U32TO32_LITTLE(((u32*)(p))[0]) +#define U8TO64_LITTLE(p) U64TO64_LITTLE(((u64*)(p))[0]) + +#define U8TO16_BIG(p) U16TO16_BIG(((u16*)(p))[0]) +#define U8TO32_BIG(p) U32TO32_BIG(((u32*)(p))[0]) +#define U8TO64_BIG(p) U64TO64_BIG(((u64*)(p))[0]) + +#define U16TO8_LITTLE(p, v) (((u16*)(p))[0] = U16TO16_LITTLE(v)) +#define U32TO8_LITTLE(p, v) (((u32*)(p))[0] = U32TO32_LITTLE(v)) +#define U64TO8_LITTLE(p, v) (((u64*)(p))[0] = U64TO64_LITTLE(v)) + +#define U16TO8_BIG(p, v) (((u16*)(p))[0] = U16TO16_BIG(v)) +#define U32TO8_BIG(p, v) (((u32*)(p))[0] = U32TO32_BIG(v)) +#define U64TO8_BIG(p, v) (((u64*)(p))[0] = U64TO64_BIG(v)) + +#else + +#define U8TO16_LITTLE(p) (((u16)((p)[0])) | ((u16)((p)[1]) << 8)) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0])) | ((u32)((p)[1]) << 8) | ((u32)((p)[2]) << 16) | ((u32)((p)[3]) << 24)) + +#ifdef ECRYPT_NATIVE64 +#define U8TO64_LITTLE(p) \ + (((u64)((p)[0])) | ((u64)((p)[1]) << 8) | ((u64)((p)[2]) << 16) | ((u64)((p)[3]) << 24) | \ + ((u64)((p)[4]) << 32) | ((u64)((p)[5]) << 40) | ((u64)((p)[6]) << 48) | \ + ((u64)((p)[7]) << 56)) +#else +#define U8TO64_LITTLE(p) ((u64)U8TO32_LITTLE(p) | ((u64)U8TO32_LITTLE((p) + 4) << 32)) +#endif + +#define U8TO16_BIG(p) (((u16)((p)[0]) << 8) | ((u16)((p)[1]))) + +#define U8TO32_BIG(p) \ + (((u32)((p)[0]) << 24) | ((u32)((p)[1]) << 16) | ((u32)((p)[2]) << 8) | ((u32)((p)[3]))) + +#ifdef ECRYPT_NATIVE64 +#define U8TO64_BIG(p) \ + (((u64)((p)[0]) << 56) | ((u64)((p)[1]) << 48) | ((u64)((p)[2]) << 40) | \ + ((u64)((p)[3]) << 32) | ((u64)((p)[4]) << 24) | ((u64)((p)[5]) << 16) | \ + ((u64)((p)[6]) << 8) | ((u64)((p)[7]))) +#else +#define U8TO64_BIG(p) (((u64)U8TO32_BIG(p) << 32) | (u64)U8TO32_BIG((p) + 4)) +#endif + +#define U16TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + } while(0) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while(0) + +#ifdef ECRYPT_NATIVE64 +#define U64TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + (p)[4] = U8V((v) >> 32); \ + (p)[5] = U8V((v) >> 40); \ + (p)[6] = U8V((v) >> 48); \ + (p)[7] = U8V((v) >> 56); \ + } while(0) +#else +#define U64TO8_LITTLE(p, v) \ + do { \ + U32TO8_LITTLE((p), U32V((v))); \ + U32TO8_LITTLE((p) + 4, U32V((v) >> 32)); \ + } while(0) +#endif + +#define U16TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v)); \ + (p)[1] = U8V((v) >> 8); \ + } while(0) + +#define U32TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v) >> 24); \ + (p)[1] = U8V((v) >> 16); \ + (p)[2] = U8V((v) >> 8); \ + (p)[3] = U8V((v)); \ + } while(0) + +#ifdef ECRYPT_NATIVE64 +#define U64TO8_BIG(p, v) \ + do { \ + (p)[0] = U8V((v) >> 56); \ + (p)[1] = U8V((v) >> 48); \ + (p)[2] = U8V((v) >> 40); \ + (p)[3] = U8V((v) >> 32); \ + (p)[4] = U8V((v) >> 24); \ + (p)[5] = U8V((v) >> 16); \ + (p)[6] = U8V((v) >> 8); \ + (p)[7] = U8V((v)); \ + } while(0) +#else +#define U64TO8_BIG(p, v) \ + do { \ + U32TO8_BIG((p), U32V((v) >> 32)); \ + U32TO8_BIG((p) + 4, U32V((v))); \ + } while(0) +#endif + +#endif + +#include "ecrypt_machine.h" + +/* ------------------------------------------------------------------------- */ + +#define AT_LEAST_ONE(n) (((n) < 1) ? 1 : (n)) + +#define ALIGN(t, v, n) \ + union { \ + t b[n]; \ + MAXT l[AT_LEAST_ONE(n * sizeof(t) / sizeof(MAXT))]; \ + } v + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_sync.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_sync.h new file mode 100644 index 000000000..f51608f9a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_sync.h @@ -0,0 +1,282 @@ +#define ECRYPT_VARIANT 1 +#define ECRYPT_API +/* ecrypt_sync.h */ + +/* + * Header file for synchronous stream ciphers without authentication + * mechanism. + * + * *** Please only edit parts marked with "[edit]". *** + */ + +#ifndef ECRYPT_SYNC +#define ECRYPT_SYNC + +#include "ecrypt_types.h" + +/* ------------------------------------------------------------------------- */ + +/* Cipher parameters */ + +/* + * The name of your cipher. + */ +#define ECRYPT_NAME "ChaCha20" +#define ECRYPT_PROFILE "_____" + +/* + * Specify which key and IV sizes are supported by your cipher. A user + * should be able to enumerate the supported sizes by running the + * following code: + * + * for (i = 0; ECRYPT_KEYSIZE(i) <= ECRYPT_MAXKEYSIZE; ++i) + * { + * keysize = ECRYPT_KEYSIZE(i); + * + * ... + * } + * + * All sizes are in bits. + */ + +#define ECRYPT_MAXKEYSIZE 256 /* [edit] */ +#define ECRYPT_KEYSIZE(i) (128 + (i)*128) /* [edit] */ + +#define ECRYPT_MAXIVSIZE 64 /* [edit] */ +#define ECRYPT_IVSIZE(i) (64 + (i)*64) /* [edit] */ + +/* ------------------------------------------------------------------------- */ + +/* Data structures */ + +/* + * ECRYPT_ctx is the structure containing the representation of the + * internal state of your cipher. + */ + +typedef struct { + u32 input[16]; /* could be compressed */ + /* + * [edit] + * + * Put here all state variable needed during the encryption process. + */ +} ECRYPT_ctx; + +/* ------------------------------------------------------------------------- */ + +/* Mandatory functions */ + +/* + * Key and message independent initialization. This function will be + * called once when the program starts (e.g., to build expanded S-box + * tables). + */ +void ECRYPT_init(void); + +/* + * Key setup. It is the user's responsibility to select the values of + * keysize and ivsize from the set of supported values specified + * above. + */ +void ECRYPT_keysetup( + ECRYPT_ctx* ctx, + const u8* key, + u32 keysize, /* Key size in bits. */ + u32 ivsize); /* IV size in bits. */ + +/* + * IV setup. After having called ECRYPT_keysetup(), the user is + * allowed to call ECRYPT_ivsetup() different times in order to + * encrypt/decrypt different messages with the same key but different + * IV's. ECRYPT_ivsetup() also sets block counter to zero. + */ +void ECRYPT_ivsetup(ECRYPT_ctx* ctx, const u8* iv); + +/* + * Block counter setup. It is used only for special purposes, + * since block counter is usually initialized with ECRYPT_ivsetup. + * ECRYPT_ctrsetup has to be called after ECRYPT_ivsetup. + */ +void ECRYPT_ctrsetup(ECRYPT_ctx* ctx, const u8* ctr); + +/* + * Encryption/decryption of arbitrary length messages. + * + * For efficiency reasons, the API provides two types of + * encrypt/decrypt functions. The ECRYPT_encrypt_bytes() function + * (declared here) encrypts byte strings of arbitrary length, while + * the ECRYPT_encrypt_blocks() function (defined later) only accepts + * lengths which are multiples of ECRYPT_BLOCKLENGTH. + * + * The user is allowed to make multiple calls to + * ECRYPT_encrypt_blocks() to incrementally encrypt a long message, + * but he is NOT allowed to make additional encryption calls once he + * has called ECRYPT_encrypt_bytes() (unless he starts a new message + * of course). For example, this sequence of calls is acceptable: + * + * ECRYPT_keysetup(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_bytes(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_blocks(); + * + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_bytes(); + * + * The following sequence is not: + * + * ECRYPT_keysetup(); + * ECRYPT_ivsetup(); + * ECRYPT_encrypt_blocks(); + * ECRYPT_encrypt_bytes(); + * ECRYPT_encrypt_blocks(); + */ + +void ECRYPT_encrypt_bytes( + ECRYPT_ctx* ctx, + const u8* plaintext, + u8* ciphertext, + u32 msglen); /* Message length in bytes. */ + +void ECRYPT_decrypt_bytes( + ECRYPT_ctx* ctx, + const u8* ciphertext, + u8* plaintext, + u32 msglen); /* Message length in bytes. */ + +/* ------------------------------------------------------------------------- */ + +/* Optional features */ + +/* + * For testing purposes it can sometimes be useful to have a function + * which immediately generates keystream without having to provide it + * with a zero plaintext. If your cipher cannot provide this function + * (e.g., because it is not strictly a synchronous cipher), please + * reset the ECRYPT_GENERATES_KEYSTREAM flag. + */ + +#define ECRYPT_GENERATES_KEYSTREAM +#ifdef ECRYPT_GENERATES_KEYSTREAM + +void ECRYPT_keystream_bytes( + ECRYPT_ctx* ctx, + u8* keystream, + u32 length); /* Length of keystream in bytes. */ + +#endif + +/* ------------------------------------------------------------------------- */ + +/* Optional optimizations */ + +/* + * By default, the functions in this section are implemented using + * calls to functions declared above. However, you might want to + * implement them differently for performance reasons. + */ + +/* + * All-in-one encryption/decryption of (short) packets. + * + * The default definitions of these functions can be found in + * "ecrypt-sync.c". If you want to implement them differently, please + * undef the ECRYPT_USES_DEFAULT_ALL_IN_ONE flag. + */ +#define ECRYPT_USES_DEFAULT_ALL_IN_ONE /* [edit] */ + +void ECRYPT_encrypt_packet( + ECRYPT_ctx* ctx, + const u8* iv, + const u8* plaintext, + u8* ciphertext, + u32 msglen); + +void ECRYPT_decrypt_packet( + ECRYPT_ctx* ctx, + const u8* iv, + const u8* ciphertext, + u8* plaintext, + u32 msglen); + +/* + * Encryption/decryption of blocks. + * + * By default, these functions are defined as macros. If you want to + * provide a different implementation, please undef the + * ECRYPT_USES_DEFAULT_BLOCK_MACROS flag and implement the functions + * declared below. + */ + +#define ECRYPT_BLOCKLENGTH 64 /* [edit] */ + +#define ECRYPT_USES_DEFAULT_BLOCK_MACROS /* [edit] */ +#ifdef ECRYPT_USES_DEFAULT_BLOCK_MACROS + +#define ECRYPT_encrypt_blocks(ctx, plaintext, ciphertext, blocks) \ + ECRYPT_encrypt_bytes(ctx, plaintext, ciphertext, (blocks)*ECRYPT_BLOCKLENGTH) + +#define ECRYPT_decrypt_blocks(ctx, ciphertext, plaintext, blocks) \ + ECRYPT_decrypt_bytes(ctx, ciphertext, plaintext, (blocks)*ECRYPT_BLOCKLENGTH) + +#ifdef ECRYPT_GENERATES_KEYSTREAM + +#define ECRYPT_keystream_blocks(ctx, keystream, blocks) \ + ECRYPT_keystream_bytes(ctx, keystream, (blocks)*ECRYPT_BLOCKLENGTH) + +#endif + +#else + +void ECRYPT_encrypt_blocks( + ECRYPT_ctx* ctx, + const u8* plaintext, + u8* ciphertext, + u32 blocks); /* Message length in blocks. */ + +void ECRYPT_decrypt_blocks( + ECRYPT_ctx* ctx, + const u8* ciphertext, + u8* plaintext, + u32 blocks); /* Message length in blocks. */ + +#ifdef ECRYPT_GENERATES_KEYSTREAM + +void ECRYPT_keystream_blocks( + ECRYPT_ctx* ctx, + const u8* keystream, + u32 blocks); /* Keystream length in blocks. */ + +#endif + +#endif + +/* + * If your cipher can be implemented in different ways, you can use + * the ECRYPT_VARIANT parameter to allow the user to choose between + * them at compile time (e.g., gcc -DECRYPT_VARIANT=3 ...). Please + * only use this possibility if you really think it could make a + * significant difference and keep the number of variants + * (ECRYPT_MAXVARIANT) as small as possible (definitely not more than + * 10). Note also that all variants should have exactly the same + * external interface (i.e., the same ECRYPT_BLOCKLENGTH, etc.). + */ +#define ECRYPT_MAXVARIANT 1 /* [edit] */ + +#ifndef ECRYPT_VARIANT +#define ECRYPT_VARIANT 1 +#endif + +#if(ECRYPT_VARIANT > ECRYPT_MAXVARIANT) +#error this variant does not exist +#endif + +/* ------------------------------------------------------------------------- */ + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_types.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_types.h new file mode 100644 index 000000000..2d1a41975 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/ecrypt_types.h @@ -0,0 +1,53 @@ +/* ecrypt_types.h */ + +/* + * *** Please do not edit this file. *** + * + * The default macros can be overridden for specific architectures by + * editing 'ecrypt_machine.h'. + */ + +#ifndef ECRYPT_TYPES +#define ECRYPT_TYPES + +#include "ecrypt_config.h" + +/* ------------------------------------------------------------------------- */ + +/* + * The following types are defined (if available): + * + * u8: unsigned integer type, at least 8 bits + * u16: unsigned integer type, at least 16 bits + * u32: unsigned integer type, at least 32 bits + * u64: unsigned integer type, at least 64 bits + * + * s8, s16, s32, s64 -> signed counterparts of u8, u16, u32, u64 + * + * The selection of minimum-width integer types is taken care of by + * 'ecrypt_config.h'. Note: to enable 64-bit types on 32-bit + * compilers, it might be necessary to switch from ISO C90 mode to ISO + * C99 mode (e.g., gcc -std=c99). + */ + +#ifdef I8T +typedef signed I8T s8; +typedef unsigned I8T u8; +#endif + +#ifdef I16T +typedef signed I16T s16; +typedef unsigned I16T u16; +#endif + +#ifdef I32T +typedef signed I32T s32; +typedef unsigned I32T u32; +#endif + +#ifdef I64T +typedef signed I64T s64; +typedef unsigned I64T u64; +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.c b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.c new file mode 100644 index 000000000..4ab353127 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.c @@ -0,0 +1,208 @@ +#include "poly1305_donna.h" +#include "poly1305_donna_32.h" + +void poly1305_update(poly1305_context* ctx, const unsigned char* m, size_t bytes) { + poly1305_state_internal_t* st = (poly1305_state_internal_t*)ctx; + size_t i = 0; + + /* handle leftover */ + if(st->leftover) { + size_t want = (poly1305_block_size - st->leftover); + if(want > bytes) want = bytes; + for(i = 0; i < want; i++) st->buffer[st->leftover + i] = m[i]; + bytes -= want; + m += want; + st->leftover += want; + if(st->leftover < poly1305_block_size) return; + poly1305_blocks(st, st->buffer, poly1305_block_size); + st->leftover = 0; + } + + /* process full blocks */ + if(bytes >= poly1305_block_size) { + size_t want = (bytes & ~(poly1305_block_size - 1)); + poly1305_blocks(st, m, want); + m += want; + bytes -= want; + } + + /* store leftover */ + if(bytes) { + for(i = 0; i < bytes; i++) st->buffer[st->leftover + i] = m[i]; + st->leftover += bytes; + } +} + +void poly1305_auth( + unsigned char mac[16], + const unsigned char* m, + size_t bytes, + const unsigned char key[32]) { + poly1305_context ctx = {0}; + poly1305_init(&ctx, key); + poly1305_update(&ctx, m, bytes); + poly1305_finish(&ctx, mac); +} + +int poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]) { + size_t i = 0; + unsigned int dif = 0; + for(i = 0; i < 16; i++) dif |= (mac1[i] ^ mac2[i]); + dif = (dif - 1) >> ((sizeof(unsigned int) * 8) - 1); + return (dif & 1); +} + +/* test a few basic operations */ +int poly1305_power_on_self_test(void) { + /* example from nacl */ + static const unsigned char nacl_key[32] = { + 0xee, 0xa6, 0xa7, 0x25, 0x1c, 0x1e, 0x72, 0x91, 0x6d, 0x11, 0xc2, + 0xcb, 0x21, 0x4d, 0x3c, 0x25, 0x25, 0x39, 0x12, 0x1d, 0x8e, 0x23, + 0x4e, 0x65, 0x2d, 0x65, 0x1f, 0xa4, 0xc8, 0xcf, 0xf8, 0x80, + }; + + static const unsigned char nacl_msg[131] = { + 0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73, 0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, + 0xce, 0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4, 0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, + 0x18, 0x6a, 0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b, 0x4d, 0xa7, 0xf0, 0x11, 0xec, + 0x48, 0xc9, 0x72, 0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2, 0x27, 0x0d, 0x6f, 0xb8, + 0x63, 0xd5, 0x17, 0x38, 0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a, 0xb9, 0x32, 0x16, + 0x45, 0x48, 0xe5, 0x26, 0xae, 0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea, 0xbd, 0x6b, + 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda, 0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde, 0x56, + 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3, 0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6, + 0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74, 0xe3, 0x55, 0xa5}; + + static const unsigned char nacl_mac[16] = { + 0xf3, + 0xff, + 0xc7, + 0x70, + 0x3f, + 0x94, + 0x00, + 0xe5, + 0x2a, + 0x7d, + 0xfb, + 0x4b, + 0x3d, + 0x33, + 0x05, + 0xd9}; + + /* generates a final value of (2^130 - 2) == 3 */ + static const unsigned char wrap_key[32] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + static const unsigned char wrap_msg[16] = { + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff, + 0xff}; + + static const unsigned char wrap_mac[16] = { + 0x03, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + }; + + /* + mac of the macs of messages of length 0 to 256, where the key and messages + have all their values set to the length + */ + static const unsigned char total_key[32] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + static const unsigned char total_mac[16] = { + 0x64, + 0xaf, + 0xe2, + 0xe8, + 0xd6, + 0xad, + 0x7b, + 0xbd, + 0xd2, + 0x87, + 0xf9, + 0x7c, + 0x44, + 0x62, + 0x3d, + 0x39}; + + poly1305_context ctx = {0}; + poly1305_context total_ctx = {0}; + unsigned char all_key[32] = {0}; + unsigned char all_msg[256] = {0}; + unsigned char mac[16] = {0}; + size_t i = 0, j = 0; + int result = 1; + + for(i = 0; i < sizeof(mac); i++) mac[i] = 0; + poly1305_auth(mac, nacl_msg, sizeof(nacl_msg), nacl_key); + result &= poly1305_verify(nacl_mac, mac); + + for(i = 0; i < sizeof(mac); i++) mac[i] = 0; + poly1305_init(&ctx, nacl_key); + poly1305_update(&ctx, nacl_msg + 0, 32); + poly1305_update(&ctx, nacl_msg + 32, 64); + poly1305_update(&ctx, nacl_msg + 96, 16); + poly1305_update(&ctx, nacl_msg + 112, 8); + poly1305_update(&ctx, nacl_msg + 120, 4); + poly1305_update(&ctx, nacl_msg + 124, 2); + poly1305_update(&ctx, nacl_msg + 126, 1); + poly1305_update(&ctx, nacl_msg + 127, 1); + poly1305_update(&ctx, nacl_msg + 128, 1); + poly1305_update(&ctx, nacl_msg + 129, 1); + poly1305_update(&ctx, nacl_msg + 130, 1); + poly1305_finish(&ctx, mac); + result &= poly1305_verify(nacl_mac, mac); + + for(i = 0; i < sizeof(mac); i++) mac[i] = 0; + poly1305_auth(mac, wrap_msg, sizeof(wrap_msg), wrap_key); + result &= poly1305_verify(wrap_mac, mac); + + poly1305_init(&total_ctx, total_key); + for(i = 0; i < 256; i++) { + /* set key and message to 'i,i,i..' */ + for(j = 0; j < sizeof(all_key); j++) all_key[j] = i; + for(j = 0; j < i; j++) all_msg[j] = i; + poly1305_auth(mac, all_msg, i, all_key); + poly1305_update(&total_ctx, mac, 16); + } + poly1305_finish(&total_ctx, mac); + result &= poly1305_verify(total_mac, mac); + + return result; +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.h new file mode 100644 index 000000000..108dee1f6 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna.h @@ -0,0 +1,23 @@ +#ifndef POLY1305_DONNA_H +#define POLY1305_DONNA_H + +#include + +typedef struct poly1305_context { + size_t aligner; + unsigned char opaque[136]; +} poly1305_context; + +void poly1305_init(poly1305_context* ctx, const unsigned char key[32]); +void poly1305_update(poly1305_context* ctx, const unsigned char* m, size_t bytes); +void poly1305_finish(poly1305_context* ctx, unsigned char mac[16]); +void poly1305_auth( + unsigned char mac[16], + const unsigned char* m, + size_t bytes, + const unsigned char key[32]); + +int poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]); +int poly1305_power_on_self_test(void); + +#endif /* POLY1305_DONNA_H */ diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna_32.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna_32.h new file mode 100644 index 000000000..c2289bb98 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/poly1305_donna_32.h @@ -0,0 +1,252 @@ +/* + poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition +*/ + +#if defined(_MSC_VER) +#define POLY1305_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +#define POLY1305_NOINLINE __attribute__((noinline)) +#else +#define POLY1305_NOINLINE +#endif + +#define poly1305_block_size 16 + +/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */ +typedef struct poly1305_state_internal_t { + unsigned long r[5]; + unsigned long h[5]; + unsigned long pad[4]; + size_t leftover; + unsigned char buffer[poly1305_block_size]; + unsigned char final; +} poly1305_state_internal_t; + +/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ +static unsigned long U8TO32(const unsigned char* p) { + return ( + ((unsigned long)(p[0] & 0xff)) | ((unsigned long)(p[1] & 0xff) << 8) | + ((unsigned long)(p[2] & 0xff) << 16) | ((unsigned long)(p[3] & 0xff) << 24)); +} + +/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ +static void U32TO8(unsigned char* p, unsigned long v) { + p[0] = (v)&0xff; + p[1] = (v >> 8) & 0xff; + p[2] = (v >> 16) & 0xff; + p[3] = (v >> 24) & 0xff; +} + +void poly1305_init(poly1305_context* ctx, const unsigned char key[32]) { + poly1305_state_internal_t* st = (poly1305_state_internal_t*)ctx; + + /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ + st->r[0] = (U8TO32(&key[0])) & 0x3ffffff; + st->r[1] = (U8TO32(&key[3]) >> 2) & 0x3ffff03; + st->r[2] = (U8TO32(&key[6]) >> 4) & 0x3ffc0ff; + st->r[3] = (U8TO32(&key[9]) >> 6) & 0x3f03fff; + st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff; + + /* h = 0 */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + + /* save pad for later */ + st->pad[0] = U8TO32(&key[16]); + st->pad[1] = U8TO32(&key[20]); + st->pad[2] = U8TO32(&key[24]); + st->pad[3] = U8TO32(&key[28]); + + st->leftover = 0; + st->final = 0; +} + +static void poly1305_blocks(poly1305_state_internal_t* st, const unsigned char* m, size_t bytes) { + const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */ + unsigned long r0, r1, r2, r3, r4; + unsigned long s1, s2, s3, s4; + unsigned long h0, h1, h2, h3, h4; + unsigned long long d0, d1, d2, d3, d4; + unsigned long c; + + r0 = st->r[0]; + r1 = st->r[1]; + r2 = st->r[2]; + r3 = st->r[3]; + r4 = st->r[4]; + + s1 = r1 * 5; + s2 = r2 * 5; + s3 = r3 * 5; + s4 = r4 * 5; + + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + while(bytes >= poly1305_block_size) { + /* h += m[i] */ + h0 += (U8TO32(m + 0)) & 0x3ffffff; + h1 += (U8TO32(m + 3) >> 2) & 0x3ffffff; + h2 += (U8TO32(m + 6) >> 4) & 0x3ffffff; + h3 += (U8TO32(m + 9) >> 6) & 0x3ffffff; + h4 += (U8TO32(m + 12) >> 8) | hibit; + + /* h *= r */ + d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + + ((unsigned long long)h4 * s1); + d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + + ((unsigned long long)h4 * s2); + d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + + ((unsigned long long)h4 * s3); + d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + + ((unsigned long long)h4 * s4); + d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + + ((unsigned long long)h4 * r0); + + /* (partial) h %= p */ + c = (unsigned long)(d0 >> 26); + h0 = (unsigned long)d0 & 0x3ffffff; + d1 += c; + c = (unsigned long)(d1 >> 26); + h1 = (unsigned long)d1 & 0x3ffffff; + d2 += c; + c = (unsigned long)(d2 >> 26); + h2 = (unsigned long)d2 & 0x3ffffff; + d3 += c; + c = (unsigned long)(d3 >> 26); + h3 = (unsigned long)d3 & 0x3ffffff; + d4 += c; + c = (unsigned long)(d4 >> 26); + h4 = (unsigned long)d4 & 0x3ffffff; + h0 += c * 5; + c = (h0 >> 26); + h0 = h0 & 0x3ffffff; + h1 += c; + + m += poly1305_block_size; + bytes -= poly1305_block_size; + } + + st->h[0] = h0; + st->h[1] = h1; + st->h[2] = h2; + st->h[3] = h3; + st->h[4] = h4; +} + +POLY1305_NOINLINE void poly1305_finish(poly1305_context* ctx, unsigned char mac[16]) { + poly1305_state_internal_t* st = (poly1305_state_internal_t*)ctx; + unsigned long h0, h1, h2, h3, h4, c; + unsigned long g0, g1, g2, g3, g4; + unsigned long long f; + unsigned long mask; + + /* process the remaining block */ + if(st->leftover) { + size_t i = st->leftover; + st->buffer[i++] = 1; + for(; i < poly1305_block_size; i++) st->buffer[i] = 0; + st->final = 1; + poly1305_blocks(st, st->buffer, poly1305_block_size); + } + + /* fully carry h */ + h0 = st->h[0]; + h1 = st->h[1]; + h2 = st->h[2]; + h3 = st->h[3]; + h4 = st->h[4]; + + c = h1 >> 26; + h1 = h1 & 0x3ffffff; + h2 += c; + c = h2 >> 26; + h2 = h2 & 0x3ffffff; + h3 += c; + c = h3 >> 26; + h3 = h3 & 0x3ffffff; + h4 += c; + c = h4 >> 26; + h4 = h4 & 0x3ffffff; + h0 += c * 5; + c = h0 >> 26; + h0 = h0 & 0x3ffffff; + h1 += c; + + /* compute h + -p */ + g0 = h0 + 5; + c = g0 >> 26; + g0 &= 0x3ffffff; + g1 = h1 + c; + c = g1 >> 26; + g1 &= 0x3ffffff; + g2 = h2 + c; + c = g2 >> 26; + g2 &= 0x3ffffff; + g3 = h3 + c; + c = g3 >> 26; + g3 &= 0x3ffffff; + g4 = h4 + c - (1UL << 26); + + /* select h if h < p, or h + -p if h >= p */ + mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1; + g0 &= mask; + g1 &= mask; + g2 &= mask; + g3 &= mask; + g4 &= mask; + mask = ~mask; + h0 = (h0 & mask) | g0; + h1 = (h1 & mask) | g1; + h2 = (h2 & mask) | g2; + h3 = (h3 & mask) | g3; + h4 = (h4 & mask) | g4; + + /* h = h % (2^128) */ + h0 = ((h0) | (h1 << 26)) & 0xffffffff; + h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; + h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; + h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; + + /* mac = (h + pad) % (2^128) */ + f = (unsigned long long)h0 + st->pad[0]; + h0 = (unsigned long)f; + f = (unsigned long long)h1 + st->pad[1] + (f >> 32); + h1 = (unsigned long)f; + f = (unsigned long long)h2 + st->pad[2] + (f >> 32); + h2 = (unsigned long)f; + f = (unsigned long long)h3 + st->pad[3] + (f >> 32); + h3 = (unsigned long)f; + + U32TO8(mac + 0, h0); + U32TO8(mac + 4, h1); + U32TO8(mac + 8, h2); + U32TO8(mac + 12, h3); + + /* zero out the state */ + st->h[0] = 0; + st->h[1] = 0; + st->h[2] = 0; + st->h[3] = 0; + st->h[4] = 0; + st->r[0] = 0; + st->r[1] = 0; + st->r[2] = 0; + st->r[3] = 0; + st->r[4] = 0; + st->pad[0] = 0; + st->pad[1] = 0; + st->pad[2] = 0; + st->pad[3] = 0; +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.c b/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.c new file mode 100644 index 000000000..f26026fbc --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.c @@ -0,0 +1,46 @@ +// Implementation of the ChaCha20 + Poly1305 AEAD construction +// as described in RFC 7539. + +#include +#include "rfc7539.h" +#include "ecrypt_portable.h" + +// Initialize the ChaCha20 + Poly1305 context for encryption or decryption +// using a 32 byte key and 12 byte nonce as in the RFC 7539 style. +void rfc7539_init(chacha20poly1305_ctx* ctx, const uint8_t key[32], const uint8_t nonce[12]) { + unsigned char block0[64] = {0}; + + ECRYPT_keysetup(&ctx->chacha20, key, 256, 16); + ctx->chacha20.input[12] = 0; + ctx->chacha20.input[13] = U8TO32_LITTLE(nonce + 0); + ctx->chacha20.input[14] = U8TO32_LITTLE(nonce + 4); + ctx->chacha20.input[15] = U8TO32_LITTLE(nonce + 8); + + // Encrypt 64 bytes of zeros and use the first 32 bytes + // as the Poly1305 key. + ECRYPT_encrypt_bytes(&ctx->chacha20, block0, block0, 64); + poly1305_init(&ctx->poly1305, block0); +} + +// Include authenticated data in the Poly1305 MAC using the RFC 7539 +// style with 16 byte padding. This must only be called once and prior +// to encryption or decryption. +void rfc7539_auth(chacha20poly1305_ctx* ctx, const uint8_t* in, size_t n) { + uint8_t padding[16] = {0}; + poly1305_update(&ctx->poly1305, in, n); + if(n % 16 != 0) poly1305_update(&ctx->poly1305, padding, 16 - n % 16); +} + +// Compute RFC 7539-style Poly1305 MAC. +void rfc7539_finish(chacha20poly1305_ctx* ctx, int64_t alen, int64_t plen, uint8_t mac[16]) { + uint8_t padding[16] = {0}; + uint8_t lengths[16] = {0}; + + memcpy(lengths, &alen, sizeof(int64_t)); + memcpy(lengths + 8, &plen, sizeof(int64_t)); + + if(plen % 16 != 0) poly1305_update(&ctx->poly1305, padding, 16 - plen % 16); + poly1305_update(&ctx->poly1305, lengths, 16); + + poly1305_finish(&ctx->poly1305, mac); +} diff --git a/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.h b/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.h new file mode 100644 index 000000000..262dc433b --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha20poly1305/rfc7539.h @@ -0,0 +1,10 @@ +#ifndef RFC7539_H +#define RFC7539_H + +#include "chacha20poly1305.h" + +void rfc7539_init(chacha20poly1305_ctx* ctx, const uint8_t key[32], const uint8_t nonce[12]); +void rfc7539_auth(chacha20poly1305_ctx* ctx, const uint8_t* in, size_t n); +void rfc7539_finish(chacha20poly1305_ctx* ctx, int64_t alen, int64_t plen, uint8_t mac[16]); + +#endif // RFC7539_H diff --git a/applications/external/flipbip/lib/crypto/chacha_drbg.c b/applications/external/flipbip/lib/crypto/chacha_drbg.c new file mode 100644 index 000000000..fe9b5fd40 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha_drbg.c @@ -0,0 +1,131 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "chacha_drbg.h" + +#include +#include +#include + +#include "chacha20poly1305/ecrypt_portable.h" +#include "memzero.h" +#include "sha2.h" + +#define CHACHA_DRBG_KEY_LENGTH 32 +#define CHACHA_DRBG_COUNTER_LENGTH 8 +#define CHACHA_DRBG_IV_LENGTH 8 +#define CHACHA_DRBG_SEED_LENGTH \ + (CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_COUNTER_LENGTH + CHACHA_DRBG_IV_LENGTH) + +#define MAX(a, b) (a) > (b) ? (a) : (b) + +static void derivation_function( + const uint8_t* input1, + size_t input1_length, + const uint8_t* input2, + size_t input2_length, + uint8_t* output, + size_t output_length) { + // Implementation of Hash_df from NIST SP 800-90A + uint32_t block_count = (output_length - 1) / SHA256_DIGEST_LENGTH + 1; + size_t partial_block_length = output_length % SHA256_DIGEST_LENGTH; + assert(block_count <= 255); + + uint32_t output_length_bits = output_length * 8; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(output_length_bits, output_length_bits); +#endif + + SHA256_CTX ctx = {0}; + + for(uint8_t counter = 1; counter <= block_count; counter++) { + sha256_Init(&ctx); + sha256_Update(&ctx, &counter, sizeof(counter)); + sha256_Update(&ctx, (uint8_t*)&output_length_bits, sizeof(output_length_bits)); + sha256_Update(&ctx, input1, input1_length); + sha256_Update(&ctx, input2, input2_length); + + if(counter != block_count || partial_block_length == 0) { + sha256_Final(&ctx, output); + output += SHA256_DIGEST_LENGTH; + } else { // last block is partial + uint8_t digest[SHA256_DIGEST_LENGTH] = {0}; + sha256_Final(&ctx, digest); + memcpy(output, digest, partial_block_length); + memzero(digest, sizeof(digest)); + } + } + + memzero(&ctx, sizeof(ctx)); +} + +void chacha_drbg_init( + CHACHA_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_length, + const uint8_t* nonce, + size_t nonce_length) { + uint8_t buffer[MAX(CHACHA_DRBG_KEY_LENGTH, CHACHA_DRBG_IV_LENGTH)] = {0}; + ECRYPT_keysetup( + &ctx->chacha_ctx, buffer, CHACHA_DRBG_KEY_LENGTH * 8, CHACHA_DRBG_IV_LENGTH * 8); + ECRYPT_ivsetup(&ctx->chacha_ctx, buffer); + + chacha_drbg_reseed(ctx, entropy, entropy_length, nonce, nonce_length); +} + +static void chacha_drbg_update(CHACHA_DRBG_CTX* ctx, const uint8_t data[CHACHA_DRBG_SEED_LENGTH]) { + uint8_t seed[CHACHA_DRBG_SEED_LENGTH] = {0}; + + if(data) + ECRYPT_encrypt_bytes(&ctx->chacha_ctx, data, seed, CHACHA_DRBG_SEED_LENGTH); + else + ECRYPT_keystream_bytes(&ctx->chacha_ctx, seed, CHACHA_DRBG_SEED_LENGTH); + + ECRYPT_keysetup(&ctx->chacha_ctx, seed, CHACHA_DRBG_KEY_LENGTH * 8, CHACHA_DRBG_IV_LENGTH * 8); + + ECRYPT_ivsetup(&ctx->chacha_ctx, seed + CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_COUNTER_LENGTH); + + ECRYPT_ctrsetup(&ctx->chacha_ctx, seed + CHACHA_DRBG_KEY_LENGTH); + + memzero(seed, sizeof(seed)); +} + +void chacha_drbg_generate(CHACHA_DRBG_CTX* ctx, uint8_t* output, size_t output_length) { + assert(output_length < 65536); + assert(ctx->reseed_counter + 1 != 0); + + ECRYPT_keystream_bytes(&ctx->chacha_ctx, output, output_length); + chacha_drbg_update(ctx, NULL); + ctx->reseed_counter++; +} + +void chacha_drbg_reseed( + CHACHA_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_length, + const uint8_t* additional_input, + size_t additional_input_length) { + uint8_t seed[CHACHA_DRBG_SEED_LENGTH] = {0}; + derivation_function( + entropy, entropy_length, additional_input, additional_input_length, seed, sizeof(seed)); + chacha_drbg_update(ctx, seed); + memzero(seed, sizeof(seed)); + + ctx->reseed_counter = 1; +} diff --git a/applications/external/flipbip/lib/crypto/chacha_drbg.h b/applications/external/flipbip/lib/crypto/chacha_drbg.h new file mode 100644 index 000000000..740c11c6c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/chacha_drbg.h @@ -0,0 +1,59 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __CHACHA_DRBG__ +#define __CHACHA_DRBG__ + +#include "chacha20poly1305/chacha20poly1305.h" +#include "sha2.h" + +// A very fast deterministic random bit generator based on CTR_DRBG in NIST SP +// 800-90A. Chacha is used instead of a block cipher in the counter mode, SHA256 +// is used as a derivation function. The highest supported security strength is +// at least 256 bits. Reseeding is left up to caller. + +// Length of inputs of chacha_drbg_init (entropy and nonce) or +// chacha_drbg_reseed (entropy and additional_input) that fill exactly +// block_count blocks of hash function in derivation_function. There is no need +// the input to have this length, it's just an optimalization. +#define CHACHA_DRBG_OPTIMAL_RESEED_LENGTH(block_count) \ + ((block_count)*SHA256_BLOCK_LENGTH - 1 - 4 - 9) +// 1 = sizeof(counter), 4 = sizeof(output_length) in +// derivation_function, 9 is length of SHA256 padding of message +// aligned to bytes + +typedef struct _CHACHA_DRBG_CTX { + ECRYPT_ctx chacha_ctx; + uint32_t reseed_counter; +} CHACHA_DRBG_CTX; + +void chacha_drbg_init( + CHACHA_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_length, + const uint8_t* nonce, + size_t nonce_length); +void chacha_drbg_generate(CHACHA_DRBG_CTX* ctx, uint8_t* output, size_t output_length); +void chacha_drbg_reseed( + CHACHA_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_length, + const uint8_t* additional_input, + size_t additional_input_length); +#endif // __CHACHA_DRBG__ diff --git a/applications/external/flipbip/lib/crypto/check_mem.h b/applications/external/flipbip/lib/crypto/check_mem.h new file mode 100644 index 000000000..ef8f8c79a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/check_mem.h @@ -0,0 +1,34 @@ +#ifndef CHECK_MEM_H +#define CHECK_MEM_H + +#if CHECK_MAJOR_VERSION == 0 && CHECK_MINOR_VERSION < 11 + +#define _ck_assert_mem(X, Y, L, OP) \ + do { \ + const char* _ck_x = (const char*)(void*)(X); \ + const char* _ck_y = (const char*)(void*)(Y); \ + size_t _ck_l = (L); \ + char _ck_x_str[129]; \ + char _ck_y_str[129]; \ + static char _ck_hexdigits[] = "0123456789abcdef"; \ + size_t _ck_i; \ + for(_ck_i = 0; _ck_i < ((_ck_l > 64) ? 64 : _ck_l); _ck_i++) { \ + _ck_x_str[_ck_i * 2] = _ck_hexdigits[(_ck_x[_ck_i] >> 4) & 0xF]; \ + _ck_y_str[_ck_i * 2] = _ck_hexdigits[(_ck_y[_ck_i] >> 4) & 0xF]; \ + _ck_x_str[_ck_i * 2 + 1] = _ck_hexdigits[_ck_x[_ck_i] & 0xF]; \ + _ck_y_str[_ck_i * 2 + 1] = _ck_hexdigits[_ck_y[_ck_i] & 0xF]; \ + } \ + _ck_x_str[_ck_i * 2] = 0; \ + _ck_y_str[_ck_i * 2] = 0; \ + ck_assert_msg( \ + 0 OP memcmp(_ck_y, _ck_x, _ck_l), \ + "Assertion '" #X #OP #Y "' failed: " #X "==\"%s\", " #Y "==\"%s\"", \ + _ck_x_str, \ + _ck_y_str); \ + } while(0) +#define ck_assert_mem_eq(X, Y, L) _ck_assert_mem(X, Y, L, ==) +#define ck_assert_mem_ne(X, Y, L) _ck_assert_mem(X, Y, L, !=) + +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/curves.c b/applications/external/flipbip/lib/crypto/curves.c new file mode 100644 index 000000000..dcdaf7ba4 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/curves.c @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2016 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "curves.h" + +const char SECP256K1_NAME[] = "secp256k1"; +const char SECP256K1_DECRED_NAME[] = "secp256k1-decred"; +const char SECP256K1_GROESTL_NAME[] = "secp256k1-groestl"; +const char SECP256K1_SMART_NAME[] = "secp256k1-smart"; +const char NIST256P1_NAME[] = "nist256p1"; +const char ED25519_NAME[] = "ed25519"; +const char ED25519_SEED_NAME[] = "ed25519 seed"; +#if USE_CARDANO +const char ED25519_CARDANO_NAME[] = "ed25519 cardano seed"; +#endif +const char ED25519_SHA3_NAME[] = "ed25519-sha3"; +#if USE_KECCAK +const char ED25519_KECCAK_NAME[] = "ed25519-keccak"; +#endif +const char CURVE25519_NAME[] = "curve25519"; diff --git a/applications/external/flipbip/lib/crypto/curves.h b/applications/external/flipbip/lib/crypto/curves.h new file mode 100644 index 000000000..aabf5b369 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/curves.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2016 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CURVES_H__ +#define __CURVES_H__ + +#include "options.h" + +extern const char SECP256K1_NAME[]; +extern const char SECP256K1_DECRED_NAME[]; +extern const char SECP256K1_GROESTL_NAME[]; +extern const char SECP256K1_SMART_NAME[]; +extern const char NIST256P1_NAME[]; +extern const char ED25519_NAME[]; +extern const char ED25519_SEED_NAME[]; +extern const char ED25519_CARDANO_NAME[]; +extern const char ED25519_SHA3_NAME[]; +#if USE_KECCAK +extern const char ED25519_KECCAK_NAME[]; +#endif +extern const char CURVE25519_NAME[]; + +#endif diff --git a/applications/external/flipbip/lib/crypto/ecdsa.c b/applications/external/flipbip/lib/crypto/ecdsa.c new file mode 100644 index 000000000..80ab76b49 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ecdsa.c @@ -0,0 +1,1284 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "address.h" +#include "base58.h" +#include "bignum.h" +#include "ecdsa.h" +#include "hmac.h" +#include "memzero.h" +#include "rand.h" +#include "rfc6979.h" +#include "secp256k1.h" + +// Set cp2 = cp1 +void point_copy(const curve_point* cp1, curve_point* cp2) { + *cp2 = *cp1; +} + +// cp2 = cp1 + cp2 +void point_add(const ecdsa_curve* curve, const curve_point* cp1, curve_point* cp2) { + bignum256 lambda = {0}, inv = {0}, xr = {0}, yr = {0}; + + if(point_is_infinity(cp1)) { + return; + } + if(point_is_infinity(cp2)) { + point_copy(cp1, cp2); + return; + } + if(point_is_equal(cp1, cp2)) { + point_double(curve, cp2); + return; + } + if(point_is_negative_of(cp1, cp2)) { + point_set_infinity(cp2); + return; + } + + // lambda = (y2 - y1) / (x2 - x1) + bn_subtractmod(&(cp2->x), &(cp1->x), &inv, &curve->prime); + bn_inverse(&inv, &curve->prime); + bn_subtractmod(&(cp2->y), &(cp1->y), &lambda, &curve->prime); + bn_multiply(&inv, &lambda, &curve->prime); + + // xr = lambda^2 - x1 - x2 + xr = lambda; + bn_multiply(&xr, &xr, &curve->prime); + yr = cp1->x; + bn_addmod(&yr, &(cp2->x), &curve->prime); + bn_subtractmod(&xr, &yr, &xr, &curve->prime); + bn_fast_mod(&xr, &curve->prime); + bn_mod(&xr, &curve->prime); + + // yr = lambda (x1 - xr) - y1 + bn_subtractmod(&(cp1->x), &xr, &yr, &curve->prime); + bn_multiply(&lambda, &yr, &curve->prime); + bn_subtractmod(&yr, &(cp1->y), &yr, &curve->prime); + bn_fast_mod(&yr, &curve->prime); + bn_mod(&yr, &curve->prime); + + cp2->x = xr; + cp2->y = yr; +} + +// cp = cp + cp +void point_double(const ecdsa_curve* curve, curve_point* cp) { + bignum256 lambda = {0}, xr = {0}, yr = {0}; + + if(point_is_infinity(cp)) { + return; + } + if(bn_is_zero(&(cp->y))) { + point_set_infinity(cp); + return; + } + + // lambda = (3 x^2 + a) / (2 y) + lambda = cp->y; + bn_mult_k(&lambda, 2, &curve->prime); + bn_fast_mod(&lambda, &curve->prime); + bn_mod(&lambda, &curve->prime); + bn_inverse(&lambda, &curve->prime); + + xr = cp->x; + bn_multiply(&xr, &xr, &curve->prime); + bn_mult_k(&xr, 3, &curve->prime); + bn_subi(&xr, -curve->a, &curve->prime); + bn_multiply(&xr, &lambda, &curve->prime); + + // xr = lambda^2 - 2*x + xr = lambda; + bn_multiply(&xr, &xr, &curve->prime); + yr = cp->x; + bn_lshift(&yr); + bn_subtractmod(&xr, &yr, &xr, &curve->prime); + bn_fast_mod(&xr, &curve->prime); + bn_mod(&xr, &curve->prime); + + // yr = lambda (x - xr) - y + bn_subtractmod(&(cp->x), &xr, &yr, &curve->prime); + bn_multiply(&lambda, &yr, &curve->prime); + bn_subtractmod(&yr, &(cp->y), &yr, &curve->prime); + bn_fast_mod(&yr, &curve->prime); + bn_mod(&yr, &curve->prime); + + cp->x = xr; + cp->y = yr; +} + +// set point to internal representation of point at infinity +void point_set_infinity(curve_point* p) { + bn_zero(&(p->x)); + bn_zero(&(p->y)); +} + +// return true iff p represent point at infinity +// both coords are zero in internal representation +int point_is_infinity(const curve_point* p) { + return bn_is_zero(&(p->x)) && bn_is_zero(&(p->y)); +} + +// return true iff both points are equal +int point_is_equal(const curve_point* p, const curve_point* q) { + return bn_is_equal(&(p->x), &(q->x)) && bn_is_equal(&(p->y), &(q->y)); +} + +// returns true iff p == -q +// expects p and q be valid points on curve other than point at infinity +int point_is_negative_of(const curve_point* p, const curve_point* q) { + // if P == (x, y), then -P would be (x, -y) on this curve + if(!bn_is_equal(&(p->x), &(q->x))) { + return 0; + } + + // we shouldn't hit this for a valid point + if(bn_is_zero(&(p->y))) { + return 0; + } + + return !bn_is_equal(&(p->y), &(q->y)); +} + +typedef struct jacobian_curve_point { + bignum256 x, y, z; +} jacobian_curve_point; + +// generate random K for signing/side-channel noise +static void generate_k_random(bignum256* k, const bignum256* prime) { + do { + int i = 0; + for(i = 0; i < 8; i++) { + k->val[i] = random32() & ((1u << BN_BITS_PER_LIMB) - 1); + } + k->val[8] = random32() & ((1u << BN_BITS_LAST_LIMB) - 1); + // check that k is in range and not zero. + } while(bn_is_zero(k) || !bn_is_less(k, prime)); +} + +void curve_to_jacobian(const curve_point* p, jacobian_curve_point* jp, const bignum256* prime) { + // randomize z coordinate + generate_k_random(&jp->z, prime); + + jp->x = jp->z; + bn_multiply(&jp->z, &jp->x, prime); + // x = z^2 + jp->y = jp->x; + bn_multiply(&jp->z, &jp->y, prime); + // y = z^3 + + bn_multiply(&p->x, &jp->x, prime); + bn_multiply(&p->y, &jp->y, prime); +} + +void jacobian_to_curve(const jacobian_curve_point* jp, curve_point* p, const bignum256* prime) { + p->y = jp->z; + bn_inverse(&p->y, prime); + // p->y = z^-1 + p->x = p->y; + bn_multiply(&p->x, &p->x, prime); + // p->x = z^-2 + bn_multiply(&p->x, &p->y, prime); + // p->y = z^-3 + bn_multiply(&jp->x, &p->x, prime); + // p->x = jp->x * z^-2 + bn_multiply(&jp->y, &p->y, prime); + // p->y = jp->y * z^-3 + bn_mod(&p->x, prime); + bn_mod(&p->y, prime); +} + +void point_jacobian_add(const curve_point* p1, jacobian_curve_point* p2, const ecdsa_curve* curve) { + bignum256 r = {0}, h = {0}, r2 = {0}; + bignum256 hcby = {0}, hsqx = {0}; + bignum256 xz = {0}, yz = {0}, az = {0}; + int is_doubling = 0; + const bignum256* prime = &curve->prime; + int a = curve->a; + + assert(-3 <= a && a <= 0); + + /* First we bring p1 to the same denominator: + * x1' := x1 * z2^2 + * y1' := y1 * z2^3 + */ + /* + * lambda = ((y1' - y2)/z2^3) / ((x1' - x2)/z2^2) + * = (y1' - y2) / (x1' - x2) z2 + * x3/z3^2 = lambda^2 - (x1' + x2)/z2^2 + * y3/z3^3 = 1/2 lambda * (2x3/z3^2 - (x1' + x2)/z2^2) + (y1'+y2)/z2^3 + * + * For the special case x1=x2, y1=y2 (doubling) we have + * lambda = 3/2 ((x2/z2^2)^2 + a) / (y2/z2^3) + * = 3/2 (x2^2 + a*z2^4) / y2*z2) + * + * to get rid of fraction we write lambda as + * lambda = r / (h*z2) + * with r = is_doubling ? 3/2 x2^2 + az2^4 : (y1 - y2) + * h = is_doubling ? y1+y2 : (x1 - x2) + * + * With z3 = h*z2 (the denominator of lambda) + * we get x3 = lambda^2*z3^2 - (x1' + x2)/z2^2*z3^2 + * = r^2 - h^2 * (x1' + x2) + * and y3 = 1/2 r * (2x3 - h^2*(x1' + x2)) + h^3*(y1' + y2) + */ + + /* h = x1 - x2 + * r = y1 - y2 + * x3 = r^2 - h^3 - 2*h^2*x2 + * y3 = r*(h^2*x2 - x3) - h^3*y2 + * z3 = h*z2 + */ + + xz = p2->z; + bn_multiply(&xz, &xz, prime); // xz = z2^2 + yz = p2->z; + bn_multiply(&xz, &yz, prime); // yz = z2^3 + + if(a != 0) { + az = xz; + bn_multiply(&az, &az, prime); // az = z2^4 + bn_mult_k(&az, -a, prime); // az = -az2^4 + } + + bn_multiply(&p1->x, &xz, prime); // xz = x1' = x1*z2^2; + h = xz; + bn_subtractmod(&h, &p2->x, &h, prime); + bn_fast_mod(&h, prime); + // h = x1' - x2; + + bn_add(&xz, &p2->x); + // xz = x1' + x2 + + // check for h == 0 % prime. Note that h never normalizes to + // zero, since h = x1' + 2*prime - x2 > 0 and a positive + // multiple of prime is always normalized to prime by + // bn_fast_mod. + is_doubling = bn_is_equal(&h, prime); + + bn_multiply(&p1->y, &yz, prime); // yz = y1' = y1*z2^3; + bn_subtractmod(&yz, &p2->y, &r, prime); + // r = y1' - y2; + + bn_add(&yz, &p2->y); + // yz = y1' + y2 + + r2 = p2->x; + bn_multiply(&r2, &r2, prime); + bn_mult_k(&r2, 3, prime); + + if(a != 0) { + // subtract -a z2^4, i.e, add a z2^4 + bn_subtractmod(&r2, &az, &r2, prime); + } + bn_cmov(&r, is_doubling, &r2, &r); + bn_cmov(&h, is_doubling, &yz, &h); + + // hsqx = h^2 + hsqx = h; + bn_multiply(&hsqx, &hsqx, prime); + + // hcby = h^3 + hcby = h; + bn_multiply(&hsqx, &hcby, prime); + + // hsqx = h^2 * (x1 + x2) + bn_multiply(&xz, &hsqx, prime); + + // hcby = h^3 * (y1 + y2) + bn_multiply(&yz, &hcby, prime); + + // z3 = h*z2 + bn_multiply(&h, &p2->z, prime); + + // x3 = r^2 - h^2 (x1 + x2) + p2->x = r; + bn_multiply(&p2->x, &p2->x, prime); + bn_subtractmod(&p2->x, &hsqx, &p2->x, prime); + bn_fast_mod(&p2->x, prime); + + // y3 = 1/2 (r*(h^2 (x1 + x2) - 2x3) - h^3 (y1 + y2)) + bn_subtractmod(&hsqx, &p2->x, &p2->y, prime); + bn_subtractmod(&p2->y, &p2->x, &p2->y, prime); + bn_multiply(&r, &p2->y, prime); + bn_subtractmod(&p2->y, &hcby, &p2->y, prime); + bn_mult_half(&p2->y, prime); + bn_fast_mod(&p2->y, prime); +} + +void point_jacobian_double(jacobian_curve_point* p, const ecdsa_curve* curve) { + bignum256 az4 = {0}, m = {0}, msq = {0}, ysq = {0}, xysq = {0}; + const bignum256* prime = &curve->prime; + + assert(-3 <= curve->a && curve->a <= 0); + /* usual algorithm: + * + * lambda = (3((x/z^2)^2 + a) / 2y/z^3) = (3x^2 + az^4)/2yz + * x3/z3^2 = lambda^2 - 2x/z^2 + * y3/z3^3 = lambda * (x/z^2 - x3/z3^2) - y/z^3 + * + * to get rid of fraction we set + * m = (3 x^2 + az^4) / 2 + * Hence, + * lambda = m / yz = m / z3 + * + * With z3 = yz (the denominator of lambda) + * we get x3 = lambda^2*z3^2 - 2*x/z^2*z3^2 + * = m^2 - 2*xy^2 + * and y3 = (lambda * (x/z^2 - x3/z3^2) - y/z^3) * z3^3 + * = m * (xy^2 - x3) - y^4 + */ + + /* m = (3*x^2 + a z^4) / 2 + * x3 = m^2 - 2*xy^2 + * y3 = m*(xy^2 - x3) - 8y^4 + * z3 = y*z + */ + + m = p->x; + bn_multiply(&m, &m, prime); + bn_mult_k(&m, 3, prime); + + az4 = p->z; + bn_multiply(&az4, &az4, prime); + bn_multiply(&az4, &az4, prime); + bn_mult_k(&az4, -curve->a, prime); + bn_subtractmod(&m, &az4, &m, prime); + bn_mult_half(&m, prime); + + // msq = m^2 + msq = m; + bn_multiply(&msq, &msq, prime); + // ysq = y^2 + ysq = p->y; + bn_multiply(&ysq, &ysq, prime); + // xysq = xy^2 + xysq = p->x; + bn_multiply(&ysq, &xysq, prime); + + // z3 = yz + bn_multiply(&p->y, &p->z, prime); + + // x3 = m^2 - 2*xy^2 + p->x = xysq; + bn_lshift(&p->x); + bn_fast_mod(&p->x, prime); + bn_subtractmod(&msq, &p->x, &p->x, prime); + bn_fast_mod(&p->x, prime); + + // y3 = m*(xy^2 - x3) - y^4 + bn_subtractmod(&xysq, &p->x, &p->y, prime); + bn_multiply(&m, &p->y, prime); + bn_multiply(&ysq, &ysq, prime); + bn_subtractmod(&p->y, &ysq, &p->y, prime); + bn_fast_mod(&p->y, prime); +} + +// res = k * p +// returns 0 on success +int point_multiply( + const ecdsa_curve* curve, + const bignum256* k, + const curve_point* p, + curve_point* res) { + // this algorithm is loosely based on + // Katsuyuki Okeya and Tsuyoshi Takagi, The Width-w NAF Method Provides + // Small Memory and Fast Elliptic Scalar Multiplications Secure against + // Side Channel Attacks. + if(!bn_is_less(k, &curve->order)) { + return 1; + } + + int i = 0, j = 0; + static CONFIDENTIAL bignum256 a; + uint32_t* aptr = NULL; + uint32_t abits = 0; + int ashift = 0; + uint32_t is_even = (k->val[0] & 1) - 1; + uint32_t bits = {0}, sign = {0}, nsign = {0}; + static CONFIDENTIAL jacobian_curve_point jres; + curve_point pmult[8] = {0}; + const bignum256* prime = &curve->prime; + + // is_even = 0xffffffff if k is even, 0 otherwise. + + // add 2^256. + // make number odd: subtract curve->order if even + uint32_t tmp = 1; + uint32_t is_non_zero = 0; + for(j = 0; j < 8; j++) { + is_non_zero |= k->val[j]; + tmp += (BN_BASE - 1) + k->val[j] - (curve->order.val[j] & is_even); + a.val[j] = tmp & (BN_BASE - 1); + tmp >>= BN_BITS_PER_LIMB; + } + is_non_zero |= k->val[j]; + a.val[j] = tmp + 0xffffff + k->val[j] - (curve->order.val[j] & is_even); + assert((a.val[0] & 1) != 0); + + // special case 0*p: just return zero. We don't care about constant time. + if(!is_non_zero) { + point_set_infinity(res); + return 1; + } + + // Now a = k + 2^256 (mod curve->order) and a is odd. + // + // The idea is to bring the new a into the form. + // sum_{i=0..64} a[i] 16^i, where |a[i]| < 16 and a[i] is odd. + // a[0] is odd, since a is odd. If a[i] would be even, we can + // add 1 to it and subtract 16 from a[i-1]. Afterwards, + // a[64] = 1, which is the 2^256 that we added before. + // + // Since k = a - 2^256 (mod curve->order), we can compute + // k*p = sum_{i=0..63} a[i] 16^i * p + // + // We compute |a[i]| * p in advance for all possible + // values of |a[i]| * p. pmult[i] = (2*i+1) * p + // We compute p, 3*p, ..., 15*p and store it in the table pmult. + // store p^2 temporarily in pmult[7] + pmult[7] = *p; + point_double(curve, &pmult[7]); + // compute 3*p, etc by repeatedly adding p^2. + pmult[0] = *p; + for(i = 1; i < 8; i++) { + pmult[i] = pmult[7]; + point_add(curve, &pmult[i - 1], &pmult[i]); + } + + // now compute res = sum_{i=0..63} a[i] * 16^i * p step by step, + // starting with i = 63. + // initialize jres = |a[63]| * p. + // Note that a[i] = a>>(4*i) & 0xf if (a&0x10) != 0 + // and - (16 - (a>>(4*i) & 0xf)) otherwise. We can compute this as + // ((a ^ (((a >> 4) & 1) - 1)) & 0xf) >> 1 + // since a is odd. + aptr = &a.val[8]; + abits = *aptr; + ashift = 256 - (BN_BITS_PER_LIMB * 8) - 4; + bits = abits >> ashift; + sign = (bits >> 4) - 1; + bits ^= sign; + bits &= 15; + curve_to_jacobian(&pmult[bits >> 1], &jres, prime); + for(i = 62; i >= 0; i--) { + // sign = sign(a[i+1]) (0xffffffff for negative, 0 for positive) + // invariant jres = (-1)^sign sum_{j=i+1..63} (a[j] * 16^{j-i-1} * p) + // abits >> (ashift - 4) = lowbits(a >> (i*4)) + + point_jacobian_double(&jres, curve); + point_jacobian_double(&jres, curve); + point_jacobian_double(&jres, curve); + point_jacobian_double(&jres, curve); + + // get lowest 5 bits of a >> (i*4). + ashift -= 4; + if(ashift < 0) { + // the condition only depends on the iteration number and + // leaks no private information to a side-channel. + bits = abits << (-ashift); + abits = *(--aptr); + ashift += BN_BITS_PER_LIMB; + bits |= abits >> ashift; + } else { + bits = abits >> ashift; + } + bits &= 31; + nsign = (bits >> 4) - 1; + bits ^= nsign; + bits &= 15; + + // negate last result to make signs of this round and the + // last round equal. + bn_cnegate((sign ^ nsign) & 1, &jres.z, prime); + + // add odd factor + point_jacobian_add(&pmult[bits >> 1], &jres, curve); + sign = nsign; + } + bn_cnegate(sign & 1, &jres.z, prime); + jacobian_to_curve(&jres, res, prime); + memzero(&a, sizeof(a)); + memzero(&jres, sizeof(jres)); + + return 0; +} + +#if USE_PRECOMPUTED_CP + +// res = k * G +// k must be a normalized number with 0 <= k < curve->order +// returns 0 on success +int scalar_multiply(const ecdsa_curve* curve, const bignum256* k, curve_point* res) { + if(!bn_is_less(k, &curve->order)) { + return 1; + } + + int i = {0}, j = {0}; + static CONFIDENTIAL bignum256 a; + uint32_t is_even = (k->val[0] & 1) - 1; + uint32_t lowbits = 0; + static CONFIDENTIAL jacobian_curve_point jres; + const bignum256* prime = &curve->prime; + + // is_even = 0xffffffff if k is even, 0 otherwise. + + // add 2^256. + // make number odd: subtract curve->order if even + uint32_t tmp = 1; + uint32_t is_non_zero = 0; + for(j = 0; j < 8; j++) { + is_non_zero |= k->val[j]; + tmp += (BN_BASE - 1) + k->val[j] - (curve->order.val[j] & is_even); + a.val[j] = tmp & (BN_BASE - 1); + tmp >>= BN_BITS_PER_LIMB; + } + is_non_zero |= k->val[j]; + a.val[j] = tmp + 0xffffff + k->val[j] - (curve->order.val[j] & is_even); + assert((a.val[0] & 1) != 0); + + // special case 0*G: just return zero. We don't care about constant time. + if(!is_non_zero) { + point_set_infinity(res); + return 0; + } + + // Now a = k + 2^256 (mod curve->order) and a is odd. + // + // The idea is to bring the new a into the form. + // sum_{i=0..64} a[i] 16^i, where |a[i]| < 16 and a[i] is odd. + // a[0] is odd, since a is odd. If a[i] would be even, we can + // add 1 to it and subtract 16 from a[i-1]. Afterwards, + // a[64] = 1, which is the 2^256 that we added before. + // + // Since k = a - 2^256 (mod curve->order), we can compute + // k*G = sum_{i=0..63} a[i] 16^i * G + // + // We have a big table curve->cp that stores all possible + // values of |a[i]| 16^i * G. + // curve->cp[i][j] = (2*j+1) * 16^i * G + + // now compute res = sum_{i=0..63} a[i] * 16^i * G step by step. + // initial res = |a[0]| * G. Note that a[0] = a & 0xf if (a&0x10) != 0 + // and - (16 - (a & 0xf)) otherwise. We can compute this as + // ((a ^ (((a >> 4) & 1) - 1)) & 0xf) >> 1 + // since a is odd. + lowbits = a.val[0] & ((1 << 5) - 1); + lowbits ^= (lowbits >> 4) - 1; + lowbits &= 15; + curve_to_jacobian(&curve->cp[0][lowbits >> 1], &jres, prime); + for(i = 1; i < 64; i++) { + // invariant res = sign(a[i-1]) sum_{j=0..i-1} (a[j] * 16^j * G) + + // shift a by 4 places. + for(j = 0; j < 8; j++) { + a.val[j] = (a.val[j] >> 4) | ((a.val[j + 1] & 0xf) << (BN_BITS_PER_LIMB - 4)); + } + a.val[j] >>= 4; + // a = old(a)>>(4*i) + // a is even iff sign(a[i-1]) = -1 + + lowbits = a.val[0] & ((1 << 5) - 1); + lowbits ^= (lowbits >> 4) - 1; + lowbits &= 15; + // negate last result to make signs of this round and the + // last round equal. + bn_cnegate(~lowbits & 1, &jres.y, prime); + + // add odd factor + point_jacobian_add(&curve->cp[i][lowbits >> 1], &jres, curve); + } + bn_cnegate(~(a.val[0] >> 4) & 1, &jres.y, prime); + jacobian_to_curve(&jres, res, prime); + memzero(&a, sizeof(a)); + memzero(&jres, sizeof(jres)); + + return 0; +} + +#else + +int scalar_multiply(const ecdsa_curve* curve, const bignum256* k, curve_point* res) { + return point_multiply(curve, k, &curve->G, res); +} + +#endif + +int ecdh_multiply( + const ecdsa_curve* curve, + const uint8_t* priv_key, + const uint8_t* pub_key, + uint8_t* session_key) { + curve_point point = {0}; + if(!ecdsa_read_pubkey(curve, pub_key, &point)) { + return 1; + } + + bignum256 k = {0}; + bn_read_be(priv_key, &k); + if(bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + return 2; + } + + point_multiply(curve, &k, &point, &point); + memzero(&k, sizeof(k)); + + session_key[0] = 0x04; + bn_write_be(&point.x, session_key + 1); + bn_write_be(&point.y, session_key + 33); + memzero(&point, sizeof(point)); + + return 0; +} + +// msg is a data to be signed +// msg_len is the message length +int ecdsa_sign( + const ecdsa_curve* curve, + HasherType hasher_sign, + const uint8_t* priv_key, + const uint8_t* msg, + uint32_t msg_len, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])) { + uint8_t hash[32] = {0}; + hasher_Raw(hasher_sign, msg, msg_len, hash); + int res = ecdsa_sign_digest(curve, priv_key, hash, sig, pby, is_canonical); + memzero(hash, sizeof(hash)); + return res; +} + +// uses secp256k1 curve +// priv_key is a 32 byte big endian stored number +// sig is 64 bytes long array for the signature +// digest is 32 bytes of digest +// is_canonical is an optional function that checks if the signature +// conforms to additional coin-specific rules. +int ecdsa_sign_digest( + const ecdsa_curve* curve, + const uint8_t* priv_key, + const uint8_t* digest, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])) { + int i = 0; + curve_point R = {0}; + bignum256 k = {0}, z = {0}, randk = {0}; + bignum256* s = &R.y; + uint8_t by; // signature recovery byte + +#if USE_RFC6979 + rfc6979_state rng = {0}; + init_rfc6979(priv_key, digest, curve, &rng); +#endif + + bn_read_be(digest, &z); + if(bn_is_zero(&z)) { + // The probability of the digest being all-zero by chance is infinitesimal, + // so this is most likely an indication of a bug. Furthermore, the signature + // has no value, because in this case it can be easily forged for any public + // key, see ecdsa_verify_digest(). + return 1; + } + + for(i = 0; i < 10000; i++) { +#if USE_RFC6979 + // generate K deterministically + generate_k_rfc6979(&k, &rng); + // if k is too big or too small, we don't like it + if(bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + continue; + } +#else + // generate random number k + generate_k_random(&k, &curve->order); +#endif + + // compute k*G + scalar_multiply(curve, &k, &R); + by = R.y.val[0] & 1; + // r = (rx mod n) + if(!bn_is_less(&R.x, &curve->order)) { + bn_subtract(&R.x, &curve->order, &R.x); + by |= 2; + } + // if r is zero, we retry + if(bn_is_zero(&R.x)) { + continue; + } + + bn_read_be(priv_key, s); + if(bn_is_zero(s) || !bn_is_less(s, &curve->order)) { + // Invalid private key. + return 2; + } + + // randomize operations to counter side-channel attacks + generate_k_random(&randk, &curve->order); + bn_multiply(&randk, &k, &curve->order); // k*rand + bn_inverse(&k, &curve->order); // (k*rand)^-1 + bn_multiply(&R.x, s, &curve->order); // R.x*priv + bn_add(s, &z); // R.x*priv + z + bn_multiply(&k, s, &curve->order); // (k*rand)^-1 (R.x*priv + z) + bn_multiply(&randk, s, &curve->order); // k^-1 (R.x*priv + z) + bn_mod(s, &curve->order); + // if s is zero, we retry + if(bn_is_zero(s)) { + continue; + } + + // if S > order/2 => S = -S + if(bn_is_less(&curve->order_half, s)) { + bn_subtract(&curve->order, s, s); + by ^= 1; + } + // we are done, R.x and s is the result signature + bn_write_be(&R.x, sig); + bn_write_be(s, sig + 32); + + // check if the signature is acceptable or retry + if(is_canonical && !is_canonical(by, sig)) { + continue; + } + + if(pby) { + *pby = by; + } + + memzero(&k, sizeof(k)); + memzero(&randk, sizeof(randk)); +#if USE_RFC6979 + memzero(&rng, sizeof(rng)); +#endif + return 0; + } + + // Too many retries without a valid signature + // -> fail with an error + memzero(&k, sizeof(k)); + memzero(&randk, sizeof(randk)); +#if USE_RFC6979 + memzero(&rng, sizeof(rng)); +#endif + return -1; +} + +// returns 0 on success +int ecdsa_get_public_key33(const ecdsa_curve* curve, const uint8_t* priv_key, uint8_t* pub_key) { + curve_point R = {0}; + bignum256 k = {0}; + + bn_read_be(priv_key, &k); + if(bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + memzero(pub_key, 33); + return -1; + } + + // compute k*G + if(scalar_multiply(curve, &k, &R) != 0) { + memzero(&k, sizeof(k)); + return 1; + } + pub_key[0] = 0x02 | (R.y.val[0] & 0x01); + bn_write_be(&R.x, pub_key + 1); + memzero(&R, sizeof(R)); + memzero(&k, sizeof(k)); + return 0; +} + +// returns 0 on success +int ecdsa_get_public_key65(const ecdsa_curve* curve, const uint8_t* priv_key, uint8_t* pub_key) { + curve_point R = {0}; + bignum256 k = {0}; + + bn_read_be(priv_key, &k); + if(bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + memzero(pub_key, 65); + return -1; + } + + // compute k*G + if(scalar_multiply(curve, &k, &R) != 0) { + memzero(&k, sizeof(k)); + return 1; + } + pub_key[0] = 0x04; + bn_write_be(&R.x, pub_key + 1); + bn_write_be(&R.y, pub_key + 33); + memzero(&R, sizeof(R)); + memzero(&k, sizeof(k)); + return 0; +} + +int ecdsa_uncompress_pubkey( + const ecdsa_curve* curve, + const uint8_t* pub_key, + uint8_t* uncompressed) { + curve_point pub = {0}; + + if(!ecdsa_read_pubkey(curve, pub_key, &pub)) { + return 0; + } + + uncompressed[0] = 4; + bn_write_be(&pub.x, uncompressed + 1); + bn_write_be(&pub.y, uncompressed + 33); + + return 1; +} + +void ecdsa_get_pubkeyhash(const uint8_t* pub_key, HasherType hasher_pubkey, uint8_t* pubkeyhash) { + uint8_t h[HASHER_DIGEST_LENGTH] = {0}; + if(pub_key[0] == 0x04) { // uncompressed format + hasher_Raw(hasher_pubkey, pub_key, 65, h); + } else if(pub_key[0] == 0x00) { // point at infinity + hasher_Raw(hasher_pubkey, pub_key, 1, h); + } else { // expecting compressed format + hasher_Raw(hasher_pubkey, pub_key, 33, h); + } + memcpy(pubkeyhash, h, 20); + memzero(h, sizeof(h)); +} + +void ecdsa_get_address_raw( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + uint8_t* addr_raw) { + size_t prefix_len = address_prefix_bytes_len(version); + address_write_prefix_bytes(version, addr_raw); + ecdsa_get_pubkeyhash(pub_key, hasher_pubkey, addr_raw + prefix_len); +} + +void ecdsa_get_address( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize) { + uint8_t raw[MAX_ADDR_RAW_SIZE] = {0}; + size_t prefix_len = address_prefix_bytes_len(version); + ecdsa_get_address_raw(pub_key, version, hasher_pubkey, raw); + base58_encode_check(raw, 20 + prefix_len, hasher_base58, addr, addrsize); + // not as important to clear this one, but we might as well + memzero(raw, sizeof(raw)); +} + +void ecdsa_get_address_segwit_p2sh_raw( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + uint8_t* addr_raw) { + uint8_t buf[32 + 2] = {0}; + buf[0] = 0; // version byte + buf[1] = 20; // push 20 bytes + ecdsa_get_pubkeyhash(pub_key, hasher_pubkey, buf + 2); + size_t prefix_len = address_prefix_bytes_len(version); + address_write_prefix_bytes(version, addr_raw); + hasher_Raw(hasher_pubkey, buf, 22, addr_raw + prefix_len); +} + +void ecdsa_get_address_segwit_p2sh( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize) { + uint8_t raw[MAX_ADDR_RAW_SIZE] = {0}; + size_t prefix_len = address_prefix_bytes_len(version); + ecdsa_get_address_segwit_p2sh_raw(pub_key, version, hasher_pubkey, raw); + base58_encode_check(raw, prefix_len + 20, hasher_base58, addr, addrsize); + memzero(raw, sizeof(raw)); +} + +void ecdsa_get_wif( + const uint8_t* priv_key, + uint32_t version, + HasherType hasher_base58, + char* wif, + int wifsize) { + uint8_t wif_raw[MAX_WIF_RAW_SIZE] = {0}; + size_t prefix_len = address_prefix_bytes_len(version); + address_write_prefix_bytes(version, wif_raw); + memcpy(wif_raw + prefix_len, priv_key, 32); + wif_raw[prefix_len + 32] = 0x01; + base58_encode_check(wif_raw, prefix_len + 32 + 1, hasher_base58, wif, wifsize); + // private keys running around our stack can cause trouble + memzero(wif_raw, sizeof(wif_raw)); +} + +int ecdsa_address_decode( + const char* addr, + uint32_t version, + HasherType hasher_base58, + uint8_t* out) { + if(!addr) return 0; + int prefix_len = address_prefix_bytes_len(version); + return base58_decode_check(addr, hasher_base58, out, 20 + prefix_len) == 20 + prefix_len && + address_check_prefix(out, version); +} + +void compress_coords(const curve_point* cp, uint8_t* compressed) { + compressed[0] = bn_is_odd(&cp->y) ? 0x03 : 0x02; + bn_write_be(&cp->x, compressed + 1); +} + +void uncompress_coords(const ecdsa_curve* curve, uint8_t odd, const bignum256* x, bignum256* y) { + // y^2 = x^3 + a*x + b + memcpy(y, x, sizeof(bignum256)); // y is x + bn_multiply(x, y, &curve->prime); // y is x^2 + bn_subi(y, -curve->a, &curve->prime); // y is x^2 + a + bn_multiply(x, y, &curve->prime); // y is x^3 + ax + bn_add(y, &curve->b); // y is x^3 + ax + b + bn_sqrt(y, &curve->prime); // y = sqrt(y) + if((odd & 0x01) != (y->val[0] & 1)) { + bn_subtract(&curve->prime, y, y); // y = -y + } +} + +int ecdsa_read_pubkey(const ecdsa_curve* curve, const uint8_t* pub_key, curve_point* pub) { + if(!curve) { + curve = &secp256k1; + } + if(pub_key[0] == 0x04) { + bn_read_be(pub_key + 1, &(pub->x)); + bn_read_be(pub_key + 33, &(pub->y)); + return ecdsa_validate_pubkey(curve, pub); + } + if(pub_key[0] == 0x02 || pub_key[0] == 0x03) { // compute missing y coords + bn_read_be(pub_key + 1, &(pub->x)); + uncompress_coords(curve, pub_key[0], &(pub->x), &(pub->y)); + return ecdsa_validate_pubkey(curve, pub); + } + // error + return 0; +} + +// Verifies that: +// - pub is not the point at infinity. +// - pub->x and pub->y are in range [0,p-1]. +// - pub is on the curve. +// We assume that all curves using this code have cofactor 1, so there is no +// need to verify that pub is a scalar multiple of G. +int ecdsa_validate_pubkey(const ecdsa_curve* curve, const curve_point* pub) { + bignum256 y_2 = {0}, x3_ax_b = {0}; + + if(point_is_infinity(pub)) { + return 0; + } + + if(!bn_is_less(&(pub->x), &curve->prime) || !bn_is_less(&(pub->y), &curve->prime)) { + return 0; + } + + memcpy(&y_2, &(pub->y), sizeof(bignum256)); + memcpy(&x3_ax_b, &(pub->x), sizeof(bignum256)); + + // y^2 + bn_multiply(&(pub->y), &y_2, &curve->prime); + bn_mod(&y_2, &curve->prime); + + // x^3 + ax + b + bn_multiply(&(pub->x), &x3_ax_b, &curve->prime); // x^2 + bn_subi(&x3_ax_b, -curve->a, &curve->prime); // x^2 + a + bn_multiply(&(pub->x), &x3_ax_b, &curve->prime); // x^3 + ax + bn_addmod(&x3_ax_b, &curve->b, &curve->prime); // x^3 + ax + b + bn_mod(&x3_ax_b, &curve->prime); + + if(!bn_is_equal(&x3_ax_b, &y_2)) { + return 0; + } + + return 1; +} + +// uses secp256k1 curve +// pub_key - 65 bytes uncompressed key +// signature - 64 bytes signature +// msg is a data that was signed +// msg_len is the message length + +int ecdsa_verify( + const ecdsa_curve* curve, + HasherType hasher_sign, + const uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* msg, + uint32_t msg_len) { + uint8_t hash[32] = {0}; + hasher_Raw(hasher_sign, msg, msg_len, hash); + int res = ecdsa_verify_digest(curve, pub_key, sig, hash); + memzero(hash, sizeof(hash)); + return res; +} + +// Compute public key from signature and recovery id. +// returns 0 if the key is successfully recovered +int ecdsa_recover_pub_from_sig( + const ecdsa_curve* curve, + uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* digest, + int recid) { + bignum256 r = {0}, s = {0}, e = {0}; + curve_point cp = {0}, cp2 = {0}; + + // read r and s + bn_read_be(sig, &r); + bn_read_be(sig + 32, &s); + if(!bn_is_less(&r, &curve->order) || bn_is_zero(&r)) { + return 1; + } + if(!bn_is_less(&s, &curve->order) || bn_is_zero(&s)) { + return 1; + } + // cp = R = k * G (k is secret nonce when signing) + memcpy(&cp.x, &r, sizeof(bignum256)); + if(recid & 2) { + bn_add(&cp.x, &curve->order); + if(!bn_is_less(&cp.x, &curve->prime)) { + return 1; + } + } + // compute y from x + uncompress_coords(curve, recid & 1, &cp.x, &cp.y); + if(!ecdsa_validate_pubkey(curve, &cp)) { + return 1; + } + // e = -digest + bn_read_be(digest, &e); + bn_mod(&e, &curve->order); + bn_subtract(&curve->order, &e, &e); + // r = r^-1 + bn_inverse(&r, &curve->order); + // e = -digest * r^-1 + bn_multiply(&r, &e, &curve->order); + bn_mod(&e, &curve->order); + // s = s * r^-1 + bn_multiply(&r, &s, &curve->order); + bn_mod(&s, &curve->order); + // cp = s * r^-1 * k * G + point_multiply(curve, &s, &cp, &cp); + // cp2 = -digest * r^-1 * G + scalar_multiply(curve, &e, &cp2); + // cp = (s * r^-1 * k - digest * r^-1) * G = Pub + point_add(curve, &cp2, &cp); + // The point at infinity is not considered to be a valid public key. + if(point_is_infinity(&cp)) { + return 1; + } + pub_key[0] = 0x04; + bn_write_be(&cp.x, pub_key + 1); + bn_write_be(&cp.y, pub_key + 33); + return 0; +} + +// returns 0 if verification succeeded +int ecdsa_verify_digest( + const ecdsa_curve* curve, + const uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* digest) { + curve_point pub = {0}, res = {0}; + bignum256 r = {0}, s = {0}, z = {0}; + int result = 0; + + if(!ecdsa_read_pubkey(curve, pub_key, &pub)) { + result = 1; + } + + if(result == 0) { + bn_read_be(sig, &r); + bn_read_be(sig + 32, &s); + bn_read_be(digest, &z); + if(bn_is_zero(&r) || bn_is_zero(&s) || (!bn_is_less(&r, &curve->order)) || + (!bn_is_less(&s, &curve->order))) { + result = 2; + } + if(bn_is_zero(&z)) { + // The digest was all-zero. The probability of this happening by chance is + // infinitesimal, but it could be induced by a fault injection. In this + // case the signature (r,s) can be forged by taking r := (t * Q).x mod n + // and s := r * t^-1 mod n for any t in [1, n-1]. We fail verification, + // because there is no guarantee that the signature was created by the + // owner of the private key. + result = 3; + } + } + + if(result == 0) { + bn_inverse(&s, &curve->order); // s = s^-1 + bn_multiply(&s, &z, &curve->order); // z = z * s [u1 = z * s^-1 mod n] + bn_mod(&z, &curve->order); + } + + if(result == 0) { + bn_multiply(&r, &s, &curve->order); // s = r * s [u2 = r * s^-1 mod n] + bn_mod(&s, &curve->order); + scalar_multiply(curve, &z, &res); // res = z * G [= u1 * G] + point_multiply(curve, &s, &pub, &pub); // pub = s * pub [= u2 * Q] + point_add(curve, &pub, &res); // res = pub + res [R = u1 * G + u2 * Q] + if(point_is_infinity(&res)) { + // R == Infinity + result = 4; + } + } + + if(result == 0) { + bn_mod(&(res.x), &curve->order); + if(!bn_is_equal(&res.x, &r)) { + // R.x != r + // signature does not match + result = 5; + } + } + + memzero(&pub, sizeof(pub)); + memzero(&res, sizeof(res)); + memzero(&r, sizeof(r)); + memzero(&s, sizeof(s)); + memzero(&z, sizeof(z)); + + // all OK + return result; +} + +int ecdsa_sig_to_der(const uint8_t* sig, uint8_t* der) { + int i = 0; + uint8_t *p = der, *len = NULL, *len1 = NULL, *len2 = NULL; + *p = 0x30; + p++; // sequence + *p = 0x00; + len = p; + p++; // len(sequence) + + *p = 0x02; + p++; // integer + *p = 0x00; + len1 = p; + p++; // len(integer) + + // process R + i = 0; + while(i < 31 && sig[i] == 0) { + i++; + } // skip leading zeroes + if(sig[i] >= 0x80) { // put zero in output if MSB set + *p = 0x00; + p++; + *len1 = *len1 + 1; + } + while(i < 32) { // copy bytes to output + *p = sig[i]; + p++; + *len1 = *len1 + 1; + i++; + } + + *p = 0x02; + p++; // integer + *p = 0x00; + len2 = p; + p++; // len(integer) + + // process S + i = 32; + while(i < 63 && sig[i] == 0) { + i++; + } // skip leading zeroes + if(sig[i] >= 0x80) { // put zero in output if MSB set + *p = 0x00; + p++; + *len2 = *len2 + 1; + } + while(i < 64) { // copy bytes to output + *p = sig[i]; + p++; + *len2 = *len2 + 1; + i++; + } + + *len = *len1 + *len2 + 4; + return *len + 2; +} + +// Parse a DER-encoded signature. We don't check whether the encoded integers +// satisfy DER requirements regarding leading zeros. +int ecdsa_sig_from_der(const uint8_t* der, size_t der_len, uint8_t sig[64]) { + memzero(sig, 64); + + // Check sequence header. + if(der_len < 2 || der_len > 72 || der[0] != 0x30 || der[1] != der_len - 2) { + return 1; + } + + // Read two DER-encoded integers. + size_t pos = 2; + for(int i = 0; i < 2; ++i) { + // Check integer header. + if(der_len < pos + 2 || der[pos] != 0x02) { + return 1; + } + + // Locate the integer. + size_t int_len = der[pos + 1]; + pos += 2; + if(pos + int_len > der_len) { + return 1; + } + + // Skip a possible leading zero. + if(int_len != 0 && der[pos] == 0) { + int_len--; + pos++; + } + + // Copy the integer to the output, making sure it fits. + if(int_len > 32) { + return 1; + } + memcpy(sig + 32 * (i + 1) - int_len, der + pos, int_len); + + // Move on to the next one. + pos += int_len; + } + + // Check that there are no trailing elements in the sequence. + if(pos != der_len) { + return 1; + } + + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/ecdsa.h b/applications/external/flipbip/lib/crypto/ecdsa.h new file mode 100644 index 000000000..26192aa08 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ecdsa.h @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __ECDSA_H__ +#define __ECDSA_H__ + +#include +#include "bignum.h" +#include "hasher.h" +#include "options.h" + +// curve point x and y +typedef struct { + bignum256 x, y; +} curve_point; + +typedef struct { + bignum256 prime; // prime order of the finite field + curve_point G; // initial curve point + bignum256 order; // order of G + bignum256 order_half; // order of G divided by 2 + int a; // coefficient 'a' of the elliptic curve + bignum256 b; // coefficient 'b' of the elliptic curve + +#if USE_PRECOMPUTED_CP + const curve_point cp[64][8]; +#endif + +} ecdsa_curve; + +// 4 byte prefix + 40 byte data (segwit) +// 1 byte prefix + 64 byte data (cashaddr) +#define MAX_ADDR_RAW_SIZE 65 +// bottle neck is cashaddr +// segwit is at most 90 characters plus NUL separator +// cashaddr: human readable prefix + 1 separator + 104 data + 8 checksum + 1 NUL +// we choose 130 as maximum (including NUL character) +#define MAX_ADDR_SIZE 130 +// 4 byte prefix + 32 byte privkey + 1 byte compressed marker +#define MAX_WIF_RAW_SIZE (4 + 32 + 1) +// (4 + 32 + 1 + 4 [checksum]) * 8 / log2(58) plus NUL. +#define MAX_WIF_SIZE (57) + +void point_copy(const curve_point* cp1, curve_point* cp2); +void point_add(const ecdsa_curve* curve, const curve_point* cp1, curve_point* cp2); +void point_double(const ecdsa_curve* curve, curve_point* cp); +int point_multiply( + const ecdsa_curve* curve, + const bignum256* k, + const curve_point* p, + curve_point* res); +void point_set_infinity(curve_point* p); +int point_is_infinity(const curve_point* p); +int point_is_equal(const curve_point* p, const curve_point* q); +int point_is_negative_of(const curve_point* p, const curve_point* q); +int scalar_multiply(const ecdsa_curve* curve, const bignum256* k, curve_point* res); +int ecdh_multiply( + const ecdsa_curve* curve, + const uint8_t* priv_key, + const uint8_t* pub_key, + uint8_t* session_key); +void compress_coords(const curve_point* cp, uint8_t* compressed); +void uncompress_coords(const ecdsa_curve* curve, uint8_t odd, const bignum256* x, bignum256* y); +int ecdsa_uncompress_pubkey( + const ecdsa_curve* curve, + const uint8_t* pub_key, + uint8_t* uncompressed); + +int ecdsa_sign( + const ecdsa_curve* curve, + HasherType hasher_sign, + const uint8_t* priv_key, + const uint8_t* msg, + uint32_t msg_len, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])); +int ecdsa_sign_digest( + const ecdsa_curve* curve, + const uint8_t* priv_key, + const uint8_t* digest, + uint8_t* sig, + uint8_t* pby, + int (*is_canonical)(uint8_t by, uint8_t sig[64])); +int ecdsa_get_public_key33(const ecdsa_curve* curve, const uint8_t* priv_key, uint8_t* pub_key); +int ecdsa_get_public_key65(const ecdsa_curve* curve, const uint8_t* priv_key, uint8_t* pub_key); +void ecdsa_get_pubkeyhash(const uint8_t* pub_key, HasherType hasher_pubkey, uint8_t* pubkeyhash); +void ecdsa_get_address_raw( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + uint8_t* addr_raw); +void ecdsa_get_address( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize); +void ecdsa_get_address_segwit_p2sh_raw( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + uint8_t* addr_raw); +void ecdsa_get_address_segwit_p2sh( + const uint8_t* pub_key, + uint32_t version, + HasherType hasher_pubkey, + HasherType hasher_base58, + char* addr, + int addrsize); +void ecdsa_get_wif( + const uint8_t* priv_key, + uint32_t version, + HasherType hasher_base58, + char* wif, + int wifsize); + +int ecdsa_address_decode( + const char* addr, + uint32_t version, + HasherType hasher_base58, + uint8_t* out); +int ecdsa_read_pubkey(const ecdsa_curve* curve, const uint8_t* pub_key, curve_point* pub); +int ecdsa_validate_pubkey(const ecdsa_curve* curve, const curve_point* pub); +int ecdsa_verify( + const ecdsa_curve* curve, + HasherType hasher_sign, + const uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* msg, + uint32_t msg_len); +int ecdsa_verify_digest( + const ecdsa_curve* curve, + const uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* digest); +int ecdsa_recover_pub_from_sig( + const ecdsa_curve* curve, + uint8_t* pub_key, + const uint8_t* sig, + const uint8_t* digest, + int recid); +int ecdsa_sig_to_der(const uint8_t* sig, uint8_t* der); +int ecdsa_sig_from_der(const uint8_t* der, size_t der_len, uint8_t sig[64]); + +#endif diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.c b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.c new file mode 100644 index 000000000..603abeb24 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.c @@ -0,0 +1,953 @@ +/* + Public domain by Andrew M. + See: https://github.com/floodyberry/curve25519-donna + + 32 bit integer curve25519 implementation +*/ + +#include "ed25519_donna.h" + +static const uint32_t reduce_mask_25 = (1 << 25) - 1; +static const uint32_t reduce_mask_26 = (1 << 26) - 1; + +/* out = in */ +void curve25519_copy(bignum25519 out, const bignum25519 in) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[4] = in[4]; + out[5] = in[5]; + out[6] = in[6]; + out[7] = in[7]; + out[8] = in[8]; + out[9] = in[9]; +} + +/* out = a + b */ +void curve25519_add(bignum25519 out, const bignum25519 a, const bignum25519 b) { + out[0] = a[0] + b[0]; + out[1] = a[1] + b[1]; + out[2] = a[2] + b[2]; + out[3] = a[3] + b[3]; + out[4] = a[4] + b[4]; + out[5] = a[5] + b[5]; + out[6] = a[6] + b[6]; + out[7] = a[7] + b[7]; + out[8] = a[8] + b[8]; + out[9] = a[9] + b[9]; +} + +void curve25519_add_after_basic(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = a[0] + b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = a[1] + b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = a[2] + b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = a[3] + b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = a[4] + b[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = a[5] + b[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = a[6] + b[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = a[7] + b[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = a[8] + b[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = a[9] + b[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +void curve25519_add_reduce(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = a[0] + b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = a[1] + b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = a[2] + b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = a[3] + b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = a[4] + b[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = a[5] + b[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = a[6] + b[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = a[7] + b[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = a[8] + b[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = a[9] + b[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +/* multiples of p */ +static const uint32_t twoP0 = 0x07ffffda; +static const uint32_t twoP13579 = 0x03fffffe; +static const uint32_t twoP2468 = 0x07fffffe; +static const uint32_t fourP0 = 0x0fffffb4; +static const uint32_t fourP13579 = 0x07fffffc; +static const uint32_t fourP2468 = 0x0ffffffc; + +/* out = a - b */ +void curve25519_sub(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = twoP0 + a[0] - b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = twoP13579 + a[1] - b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = twoP2468 + a[2] - b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = twoP13579 + a[3] - b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = twoP2468 + a[4] - b[4] + c; + out[5] = twoP13579 + a[5] - b[5]; + out[6] = twoP2468 + a[6] - b[6]; + out[7] = twoP13579 + a[7] - b[7]; + out[8] = twoP2468 + a[8] - b[8]; + out[9] = twoP13579 + a[9] - b[9]; +} + +/* out = in * scalar */ +void curve25519_scalar_product(bignum25519 out, const bignum25519 in, const uint32_t scalar) { + uint64_t a = 0; + uint32_t c = 0; + a = mul32x32_64(in[0], scalar); + out[0] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[1], scalar) + c; + out[1] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + a = mul32x32_64(in[2], scalar) + c; + out[2] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[3], scalar) + c; + out[3] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + a = mul32x32_64(in[4], scalar) + c; + out[4] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[5], scalar) + c; + out[5] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + a = mul32x32_64(in[6], scalar) + c; + out[6] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[7], scalar) + c; + out[7] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + a = mul32x32_64(in[8], scalar) + c; + out[8] = (uint32_t)a & reduce_mask_26; + c = (uint32_t)(a >> 26); + a = mul32x32_64(in[9], scalar) + c; + out[9] = (uint32_t)a & reduce_mask_25; + c = (uint32_t)(a >> 25); + out[0] += c * 19; +} + +/* out = a - b, where a is the result of a basic op (add,sub) */ +void curve25519_sub_after_basic(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = fourP0 + a[0] - b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = fourP13579 + a[1] - b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = fourP2468 + a[2] - b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = fourP13579 + a[3] - b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = fourP2468 + a[4] - b[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = fourP13579 + a[5] - b[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = fourP2468 + a[6] - b[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = fourP13579 + a[7] - b[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = fourP2468 + a[8] - b[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = fourP13579 + a[9] - b[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +void curve25519_sub_reduce(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t c = 0; + out[0] = fourP0 + a[0] - b[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = fourP13579 + a[1] - b[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = fourP2468 + a[2] - b[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = fourP13579 + a[3] - b[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = fourP2468 + a[4] - b[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = fourP13579 + a[5] - b[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = fourP2468 + a[6] - b[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = fourP13579 + a[7] - b[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = fourP2468 + a[8] - b[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = fourP13579 + a[9] - b[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +/* out = -a */ +void curve25519_neg(bignum25519 out, const bignum25519 a) { + uint32_t c = 0; + out[0] = twoP0 - a[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = twoP13579 - a[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = twoP2468 - a[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = twoP13579 - a[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = twoP2468 - a[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = twoP13579 - a[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = twoP2468 - a[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = twoP13579 - a[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = twoP2468 - a[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = twoP13579 - a[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +/* out = a * b */ +#define curve25519_mul_noinline curve25519_mul +void curve25519_mul(bignum25519 out, const bignum25519 a, const bignum25519 b) { + uint32_t r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0; + uint32_t s0 = 0, s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, s7 = 0, s8 = 0, s9 = 0; + uint64_t m0 = 0, m1 = 0, m2 = 0, m3 = 0, m4 = 0, m5 = 0, m6 = 0, m7 = 0, m8 = 0, m9 = 0, c = 0; + uint32_t p = 0; + + r0 = b[0]; + r1 = b[1]; + r2 = b[2]; + r3 = b[3]; + r4 = b[4]; + r5 = b[5]; + r6 = b[6]; + r7 = b[7]; + r8 = b[8]; + r9 = b[9]; + + s0 = a[0]; + s1 = a[1]; + s2 = a[2]; + s3 = a[3]; + s4 = a[4]; + s5 = a[5]; + s6 = a[6]; + s7 = a[7]; + s8 = a[8]; + s9 = a[9]; + + m1 = mul32x32_64(r0, s1) + mul32x32_64(r1, s0); + m3 = mul32x32_64(r0, s3) + mul32x32_64(r1, s2) + mul32x32_64(r2, s1) + mul32x32_64(r3, s0); + m5 = mul32x32_64(r0, s5) + mul32x32_64(r1, s4) + mul32x32_64(r2, s3) + mul32x32_64(r3, s2) + + mul32x32_64(r4, s1) + mul32x32_64(r5, s0); + m7 = mul32x32_64(r0, s7) + mul32x32_64(r1, s6) + mul32x32_64(r2, s5) + mul32x32_64(r3, s4) + + mul32x32_64(r4, s3) + mul32x32_64(r5, s2) + mul32x32_64(r6, s1) + mul32x32_64(r7, s0); + m9 = mul32x32_64(r0, s9) + mul32x32_64(r1, s8) + mul32x32_64(r2, s7) + mul32x32_64(r3, s6) + + mul32x32_64(r4, s5) + mul32x32_64(r5, s4) + mul32x32_64(r6, s3) + mul32x32_64(r7, s2) + + mul32x32_64(r8, s1) + mul32x32_64(r9, s0); + + r1 *= 2; + r3 *= 2; + r5 *= 2; + r7 *= 2; + + m0 = mul32x32_64(r0, s0); + m2 = mul32x32_64(r0, s2) + mul32x32_64(r1, s1) + mul32x32_64(r2, s0); + m4 = mul32x32_64(r0, s4) + mul32x32_64(r1, s3) + mul32x32_64(r2, s2) + mul32x32_64(r3, s1) + + mul32x32_64(r4, s0); + m6 = mul32x32_64(r0, s6) + mul32x32_64(r1, s5) + mul32x32_64(r2, s4) + mul32x32_64(r3, s3) + + mul32x32_64(r4, s2) + mul32x32_64(r5, s1) + mul32x32_64(r6, s0); + m8 = mul32x32_64(r0, s8) + mul32x32_64(r1, s7) + mul32x32_64(r2, s6) + mul32x32_64(r3, s5) + + mul32x32_64(r4, s4) + mul32x32_64(r5, s3) + mul32x32_64(r6, s2) + mul32x32_64(r7, s1) + + mul32x32_64(r8, s0); + + r1 *= 19; + r2 *= 19; + r3 = (r3 / 2) * 19; + r4 *= 19; + r5 = (r5 / 2) * 19; + r6 *= 19; + r7 = (r7 / 2) * 19; + r8 *= 19; + r9 *= 19; + + m1 += + (mul32x32_64(r9, s2) + mul32x32_64(r8, s3) + mul32x32_64(r7, s4) + mul32x32_64(r6, s5) + + mul32x32_64(r5, s6) + mul32x32_64(r4, s7) + mul32x32_64(r3, s8) + mul32x32_64(r2, s9)); + m3 += + (mul32x32_64(r9, s4) + mul32x32_64(r8, s5) + mul32x32_64(r7, s6) + mul32x32_64(r6, s7) + + mul32x32_64(r5, s8) + mul32x32_64(r4, s9)); + m5 += (mul32x32_64(r9, s6) + mul32x32_64(r8, s7) + mul32x32_64(r7, s8) + mul32x32_64(r6, s9)); + m7 += (mul32x32_64(r9, s8) + mul32x32_64(r8, s9)); + + r3 *= 2; + r5 *= 2; + r7 *= 2; + r9 *= 2; + + m0 += + (mul32x32_64(r9, s1) + mul32x32_64(r8, s2) + mul32x32_64(r7, s3) + mul32x32_64(r6, s4) + + mul32x32_64(r5, s5) + mul32x32_64(r4, s6) + mul32x32_64(r3, s7) + mul32x32_64(r2, s8) + + mul32x32_64(r1, s9)); + m2 += + (mul32x32_64(r9, s3) + mul32x32_64(r8, s4) + mul32x32_64(r7, s5) + mul32x32_64(r6, s6) + + mul32x32_64(r5, s7) + mul32x32_64(r4, s8) + mul32x32_64(r3, s9)); + m4 += + (mul32x32_64(r9, s5) + mul32x32_64(r8, s6) + mul32x32_64(r7, s7) + mul32x32_64(r6, s8) + + mul32x32_64(r5, s9)); + m6 += (mul32x32_64(r9, s7) + mul32x32_64(r8, s8) + mul32x32_64(r7, s9)); + m8 += (mul32x32_64(r9, s9)); + + r0 = (uint32_t)m0 & reduce_mask_26; + c = (m0 >> 26); + m1 += c; + r1 = (uint32_t)m1 & reduce_mask_25; + c = (m1 >> 25); + m2 += c; + r2 = (uint32_t)m2 & reduce_mask_26; + c = (m2 >> 26); + m3 += c; + r3 = (uint32_t)m3 & reduce_mask_25; + c = (m3 >> 25); + m4 += c; + r4 = (uint32_t)m4 & reduce_mask_26; + c = (m4 >> 26); + m5 += c; + r5 = (uint32_t)m5 & reduce_mask_25; + c = (m5 >> 25); + m6 += c; + r6 = (uint32_t)m6 & reduce_mask_26; + c = (m6 >> 26); + m7 += c; + r7 = (uint32_t)m7 & reduce_mask_25; + c = (m7 >> 25); + m8 += c; + r8 = (uint32_t)m8 & reduce_mask_26; + c = (m8 >> 26); + m9 += c; + r9 = (uint32_t)m9 & reduce_mask_25; + p = (uint32_t)(m9 >> 25); + m0 = r0 + mul32x32_64(p, 19); + r0 = (uint32_t)m0 & reduce_mask_26; + p = (uint32_t)(m0 >> 26); + r1 += p; + + out[0] = r0; + out[1] = r1; + out[2] = r2; + out[3] = r3; + out[4] = r4; + out[5] = r5; + out[6] = r6; + out[7] = r7; + out[8] = r8; + out[9] = r9; +} + +/* out = in * in */ +void curve25519_square(bignum25519 out, const bignum25519 in) { + uint32_t r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0; + uint32_t d6 = 0, d7 = 0, d8 = 0, d9 = 0; + uint64_t m0 = 0, m1 = 0, m2 = 0, m3 = 0, m4 = 0, m5 = 0, m6 = 0, m7 = 0, m8 = 0, m9 = 0, c = 0; + uint32_t p = 0; + + r0 = in[0]; + r1 = in[1]; + r2 = in[2]; + r3 = in[3]; + r4 = in[4]; + r5 = in[5]; + r6 = in[6]; + r7 = in[7]; + r8 = in[8]; + r9 = in[9]; + + m0 = mul32x32_64(r0, r0); + r0 *= 2; + m1 = mul32x32_64(r0, r1); + m2 = mul32x32_64(r0, r2) + mul32x32_64(r1, r1 * 2); + r1 *= 2; + m3 = mul32x32_64(r0, r3) + mul32x32_64(r1, r2); + m4 = mul32x32_64(r0, r4) + mul32x32_64(r1, r3 * 2) + mul32x32_64(r2, r2); + r2 *= 2; + m5 = mul32x32_64(r0, r5) + mul32x32_64(r1, r4) + mul32x32_64(r2, r3); + m6 = mul32x32_64(r0, r6) + mul32x32_64(r1, r5 * 2) + mul32x32_64(r2, r4) + + mul32x32_64(r3, r3 * 2); + r3 *= 2; + m7 = mul32x32_64(r0, r7) + mul32x32_64(r1, r6) + mul32x32_64(r2, r5) + mul32x32_64(r3, r4); + m8 = mul32x32_64(r0, r8) + mul32x32_64(r1, r7 * 2) + mul32x32_64(r2, r6) + + mul32x32_64(r3, r5 * 2) + mul32x32_64(r4, r4); + m9 = mul32x32_64(r0, r9) + mul32x32_64(r1, r8) + mul32x32_64(r2, r7) + mul32x32_64(r3, r6) + + mul32x32_64(r4, r5 * 2); + + d6 = r6 * 19; + d7 = r7 * 2 * 19; + d8 = r8 * 19; + d9 = r9 * 2 * 19; + + m0 += + (mul32x32_64(d9, r1) + mul32x32_64(d8, r2) + mul32x32_64(d7, r3) + + mul32x32_64(d6, r4 * 2) + mul32x32_64(r5, r5 * 2 * 19)); + m1 += + (mul32x32_64(d9, r2 / 2) + mul32x32_64(d8, r3) + mul32x32_64(d7, r4) + + mul32x32_64(d6, r5 * 2)); + m2 += + (mul32x32_64(d9, r3) + mul32x32_64(d8, r4 * 2) + mul32x32_64(d7, r5 * 2) + + mul32x32_64(d6, r6)); + m3 += (mul32x32_64(d9, r4) + mul32x32_64(d8, r5 * 2) + mul32x32_64(d7, r6)); + m4 += (mul32x32_64(d9, r5 * 2) + mul32x32_64(d8, r6 * 2) + mul32x32_64(d7, r7)); + m5 += (mul32x32_64(d9, r6) + mul32x32_64(d8, r7 * 2)); + m6 += (mul32x32_64(d9, r7 * 2) + mul32x32_64(d8, r8)); + m7 += (mul32x32_64(d9, r8)); + m8 += (mul32x32_64(d9, r9)); + + r0 = (uint32_t)m0 & reduce_mask_26; + c = (m0 >> 26); + m1 += c; + r1 = (uint32_t)m1 & reduce_mask_25; + c = (m1 >> 25); + m2 += c; + r2 = (uint32_t)m2 & reduce_mask_26; + c = (m2 >> 26); + m3 += c; + r3 = (uint32_t)m3 & reduce_mask_25; + c = (m3 >> 25); + m4 += c; + r4 = (uint32_t)m4 & reduce_mask_26; + c = (m4 >> 26); + m5 += c; + r5 = (uint32_t)m5 & reduce_mask_25; + c = (m5 >> 25); + m6 += c; + r6 = (uint32_t)m6 & reduce_mask_26; + c = (m6 >> 26); + m7 += c; + r7 = (uint32_t)m7 & reduce_mask_25; + c = (m7 >> 25); + m8 += c; + r8 = (uint32_t)m8 & reduce_mask_26; + c = (m8 >> 26); + m9 += c; + r9 = (uint32_t)m9 & reduce_mask_25; + p = (uint32_t)(m9 >> 25); + m0 = r0 + mul32x32_64(p, 19); + r0 = (uint32_t)m0 & reduce_mask_26; + p = (uint32_t)(m0 >> 26); + r1 += p; + + out[0] = r0; + out[1] = r1; + out[2] = r2; + out[3] = r3; + out[4] = r4; + out[5] = r5; + out[6] = r6; + out[7] = r7; + out[8] = r8; + out[9] = r9; +} + +/* out = in ^ (2 * count) */ +void curve25519_square_times(bignum25519 out, const bignum25519 in, int count) { + uint32_t r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0, r8 = 0, r9 = 0; + uint32_t d6 = 0, d7 = 0, d8 = 0, d9 = 0; + uint64_t m0 = 0, m1 = 0, m2 = 0, m3 = 0, m4 = 0, m5 = 0, m6 = 0, m7 = 0, m8 = 0, m9 = 0, c = 0; + uint32_t p = 0; + + r0 = in[0]; + r1 = in[1]; + r2 = in[2]; + r3 = in[3]; + r4 = in[4]; + r5 = in[5]; + r6 = in[6]; + r7 = in[7]; + r8 = in[8]; + r9 = in[9]; + + do { + m0 = mul32x32_64(r0, r0); + r0 *= 2; + m1 = mul32x32_64(r0, r1); + m2 = mul32x32_64(r0, r2) + mul32x32_64(r1, r1 * 2); + r1 *= 2; + m3 = mul32x32_64(r0, r3) + mul32x32_64(r1, r2); + m4 = mul32x32_64(r0, r4) + mul32x32_64(r1, r3 * 2) + mul32x32_64(r2, r2); + r2 *= 2; + m5 = mul32x32_64(r0, r5) + mul32x32_64(r1, r4) + mul32x32_64(r2, r3); + m6 = mul32x32_64(r0, r6) + mul32x32_64(r1, r5 * 2) + mul32x32_64(r2, r4) + + mul32x32_64(r3, r3 * 2); + r3 *= 2; + m7 = mul32x32_64(r0, r7) + mul32x32_64(r1, r6) + mul32x32_64(r2, r5) + mul32x32_64(r3, r4); + m8 = mul32x32_64(r0, r8) + mul32x32_64(r1, r7 * 2) + mul32x32_64(r2, r6) + + mul32x32_64(r3, r5 * 2) + mul32x32_64(r4, r4); + m9 = mul32x32_64(r0, r9) + mul32x32_64(r1, r8) + mul32x32_64(r2, r7) + + mul32x32_64(r3, r6) + mul32x32_64(r4, r5 * 2); + + d6 = r6 * 19; + d7 = r7 * 2 * 19; + d8 = r8 * 19; + d9 = r9 * 2 * 19; + + m0 += + (mul32x32_64(d9, r1) + mul32x32_64(d8, r2) + mul32x32_64(d7, r3) + + mul32x32_64(d6, r4 * 2) + mul32x32_64(r5, r5 * 2 * 19)); + m1 += + (mul32x32_64(d9, r2 / 2) + mul32x32_64(d8, r3) + mul32x32_64(d7, r4) + + mul32x32_64(d6, r5 * 2)); + m2 += + (mul32x32_64(d9, r3) + mul32x32_64(d8, r4 * 2) + mul32x32_64(d7, r5 * 2) + + mul32x32_64(d6, r6)); + m3 += (mul32x32_64(d9, r4) + mul32x32_64(d8, r5 * 2) + mul32x32_64(d7, r6)); + m4 += (mul32x32_64(d9, r5 * 2) + mul32x32_64(d8, r6 * 2) + mul32x32_64(d7, r7)); + m5 += (mul32x32_64(d9, r6) + mul32x32_64(d8, r7 * 2)); + m6 += (mul32x32_64(d9, r7 * 2) + mul32x32_64(d8, r8)); + m7 += (mul32x32_64(d9, r8)); + m8 += (mul32x32_64(d9, r9)); + + r0 = (uint32_t)m0 & reduce_mask_26; + c = (m0 >> 26); + m1 += c; + r1 = (uint32_t)m1 & reduce_mask_25; + c = (m1 >> 25); + m2 += c; + r2 = (uint32_t)m2 & reduce_mask_26; + c = (m2 >> 26); + m3 += c; + r3 = (uint32_t)m3 & reduce_mask_25; + c = (m3 >> 25); + m4 += c; + r4 = (uint32_t)m4 & reduce_mask_26; + c = (m4 >> 26); + m5 += c; + r5 = (uint32_t)m5 & reduce_mask_25; + c = (m5 >> 25); + m6 += c; + r6 = (uint32_t)m6 & reduce_mask_26; + c = (m6 >> 26); + m7 += c; + r7 = (uint32_t)m7 & reduce_mask_25; + c = (m7 >> 25); + m8 += c; + r8 = (uint32_t)m8 & reduce_mask_26; + c = (m8 >> 26); + m9 += c; + r9 = (uint32_t)m9 & reduce_mask_25; + p = (uint32_t)(m9 >> 25); + m0 = r0 + mul32x32_64(p, 19); + r0 = (uint32_t)m0 & reduce_mask_26; + p = (uint32_t)(m0 >> 26); + r1 += p; + } while(--count); + + out[0] = r0; + out[1] = r1; + out[2] = r2; + out[3] = r3; + out[4] = r4; + out[5] = r5; + out[6] = r6; + out[7] = r7; + out[8] = r8; + out[9] = r9; +} + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +void curve25519_expand(bignum25519 out, const unsigned char in[32]) { + uint32_t x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0; +#define F(s) \ + ((((uint32_t)in[s + 0])) | (((uint32_t)in[s + 1]) << 8) | (((uint32_t)in[s + 2]) << 16) | \ + (((uint32_t)in[s + 3]) << 24)) + x0 = F(0); + x1 = F(4); + x2 = F(8); + x3 = F(12); + x4 = F(16); + x5 = F(20); + x6 = F(24); + x7 = F(28); +#undef F + + out[0] = (x0)&reduce_mask_26; + out[1] = ((((uint64_t)x1 << 32) | x0) >> 26) & reduce_mask_25; + out[2] = ((((uint64_t)x2 << 32) | x1) >> 19) & reduce_mask_26; + out[3] = ((((uint64_t)x3 << 32) | x2) >> 13) & reduce_mask_25; + out[4] = ((x3) >> 6) & reduce_mask_26; + out[5] = (x4)&reduce_mask_25; + out[6] = ((((uint64_t)x5 << 32) | x4) >> 25) & reduce_mask_26; + out[7] = ((((uint64_t)x6 << 32) | x5) >> 19) & reduce_mask_25; + out[8] = ((((uint64_t)x7 << 32) | x6) >> 12) & reduce_mask_26; + out[9] = ((x7) >> 6) & reduce_mask_25; /* ignore the top bit */ +} + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array + */ +void curve25519_contract(unsigned char out[32], const bignum25519 in) { + bignum25519 f = {0}; + curve25519_copy(f, in); + +#define carry_pass() \ + f[1] += f[0] >> 26; \ + f[0] &= reduce_mask_26; \ + f[2] += f[1] >> 25; \ + f[1] &= reduce_mask_25; \ + f[3] += f[2] >> 26; \ + f[2] &= reduce_mask_26; \ + f[4] += f[3] >> 25; \ + f[3] &= reduce_mask_25; \ + f[5] += f[4] >> 26; \ + f[4] &= reduce_mask_26; \ + f[6] += f[5] >> 25; \ + f[5] &= reduce_mask_25; \ + f[7] += f[6] >> 26; \ + f[6] &= reduce_mask_26; \ + f[8] += f[7] >> 25; \ + f[7] &= reduce_mask_25; \ + f[9] += f[8] >> 26; \ + f[8] &= reduce_mask_26; + +#define carry_pass_full() \ + carry_pass() f[0] += 19 * (f[9] >> 25); \ + f[9] &= reduce_mask_25; + +#define carry_pass_final() carry_pass() f[9] &= reduce_mask_25; + + carry_pass_full() carry_pass_full() + + /* now t is between 0 and 2^255-1, properly carried. */ + /* case 1: between 0 and 2^255-20. case 2: between 2^255-19 and 2^255-1. */ + f[0] += 19; + carry_pass_full() + + /* now between 19 and 2^255-1 in both cases, and offset by 19. */ + f[0] += (reduce_mask_26 + 1) - 19; + f[1] += (reduce_mask_25 + 1) - 1; + f[2] += (reduce_mask_26 + 1) - 1; + f[3] += (reduce_mask_25 + 1) - 1; + f[4] += (reduce_mask_26 + 1) - 1; + f[5] += (reduce_mask_25 + 1) - 1; + f[6] += (reduce_mask_26 + 1) - 1; + f[7] += (reduce_mask_25 + 1) - 1; + f[8] += (reduce_mask_26 + 1) - 1; + f[9] += (reduce_mask_25 + 1) - 1; + + /* now between 2^255 and 2^256-20, and offset by 2^255. */ + carry_pass_final() + +#undef carry_pass +#undef carry_full +#undef carry_final + + f[1] <<= 2; + f[2] <<= 3; + f[3] <<= 5; + f[4] <<= 6; + f[6] <<= 1; + f[7] <<= 3; + f[8] <<= 4; + f[9] <<= 6; + +#define F(i, s) \ + out[s + 0] |= (unsigned char)(f[i] & 0xff); \ + out[s + 1] = (unsigned char)((f[i] >> 8) & 0xff); \ + out[s + 2] = (unsigned char)((f[i] >> 16) & 0xff); \ + out[s + 3] = (unsigned char)((f[i] >> 24) & 0xff); + + out[0] = 0; + out[16] = 0; + F(0, 0); + F(1, 3); + F(2, 6); + F(3, 9); + F(4, 12); + F(5, 16); + F(6, 19); + F(7, 22); + F(8, 25); + F(9, 28); +#undef F +} + +/* if (iswap) swap(a, b) */ +void curve25519_swap_conditional(bignum25519 a, bignum25519 b, uint32_t iswap) { + const uint32_t swap = (uint32_t)(-(int32_t)iswap); + uint32_t x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0, x8 = 0, x9 = 0; + + x0 = swap & (a[0] ^ b[0]); + a[0] ^= x0; + b[0] ^= x0; + x1 = swap & (a[1] ^ b[1]); + a[1] ^= x1; + b[1] ^= x1; + x2 = swap & (a[2] ^ b[2]); + a[2] ^= x2; + b[2] ^= x2; + x3 = swap & (a[3] ^ b[3]); + a[3] ^= x3; + b[3] ^= x3; + x4 = swap & (a[4] ^ b[4]); + a[4] ^= x4; + b[4] ^= x4; + x5 = swap & (a[5] ^ b[5]); + a[5] ^= x5; + b[5] ^= x5; + x6 = swap & (a[6] ^ b[6]); + a[6] ^= x6; + b[6] ^= x6; + x7 = swap & (a[7] ^ b[7]); + a[7] ^= x7; + b[7] ^= x7; + x8 = swap & (a[8] ^ b[8]); + a[8] ^= x8; + b[8] ^= x8; + x9 = swap & (a[9] ^ b[9]); + a[9] ^= x9; + b[9] ^= x9; +} + +void curve25519_set(bignum25519 r, uint32_t x) { + r[0] = x & reduce_mask_26; + x >>= 26; + r[1] = x & reduce_mask_25; + r[2] = 0; + r[3] = 0; + r[4] = 0; + r[5] = 0; + r[6] = 0; + r[7] = 0; + r[8] = 0; + r[9] = 0; +} + +void curve25519_set_d(bignum25519 r) { + curve25519_copy(r, ge25519_ecd); +} + +void curve25519_set_2d(bignum25519 r) { + curve25519_copy(r, ge25519_ec2d); +} + +void curve25519_set_sqrtneg1(bignum25519 r) { + curve25519_copy(r, ge25519_sqrtneg1); +} + +int curve25519_isnegative(const bignum25519 f) { + unsigned char s[32] = {0}; + curve25519_contract(s, f); + return s[0] & 1; +} + +int curve25519_isnonzero(const bignum25519 f) { + unsigned char s[32] = {0}; + curve25519_contract(s, f); + return ((((int)(s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] | s[9] | s[10] | s[11] | s[12] | s[13] | s[14] | s[15] | s[16] | s[17] | s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | s[27] | s[28] | s[29] | s[30] | s[31]) - + 1) >> + 8) + + 1) & + 0x1; +} + +void curve25519_reduce(bignum25519 out, const bignum25519 in) { + uint32_t c = 0; + out[0] = in[0]; + c = (out[0] >> 26); + out[0] &= reduce_mask_26; + out[1] = in[1] + c; + c = (out[1] >> 25); + out[1] &= reduce_mask_25; + out[2] = in[2] + c; + c = (out[2] >> 26); + out[2] &= reduce_mask_26; + out[3] = in[3] + c; + c = (out[3] >> 25); + out[3] &= reduce_mask_25; + out[4] = in[4] + c; + c = (out[4] >> 26); + out[4] &= reduce_mask_26; + out[5] = in[5] + c; + c = (out[5] >> 25); + out[5] &= reduce_mask_25; + out[6] = in[6] + c; + c = (out[6] >> 26); + out[6] &= reduce_mask_26; + out[7] = in[7] + c; + c = (out[7] >> 25); + out[7] &= reduce_mask_25; + out[8] = in[8] + c; + c = (out[8] >> 26); + out[8] &= reduce_mask_26; + out[9] = in[9] + c; + c = (out[9] >> 25); + out[9] &= reduce_mask_25; + out[0] += 19 * c; +} + +void curve25519_divpowm1(bignum25519 r, const bignum25519 u, const bignum25519 v) { + bignum25519 v3 = {0}, uv7 = {0}, t0 = {0}, t1 = {0}, t2 = {0}; + int i = 0; + + curve25519_square(v3, v); + curve25519_mul(v3, v3, v); /* v3 = v^3 */ + curve25519_square(uv7, v3); + curve25519_mul(uv7, uv7, v); + curve25519_mul(uv7, uv7, u); /* uv7 = uv^7 */ + + /*fe_pow22523(uv7, uv7);*/ + /* From fe_pow22523.c */ + + curve25519_square(t0, uv7); + curve25519_square(t1, t0); + curve25519_square(t1, t1); + curve25519_mul(t1, uv7, t1); + curve25519_mul(t0, t0, t1); + curve25519_square(t0, t0); + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for(i = 0; i < 4; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for(i = 0; i < 9; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t1, t1, t0); + curve25519_square(t2, t1); + for(i = 0; i < 19; ++i) { + curve25519_square(t2, t2); + } + curve25519_mul(t1, t2, t1); + for(i = 0; i < 10; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t1, t0); + for(i = 0; i < 49; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t1, t1, t0); + curve25519_square(t2, t1); + for(i = 0; i < 99; ++i) { + curve25519_square(t2, t2); + } + curve25519_mul(t1, t2, t1); + for(i = 0; i < 50; ++i) { + curve25519_square(t1, t1); + } + curve25519_mul(t0, t1, t0); + curve25519_square(t0, t0); + curve25519_square(t0, t0); + curve25519_mul(t0, t0, uv7); + + /* End fe_pow22523.c */ + /* t0 = (uv^7)^((q-5)/8) */ + curve25519_mul(t0, t0, v3); + curve25519_mul(r, t0, u); /* u^(m+1)v^(-(m+1)) */ +} + +void curve25519_expand_reduce(bignum25519 out, const unsigned char in[32]) { + uint32_t x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0; +#define F(s) \ + ((((uint32_t)in[s + 0])) | (((uint32_t)in[s + 1]) << 8) | (((uint32_t)in[s + 2]) << 16) | \ + (((uint32_t)in[s + 3]) << 24)) + x0 = F(0); + x1 = F(4); + x2 = F(8); + x3 = F(12); + x4 = F(16); + x5 = F(20); + x6 = F(24); + x7 = F(28); +#undef F + + out[0] = (x0)&reduce_mask_26; + out[1] = ((((uint64_t)x1 << 32) | x0) >> 26) & reduce_mask_25; + out[2] = ((((uint64_t)x2 << 32) | x1) >> 19) & reduce_mask_26; + out[3] = ((((uint64_t)x3 << 32) | x2) >> 13) & reduce_mask_25; + out[4] = ((x3) >> 6) & reduce_mask_26; + out[5] = (x4)&reduce_mask_25; + out[6] = ((((uint64_t)x5 << 32) | x4) >> 25) & reduce_mask_26; + out[7] = ((((uint64_t)x6 << 32) | x5) >> 19) & reduce_mask_25; + out[8] = ((((uint64_t)x7 << 32) | x6) >> 12) & reduce_mask_26; + out[9] = ((x7) >> 6); // & reduce_mask_25; /* ignore the top bit */ + out[0] += 19 * (out[9] >> 25); + out[9] &= reduce_mask_25; +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.h b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.h new file mode 100644 index 000000000..87bea94f9 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_32bit.h @@ -0,0 +1,79 @@ +/* + Public domain by Andrew M. + See: https://github.com/floodyberry/curve25519-donna + + 32 bit integer curve25519 implementation +*/ + +typedef uint32_t bignum25519[10]; + +/* out = in */ +void curve25519_copy(bignum25519 out, const bignum25519 in); + +/* out = a + b */ +void curve25519_add(bignum25519 out, const bignum25519 a, const bignum25519 b); + +void curve25519_add_after_basic(bignum25519 out, const bignum25519 a, const bignum25519 b); + +void curve25519_add_reduce(bignum25519 out, const bignum25519 a, const bignum25519 b); + +/* out = a - b */ +void curve25519_sub(bignum25519 out, const bignum25519 a, const bignum25519 b); + +/* out = in * scalar */ +void curve25519_scalar_product(bignum25519 out, const bignum25519 in, const uint32_t scalar); + +/* out = a - b, where a is the result of a basic op (add,sub) */ +void curve25519_sub_after_basic(bignum25519 out, const bignum25519 a, const bignum25519 b); + +void curve25519_sub_reduce(bignum25519 out, const bignum25519 a, const bignum25519 b); + +/* out = -a */ +void curve25519_neg(bignum25519 out, const bignum25519 a); + +/* out = a * b */ +#define curve25519_mul_noinline curve25519_mul +void curve25519_mul(bignum25519 out, const bignum25519 a, const bignum25519 b); + +/* out = in * in */ +void curve25519_square(bignum25519 out, const bignum25519 in); + +/* out = in ^ (2 * count) */ +void curve25519_square_times(bignum25519 out, const bignum25519 in, int count); + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +void curve25519_expand(bignum25519 out, const unsigned char in[32]); + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array + */ +void curve25519_contract(unsigned char out[32], const bignum25519 in); + +/* if (iswap) swap(a, b) */ +void curve25519_swap_conditional(bignum25519 a, bignum25519 b, uint32_t iswap); + +/* uint32_t to Zmod(2^255-19) */ +void curve25519_set(bignum25519 r, uint32_t x); + +/* set d */ +void curve25519_set_d(bignum25519 r); + +/* set 2d */ +void curve25519_set_2d(bignum25519 r); + +/* set sqrt(-1) */ +void curve25519_set_sqrtneg1(bignum25519 r); + +/* constant time Zmod(2^255-19) negative test */ +int curve25519_isnegative(const bignum25519 f); + +/* constant time Zmod(2^255-19) non-zero test */ +int curve25519_isnonzero(const bignum25519 f); + +/* reduce Zmod(2^255-19) */ +void curve25519_reduce(bignum25519 r, const bignum25519 in); + +void curve25519_divpowm1(bignum25519 r, const bignum25519 u, const bignum25519 v); + +/* Zmod(2^255-19) from byte array to bignum25519 expansion with modular reduction */ +void curve25519_expand_reduce(bignum25519 out, const unsigned char in[32]); diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.c b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.c new file mode 100644 index 000000000..22a7b0254 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.c @@ -0,0 +1,66 @@ +/* + Public domain by Andrew M. + See: https://github.com/floodyberry/curve25519-donna + + Curve25519 implementation agnostic helpers +*/ + +#include "ed25519_donna.h" + +/* + * In: b = 2^5 - 2^0 + * Out: b = 2^250 - 2^0 + */ +void curve25519_pow_two5mtwo0_two250mtwo0(bignum25519 b) { + bignum25519 ALIGN(16) t0 = {0}, c = {0}; + + /* 2^5 - 2^0 */ /* b */ + /* 2^10 - 2^5 */ curve25519_square_times(t0, b, 5); + /* 2^10 - 2^0 */ curve25519_mul_noinline(b, t0, b); + /* 2^20 - 2^10 */ curve25519_square_times(t0, b, 10); + /* 2^20 - 2^0 */ curve25519_mul_noinline(c, t0, b); + /* 2^40 - 2^20 */ curve25519_square_times(t0, c, 20); + /* 2^40 - 2^0 */ curve25519_mul_noinline(t0, t0, c); + /* 2^50 - 2^10 */ curve25519_square_times(t0, t0, 10); + /* 2^50 - 2^0 */ curve25519_mul_noinline(b, t0, b); + /* 2^100 - 2^50 */ curve25519_square_times(t0, b, 50); + /* 2^100 - 2^0 */ curve25519_mul_noinline(c, t0, b); + /* 2^200 - 2^100 */ curve25519_square_times(t0, c, 100); + /* 2^200 - 2^0 */ curve25519_mul_noinline(t0, t0, c); + /* 2^250 - 2^50 */ curve25519_square_times(t0, t0, 50); + /* 2^250 - 2^0 */ curve25519_mul_noinline(b, t0, b); +} + +/* + * z^(p - 2) = z(2^255 - 21) + */ +void curve25519_recip(bignum25519 out, const bignum25519 z) { + bignum25519 ALIGN(16) a = {0}, t0 = {0}, b = {0}; + + /* 2 */ curve25519_square_times(a, z, 1); /* a = 2 */ + /* 8 */ curve25519_square_times(t0, a, 2); + /* 9 */ curve25519_mul_noinline(b, t0, z); /* b = 9 */ + /* 11 */ curve25519_mul_noinline(a, b, a); /* a = 11 */ + /* 22 */ curve25519_square_times(t0, a, 1); + /* 2^5 - 2^0 = 31 */ curve25519_mul_noinline(b, t0, b); + /* 2^250 - 2^0 */ curve25519_pow_two5mtwo0_two250mtwo0(b); + /* 2^255 - 2^5 */ curve25519_square_times(b, b, 5); + /* 2^255 - 21 */ curve25519_mul_noinline(out, b, a); +} + +/* + * z^((p-5)/8) = z^(2^252 - 3) + */ +void curve25519_pow_two252m3(bignum25519 two252m3, const bignum25519 z) { + bignum25519 ALIGN(16) b, c, t0; + + /* 2 */ curve25519_square_times(c, z, 1); /* c = 2 */ + /* 8 */ curve25519_square_times(t0, c, 2); /* t0 = 8 */ + /* 9 */ curve25519_mul_noinline(b, t0, z); /* b = 9 */ + /* 11 */ curve25519_mul_noinline(c, b, c); /* c = 11 */ + /* 22 */ curve25519_square_times(t0, c, 1); + /* 2^5 - 2^0 = 31 */ curve25519_mul_noinline(b, t0, b); + /* 2^250 - 2^0 */ curve25519_pow_two5mtwo0_two250mtwo0(b); + /* 2^252 - 2^2 */ curve25519_square_times(b, b, 2); + /* 2^252 - 3 */ curve25519_mul_noinline(two252m3, b, z); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.h b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.h new file mode 100644 index 000000000..62fde9099 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_helpers.h @@ -0,0 +1,22 @@ +/* + Public domain by Andrew M. + See: https://github.com/floodyberry/curve25519-donna + + Curve25519 implementation agnostic helpers +*/ + +/* + * In: b = 2^5 - 2^0 + * Out: b = 2^250 - 2^0 + */ +void curve25519_pow_two5mtwo0_two250mtwo0(bignum25519 b); + +/* + * z^(p - 2) = z(2^255 - 21) + */ +void curve25519_recip(bignum25519 out, const bignum25519 z); + +/* + * z^((p-5)/8) = z^(2^252 - 3) + */ +void curve25519_pow_two252m3(bignum25519 two252m3, const bignum25519 z); diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.c b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.c new file mode 100644 index 000000000..ff162869a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.c @@ -0,0 +1,70 @@ +#include "ed25519_donna.h" +#include "ed25519.h" + +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * mypublic: the packed little endian x coordinate of the resulting curve point + * n: a little endian, 32-byte number + * basepoint: a packed little endian point of the curve + */ + +void curve25519_scalarmult_donna( + curve25519_key mypublic, + const curve25519_key n, + const curve25519_key basepoint) { + bignum25519 nqpqx = {1}, nqpqz = {0}, nqz = {1}, nqx = {0}; + bignum25519 q = {0}, qx = {0}, qpqx = {0}, qqx = {0}, zzz = {0}, zmone = {0}; + size_t bit = 0, lastbit = 0; + int32_t i = 0; + + curve25519_expand(q, basepoint); + curve25519_copy(nqx, q); + + /* bit 255 is always 0, and bit 254 is always 1, so skip bit 255 and + start pre-swapped on bit 254 */ + lastbit = 1; + + /* we are doing bits 254..3 in the loop, but are swapping in bits 253..2 */ + for(i = 253; i >= 2; i--) { + curve25519_add(qx, nqx, nqz); + curve25519_sub(nqz, nqx, nqz); + curve25519_add(qpqx, nqpqx, nqpqz); + curve25519_sub(nqpqz, nqpqx, nqpqz); + curve25519_mul(nqpqx, qpqx, nqz); + curve25519_mul(nqpqz, qx, nqpqz); + curve25519_add(qqx, nqpqx, nqpqz); + curve25519_sub(nqpqz, nqpqx, nqpqz); + curve25519_square(nqpqz, nqpqz); + curve25519_square(nqpqx, qqx); + curve25519_mul(nqpqz, nqpqz, q); + curve25519_square(qx, qx); + curve25519_square(nqz, nqz); + curve25519_mul(nqx, qx, nqz); + curve25519_sub(nqz, qx, nqz); + curve25519_scalar_product(zzz, nqz, 121665); + curve25519_add(zzz, zzz, qx); + curve25519_mul(nqz, nqz, zzz); + + bit = (n[i / 8] >> (i & 7)) & 1; + curve25519_swap_conditional(nqx, nqpqx, bit ^ lastbit); + curve25519_swap_conditional(nqz, nqpqz, bit ^ lastbit); + lastbit = bit; + } + + /* the final 3 bits are always zero, so we only need to double */ + for(i = 0; i < 3; i++) { + curve25519_add(qx, nqx, nqz); + curve25519_sub(nqz, nqx, nqz); + curve25519_square(qx, qx); + curve25519_square(nqz, nqz); + curve25519_mul(nqx, qx, nqz); + curve25519_sub(nqz, qx, nqz); + curve25519_scalar_product(zzz, nqz, 121665); + curve25519_add(zzz, zzz, qx); + curve25519_mul(nqz, nqz, zzz); + } + + curve25519_recip(zmone, nqz); + curve25519_mul(nqz, nqx, zmone); + curve25519_contract(mypublic, nqz); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.h b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.h new file mode 100644 index 000000000..c6f18ed60 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/curve25519_donna_scalarmult_base.h @@ -0,0 +1,11 @@ +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * mypublic: the packed little endian x coordinate of the resulting curve point + * n: a little endian, 32-byte number + * basepoint: a packed little endian point of the curve + */ + +void curve25519_scalarmult_donna( + curve25519_key mypublic, + const curve25519_key n, + const curve25519_key basepoint); diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.c new file mode 100644 index 000000000..2ccc2021d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.c @@ -0,0 +1,336 @@ +/* + Public domain by Andrew M. + + Ed25519 reference implementation using Ed25519-donna +*/ + +/* define ED25519_SUFFIX to have it appended to the end of each public function */ +#ifdef ED25519_SUFFIX +#define ED25519_FN3(fn, suffix) fn##suffix +#define ED25519_FN2(fn, suffix) ED25519_FN3(fn, suffix) +#define ED25519_FN(fn) ED25519_FN2(fn, ED25519_SUFFIX) +#else +#define ED25519_FN(fn) fn +#endif + +#include "ed25519_donna.h" +#include "ed25519.h" + +#include "ed25519_hash_custom.h" +#include "../rand.h" +#include "../memzero.h" + +/* + Generates a (extsk[0..31]) and aExt (extsk[32..63]) +*/ +DONNA_INLINE static void ed25519_extsk(hash_512bits extsk, const ed25519_secret_key sk) { + ed25519_hash(extsk, sk, 32); + extsk[0] &= 248; + extsk[31] &= 127; + extsk[31] |= 64; +} + +static void ed25519_hram( + hash_512bits hram, + const ed25519_public_key R, + const ed25519_public_key pk, + const unsigned char* m, + size_t mlen) { + ed25519_hash_context ctx; + ed25519_hash_init(&ctx); + ed25519_hash_update(&ctx, R, 32); + ed25519_hash_update(&ctx, pk, 32); + ed25519_hash_update(&ctx, m, mlen); + ed25519_hash_final(&ctx, hram); +} + +void ED25519_FN(ed25519_publickey)(const ed25519_secret_key sk, ed25519_public_key pk) { + hash_512bits extsk = {0}; + ed25519_extsk(extsk, sk); + ed25519_publickey_ext(extsk, pk); + memzero(&extsk, sizeof(extsk)); +} + +void ED25519_FN(ed25519_cosi_commit)(ed25519_secret_key nonce, ed25519_public_key commitment) { + bignum256modm r = {0}; + ge25519 ALIGN(16) R; + unsigned char extnonce[64] = {0}; + + /* r = random512 mod L */ + random_buffer(extnonce, sizeof(extnonce)); + expand256_modm(r, extnonce, sizeof(extnonce)); + memzero(&extnonce, sizeof(extnonce)); + contract256_modm(nonce, r); + + /* R = rB */ + ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r); + memzero(&r, sizeof(r)); + ge25519_pack(commitment, &R); +} + +int ED25519_FN(ed25519_cosi_sign)( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + const ed25519_secret_key nonce, + const ed25519_public_key R, + const ed25519_public_key pk, + ed25519_cosi_signature sig) { + bignum256modm r = {0}, S = {0}, a = {0}; + hash_512bits extsk = {0}, hram = {0}; + + ed25519_extsk(extsk, sk); + + /* r */ + expand_raw256_modm(r, nonce); + if(!is_reduced256_modm(r)) return -1; + + /* S = H(R,A,m).. */ + ed25519_hram(hram, R, pk, m, mlen); + expand256_modm(S, hram, 64); + + /* S = H(R,A,m)a */ + expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); + mul256_modm(S, S, a); + memzero(&a, sizeof(a)); + + /* S = (r + H(R,A,m)a) */ + add256_modm(S, S, r); + memzero(&r, sizeof(r)); + + /* S = (r + H(R,A,m)a) mod L */ + contract256_modm(sig, S); + + return 0; +} + +void ED25519_FN(ed25519_sign_ext)( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + const ed25519_secret_key skext, + ed25519_signature RS) { + ed25519_hash_context ctx; + bignum256modm r = {0}, S = {0}, a = {0}; + ge25519 ALIGN(16) R = {0}; + ge25519 ALIGN(16) A = {0}; + ed25519_public_key pk = {0}; + hash_512bits extsk = {0}, hashr = {0}, hram = {0}; + + /* we don't stretch the key through hashing first since its already 64 bytes */ + + memcpy(extsk, sk, 32); + memcpy(extsk + 32, skext, 32); + + /* r = H(aExt[32..64], m) */ + ed25519_hash_init(&ctx); + ed25519_hash_update(&ctx, extsk + 32, 32); + ed25519_hash_update(&ctx, m, mlen); + ed25519_hash_final(&ctx, hashr); + expand256_modm(r, hashr, 64); + memzero(&hashr, sizeof(hashr)); + + /* R = rB */ + ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r); + ge25519_pack(RS, &R); + + /* a = aExt[0..31] */ + expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); + + /* A = aB */ + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + ge25519_pack(pk, &A); + + /* S = H(R,A,m).. */ + ed25519_hram(hram, RS, pk, m, mlen); + expand256_modm(S, hram, 64); + + /* S = H(R,A,m)a */ + mul256_modm(S, S, a); + memzero(&a, sizeof(a)); + + /* S = (r + H(R,A,m)a) */ + add256_modm(S, S, r); + memzero(&r, sizeof(r)); + + /* S = (r + H(R,A,m)a) mod L */ + contract256_modm(RS + 32, S); +} + +void ED25519_FN(ed25519_sign)( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + ed25519_signature RS) { + hash_512bits extsk = {0}; + ed25519_extsk(extsk, sk); + ED25519_FN(ed25519_sign_ext)(m, mlen, extsk, extsk + 32, RS); + memzero(&extsk, sizeof(extsk)); +} + +int ED25519_FN(ed25519_sign_open)( + const unsigned char* m, + size_t mlen, + const ed25519_public_key pk, + const ed25519_signature RS) { + ge25519 ALIGN(16) R = {0}, A = {0}; + hash_512bits hash = {0}; + bignum256modm hram = {0}, S = {0}; + unsigned char checkR[32] = {0}; + + if((RS[63] & 224) || !ge25519_unpack_negative_vartime(&A, pk)) return -1; + + /* hram = H(R,A,m) */ + ed25519_hram(hash, RS, pk, m, mlen); + expand256_modm(hram, hash, 64); + + /* S */ + expand_raw256_modm(S, RS + 32); + if(!is_reduced256_modm(S)) return -1; + + /* SB - H(R,A,m)A */ + ge25519_double_scalarmult_vartime(&R, &A, hram, S); + ge25519_pack(checkR, &R); + + /* check that R = SB - H(R,A,m)A */ + return ed25519_verify(RS, checkR, 32) ? 0 : -1; +} + +int ED25519_FN(ed25519_scalarmult)( + ed25519_public_key res, + const ed25519_secret_key sk, + const ed25519_public_key pk) { + bignum256modm a = {0}; + ge25519 ALIGN(16) A = {0}, P = {0}; + hash_512bits extsk = {0}; + + ed25519_extsk(extsk, sk); + expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); + + if(!ge25519_unpack_negative_vartime(&P, pk)) { + return -1; + } + + ge25519_scalarmult(&A, &P, a); + memzero(&a, sizeof(a)); + curve25519_neg(A.x, A.x); + ge25519_pack(res, &A); + return 0; +} + +#ifndef ED25519_SUFFIX + +#include "curve25519_donna_scalarmult_base.h" + +void ed25519_publickey_ext(const ed25519_secret_key extsk, ed25519_public_key pk) { + bignum256modm a = {0}; + ge25519 ALIGN(16) A = {0}; + + expand256_modm(a, extsk, 32); + + /* A = aB */ + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + memzero(&a, sizeof(a)); + ge25519_pack(pk, &A); +} + +int ed25519_cosi_combine_publickeys( + ed25519_public_key res, + CONST ed25519_public_key* pks, + size_t n) { + size_t i = 0; + ge25519 P = {0}; + ge25519_pniels sump = {0}; + ge25519_p1p1 sump1 = {0}; + + if(n == 1) { + memcpy(res, pks, sizeof(ed25519_public_key)); + return 0; + } + if(!ge25519_unpack_negative_vartime(&P, pks[i++])) { + return -1; + } + ge25519_full_to_pniels(&sump, &P); + while(i < n - 1) { + if(!ge25519_unpack_negative_vartime(&P, pks[i++])) { + return -1; + } + ge25519_pnielsadd(&sump, &P, &sump); + } + if(!ge25519_unpack_negative_vartime(&P, pks[i++])) { + return -1; + } + ge25519_pnielsadd_p1p1(&sump1, &P, &sump, 0); + ge25519_p1p1_to_partial(&P, &sump1); + curve25519_neg(P.x, P.x); + ge25519_pack(res, &P); + return 0; +} + +void ed25519_cosi_combine_signatures( + ed25519_signature res, + const ed25519_public_key R, + CONST ed25519_cosi_signature* sigs, + size_t n) { + bignum256modm s = {0}, t = {0}; + size_t i = 0; + + expand256_modm(s, sigs[i++], 32); + while(i < n) { + expand256_modm(t, sigs[i++], 32); + add256_modm(s, s, t); + } + memcpy(res, R, 32); + contract256_modm(res + 32, s); +} + +/* + Fast Curve25519 basepoint scalar multiplication +*/ +void curve25519_scalarmult_basepoint(curve25519_key pk, const curve25519_key e) { + curve25519_key ec = {0}; + bignum256modm s = {0}; + bignum25519 ALIGN(16) yplusz = {0}, zminusy = {0}; + ge25519 ALIGN(16) p = {0}; + size_t i = 0; + + /* clamp */ + for(i = 0; i < 32; i++) ec[i] = e[i]; + ec[0] &= 248; + ec[31] &= 127; + ec[31] |= 64; + + expand_raw256_modm(s, ec); + memzero(&ec, sizeof(ec)); + + /* scalar * basepoint */ + ge25519_scalarmult_base_niels(&p, ge25519_niels_base_multiples, s); + memzero(&s, sizeof(s)); + + /* u = (y + z) / (z - y) */ + curve25519_add(yplusz, p.y, p.z); + curve25519_sub(zminusy, p.z, p.y); + curve25519_recip(zminusy, zminusy); + curve25519_mul(yplusz, yplusz, zminusy); + curve25519_contract(pk, yplusz); +} + +void curve25519_scalarmult( + curve25519_key mypublic, + const curve25519_key secret, + const curve25519_key basepoint) { + curve25519_key e = {0}; + size_t i = 0; + + for(i = 0; i < 32; ++i) e[i] = secret[i]; + e[0] &= 0xf8; + e[31] &= 0x7f; + e[31] |= 0x40; + curve25519_scalarmult_donna(mypublic, e, basepoint); + memzero(&e, sizeof(e)); +} + +#endif // ED25519_SUFFIX diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.h new file mode 100644 index 000000000..f2f0017b6 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519.h @@ -0,0 +1,78 @@ +#ifndef ED25519_H +#define ED25519_H + +#include "../options.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef unsigned char ed25519_signature[64]; +typedef unsigned char ed25519_public_key[32]; +typedef unsigned char ed25519_secret_key[32]; + +typedef unsigned char curve25519_key[32]; + +typedef unsigned char ed25519_cosi_signature[32]; + +void ed25519_publickey(const ed25519_secret_key sk, ed25519_public_key pk); +void ed25519_publickey_ext(const ed25519_secret_key extsk, ed25519_public_key pk); + +int ed25519_sign_open( + const unsigned char* m, + size_t mlen, + const ed25519_public_key pk, + const ed25519_signature RS); +void ed25519_sign( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + ed25519_signature RS); +void ed25519_sign_ext( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + const ed25519_secret_key skext, + ed25519_signature RS); + +int ed25519_scalarmult( + ed25519_public_key res, + const ed25519_secret_key sk, + const ed25519_public_key pk); + +void curve25519_scalarmult( + curve25519_key mypublic, + const curve25519_key secret, + const curve25519_key basepoint); +void curve25519_scalarmult_basepoint(curve25519_key mypublic, const curve25519_key secret); + +#if !defined(__GNUC__) || __GNUC__ > 4 +#define CONST const +#else +#define CONST +#endif + +int ed25519_cosi_combine_publickeys( + ed25519_public_key res, + CONST ed25519_public_key* pks, + size_t n); +void ed25519_cosi_combine_signatures( + ed25519_signature res, + const ed25519_public_key R, + CONST ed25519_cosi_signature* sigs, + size_t n); +void ed25519_cosi_commit(ed25519_secret_key nonce, ed25519_public_key commitment); +int ed25519_cosi_sign( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key key, + const ed25519_secret_key nonce, + const ed25519_public_key R, + const ed25519_public_key pk, + ed25519_cosi_signature sig); + +#if defined(__cplusplus) +} +#endif + +#endif // ED25519_H diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna.h new file mode 100644 index 000000000..00746ab8d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna.h @@ -0,0 +1,52 @@ +/* + Public domain by Andrew M. + Modified from the amd64-51-30k implementation by + Daniel J. Bernstein + Niels Duif + Tanja Lange + Peter Schwabe + Bo-Yin Yang +*/ + +#ifndef ED25519_DONNA_H +#define ED25519_DONNA_H + +#include "ed25519_donna_portable.h" + +#include "curve25519_donna_32bit.h" + +#include "curve25519_donna_helpers.h" + +#include "modm_donna_32bit.h" + +typedef unsigned char hash_512bits[64]; + +/* + * Arithmetic on the twisted Edwards curve -x^2 + y^2 = 1 + dx^2y^2 + * with d = -(121665/121666) = 37095705934669439343138083508754565189542113879843219016388785533085940283555 + * Base point: (15112221349535400772501151409588531511454012693041857206046113283949847762202,46316835694926478169428394003475163141307993866256225615783033603165251855960); + */ + +typedef struct ge25519_t { + bignum25519 x, y, z, t; +} ge25519; + +typedef struct ge25519_p1p1_t { + bignum25519 x, y, z, t; +} ge25519_p1p1; + +typedef struct ge25519_niels_t { + bignum25519 ysubx, xaddy, t2d; +} ge25519_niels; + +typedef struct ge25519_pniels_t { + bignum25519 ysubx, xaddy, z, t2d; +} ge25519_pniels; + +#include "ed25519_donna_basepoint_table.h" + +#include "ed25519_donna_32bit_tables.h" + +#include "ed25519_donna_impl_base.h" + +#endif diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.c new file mode 100644 index 000000000..4b932dd0f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.c @@ -0,0 +1,1049 @@ +#include "ed25519_donna.h" + +const ge25519 ALIGN(16) ge25519_basepoint = { + {0x0325d51a, + 0x018b5823, + 0x00f6592a, + 0x0104a92d, + 0x01a4b31d, + 0x01d6dc5c, + 0x027118fe, + 0x007fd814, + 0x013cd6e5, + 0x0085a4db}, + {0x02666658, + 0x01999999, + 0x00cccccc, + 0x01333333, + 0x01999999, + 0x00666666, + 0x03333333, + 0x00cccccc, + 0x02666666, + 0x01999999}, + {0x00000001, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000}, + {0x01b7dda3, + 0x01a2ace9, + 0x025eadbb, + 0x0003ba8a, + 0x0083c27e, + 0x00abe37d, + 0x01274732, + 0x00ccacdd, + 0x00fd78b7, + 0x019e1d7c}}; + +/* + d +*/ + +const bignum25519 ALIGN(16) ge25519_ecd = { + 0x035978a3, + 0x00d37284, + 0x03156ebd, + 0x006a0a0e, + 0x0001c029, + 0x0179e898, + 0x03a03cbb, + 0x01ce7198, + 0x02e2b6ff, + 0x01480db3}; + +const bignum25519 ALIGN(16) ge25519_ec2d = { + 0x02b2f159, + 0x01a6e509, + 0x022add7a, + 0x00d4141d, + 0x00038052, + 0x00f3d130, + 0x03407977, + 0x019ce331, + 0x01c56dff, + 0x00901b67}; + +/* + sqrt(-1) +*/ + +const bignum25519 ALIGN(16) ge25519_sqrtneg1 = { + 0x020ea0b0, + 0x0186c9d2, + 0x008f189d, + 0x0035697f, + 0x00bd0c60, + 0x01fbd7a7, + 0x02804c9e, + 0x01e16569, + 0x0004fc1d, + 0x00ae0c92}; + +const ge25519_niels ALIGN(16) ge25519_niels_sliding_multiples[32] = { + {{0x0340913e, + 0x000e4175, + 0x03d673a2, + 0x002e8a05, + 0x03f4e67c, + 0x008f8a09, + 0x00c21a34, + 0x004cf4b8, + 0x01298f81, + 0x0113f4be}, + {0x018c3b85, + 0x0124f1bd, + 0x01c325f7, + 0x0037dc60, + 0x033e4cb7, + 0x003d42c2, + 0x01a44c32, + 0x014ca4e1, + 0x03a33d4b, + 0x001f3e74}, + {0x037aaa68, + 0x00448161, + 0x0093d579, + 0x011e6556, + 0x009b67a0, + 0x0143598c, + 0x01bee5ee, + 0x00b50b43, + 0x0289f0c6, + 0x01bc45ed}}, + {{0x00fcd265, + 0x0047fa29, + 0x034faacc, + 0x01ef2e0d, + 0x00ef4d4f, + 0x014bd6bd, + 0x00f98d10, + 0x014c5026, + 0x007555bd, + 0x00aae456}, + {0x00ee9730, + 0x016c2a13, + 0x017155e4, + 0x01874432, + 0x00096a10, + 0x01016732, + 0x01a8014f, + 0x011e9823, + 0x01b9a80f, + 0x01e85938}, + {0x01d0d889, + 0x01a4cfc3, + 0x034c4295, + 0x0110e1ae, + 0x0162508c, + 0x00f2db4c, + 0x0072a2c6, + 0x0098da2e, + 0x02f12b9b, + 0x0168a09a}}, + {{0x0047d6ba, + 0x0060b0e9, + 0x0136eff2, + 0x008a5939, + 0x03540053, + 0x0064a087, + 0x02788e5c, + 0x00be7c67, + 0x033eb1b5, + 0x005529f9}, + {0x00a5bb33, + 0x00af1102, + 0x01a05442, + 0x001e3af7, + 0x02354123, + 0x00bfec44, + 0x01f5862d, + 0x00dd7ba3, + 0x03146e20, + 0x00a51733}, + {0x012a8285, + 0x00f6fc60, + 0x023f9797, + 0x003e85ee, + 0x009c3820, + 0x01bda72d, + 0x01b3858d, + 0x00d35683, + 0x0296b3bb, + 0x010eaaf9}}, + {{0x023221b1, + 0x01cb26aa, + 0x0074f74d, + 0x0099ddd1, + 0x01b28085, + 0x00192c3a, + 0x013b27c9, + 0x00fc13bd, + 0x01d2e531, + 0x0075bb75}, + {0x004ea3bf, + 0x00973425, + 0x001a4d63, + 0x01d59cee, + 0x01d1c0d4, + 0x00542e49, + 0x01294114, + 0x004fce36, + 0x029283c9, + 0x01186fa9}, + {0x01b8b3a2, + 0x00db7200, + 0x00935e30, + 0x003829f5, + 0x02cc0d7d, + 0x0077adf3, + 0x0220dd2c, + 0x0014ea53, + 0x01c6a0f9, + 0x01ea7eec}}, + {{0x039d8064, + 0x01885f80, + 0x00337e6d, + 0x01b7a902, + 0x02628206, + 0x015eb044, + 0x01e30473, + 0x0191f2d9, + 0x011fadc9, + 0x01270169}, + {0x02a8632f, + 0x0199e2a9, + 0x00d8b365, + 0x017a8de2, + 0x02994279, + 0x0086f5b5, + 0x0119e4e3, + 0x01eb39d6, + 0x0338add7, + 0x00d2e7b4}, + {0x0045af1b, + 0x013a2fe4, + 0x0245e0d6, + 0x014538ce, + 0x038bfe0f, + 0x01d4cf16, + 0x037e14c9, + 0x0160d55e, + 0x0021b008, + 0x01cf05c8}}, + {{0x01864348, + 0x01d6c092, + 0x0070262b, + 0x014bb844, + 0x00fb5acd, + 0x008deb95, + 0x003aaab5, + 0x00eff474, + 0x00029d5c, + 0x0062ad66}, + {0x02802ade, + 0x01c02122, + 0x01c4e5f7, + 0x00781181, + 0x039767fb, + 0x01703406, + 0x0342388b, + 0x01f5e227, + 0x022546d8, + 0x0109d6ab}, + {0x016089e9, + 0x00cb317f, + 0x00949b05, + 0x01099417, + 0x000c7ad2, + 0x011a8622, + 0x0088ccda, + 0x01290886, + 0x022b53df, + 0x00f71954}}, + {{0x027fbf93, + 0x01c04ecc, + 0x01ed6a0d, + 0x004cdbbb, + 0x02bbf3af, + 0x00ad5968, + 0x01591955, + 0x0094f3a2, + 0x02d17602, + 0x00099e20}, + {0x02007f6d, + 0x003088a8, + 0x03db77ee, + 0x00d5ade6, + 0x02fe12ce, + 0x0107ba07, + 0x0107097d, + 0x00482a6f, + 0x02ec346f, + 0x008d3f5f}, + {0x032ea378, + 0x0028465c, + 0x028e2a6c, + 0x018efc6e, + 0x0090df9a, + 0x01a7e533, + 0x039bfc48, + 0x010c745d, + 0x03daa097, + 0x0125ee9b}}, + {{0x028ccf0b, + 0x00f36191, + 0x021ac081, + 0x012154c8, + 0x034e0a6e, + 0x01b25192, + 0x00180403, + 0x01d7eea1, + 0x00218d05, + 0x010ed735}, + {0x03cfeaa0, + 0x01b300c4, + 0x008da499, + 0x0068c4e1, + 0x0219230a, + 0x01f2d4d0, + 0x02defd60, + 0x00e565b7, + 0x017f12de, + 0x018788a4}, + {0x03d0b516, + 0x009d8be6, + 0x03ddcbb3, + 0x0071b9fe, + 0x03ace2bd, + 0x01d64270, + 0x032d3ec9, + 0x01084065, + 0x0210ae4d, + 0x01447584}}, + {{0x0020de87, + 0x00e19211, + 0x01b68102, + 0x00b5ac97, + 0x022873c0, + 0x01942d25, + 0x01271394, + 0x0102073f, + 0x02fe2482, + 0x01c69ff9}, + {0x010e9d81, + 0x019dbbe5, + 0x0089f258, + 0x006e06b8, + 0x02951883, + 0x018f1248, + 0x019b3237, + 0x00bc7553, + 0x024ddb85, + 0x01b4c964}, + {0x01c8c854, + 0x0060ae29, + 0x01406d8e, + 0x01cff2f9, + 0x00cff451, + 0x01778d0c, + 0x03ac8c41, + 0x01552e59, + 0x036559ee, + 0x011d1b12}}, + {{0x00741147, + 0x0151b219, + 0x01092690, + 0x00e877e6, + 0x01f4d6bb, + 0x0072a332, + 0x01cd3b03, + 0x00dadff2, + 0x0097db5e, + 0x0086598d}, + {0x01c69a2b, + 0x01decf1b, + 0x02c2fa6e, + 0x013b7c4f, + 0x037beac8, + 0x013a16b5, + 0x028e7bda, + 0x01f6e8ac, + 0x01e34fe9, + 0x01726947}, + {0x01f10e67, + 0x003c73de, + 0x022b7ea2, + 0x010f32c2, + 0x03ff776a, + 0x00142277, + 0x01d38b88, + 0x00776138, + 0x03c60822, + 0x01201140}}, + {{0x0236d175, + 0x0008748e, + 0x03c6476d, + 0x013f4cdc, + 0x02eed02a, + 0x00838a47, + 0x032e7210, + 0x018bcbb3, + 0x00858de4, + 0x01dc7826}, + {0x00a37fc7, + 0x0127b40b, + 0x01957884, + 0x011d30ad, + 0x02816683, + 0x016e0e23, + 0x00b76be4, + 0x012db115, + 0x02516506, + 0x0154ce62}, + {0x00451edf, + 0x00bd749e, + 0x03997342, + 0x01cc2c4c, + 0x00eb6975, + 0x01a59508, + 0x03a516cf, + 0x00c228ef, + 0x0168ff5a, + 0x01697b47}}, + {{0x00527359, + 0x01783156, + 0x03afd75c, + 0x00ce56dc, + 0x00e4b970, + 0x001cabe9, + 0x029e0f6d, + 0x0188850c, + 0x0135fefd, + 0x00066d80}, + {0x02150e83, + 0x01448abf, + 0x02bb0232, + 0x012bf259, + 0x033c8268, + 0x00711e20, + 0x03fc148f, + 0x005e0e70, + 0x017d8bf9, + 0x0112b2e2}, + {0x02134b83, + 0x001a0517, + 0x0182c3cc, + 0x00792182, + 0x0313d799, + 0x001a3ed7, + 0x0344547e, + 0x01f24a0d, + 0x03de6ad2, + 0x00543127}}, + {{0x00dca868, + 0x00618f27, + 0x015a1709, + 0x00ddc38a, + 0x0320fd13, + 0x0036168d, + 0x0371ab06, + 0x01783fc7, + 0x0391e05f, + 0x01e29b5d}, + {0x01471138, + 0x00fca542, + 0x00ca31cf, + 0x01ca7bad, + 0x0175bfbc, + 0x01a708ad, + 0x03bce212, + 0x01244215, + 0x0075bb99, + 0x01acad68}, + {0x03a0b976, + 0x01dc12d1, + 0x011aab17, + 0x00aba0ba, + 0x029806cd, + 0x0142f590, + 0x018fd8ea, + 0x01a01545, + 0x03c4ad55, + 0x01c971ff}}, + {{0x00d098c0, + 0x000afdc7, + 0x006cd230, + 0x01276af3, + 0x03f905b2, + 0x0102994c, + 0x002eb8a4, + 0x015cfbeb, + 0x025f855f, + 0x01335518}, + {0x01cf99b2, + 0x0099c574, + 0x01a69c88, + 0x00881510, + 0x01cd4b54, + 0x0112109f, + 0x008abdc5, + 0x0074647a, + 0x0277cb1f, + 0x01e53324}, + {0x02ac5053, + 0x01b109b0, + 0x024b095e, + 0x016997b3, + 0x02f26bb6, + 0x00311021, + 0x00197885, + 0x01d0a55a, + 0x03b6fcc8, + 0x01c020d5}}, + {{0x02584a34, + 0x00e7eee0, + 0x03257a03, + 0x011e95a3, + 0x011ead91, + 0x00536202, + 0x00b1ce24, + 0x008516c6, + 0x03669d6d, + 0x004ea4a8}, + {0x00773f01, + 0x0019c9ce, + 0x019f6171, + 0x01d4afde, + 0x02e33323, + 0x01ad29b6, + 0x02ead1dc, + 0x01ed51a5, + 0x01851ad0, + 0x001bbdfa}, + {0x00577de5, + 0x00ddc730, + 0x038b9952, + 0x00f281ae, + 0x01d50390, + 0x0002e071, + 0x000780ec, + 0x010d448d, + 0x01f8a2af, + 0x00f0a5b7}}, + {{0x031f2541, + 0x00d34bae, + 0x0323ff9d, + 0x003a056d, + 0x02e25443, + 0x00a1ad05, + 0x00d1bee8, + 0x002f7f8e, + 0x03007477, + 0x002a24b1}, + {0x0114a713, + 0x01457e76, + 0x032255d5, + 0x01cc647f, + 0x02a4bdef, + 0x0153d730, + 0x00118bcf, + 0x00f755ff, + 0x013490c7, + 0x01ea674e}, + {0x02bda3e8, + 0x00bb490d, + 0x00f291ea, + 0x000abf40, + 0x01dea321, + 0x002f9ce0, + 0x00b2b193, + 0x00fa54b5, + 0x0128302f, + 0x00a19d8b}}, + {{0x022ef5bd, + 0x01638af3, + 0x038c6f8a, + 0x01a33a3d, + 0x039261b2, + 0x01bb89b8, + 0x010bcf9d, + 0x00cf42a9, + 0x023d6f17, + 0x01da1bca}, + {0x00e35b25, + 0x000d824f, + 0x0152e9cf, + 0x00ed935d, + 0x020b8460, + 0x01c7b83f, + 0x00c969e5, + 0x01a74198, + 0x0046a9d9, + 0x00cbc768}, + {0x01597c6a, + 0x0144a99b, + 0x00a57551, + 0x0018269c, + 0x023c464c, + 0x0009b022, + 0x00ee39e1, + 0x0114c7f2, + 0x038a9ad2, + 0x01584c17}}, + {{0x03b0c0d5, + 0x00b30a39, + 0x038a6ce4, + 0x01ded83a, + 0x01c277a6, + 0x01010a61, + 0x0346d3eb, + 0x018d995e, + 0x02f2c57c, + 0x000c286b}, + {0x0092aed1, + 0x0125e37b, + 0x027ca201, + 0x001a6b6b, + 0x03290f55, + 0x0047ba48, + 0x018d916c, + 0x01a59062, + 0x013e35d4, + 0x0002abb1}, + {0x003ad2aa, + 0x007ddcc0, + 0x00c10f76, + 0x0001590b, + 0x002cfca6, + 0x000ed23e, + 0x00ee4329, + 0x00900f04, + 0x01c24065, + 0x0082fa70}}, + {{0x02025e60, + 0x003912b8, + 0x0327041c, + 0x017e5ee5, + 0x02c0ecec, + 0x015a0d1c, + 0x02b1ce7c, + 0x0062220b, + 0x0145067e, + 0x01a5d931}, + {0x009673a6, + 0x00e1f609, + 0x00927c2a, + 0x016faa37, + 0x01650ef0, + 0x016f63b5, + 0x03cd40e1, + 0x003bc38f, + 0x0361f0ac, + 0x01d42acc}, + {0x02f81037, + 0x008ca0e8, + 0x017e23d1, + 0x011debfe, + 0x01bcbb68, + 0x002e2563, + 0x03e8add6, + 0x000816e5, + 0x03fb7075, + 0x0153e5ac}}, + {{0x02b11ecd, + 0x016bf185, + 0x008f22ef, + 0x00e7d2bb, + 0x0225d92e, + 0x00ece785, + 0x00508873, + 0x017e16f5, + 0x01fbe85d, + 0x01e39a0e}, + {0x01669279, + 0x017c810a, + 0x024941f5, + 0x0023ebeb, + 0x00eb7688, + 0x005760f1, + 0x02ca4146, + 0x0073cde7, + 0x0052bb75, + 0x00f5ffa7}, + {0x03b8856b, + 0x00cb7dcd, + 0x02f14e06, + 0x001820d0, + 0x01d74175, + 0x00e59e22, + 0x03fba550, + 0x00484641, + 0x03350088, + 0x01c3c9a3}}, + {{0x00dcf355, + 0x0104481c, + 0x0022e464, + 0x01f73fe7, + 0x00e03325, + 0x0152b698, + 0x02ef769a, + 0x00973663, + 0x00039b8c, + 0x0101395b}, + {0x01805f47, + 0x019160ec, + 0x03832cd0, + 0x008b06eb, + 0x03d4d717, + 0x004cb006, + 0x03a75b8f, + 0x013b3d30, + 0x01cfad88, + 0x01f034d1}, + {0x0078338a, + 0x01c7d2e3, + 0x02bc2b23, + 0x018b3f05, + 0x0280d9aa, + 0x005f3d44, + 0x0220a95a, + 0x00eeeb97, + 0x0362aaec, + 0x00835d51}}, + {{0x01b9f543, + 0x013fac4d, + 0x02ad93ae, + 0x018ef464, + 0x0212cdf7, + 0x01138ba9, + 0x011583ab, + 0x019c3d26, + 0x028790b4, + 0x00e2e2b6}, + {0x033bb758, + 0x01f0dbf1, + 0x03734bd1, + 0x0129b1e5, + 0x02b3950e, + 0x003bc922, + 0x01a53ec8, + 0x018c5532, + 0x006f3cee, + 0x00ae3c79}, + {0x0351f95d, + 0x0012a737, + 0x03d596b8, + 0x017658fe, + 0x00ace54a, + 0x008b66da, + 0x0036c599, + 0x012a63a2, + 0x032ceba1, + 0x00126bac}}, + {{0x03dcfe7e, + 0x019f4f18, + 0x01c81aee, + 0x0044bc2b, + 0x00827165, + 0x014f7c13, + 0x03b430f0, + 0x00bf96cc, + 0x020c8d62, + 0x01471997}, + {0x01fc7931, + 0x001f42dd, + 0x00ba754a, + 0x005bd339, + 0x003fbe49, + 0x016b3930, + 0x012a159c, + 0x009f83b0, + 0x03530f67, + 0x01e57b85}, + {0x02ecbd81, + 0x0096c294, + 0x01fce4a9, + 0x017701a5, + 0x0175047d, + 0x00ee4a31, + 0x012686e5, + 0x008efcd4, + 0x0349dc54, + 0x01b3466f}}, + {{0x02179ca3, + 0x01d86414, + 0x03f0afd0, + 0x00305964, + 0x015c7428, + 0x0099711e, + 0x015d5442, + 0x00c71014, + 0x01b40b2e, + 0x01d483cf}, + {0x01afc386, + 0x01984859, + 0x036203ff, + 0x0045c6a8, + 0x0020a8aa, + 0x00990baa, + 0x03313f10, + 0x007ceede, + 0x027429e4, + 0x017806ce}, + {0x039357a1, + 0x0142f8f4, + 0x0294a7b6, + 0x00eaccf4, + 0x0259edb3, + 0x01311e6e, + 0x004d326f, + 0x0130c346, + 0x01ccef3c, + 0x01c424b2}}, + {{0x0364918c, + 0x00148fc0, + 0x01638a7b, + 0x01a1fd5b, + 0x028ad013, + 0x0081e5a4, + 0x01a54f33, + 0x0174e101, + 0x003d0257, + 0x003a856c}, + {0x00051dcf, + 0x00f62b1d, + 0x0143d0ad, + 0x0042adbd, + 0x000fda90, + 0x01743ceb, + 0x0173e5e4, + 0x017bc749, + 0x03b7137a, + 0x0105ce96}, + {0x00f9218a, + 0x015b8c7c, + 0x00e102f8, + 0x0158d7e2, + 0x0169a5b8, + 0x00b2f176, + 0x018b347a, + 0x014cfef2, + 0x0214a4e3, + 0x017f1595}}, + {{0x006d7ae5, + 0x0195c371, + 0x0391e26d, + 0x0062a7c6, + 0x003f42ab, + 0x010dad86, + 0x024f8198, + 0x01542b2a, + 0x0014c454, + 0x0189c471}, + {0x0390988e, + 0x00b8799d, + 0x02e44912, + 0x0078e2e6, + 0x00075654, + 0x01923eed, + 0x0040cd72, + 0x00a37c76, + 0x0009d466, + 0x00c8531d}, + {0x02651770, + 0x00609d01, + 0x0286c265, + 0x0134513c, + 0x00ee9281, + 0x005d223c, + 0x035c760c, + 0x00679b36, + 0x0073ecb8, + 0x016faa50}}, + {{0x02c89be4, + 0x016fc244, + 0x02f38c83, + 0x018beb72, + 0x02b3ce2c, + 0x0097b065, + 0x034f017b, + 0x01dd957f, + 0x00148f61, + 0x00eab357}, + {0x0343d2f8, + 0x003398fc, + 0x011e368e, + 0x00782a1f, + 0x00019eea, + 0x00117b6f, + 0x0128d0d1, + 0x01a5e6bb, + 0x01944f1b, + 0x012b41e1}, + {0x03318301, + 0x018ecd30, + 0x0104d0b1, + 0x0038398b, + 0x03726701, + 0x019da88c, + 0x002d9769, + 0x00a7a681, + 0x031d9028, + 0x00ebfc32}}, + {{0x0220405e, + 0x0171face, + 0x02d930f8, + 0x017f6d6a, + 0x023b8c47, + 0x0129d5f9, + 0x02972456, + 0x00a3a524, + 0x006f4cd2, + 0x004439fa}, + {0x00c53505, + 0x0190c2fd, + 0x00507244, + 0x009930f9, + 0x01a39270, + 0x01d327c6, + 0x0399bc47, + 0x01cfe13d, + 0x0332bd99, + 0x00b33e7d}, + {0x0203f5e4, + 0x003627b5, + 0x00018af8, + 0x01478581, + 0x004a2218, + 0x002e3bb7, + 0x039384d0, + 0x0146ea62, + 0x020b9693, + 0x0017155f}}, + {{0x03c97e6f, + 0x00738c47, + 0x03b5db1f, + 0x01808fcf, + 0x01e8fc98, + 0x01ed25dd, + 0x01bf5045, + 0x00eb5c2b, + 0x0178fe98, + 0x01b85530}, + {0x01c20eb0, + 0x01aeec22, + 0x030b9eee, + 0x01b7d07e, + 0x0187e16f, + 0x014421fb, + 0x009fa731, + 0x0040b6d7, + 0x00841861, + 0x00a27fbc}, + {0x02d69abf, + 0x0058cdbf, + 0x0129f9ec, + 0x013c19ae, + 0x026c5b93, + 0x013a7fe7, + 0x004bb2ba, + 0x0063226f, + 0x002a95ca, + 0x01abefd9}}, + {{0x02f5d2c1, + 0x00378318, + 0x03734fb5, + 0x01258073, + 0x0263f0f6, + 0x01ad70e0, + 0x01b56d06, + 0x01188fbd, + 0x011b9503, + 0x0036d2e1}, + {0x0113a8cc, + 0x01541c3e, + 0x02ac2bbc, + 0x01d95867, + 0x01f47459, + 0x00ead489, + 0x00ab5b48, + 0x01db3b45, + 0x00edb801, + 0x004b024f}, + {0x00b8190f, + 0x011fe4c2, + 0x00621f82, + 0x010508d7, + 0x001a5a76, + 0x00c7d7fd, + 0x03aab96d, + 0x019cd9dc, + 0x019c6635, + 0x00ceaa1e}}, + {{0x01085cf2, + 0x01fd47af, + 0x03e3f5e1, + 0x004b3e99, + 0x01e3d46a, + 0x0060033c, + 0x015ff0a8, + 0x0150cdd8, + 0x029e8e21, + 0x008cf1bc}, + {0x00156cb1, + 0x003d623f, + 0x01a4f069, + 0x00d8d053, + 0x01b68aea, + 0x01ca5ab6, + 0x0316ae43, + 0x0134dc44, + 0x001c8d58, + 0x0084b343}, + {0x0318c781, + 0x0135441f, + 0x03a51a5e, + 0x019293f4, + 0x0048bb37, + 0x013d3341, + 0x0143151e, + 0x019c74e1, + 0x00911914, + 0x0076ddde}}, + {{0x006bc26f, + 0x00d48e5f, + 0x00227bbe, + 0x00629ea8, + 0x01ea5f8b, + 0x0179a330, + 0x027a1d5f, + 0x01bf8f8e, + 0x02d26e2a, + 0x00c6b65e}, + {0x01701ab6, + 0x0051da77, + 0x01b4b667, + 0x00a0ce7c, + 0x038ae37b, + 0x012ac852, + 0x03a0b0fe, + 0x0097c2bb, + 0x00a017d2, + 0x01eb8b2a}, + {0x0120b962, + 0x0005fb42, + 0x0353b6fd, + 0x0061f8ce, + 0x007a1463, + 0x01560a64, + 0x00e0a792, + 0x01907c92, + 0x013a6622, + 0x007b47f1}}}; diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.h new file mode 100644 index 000000000..f76a832cf --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_32bit_tables.h @@ -0,0 +1,17 @@ +extern const ge25519 ALIGN(16) ge25519_basepoint; + +/* + d +*/ + +extern const bignum25519 ALIGN(16) ge25519_ecd; + +extern const bignum25519 ALIGN(16) ge25519_ec2d; + +/* + sqrt(-1) +*/ + +extern const bignum25519 ALIGN(16) ge25519_sqrtneg1; + +extern const ge25519_niels ALIGN(16) ge25519_niels_sliding_multiples[32]; diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.c new file mode 100644 index 000000000..29d814675 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.c @@ -0,0 +1,1796 @@ +#include "ed25519_donna.h" + +/* multiples of the base point in packed {ysubx, xaddy, t2d} form */ +const uint8_t ALIGN(16) ge25519_niels_base_multiples[256][96] = { + {0x3e, 0x91, 0x40, 0xd7, 0x05, 0x39, 0x10, 0x9d, 0xb3, 0xbe, 0x40, 0xd1, 0x05, 0x9f, + 0x39, 0xfd, 0x09, 0x8a, 0x8f, 0x68, 0x34, 0x84, 0xc1, 0xa5, 0x67, 0x12, 0xf8, 0x98, + 0x92, 0x2f, 0xfd, 0x44, 0x85, 0x3b, 0x8c, 0xf5, 0xc6, 0x93, 0xbc, 0x2f, 0x19, 0x0e, + 0x8c, 0xfb, 0xc6, 0x2d, 0x93, 0xcf, 0xc2, 0x42, 0x3d, 0x64, 0x98, 0x48, 0x0b, 0x27, + 0x65, 0xba, 0xd4, 0x33, 0x3a, 0x9d, 0xcf, 0x07, 0x59, 0xbb, 0x6f, 0x4b, 0x67, 0x15, + 0xbd, 0xdb, 0xea, 0xa5, 0xa2, 0xee, 0x00, 0x3f, 0xe1, 0x41, 0xfa, 0xc6, 0x57, 0xc9, + 0x1c, 0x9d, 0xd4, 0xcd, 0xca, 0xec, 0x16, 0xaf, 0x1f, 0xbe, 0x0e, 0x4f}, + {0xa8, 0xd5, 0xb4, 0x42, 0x60, 0xa5, 0x99, 0x8a, 0xf6, 0xac, 0x60, 0x4e, 0x0c, 0x81, + 0x2b, 0x8f, 0xaa, 0x37, 0x6e, 0xb1, 0x6b, 0x23, 0x9e, 0xe0, 0x55, 0x25, 0xc9, 0x69, + 0xa6, 0x95, 0xb5, 0x6b, 0xd7, 0x71, 0x3c, 0x93, 0xfc, 0xe7, 0x24, 0x92, 0xb5, 0xf5, + 0x0f, 0x7a, 0x96, 0x9d, 0x46, 0x9f, 0x02, 0x07, 0xd6, 0xe1, 0x65, 0x9a, 0xa6, 0x5a, + 0x2e, 0x2e, 0x7d, 0xa8, 0x3f, 0x06, 0x0c, 0x59, 0x02, 0x68, 0xd3, 0xda, 0xaa, 0x7e, + 0x34, 0x6e, 0x05, 0x48, 0xee, 0x83, 0x93, 0x59, 0xf3, 0xba, 0x26, 0x68, 0x07, 0xe6, + 0x10, 0xbe, 0xca, 0x3b, 0xb8, 0xd1, 0x5e, 0x16, 0x0a, 0x4f, 0x31, 0x49}, + {0x65, 0xd2, 0xfc, 0xa4, 0xe8, 0x1f, 0x61, 0x56, 0x7d, 0xba, 0xc1, 0xe5, 0xfd, 0x53, + 0xd3, 0x3b, 0xbd, 0xd6, 0x4b, 0x21, 0x1a, 0xf3, 0x31, 0x81, 0x62, 0xda, 0x5b, 0x55, + 0x87, 0x15, 0xb9, 0x2a, 0x30, 0x97, 0xee, 0x4c, 0xa8, 0xb0, 0x25, 0xaf, 0x8a, 0x4b, + 0x86, 0xe8, 0x30, 0x84, 0x5a, 0x02, 0x32, 0x67, 0x01, 0x9f, 0x02, 0x50, 0x1b, 0xc1, + 0xf4, 0xf8, 0x80, 0x9a, 0x1b, 0x4e, 0x16, 0x7a, 0x34, 0x48, 0x67, 0xf1, 0xf4, 0x11, + 0xf2, 0x9b, 0x95, 0xf8, 0x2d, 0xf6, 0x17, 0x6b, 0x4e, 0xb8, 0x4e, 0x2a, 0x72, 0x5b, + 0x07, 0x6f, 0xde, 0xd7, 0x21, 0x2a, 0xbb, 0x63, 0xb9, 0x04, 0x9a, 0x54}, + {0xbf, 0x18, 0x68, 0x05, 0x0a, 0x05, 0xfe, 0x95, 0xa9, 0xfa, 0x60, 0x56, 0x71, 0x89, + 0x7e, 0x32, 0x73, 0x50, 0xa0, 0x06, 0xcd, 0xe3, 0xe8, 0xc3, 0x9a, 0xa4, 0x45, 0x74, + 0x4c, 0x3f, 0x93, 0x27, 0x9f, 0x09, 0xfc, 0x8e, 0xb9, 0x51, 0x73, 0x28, 0x38, 0x25, + 0xfd, 0x7d, 0xf4, 0xc6, 0x65, 0x67, 0x65, 0x92, 0x0a, 0xfb, 0x3d, 0x8d, 0x34, 0xca, + 0x27, 0x87, 0xe5, 0x21, 0x03, 0x91, 0x0e, 0x68, 0xb0, 0x26, 0x14, 0xe5, 0xec, 0x45, + 0x1e, 0xbf, 0x94, 0x0f, 0xba, 0x6d, 0x3d, 0xc6, 0x2b, 0xe3, 0xc0, 0x52, 0xf8, 0x8c, + 0xd5, 0x74, 0x29, 0xe4, 0x18, 0x4c, 0xe6, 0xb0, 0xb1, 0x79, 0xf0, 0x44}, + {0xba, 0xd6, 0x47, 0xa4, 0xc3, 0x82, 0x91, 0x7f, 0xb7, 0x29, 0x27, 0x4b, 0xd1, 0x14, + 0x00, 0xd5, 0x87, 0xa0, 0x64, 0xb8, 0x1c, 0xf1, 0x3c, 0xe3, 0xf3, 0x55, 0x1b, 0xeb, + 0x73, 0x7e, 0x4a, 0x15, 0x33, 0xbb, 0xa5, 0x08, 0x44, 0xbc, 0x12, 0xa2, 0x02, 0xed, + 0x5e, 0xc7, 0xc3, 0x48, 0x50, 0x8d, 0x44, 0xec, 0xbf, 0x5a, 0x0c, 0xeb, 0x1b, 0xdd, + 0xeb, 0x06, 0xe2, 0x46, 0xf1, 0xcc, 0x45, 0x29, 0xb3, 0x03, 0xd0, 0xe7, 0x79, 0xa1, + 0x32, 0xc8, 0x7e, 0x4d, 0x12, 0x00, 0x0a, 0x9d, 0x72, 0x5f, 0xf3, 0x8f, 0x6d, 0x0e, + 0xa1, 0xd4, 0xc1, 0x62, 0x98, 0x7a, 0xb2, 0x38, 0x59, 0xac, 0xb8, 0x68}, + {0xa4, 0x8c, 0x7d, 0x7b, 0xb6, 0x06, 0x98, 0x49, 0x39, 0x27, 0xd2, 0x27, 0x84, 0xe2, + 0x5b, 0x57, 0xb9, 0x53, 0x45, 0x20, 0xe7, 0x5c, 0x08, 0xbb, 0x84, 0x78, 0x41, 0xae, + 0x41, 0x4c, 0xb6, 0x38, 0x31, 0x71, 0x15, 0x77, 0xeb, 0xee, 0x0c, 0x3a, 0x88, 0xaf, + 0xc8, 0x00, 0x89, 0x15, 0x27, 0x9b, 0x36, 0xa7, 0x59, 0xda, 0x68, 0xb6, 0x65, 0x80, + 0xbd, 0x38, 0xcc, 0xa2, 0xb6, 0x7b, 0xe5, 0x51, 0xa4, 0xe3, 0x9d, 0x68, 0x91, 0xad, + 0x9d, 0x8f, 0x37, 0x91, 0xfb, 0xf8, 0x28, 0x24, 0x5f, 0x17, 0x88, 0xb9, 0xcf, 0x9f, + 0x32, 0xb5, 0x0a, 0x05, 0x9f, 0xc0, 0x54, 0x13, 0xa2, 0xdf, 0x65, 0x78}, + {0xb1, 0x21, 0x32, 0xaa, 0x9a, 0x2c, 0x6f, 0xba, 0xa7, 0x23, 0xba, 0x3b, 0x53, 0x21, + 0xa0, 0x6c, 0x3a, 0x2c, 0x19, 0x92, 0x4f, 0x76, 0xea, 0x9d, 0xe0, 0x17, 0x53, 0x2e, + 0x5d, 0xdd, 0x6e, 0x1d, 0xbf, 0xa3, 0x4e, 0x94, 0xd0, 0x5c, 0x1a, 0x6b, 0xd2, 0xc0, + 0x9d, 0xb3, 0x3a, 0x35, 0x70, 0x74, 0x49, 0x2e, 0x54, 0x28, 0x82, 0x52, 0xb2, 0x71, + 0x7e, 0x92, 0x3c, 0x28, 0x69, 0xea, 0x1b, 0x46, 0x36, 0xda, 0x0f, 0xab, 0xac, 0x8a, + 0x7a, 0x21, 0xc8, 0x49, 0x35, 0x3d, 0x54, 0xc6, 0x28, 0xa5, 0x68, 0x75, 0xab, 0x13, + 0x8b, 0x5b, 0xd0, 0x37, 0x37, 0xbc, 0x2c, 0x3a, 0x62, 0xef, 0x3c, 0x23}, + {0xd9, 0x34, 0x92, 0xf3, 0xed, 0x5d, 0xa7, 0xe2, 0xf9, 0x58, 0xb5, 0xe1, 0x80, 0x76, + 0x3d, 0x96, 0xfb, 0x23, 0x3c, 0x6e, 0xac, 0x41, 0x27, 0x2c, 0xc3, 0x01, 0x0e, 0x32, + 0xa1, 0x24, 0x90, 0x3a, 0x8f, 0x3e, 0xdd, 0x04, 0x66, 0x59, 0xb7, 0x59, 0x2c, 0x70, + 0x88, 0xe2, 0x77, 0x03, 0xb3, 0x6c, 0x23, 0xc3, 0xd9, 0x5e, 0x66, 0x9c, 0x33, 0xb1, + 0x2f, 0xe5, 0xbc, 0x61, 0x60, 0xe7, 0x15, 0x09, 0x7e, 0xa3, 0x34, 0xa8, 0x35, 0xe8, + 0x7d, 0xdf, 0xea, 0x57, 0x98, 0x68, 0xda, 0x9c, 0xe1, 0x8b, 0x26, 0xb3, 0x67, 0x71, + 0x36, 0x85, 0x11, 0x2c, 0xc2, 0xd5, 0xef, 0xdb, 0xd9, 0xb3, 0x9e, 0x58}, + {0x5e, 0x51, 0xaa, 0x49, 0x54, 0x63, 0x5b, 0xed, 0x3a, 0x82, 0xc6, 0x0b, 0x9f, 0xc4, + 0x65, 0xa8, 0xc4, 0xd1, 0x42, 0x5b, 0xe9, 0x1f, 0x0c, 0x85, 0xb9, 0x15, 0xd3, 0x03, + 0x6f, 0x6d, 0xd7, 0x30, 0x1d, 0x9c, 0x2f, 0x63, 0x0e, 0xdd, 0xcc, 0x2e, 0x15, 0x31, + 0x89, 0x76, 0x96, 0xb6, 0xd0, 0x51, 0x58, 0x7a, 0x63, 0xa8, 0x6b, 0xb7, 0xdf, 0x52, + 0x39, 0xef, 0x0e, 0xa0, 0x49, 0x7d, 0xd3, 0x6d, 0xc7, 0xe4, 0x06, 0x21, 0x17, 0x44, + 0x44, 0x6c, 0x69, 0x7f, 0x8d, 0x92, 0x80, 0xd6, 0x53, 0xfb, 0x26, 0x3f, 0x4d, 0x69, + 0xa4, 0x9e, 0x73, 0xb4, 0xb0, 0x4b, 0x86, 0x2e, 0x11, 0x97, 0xc6, 0x10}, + {0xde, 0x5f, 0xbe, 0x7d, 0x27, 0xc4, 0x93, 0x64, 0xa2, 0x7e, 0xad, 0x19, 0xad, 0x4f, + 0x5d, 0x26, 0x90, 0x45, 0x30, 0x46, 0xc8, 0xdf, 0x00, 0x0e, 0x09, 0xfe, 0x66, 0xed, + 0xab, 0x1c, 0xe6, 0x25, 0x05, 0xc8, 0x58, 0x83, 0xa0, 0x2a, 0xa6, 0x0c, 0x47, 0x42, + 0x20, 0x7a, 0xe3, 0x4a, 0x3d, 0x6a, 0xdc, 0xed, 0x11, 0x3b, 0xa6, 0xd3, 0x64, 0x74, + 0xef, 0x06, 0x08, 0x55, 0xaf, 0x9b, 0xbf, 0x03, 0x04, 0x66, 0x58, 0xcc, 0x28, 0xe1, + 0x13, 0x3f, 0x7e, 0x74, 0x59, 0xb4, 0xec, 0x73, 0x58, 0x6f, 0xf5, 0x68, 0x12, 0xcc, + 0xed, 0x3d, 0xb6, 0xa0, 0x2c, 0xe2, 0x86, 0x45, 0x63, 0x78, 0x6d, 0x56}, + {0x34, 0x08, 0xc1, 0x9c, 0x9f, 0xa4, 0x37, 0x16, 0x51, 0xc4, 0x9b, 0xa8, 0xd5, 0x56, + 0x8e, 0xbc, 0xdb, 0xd2, 0x7f, 0x7f, 0x0f, 0xec, 0xb5, 0x1c, 0xd9, 0x35, 0xcc, 0x5e, + 0xca, 0x5b, 0x97, 0x33, 0xd0, 0x2f, 0x5a, 0xc6, 0x85, 0x42, 0x05, 0xa1, 0xc3, 0x67, + 0x16, 0xf3, 0x2a, 0x11, 0x64, 0x6c, 0x58, 0xee, 0x1a, 0x73, 0x40, 0xe2, 0x0a, 0x68, + 0x2a, 0xb2, 0x93, 0x47, 0xf3, 0xa5, 0xfb, 0x14, 0xd4, 0xf7, 0x85, 0x69, 0x16, 0x46, + 0xd7, 0x3c, 0x57, 0x00, 0xc8, 0xc9, 0x84, 0x5e, 0x3e, 0x59, 0x1e, 0x13, 0x61, 0x7b, + 0xb6, 0xf2, 0xc3, 0x2f, 0x6c, 0x52, 0xfc, 0x83, 0xea, 0x9c, 0x82, 0x14}, + {0xc2, 0x95, 0xdd, 0x97, 0x84, 0x7b, 0x43, 0xff, 0xa7, 0xb5, 0x4e, 0xaa, 0x30, 0x4e, + 0x74, 0x6c, 0x8b, 0xe8, 0x85, 0x3c, 0x61, 0x5d, 0x0c, 0x9e, 0x73, 0x81, 0x75, 0x5f, + 0x1e, 0xc7, 0xd9, 0x2f, 0xb8, 0xec, 0x71, 0x4e, 0x2f, 0x0b, 0xe7, 0x21, 0xe3, 0x77, + 0xa4, 0x40, 0xb9, 0xdd, 0x56, 0xe6, 0x80, 0x4f, 0x1d, 0xce, 0xce, 0x56, 0x65, 0xbf, + 0x7e, 0x7b, 0x5d, 0x53, 0xc4, 0x3b, 0xfc, 0x05, 0xdd, 0xde, 0xaf, 0x52, 0xae, 0xb3, + 0xb8, 0x24, 0xcf, 0x30, 0x3b, 0xed, 0x8c, 0x63, 0x95, 0x34, 0x95, 0x81, 0xbe, 0xa9, + 0x83, 0xbc, 0xa4, 0x33, 0x04, 0x1f, 0x65, 0x5c, 0x47, 0x67, 0x37, 0x37}, + {0xd9, 0xad, 0xd1, 0x40, 0xfd, 0x99, 0xba, 0x2f, 0x27, 0xd0, 0xf4, 0x96, 0x6f, 0x16, + 0x07, 0xb3, 0xae, 0x3b, 0xf0, 0x15, 0x52, 0xf0, 0x63, 0x43, 0x99, 0xf9, 0x18, 0x3b, + 0x6c, 0xa5, 0xbe, 0x1f, 0x90, 0x65, 0x24, 0x14, 0xcb, 0x95, 0x40, 0x63, 0x35, 0x55, + 0xc1, 0x16, 0x40, 0x14, 0x12, 0xef, 0x60, 0xbc, 0x10, 0x89, 0x0c, 0x14, 0x38, 0x9e, + 0x8c, 0x7c, 0x90, 0x30, 0x57, 0x90, 0xf5, 0x6b, 0x8a, 0x5b, 0x41, 0xe1, 0xf1, 0x78, + 0xa7, 0x0f, 0x7e, 0xa7, 0xc3, 0xba, 0xf7, 0x9f, 0x40, 0x06, 0x50, 0x9a, 0xa2, 0x9a, + 0xb8, 0xd7, 0x52, 0x6f, 0x56, 0x5a, 0x63, 0x7a, 0xf6, 0x1c, 0x52, 0x02}, + {0x94, 0x52, 0x9d, 0x0a, 0x0b, 0xee, 0x3f, 0x51, 0x66, 0x5a, 0xdf, 0x0f, 0x5c, 0xe7, + 0x98, 0x8f, 0xce, 0x07, 0xe1, 0xbf, 0x88, 0x86, 0x61, 0xd4, 0xed, 0x2c, 0x38, 0x71, + 0x7e, 0x0a, 0xa0, 0x3f, 0xe4, 0x5e, 0x2f, 0x77, 0x20, 0x67, 0x14, 0xb1, 0xce, 0x9a, + 0x07, 0x96, 0xb1, 0x94, 0xf8, 0xe8, 0x4a, 0x82, 0xac, 0x00, 0x4d, 0x22, 0xf8, 0x4a, + 0xc4, 0x6c, 0xcd, 0xf7, 0xd9, 0x53, 0x17, 0x00, 0x34, 0xdb, 0x3d, 0x96, 0x2d, 0x23, + 0x69, 0x3c, 0x58, 0x38, 0x97, 0xb4, 0xda, 0x87, 0xde, 0x1d, 0x85, 0xf2, 0x91, 0xa0, + 0xf9, 0xd1, 0xd7, 0xaa, 0xb6, 0xed, 0x48, 0xa0, 0x2f, 0xfe, 0xb5, 0x12}, + {0x4d, 0xe3, 0xfc, 0x96, 0xc4, 0xfb, 0xf0, 0x71, 0xed, 0x5b, 0xf3, 0xad, 0x6b, 0x82, + 0xb9, 0x73, 0x61, 0xc5, 0x28, 0xff, 0x61, 0x72, 0x04, 0xd2, 0x6f, 0x20, 0xb1, 0x6f, + 0xf9, 0x76, 0x9b, 0x74, 0x92, 0x1e, 0x6f, 0xad, 0x26, 0x7c, 0x2b, 0xdf, 0x13, 0x89, + 0x4b, 0x50, 0x23, 0xd3, 0x66, 0x4b, 0xc3, 0x8b, 0x1c, 0x75, 0xc0, 0x9d, 0x40, 0x8c, + 0xb8, 0xc7, 0x96, 0x07, 0xc2, 0x93, 0x7e, 0x6f, 0x05, 0xae, 0xa6, 0xae, 0x04, 0xf6, + 0x5a, 0x1f, 0x99, 0x9c, 0xe4, 0xbe, 0xf1, 0x51, 0x23, 0xc1, 0x66, 0x6b, 0xff, 0xee, + 0xb5, 0x08, 0xa8, 0x61, 0x51, 0x21, 0xe0, 0x01, 0x0f, 0xc1, 0xce, 0x0f}, + {0x44, 0x1e, 0xfe, 0x49, 0xa6, 0x58, 0x4d, 0x64, 0x7e, 0x77, 0xad, 0x31, 0xa2, 0xae, + 0xfc, 0x21, 0xd2, 0xd0, 0x7f, 0x88, 0x5a, 0x1c, 0x44, 0x02, 0xf3, 0x11, 0xc5, 0x83, + 0x71, 0xaa, 0x01, 0x49, 0x45, 0x4e, 0x24, 0xc4, 0x9d, 0xd2, 0xf2, 0x3d, 0x0a, 0xde, + 0xd8, 0x93, 0x74, 0x0e, 0x02, 0x2b, 0x4d, 0x21, 0x0c, 0x82, 0x7e, 0x06, 0xc8, 0x6c, + 0x0a, 0xb9, 0xea, 0x6f, 0x16, 0x79, 0x37, 0x41, 0xf0, 0xf8, 0x1a, 0x8c, 0x54, 0xb7, + 0xb1, 0x08, 0xb4, 0x99, 0x62, 0x24, 0x7c, 0x7a, 0x0f, 0xce, 0x39, 0xd9, 0x06, 0x1e, + 0xf9, 0xb0, 0x60, 0xf7, 0x13, 0x12, 0x6d, 0x72, 0x7b, 0x88, 0xbb, 0x41}, + {0xbe, 0x46, 0x43, 0x74, 0x44, 0x7d, 0xe8, 0x40, 0x25, 0x2b, 0xb5, 0x15, 0xd4, 0xda, + 0x48, 0x1d, 0x3e, 0x60, 0x3b, 0xa1, 0x18, 0x8a, 0x3a, 0x7c, 0xf7, 0xbd, 0xcd, 0x2f, + 0xc1, 0x28, 0xb7, 0x4e, 0xae, 0x91, 0x66, 0x7c, 0x59, 0x4c, 0x23, 0x7e, 0xc8, 0xb4, + 0x85, 0x0a, 0x3d, 0x9d, 0x88, 0x64, 0xe7, 0xfa, 0x4a, 0x35, 0x0c, 0xc9, 0xe2, 0xda, + 0x1d, 0x9e, 0x6a, 0x0c, 0x07, 0x1e, 0x87, 0x0a, 0x89, 0x89, 0xbc, 0x4b, 0x99, 0xb5, + 0x01, 0x33, 0x60, 0x42, 0xdd, 0x5b, 0x3a, 0xae, 0x6b, 0x73, 0x3c, 0x9e, 0xd5, 0x19, + 0xe2, 0xad, 0x61, 0x0d, 0x64, 0xd4, 0x85, 0x26, 0x0f, 0x30, 0xe7, 0x3e}, + {0xb7, 0xd6, 0x7d, 0x9e, 0xe4, 0x55, 0xd2, 0xf5, 0xac, 0x1e, 0x0b, 0x61, 0x5c, 0x11, + 0x16, 0x80, 0xca, 0x87, 0xe1, 0x92, 0x5d, 0x97, 0x99, 0x3c, 0xc2, 0x25, 0x91, 0x97, + 0x62, 0x57, 0x81, 0x13, 0x18, 0x75, 0x1e, 0x84, 0x47, 0x79, 0xfa, 0x43, 0xd7, 0x46, + 0x9c, 0x63, 0x59, 0xfa, 0xc6, 0xe5, 0x74, 0x2b, 0x05, 0xe3, 0x1d, 0x5e, 0x06, 0xa1, + 0x30, 0x90, 0xb8, 0xcf, 0xa2, 0xc6, 0x47, 0x7d, 0xe0, 0xd6, 0xf0, 0x8e, 0x14, 0xd0, + 0xda, 0x3f, 0x3c, 0x6f, 0x54, 0x91, 0x9a, 0x74, 0x3e, 0x9d, 0x57, 0x81, 0xbb, 0x26, + 0x10, 0x62, 0xec, 0x71, 0x80, 0xec, 0xc9, 0x34, 0x8d, 0xf5, 0x8c, 0x14}, + {0x27, 0xf0, 0x34, 0x79, 0xf6, 0x92, 0xa4, 0x46, 0xa9, 0x0a, 0x84, 0xf6, 0xbe, 0x84, + 0x99, 0x46, 0x54, 0x18, 0x61, 0x89, 0x2a, 0xbc, 0xa1, 0x5c, 0xd4, 0xbb, 0x5d, 0xbd, + 0x1e, 0xfa, 0xf2, 0x3f, 0x6d, 0x75, 0xe4, 0x9a, 0x7d, 0x2f, 0x57, 0xe2, 0x7f, 0x48, + 0xf3, 0x88, 0xbb, 0x45, 0xc3, 0x56, 0x8d, 0xa8, 0x60, 0x69, 0x6d, 0x0b, 0xd1, 0x9f, + 0xb9, 0xa1, 0xae, 0x4e, 0xad, 0xeb, 0x8f, 0x27, 0x66, 0x39, 0x93, 0x8c, 0x1f, 0x68, + 0xaa, 0xb1, 0x98, 0x0c, 0x29, 0x20, 0x9c, 0x94, 0x21, 0x8c, 0x52, 0x3c, 0x9d, 0x21, + 0x91, 0x52, 0x11, 0x39, 0x7b, 0x67, 0x9c, 0xfe, 0x02, 0xdd, 0x04, 0x41}, + {0x2a, 0x42, 0x24, 0x11, 0x5e, 0xbf, 0xb2, 0x72, 0xb5, 0x3a, 0xa3, 0x98, 0x33, 0x0c, + 0xfa, 0xa1, 0x66, 0xb6, 0x52, 0xfa, 0x01, 0x61, 0xcb, 0x94, 0xd5, 0x53, 0xaf, 0xaf, + 0x00, 0x3b, 0x86, 0x2c, 0xb8, 0x6a, 0x09, 0xdb, 0x06, 0x4e, 0x21, 0x81, 0x35, 0x4f, + 0xe4, 0x0c, 0xc9, 0xb6, 0xa8, 0x21, 0xf5, 0x2a, 0x9e, 0x40, 0x2a, 0xc1, 0x24, 0x65, + 0x81, 0xa4, 0xfc, 0x8e, 0xa4, 0xb5, 0x65, 0x01, 0x76, 0x6a, 0x84, 0xa0, 0x74, 0xa4, + 0x90, 0xf1, 0xc0, 0x7c, 0x2f, 0xcd, 0x84, 0xf9, 0xef, 0x12, 0x8f, 0x2b, 0xaa, 0x58, + 0x06, 0x29, 0x5e, 0x69, 0xb8, 0xc8, 0xfe, 0xbf, 0xd9, 0x67, 0x1b, 0x59}, + {0xfa, 0x9b, 0xb4, 0x80, 0x1c, 0x0d, 0x2f, 0x31, 0x8a, 0xec, 0xf3, 0xab, 0x5e, 0x51, + 0x79, 0x59, 0x88, 0x1c, 0xf0, 0x9e, 0xc0, 0x33, 0x70, 0x72, 0xcb, 0x7b, 0x8f, 0xca, + 0xc7, 0x2e, 0xe0, 0x3d, 0x5d, 0xb5, 0x18, 0x9f, 0x71, 0xb3, 0xb9, 0x99, 0x1e, 0x64, + 0x8c, 0xa1, 0xfa, 0xe5, 0x65, 0xe4, 0xed, 0x05, 0x9f, 0xc2, 0x36, 0x11, 0x08, 0x61, + 0x8b, 0x12, 0x30, 0x70, 0x86, 0x4f, 0x9b, 0x48, 0xef, 0x92, 0xeb, 0x3a, 0x2d, 0x10, + 0x32, 0xd2, 0x61, 0xa8, 0x16, 0x61, 0xb4, 0x53, 0x62, 0xe1, 0x24, 0xaa, 0x0b, 0x19, + 0xe7, 0xab, 0x7e, 0x3d, 0xbf, 0xbe, 0x6c, 0x49, 0xba, 0xfb, 0xf5, 0x49}, + {0xd4, 0xcf, 0x5b, 0x8a, 0x10, 0x9a, 0x94, 0x30, 0xeb, 0x73, 0x64, 0xbc, 0x70, 0xdd, + 0x40, 0xdc, 0x1c, 0x0d, 0x7c, 0x30, 0xc1, 0x94, 0xc2, 0x92, 0x74, 0x6e, 0xfa, 0xcb, + 0x6d, 0xa8, 0x04, 0x56, 0x2e, 0x57, 0x9c, 0x1e, 0x8c, 0x62, 0x5d, 0x15, 0x41, 0x47, + 0x88, 0xc5, 0xac, 0x86, 0x4d, 0x8a, 0xeb, 0x63, 0x57, 0x51, 0xf6, 0x52, 0xa3, 0x91, + 0x5b, 0x51, 0x67, 0x88, 0xc2, 0xa6, 0xa1, 0x06, 0xb6, 0x64, 0x17, 0x7c, 0xd4, 0xd1, + 0x88, 0x72, 0x51, 0x8b, 0x41, 0xe0, 0x40, 0x11, 0x54, 0x72, 0xd1, 0xf6, 0xac, 0x18, + 0x60, 0x1a, 0x03, 0x9f, 0xc6, 0x42, 0x27, 0xfe, 0x89, 0x9e, 0x98, 0x20}, + {0x7f, 0xcc, 0x2d, 0x3a, 0xfd, 0x77, 0x97, 0x49, 0x92, 0xd8, 0x4f, 0xa5, 0x2c, 0x7c, + 0x85, 0x32, 0xa0, 0xe3, 0x07, 0xd2, 0x64, 0xd8, 0x79, 0xa2, 0x29, 0x7e, 0xa6, 0x0c, + 0x1d, 0xed, 0x03, 0x04, 0x2e, 0xec, 0xea, 0x85, 0x8b, 0x27, 0x74, 0x16, 0xdf, 0x2b, + 0xcb, 0x7a, 0x07, 0xdc, 0x21, 0x56, 0x5a, 0xf4, 0xcb, 0x61, 0x16, 0x4c, 0x0a, 0x64, + 0xd3, 0x95, 0x05, 0xf7, 0x50, 0x99, 0x0b, 0x73, 0x52, 0xc5, 0x4e, 0x87, 0x35, 0x2d, + 0x4b, 0xc9, 0x8d, 0x6f, 0x24, 0x98, 0xcf, 0xc8, 0xe6, 0xc5, 0xce, 0x35, 0xc0, 0x16, + 0xfa, 0x46, 0xcb, 0xf7, 0xcc, 0x3d, 0x30, 0x08, 0x43, 0x45, 0xd7, 0x5b}, + {0xc2, 0x4c, 0xb2, 0x28, 0x95, 0xd1, 0x9a, 0x7f, 0x81, 0xc1, 0x35, 0x63, 0x65, 0x54, + 0x6b, 0x7f, 0x36, 0x72, 0xc0, 0x4f, 0x6e, 0xb6, 0xb8, 0x66, 0x83, 0xad, 0x80, 0x73, + 0x00, 0x78, 0x3a, 0x13, 0x2a, 0x79, 0xe7, 0x15, 0x21, 0x93, 0xc4, 0x85, 0xc9, 0xdd, + 0xcd, 0xbd, 0xa2, 0x89, 0x4c, 0xc6, 0x62, 0xd7, 0xa3, 0xad, 0xa8, 0x3d, 0x1e, 0x9d, + 0x2c, 0xf8, 0x67, 0x30, 0x12, 0xdb, 0xb7, 0x5b, 0xbe, 0x62, 0xca, 0xc6, 0x67, 0xf4, + 0x61, 0x09, 0xee, 0x52, 0x19, 0x21, 0xd6, 0x21, 0xec, 0x04, 0x70, 0x47, 0xd5, 0x9b, + 0x77, 0x60, 0x23, 0x18, 0xd2, 0xe0, 0xf0, 0x58, 0x6d, 0xca, 0x0d, 0x74}, + {0x4e, 0xce, 0xcf, 0x52, 0x07, 0xee, 0x48, 0xdf, 0xb7, 0x08, 0xec, 0x06, 0xf3, 0xfa, + 0xff, 0xc3, 0xc4, 0x59, 0x54, 0xb9, 0x2a, 0x0b, 0x71, 0x05, 0x8d, 0xa3, 0x3e, 0x96, + 0xfa, 0x25, 0x1d, 0x16, 0x3c, 0x43, 0x78, 0x04, 0x57, 0x8c, 0x1a, 0x23, 0x9d, 0x43, + 0x81, 0xc2, 0x0e, 0x27, 0xb5, 0xb7, 0x9f, 0x07, 0xd9, 0xe3, 0xea, 0x99, 0xaa, 0xdb, + 0xd9, 0x03, 0x2b, 0x6c, 0x25, 0xf5, 0x03, 0x2c, 0x7d, 0xa4, 0x53, 0x7b, 0x75, 0x18, + 0x0f, 0x79, 0x79, 0x58, 0x0c, 0xcf, 0x30, 0x01, 0x7b, 0x30, 0xf9, 0xf7, 0x7e, 0x25, + 0x77, 0x3d, 0x90, 0x31, 0xaf, 0xbb, 0x96, 0xbd, 0xbd, 0x68, 0x94, 0x69}, + {0xcf, 0xfe, 0xda, 0xf4, 0x46, 0x2f, 0x1f, 0xbd, 0xf7, 0xd6, 0x7f, 0xa4, 0x14, 0x01, + 0xef, 0x7c, 0x7f, 0xb3, 0x47, 0x4a, 0xda, 0xfd, 0x1f, 0xd3, 0x85, 0x57, 0x90, 0x73, + 0xa4, 0x19, 0x52, 0x52, 0x48, 0x19, 0xa9, 0x6a, 0xe6, 0x3d, 0xdd, 0xd8, 0xcc, 0xd2, + 0xc0, 0x2f, 0xc2, 0x64, 0x50, 0x48, 0x2f, 0xea, 0xfd, 0x34, 0x66, 0x24, 0x48, 0x9b, + 0x3a, 0x2e, 0x4a, 0x6c, 0x4e, 0x1c, 0x3e, 0x29, 0xe1, 0x12, 0x51, 0x92, 0x4b, 0x13, + 0x6e, 0x37, 0xa0, 0x5d, 0xa1, 0xdc, 0xb5, 0x78, 0x37, 0x70, 0x11, 0x31, 0x1c, 0x46, + 0xaf, 0x89, 0x45, 0xb0, 0x23, 0x28, 0x03, 0x7f, 0x44, 0x5c, 0x60, 0x5b}, + {0x89, 0x7c, 0xc4, 0x20, 0x59, 0x80, 0x65, 0xb9, 0xcc, 0x8f, 0x3b, 0x92, 0x0c, 0x10, + 0xf0, 0xe7, 0x77, 0xef, 0xe2, 0x02, 0x65, 0x25, 0x01, 0x00, 0xee, 0xb3, 0xae, 0xa8, + 0xce, 0x6d, 0xa7, 0x24, 0x4c, 0xf0, 0xe7, 0xf0, 0xc6, 0xfe, 0xe9, 0x3b, 0x62, 0x49, + 0xe3, 0x75, 0x9e, 0x57, 0x6a, 0x86, 0x1a, 0xe6, 0x1d, 0x1e, 0x16, 0xef, 0x42, 0x55, + 0xd5, 0xbd, 0x5a, 0xcc, 0xf4, 0xfe, 0x12, 0x2f, 0x40, 0xc7, 0xc0, 0xdf, 0xb2, 0x22, + 0x45, 0x0a, 0x07, 0xa4, 0xc9, 0x40, 0x7f, 0x6e, 0xd0, 0x10, 0x68, 0xf6, 0xcf, 0x78, + 0x41, 0x14, 0xcf, 0xc6, 0x90, 0x37, 0xa4, 0x18, 0x25, 0x7b, 0x60, 0x5e}, + {0x18, 0x18, 0xdf, 0x6c, 0x8f, 0x1d, 0xb3, 0x58, 0xa2, 0x58, 0x62, 0xc3, 0x4f, 0xa7, + 0xcf, 0x35, 0x6e, 0x1d, 0xe6, 0x66, 0x4f, 0xff, 0xb3, 0xe1, 0xf7, 0xd5, 0xcd, 0x6c, + 0xab, 0xac, 0x67, 0x50, 0x14, 0xcf, 0x96, 0xa5, 0x1c, 0x43, 0x2c, 0xa0, 0x00, 0xe4, + 0xd3, 0xae, 0x40, 0x2d, 0xc4, 0xe3, 0xdb, 0x26, 0x0f, 0x2e, 0x80, 0x26, 0x45, 0xd2, + 0x68, 0x70, 0x45, 0x9e, 0x13, 0x33, 0x1f, 0x20, 0x51, 0x9d, 0x03, 0x08, 0x6b, 0x7f, + 0x52, 0xfd, 0x06, 0x00, 0x7c, 0x01, 0x64, 0x49, 0xb1, 0x18, 0xa8, 0xa4, 0x25, 0x2e, + 0xb0, 0x0e, 0x22, 0xd5, 0x75, 0x03, 0x46, 0x62, 0x88, 0xba, 0x7c, 0x39}, + {0xb2, 0x59, 0x59, 0xf0, 0x93, 0x30, 0xc1, 0x30, 0x76, 0x79, 0xa9, 0xe9, 0x8d, 0xa1, + 0x3a, 0xe2, 0x26, 0x5e, 0x1d, 0x72, 0x91, 0xd4, 0x2f, 0x22, 0x3a, 0x6c, 0x6e, 0x76, + 0x20, 0xd3, 0x39, 0x23, 0xe7, 0x79, 0x13, 0xc8, 0xfb, 0xc3, 0x15, 0x78, 0xf1, 0x2a, + 0xe1, 0xdd, 0x20, 0x94, 0x61, 0xa6, 0xd5, 0xfd, 0xa8, 0x85, 0xf8, 0xc0, 0xa9, 0xff, + 0x52, 0xc2, 0xe1, 0xc1, 0x22, 0x40, 0x1b, 0x77, 0xa7, 0x2f, 0x3a, 0x51, 0x86, 0xd9, + 0x7d, 0xd8, 0x08, 0xcf, 0xd4, 0xf9, 0x71, 0x9b, 0xac, 0xf5, 0xb3, 0x83, 0xa2, 0x1e, + 0x1b, 0xc3, 0x6b, 0xd0, 0x76, 0x1a, 0x97, 0x19, 0x92, 0x18, 0x1a, 0x33}, + {0xc6, 0x80, 0x4f, 0xfb, 0x45, 0x6f, 0x16, 0xf5, 0xcf, 0x75, 0xc7, 0x61, 0xde, 0xc7, + 0x36, 0x9c, 0x1c, 0xd9, 0x41, 0x90, 0x1b, 0xe8, 0xd4, 0xe3, 0x21, 0xfe, 0xbd, 0x83, + 0x6b, 0x7c, 0x16, 0x31, 0xaf, 0x72, 0x75, 0x9d, 0x3a, 0x2f, 0x51, 0x26, 0x9e, 0x4a, + 0x07, 0x68, 0x88, 0xe2, 0xcb, 0x5b, 0xc4, 0xf7, 0x80, 0x11, 0xc1, 0xc1, 0xed, 0x84, + 0x7b, 0xa6, 0x49, 0xf6, 0x9f, 0x61, 0xc9, 0x1a, 0x68, 0x10, 0x4b, 0x52, 0x42, 0x38, + 0x2b, 0xf2, 0x87, 0xe9, 0x9c, 0xee, 0x3b, 0x34, 0x68, 0x50, 0xc8, 0x50, 0x62, 0x4a, + 0x84, 0x71, 0x9d, 0xfc, 0x11, 0xb1, 0x08, 0x1f, 0x34, 0x36, 0x24, 0x61}, + {0x8d, 0x89, 0x4e, 0x87, 0xdb, 0x41, 0x9d, 0xd9, 0x20, 0xdc, 0x07, 0x6c, 0xf1, 0xa5, + 0xfe, 0x09, 0xbc, 0x9b, 0x0f, 0xd0, 0x67, 0x2c, 0x3d, 0x79, 0x40, 0xff, 0x5e, 0x9e, + 0x30, 0xe2, 0xeb, 0x46, 0x38, 0x26, 0x2d, 0x1a, 0xe3, 0x49, 0x63, 0x8b, 0x35, 0xfd, + 0xd3, 0x9b, 0x00, 0xb7, 0xdf, 0x9d, 0xa4, 0x6b, 0xa0, 0xa3, 0xb8, 0xf1, 0x8b, 0x7f, + 0x45, 0x04, 0xd9, 0x78, 0x31, 0xaa, 0x22, 0x15, 0x38, 0x49, 0x61, 0x69, 0x53, 0x2f, + 0x38, 0x2c, 0x10, 0x6d, 0x2d, 0xb7, 0x9a, 0x40, 0xfe, 0xda, 0x27, 0xf2, 0x46, 0xb6, + 0x91, 0x33, 0xc8, 0xe8, 0x6c, 0x30, 0x24, 0x05, 0xf5, 0x70, 0xfe, 0x45}, + {0x8c, 0x0b, 0x0c, 0x96, 0xa6, 0x75, 0x48, 0xda, 0x20, 0x2f, 0x0e, 0xef, 0x76, 0xd0, + 0x68, 0x5b, 0xd4, 0x8f, 0x0b, 0x3d, 0xcf, 0x51, 0xfb, 0x07, 0xd4, 0x92, 0xe3, 0xa0, + 0x23, 0x16, 0x8d, 0x42, 0x91, 0x14, 0x95, 0xc8, 0x20, 0x49, 0xf2, 0x62, 0xa2, 0x0c, + 0x63, 0x3f, 0xc8, 0x07, 0xf0, 0x05, 0xb8, 0xd4, 0xc9, 0xf5, 0xd2, 0x45, 0xbb, 0x6f, + 0x45, 0x22, 0x7a, 0xb5, 0x6d, 0x9f, 0x61, 0x16, 0xfd, 0x08, 0xa3, 0x01, 0x44, 0x4a, + 0x4f, 0x08, 0xac, 0xca, 0xa5, 0x76, 0xc3, 0x19, 0x22, 0xa8, 0x7d, 0xbc, 0xd1, 0x43, + 0x46, 0xde, 0xb8, 0xde, 0xc6, 0x38, 0xbd, 0x60, 0x2d, 0x59, 0x81, 0x1d}, + {0x5f, 0xac, 0x0d, 0xa6, 0x56, 0x87, 0x36, 0x61, 0x57, 0xdc, 0xab, 0xeb, 0x6a, 0x2f, + 0xe0, 0x17, 0x7d, 0x0f, 0xce, 0x4c, 0x2d, 0x3f, 0x19, 0x7f, 0xf0, 0xdc, 0xec, 0x89, + 0x77, 0x4a, 0x23, 0x20, 0xe8, 0xc5, 0x85, 0x7b, 0x9f, 0xb6, 0x65, 0x87, 0xb2, 0xba, + 0x68, 0xd1, 0x8b, 0x67, 0xf0, 0x6f, 0x9b, 0x0f, 0x33, 0x1d, 0x7c, 0xe7, 0x70, 0x3a, + 0x7c, 0x8e, 0xaf, 0xb0, 0x51, 0x6d, 0x5f, 0x3a, 0x52, 0xb2, 0x78, 0x71, 0xb6, 0x0d, + 0xd2, 0x76, 0x60, 0xd1, 0x1e, 0xd5, 0xf9, 0x34, 0x1c, 0x07, 0x70, 0x11, 0xe4, 0xb3, + 0x20, 0x4a, 0x2a, 0xf6, 0x66, 0xe3, 0xff, 0x3c, 0x35, 0x82, 0xd6, 0x7c}, + {0xb6, 0xfa, 0x87, 0xd8, 0x5b, 0xa4, 0xe1, 0x0b, 0x6e, 0x3b, 0x40, 0xba, 0x32, 0x6a, + 0x84, 0x2a, 0x00, 0x60, 0x6e, 0xe9, 0x12, 0x10, 0x92, 0xd9, 0x43, 0x09, 0xdc, 0x3b, + 0x86, 0xc8, 0x38, 0x28, 0xf3, 0xf4, 0xac, 0x68, 0x60, 0xcd, 0x65, 0xa6, 0xd3, 0xe3, + 0xd7, 0x3c, 0x18, 0x2d, 0xd9, 0x42, 0xd9, 0x25, 0x60, 0x33, 0x9d, 0x38, 0x59, 0x57, + 0xff, 0xd8, 0x2c, 0x2b, 0x3b, 0x25, 0xf0, 0x3e, 0x30, 0x50, 0x46, 0x4a, 0xcf, 0xb0, + 0x6b, 0xd1, 0xab, 0x77, 0xc5, 0x15, 0x41, 0x6b, 0x49, 0xfa, 0x9d, 0x41, 0xab, 0xf4, + 0x8a, 0xae, 0xcf, 0x82, 0x12, 0x28, 0xa8, 0x06, 0xa6, 0xb8, 0xdc, 0x21}, + {0xc8, 0x9f, 0x9d, 0x8c, 0x46, 0x04, 0x60, 0x5c, 0xcb, 0xa3, 0x2a, 0xd4, 0x6e, 0x09, + 0x40, 0x25, 0x9c, 0x2f, 0xee, 0x12, 0x4c, 0x4d, 0x5b, 0x12, 0xab, 0x1d, 0xa3, 0x94, + 0x81, 0xd0, 0xc3, 0x0b, 0xba, 0x31, 0x77, 0xbe, 0xfa, 0x00, 0x8d, 0x9a, 0x89, 0x18, + 0x9e, 0x62, 0x7e, 0x60, 0x03, 0x82, 0x7f, 0xd9, 0xf3, 0x43, 0x37, 0x02, 0xcc, 0xb2, + 0x8b, 0x67, 0x6f, 0x6c, 0xbf, 0x0d, 0x84, 0x5d, 0x8b, 0xe1, 0x9f, 0x30, 0x0d, 0x38, + 0x6e, 0x70, 0xc7, 0x65, 0xe1, 0xb9, 0xa6, 0x2d, 0xb0, 0x6e, 0xab, 0x20, 0xae, 0x7d, + 0x99, 0xba, 0xbb, 0x57, 0xdd, 0x96, 0xc1, 0x2a, 0x23, 0x76, 0x42, 0x3a}, + {0xfa, 0x84, 0x70, 0x8a, 0x2c, 0x43, 0x42, 0x4b, 0x45, 0xe5, 0xb9, 0xdf, 0xe3, 0x19, + 0x8a, 0x89, 0x5d, 0xe4, 0x58, 0x9c, 0x21, 0x00, 0x9f, 0xbe, 0xd1, 0xeb, 0x6d, 0xa1, + 0xce, 0x77, 0xf1, 0x1f, 0xcb, 0x7e, 0x44, 0xdb, 0x72, 0xc1, 0xf8, 0x3b, 0xbd, 0x2d, + 0x28, 0xc6, 0x1f, 0xc4, 0xcf, 0x5f, 0xfe, 0x15, 0xaa, 0x75, 0xc0, 0xff, 0xac, 0x80, + 0xf9, 0xa9, 0xe1, 0x24, 0xe8, 0xc9, 0x70, 0x07, 0xfd, 0xb5, 0xb5, 0x45, 0x9a, 0xd9, + 0x61, 0xcf, 0x24, 0x79, 0x3a, 0x1b, 0xe9, 0x84, 0x09, 0x86, 0x89, 0x3e, 0x3e, 0x30, + 0x19, 0x09, 0x30, 0xe7, 0x1e, 0x0b, 0x50, 0x41, 0xfd, 0x64, 0xf2, 0x39}, + {0x9c, 0xe2, 0xe7, 0xdb, 0x17, 0x34, 0xad, 0xa7, 0x9c, 0x13, 0x9c, 0x2b, 0x6a, 0x37, + 0x94, 0xbd, 0xa9, 0x7b, 0x59, 0x93, 0x8e, 0x1b, 0xe9, 0xa0, 0x40, 0x98, 0x88, 0x68, + 0x34, 0xd7, 0x12, 0x17, 0xe1, 0x7b, 0x09, 0xfe, 0xab, 0x4a, 0x9b, 0xd1, 0x29, 0x19, + 0xe0, 0xdf, 0xe1, 0xfc, 0x6d, 0xa4, 0xff, 0xf1, 0xa6, 0x2c, 0x94, 0x08, 0xc9, 0xc3, + 0x4e, 0xf1, 0x35, 0x2c, 0x27, 0x21, 0xc6, 0x65, 0xdd, 0x93, 0x31, 0xce, 0xf8, 0x89, + 0x2b, 0xe7, 0xbb, 0xc0, 0x25, 0xa1, 0x56, 0x33, 0x10, 0x4d, 0x83, 0xfe, 0x1c, 0x2e, + 0x3d, 0xa9, 0x19, 0x04, 0x72, 0xe2, 0x9c, 0xb1, 0x0a, 0x80, 0xf9, 0x22}, + {0xcb, 0xf8, 0x9e, 0x3e, 0x8a, 0x36, 0x5a, 0x60, 0x15, 0x47, 0x50, 0xa5, 0x22, 0xc0, + 0xe9, 0xe3, 0x8f, 0x24, 0x24, 0x5f, 0xb0, 0x48, 0x3d, 0x55, 0xe5, 0x26, 0x76, 0x64, + 0xcd, 0x16, 0xf4, 0x13, 0xac, 0xfd, 0x6e, 0x9a, 0xdd, 0x9f, 0x02, 0x42, 0x41, 0x49, + 0xa5, 0x34, 0xbe, 0xce, 0x12, 0xb9, 0x7b, 0xf3, 0xbd, 0x87, 0xb9, 0x64, 0x0f, 0x64, + 0xb4, 0xca, 0x98, 0x85, 0xd3, 0xa4, 0x71, 0x41, 0x8c, 0x4c, 0xc9, 0x99, 0xaa, 0x58, + 0x27, 0xfa, 0x07, 0xb8, 0x00, 0xb0, 0x6f, 0x6f, 0x00, 0x23, 0x92, 0x53, 0xda, 0xad, + 0xdd, 0x91, 0xd2, 0xfb, 0xab, 0xd1, 0x4b, 0x57, 0xfa, 0x14, 0x82, 0x50}, + {0x4b, 0xfe, 0xd6, 0x3e, 0x15, 0x69, 0x02, 0xc2, 0xc4, 0x77, 0x1d, 0x51, 0x39, 0x67, + 0x5a, 0xa6, 0x94, 0xaf, 0x14, 0x2c, 0x46, 0x26, 0xde, 0xcb, 0x4b, 0xa7, 0xab, 0x6f, + 0xec, 0x60, 0xf9, 0x22, 0xd6, 0x03, 0xd0, 0x53, 0xbb, 0x15, 0x1a, 0x46, 0x65, 0xc9, + 0xf3, 0xbc, 0x88, 0x28, 0x10, 0xb2, 0x5a, 0x3a, 0x68, 0x6c, 0x75, 0x76, 0xc5, 0x27, + 0x47, 0xb4, 0x6c, 0xc8, 0xa4, 0x58, 0x77, 0x3a, 0x76, 0x50, 0xae, 0x93, 0xf6, 0x11, + 0x81, 0x54, 0xa6, 0x54, 0xfd, 0x1d, 0xdf, 0x21, 0xae, 0x1d, 0x65, 0x5e, 0x11, 0xf3, + 0x90, 0x8c, 0x24, 0x12, 0x94, 0xf4, 0xe7, 0x8d, 0x5f, 0xd1, 0x9f, 0x5d}, + {0x7f, 0x72, 0x63, 0x6d, 0xd3, 0x08, 0x14, 0x03, 0x33, 0xb5, 0xc7, 0xd7, 0xef, 0x9a, + 0x37, 0x6a, 0x4b, 0xe2, 0xae, 0xcc, 0xc5, 0x8f, 0xe1, 0xa9, 0xd3, 0xbe, 0x8f, 0x4f, + 0x91, 0x35, 0x2f, 0x33, 0x1e, 0x52, 0xd7, 0xee, 0x2a, 0x4d, 0x24, 0x3f, 0x15, 0x96, + 0x2e, 0x43, 0x28, 0x90, 0x3a, 0x8e, 0xd4, 0x16, 0x9c, 0x2e, 0x77, 0xba, 0x64, 0xe1, + 0xd8, 0x98, 0xeb, 0x47, 0xfa, 0x87, 0xc1, 0x3b, 0x0c, 0xc2, 0x86, 0xea, 0x15, 0x01, + 0x47, 0x6d, 0x25, 0xd1, 0x46, 0x6c, 0xcb, 0xb7, 0x8a, 0x99, 0x88, 0x01, 0x66, 0x3a, + 0xb5, 0x32, 0x78, 0xd7, 0x03, 0xba, 0x6f, 0x90, 0xce, 0x81, 0x0d, 0x45}, + {0x75, 0x52, 0x20, 0xa6, 0xa1, 0xb6, 0x7b, 0x6e, 0x83, 0x8e, 0x3c, 0x41, 0xd7, 0x21, + 0x4f, 0xaa, 0xb2, 0x5c, 0x8f, 0xe8, 0x55, 0xd1, 0x56, 0x6f, 0xe1, 0x5b, 0x34, 0xa6, + 0x4b, 0x5d, 0xe2, 0x2d, 0x3f, 0x74, 0xae, 0x1c, 0x96, 0xd8, 0x74, 0xd0, 0xed, 0x63, + 0x1c, 0xee, 0xf5, 0x18, 0x6d, 0xf8, 0x29, 0xed, 0xf4, 0xe7, 0x5b, 0xc5, 0xbd, 0x97, + 0x08, 0xb1, 0x3a, 0x66, 0x79, 0xd2, 0xba, 0x4c, 0xcd, 0x1f, 0xd7, 0xa0, 0x24, 0x90, + 0xd1, 0x80, 0xf8, 0x8a, 0x28, 0xfb, 0x0a, 0xc2, 0x25, 0xc5, 0x19, 0x64, 0x3a, 0x5f, + 0x4b, 0x97, 0xa3, 0xb1, 0x33, 0x72, 0x00, 0xe2, 0xef, 0xbc, 0x7f, 0x7d}, + {0x01, 0x28, 0x6b, 0x26, 0x6a, 0x1e, 0xef, 0xfa, 0x16, 0x9f, 0x73, 0xd5, 0xc4, 0x68, + 0x6c, 0x86, 0x2c, 0x76, 0x03, 0x1b, 0xbc, 0x2f, 0x8a, 0xf6, 0x8d, 0x5a, 0xb7, 0x87, + 0x5e, 0x43, 0x75, 0x59, 0x94, 0x90, 0xc2, 0xf3, 0xc5, 0x5d, 0x7c, 0xcd, 0xab, 0x05, + 0x91, 0x2a, 0x9a, 0xa2, 0x81, 0xc7, 0x58, 0x30, 0x1c, 0x42, 0x36, 0x1d, 0xc6, 0x80, + 0xd7, 0xd4, 0xd8, 0xdc, 0x96, 0xd1, 0x9c, 0x4f, 0x68, 0x37, 0x7b, 0x6a, 0xd8, 0x97, + 0x92, 0x19, 0x63, 0x7a, 0xd1, 0x1a, 0x24, 0x58, 0xd0, 0xd0, 0x17, 0x0c, 0x1c, 0x5c, + 0xad, 0x9c, 0x02, 0xba, 0x07, 0x03, 0x7a, 0x38, 0x84, 0xd0, 0xcd, 0x7c}, + {0x17, 0x04, 0x26, 0x6d, 0x2c, 0x42, 0xa6, 0xdc, 0xbd, 0x40, 0x82, 0x94, 0x50, 0x3d, + 0x15, 0xae, 0x77, 0xc6, 0x68, 0xfb, 0xb4, 0xc1, 0xc0, 0xa9, 0x53, 0xcf, 0xd0, 0x61, + 0xed, 0xd0, 0x8b, 0x42, 0x93, 0xcc, 0x60, 0x67, 0x18, 0x84, 0x0c, 0x9b, 0x99, 0x2a, + 0xb3, 0x1a, 0x7a, 0x00, 0xae, 0xcd, 0x18, 0xda, 0x0b, 0x62, 0x86, 0xec, 0x8d, 0xa8, + 0x44, 0xca, 0x90, 0x81, 0x84, 0xca, 0x93, 0x35, 0xa7, 0x9a, 0x84, 0x5e, 0x9a, 0x18, + 0x13, 0x92, 0xcd, 0xfa, 0xd8, 0x65, 0x35, 0xc3, 0xd8, 0xd4, 0xd1, 0xbb, 0xfd, 0x53, + 0x5b, 0x54, 0x52, 0x8c, 0xe6, 0x63, 0x2d, 0xda, 0x08, 0x83, 0x39, 0x27}, + {0x13, 0xd4, 0x5e, 0x43, 0x28, 0x8d, 0xc3, 0x42, 0xc9, 0xcc, 0x78, 0x32, 0x60, 0xf3, + 0x50, 0xbd, 0xef, 0x03, 0xda, 0x79, 0x1a, 0xab, 0x07, 0xbb, 0x55, 0x33, 0x8c, 0xbe, + 0xae, 0x97, 0x95, 0x26, 0x53, 0x24, 0x70, 0x0a, 0x4c, 0x0e, 0xa1, 0xb9, 0xde, 0x1b, + 0x7d, 0xd5, 0x66, 0x58, 0xa2, 0x0f, 0xf7, 0xda, 0x27, 0xcd, 0xb5, 0xd9, 0xb9, 0xff, + 0xfd, 0x33, 0x2c, 0x49, 0x45, 0x29, 0x2c, 0x57, 0xbe, 0x30, 0xcd, 0xd6, 0x45, 0xc7, + 0x7f, 0xc7, 0xfb, 0xae, 0xba, 0xe3, 0xd3, 0xe8, 0xdf, 0xe4, 0x0c, 0xda, 0x5d, 0xaa, + 0x30, 0x88, 0x2c, 0xa2, 0x80, 0xca, 0x5b, 0xc0, 0x98, 0x54, 0x98, 0x7f}, + {0x17, 0xe1, 0x0b, 0x9f, 0x88, 0xce, 0x49, 0x38, 0x88, 0xa2, 0x54, 0x7b, 0x1b, 0xad, + 0x05, 0x80, 0x1c, 0x92, 0xfc, 0x23, 0x9f, 0xc3, 0xa3, 0x3d, 0x04, 0xf3, 0x31, 0x0a, + 0x47, 0xec, 0xc2, 0x76, 0x63, 0x63, 0xbf, 0x0f, 0x52, 0x15, 0x56, 0xd3, 0xa6, 0xfb, + 0x4d, 0xcf, 0x45, 0x5a, 0x04, 0x08, 0xc2, 0xa0, 0x3f, 0x87, 0xbc, 0x4f, 0xc2, 0xee, + 0xe7, 0x12, 0x9b, 0xd6, 0x3c, 0x65, 0xf2, 0x30, 0x85, 0x0c, 0xc1, 0xaa, 0x38, 0xc9, + 0x08, 0x8a, 0xcb, 0x6b, 0x27, 0xdb, 0x60, 0x9b, 0x17, 0x46, 0x70, 0xac, 0x6f, 0x0e, + 0x1e, 0xc0, 0x20, 0xa9, 0xda, 0x73, 0x64, 0x59, 0xf1, 0x73, 0x12, 0x2f}, + {0x11, 0x1e, 0xe0, 0x8a, 0x7c, 0xfc, 0x39, 0x47, 0x9f, 0xab, 0x6a, 0x4a, 0x90, 0x74, + 0x52, 0xfd, 0x2e, 0x8f, 0x72, 0x87, 0x82, 0x8a, 0xd9, 0x41, 0xf2, 0x69, 0x5b, 0xd8, + 0x2a, 0x57, 0x9e, 0x5d, 0xc0, 0x0b, 0xa7, 0x55, 0xd7, 0x8b, 0x48, 0x30, 0xe7, 0x42, + 0xd4, 0xf1, 0xa4, 0xb5, 0xd6, 0x06, 0x62, 0x61, 0x59, 0xbc, 0x9e, 0xa6, 0xd1, 0xea, + 0x84, 0xf7, 0xc5, 0xed, 0x97, 0x19, 0xac, 0x38, 0x3b, 0xb1, 0x51, 0xa7, 0x17, 0xb5, + 0x66, 0x06, 0x8c, 0x85, 0x9b, 0x7e, 0x86, 0x06, 0x7d, 0x74, 0x49, 0xde, 0x4d, 0x45, + 0x11, 0xc0, 0xac, 0xac, 0x9c, 0xe6, 0xe9, 0xbf, 0x9c, 0xcd, 0xdf, 0x22}, + {0xd9, 0x0c, 0x0d, 0xc3, 0xe0, 0xd2, 0xdb, 0x8d, 0x33, 0x43, 0xbb, 0xac, 0x5f, 0x66, + 0x8e, 0xad, 0x1f, 0x96, 0x2a, 0x32, 0x8c, 0x25, 0x6b, 0x8f, 0xc7, 0xc1, 0x48, 0x54, + 0xc0, 0x16, 0x29, 0x6b, 0xa1, 0xe0, 0x3b, 0x10, 0xb4, 0x59, 0xec, 0x56, 0x69, 0xf9, + 0x59, 0xd2, 0xec, 0xba, 0xe3, 0x2e, 0x32, 0xcd, 0xf5, 0x13, 0x94, 0xb2, 0x7c, 0x79, + 0x72, 0xe4, 0xcd, 0x24, 0x78, 0x87, 0xe9, 0x0f, 0x3b, 0x91, 0xba, 0x0a, 0xd1, 0x34, + 0xdb, 0x7e, 0x0e, 0xac, 0x6d, 0x2e, 0x82, 0xcd, 0xa3, 0x4e, 0x15, 0xf8, 0x78, 0x65, + 0xff, 0x3d, 0x08, 0x66, 0x17, 0x0a, 0xf0, 0x7f, 0x30, 0x3f, 0x30, 0x4c}, + {0x85, 0x8c, 0xb2, 0x17, 0xd6, 0x3b, 0x0a, 0xd3, 0xea, 0x3b, 0x77, 0x39, 0xb7, 0x77, + 0xd3, 0xc5, 0xbf, 0x5c, 0x6a, 0x1e, 0x8c, 0xe7, 0xc6, 0xc6, 0xc4, 0xb7, 0x2a, 0x8b, + 0xf7, 0xb8, 0x61, 0x0d, 0x00, 0x45, 0xd9, 0x0d, 0x58, 0x03, 0xfc, 0x29, 0x93, 0xec, + 0xbb, 0x6f, 0xa4, 0x7a, 0xd2, 0xec, 0xf8, 0xa7, 0xe2, 0xc2, 0x5f, 0x15, 0x0a, 0x13, + 0xd5, 0xa1, 0x06, 0xb7, 0x1a, 0x15, 0x6b, 0x41, 0xb0, 0x36, 0xc1, 0xe9, 0xef, 0xd7, + 0xa8, 0x56, 0x20, 0x4b, 0xe4, 0x58, 0xcd, 0xe5, 0x07, 0xbd, 0xab, 0xe0, 0x57, 0x1b, + 0xda, 0x2f, 0xe6, 0xaf, 0xd2, 0xe8, 0x77, 0x42, 0xf7, 0x2a, 0x1a, 0x19}, + {0x31, 0x14, 0x3c, 0xc5, 0x4b, 0xf7, 0x16, 0xce, 0xde, 0xed, 0x72, 0x20, 0xce, 0x25, + 0x97, 0x2b, 0xe7, 0x3e, 0xb2, 0xb5, 0x6f, 0xc3, 0xb9, 0xb8, 0x08, 0xc9, 0x5c, 0x0b, + 0x45, 0x0e, 0x2e, 0x7e, 0xfb, 0x0e, 0x46, 0x4f, 0x43, 0x2b, 0xe6, 0x9f, 0xd6, 0x07, + 0x36, 0xa6, 0xd4, 0x03, 0xd3, 0xde, 0x24, 0xda, 0xa0, 0xb7, 0x0e, 0x21, 0x52, 0xf0, + 0x93, 0x5b, 0x54, 0x00, 0xbe, 0x7d, 0x7e, 0x23, 0x30, 0xb4, 0x01, 0x67, 0xed, 0x75, + 0x35, 0x01, 0x10, 0xfd, 0x0b, 0x9f, 0xe6, 0x94, 0x10, 0x23, 0x22, 0x7f, 0xe4, 0x83, + 0x15, 0x0f, 0x32, 0x75, 0xe3, 0x55, 0x11, 0xb1, 0x99, 0xa6, 0xaf, 0x71}, + {0x1d, 0xb6, 0x53, 0x39, 0x9b, 0x6f, 0xce, 0x65, 0xe6, 0x41, 0xa1, 0xaf, 0xea, 0x39, + 0x58, 0xc6, 0xfe, 0x59, 0xf7, 0xa9, 0xfd, 0x5f, 0x43, 0x0f, 0x8e, 0xc2, 0xb1, 0xc2, + 0xe9, 0x42, 0x11, 0x02, 0xd6, 0x50, 0x3b, 0x47, 0x1c, 0x3c, 0x42, 0xea, 0x10, 0xef, + 0x38, 0x3b, 0x1f, 0x7a, 0xe8, 0x51, 0x95, 0xbe, 0xc9, 0xb2, 0x5f, 0xbf, 0x84, 0x9b, + 0x1c, 0x9a, 0xf8, 0x78, 0xbc, 0x1f, 0x73, 0x00, 0x80, 0x18, 0xf8, 0x48, 0x18, 0xc7, + 0x30, 0xe4, 0x19, 0xc1, 0xce, 0x5e, 0x22, 0x0c, 0x96, 0xbf, 0xe3, 0x15, 0xba, 0x6b, + 0x83, 0xe0, 0xda, 0xb6, 0x08, 0x58, 0xe1, 0x47, 0x33, 0x6f, 0x4d, 0x4c}, + {0xc9, 0x1f, 0x7d, 0xc1, 0xcf, 0xec, 0xf7, 0x18, 0x14, 0x3c, 0x40, 0x51, 0xa6, 0xf5, + 0x75, 0x6c, 0xdf, 0x0c, 0xee, 0xf7, 0x2b, 0x71, 0xde, 0xdb, 0x22, 0x7a, 0xe4, 0xa7, + 0xaa, 0xdd, 0x3f, 0x19, 0x70, 0x19, 0x8f, 0x98, 0xfc, 0xdd, 0x0c, 0x2f, 0x1b, 0xf5, + 0xb9, 0xb0, 0x27, 0x62, 0x91, 0x6b, 0xbe, 0x76, 0x91, 0x77, 0xc4, 0xb6, 0xc7, 0x6e, + 0xa8, 0x9f, 0x8f, 0xa8, 0x00, 0x95, 0xbf, 0x38, 0x6f, 0x87, 0xe8, 0x37, 0x3c, 0xc9, + 0xd2, 0x1f, 0x2c, 0x46, 0xd1, 0x18, 0x5a, 0x1e, 0xf6, 0xa2, 0x76, 0x12, 0x24, 0x39, + 0x82, 0xf5, 0x80, 0x50, 0x69, 0x49, 0x0d, 0xbf, 0x9e, 0xb9, 0x6f, 0x6a}, + {0xeb, 0x55, 0x08, 0x56, 0xbb, 0xc1, 0x46, 0x6a, 0x9d, 0xf0, 0x93, 0xf8, 0x38, 0xbb, + 0x16, 0x24, 0xc1, 0xac, 0x71, 0x8f, 0x37, 0x11, 0x1d, 0xd7, 0xea, 0x96, 0x18, 0xa3, + 0x14, 0x69, 0xf7, 0x75, 0xc6, 0x23, 0xe4, 0xb6, 0xb5, 0x22, 0xb1, 0xee, 0x8e, 0xff, + 0x86, 0xf2, 0x10, 0x70, 0x9d, 0x93, 0x8c, 0x5d, 0xcf, 0x1d, 0x83, 0x2a, 0xa9, 0x90, + 0x10, 0xeb, 0xc5, 0x42, 0x9f, 0xda, 0x6f, 0x13, 0xd1, 0xbd, 0x05, 0xa3, 0xb1, 0xdf, + 0x4c, 0xf9, 0x08, 0x2c, 0xf8, 0x9f, 0x9d, 0x4b, 0x36, 0x0f, 0x8a, 0x58, 0xbb, 0xc3, + 0xa5, 0xd8, 0x87, 0x2a, 0xba, 0xdc, 0xe8, 0x0b, 0x51, 0x83, 0x21, 0x02}, + {0x14, 0x2d, 0xad, 0x5e, 0x38, 0x66, 0xf7, 0x4a, 0x30, 0x58, 0x7c, 0xca, 0x80, 0xd8, + 0x8e, 0xa0, 0x3d, 0x1e, 0x21, 0x10, 0xe6, 0xa6, 0x13, 0x0d, 0x03, 0x6c, 0x80, 0x7b, + 0xe1, 0x1c, 0x07, 0x6a, 0x7f, 0x7a, 0x30, 0x43, 0x01, 0x71, 0x5a, 0x9d, 0x5f, 0xa4, + 0x7d, 0xc4, 0x9e, 0xde, 0x63, 0xb0, 0xd3, 0x7a, 0x92, 0xbe, 0x52, 0xfe, 0xbb, 0x22, + 0x6c, 0x42, 0x40, 0xfd, 0x41, 0xc4, 0x87, 0x13, 0xf8, 0x8a, 0x97, 0x87, 0xd1, 0xc3, + 0xd3, 0xb5, 0x13, 0x44, 0x0e, 0x7f, 0x3d, 0x5a, 0x2b, 0x72, 0xa0, 0x7c, 0x47, 0xbb, + 0x48, 0x48, 0x7b, 0x0d, 0x92, 0xdc, 0x1e, 0xaf, 0x6a, 0xb2, 0x71, 0x31}, + {0xa8, 0x4c, 0x56, 0x97, 0x90, 0x31, 0x2f, 0xa9, 0x19, 0xe1, 0x75, 0x22, 0x4c, 0xb8, + 0x7b, 0xff, 0x50, 0x51, 0x87, 0xa4, 0x37, 0xfe, 0x55, 0x4f, 0x5a, 0x83, 0xf0, 0x3c, + 0x87, 0xd4, 0x1f, 0x22, 0xd1, 0x47, 0x8a, 0xb2, 0xd8, 0xb7, 0x0d, 0xa6, 0xf1, 0xa4, + 0x70, 0x17, 0xd6, 0x14, 0xbf, 0xa6, 0x58, 0xbd, 0xdd, 0x53, 0x93, 0xf8, 0xa1, 0xd4, + 0xe9, 0x43, 0x42, 0x34, 0x63, 0x4a, 0x51, 0x6c, 0x41, 0x63, 0x15, 0x3a, 0x4f, 0x20, + 0x22, 0x23, 0x2d, 0x03, 0x0a, 0xba, 0xe9, 0xe0, 0x73, 0xfb, 0x0e, 0x03, 0x0f, 0x41, + 0x4c, 0xdd, 0xe0, 0xfc, 0xaa, 0x4a, 0x92, 0xfb, 0x96, 0xa5, 0xda, 0x48}, + {0xc7, 0x9c, 0xa5, 0x5c, 0x66, 0x8e, 0xca, 0x6e, 0xa0, 0xac, 0x38, 0x2e, 0x4b, 0x25, + 0x47, 0xa8, 0xce, 0x17, 0x1e, 0xd2, 0x08, 0xc7, 0xaf, 0x31, 0xf7, 0x4a, 0xd8, 0xca, + 0xfc, 0xd6, 0x6d, 0x67, 0x93, 0x97, 0x4c, 0xc8, 0x5d, 0x1d, 0xf6, 0x14, 0x06, 0x82, + 0x41, 0xef, 0xe3, 0xf9, 0x41, 0x99, 0xac, 0x77, 0x62, 0x34, 0x8f, 0xb8, 0xf5, 0xcd, + 0xa9, 0x79, 0x8a, 0x0e, 0xfa, 0x37, 0xc8, 0x58, 0x58, 0x90, 0xfc, 0x96, 0x85, 0x68, + 0xf9, 0x0c, 0x1b, 0xa0, 0x56, 0x7b, 0xf3, 0xbb, 0xdc, 0x1d, 0x6a, 0xd6, 0x35, 0x49, + 0x7d, 0xe7, 0xc2, 0xdc, 0x0a, 0x7f, 0xa5, 0xc6, 0xf2, 0x73, 0x4f, 0x1c}, + {0xbb, 0xa0, 0x5f, 0x30, 0xbd, 0x4f, 0x7a, 0x0e, 0xad, 0x63, 0xc6, 0x54, 0xe0, 0x4c, + 0x9d, 0x82, 0x48, 0x38, 0xe3, 0x2f, 0x83, 0xc3, 0x21, 0xf4, 0x42, 0x4c, 0xf6, 0x1b, + 0x0d, 0xc8, 0x5a, 0x79, 0x84, 0x34, 0x7c, 0xfc, 0x6e, 0x70, 0x6e, 0xb3, 0x61, 0xcf, + 0xc1, 0xc3, 0xb4, 0xc9, 0xdf, 0x73, 0xe5, 0xc7, 0x1c, 0x78, 0xc9, 0x79, 0x1d, 0xeb, + 0x5c, 0x67, 0xaf, 0x7d, 0xdb, 0x9a, 0x45, 0x70, 0xb3, 0x2b, 0xb4, 0x91, 0x49, 0xdb, + 0x91, 0x1b, 0xca, 0xdc, 0x02, 0x4b, 0x23, 0x96, 0x26, 0x57, 0xdc, 0x78, 0x8c, 0x1f, + 0xe5, 0x9e, 0xdf, 0x9f, 0xd3, 0x1f, 0xe2, 0x8c, 0x84, 0x62, 0xe1, 0x5f}, + {0x1a, 0x96, 0x94, 0xe1, 0x4f, 0x21, 0x59, 0x4e, 0x4f, 0xcd, 0x71, 0x0d, 0xc7, 0x7d, + 0xbe, 0x49, 0x2d, 0xf2, 0x50, 0x3b, 0xd2, 0xcf, 0x00, 0x93, 0x32, 0x72, 0x91, 0xfc, + 0x46, 0xd4, 0x89, 0x47, 0x08, 0xb2, 0x7c, 0x5d, 0x2d, 0x85, 0x79, 0x28, 0xe7, 0xf2, + 0x7d, 0x68, 0x70, 0xdd, 0xde, 0xb8, 0x91, 0x78, 0x68, 0x21, 0xab, 0xff, 0x0b, 0xdc, + 0x35, 0xaa, 0x7d, 0x67, 0x43, 0xc0, 0x44, 0x2b, 0x8e, 0xb7, 0x4e, 0x07, 0xab, 0x87, + 0x1c, 0x1a, 0x67, 0xf4, 0xda, 0x99, 0x8e, 0xd1, 0xc6, 0xfa, 0x67, 0x90, 0x4f, 0x48, + 0xcd, 0xbb, 0xac, 0x3e, 0xe4, 0xa4, 0xb9, 0x2b, 0xef, 0x2e, 0xc5, 0x60}, + {0xf1, 0x8b, 0xfd, 0x3b, 0xbc, 0x89, 0x5d, 0x0b, 0x1a, 0x55, 0xf3, 0xc9, 0x37, 0x92, + 0x6b, 0xb0, 0xf5, 0x28, 0x30, 0xd5, 0xb0, 0x16, 0x4c, 0x0e, 0xab, 0xca, 0xcf, 0x2c, + 0x31, 0x9c, 0xbc, 0x10, 0x11, 0x6d, 0xae, 0x7c, 0xc2, 0xc5, 0x2b, 0x70, 0xab, 0x8c, + 0xa4, 0x54, 0x9b, 0x69, 0xc7, 0x44, 0xb2, 0x2e, 0x49, 0xba, 0x56, 0x40, 0xbc, 0xef, + 0x6d, 0x67, 0xb6, 0xd9, 0x48, 0x72, 0xd7, 0x70, 0x5b, 0xa0, 0xc2, 0x3e, 0x4b, 0xe8, + 0x8a, 0xaa, 0xe0, 0x81, 0x17, 0xed, 0xf4, 0x9e, 0x69, 0x98, 0xd1, 0x85, 0x8e, 0x70, + 0xe4, 0x13, 0x45, 0x79, 0x13, 0xf4, 0x76, 0xa9, 0xd3, 0x5b, 0x75, 0x63}, + {0x53, 0x08, 0xd1, 0x2a, 0x3e, 0xa0, 0x5f, 0xb5, 0x69, 0x35, 0xe6, 0x9e, 0x90, 0x75, + 0x6f, 0x35, 0x90, 0xb8, 0x69, 0xbe, 0xfd, 0xf1, 0xf9, 0x9f, 0x84, 0x6f, 0xc1, 0x8b, + 0xc4, 0xc1, 0x8c, 0x0d, 0xb7, 0xac, 0xf1, 0x97, 0x18, 0x10, 0xc7, 0x3d, 0xd8, 0xbb, + 0x65, 0xc1, 0x5e, 0x7d, 0xda, 0x5d, 0x0f, 0x02, 0xa1, 0x0f, 0x9c, 0x5b, 0x8e, 0x50, + 0x56, 0x2a, 0xc5, 0x37, 0x17, 0x75, 0x63, 0x27, 0xa9, 0x19, 0xb4, 0x6e, 0xd3, 0x02, + 0x94, 0x02, 0xa5, 0x60, 0xb4, 0x77, 0x7e, 0x4e, 0xb4, 0xf0, 0x56, 0x49, 0x3c, 0xd4, + 0x30, 0x62, 0xa8, 0xcf, 0xe7, 0x66, 0xd1, 0x7a, 0x8a, 0xdd, 0xc2, 0x70}, + {0x0e, 0xec, 0x6f, 0x9f, 0x50, 0x94, 0x61, 0x65, 0x8d, 0x51, 0xc6, 0x46, 0xa9, 0x7e, + 0x2e, 0xee, 0x5c, 0x9b, 0xe0, 0x67, 0xf3, 0xc1, 0x33, 0x97, 0x95, 0x84, 0x94, 0x63, + 0x63, 0xac, 0x0f, 0x2e, 0x13, 0x7e, 0xed, 0xb8, 0x7d, 0x96, 0xd4, 0x91, 0x7a, 0x81, + 0x76, 0xd7, 0x0a, 0x2f, 0x25, 0x74, 0x64, 0x25, 0x85, 0x0d, 0xe0, 0x82, 0x09, 0xe4, + 0xe5, 0x3c, 0xa5, 0x16, 0x38, 0x61, 0xb8, 0x32, 0x64, 0xcd, 0x48, 0xe4, 0xbe, 0xf7, + 0xe7, 0x79, 0xd0, 0x86, 0x78, 0x08, 0x67, 0x3a, 0xc8, 0x6a, 0x2e, 0xdb, 0xe4, 0xa0, + 0xd9, 0xd4, 0x9f, 0xf8, 0x41, 0x4f, 0x5a, 0x73, 0x5c, 0x21, 0x79, 0x41}, + {0x2a, 0xed, 0xdc, 0xd7, 0xe7, 0x94, 0x70, 0x8c, 0x70, 0x9c, 0xd3, 0x47, 0xc3, 0x8a, + 0xfb, 0x97, 0x02, 0xd9, 0x06, 0xa9, 0x33, 0xe0, 0x3b, 0xe1, 0x76, 0x9d, 0xd9, 0x0c, + 0xa3, 0x44, 0x03, 0x70, 0x34, 0xcd, 0x6b, 0x28, 0xb9, 0x33, 0xae, 0xe4, 0xdc, 0xd6, + 0x9d, 0x55, 0xb6, 0x7e, 0xef, 0xb7, 0x1f, 0x8e, 0xd3, 0xb3, 0x1f, 0x14, 0x8b, 0x27, + 0x86, 0xc2, 0x41, 0x22, 0x66, 0x85, 0xfa, 0x31, 0xf4, 0x22, 0x36, 0x2e, 0x42, 0x6c, + 0x82, 0xaf, 0x2d, 0x50, 0x33, 0x98, 0x87, 0x29, 0x20, 0xc1, 0x23, 0x91, 0x38, 0x2b, + 0xe1, 0xb7, 0xc1, 0x9b, 0x89, 0x24, 0x95, 0xa9, 0x12, 0x23, 0xbb, 0x24}, + {0xc3, 0x67, 0xde, 0x32, 0x17, 0xed, 0xa8, 0xb1, 0x48, 0x49, 0x1b, 0x46, 0x18, 0x94, + 0xb4, 0x3c, 0xd2, 0xbc, 0xcf, 0x76, 0x43, 0x43, 0xbd, 0x8e, 0x08, 0x80, 0x18, 0x1e, + 0x87, 0x3e, 0xee, 0x0f, 0x6b, 0x5c, 0xf8, 0xf5, 0x2a, 0x0c, 0xf8, 0x41, 0x94, 0x67, + 0xfa, 0x04, 0xc3, 0x84, 0x72, 0x68, 0xad, 0x1b, 0xba, 0xa3, 0x99, 0xdf, 0x45, 0x89, + 0x16, 0x5d, 0xeb, 0xff, 0xf9, 0x2a, 0x1d, 0x0d, 0xdf, 0x1e, 0x62, 0x32, 0xa1, 0x8a, + 0xda, 0xa9, 0x79, 0x65, 0x22, 0x59, 0xa1, 0x22, 0xb8, 0x30, 0x93, 0xc1, 0x9a, 0xa7, + 0x7b, 0x19, 0x04, 0x40, 0x76, 0x1d, 0x53, 0x18, 0x97, 0xd7, 0xac, 0x16}, + {0x3d, 0x1d, 0x9b, 0x2d, 0xaf, 0x72, 0xdf, 0x72, 0x5a, 0x24, 0x32, 0xa4, 0x36, 0x2a, + 0x46, 0x63, 0x37, 0x96, 0xb3, 0x16, 0x79, 0xa0, 0xce, 0x3e, 0x09, 0x23, 0x30, 0xb9, + 0xf6, 0x0e, 0x3e, 0x12, 0xad, 0xb6, 0x87, 0x78, 0xc5, 0xc6, 0x59, 0xc9, 0xba, 0xfe, + 0x90, 0x5f, 0xad, 0x9e, 0xe1, 0x94, 0x04, 0xf5, 0x42, 0xa3, 0x62, 0x4e, 0xe2, 0x16, + 0x00, 0x17, 0x16, 0x18, 0x4b, 0xd3, 0x4e, 0x16, 0x9a, 0xe6, 0x2f, 0x19, 0x4c, 0xd9, + 0x7e, 0x48, 0x13, 0x15, 0x91, 0x3a, 0xea, 0x2c, 0xae, 0x61, 0x27, 0xde, 0xa4, 0xb9, + 0xd3, 0xf6, 0x7b, 0x87, 0xeb, 0xf3, 0x73, 0x10, 0xc6, 0x0f, 0xda, 0x78}, + {0x6a, 0xc6, 0x2b, 0xe5, 0x28, 0x5d, 0xf1, 0x5b, 0x8e, 0x1a, 0xf0, 0x70, 0x18, 0xe3, + 0x47, 0x2c, 0xdd, 0x8b, 0xc2, 0x06, 0xbc, 0xaf, 0x19, 0x24, 0x3a, 0x17, 0x6b, 0x25, + 0xeb, 0xde, 0x25, 0x2d, 0x94, 0x3a, 0x0c, 0x68, 0xf1, 0x80, 0x9f, 0xa2, 0xe6, 0xe7, + 0xe9, 0x1a, 0x15, 0x7e, 0xf7, 0x71, 0x73, 0x79, 0x01, 0x48, 0x58, 0xf1, 0x00, 0x11, + 0xdd, 0x8d, 0xb3, 0x16, 0xb3, 0xa4, 0x4a, 0x05, 0xb8, 0x7c, 0x26, 0x19, 0x8d, 0x46, + 0xc8, 0xdf, 0xaf, 0x4d, 0xe5, 0x66, 0x9c, 0x78, 0x28, 0x0b, 0x17, 0xec, 0x6e, 0x66, + 0x2a, 0x1d, 0xeb, 0x2a, 0x60, 0xa7, 0x7d, 0xab, 0xa6, 0x10, 0x46, 0x13}, + {0xfe, 0xb0, 0xf6, 0x8d, 0xc7, 0x8e, 0x13, 0x51, 0x1b, 0xf5, 0x75, 0xe5, 0x89, 0xda, + 0x97, 0x53, 0xb9, 0xf1, 0x7a, 0x71, 0x1d, 0x7a, 0x20, 0x09, 0x50, 0xd6, 0x20, 0x2b, + 0xba, 0xfd, 0x02, 0x21, 0x15, 0xf5, 0xd1, 0x77, 0xe7, 0x65, 0x2a, 0xcd, 0xf1, 0x60, + 0xaa, 0x8f, 0x87, 0x91, 0x89, 0x54, 0xe5, 0x06, 0xbc, 0xda, 0xbc, 0x3b, 0xb7, 0xb1, + 0xfb, 0xc9, 0x7c, 0xa9, 0xcb, 0x78, 0x48, 0x65, 0xa1, 0xe6, 0x5c, 0x05, 0x05, 0xe4, + 0x9e, 0x96, 0x29, 0xad, 0x51, 0x12, 0x68, 0xa7, 0xbc, 0x36, 0x15, 0xa4, 0x7d, 0xaa, + 0x17, 0xf5, 0x1a, 0x3a, 0xba, 0xb2, 0xec, 0x29, 0xdb, 0x25, 0xd7, 0x0a}, + {0x57, 0x24, 0x4e, 0x83, 0xb1, 0x67, 0x42, 0xdc, 0xc5, 0x1b, 0xce, 0x70, 0xb5, 0x44, + 0x75, 0xb6, 0xd7, 0x5e, 0xd1, 0xf7, 0x0b, 0x7a, 0xf0, 0x1a, 0x50, 0x36, 0xa0, 0x71, + 0xfb, 0xcf, 0xef, 0x4a, 0x85, 0x6f, 0x05, 0x9b, 0x0c, 0xbc, 0xc7, 0xfe, 0xd7, 0xff, + 0xf5, 0xe7, 0x68, 0x52, 0x7d, 0x53, 0xfa, 0xae, 0x12, 0x43, 0x62, 0xc6, 0xaf, 0x77, + 0xd9, 0x9f, 0x39, 0x02, 0x53, 0x5f, 0x67, 0x4f, 0x1e, 0x17, 0x15, 0x04, 0x36, 0x36, + 0x2d, 0xc3, 0x3b, 0x48, 0x98, 0x89, 0x11, 0xef, 0x2b, 0xcd, 0x10, 0x51, 0x94, 0xd0, + 0xad, 0x6e, 0x0a, 0x87, 0x61, 0x65, 0xa8, 0xa2, 0x72, 0xbb, 0xcc, 0x0b}, + {0xc8, 0xa9, 0xb1, 0xea, 0x2f, 0x96, 0x5e, 0x18, 0xcd, 0x7d, 0x14, 0x65, 0x35, 0xe6, + 0xe7, 0x86, 0xf2, 0x6d, 0x5b, 0xbb, 0x31, 0xe0, 0x92, 0xb0, 0x3e, 0xb7, 0xd6, 0x59, + 0xab, 0xf0, 0x24, 0x40, 0x96, 0x12, 0xfe, 0x50, 0x4c, 0x5e, 0x6d, 0x18, 0x7e, 0x9f, + 0xe8, 0xfe, 0x82, 0x7b, 0x39, 0xe0, 0xb0, 0x31, 0x70, 0x50, 0xc5, 0xf6, 0xc7, 0x3b, + 0xc2, 0x37, 0x8f, 0x10, 0x69, 0xfd, 0x78, 0x66, 0xc2, 0x63, 0x68, 0x63, 0x31, 0xfa, + 0x86, 0x15, 0xf2, 0x33, 0x2d, 0x57, 0x48, 0x8c, 0xf6, 0x07, 0xfc, 0xae, 0x9e, 0x78, + 0x9f, 0xcc, 0x73, 0x4f, 0x01, 0x47, 0xad, 0x8e, 0x10, 0xe2, 0x42, 0x2d}, + {0x9b, 0xd2, 0xdf, 0x94, 0x15, 0x13, 0xf5, 0x97, 0x6a, 0x4c, 0x3f, 0x31, 0x5d, 0x98, + 0x55, 0x61, 0x10, 0x50, 0x45, 0x08, 0x07, 0x3f, 0xa1, 0xeb, 0x22, 0xd3, 0xd2, 0xb8, + 0x08, 0x26, 0x6b, 0x67, 0x93, 0x75, 0x53, 0x0f, 0x0d, 0x7b, 0x71, 0x21, 0x4c, 0x06, + 0x1e, 0x13, 0x0b, 0x69, 0x4e, 0x91, 0x9f, 0xe0, 0x2a, 0x75, 0xae, 0x87, 0xb6, 0x1b, + 0x6e, 0x3c, 0x42, 0x9b, 0xa7, 0xf3, 0x0b, 0x42, 0x47, 0x2b, 0x5b, 0x1c, 0x65, 0xba, + 0x38, 0x81, 0x80, 0x1b, 0x1b, 0x31, 0xec, 0xb6, 0x71, 0x86, 0xb0, 0x35, 0x31, 0xbc, + 0xb1, 0x0c, 0xff, 0x7b, 0xe0, 0xf1, 0x0c, 0x9c, 0xfa, 0x2f, 0x5d, 0x74}, + {0xbd, 0xc8, 0xc9, 0x2b, 0x1e, 0x5a, 0x52, 0xbf, 0x81, 0x9d, 0x47, 0x26, 0x08, 0x26, + 0x5b, 0xea, 0xdb, 0x55, 0x01, 0xdf, 0x0e, 0xc7, 0x11, 0xd5, 0xd0, 0xf5, 0x0c, 0x96, + 0xeb, 0x3c, 0xe2, 0x1a, 0x6a, 0x4e, 0xd3, 0x21, 0x57, 0xdf, 0x36, 0x60, 0xd0, 0xb3, + 0x7b, 0x99, 0x27, 0x88, 0xdb, 0xb1, 0xfa, 0x6a, 0x75, 0xc8, 0xc3, 0x09, 0xc2, 0xd3, + 0x39, 0xc8, 0x1d, 0x4c, 0xe5, 0x5b, 0xe1, 0x06, 0x4a, 0x99, 0x32, 0x19, 0x87, 0x5d, + 0x72, 0x5b, 0xb0, 0xda, 0xb1, 0xce, 0xb5, 0x1c, 0x35, 0x32, 0x05, 0xca, 0xb7, 0xda, + 0x49, 0x15, 0xc4, 0x7d, 0xf7, 0xc1, 0x8e, 0x27, 0x61, 0xd8, 0xde, 0x58}, + {0x5c, 0xc5, 0x66, 0xf2, 0x93, 0x37, 0x17, 0xd8, 0x49, 0x4e, 0x45, 0xcc, 0xc5, 0x76, + 0xc9, 0xc8, 0xa8, 0xc3, 0x26, 0xbc, 0xf8, 0x82, 0xe3, 0x5c, 0xf9, 0xf6, 0x85, 0x54, + 0xe8, 0x9d, 0xf3, 0x2f, 0xa8, 0xc9, 0xc2, 0xb6, 0xa8, 0x5b, 0xfb, 0x2d, 0x8c, 0x59, + 0x2c, 0xf5, 0x8e, 0xef, 0xee, 0x48, 0x73, 0x15, 0x2d, 0xf1, 0x07, 0x91, 0x80, 0x33, + 0xd8, 0x5b, 0x1d, 0x53, 0x6b, 0x69, 0xba, 0x08, 0x7a, 0xc5, 0xef, 0xc3, 0xee, 0x3e, + 0xed, 0x77, 0x11, 0x48, 0xff, 0xd4, 0x17, 0x55, 0xe0, 0x04, 0xcb, 0x71, 0xa6, 0xf1, + 0x3f, 0x7a, 0x3d, 0xea, 0x54, 0xfe, 0x7c, 0x94, 0xb4, 0x33, 0x06, 0x12}, + {0x42, 0x00, 0x61, 0x91, 0x78, 0x98, 0x94, 0x0b, 0xe8, 0xfa, 0xeb, 0xec, 0x3c, 0xb1, + 0xe7, 0x4e, 0xc0, 0xa4, 0xf0, 0x94, 0x95, 0x73, 0xbe, 0x70, 0x85, 0x91, 0xd5, 0xb4, + 0x99, 0x0a, 0xd3, 0x35, 0x0a, 0x10, 0x12, 0x49, 0x47, 0x31, 0xbd, 0x82, 0x06, 0xbe, + 0x6f, 0x7e, 0x6d, 0x7b, 0x23, 0xde, 0xc6, 0x79, 0xea, 0x11, 0x19, 0x76, 0x1e, 0xe1, + 0xde, 0x3b, 0x39, 0xcb, 0xe3, 0x3b, 0x43, 0x07, 0xf4, 0x97, 0xe9, 0x5c, 0xc0, 0x44, + 0x79, 0xff, 0xa3, 0x51, 0x5c, 0xb0, 0xe4, 0x3d, 0x5d, 0x57, 0x7c, 0x84, 0x76, 0x5a, + 0xfd, 0x81, 0x33, 0x58, 0x9f, 0xda, 0xf6, 0x7a, 0xde, 0x3e, 0x87, 0x2d}, + {0x09, 0x34, 0x37, 0x43, 0x64, 0x31, 0x7a, 0x15, 0xd9, 0x81, 0xaa, 0xf4, 0xee, 0xb7, + 0xb8, 0xfa, 0x06, 0x48, 0xa6, 0xf5, 0xe6, 0xfe, 0x93, 0xb0, 0xb6, 0xa7, 0x7f, 0x70, + 0x54, 0x36, 0x77, 0x2e, 0x81, 0xf9, 0x5d, 0x4e, 0xe1, 0x02, 0x62, 0xaa, 0xf5, 0xe1, + 0x15, 0x50, 0x17, 0x59, 0x0d, 0xa2, 0x6c, 0x1d, 0xe2, 0xba, 0xd3, 0x75, 0xa2, 0x18, + 0x53, 0x02, 0x60, 0x01, 0x8a, 0x61, 0x43, 0x05, 0xc1, 0x23, 0x4c, 0x97, 0xf4, 0xbd, + 0xea, 0x0d, 0x93, 0x46, 0xce, 0x9d, 0x25, 0x0a, 0x6f, 0xaa, 0x2c, 0xba, 0x9a, 0xa2, + 0xb8, 0x2c, 0x20, 0x04, 0x0d, 0x96, 0x07, 0x2d, 0x36, 0x43, 0x14, 0x4b}, + {0x7a, 0x1f, 0x6e, 0xb6, 0xc7, 0xb7, 0xc4, 0xcc, 0x7e, 0x2f, 0x0c, 0xf5, 0x25, 0x7e, + 0x15, 0x44, 0x1c, 0xaf, 0x3e, 0x71, 0xfc, 0x6d, 0xf0, 0x3e, 0xf7, 0x63, 0xda, 0x52, + 0x67, 0x44, 0x2f, 0x58, 0xcb, 0x9c, 0x52, 0x1c, 0xe9, 0x54, 0x7c, 0x96, 0xfb, 0x35, + 0xc6, 0x64, 0x92, 0x26, 0xf6, 0x30, 0x65, 0x19, 0x12, 0x78, 0xf4, 0xaf, 0x47, 0x27, + 0x5c, 0x6f, 0xf6, 0xea, 0x18, 0x84, 0x03, 0x17, 0xe4, 0x4c, 0x32, 0x20, 0xd3, 0x7b, + 0x31, 0xc6, 0xc4, 0x8b, 0x48, 0xa4, 0xe8, 0x42, 0x10, 0xa8, 0x64, 0x13, 0x5a, 0x4e, + 0x8b, 0xf1, 0x1e, 0xb2, 0xc9, 0x8d, 0xa2, 0xcd, 0x4b, 0x1c, 0x2a, 0x0c}, + {0x47, 0x04, 0x1f, 0x6f, 0xd0, 0xc7, 0x4d, 0xd2, 0x59, 0xc0, 0x87, 0xdb, 0x3e, 0x9e, + 0x26, 0xb2, 0x8f, 0xd2, 0xb2, 0xfb, 0x72, 0x02, 0x5b, 0xd1, 0x77, 0x48, 0xf6, 0xc6, + 0xd1, 0x8b, 0x55, 0x7c, 0x45, 0x69, 0xbd, 0x69, 0x48, 0x81, 0xc4, 0xed, 0x22, 0x8d, + 0x1c, 0xbe, 0x7d, 0x90, 0x6d, 0x0d, 0xab, 0xc5, 0x5c, 0xd5, 0x12, 0xd2, 0x3b, 0xc6, + 0x83, 0xdc, 0x14, 0xa3, 0x30, 0x9b, 0x6a, 0x5a, 0x3d, 0x46, 0x96, 0xd3, 0x24, 0x15, + 0xec, 0xd0, 0xf0, 0x24, 0x5a, 0xc3, 0x8a, 0x62, 0xbb, 0x12, 0xa4, 0x5f, 0xbc, 0x1c, + 0x79, 0x3a, 0x0c, 0xa5, 0xc3, 0xaf, 0xfb, 0x0a, 0xca, 0xa5, 0x04, 0x04}, + {0xd6, 0x43, 0xa7, 0x0a, 0x07, 0x40, 0x1f, 0x8c, 0xe8, 0x5e, 0x26, 0x5b, 0xcb, 0xd0, + 0xba, 0xcc, 0xde, 0xd2, 0x8f, 0x66, 0x6b, 0x04, 0x4b, 0x57, 0x33, 0x96, 0xdd, 0xca, + 0xfd, 0x5b, 0x39, 0x46, 0xd1, 0x6f, 0x41, 0x2a, 0x1b, 0x9e, 0xbc, 0x62, 0x8b, 0x59, + 0x50, 0xe3, 0x28, 0xf7, 0xc6, 0xb5, 0x67, 0x69, 0x5d, 0x3d, 0xd8, 0x3f, 0x34, 0x04, + 0x98, 0xee, 0xf8, 0xe7, 0x16, 0x75, 0x52, 0x39, 0x9c, 0x9a, 0x5d, 0x1a, 0x2d, 0xdb, + 0x7f, 0x11, 0x2a, 0x5c, 0x00, 0xd1, 0xbc, 0x45, 0x77, 0x9c, 0xea, 0x6f, 0xd5, 0x54, + 0xf1, 0xbe, 0xd4, 0xef, 0x16, 0xd0, 0x22, 0xe8, 0x29, 0x9a, 0x57, 0x76}, + {0x17, 0x2a, 0xc0, 0x49, 0x7e, 0x8e, 0xb6, 0x45, 0x7f, 0xa3, 0xa9, 0xbc, 0xa2, 0x51, + 0xcd, 0x23, 0x1b, 0x4c, 0x22, 0xec, 0x11, 0x5f, 0xd6, 0x3e, 0xb1, 0xbd, 0x05, 0x9e, + 0xdc, 0x84, 0xa3, 0x43, 0xf2, 0x34, 0xb4, 0x52, 0x13, 0xb5, 0x3c, 0x33, 0xe1, 0x80, + 0xde, 0x93, 0x49, 0x28, 0x32, 0xd8, 0xce, 0x35, 0x0d, 0x75, 0x87, 0x28, 0x51, 0xb5, + 0xc1, 0x77, 0x27, 0x2a, 0xbb, 0x14, 0xc5, 0x02, 0x45, 0xb6, 0xf1, 0x8b, 0xda, 0xd5, + 0x4b, 0x68, 0x53, 0x4b, 0xb5, 0xf6, 0x7e, 0xd3, 0x8b, 0xfb, 0x53, 0xd2, 0xb0, 0xa9, + 0xd7, 0x16, 0x39, 0x31, 0x59, 0x80, 0x54, 0x61, 0x09, 0x92, 0x60, 0x11}, + {0xaa, 0xcf, 0xda, 0x29, 0x69, 0x16, 0x4d, 0xb4, 0x8f, 0x59, 0x13, 0x84, 0x4c, 0x9f, + 0x52, 0xda, 0x59, 0x55, 0x3d, 0x45, 0xca, 0x63, 0xef, 0xe9, 0x0b, 0x8e, 0x69, 0xc5, + 0x5b, 0x12, 0x1e, 0x35, 0xcd, 0x4d, 0x9b, 0x36, 0x16, 0x56, 0x38, 0x7a, 0x63, 0x35, + 0x5c, 0x65, 0xa7, 0x2c, 0xc0, 0x75, 0x21, 0x80, 0xf1, 0xd4, 0xf9, 0x1b, 0xc2, 0x7d, + 0x42, 0xe0, 0xe6, 0x91, 0x74, 0x7d, 0x63, 0x2f, 0xbe, 0x7b, 0xf6, 0x1a, 0x46, 0x9b, + 0xb4, 0xd4, 0x61, 0x89, 0xab, 0xc8, 0x7a, 0x03, 0x03, 0xd6, 0xfb, 0x99, 0xa6, 0xf9, + 0x9f, 0xe1, 0xde, 0x71, 0x9a, 0x2a, 0xce, 0xe7, 0x06, 0x2d, 0x18, 0x7f}, + {0xec, 0x68, 0x01, 0xab, 0x64, 0x8e, 0x7c, 0x7a, 0x43, 0xc5, 0xed, 0x15, 0x55, 0x4a, + 0x5a, 0xcb, 0xda, 0x0e, 0xcd, 0x47, 0xd3, 0x19, 0x55, 0x09, 0xb0, 0x93, 0x3e, 0x34, + 0x8c, 0xac, 0xd4, 0x67, 0x22, 0x75, 0x21, 0x8e, 0x72, 0x4b, 0x45, 0x09, 0xd8, 0xb8, + 0x84, 0xd4, 0xf4, 0xe8, 0x58, 0xaa, 0x3c, 0x90, 0x46, 0x7f, 0x4d, 0x25, 0x58, 0xd3, + 0x17, 0x52, 0x1c, 0x24, 0x43, 0xc0, 0xac, 0x44, 0x77, 0x57, 0x7a, 0x4f, 0xbb, 0x6b, + 0x7d, 0x1c, 0xe1, 0x13, 0x83, 0x91, 0xd4, 0xfe, 0x35, 0x8b, 0x84, 0x46, 0x6b, 0xc9, + 0xc6, 0xa1, 0xdc, 0x4a, 0xbd, 0x71, 0xad, 0x12, 0x83, 0x1c, 0x6d, 0x55}, + {0x82, 0x39, 0x8d, 0x0c, 0xe3, 0x40, 0xef, 0x17, 0x34, 0xfa, 0xa3, 0x15, 0x3e, 0x07, + 0xf7, 0x31, 0x6e, 0x64, 0x73, 0x07, 0xcb, 0xf3, 0x21, 0x4f, 0xff, 0x4e, 0x82, 0x1d, + 0x6d, 0x6c, 0x6c, 0x74, 0x21, 0xe8, 0x1b, 0xb1, 0x56, 0x67, 0xf0, 0x81, 0xdd, 0xf3, + 0xa3, 0x10, 0x23, 0xf8, 0xaf, 0x0f, 0x5d, 0x46, 0x99, 0x6a, 0x55, 0xd0, 0xb2, 0xf8, + 0x05, 0x7f, 0x8c, 0xcc, 0x38, 0xbe, 0x7a, 0x09, 0xa4, 0x2d, 0xa5, 0x7e, 0x87, 0xc9, + 0x49, 0x0c, 0x43, 0x1d, 0xdc, 0x9b, 0x55, 0x69, 0x43, 0x4c, 0xd2, 0xeb, 0xcc, 0xf7, + 0x09, 0x38, 0x2c, 0x02, 0xbd, 0x84, 0xee, 0x4b, 0xa3, 0x14, 0x7e, 0x57}, + {0x0a, 0x3b, 0xa7, 0x61, 0xac, 0x68, 0xe2, 0xf0, 0xf5, 0xa5, 0x91, 0x37, 0x10, 0xfa, + 0xfa, 0xf2, 0xe9, 0x00, 0x6d, 0x6b, 0x82, 0x3e, 0xe1, 0xc1, 0x42, 0x8f, 0xd7, 0x6f, + 0xe9, 0x7e, 0xfa, 0x60, 0x2b, 0xd7, 0x4d, 0xbd, 0xbe, 0xce, 0xfe, 0x94, 0x11, 0x22, + 0x0f, 0x06, 0xda, 0x4f, 0x6a, 0xf4, 0xff, 0xd1, 0xc8, 0xc0, 0x77, 0x59, 0x4a, 0x12, + 0x95, 0x92, 0x00, 0xfb, 0xb8, 0x04, 0x53, 0x70, 0xc6, 0x6e, 0x29, 0x4d, 0x35, 0x1d, + 0x3d, 0xb6, 0xd8, 0x31, 0xad, 0x5f, 0x3e, 0x05, 0xc3, 0xf3, 0xec, 0x42, 0xbd, 0xb4, + 0x8c, 0x95, 0x0b, 0x67, 0xfd, 0x53, 0x63, 0xa1, 0x0c, 0x8e, 0x39, 0x21}, + {0xf3, 0x33, 0x2b, 0x38, 0x8a, 0x05, 0xf5, 0x89, 0xb4, 0xc0, 0x48, 0xad, 0x0b, 0xba, + 0xe2, 0x5a, 0x6e, 0xb3, 0x3d, 0xa5, 0x03, 0xb5, 0x93, 0x8f, 0xe6, 0x32, 0xa2, 0x95, + 0x9d, 0xed, 0xa3, 0x5a, 0x01, 0x56, 0xb7, 0xb4, 0xf9, 0xaa, 0x98, 0x27, 0x72, 0xad, + 0x8d, 0x5c, 0x13, 0x72, 0xac, 0x5e, 0x23, 0xa0, 0xb7, 0x61, 0x61, 0xaa, 0xce, 0xd2, + 0x4e, 0x7d, 0x8f, 0xe9, 0x84, 0xb2, 0xbf, 0x1b, 0x61, 0x65, 0xd9, 0xc7, 0xe9, 0x77, + 0x67, 0x65, 0x36, 0x80, 0xc7, 0x72, 0x54, 0x12, 0x2b, 0xcb, 0xee, 0x6e, 0x50, 0xd9, + 0x99, 0x32, 0x05, 0x65, 0xcc, 0x57, 0x89, 0x5e, 0x4e, 0xe1, 0x07, 0x4a}, + {0x99, 0xf9, 0x0d, 0x98, 0xcb, 0x12, 0xe4, 0x4e, 0x71, 0xc7, 0x6e, 0x3c, 0x6f, 0xd7, + 0x15, 0xa3, 0xfd, 0x77, 0x5c, 0x92, 0xde, 0xed, 0xa5, 0xbb, 0x02, 0x34, 0x31, 0x1d, + 0x39, 0xac, 0x0b, 0x3f, 0x9b, 0xa4, 0x77, 0xc4, 0xcd, 0x58, 0x0b, 0x24, 0x17, 0xf0, + 0x47, 0x64, 0xde, 0xda, 0x38, 0xfd, 0xad, 0x6a, 0xc8, 0xa7, 0x32, 0x8d, 0x92, 0x19, + 0x81, 0xa0, 0xaf, 0x84, 0xed, 0x7a, 0xaf, 0x50, 0xe5, 0x5b, 0xf6, 0x15, 0x01, 0xde, + 0x4f, 0x6e, 0xb2, 0x09, 0x61, 0x21, 0x21, 0x26, 0x98, 0x29, 0xd9, 0xd6, 0xad, 0x0b, + 0x81, 0x05, 0x02, 0x78, 0x06, 0xd0, 0xeb, 0xba, 0x16, 0xa3, 0x21, 0x19}, + {0xfc, 0x70, 0xb8, 0xdf, 0x7e, 0x2f, 0x42, 0x89, 0xbd, 0xb3, 0x76, 0x4f, 0xeb, 0x6b, + 0x29, 0x2c, 0xf7, 0x4d, 0xc2, 0x36, 0xd4, 0xf1, 0x38, 0x07, 0xb0, 0xae, 0x73, 0xe2, + 0x41, 0xdf, 0x58, 0x64, 0x8b, 0xc1, 0xf3, 0xd9, 0x9a, 0xad, 0x5a, 0xd7, 0x9c, 0xc1, + 0xb1, 0x60, 0xef, 0x0e, 0x6a, 0x56, 0xd9, 0x0e, 0x5c, 0x25, 0xac, 0x0b, 0x9a, 0x3e, + 0xf5, 0xc7, 0x62, 0xa0, 0xec, 0x9d, 0x04, 0x7b, 0x83, 0x44, 0x44, 0x35, 0x7a, 0xe3, + 0xcb, 0xdc, 0x93, 0xbe, 0xed, 0x0f, 0x33, 0x79, 0x88, 0x75, 0x87, 0xdd, 0xc5, 0x12, + 0xc3, 0x04, 0x60, 0x78, 0x64, 0x0e, 0x95, 0xc2, 0xcb, 0xdc, 0x93, 0x60}, + {0x6d, 0x70, 0xe0, 0x85, 0x85, 0x9a, 0xf3, 0x1f, 0x33, 0x39, 0xe7, 0xb3, 0xd8, 0xa5, + 0xd0, 0x36, 0x3b, 0x45, 0x8f, 0x71, 0xe1, 0xf2, 0xb9, 0x43, 0x7c, 0xa9, 0x27, 0x48, + 0x08, 0xea, 0xd1, 0x57, 0x4b, 0x03, 0x84, 0x60, 0xbe, 0xee, 0xde, 0x6b, 0x54, 0xb8, + 0x0f, 0x78, 0xb6, 0xc2, 0x99, 0x31, 0x95, 0x06, 0x2d, 0xb6, 0xab, 0x76, 0x33, 0x97, + 0x90, 0x7d, 0x64, 0x8b, 0xc9, 0x80, 0x31, 0x6e, 0x71, 0xb0, 0x28, 0xa1, 0xe7, 0xb6, + 0x7a, 0xee, 0xaa, 0x8b, 0xa8, 0x93, 0x6d, 0x59, 0xc1, 0xa4, 0x30, 0x61, 0x21, 0xb2, + 0x82, 0xde, 0xb4, 0xf7, 0x18, 0xbd, 0x97, 0xdd, 0x9d, 0x99, 0x3e, 0x36}, + {0xc4, 0x1f, 0xee, 0x35, 0xc1, 0x43, 0xa8, 0x96, 0xcf, 0xc8, 0xe4, 0x08, 0x55, 0xb3, + 0x6e, 0x97, 0x30, 0xd3, 0x8c, 0xb5, 0x01, 0x68, 0x2f, 0xb4, 0x2b, 0x05, 0x3a, 0x69, + 0x78, 0x9b, 0xee, 0x48, 0xc6, 0xae, 0x4b, 0xe2, 0xdc, 0x48, 0x18, 0x2f, 0x60, 0xaf, + 0xbc, 0xba, 0x55, 0x72, 0x9b, 0x76, 0x31, 0xe9, 0xef, 0x3c, 0x6e, 0x3c, 0xcb, 0x90, + 0x55, 0xb3, 0xf9, 0xc6, 0x9b, 0x97, 0x1f, 0x23, 0xc6, 0xf3, 0x2a, 0xcc, 0x4b, 0xde, + 0x31, 0x5c, 0x1f, 0x8d, 0x20, 0xfe, 0x30, 0xb0, 0x4b, 0xb0, 0x66, 0xb4, 0x4f, 0xc1, + 0x09, 0x70, 0x8d, 0xb7, 0x13, 0x24, 0x79, 0x08, 0x9b, 0xfa, 0x9b, 0x07}, + {0xf4, 0x0d, 0x30, 0xda, 0x51, 0x3a, 0x90, 0xe3, 0xb0, 0x5a, 0xa9, 0x3d, 0x23, 0x64, + 0x39, 0x84, 0x80, 0x64, 0x35, 0x0b, 0x2d, 0xf1, 0x3c, 0xed, 0x94, 0x71, 0x81, 0x84, + 0xf6, 0x77, 0x8c, 0x03, 0x45, 0x42, 0xd5, 0xa2, 0x80, 0xed, 0xc9, 0xf3, 0x52, 0x39, + 0xf6, 0x77, 0x78, 0x8b, 0xa0, 0x0a, 0x75, 0x54, 0x08, 0xd1, 0x63, 0xac, 0x6d, 0xd7, + 0x6b, 0x63, 0x70, 0x94, 0x15, 0xfb, 0xf4, 0x1e, 0xec, 0x7b, 0x16, 0x5b, 0xe6, 0x5e, + 0x4e, 0x85, 0xc2, 0xcd, 0xd0, 0x96, 0x42, 0x0a, 0x59, 0x59, 0x99, 0x21, 0x10, 0x98, + 0x34, 0xdf, 0xb2, 0x72, 0x56, 0xff, 0x0b, 0x4a, 0x2a, 0xe9, 0x5e, 0x57}, + {0xcf, 0x2f, 0x18, 0x8a, 0x90, 0x80, 0xc0, 0xd4, 0xbd, 0x9d, 0x48, 0x99, 0xc2, 0x70, + 0xe1, 0x30, 0xde, 0x33, 0xf7, 0x52, 0x57, 0xbd, 0xba, 0x05, 0x00, 0xfd, 0xd3, 0x2c, + 0x11, 0xe7, 0xd4, 0x43, 0x01, 0xd8, 0xa4, 0x0a, 0x45, 0xbc, 0x46, 0x5d, 0xd8, 0xb9, + 0x33, 0xa5, 0x27, 0x12, 0xaf, 0xc3, 0xc2, 0x06, 0x89, 0x2b, 0x26, 0x3b, 0x9e, 0x38, + 0x1b, 0x58, 0x2f, 0x38, 0x7e, 0x1e, 0x0a, 0x20, 0xc5, 0x3a, 0xf9, 0xea, 0x67, 0xb9, + 0x8d, 0x51, 0xc0, 0x52, 0x66, 0x05, 0x9b, 0x98, 0xbc, 0x71, 0xf5, 0x97, 0x71, 0x56, + 0xd9, 0x85, 0x2b, 0xfe, 0x38, 0x4e, 0x1e, 0x65, 0x52, 0xca, 0x0e, 0x05}, + {0x9c, 0x0c, 0x3f, 0x45, 0xde, 0x1a, 0x43, 0xc3, 0x9b, 0x3b, 0x70, 0xff, 0x5e, 0x04, + 0xf5, 0xe9, 0x3d, 0x7b, 0x84, 0xed, 0xc9, 0x7a, 0xd9, 0xfc, 0xc6, 0xf4, 0x58, 0x1c, + 0xc2, 0xe6, 0x0e, 0x4b, 0xea, 0x68, 0xe6, 0x60, 0x76, 0x39, 0xac, 0x97, 0x97, 0xb4, + 0x3a, 0x15, 0xfe, 0xbb, 0x19, 0x9b, 0x9f, 0xa7, 0xec, 0x34, 0xb5, 0x79, 0xb1, 0x4c, + 0x57, 0xae, 0x31, 0xa1, 0x9f, 0xc0, 0x51, 0x61, 0x96, 0x5d, 0xf0, 0xfd, 0x0d, 0x5c, + 0xf5, 0x3a, 0x7a, 0xee, 0xb4, 0x2a, 0xe0, 0x2e, 0x26, 0xdd, 0x09, 0x17, 0x17, 0x12, + 0x87, 0xbb, 0xb2, 0x11, 0x0b, 0x03, 0x0f, 0x80, 0xfa, 0x24, 0xef, 0x1f}, + {0x96, 0x31, 0xa7, 0x1a, 0xfb, 0x53, 0xd6, 0x37, 0x18, 0x64, 0xd7, 0x3f, 0x30, 0x95, + 0x94, 0x0f, 0xb2, 0x17, 0x3a, 0xfb, 0x09, 0x0b, 0x20, 0xad, 0x3e, 0x61, 0xc8, 0x2f, + 0x29, 0x49, 0x4d, 0x54, 0x86, 0x6b, 0x97, 0x30, 0xf5, 0xaf, 0xd2, 0x22, 0x04, 0x46, + 0xd2, 0xc2, 0x06, 0xb8, 0x90, 0x8d, 0xe5, 0xba, 0xe5, 0x4d, 0x6c, 0x89, 0xa1, 0xdc, + 0x17, 0x0c, 0x34, 0xc8, 0xe6, 0x5f, 0x00, 0x28, 0x88, 0x86, 0x52, 0x34, 0x9f, 0xba, + 0xef, 0x6a, 0xa1, 0x7d, 0x10, 0x25, 0x94, 0xff, 0x1b, 0x5c, 0x36, 0x4b, 0xd9, 0x66, + 0xcd, 0xbb, 0x5b, 0xf7, 0xfa, 0x6d, 0x31, 0x0f, 0x93, 0x72, 0xe4, 0x72}, + {0x4f, 0x08, 0x81, 0x97, 0x8c, 0x20, 0x95, 0x26, 0xe1, 0x0e, 0x45, 0x23, 0x0b, 0x2a, + 0x50, 0xb1, 0x02, 0xde, 0xef, 0x03, 0xa6, 0xae, 0x9d, 0xfd, 0x4c, 0xa3, 0x33, 0x27, + 0x8c, 0x2e, 0x9d, 0x5a, 0x27, 0x76, 0x2a, 0xd3, 0x35, 0xf6, 0xf3, 0x07, 0xf0, 0x66, + 0x65, 0x5f, 0x86, 0x4d, 0xaa, 0x7a, 0x50, 0x44, 0xd0, 0x28, 0x97, 0xe7, 0x85, 0x3c, + 0x38, 0x64, 0xe0, 0x0f, 0x00, 0x7f, 0xee, 0x1f, 0xe5, 0xf7, 0xdb, 0x03, 0xda, 0x05, + 0x53, 0x76, 0xbd, 0xcd, 0x34, 0x14, 0x49, 0xf2, 0xda, 0xa4, 0xec, 0x88, 0x4a, 0xd2, + 0xcd, 0xd5, 0x4a, 0x7b, 0x43, 0x05, 0x04, 0xee, 0x51, 0x40, 0xf9, 0x00}, + {0xb2, 0x30, 0xd3, 0xc3, 0x23, 0x6b, 0x35, 0x8d, 0x06, 0x1b, 0x47, 0xb0, 0x9b, 0x8b, + 0x1c, 0xf2, 0x3c, 0xb8, 0x42, 0x6e, 0x6c, 0x31, 0x6c, 0xb3, 0x0d, 0xb1, 0xea, 0x8b, + 0x7e, 0x9c, 0xd7, 0x07, 0x53, 0x97, 0xaf, 0x07, 0xbb, 0x93, 0xef, 0xd7, 0xa7, 0x66, + 0xb7, 0x3d, 0xcf, 0xd0, 0x3e, 0x58, 0xc5, 0x1e, 0x0b, 0x6e, 0xbf, 0x98, 0x69, 0xce, + 0x52, 0x04, 0xd4, 0x5d, 0xd2, 0xff, 0xb7, 0x47, 0x12, 0xdd, 0x08, 0xbc, 0x9c, 0xfb, + 0xfb, 0x87, 0x9b, 0xc2, 0xee, 0xe1, 0x3a, 0x6b, 0x06, 0x8a, 0xbf, 0xc1, 0x1f, 0xdb, + 0x2b, 0x24, 0x57, 0x0d, 0xb6, 0x4b, 0xa6, 0x5e, 0xa3, 0x20, 0x35, 0x1c}, + {0x4a, 0xa3, 0xcb, 0xbc, 0xa6, 0x53, 0xd2, 0x80, 0x9b, 0x21, 0x38, 0x38, 0xa1, 0xc3, + 0x61, 0x3e, 0x96, 0xe3, 0x82, 0x98, 0x01, 0xb6, 0xc3, 0x90, 0x6f, 0xe6, 0x0e, 0x5d, + 0x77, 0x05, 0x3d, 0x1c, 0x59, 0xc0, 0x6b, 0x21, 0x40, 0x6f, 0xa8, 0xcd, 0x7e, 0xd8, + 0xbc, 0x12, 0x1d, 0x23, 0xbb, 0x1f, 0x90, 0x09, 0xc7, 0x17, 0x9e, 0x6a, 0x95, 0xb4, + 0x55, 0x2e, 0xd1, 0x66, 0x3b, 0x0c, 0x75, 0x38, 0x1a, 0xe5, 0x22, 0x94, 0x40, 0xf1, + 0x2e, 0x69, 0x71, 0xf6, 0x5d, 0x2b, 0x3c, 0xc7, 0xc0, 0xcb, 0x29, 0xe0, 0x4c, 0x74, + 0xe7, 0x4f, 0x01, 0x21, 0x7c, 0x48, 0x30, 0xd3, 0xc7, 0xe2, 0x21, 0x06}, + {0x8d, 0x83, 0x59, 0x82, 0xcc, 0x60, 0x98, 0xaf, 0xdc, 0x9a, 0x9f, 0xc6, 0xc1, 0x48, + 0xea, 0x90, 0x30, 0x1e, 0x58, 0x65, 0x37, 0x48, 0x26, 0x65, 0xbc, 0xa5, 0xd3, 0x7b, + 0x09, 0xd6, 0x07, 0x00, 0xf3, 0xf0, 0xdb, 0xb0, 0x96, 0x17, 0xae, 0xb7, 0x96, 0xe1, + 0x7c, 0xe1, 0xb9, 0xaf, 0xdf, 0x54, 0xb4, 0xa3, 0xaa, 0xe9, 0x71, 0x30, 0x92, 0x25, + 0x9d, 0x2e, 0x00, 0xa1, 0x9c, 0x58, 0x8e, 0x5d, 0x4b, 0xa9, 0x42, 0x08, 0x95, 0x1d, + 0xbf, 0xc0, 0x3e, 0x2e, 0x8f, 0x58, 0x63, 0xc3, 0xd3, 0xb2, 0xef, 0xe2, 0x51, 0xbb, + 0x38, 0x14, 0x96, 0x0a, 0x86, 0xbf, 0x1c, 0x3c, 0x78, 0xd7, 0x83, 0x15}, + {0xe1, 0x7a, 0xa2, 0x5d, 0xef, 0xa2, 0xee, 0xec, 0x74, 0x01, 0x67, 0x55, 0x14, 0x3a, + 0x7c, 0x59, 0x7a, 0x16, 0x09, 0x66, 0x12, 0x2a, 0xa6, 0xc9, 0x70, 0x8f, 0xed, 0x81, + 0x2e, 0x5f, 0x2a, 0x25, 0xc7, 0x28, 0x9d, 0xcc, 0x04, 0x47, 0x03, 0x90, 0x8f, 0xc5, + 0x2c, 0xf7, 0x9e, 0x67, 0x1b, 0x1d, 0x26, 0x87, 0x5b, 0xbe, 0x5f, 0x2b, 0xe1, 0x16, + 0x0a, 0x58, 0xc5, 0x83, 0x4e, 0x06, 0x58, 0x49, 0x0d, 0xe8, 0x66, 0x50, 0x26, 0x94, + 0x28, 0x0d, 0x6b, 0x8c, 0x7c, 0x30, 0x85, 0xf7, 0xc3, 0xfc, 0xfd, 0x12, 0x11, 0x0c, + 0x78, 0xda, 0x53, 0x1b, 0x88, 0xb3, 0x43, 0xd8, 0x0b, 0x17, 0x9c, 0x07}, + {0xff, 0x6f, 0xfa, 0x64, 0xe4, 0xec, 0x06, 0x05, 0x23, 0xe5, 0x05, 0x62, 0x1e, 0x43, + 0xe3, 0xbe, 0x42, 0xea, 0xb8, 0x51, 0x24, 0x42, 0x79, 0x35, 0x00, 0xfb, 0xc9, 0x4a, + 0xe3, 0x05, 0xec, 0x6d, 0x56, 0xd0, 0xd5, 0xc0, 0x50, 0xcd, 0xd6, 0xcd, 0x3b, 0x57, + 0x03, 0xbb, 0x6d, 0x68, 0xf7, 0x9a, 0x48, 0xef, 0xc3, 0xf3, 0x3f, 0x72, 0xa6, 0x3c, + 0xcc, 0x8a, 0x7b, 0x31, 0xd7, 0xc0, 0x68, 0x67, 0xb3, 0xc1, 0x55, 0xf1, 0xe5, 0x25, + 0xb6, 0x94, 0x91, 0x7b, 0x7b, 0x99, 0xa7, 0xf3, 0x7b, 0x41, 0x00, 0x26, 0x6b, 0x6d, + 0xdc, 0xbd, 0x2c, 0xc2, 0xf4, 0x52, 0xcd, 0xdd, 0x14, 0x5e, 0x44, 0x51}, + {0x51, 0x49, 0x14, 0x3b, 0x4b, 0x2b, 0x50, 0x57, 0xb3, 0xbc, 0x4b, 0x44, 0x6b, 0xff, + 0x67, 0x8e, 0xdb, 0x85, 0x63, 0x16, 0x27, 0x69, 0xbd, 0xb8, 0xc8, 0x95, 0x92, 0xe3, + 0x31, 0x6f, 0x18, 0x13, 0x55, 0xa4, 0xbe, 0x2b, 0xab, 0x47, 0x31, 0x89, 0x29, 0x91, + 0x07, 0x92, 0x4f, 0xa2, 0x53, 0x8c, 0xa7, 0xf7, 0x30, 0xbe, 0x48, 0xf9, 0x49, 0x4b, + 0x3d, 0xd4, 0x4f, 0x6e, 0x08, 0x90, 0xe9, 0x12, 0x2e, 0xbb, 0xdf, 0x7f, 0xb3, 0x96, + 0x0c, 0xf1, 0xf9, 0xea, 0x1c, 0x12, 0x5e, 0x93, 0x9a, 0x9f, 0x3f, 0x98, 0x5b, 0x3a, + 0xc4, 0x36, 0x11, 0xdf, 0xaf, 0x99, 0x3e, 0x5d, 0xf0, 0xe3, 0xb2, 0x77}, + {0xde, 0xc4, 0x2e, 0x9c, 0xc5, 0xa9, 0x6f, 0x29, 0xcb, 0xf3, 0x84, 0x4f, 0xbf, 0x61, + 0x8b, 0xbc, 0x08, 0xf9, 0xa8, 0x17, 0xd9, 0x06, 0x77, 0x1c, 0x5d, 0x25, 0xd3, 0x7a, + 0xfc, 0x95, 0xb7, 0x63, 0xa4, 0xb0, 0xdd, 0x12, 0x9c, 0x63, 0x98, 0xd5, 0x6b, 0x86, + 0x24, 0xc0, 0x30, 0x9f, 0xd1, 0xa5, 0x60, 0xe4, 0xfc, 0x58, 0x03, 0x2f, 0x7c, 0xd1, + 0x8a, 0x5e, 0x09, 0x2e, 0x15, 0x95, 0xa1, 0x07, 0xc8, 0x5f, 0x9e, 0x38, 0x02, 0x8f, + 0x36, 0xa8, 0x3b, 0xe4, 0x8d, 0xcf, 0x02, 0x3b, 0x43, 0x90, 0x43, 0x26, 0x41, 0xc5, + 0x5d, 0xfd, 0xa1, 0xaf, 0x37, 0x01, 0x2f, 0x03, 0x3d, 0xe8, 0x8f, 0x3e}, + {0x94, 0xa2, 0x70, 0x05, 0xb9, 0x15, 0x8b, 0x2f, 0x49, 0x45, 0x08, 0x67, 0x70, 0x42, + 0xf2, 0x94, 0x84, 0xfd, 0xbb, 0x61, 0xe1, 0x5a, 0x1c, 0xde, 0x07, 0x40, 0xac, 0x7f, + 0x79, 0x3b, 0xba, 0x75, 0x3c, 0xd1, 0xef, 0xe8, 0x8d, 0x4c, 0x70, 0x08, 0x31, 0x37, + 0xe0, 0x33, 0x8e, 0x1a, 0xc5, 0xdf, 0xe3, 0xcd, 0x60, 0x12, 0xa5, 0x5d, 0x9d, 0xa5, + 0x86, 0x8c, 0x25, 0xa6, 0x99, 0x08, 0xd6, 0x22, 0x96, 0xd1, 0xcd, 0x70, 0xc0, 0xdb, + 0x39, 0x62, 0x9a, 0x8a, 0x7d, 0x6c, 0x8b, 0x8a, 0xfe, 0x60, 0x60, 0x12, 0x40, 0xeb, + 0xbc, 0x47, 0x88, 0xb3, 0x5e, 0x9e, 0x77, 0x87, 0x7b, 0xd0, 0x04, 0x09}, + {0x9c, 0x91, 0xba, 0xdd, 0xd4, 0x1f, 0xce, 0xb4, 0xaa, 0x8d, 0x4c, 0xc7, 0x3e, 0xdb, + 0x31, 0xcf, 0x51, 0xcc, 0x86, 0xad, 0x63, 0xcc, 0x63, 0x2c, 0x07, 0xde, 0x1d, 0xbc, + 0x3f, 0x14, 0xe2, 0x43, 0xb9, 0x40, 0xf9, 0x48, 0x66, 0x2d, 0x32, 0xf4, 0x39, 0x0c, + 0x2d, 0xbd, 0x0c, 0x2f, 0x95, 0x06, 0x31, 0xf9, 0x81, 0xa0, 0xad, 0x97, 0x76, 0x16, + 0x6c, 0x2a, 0xf7, 0xba, 0xce, 0xaa, 0x40, 0x62, 0xa0, 0x95, 0xa2, 0x5b, 0x9c, 0x74, + 0x34, 0xf8, 0x5a, 0xd2, 0x37, 0xca, 0x5b, 0x7c, 0x94, 0xd6, 0x6a, 0x31, 0xc9, 0xe7, + 0xa7, 0x3b, 0xf1, 0x66, 0xac, 0x0c, 0xb4, 0x8d, 0x23, 0xaf, 0xbd, 0x56}, + {0xeb, 0x33, 0x35, 0xf5, 0xe3, 0xb9, 0x2a, 0x36, 0x40, 0x3d, 0xb9, 0x6e, 0xd5, 0x68, + 0x85, 0x33, 0x72, 0x55, 0x5a, 0x1d, 0x52, 0x14, 0x0e, 0x9e, 0x18, 0x13, 0x74, 0x83, + 0x6d, 0xa8, 0x24, 0x1d, 0xb2, 0x3b, 0x9d, 0xc1, 0x6c, 0xd3, 0x10, 0x13, 0xb9, 0x86, + 0x23, 0x62, 0xb7, 0x6b, 0x2a, 0x06, 0x5c, 0x4f, 0xa1, 0xd7, 0x91, 0x85, 0x9b, 0x7c, + 0x54, 0x57, 0x1e, 0x7e, 0x50, 0x31, 0xaa, 0x03, 0x1f, 0xce, 0xd4, 0xff, 0x48, 0x76, + 0xec, 0xf4, 0x1c, 0x8c, 0xac, 0x54, 0xf0, 0xea, 0x45, 0xe0, 0x7c, 0x35, 0x09, 0x1d, + 0x82, 0x25, 0xd2, 0x88, 0x59, 0x48, 0xeb, 0x9a, 0xdc, 0x61, 0xb2, 0x43}, + {0xbb, 0x79, 0xbb, 0x88, 0x19, 0x1e, 0x5b, 0xe5, 0x9d, 0x35, 0x7a, 0xc1, 0x7d, 0xd0, + 0x9e, 0xa0, 0x33, 0xea, 0x3d, 0x60, 0xe2, 0x2e, 0x2c, 0xb0, 0xc2, 0x6b, 0x27, 0x5b, + 0xcf, 0x55, 0x60, 0x32, 0x64, 0x13, 0x95, 0x6c, 0x8b, 0x3d, 0x51, 0x19, 0x7b, 0xf4, + 0x0b, 0x00, 0x26, 0x71, 0xfe, 0x94, 0x67, 0x95, 0x4f, 0xd5, 0xdd, 0x10, 0x8d, 0x02, + 0x64, 0x09, 0x94, 0x42, 0xe2, 0xd5, 0xb4, 0x02, 0xf2, 0x8d, 0xd1, 0x28, 0xcb, 0x55, + 0xa1, 0xb4, 0x08, 0xe5, 0x6c, 0x18, 0x46, 0x46, 0xcc, 0xea, 0x89, 0x43, 0x82, 0x6c, + 0x93, 0xf4, 0x9c, 0xc4, 0x10, 0x34, 0x5d, 0xae, 0x09, 0xc8, 0xa6, 0x27}, + {0x88, 0xb1, 0x0d, 0x1f, 0xcd, 0xeb, 0xa6, 0x8b, 0xe8, 0x5b, 0x5a, 0x67, 0x3a, 0xd7, + 0xd3, 0x37, 0x5a, 0x58, 0xf5, 0x15, 0xa3, 0xdf, 0x2e, 0xf2, 0x7e, 0xa1, 0x60, 0xff, + 0x74, 0x71, 0xb6, 0x2c, 0x54, 0x69, 0x3d, 0xc4, 0x0a, 0x27, 0x2c, 0xcd, 0xb2, 0xca, + 0x66, 0x6a, 0x57, 0x3e, 0x4a, 0xdd, 0x6c, 0x03, 0xd7, 0x69, 0x24, 0x59, 0xfa, 0x79, + 0x99, 0x25, 0x8c, 0x3d, 0x60, 0x03, 0x15, 0x22, 0xd0, 0xe1, 0x0b, 0x39, 0xf9, 0xcd, + 0xee, 0x59, 0xf1, 0xe3, 0x8c, 0x72, 0x44, 0x20, 0x42, 0xa9, 0xf4, 0xf0, 0x94, 0x7a, + 0x66, 0x1c, 0x89, 0x82, 0x36, 0xf4, 0x90, 0x38, 0xb7, 0xf4, 0x1d, 0x7b}, + {0x24, 0xa2, 0xb2, 0xb3, 0xe0, 0xf2, 0x92, 0xe4, 0x60, 0x11, 0x55, 0x2b, 0x06, 0x9e, + 0x6c, 0x7c, 0x0e, 0x7b, 0x7f, 0x0d, 0xe2, 0x8f, 0xeb, 0x15, 0x92, 0x59, 0xfc, 0x58, + 0x26, 0xef, 0xfc, 0x61, 0x8c, 0xf5, 0xf8, 0x07, 0x18, 0x22, 0x2e, 0x5f, 0xd4, 0x09, + 0x94, 0xd4, 0x9f, 0x5c, 0x55, 0xe3, 0x30, 0xa6, 0xb6, 0x1f, 0x8d, 0xa8, 0xaa, 0xb2, + 0x3d, 0xe0, 0x52, 0xd3, 0x45, 0x82, 0x69, 0x68, 0x7a, 0x18, 0x18, 0x2a, 0x85, 0x5d, + 0xb1, 0xdb, 0xd7, 0xac, 0xdd, 0x86, 0xd3, 0xaa, 0xe4, 0xf3, 0x82, 0xc4, 0xf6, 0x0f, + 0x81, 0xe2, 0xba, 0x44, 0xcf, 0x01, 0xaf, 0x3d, 0x47, 0x4c, 0xcf, 0x46}, + {0xf9, 0xe5, 0xc4, 0x9e, 0xed, 0x25, 0x65, 0x42, 0x03, 0x33, 0x90, 0x16, 0x01, 0xda, + 0x5e, 0x0e, 0xdc, 0xca, 0xe5, 0xcb, 0xf2, 0xa7, 0xb1, 0x72, 0x40, 0x5f, 0xeb, 0x14, + 0xcd, 0x7b, 0x38, 0x29, 0x40, 0x81, 0x49, 0xf1, 0xa7, 0x6e, 0x3c, 0x21, 0x54, 0x48, + 0x2b, 0x39, 0xf8, 0x7e, 0x1e, 0x7c, 0xba, 0xce, 0x29, 0x56, 0x8c, 0xc3, 0x88, 0x24, + 0xbb, 0xc5, 0x8c, 0x0d, 0xe5, 0xaa, 0x65, 0x10, 0x57, 0x0d, 0x20, 0xdf, 0x25, 0x45, + 0x2c, 0x1c, 0x4a, 0x67, 0xca, 0xbf, 0xd6, 0x2d, 0x3b, 0x5c, 0x30, 0x40, 0x83, 0xe1, + 0xb1, 0xe7, 0x07, 0x0a, 0x16, 0xe7, 0x1c, 0x4f, 0xe6, 0x98, 0xa1, 0x69}, + {0xbc, 0x78, 0x1a, 0xd9, 0xe0, 0xb2, 0x62, 0x90, 0x67, 0x96, 0x50, 0xc8, 0x9c, 0x88, + 0xc9, 0x47, 0xb8, 0x70, 0x50, 0x40, 0x66, 0x4a, 0xf5, 0x9d, 0xbf, 0xa1, 0x93, 0x24, + 0xa9, 0xe6, 0x69, 0x73, 0xed, 0xca, 0xc5, 0xdc, 0x34, 0x44, 0x01, 0xe1, 0x33, 0xfb, + 0x84, 0x3c, 0x96, 0x5d, 0xed, 0x47, 0xe7, 0xa0, 0x86, 0xed, 0x76, 0x95, 0x01, 0x70, + 0xe4, 0xf9, 0x67, 0xd2, 0x7b, 0x69, 0xb2, 0x25, 0x64, 0x68, 0x98, 0x13, 0xfb, 0x3f, + 0x67, 0x9d, 0xb8, 0xc7, 0x5d, 0x41, 0xd9, 0xfb, 0xa5, 0x3c, 0x5e, 0x3b, 0x27, 0xdf, + 0x3b, 0xcc, 0x4e, 0xe0, 0xd2, 0x4c, 0x4e, 0xb5, 0x3d, 0x68, 0x20, 0x14}, + {0x97, 0xd1, 0x9d, 0x24, 0x1e, 0xbd, 0x78, 0xb4, 0x02, 0xc1, 0x58, 0x5e, 0x00, 0x35, + 0x0c, 0x62, 0x5c, 0xac, 0xba, 0xcc, 0x2f, 0xd3, 0x02, 0xfb, 0x2d, 0xa7, 0x08, 0xf5, + 0xeb, 0x3b, 0xb6, 0x60, 0xd0, 0x5a, 0xcc, 0xc1, 0x6f, 0xbb, 0xee, 0x34, 0x8b, 0xac, + 0x46, 0x96, 0xe9, 0x0c, 0x1b, 0x6a, 0x53, 0xde, 0x6b, 0xa6, 0x49, 0xda, 0xb0, 0xd3, + 0xc1, 0x81, 0xd0, 0x61, 0x41, 0x3b, 0xe8, 0x31, 0x4f, 0x2b, 0x06, 0x9e, 0x12, 0xc7, + 0xe8, 0x97, 0xd8, 0x0a, 0x32, 0x29, 0x4f, 0x8f, 0xe4, 0x49, 0x3f, 0x68, 0x18, 0x6f, + 0x4b, 0xe1, 0xec, 0x5b, 0x17, 0x03, 0x55, 0x2d, 0xb6, 0x1e, 0xcf, 0x55}, + {0x58, 0x3d, 0xc2, 0x65, 0x10, 0x10, 0x79, 0x58, 0x9c, 0x81, 0x94, 0x50, 0x6d, 0x08, + 0x9d, 0x8b, 0xa7, 0x5f, 0xc5, 0x12, 0xa9, 0x2f, 0x40, 0xe2, 0xd4, 0x91, 0x08, 0x57, + 0x64, 0x65, 0x9a, 0x66, 0x52, 0x8c, 0xf5, 0x7d, 0xe3, 0xb5, 0x76, 0x30, 0x36, 0xcc, + 0x99, 0xe7, 0xdd, 0xb9, 0x3a, 0xd7, 0x20, 0xee, 0x13, 0x49, 0xe3, 0x1c, 0x83, 0xbd, + 0x33, 0x01, 0xba, 0x62, 0xaa, 0xfb, 0x56, 0x1a, 0xec, 0xc9, 0x9d, 0x5c, 0x50, 0x6b, + 0x3e, 0x94, 0x1a, 0x37, 0x7c, 0xa7, 0xbb, 0x57, 0x25, 0x30, 0x51, 0x76, 0x34, 0x41, + 0x56, 0xae, 0x73, 0x98, 0x5c, 0x8a, 0xc5, 0x99, 0x67, 0x83, 0xc4, 0x13}, + {0xb9, 0xe1, 0xb3, 0x5a, 0x46, 0x5d, 0x3a, 0x42, 0x61, 0x3f, 0xf1, 0xc7, 0x87, 0xc1, + 0x13, 0xfc, 0xb6, 0xb9, 0xb5, 0xec, 0x64, 0x36, 0xf8, 0x19, 0x07, 0xb6, 0x37, 0xa6, + 0x93, 0x0c, 0xf8, 0x66, 0x80, 0xd0, 0x8b, 0x5d, 0x6a, 0xfb, 0xdc, 0xc4, 0x42, 0x48, + 0x1a, 0x57, 0xec, 0xc4, 0xeb, 0xde, 0x65, 0x53, 0xe5, 0xb8, 0x83, 0xe8, 0xb2, 0xd4, + 0x27, 0xb8, 0xe5, 0xc8, 0x7d, 0xc8, 0xbd, 0x50, 0x11, 0xe1, 0xdf, 0x6e, 0x83, 0x37, + 0x6d, 0x60, 0xd9, 0xab, 0x11, 0xf0, 0x15, 0x3e, 0x35, 0x32, 0x96, 0x3b, 0xb7, 0x25, + 0xc3, 0x3a, 0xb0, 0x64, 0xae, 0xd5, 0x5f, 0x72, 0x44, 0x64, 0xd5, 0x1d}, + {0x7d, 0x12, 0x62, 0x33, 0xf8, 0x7f, 0xa4, 0x8f, 0x15, 0x7c, 0xcd, 0x71, 0xc4, 0x6a, + 0x9f, 0xbc, 0x8b, 0x0c, 0x22, 0x49, 0x43, 0x45, 0x71, 0x6e, 0x2e, 0x73, 0x9f, 0x21, + 0x12, 0x59, 0x64, 0x0e, 0x9a, 0xc8, 0xba, 0x08, 0x00, 0xe6, 0x97, 0xc2, 0xe0, 0xc3, + 0xe1, 0xea, 0x11, 0xea, 0x4c, 0x7d, 0x7c, 0x97, 0xe7, 0x9f, 0xe1, 0x8b, 0xe3, 0xf3, + 0xcd, 0x05, 0xa3, 0x63, 0x0f, 0x45, 0x3a, 0x3a, 0x27, 0x46, 0x39, 0xd8, 0x31, 0x2f, + 0x8f, 0x07, 0x10, 0xa5, 0x94, 0xde, 0x83, 0x31, 0x9d, 0x38, 0x80, 0x6f, 0x99, 0x17, + 0x6d, 0x6c, 0xe3, 0xd1, 0x7b, 0xa8, 0xa9, 0x93, 0x93, 0x8d, 0x8c, 0x31}, + {0x19, 0xfe, 0xff, 0x2a, 0x03, 0x5d, 0x74, 0xf2, 0x66, 0xdb, 0x24, 0x7f, 0x49, 0x3c, + 0x9f, 0x0c, 0xef, 0x98, 0x85, 0xba, 0xe3, 0xd3, 0x98, 0xbc, 0x14, 0x53, 0x1d, 0x9a, + 0x67, 0x7c, 0x4c, 0x22, 0x98, 0xd3, 0x1d, 0xab, 0x29, 0x9e, 0x66, 0x5d, 0x3b, 0x9e, + 0x2d, 0x34, 0x58, 0x16, 0x92, 0xfc, 0xcd, 0x73, 0x59, 0xf3, 0xfd, 0x1d, 0x85, 0x55, + 0xf6, 0x0a, 0x95, 0x25, 0xc3, 0x41, 0x9a, 0x50, 0xe9, 0x25, 0xf9, 0xa6, 0xdc, 0x6e, + 0xc0, 0xbd, 0x33, 0x1f, 0x1b, 0x64, 0xf4, 0xf3, 0x3e, 0x79, 0x89, 0x3e, 0x83, 0x9d, + 0x80, 0x12, 0xec, 0x82, 0x89, 0x13, 0xa1, 0x28, 0x23, 0xf0, 0xbf, 0x05}, + {0x0b, 0xe0, 0xca, 0x23, 0x70, 0x13, 0x32, 0x36, 0x59, 0xcf, 0xac, 0xd1, 0x0a, 0xcf, + 0x4a, 0x54, 0x88, 0x1c, 0x1a, 0xd2, 0x49, 0x10, 0x74, 0x96, 0xa7, 0x44, 0x2a, 0xfa, + 0xc3, 0x8c, 0x0b, 0x78, 0xe4, 0x12, 0xc5, 0x0d, 0xdd, 0xa0, 0x81, 0x68, 0xfe, 0xfa, + 0xa5, 0x44, 0xc8, 0x0d, 0xe7, 0x4f, 0x40, 0x52, 0x4a, 0x8f, 0x6b, 0x8e, 0x74, 0x1f, + 0xea, 0xa3, 0x01, 0xee, 0xcd, 0x77, 0x62, 0x57, 0x5f, 0x30, 0x4f, 0x23, 0xbc, 0x8a, + 0xf3, 0x1e, 0x08, 0xde, 0x05, 0x14, 0xbd, 0x7f, 0x57, 0x9a, 0x0d, 0x2a, 0xe6, 0x34, + 0x14, 0xa5, 0x82, 0x5e, 0xa1, 0xb7, 0x71, 0x62, 0x72, 0x18, 0xf4, 0x5f}, + {0x9d, 0xdb, 0x89, 0x17, 0x0c, 0x08, 0x8e, 0x39, 0xf5, 0x78, 0xe7, 0xf3, 0x25, 0x20, + 0x60, 0xa7, 0x5d, 0x03, 0xbd, 0x06, 0x4c, 0x89, 0x98, 0xfa, 0xbe, 0x66, 0xa9, 0x25, + 0xdc, 0x03, 0x6a, 0x10, 0x40, 0x95, 0xb6, 0x13, 0xe8, 0x47, 0xdb, 0xe5, 0xe1, 0x10, + 0x26, 0x43, 0x3b, 0x2a, 0x5d, 0xf3, 0x76, 0x12, 0x78, 0x38, 0xe9, 0x26, 0x1f, 0xac, + 0x69, 0xcb, 0xa0, 0xa0, 0x8c, 0xdb, 0xd4, 0x29, 0xd0, 0x53, 0x33, 0x33, 0xaf, 0x0a, + 0xad, 0xd9, 0xe5, 0x09, 0xd3, 0xac, 0xa5, 0x9d, 0x66, 0x38, 0xf0, 0xf7, 0x88, 0xc8, + 0x8a, 0x65, 0x57, 0x3c, 0xfa, 0xbe, 0x2c, 0x05, 0x51, 0x8a, 0xb3, 0x4a}, + {0x93, 0xd5, 0x68, 0x67, 0x25, 0x2b, 0x7c, 0xda, 0x13, 0xca, 0x22, 0x44, 0x57, 0xc0, + 0xc1, 0x98, 0x1d, 0xce, 0x0a, 0xca, 0xd5, 0x0b, 0xa8, 0xf1, 0x90, 0xa6, 0x88, 0xc0, + 0xad, 0xd1, 0xcd, 0x29, 0x9c, 0xc0, 0xdd, 0x5f, 0xef, 0xd1, 0xcf, 0xd6, 0xce, 0x5d, + 0x57, 0xf7, 0xfd, 0x3e, 0x2b, 0xe8, 0xc2, 0x34, 0x16, 0x20, 0x5d, 0x6b, 0xd5, 0x25, + 0x9b, 0x2b, 0xed, 0x04, 0xbb, 0xc6, 0x41, 0x30, 0x48, 0xe1, 0x56, 0xd9, 0xf9, 0xf2, + 0xf2, 0x0f, 0x2e, 0x6b, 0x35, 0x9f, 0x75, 0x97, 0xe7, 0xad, 0x5c, 0x02, 0x6c, 0x5f, + 0xbb, 0x98, 0x46, 0x1a, 0x7b, 0x9a, 0x04, 0x14, 0x68, 0xbd, 0x4b, 0x10}, + {0x67, 0xed, 0xf1, 0x68, 0x31, 0xfd, 0xf0, 0x51, 0xc2, 0x3b, 0x6f, 0xd8, 0xcd, 0x1d, + 0x81, 0x2c, 0xde, 0xf2, 0xd2, 0x04, 0x43, 0x5c, 0xdc, 0x44, 0x49, 0x71, 0x2a, 0x09, + 0x57, 0xcc, 0xe8, 0x5b, 0x63, 0xf1, 0x7f, 0xd6, 0x5f, 0x9a, 0x5d, 0xa9, 0x81, 0x56, + 0xc7, 0x4c, 0x9d, 0xe6, 0x2b, 0xe9, 0x57, 0xf2, 0x20, 0xde, 0x4c, 0x02, 0xf8, 0xb7, + 0xf5, 0x2d, 0x07, 0xfb, 0x20, 0x2a, 0x4f, 0x20, 0x79, 0xb0, 0xeb, 0x30, 0x3d, 0x3b, + 0x14, 0xc8, 0x30, 0x2e, 0x65, 0xbd, 0x5a, 0x15, 0x89, 0x75, 0x31, 0x5c, 0x6d, 0x8f, + 0x31, 0x3c, 0x3c, 0x65, 0x1f, 0x16, 0x79, 0xc2, 0x17, 0xfb, 0x70, 0x25}, + {0x75, 0x15, 0xb6, 0x2c, 0x7f, 0x36, 0xfa, 0x3e, 0x6c, 0x02, 0xd6, 0x1c, 0x76, 0x6f, + 0xf9, 0xf5, 0x62, 0x25, 0xb5, 0x65, 0x2a, 0x14, 0xc7, 0xe8, 0xcd, 0x0a, 0x03, 0x53, + 0xea, 0x65, 0xcb, 0x3d, 0x5a, 0x24, 0xb8, 0x0b, 0x55, 0xa9, 0x2e, 0x19, 0xd1, 0x50, + 0x90, 0x8f, 0xa8, 0xfb, 0xe6, 0xc8, 0x35, 0xc9, 0xa4, 0x88, 0x2d, 0xea, 0x86, 0x79, + 0x68, 0x86, 0x01, 0xde, 0x91, 0x5f, 0x1c, 0x24, 0xaa, 0x6c, 0xde, 0x40, 0x29, 0x17, + 0xd8, 0x28, 0x3a, 0x73, 0xd9, 0x22, 0xf0, 0x2c, 0xbf, 0x8f, 0xd1, 0x01, 0x5b, 0x23, + 0xdd, 0xfc, 0xd7, 0x16, 0xe5, 0xf0, 0xcd, 0x5f, 0xdd, 0x0e, 0x42, 0x08}, + {0x4a, 0xfa, 0x62, 0x83, 0xab, 0x20, 0xff, 0xcd, 0x6e, 0x3e, 0x1a, 0xe2, 0xd4, 0x18, + 0xe1, 0x57, 0x2b, 0xe6, 0x39, 0xfc, 0x17, 0x96, 0x17, 0xe3, 0xfd, 0x69, 0x17, 0xbc, + 0xef, 0x53, 0x9a, 0x0d, 0xce, 0x10, 0xf4, 0x04, 0x4e, 0xc3, 0x58, 0x03, 0x85, 0x06, + 0x6e, 0x27, 0x5a, 0x5b, 0x13, 0xb6, 0x21, 0x15, 0xb9, 0xeb, 0xc7, 0x70, 0x96, 0x5d, + 0x9c, 0x88, 0xdb, 0x21, 0xf3, 0x54, 0xd6, 0x04, 0xd5, 0xb5, 0xbd, 0xdd, 0x16, 0xc1, + 0x7d, 0x5e, 0x2d, 0xdd, 0xa5, 0x8d, 0xb6, 0xde, 0x54, 0x29, 0x92, 0xa2, 0x34, 0x33, + 0x17, 0x08, 0xb6, 0x1c, 0xd7, 0x1a, 0x99, 0x18, 0x26, 0x4f, 0x7a, 0x4a}, + {0x95, 0x5f, 0xb1, 0x5f, 0x02, 0x18, 0xa7, 0xf4, 0x8f, 0x1b, 0x5c, 0x6b, 0x34, 0x5f, + 0xf6, 0x3d, 0x12, 0x11, 0xe0, 0x00, 0x85, 0xf0, 0xfc, 0xcd, 0x48, 0x18, 0xd3, 0xdd, + 0x4c, 0x0c, 0xb5, 0x11, 0x4b, 0x2a, 0x37, 0xaf, 0x91, 0xb2, 0xc3, 0x24, 0xf2, 0x47, + 0x81, 0x71, 0x70, 0x82, 0xda, 0x93, 0xf2, 0x9e, 0x89, 0x86, 0x64, 0x85, 0x84, 0xdd, + 0x33, 0xee, 0xe0, 0x23, 0x42, 0x31, 0x96, 0x4a, 0xd6, 0xff, 0xa4, 0x08, 0x44, 0x27, + 0xe8, 0xa6, 0xd9, 0x76, 0x15, 0x9c, 0x7e, 0x17, 0x8e, 0x73, 0xf2, 0xb3, 0x02, 0x3d, + 0xb6, 0x48, 0x33, 0x77, 0x51, 0xcc, 0x6b, 0xce, 0x4d, 0xce, 0x4b, 0x4f}, + {0x84, 0x25, 0x24, 0xe2, 0x5a, 0xce, 0x1f, 0xa7, 0x9e, 0x8a, 0xf5, 0x92, 0x56, 0x72, + 0xea, 0x26, 0xf4, 0x3c, 0xea, 0x1c, 0xd7, 0x09, 0x1a, 0xd2, 0xe6, 0x01, 0x1c, 0xb7, + 0x14, 0xdd, 0xfc, 0x73, 0x6f, 0x0b, 0x9d, 0xc4, 0x6e, 0x61, 0xe2, 0x30, 0x17, 0x23, + 0xec, 0xca, 0x8f, 0x71, 0x56, 0xe4, 0xa6, 0x4f, 0x6b, 0xf2, 0x9b, 0x40, 0xeb, 0x48, + 0x37, 0x5f, 0x59, 0x61, 0xe5, 0xce, 0x42, 0x30, 0x41, 0xac, 0x9b, 0x44, 0x79, 0x70, + 0x7e, 0x42, 0x0a, 0x31, 0xe2, 0xbc, 0x6d, 0xe3, 0x5a, 0x85, 0x7c, 0x1a, 0x84, 0x5f, + 0x21, 0x76, 0xae, 0x4c, 0xd6, 0xe1, 0x9c, 0x9a, 0x0c, 0x74, 0x9e, 0x38}, + {0xce, 0xb9, 0xdc, 0x34, 0xae, 0xb3, 0xfc, 0x64, 0xad, 0xd0, 0x48, 0xe3, 0x23, 0x03, + 0x50, 0x97, 0x1b, 0x38, 0xc6, 0x62, 0x7d, 0xf0, 0xb3, 0x45, 0x88, 0x67, 0x5a, 0x46, + 0x79, 0x53, 0x54, 0x61, 0x28, 0xac, 0x0e, 0x57, 0xf6, 0x78, 0xbd, 0xc9, 0xe1, 0x9c, + 0x91, 0x27, 0x32, 0x0b, 0x5b, 0xe5, 0xed, 0x91, 0x9b, 0xa1, 0xab, 0x3e, 0xfc, 0x65, + 0x90, 0x36, 0x26, 0xd6, 0xe5, 0x25, 0xc4, 0x25, 0x6e, 0xde, 0xd7, 0xf1, 0xa6, 0x06, + 0x3e, 0x3f, 0x08, 0x23, 0x06, 0x8e, 0x27, 0x76, 0xf9, 0x3e, 0x77, 0x6c, 0x8a, 0x4e, + 0x26, 0xf6, 0x14, 0x8c, 0x59, 0x47, 0x48, 0x15, 0x89, 0xa0, 0x39, 0x65}, + {0x73, 0xf7, 0xd2, 0xc3, 0x74, 0x1f, 0xd2, 0xe9, 0x45, 0x68, 0xc4, 0x25, 0x41, 0x54, + 0x50, 0xc1, 0x33, 0x9e, 0xb9, 0xf9, 0xe8, 0x5c, 0x4e, 0x62, 0x6c, 0x18, 0xcd, 0xc5, + 0xaa, 0xe4, 0xc5, 0x11, 0x19, 0x4a, 0xbb, 0x14, 0xd4, 0xdb, 0xc4, 0xdd, 0x8e, 0x4f, + 0x42, 0x98, 0x3c, 0xbc, 0xb2, 0x19, 0x69, 0x71, 0xca, 0x36, 0xd7, 0x9f, 0xa8, 0x48, + 0x90, 0xbd, 0x19, 0xf0, 0x0e, 0x32, 0x65, 0x0f, 0xc6, 0xe0, 0xfd, 0xca, 0xb1, 0xd1, + 0x86, 0xd4, 0x81, 0x51, 0x3b, 0x16, 0xe3, 0xe6, 0x3f, 0x4f, 0x9a, 0x93, 0xf2, 0xfa, + 0x0d, 0xaf, 0xa8, 0x59, 0x2a, 0x07, 0x33, 0xec, 0xbd, 0xc7, 0xab, 0x4c}, + {0x2e, 0x0a, 0x9c, 0x08, 0x24, 0x96, 0x9e, 0x23, 0x38, 0x47, 0xfe, 0x3a, 0xc0, 0xc4, + 0x48, 0xc7, 0x2a, 0xa1, 0x4f, 0x76, 0x2a, 0xed, 0xdb, 0x17, 0x82, 0x85, 0x1c, 0x32, + 0xf0, 0x93, 0x9b, 0x63, 0x89, 0xd2, 0x78, 0x3f, 0x8f, 0x78, 0x8f, 0xc0, 0x9f, 0x4d, + 0x40, 0xa1, 0x2c, 0xa7, 0x30, 0xfe, 0x9d, 0xcc, 0x65, 0xcf, 0xfc, 0x8b, 0x77, 0xf2, + 0x21, 0x20, 0xcb, 0x5a, 0x16, 0x98, 0xe4, 0x7e, 0xc3, 0xa1, 0x11, 0x91, 0xe3, 0x08, + 0xd5, 0x7b, 0x89, 0x74, 0x90, 0x80, 0xd4, 0x90, 0x2b, 0x2b, 0x19, 0xfd, 0x72, 0xae, + 0xc2, 0xae, 0xd2, 0xe7, 0xa6, 0x02, 0xb6, 0x85, 0x3c, 0x49, 0xdf, 0x0e}, + {0x68, 0x5a, 0x9b, 0x59, 0x58, 0x81, 0xcc, 0xae, 0x0e, 0xe2, 0xad, 0xeb, 0x0f, 0x4f, + 0x57, 0xea, 0x07, 0x7f, 0xb6, 0x22, 0x74, 0x1d, 0xe4, 0x4f, 0xb4, 0x4f, 0x9d, 0x01, + 0xe3, 0x92, 0x3b, 0x40, 0x13, 0x41, 0x76, 0x84, 0xd2, 0xc4, 0x67, 0x67, 0x35, 0xf8, + 0xf5, 0xf7, 0x3f, 0x40, 0x90, 0xa0, 0xde, 0xbe, 0xe6, 0xca, 0xfa, 0xcf, 0x8f, 0x1c, + 0x69, 0xa3, 0xdf, 0xd1, 0x54, 0x0c, 0xc0, 0x04, 0xf8, 0x5c, 0x46, 0x8b, 0x81, 0x2f, + 0xc2, 0x4d, 0xf8, 0xef, 0x80, 0x14, 0x5a, 0xf3, 0xa0, 0x71, 0x57, 0xd6, 0xc7, 0x04, + 0xad, 0xbf, 0xe8, 0xae, 0xf4, 0x76, 0x61, 0xb2, 0x2a, 0xb1, 0x5b, 0x35}, + {0xf4, 0xbb, 0x93, 0x74, 0xcc, 0x64, 0x1e, 0xa7, 0xc3, 0xb0, 0xa3, 0xec, 0xd9, 0x84, + 0xbd, 0xe5, 0x85, 0xe7, 0x05, 0xfa, 0x0c, 0xc5, 0x6b, 0x0a, 0x12, 0xc3, 0x2e, 0x18, + 0x32, 0x81, 0x9b, 0x0f, 0x18, 0x73, 0x8c, 0x5a, 0xc7, 0xda, 0x01, 0xa3, 0x11, 0xaa, + 0xce, 0xb3, 0x9d, 0x03, 0x90, 0xed, 0x2d, 0x3f, 0xae, 0x3b, 0xbf, 0x7c, 0x07, 0x6f, + 0x8e, 0xad, 0x52, 0xe0, 0xf8, 0xea, 0x18, 0x75, 0x32, 0x6c, 0x7f, 0x1b, 0xc4, 0x59, + 0x88, 0xa4, 0x98, 0x32, 0x38, 0xf4, 0xbc, 0x60, 0x2d, 0x0f, 0xd9, 0xd1, 0xb1, 0xc9, + 0x29, 0xa9, 0x15, 0x18, 0xc4, 0x55, 0x17, 0xbb, 0x1b, 0x87, 0xc3, 0x47}, + {0x48, 0x4f, 0xec, 0x71, 0x97, 0x53, 0x44, 0x51, 0x6e, 0x5d, 0x8c, 0xc9, 0x7d, 0xb1, + 0x05, 0xf8, 0x6b, 0xc6, 0xc3, 0x47, 0x1a, 0xc1, 0x62, 0xf7, 0xdc, 0x99, 0x46, 0x76, + 0x85, 0x9b, 0xb8, 0x00, 0xb0, 0x66, 0x50, 0xc8, 0x50, 0x5d, 0xe6, 0xfb, 0xb0, 0x99, + 0xa2, 0xb3, 0xb0, 0xc4, 0xec, 0x62, 0xe0, 0xe8, 0x1a, 0x44, 0xea, 0x54, 0x37, 0xe5, + 0x5f, 0x8d, 0xd4, 0xe8, 0x2c, 0xa0, 0xfe, 0x08, 0xd0, 0xea, 0xde, 0x68, 0x76, 0xdd, + 0x4d, 0x82, 0x23, 0x5d, 0x68, 0x4b, 0x20, 0x45, 0x64, 0xc8, 0x65, 0xd6, 0x89, 0x5d, + 0xcd, 0xcf, 0x14, 0xb5, 0x37, 0xd5, 0x75, 0x4f, 0xa7, 0x29, 0x38, 0x47}, + {0x18, 0xc4, 0x79, 0x46, 0x75, 0xda, 0xd2, 0x82, 0xf0, 0x8d, 0x61, 0xb2, 0xd8, 0xd7, + 0x3b, 0xe6, 0x0a, 0xeb, 0x47, 0xac, 0x24, 0xef, 0x5e, 0x35, 0xb4, 0xc6, 0x33, 0x48, + 0x4c, 0x68, 0x78, 0x20, 0xc9, 0x02, 0x39, 0xad, 0x3a, 0x53, 0xd9, 0x23, 0x8f, 0x58, + 0x03, 0xef, 0xce, 0xdd, 0xc2, 0x64, 0xb4, 0x2f, 0xe1, 0xcf, 0x90, 0x73, 0x25, 0x15, + 0x90, 0xd3, 0xe4, 0x44, 0x4d, 0x8b, 0x66, 0x6c, 0x0c, 0x82, 0x78, 0x7a, 0x21, 0xcf, + 0x48, 0x3b, 0x97, 0x3e, 0x27, 0x81, 0xb2, 0x0a, 0x6a, 0xf7, 0x7b, 0xed, 0x8e, 0x8c, + 0xa7, 0x65, 0x6c, 0xa9, 0x3f, 0x43, 0x8a, 0x4f, 0x05, 0xa6, 0x11, 0x74}, + {0x6d, 0xc8, 0x9d, 0xb9, 0x32, 0x9d, 0x65, 0x4d, 0x15, 0xf1, 0x3a, 0x60, 0x75, 0xdc, + 0x4c, 0x04, 0x88, 0xe4, 0xc2, 0xdc, 0x2c, 0x71, 0x4c, 0xb3, 0xff, 0x34, 0x81, 0xfb, + 0x74, 0x65, 0x13, 0x7c, 0xb4, 0x75, 0xb1, 0x18, 0x3d, 0xe5, 0x9a, 0x57, 0x02, 0xa1, + 0x92, 0xf3, 0x59, 0x31, 0x71, 0x68, 0xf5, 0x35, 0xef, 0x1e, 0xba, 0xec, 0x55, 0x84, + 0x8f, 0x39, 0x8c, 0x45, 0x72, 0xa8, 0xc9, 0x1e, 0x9b, 0x50, 0xa2, 0x00, 0xd4, 0xa4, + 0xe6, 0xb8, 0xb4, 0x82, 0xc8, 0x0b, 0x02, 0xd7, 0x81, 0x9b, 0x61, 0x75, 0x95, 0xf1, + 0x9b, 0xcc, 0xe7, 0x57, 0x60, 0x64, 0xcd, 0xc7, 0xa5, 0x88, 0xdd, 0x3a}, + {0xf2, 0xdc, 0x35, 0xb6, 0x70, 0x57, 0x89, 0xab, 0xbc, 0x1f, 0x6c, 0xf6, 0x6c, 0xef, + 0xdf, 0x02, 0x87, 0xd1, 0xb6, 0xbe, 0x68, 0x02, 0x53, 0x85, 0x74, 0x9e, 0x87, 0xcc, + 0xfc, 0x29, 0x99, 0x24, 0x46, 0x30, 0x39, 0x59, 0xd4, 0x98, 0xc2, 0x85, 0xec, 0x59, + 0xf6, 0x5f, 0x98, 0x35, 0x7e, 0x8f, 0x3a, 0x6e, 0xf6, 0xf2, 0x2a, 0xa2, 0x2c, 0x1d, + 0x20, 0xa7, 0x06, 0xa4, 0x31, 0x11, 0xba, 0x61, 0x29, 0x90, 0x95, 0x16, 0xf1, 0xa0, + 0xd0, 0xa3, 0x89, 0xbd, 0x7e, 0xba, 0x6c, 0x6b, 0x3b, 0x02, 0x07, 0x33, 0x78, 0x26, + 0x3e, 0x5a, 0xf1, 0x7b, 0xe7, 0xec, 0xd8, 0xbb, 0x0c, 0x31, 0x20, 0x56}, + {0x43, 0xd6, 0x34, 0x49, 0x43, 0x93, 0x89, 0x52, 0xf5, 0x22, 0x12, 0xa5, 0x06, 0xf8, + 0xdb, 0xb9, 0x22, 0x1c, 0xf4, 0xc3, 0x8f, 0x87, 0x6d, 0x8f, 0x30, 0x97, 0x9d, 0x4d, + 0x2a, 0x6a, 0x67, 0x37, 0xd6, 0x85, 0xe2, 0x77, 0xf4, 0xb5, 0x46, 0x66, 0x93, 0x61, + 0x8f, 0x6c, 0x67, 0xff, 0xe8, 0x40, 0xdd, 0x94, 0xb5, 0xab, 0x11, 0x73, 0xec, 0xa6, + 0x4d, 0xec, 0x8c, 0x65, 0xf3, 0x46, 0xc8, 0x7e, 0xc7, 0x2e, 0xa2, 0x1d, 0x3f, 0x8f, + 0x5e, 0x9b, 0x13, 0xcd, 0x01, 0x6c, 0x77, 0x1d, 0x0f, 0x13, 0xb8, 0x9f, 0x98, 0xa2, + 0xcf, 0x8f, 0x4c, 0x21, 0xd5, 0x9d, 0x9b, 0x39, 0x23, 0xf7, 0xaa, 0x6d}, + {0x47, 0xbe, 0x3d, 0xeb, 0x62, 0x75, 0x3a, 0x5f, 0xb8, 0xa0, 0xbd, 0x8e, 0x54, 0x38, + 0xea, 0xf7, 0x99, 0x72, 0x74, 0x45, 0x31, 0xe5, 0xc3, 0x00, 0x51, 0xd5, 0x27, 0x16, + 0xe7, 0xe9, 0x04, 0x13, 0xa2, 0x8e, 0xad, 0xac, 0xbf, 0x04, 0x3b, 0x58, 0x84, 0xe8, + 0x8b, 0x14, 0xe8, 0x43, 0xb7, 0x29, 0xdb, 0xc5, 0x10, 0x08, 0x3b, 0x58, 0x1e, 0x2b, + 0xaa, 0xbb, 0xb3, 0x8e, 0xe5, 0x49, 0x54, 0x2b, 0xfe, 0x9c, 0xdc, 0x6a, 0xd2, 0x14, + 0x98, 0x78, 0x0b, 0xdd, 0x48, 0x8b, 0x3f, 0xab, 0x1b, 0x3c, 0x0a, 0xc6, 0x79, 0xf9, + 0xff, 0xe1, 0x0f, 0xda, 0x93, 0xd6, 0x2d, 0x7c, 0x2d, 0xde, 0x68, 0x44}, + {0x9e, 0x46, 0x19, 0x94, 0x5e, 0x35, 0xbb, 0x51, 0x54, 0xc7, 0xdd, 0x23, 0x4c, 0xdc, + 0xe6, 0x33, 0x62, 0x99, 0x7f, 0x44, 0xd6, 0xb6, 0xa5, 0x93, 0x63, 0xbd, 0x44, 0xfb, + 0x6f, 0x7c, 0xce, 0x6c, 0xce, 0x07, 0x63, 0xf8, 0xc6, 0xd8, 0x9a, 0x4b, 0x28, 0x0c, + 0x5d, 0x43, 0x31, 0x35, 0x11, 0x21, 0x2c, 0x77, 0x7a, 0x65, 0xc5, 0x66, 0xa8, 0xd4, + 0x52, 0x73, 0x24, 0x63, 0x7e, 0x42, 0xa6, 0x5d, 0xca, 0x22, 0xac, 0xde, 0x88, 0xc6, + 0x94, 0x1a, 0xf8, 0x1f, 0xae, 0xbb, 0xf7, 0x6e, 0x06, 0xb9, 0x0f, 0x58, 0x59, 0x8d, + 0x38, 0x8c, 0xad, 0x88, 0xa8, 0x2c, 0x9f, 0xe7, 0xbf, 0x9a, 0xf2, 0x58}, + {0x68, 0x3e, 0xe7, 0x8d, 0xab, 0xcf, 0x0e, 0xe9, 0xa5, 0x76, 0x7e, 0x37, 0x9f, 0x6f, + 0x03, 0x54, 0x82, 0x59, 0x01, 0xbe, 0x0b, 0x5b, 0x49, 0xf0, 0x36, 0x1e, 0xf4, 0xa7, + 0xc4, 0x29, 0x76, 0x57, 0xf6, 0xcd, 0x0e, 0x71, 0xbf, 0x64, 0x5a, 0x4b, 0x3c, 0x29, + 0x2c, 0x46, 0x38, 0xe5, 0x4c, 0xb1, 0xb9, 0x3a, 0x0b, 0xd5, 0x56, 0xd0, 0x43, 0x36, + 0x70, 0x48, 0x5b, 0x18, 0x24, 0x37, 0xf9, 0x6a, 0x88, 0xa8, 0xc6, 0x09, 0x45, 0x02, + 0x20, 0x32, 0x73, 0x89, 0x55, 0x4b, 0x13, 0x36, 0xe0, 0xd2, 0x9f, 0x28, 0x33, 0x3c, + 0x23, 0x36, 0xe2, 0x83, 0x8f, 0xc1, 0xae, 0x0c, 0xbb, 0x25, 0x1f, 0x70}, + {0xed, 0x6c, 0x61, 0xe4, 0xf8, 0xb0, 0xa8, 0xc3, 0x7d, 0xa8, 0x25, 0x9e, 0x0e, 0x66, + 0x00, 0xf7, 0x9c, 0xa5, 0xbc, 0xf4, 0x1f, 0x06, 0xe3, 0x61, 0xe9, 0x0b, 0xc4, 0xbd, + 0xbf, 0x92, 0x0c, 0x2e, 0x13, 0xc1, 0xbe, 0x7c, 0xd9, 0xf6, 0x18, 0x9d, 0xe4, 0xdb, + 0xbf, 0x74, 0xe6, 0x06, 0x4a, 0x84, 0xd6, 0x60, 0x4e, 0xac, 0x22, 0xb5, 0xf5, 0x20, + 0x51, 0x5e, 0x95, 0x50, 0xc0, 0x5b, 0x0a, 0x72, 0x35, 0x5a, 0x80, 0x9b, 0x43, 0x09, + 0x3f, 0x0c, 0xfc, 0xab, 0x42, 0x62, 0x37, 0x8b, 0x4e, 0xe8, 0x46, 0x93, 0x22, 0x5c, + 0xf3, 0x17, 0x14, 0x69, 0xec, 0xf0, 0x4e, 0x14, 0xbb, 0x9c, 0x9b, 0x0e}, + {0xad, 0x20, 0x57, 0xfb, 0x8f, 0xd4, 0xba, 0xfb, 0x0e, 0x0d, 0xf9, 0xdb, 0x6b, 0x91, + 0x81, 0xee, 0xbf, 0x43, 0x55, 0x63, 0x52, 0x31, 0x81, 0xd4, 0xd8, 0x7b, 0x33, 0x3f, + 0xeb, 0x04, 0x11, 0x22, 0xee, 0xbe, 0xb1, 0x5d, 0xd5, 0x9b, 0xee, 0x8d, 0xb9, 0x3f, + 0x72, 0x0a, 0x37, 0xab, 0xc3, 0xc9, 0x91, 0xd7, 0x68, 0x1c, 0xbf, 0xf1, 0xa8, 0x44, + 0xde, 0x3c, 0xfd, 0x1c, 0x19, 0x44, 0x6d, 0x36, 0x14, 0x8c, 0xbc, 0xf2, 0x43, 0x17, + 0x3c, 0x9e, 0x3b, 0x6c, 0x85, 0xb5, 0xfc, 0x26, 0xda, 0x2e, 0x97, 0xfb, 0xa7, 0x68, + 0x0e, 0x2f, 0xb8, 0xcc, 0x44, 0x32, 0x59, 0xbc, 0xe6, 0xa4, 0x67, 0x41}, + {0x00, 0x27, 0xf6, 0x76, 0x28, 0x9d, 0x3b, 0x64, 0xeb, 0x68, 0x76, 0x0e, 0x40, 0x9d, + 0x1d, 0x5d, 0x84, 0x06, 0xfc, 0x21, 0x03, 0x43, 0x4b, 0x1b, 0x6a, 0x24, 0x55, 0x22, + 0x7e, 0xbb, 0x38, 0x79, 0xee, 0x8f, 0xce, 0xf8, 0x65, 0x26, 0xbe, 0xc2, 0x2c, 0xd6, + 0x80, 0xe8, 0x14, 0xff, 0x67, 0xe9, 0xee, 0x4e, 0x36, 0x2f, 0x7e, 0x6e, 0x2e, 0xf1, + 0xf6, 0xd2, 0x7e, 0xcb, 0x70, 0x33, 0xb3, 0x34, 0xcc, 0xd6, 0x81, 0x86, 0xee, 0x91, + 0xc5, 0xcd, 0x53, 0xa7, 0x85, 0xed, 0x9c, 0x10, 0x02, 0xce, 0x83, 0x88, 0x80, 0x58, + 0xc1, 0x85, 0x74, 0xed, 0xe4, 0x65, 0xfe, 0x2d, 0x6e, 0xfc, 0x76, 0x11}, + {0x9b, 0x61, 0x9c, 0x5b, 0xd0, 0x6c, 0xaf, 0xb4, 0x80, 0x84, 0xa5, 0xb2, 0xf4, 0xc9, + 0xdf, 0x2d, 0xc4, 0x4d, 0xe9, 0xeb, 0x02, 0xa5, 0x4f, 0x3d, 0x34, 0x5f, 0x7d, 0x67, + 0x4c, 0x3a, 0xfc, 0x08, 0xb8, 0x0e, 0x77, 0x49, 0x89, 0xe2, 0x90, 0xdb, 0xa3, 0x40, + 0xf4, 0xac, 0x2a, 0xcc, 0xfb, 0x98, 0x9b, 0x87, 0xd7, 0xde, 0xfe, 0x4f, 0x35, 0x21, + 0xb6, 0x06, 0x69, 0xf2, 0x54, 0x3e, 0x6a, 0x1f, 0xea, 0x34, 0x07, 0xd3, 0x99, 0xc1, + 0xa4, 0x60, 0xd6, 0x5c, 0x16, 0x31, 0xb6, 0x85, 0xc0, 0x40, 0x95, 0x82, 0x59, 0xf7, + 0x23, 0x3e, 0x33, 0xe2, 0xd1, 0x00, 0xb9, 0x16, 0x01, 0xad, 0x2f, 0x4f}, + {0x54, 0x4e, 0xae, 0x94, 0x41, 0xb2, 0xbe, 0x44, 0x6c, 0xef, 0x57, 0x18, 0x51, 0x1c, + 0x54, 0x5f, 0x98, 0x04, 0x8d, 0x36, 0x2d, 0x6b, 0x1e, 0xa6, 0xab, 0xf7, 0x2e, 0x97, + 0xa4, 0x84, 0x54, 0x44, 0x38, 0xb6, 0x3b, 0xb7, 0x1d, 0xd9, 0x2c, 0x96, 0x08, 0x9c, + 0x12, 0xfc, 0xaa, 0x77, 0x05, 0xe6, 0x89, 0x16, 0xb6, 0xf3, 0x39, 0x9b, 0x61, 0x6f, + 0x81, 0xee, 0x44, 0x29, 0x5f, 0x99, 0x51, 0x34, 0x7c, 0x7d, 0xea, 0x9f, 0xd0, 0xfc, + 0x52, 0x91, 0xf6, 0x5c, 0x93, 0xb0, 0x94, 0x6c, 0x81, 0x4a, 0x40, 0x5c, 0x28, 0x47, + 0xaa, 0x9a, 0x8e, 0x25, 0xb7, 0x93, 0x28, 0x04, 0xa6, 0x9c, 0xb8, 0x10}, + {0x9c, 0x28, 0x18, 0x97, 0x49, 0x47, 0x59, 0x3d, 0x26, 0x3f, 0x53, 0x24, 0xc5, 0xf8, + 0xeb, 0x12, 0x15, 0xef, 0xc3, 0x14, 0xcb, 0xbf, 0x62, 0x02, 0x8e, 0x51, 0xb7, 0x77, + 0xd5, 0x78, 0xb8, 0x20, 0x6e, 0xf0, 0x45, 0x5a, 0xbe, 0x41, 0x39, 0x75, 0x65, 0x5f, + 0x9c, 0x6d, 0xed, 0xae, 0x7c, 0xd0, 0xb6, 0x51, 0xff, 0x72, 0x9c, 0x6b, 0x77, 0x11, + 0xa9, 0x4d, 0x0d, 0xef, 0xd9, 0xd1, 0xd2, 0x17, 0x6a, 0x3e, 0x3f, 0x07, 0x18, 0xaf, + 0xf2, 0x27, 0x69, 0x10, 0x52, 0xd7, 0x19, 0xe5, 0x3f, 0xfd, 0x22, 0x00, 0xa6, 0x3c, + 0x2c, 0xb7, 0xe3, 0x22, 0xa7, 0xc6, 0x65, 0xcc, 0x63, 0x4f, 0x21, 0x72}, + {0x93, 0xa6, 0x07, 0x53, 0x40, 0x7f, 0xe3, 0xb4, 0x95, 0x67, 0x33, 0x2f, 0xd7, 0x14, + 0xa7, 0xab, 0x99, 0x10, 0x76, 0x73, 0xa7, 0xd0, 0xfb, 0xd6, 0xc9, 0xcb, 0x71, 0x81, + 0xc5, 0x48, 0xdf, 0x5f, 0xc9, 0x29, 0x3b, 0xf4, 0xb9, 0xb7, 0x9d, 0x1d, 0x75, 0x8f, + 0x51, 0x4f, 0x4a, 0x82, 0x05, 0xd6, 0xc4, 0x9d, 0x2f, 0x31, 0xbd, 0x72, 0xc0, 0xf2, + 0xb0, 0x45, 0x15, 0x5a, 0x85, 0xac, 0x24, 0x1f, 0xaa, 0x05, 0x95, 0x8e, 0x32, 0x08, + 0xd6, 0x24, 0xee, 0x20, 0x14, 0x0c, 0xd1, 0xc1, 0x48, 0x47, 0xa2, 0x25, 0xfb, 0x06, + 0x5c, 0xe4, 0xff, 0xc7, 0xe6, 0x95, 0xe3, 0x2a, 0x9e, 0x73, 0xba, 0x00}, + {0xd6, 0x90, 0x87, 0x5c, 0xde, 0x98, 0x2e, 0x59, 0xdf, 0xa2, 0xc2, 0x45, 0xd3, 0xb7, + 0xbf, 0xe5, 0x22, 0x99, 0xb4, 0xf9, 0x60, 0x3b, 0x5a, 0x11, 0xf3, 0x78, 0xad, 0x67, + 0x3e, 0x3a, 0x28, 0x03, 0x26, 0xbb, 0x88, 0xea, 0xf5, 0x26, 0x44, 0xae, 0xfb, 0x3b, + 0x97, 0x84, 0xd9, 0x79, 0x06, 0x36, 0x50, 0x4e, 0x69, 0x26, 0x0c, 0x03, 0x9f, 0x5c, + 0x26, 0xd2, 0x18, 0xd5, 0xe7, 0x7d, 0x29, 0x72, 0x39, 0xb9, 0x0c, 0xbe, 0xc7, 0x1d, + 0x24, 0x48, 0x80, 0x30, 0x63, 0x8b, 0x4d, 0x9b, 0xf1, 0x32, 0x08, 0x93, 0x28, 0x02, + 0x0d, 0xc9, 0xdf, 0xd3, 0x45, 0x19, 0x27, 0x46, 0x68, 0x29, 0xe1, 0x05}, + {0x5a, 0x49, 0x9c, 0x2d, 0xb3, 0xee, 0x82, 0xba, 0x7c, 0xb9, 0x2b, 0xf1, 0xfc, 0xc8, + 0xef, 0xce, 0xe0, 0xd1, 0xb5, 0x93, 0xae, 0xab, 0x2d, 0xb0, 0x9b, 0x8d, 0x69, 0x13, + 0x9c, 0x0c, 0xc0, 0x39, 0x50, 0x45, 0x2c, 0x24, 0xc8, 0xbb, 0xbf, 0xad, 0xd9, 0x81, + 0x30, 0xd0, 0xec, 0x0c, 0xc8, 0xbc, 0x92, 0xdf, 0xc8, 0xf5, 0xa6, 0x66, 0x35, 0x84, + 0x4c, 0xce, 0x58, 0x82, 0xd3, 0x25, 0xcf, 0x78, 0x68, 0x9d, 0x48, 0x31, 0x8e, 0x6b, + 0xae, 0x15, 0x87, 0xf0, 0x2b, 0x9c, 0xab, 0x1c, 0x85, 0xaa, 0x05, 0xfa, 0x4e, 0xf0, + 0x97, 0x5a, 0xa7, 0xc9, 0x32, 0xf8, 0x3f, 0x6b, 0x07, 0x52, 0x6b, 0x00}, + {0x1c, 0x78, 0x95, 0x9d, 0xe1, 0xcf, 0xe0, 0x29, 0xe2, 0x10, 0x63, 0x96, 0x18, 0xdf, + 0x81, 0xb6, 0x39, 0x6b, 0x51, 0x70, 0xd3, 0x39, 0xdf, 0x57, 0x22, 0x61, 0xc7, 0x3b, + 0x44, 0xe3, 0x57, 0x4d, 0x2d, 0x08, 0xce, 0xb9, 0x16, 0x7e, 0xcb, 0xf5, 0x29, 0xbc, + 0x7a, 0x41, 0x4c, 0xf1, 0x07, 0x34, 0xab, 0xa7, 0xf4, 0x2b, 0xce, 0x6b, 0xb3, 0xd4, + 0xce, 0x75, 0x9f, 0x1a, 0x56, 0xe9, 0xe2, 0x7d, 0xcb, 0x5e, 0xa5, 0xb6, 0xf4, 0xd4, + 0x70, 0xde, 0x99, 0xdb, 0x85, 0x5d, 0x7f, 0x52, 0x01, 0x48, 0x81, 0x9a, 0xee, 0xd3, + 0x40, 0xc4, 0xc9, 0xdb, 0xed, 0x29, 0x60, 0x1a, 0xaf, 0x90, 0x2a, 0x6b}, + {0x97, 0x1e, 0xe6, 0x9a, 0xfc, 0xf4, 0x23, 0x69, 0xd1, 0x5f, 0x3f, 0xe0, 0x1d, 0x28, + 0x35, 0x57, 0x2d, 0xd1, 0xed, 0xe6, 0x43, 0xae, 0x64, 0xa7, 0x4a, 0x3e, 0x2d, 0xd1, + 0xe9, 0xf4, 0xd8, 0x5f, 0x0a, 0xd8, 0xb2, 0x5b, 0x24, 0xf3, 0xeb, 0x77, 0x9b, 0x07, + 0xb9, 0x2f, 0x47, 0x1b, 0x30, 0xd8, 0x33, 0x73, 0xee, 0x4c, 0xf2, 0xe6, 0x47, 0xc6, + 0x09, 0x21, 0x6c, 0x27, 0xc8, 0x12, 0x58, 0x46, 0xd9, 0x62, 0x10, 0x2a, 0xb2, 0xbe, + 0x43, 0x4d, 0x16, 0xdc, 0x31, 0x38, 0x75, 0xfb, 0x65, 0x70, 0xd7, 0x68, 0x29, 0xde, + 0x7b, 0x4a, 0x0d, 0x18, 0x90, 0x67, 0xb1, 0x1c, 0x2b, 0x2c, 0xb3, 0x05}, + {0xfd, 0xa8, 0x4d, 0xd2, 0xcc, 0x5e, 0xc0, 0xc8, 0x83, 0xef, 0xdf, 0x05, 0xac, 0x1a, + 0xcf, 0xa1, 0x61, 0xcd, 0xf9, 0x7d, 0xf2, 0xef, 0xbe, 0xdb, 0x99, 0x1e, 0x47, 0x7b, + 0xa3, 0x56, 0x55, 0x3b, 0x95, 0x81, 0xd5, 0x7a, 0x2c, 0xa4, 0xfc, 0xf7, 0xcc, 0xf3, + 0x33, 0x43, 0x6e, 0x28, 0x14, 0x32, 0x9d, 0x97, 0x0b, 0x34, 0x0d, 0x9d, 0xc2, 0xb6, + 0xe1, 0x07, 0x73, 0x56, 0x48, 0x1a, 0x77, 0x31, 0x82, 0xd4, 0x4d, 0xe1, 0x24, 0xc5, + 0xb0, 0x32, 0xb6, 0xa4, 0x2b, 0x1a, 0x54, 0x51, 0xb3, 0xed, 0xf3, 0x5a, 0x2b, 0x28, + 0x48, 0x60, 0xd1, 0xa3, 0xeb, 0x36, 0x73, 0x7a, 0xd2, 0x79, 0xc0, 0x4f}, + {0x7f, 0x2f, 0xbf, 0x89, 0xb0, 0x38, 0xc9, 0x51, 0xa7, 0xe9, 0xdf, 0x02, 0x65, 0xbd, + 0x97, 0x24, 0x53, 0xe4, 0x80, 0x78, 0x9c, 0xc0, 0xff, 0xff, 0x92, 0x8e, 0xf9, 0xca, + 0xce, 0x67, 0x45, 0x12, 0x0d, 0xc5, 0x86, 0x0c, 0x44, 0x8b, 0x34, 0xdc, 0x51, 0xe6, + 0x94, 0xcc, 0xc9, 0xcb, 0x37, 0x13, 0xb9, 0x3c, 0x3e, 0x64, 0x4d, 0xf7, 0x22, 0x64, + 0x08, 0xcd, 0xe3, 0xba, 0xc2, 0x70, 0x11, 0x24, 0xb4, 0x73, 0xc4, 0x0a, 0x86, 0xab, + 0xf9, 0x3f, 0x35, 0xe4, 0x13, 0x01, 0xee, 0x1d, 0x91, 0xf0, 0xaf, 0xc4, 0xc6, 0xeb, + 0x60, 0x50, 0xe7, 0x4a, 0x0d, 0x00, 0x87, 0x6c, 0x96, 0x12, 0x86, 0x3f}, + {0xde, 0x0d, 0x2a, 0x78, 0xc9, 0x0c, 0x9a, 0x55, 0x85, 0x83, 0x71, 0xea, 0xb2, 0xcd, + 0x1d, 0x55, 0x8c, 0x23, 0xef, 0x31, 0x5b, 0x86, 0x62, 0x7f, 0x3d, 0x61, 0x73, 0x79, + 0x76, 0xa7, 0x4a, 0x50, 0x13, 0x8d, 0x04, 0x36, 0xfa, 0xfc, 0x18, 0x9c, 0xdd, 0x9d, + 0x89, 0x73, 0xb3, 0x9d, 0x15, 0x29, 0xaa, 0xd0, 0x92, 0x9f, 0x0b, 0x35, 0x9f, 0xdc, + 0xd4, 0x19, 0x8a, 0x87, 0xee, 0x7e, 0xf5, 0x26, 0xb1, 0xef, 0x87, 0x56, 0xd5, 0x2c, + 0xab, 0x0c, 0x7b, 0xf1, 0x7a, 0x24, 0x62, 0xd1, 0x80, 0x51, 0x67, 0x24, 0x5a, 0x4f, + 0x34, 0x5a, 0xc1, 0x85, 0x69, 0x30, 0xba, 0x9d, 0x3d, 0x94, 0x41, 0x40}, + {0x96, 0xcc, 0xeb, 0x43, 0xba, 0xee, 0xc0, 0xc3, 0xaf, 0x9c, 0xea, 0x26, 0x9c, 0x9c, + 0x74, 0x8d, 0xc6, 0xcc, 0x77, 0x1c, 0xee, 0x95, 0xfa, 0xd9, 0x0f, 0x34, 0x84, 0x76, + 0xd9, 0xa1, 0x20, 0x14, 0xdd, 0xaa, 0x6c, 0xa2, 0x43, 0x77, 0x21, 0x4b, 0xce, 0xb7, + 0x8a, 0x64, 0x24, 0xb4, 0xa6, 0x47, 0xe3, 0xc9, 0xfb, 0x03, 0x7a, 0x4f, 0x1d, 0xcb, + 0x19, 0xd0, 0x00, 0x98, 0x42, 0x31, 0xd9, 0x12, 0x4f, 0x59, 0x37, 0xd3, 0x99, 0x77, + 0xc6, 0x00, 0x7b, 0xa4, 0x3a, 0xb2, 0x40, 0x51, 0x3c, 0x5e, 0x95, 0xf3, 0x5f, 0xe3, + 0x54, 0x28, 0x18, 0x44, 0x12, 0xa0, 0x59, 0x43, 0x31, 0x92, 0x4f, 0x1b}, + {0x51, 0x09, 0x15, 0x89, 0x9d, 0x10, 0x5c, 0x3e, 0x6a, 0x69, 0xe9, 0x2d, 0x91, 0xfa, + 0xce, 0x39, 0x20, 0x30, 0x5f, 0x97, 0x3f, 0xe4, 0xea, 0x20, 0xae, 0x2d, 0x13, 0x7f, + 0x2a, 0x57, 0x9b, 0x23, 0xb1, 0x66, 0x98, 0xa4, 0x30, 0x30, 0xcf, 0x33, 0x59, 0x48, + 0x5f, 0x21, 0xd2, 0x73, 0x1f, 0x25, 0xf6, 0xf4, 0xde, 0x51, 0x40, 0xaa, 0x82, 0xab, + 0xf6, 0x23, 0x9a, 0x6f, 0xd5, 0x91, 0xf1, 0x5f, 0x68, 0x90, 0x2d, 0xac, 0x33, 0xd4, + 0x9e, 0x81, 0x23, 0x85, 0xc9, 0x5f, 0x79, 0xab, 0x83, 0x28, 0x3d, 0xeb, 0x93, 0x55, + 0x80, 0x72, 0x45, 0xef, 0xcb, 0x36, 0x8f, 0x75, 0x6a, 0x52, 0x0c, 0x02}, + {0xbc, 0xdb, 0xd8, 0x9e, 0xf8, 0x34, 0x98, 0x77, 0x6c, 0xa4, 0x7c, 0xdc, 0xf9, 0xaa, + 0xf2, 0xc8, 0x74, 0xb0, 0xe1, 0xa3, 0xdc, 0x4c, 0x52, 0xa9, 0x77, 0x38, 0x31, 0x15, + 0x46, 0xcc, 0xaa, 0x02, 0x89, 0xcc, 0x42, 0xf0, 0x59, 0xef, 0x31, 0xe9, 0xb6, 0x4b, + 0x12, 0x8e, 0x9d, 0x9c, 0x58, 0x2c, 0x97, 0x59, 0xc7, 0xae, 0x8a, 0xe1, 0xc8, 0xad, + 0x0c, 0xc5, 0x02, 0x56, 0x0a, 0xfe, 0x2c, 0x45, 0xdf, 0x77, 0x78, 0x64, 0xa0, 0xf7, + 0xa0, 0x86, 0x9f, 0x7c, 0x60, 0x0e, 0x27, 0x64, 0xc4, 0xbb, 0xc9, 0x11, 0xfb, 0xf1, + 0x25, 0xea, 0x17, 0xab, 0x7b, 0x87, 0x4b, 0x30, 0x7b, 0x7d, 0xfb, 0x4c}, + {0xfe, 0x75, 0x9b, 0xb8, 0x6c, 0x3d, 0xb4, 0x72, 0x80, 0xdc, 0x6a, 0x9c, 0xd9, 0x94, + 0xc6, 0x54, 0x9f, 0x4c, 0xe3, 0x3e, 0x37, 0xaa, 0xc3, 0xb8, 0x64, 0x53, 0x07, 0x39, + 0x2b, 0x62, 0xb4, 0x14, 0x12, 0xef, 0x89, 0x97, 0xc2, 0x99, 0x86, 0xe2, 0x0d, 0x19, + 0x57, 0xdf, 0x71, 0xcd, 0x6e, 0x2b, 0xd0, 0x70, 0xc9, 0xec, 0x57, 0xc8, 0x43, 0xc3, + 0xc5, 0x3a, 0x4d, 0x43, 0xbc, 0x4c, 0x1d, 0x5b, 0x26, 0x9f, 0x0a, 0xcc, 0x15, 0x26, + 0xfb, 0xb6, 0xe5, 0xcc, 0x8d, 0xb8, 0x2b, 0x0e, 0x4f, 0x3a, 0x05, 0xa7, 0x69, 0x33, + 0x8b, 0x49, 0x01, 0x13, 0xd1, 0x2d, 0x59, 0x58, 0x12, 0xf7, 0x98, 0x2f}, + {0x56, 0x9e, 0x0f, 0xb5, 0x4c, 0xa7, 0x94, 0x0c, 0x20, 0x13, 0x8e, 0x8e, 0xa9, 0xf4, + 0x1f, 0x5b, 0x67, 0x0f, 0x30, 0x82, 0x21, 0xcc, 0x2a, 0x9a, 0xf9, 0xaa, 0x06, 0xd8, + 0x49, 0xe2, 0x6a, 0x3a, 0x01, 0xa7, 0x54, 0x4f, 0x44, 0xae, 0x12, 0x2e, 0xde, 0xd7, + 0xcb, 0xa9, 0xf0, 0x3e, 0xfe, 0xfc, 0xe0, 0x5d, 0x83, 0x75, 0x0d, 0x89, 0xbf, 0xce, + 0x54, 0x45, 0x61, 0xe7, 0xe9, 0x62, 0x80, 0x1d, 0x5a, 0x7c, 0x90, 0xa9, 0x85, 0xda, + 0x7a, 0x65, 0x62, 0x0f, 0xb9, 0x91, 0xb5, 0xa8, 0x0e, 0x1a, 0xe9, 0xb4, 0x34, 0xdf, + 0xfb, 0x1d, 0x0e, 0x8d, 0xf3, 0x5f, 0xf2, 0xae, 0xe8, 0x8c, 0x8b, 0x29}, + {0xb2, 0x0c, 0xf7, 0xef, 0x53, 0x79, 0x92, 0x2a, 0x76, 0x70, 0x15, 0x79, 0x2a, 0xc9, + 0x89, 0x4b, 0x6a, 0xcf, 0xa7, 0x30, 0x7a, 0x45, 0x18, 0x94, 0x85, 0xe4, 0x5c, 0x4d, + 0x40, 0xa8, 0xb8, 0x34, 0xde, 0x65, 0x21, 0x0a, 0xea, 0x72, 0x7a, 0x83, 0xf6, 0x79, + 0xcf, 0x0b, 0xb4, 0x07, 0xab, 0x3f, 0x70, 0xae, 0x38, 0x77, 0xc7, 0x36, 0x16, 0x52, + 0xdc, 0xd7, 0xa7, 0x03, 0x18, 0x27, 0xa6, 0x6b, 0x35, 0x33, 0x69, 0x83, 0xb5, 0xec, + 0x6e, 0xc2, 0xfd, 0xfe, 0xb5, 0x63, 0xdf, 0x13, 0xa8, 0xd5, 0x73, 0x25, 0xb2, 0xa4, + 0x9a, 0xaa, 0x93, 0xa2, 0x6a, 0x1c, 0x5e, 0x46, 0xdd, 0x2b, 0xd6, 0x71}, + {0x80, 0xdf, 0x78, 0xd3, 0x28, 0xcc, 0x33, 0x65, 0xb4, 0xa4, 0x0f, 0x0a, 0x79, 0x43, + 0xdb, 0xf6, 0x5a, 0xda, 0x01, 0xf7, 0xf9, 0x5f, 0x64, 0xe3, 0xa4, 0x2b, 0x17, 0xf3, + 0x17, 0xf3, 0xd5, 0x74, 0xf5, 0x5e, 0xf7, 0xb1, 0xda, 0xb5, 0x2d, 0xcd, 0xf5, 0x65, + 0xb0, 0x16, 0xcf, 0x95, 0x7f, 0xd7, 0x85, 0xf0, 0x49, 0x3f, 0xea, 0x1f, 0x57, 0x14, + 0x3d, 0x2b, 0x2b, 0x26, 0x21, 0x36, 0x33, 0x1c, 0x81, 0xca, 0xd9, 0x67, 0x54, 0xe5, + 0x6f, 0xa8, 0x37, 0x8c, 0x29, 0x2b, 0x75, 0x7c, 0x8b, 0x39, 0x3b, 0x62, 0xac, 0xe3, + 0x92, 0x08, 0x6d, 0xda, 0x8c, 0xd9, 0xe9, 0x47, 0x45, 0xcc, 0xeb, 0x4a}, + {0xc9, 0x01, 0x6d, 0x27, 0x1b, 0x07, 0xf0, 0x12, 0x70, 0x8c, 0xc4, 0x86, 0xc5, 0xba, + 0xb8, 0xe7, 0xa9, 0xfb, 0xd6, 0x71, 0x9b, 0x12, 0x08, 0x53, 0x92, 0xb7, 0x3d, 0x5a, + 0xf9, 0xfb, 0x88, 0x5d, 0x10, 0xb6, 0x54, 0x73, 0x9e, 0x8d, 0x40, 0x0b, 0x6e, 0x5b, + 0xa8, 0x5b, 0x53, 0x32, 0x6b, 0x80, 0x07, 0xa2, 0x58, 0x4a, 0x03, 0x3a, 0xe6, 0xdb, + 0x2c, 0xdf, 0xa1, 0xc9, 0xdd, 0xd9, 0x3b, 0x17, 0xdf, 0x72, 0x58, 0xfe, 0x1e, 0x0f, + 0x50, 0x2b, 0xc1, 0x18, 0x39, 0xd4, 0x2e, 0x58, 0xd6, 0x58, 0xe0, 0x3a, 0x67, 0xc9, + 0x8e, 0x27, 0xed, 0xe6, 0x19, 0xa3, 0x9e, 0xb1, 0x13, 0xcd, 0xe1, 0x06}, + {0x23, 0x6f, 0x16, 0x6f, 0x51, 0xad, 0xd0, 0x40, 0xbe, 0x6a, 0xab, 0x1f, 0x93, 0x32, + 0x8e, 0x11, 0x8e, 0x08, 0x4d, 0xa0, 0x14, 0x5e, 0xe3, 0x3f, 0x66, 0x62, 0xe1, 0x26, + 0x35, 0x60, 0x80, 0x30, 0x53, 0x03, 0x5b, 0x9e, 0x62, 0xaf, 0x2b, 0x47, 0x47, 0x04, + 0x8d, 0x27, 0x90, 0x0b, 0xaa, 0x3b, 0x27, 0xbf, 0x43, 0x96, 0x46, 0x5f, 0x78, 0x0c, + 0x13, 0x7b, 0x83, 0x8d, 0x1a, 0x6a, 0x3a, 0x7f, 0x0b, 0x80, 0x3d, 0x5d, 0x39, 0x44, + 0xe6, 0xf7, 0xf6, 0xed, 0x01, 0xc9, 0x55, 0xd5, 0xa8, 0x95, 0x39, 0x63, 0x2c, 0x59, + 0x30, 0x78, 0xcd, 0x68, 0x7e, 0x30, 0x51, 0x2e, 0xed, 0xfd, 0xd0, 0x30}, + {0xb3, 0x33, 0x12, 0xf2, 0x1a, 0x4d, 0x59, 0xe0, 0x9c, 0x4d, 0xcc, 0xf0, 0x8e, 0xe7, + 0xdb, 0x1b, 0x77, 0x9a, 0x49, 0x8f, 0x7f, 0x18, 0x65, 0x69, 0x68, 0x98, 0x09, 0x2c, + 0x20, 0x14, 0x92, 0x0a, 0x50, 0x47, 0xb8, 0x68, 0x1e, 0x97, 0xb4, 0x9c, 0xcf, 0xbb, + 0x64, 0x66, 0x29, 0x72, 0x95, 0xa0, 0x2b, 0x41, 0xfa, 0x72, 0x26, 0xe7, 0x8d, 0x5c, + 0xd9, 0x89, 0xc5, 0x51, 0x43, 0x08, 0x15, 0x46, 0x2e, 0xa0, 0xb9, 0xae, 0xc0, 0x19, + 0x90, 0xbc, 0xae, 0x4c, 0x03, 0x16, 0x0d, 0x11, 0xc7, 0x55, 0xec, 0x32, 0x99, 0x65, + 0x01, 0xf5, 0x6d, 0x0e, 0xfe, 0x5d, 0xca, 0x95, 0x28, 0x0d, 0xca, 0x3b}, + {0xa4, 0x62, 0x5d, 0x3c, 0xbc, 0x31, 0xf0, 0x40, 0x60, 0x7a, 0xf0, 0xcf, 0x3e, 0x8b, + 0xfc, 0x19, 0x45, 0xb5, 0x0f, 0x13, 0xa2, 0x3d, 0x18, 0x98, 0xcd, 0x13, 0x8f, 0xae, + 0xdd, 0xde, 0x31, 0x56, 0xbf, 0x01, 0xcc, 0x9e, 0xb6, 0x8e, 0x68, 0x9c, 0x6f, 0x89, + 0x44, 0xa6, 0xad, 0x83, 0xbc, 0xf0, 0xe2, 0x9f, 0x7a, 0x5f, 0x5f, 0x95, 0x2d, 0xca, + 0x41, 0x82, 0xf2, 0x8d, 0x03, 0xb4, 0xa8, 0x4e, 0x02, 0xd2, 0xca, 0xf1, 0x0a, 0x46, + 0xed, 0x2a, 0x83, 0xee, 0x8c, 0xa4, 0x05, 0x53, 0x30, 0x46, 0x5f, 0x1a, 0xf1, 0x49, + 0x45, 0x77, 0x21, 0x91, 0x63, 0xa4, 0x2c, 0x54, 0x30, 0x09, 0xce, 0x24}, + {0x06, 0xc1, 0x06, 0xfd, 0xf5, 0x90, 0xe8, 0x1f, 0xf2, 0x10, 0x88, 0x5d, 0x35, 0x68, + 0xc4, 0xb5, 0x3e, 0xaf, 0x8c, 0x6e, 0xfe, 0x08, 0x78, 0x82, 0x4b, 0xd7, 0x06, 0x8a, + 0xc2, 0xe3, 0xd4, 0x41, 0x85, 0x0b, 0xf3, 0xfd, 0x55, 0xa1, 0xcf, 0x3f, 0xa4, 0x2e, + 0x37, 0x36, 0x8e, 0x16, 0xf7, 0xd2, 0x44, 0xf8, 0x92, 0x64, 0xde, 0x64, 0xe0, 0xb2, + 0x80, 0x42, 0x4f, 0x32, 0xa7, 0x28, 0x99, 0x54, 0x2e, 0x1a, 0xee, 0x63, 0xa7, 0x32, + 0x6e, 0xf2, 0xea, 0xfd, 0x5f, 0xd2, 0xb7, 0xe4, 0x91, 0xae, 0x69, 0x4d, 0x7f, 0xd1, + 0x3b, 0xd3, 0x3b, 0xbc, 0x6a, 0xff, 0xdc, 0xc0, 0xde, 0x66, 0x1b, 0x49}, + {0xa7, 0x32, 0xea, 0xc7, 0x3d, 0xb1, 0xf5, 0x98, 0x98, 0xdb, 0x16, 0x7e, 0xcc, 0xf8, + 0xd5, 0xe3, 0x47, 0xd9, 0xf8, 0xcb, 0x52, 0xbf, 0x0a, 0xac, 0xac, 0xe4, 0x5e, 0xc8, + 0xd0, 0x38, 0xf3, 0x08, 0xa1, 0x64, 0xda, 0xd0, 0x8e, 0x4a, 0xf0, 0x75, 0x4b, 0x28, + 0xe2, 0x67, 0xaf, 0x2c, 0x22, 0xed, 0xa4, 0x7b, 0x7b, 0x1f, 0x79, 0xa3, 0x34, 0x82, + 0x67, 0x8b, 0x01, 0xb7, 0xb0, 0xb8, 0xf6, 0x4c, 0xbd, 0x73, 0x1a, 0x99, 0x21, 0xa8, + 0x83, 0xc3, 0x7a, 0x0c, 0x32, 0xdf, 0x01, 0xbc, 0x27, 0xab, 0x63, 0x70, 0x77, 0x84, + 0x1b, 0x33, 0x3d, 0xc1, 0x99, 0x8a, 0x07, 0xeb, 0x82, 0x4a, 0x0d, 0x53}, + {0x25, 0x48, 0xf9, 0xe1, 0x30, 0x36, 0x4c, 0x00, 0x5a, 0x53, 0xab, 0x8c, 0x26, 0x78, + 0x2d, 0x7e, 0x8b, 0xff, 0x84, 0xcc, 0x23, 0x23, 0x48, 0xc7, 0xb9, 0x70, 0x17, 0x10, + 0x3f, 0x75, 0xea, 0x65, 0x9e, 0xbf, 0x9a, 0x6c, 0x45, 0x73, 0x69, 0x6d, 0x80, 0xa8, + 0x00, 0x49, 0xfc, 0xb2, 0x7f, 0x25, 0x50, 0xb8, 0xcf, 0xc8, 0x12, 0xf4, 0xac, 0x2b, + 0x5b, 0xbd, 0xbf, 0x0c, 0xe0, 0xe7, 0xb3, 0x0d, 0x63, 0x63, 0x09, 0xe2, 0x3e, 0xfc, + 0x66, 0x3d, 0x6b, 0xcb, 0xb5, 0x61, 0x7f, 0x2c, 0xd6, 0x81, 0x1a, 0x3b, 0x44, 0x13, + 0x42, 0x04, 0xbe, 0x0f, 0xdb, 0xa1, 0xe1, 0x21, 0x19, 0xec, 0xa4, 0x02}, + {0xa2, 0xb8, 0x24, 0x3b, 0x9a, 0x25, 0xe6, 0x5c, 0xb8, 0xa0, 0xaf, 0x45, 0xcc, 0x7a, + 0x57, 0xb8, 0x37, 0x70, 0xa0, 0x8b, 0xe8, 0xe6, 0xcb, 0xcc, 0xbf, 0x09, 0x78, 0x12, + 0x51, 0x3c, 0x14, 0x3d, 0x5f, 0x79, 0xcf, 0xf1, 0x62, 0x61, 0xc8, 0xf5, 0xf2, 0x57, + 0xee, 0x26, 0x19, 0x86, 0x8c, 0x11, 0x78, 0x35, 0x06, 0x1c, 0x85, 0x24, 0x21, 0x17, + 0xcf, 0x7f, 0x06, 0xec, 0x5d, 0x2b, 0xd1, 0x36, 0x57, 0x45, 0x15, 0x79, 0x91, 0x27, + 0x6d, 0x12, 0x0a, 0x3a, 0x78, 0xfc, 0x5c, 0x8f, 0xe4, 0xd5, 0xac, 0x9b, 0x17, 0xdf, + 0xe8, 0xb6, 0xbd, 0x36, 0x59, 0x28, 0xa8, 0x5b, 0x88, 0x17, 0xf5, 0x2e}, + {0xdc, 0xae, 0x58, 0x8c, 0x4e, 0x97, 0x37, 0x46, 0xa4, 0x41, 0xf0, 0xab, 0xfb, 0x22, + 0xef, 0xb9, 0x8a, 0x71, 0x80, 0xe9, 0x56, 0xd9, 0x85, 0xe1, 0xa6, 0xa8, 0x43, 0xb1, + 0xfa, 0x78, 0x1b, 0x2f, 0x51, 0x2f, 0x5b, 0x30, 0xfb, 0xbf, 0xee, 0x96, 0xb8, 0x96, + 0x95, 0x88, 0xad, 0x38, 0xf9, 0xd3, 0x25, 0xdd, 0xd5, 0x46, 0xc7, 0x2d, 0xf5, 0xf0, + 0x95, 0x00, 0x3a, 0xbb, 0x90, 0x82, 0x96, 0x57, 0x01, 0xe1, 0x20, 0x0a, 0x43, 0xb8, + 0x1a, 0xf7, 0x47, 0xec, 0xf0, 0x24, 0x8d, 0x65, 0x93, 0xf3, 0xd1, 0xee, 0xe2, 0x6e, + 0xa8, 0x09, 0x75, 0xcf, 0xe1, 0xa3, 0x2a, 0xdc, 0x35, 0x3e, 0xc4, 0x7d}, + {0xc3, 0xd9, 0x7d, 0x88, 0x65, 0x66, 0x96, 0x85, 0x55, 0x53, 0xb0, 0x4b, 0x31, 0x9b, + 0x0f, 0xc9, 0xb1, 0x79, 0x20, 0xef, 0xf8, 0x8d, 0xe0, 0xc6, 0x2f, 0xc1, 0x8c, 0x75, + 0x16, 0x20, 0xf7, 0x7e, 0x18, 0x97, 0x3e, 0x27, 0x5c, 0x2a, 0x78, 0x5a, 0x94, 0xfd, + 0x4e, 0x5e, 0x99, 0xc6, 0x76, 0x35, 0x3e, 0x7d, 0x23, 0x1f, 0x05, 0xd8, 0x2e, 0x0f, + 0x99, 0x0a, 0xd5, 0x82, 0x1d, 0xb8, 0x4f, 0x04, 0xd9, 0xe3, 0x07, 0xa9, 0xc5, 0x18, + 0xdf, 0xc1, 0x59, 0x63, 0x4c, 0xce, 0x1d, 0x37, 0xb3, 0x57, 0x49, 0xbb, 0x01, 0xb2, + 0x34, 0x45, 0x70, 0xca, 0x2e, 0xdd, 0x30, 0x9c, 0x3f, 0x82, 0x79, 0x7f}, + {0xe8, 0x13, 0xb5, 0xa3, 0x39, 0xd2, 0x34, 0x83, 0xd8, 0xa8, 0x1f, 0xb9, 0xd4, 0x70, + 0x36, 0xc1, 0x33, 0xbd, 0x90, 0xf5, 0x36, 0x41, 0xb5, 0x12, 0xb4, 0xd9, 0x84, 0xd7, + 0x73, 0x03, 0x4e, 0x0a, 0xba, 0x87, 0xf5, 0x68, 0xf0, 0x1f, 0x9c, 0x6a, 0xde, 0xc8, + 0x50, 0x00, 0x4e, 0x89, 0x27, 0x08, 0xe7, 0x5b, 0xed, 0x7d, 0x55, 0x99, 0xbf, 0x3c, + 0xf0, 0xd6, 0x06, 0x1c, 0x43, 0xb0, 0xa9, 0x64, 0x19, 0x29, 0x7d, 0x5b, 0xa1, 0xd6, + 0xb3, 0x2e, 0x35, 0x82, 0x3a, 0xd5, 0xa0, 0xf6, 0xb4, 0xb0, 0x47, 0x5d, 0xa4, 0x89, + 0x43, 0xce, 0x56, 0x71, 0x6c, 0x34, 0x18, 0xce, 0x0a, 0x7d, 0x1a, 0x07}, + {0x0b, 0xba, 0x87, 0xc8, 0xaa, 0x2d, 0x07, 0xd3, 0xee, 0x62, 0xa5, 0xbf, 0x05, 0x29, + 0x26, 0x01, 0x8b, 0x76, 0xef, 0xc0, 0x02, 0x30, 0x54, 0xcf, 0x9c, 0x7e, 0xea, 0x46, + 0x71, 0xcc, 0x3b, 0x2c, 0x31, 0x44, 0xe1, 0x20, 0x52, 0x35, 0x0c, 0xcc, 0x41, 0x51, + 0xb1, 0x09, 0x07, 0x95, 0x65, 0x0d, 0x36, 0x5f, 0x9d, 0x20, 0x1b, 0x62, 0xf5, 0x9a, + 0xd3, 0x55, 0x77, 0x61, 0xf7, 0xbc, 0x69, 0x7c, 0x5f, 0x29, 0xe8, 0x04, 0xeb, 0xd7, + 0xf0, 0x07, 0x7d, 0xf3, 0x50, 0x2f, 0x25, 0x18, 0xdb, 0x10, 0xd7, 0x98, 0x17, 0x17, + 0xa3, 0xa9, 0x51, 0xe9, 0x1d, 0xa5, 0xac, 0x22, 0x73, 0x9a, 0x5a, 0x6f}, + {0xc5, 0xc6, 0x41, 0x2f, 0x0c, 0x00, 0xa1, 0x8b, 0x9b, 0xfb, 0xfe, 0x0c, 0xc1, 0x79, + 0x9f, 0xc4, 0x9f, 0x1c, 0xc5, 0x3c, 0x70, 0x47, 0xfa, 0x4e, 0xca, 0xaf, 0x47, 0xe1, + 0xa2, 0x21, 0x4e, 0x49, 0xbe, 0x44, 0xd9, 0xa3, 0xeb, 0xd4, 0x29, 0xe7, 0x9e, 0xaf, + 0x78, 0x80, 0x40, 0x09, 0x9e, 0x8d, 0x03, 0x9c, 0x86, 0x47, 0x7a, 0x56, 0x25, 0x45, + 0x24, 0x3b, 0x8d, 0xee, 0x80, 0x96, 0xab, 0x02, 0x9a, 0x0d, 0xe5, 0xdd, 0x85, 0x8a, + 0xa4, 0xef, 0x49, 0xa2, 0xb9, 0x0f, 0x4e, 0x22, 0x9a, 0x21, 0xd9, 0xf6, 0x1e, 0xd9, + 0x1d, 0x1f, 0x09, 0xfa, 0x34, 0xbb, 0x46, 0xea, 0xcb, 0x76, 0x5d, 0x6b}, + {0x94, 0xd9, 0x0c, 0xec, 0x6c, 0x55, 0x57, 0x88, 0xba, 0x1d, 0xd0, 0x5c, 0x6f, 0xdc, + 0x72, 0x64, 0x77, 0xb4, 0x42, 0x8f, 0x14, 0x69, 0x01, 0xaf, 0x54, 0x73, 0x27, 0x85, + 0xf6, 0x33, 0xe3, 0x0a, 0x22, 0x25, 0x78, 0x1e, 0x17, 0x41, 0xf9, 0xe0, 0xd3, 0x36, + 0x69, 0x03, 0x74, 0xae, 0xe6, 0xf1, 0x46, 0xc7, 0xfc, 0xd0, 0xa2, 0x3e, 0x8b, 0x40, + 0x3e, 0x31, 0xdd, 0x03, 0x9c, 0x86, 0xfb, 0x16, 0x62, 0x09, 0xb6, 0x33, 0x97, 0x19, + 0x8e, 0x28, 0x33, 0xe1, 0xab, 0xd8, 0xb4, 0x72, 0xfc, 0x24, 0x3e, 0xd0, 0x91, 0x09, + 0xed, 0xf7, 0x11, 0x48, 0x75, 0xd0, 0x70, 0x8f, 0x8b, 0xe3, 0x81, 0x3f}, + {0xfe, 0xaf, 0xd9, 0x7e, 0xcc, 0x0f, 0x91, 0x7f, 0x4b, 0x87, 0x65, 0x24, 0xa1, 0xb8, + 0x5c, 0x54, 0x04, 0x47, 0x0c, 0x4b, 0xd2, 0x7e, 0x39, 0xa8, 0x93, 0x09, 0xf5, 0x04, + 0xc1, 0x0f, 0x51, 0x50, 0x24, 0xc8, 0x17, 0x5f, 0x35, 0x7f, 0xdb, 0x0a, 0xa4, 0x99, + 0x42, 0xd7, 0xc3, 0x23, 0xb9, 0x74, 0xf7, 0xea, 0xf8, 0xcb, 0x8b, 0x3e, 0x7c, 0xd5, + 0x3d, 0xdc, 0xde, 0x4c, 0xd3, 0xe2, 0xd3, 0x0a, 0x9d, 0x24, 0x6e, 0x33, 0xc5, 0x0f, + 0x0c, 0x6f, 0xd9, 0xcf, 0x31, 0xc3, 0x19, 0xde, 0x5e, 0x74, 0x1c, 0xfe, 0xee, 0x09, + 0x00, 0xfd, 0xd6, 0xf2, 0xbe, 0x1e, 0xfa, 0xf0, 0x8b, 0x15, 0x7c, 0x12}, + {0xa2, 0x79, 0x98, 0x2e, 0x42, 0x7c, 0x19, 0xf6, 0x47, 0x36, 0xca, 0x52, 0xd4, 0xdd, + 0x4a, 0xa4, 0xcb, 0xac, 0x4e, 0x4b, 0xc1, 0x3f, 0x41, 0x9b, 0x68, 0x4f, 0xef, 0x07, + 0x7d, 0xf8, 0x4e, 0x35, 0x74, 0xb9, 0x51, 0xae, 0xc4, 0x8f, 0xa2, 0xde, 0x96, 0xfe, + 0x4d, 0x74, 0xd3, 0x73, 0x99, 0x1d, 0xa8, 0x48, 0x38, 0x87, 0x0b, 0x68, 0x40, 0x62, + 0x95, 0xdf, 0x67, 0xd1, 0x79, 0x24, 0xd8, 0x4e, 0x75, 0xd9, 0xc5, 0x60, 0x22, 0xb5, + 0xe3, 0xfe, 0xb8, 0xb0, 0x41, 0xeb, 0xfc, 0x2e, 0x35, 0x50, 0x3c, 0x65, 0xf6, 0xa9, + 0x30, 0xac, 0x08, 0x88, 0x6d, 0x23, 0x39, 0x05, 0xd2, 0x92, 0x2d, 0x30}, + {0x3d, 0x28, 0xa4, 0xbc, 0xa2, 0xc1, 0x13, 0x78, 0xd9, 0x3d, 0x86, 0xa1, 0x91, 0xf0, + 0x62, 0xed, 0x86, 0xfa, 0x68, 0xc2, 0xb8, 0xbc, 0xc7, 0xae, 0x4c, 0xae, 0x1c, 0x6f, + 0xb7, 0xd3, 0xe5, 0x10, 0x77, 0xf1, 0xe0, 0xe4, 0xb6, 0x6f, 0xbc, 0x2d, 0x93, 0x6a, + 0xbd, 0xa4, 0x29, 0xbf, 0xe1, 0x04, 0xe8, 0xf6, 0x7a, 0x78, 0xd4, 0x66, 0x19, 0x5e, + 0x60, 0xd0, 0x26, 0xb4, 0x5e, 0x5f, 0xdc, 0x0e, 0x67, 0x8e, 0xda, 0x53, 0xd6, 0xbf, + 0x53, 0x54, 0x41, 0xf6, 0xa9, 0x24, 0xec, 0x1e, 0xdc, 0xe9, 0x23, 0x8a, 0x57, 0x03, + 0x3b, 0x26, 0x87, 0xbf, 0x72, 0xba, 0x1c, 0x36, 0x51, 0x6c, 0xb4, 0x45}, + {0xa1, 0x7f, 0x4f, 0x31, 0xbf, 0x2a, 0x40, 0xa9, 0x50, 0xf4, 0x8c, 0x8e, 0xdc, 0xf1, + 0x57, 0xe2, 0x84, 0xbe, 0xa8, 0x23, 0x4b, 0xd5, 0xbb, 0x1d, 0x3b, 0x71, 0xcb, 0x6d, + 0xa3, 0xbf, 0x77, 0x21, 0xe4, 0xe3, 0x7f, 0x8a, 0xdd, 0x4d, 0x9d, 0xce, 0x30, 0x0e, + 0x62, 0x76, 0x56, 0x64, 0x13, 0xab, 0x58, 0x99, 0x0e, 0xb3, 0x7b, 0x4f, 0x59, 0x4b, + 0xdf, 0x29, 0x12, 0x32, 0xef, 0x0a, 0x1c, 0x5c, 0x8f, 0xdb, 0x79, 0xfa, 0xbc, 0x1b, + 0x08, 0x37, 0xb3, 0x59, 0x5f, 0xc2, 0x1e, 0x81, 0x48, 0x60, 0x87, 0x24, 0x83, 0x9c, + 0x65, 0x76, 0x7a, 0x08, 0xbb, 0xb5, 0x8a, 0x7d, 0x38, 0x19, 0xe6, 0x4a}, + {0x2e, 0xa3, 0x44, 0x53, 0xaa, 0xf6, 0xdb, 0x8d, 0x78, 0x40, 0x1b, 0xb4, 0xb4, 0xea, + 0x88, 0x7d, 0x60, 0x0d, 0x13, 0x4a, 0x97, 0xeb, 0xb0, 0x5e, 0x03, 0x3e, 0xbf, 0x17, + 0x1b, 0xd9, 0x00, 0x1a, 0x83, 0xfb, 0x5b, 0x98, 0x44, 0x7e, 0x11, 0x61, 0x36, 0x31, + 0x96, 0x71, 0x2a, 0x46, 0xe0, 0xfc, 0x4b, 0x90, 0x25, 0xd4, 0x48, 0x34, 0xac, 0x83, + 0x64, 0x3d, 0xa4, 0x5b, 0xbe, 0x5a, 0x68, 0x75, 0xb2, 0xf2, 0x61, 0xeb, 0x33, 0x09, + 0x96, 0x6e, 0x52, 0x49, 0xff, 0xc9, 0xa8, 0x0f, 0x3d, 0x54, 0x69, 0x65, 0xf6, 0x7a, + 0x10, 0x75, 0x72, 0xdf, 0xaa, 0xe6, 0xb0, 0x23, 0xb6, 0x29, 0x55, 0x13}, + {0x18, 0xd5, 0xd1, 0xad, 0xd7, 0xdb, 0xf0, 0x18, 0x11, 0x1f, 0xc1, 0xcf, 0x88, 0x78, + 0x9f, 0x97, 0x9b, 0x75, 0x14, 0x71, 0xf0, 0xe1, 0x32, 0x87, 0x01, 0x3a, 0xca, 0x65, + 0x1a, 0xb8, 0xb5, 0x79, 0xfe, 0x83, 0x2e, 0xe2, 0xbc, 0x16, 0xc7, 0xf5, 0xc1, 0x85, + 0x09, 0xe8, 0x19, 0xeb, 0x2b, 0xb4, 0xae, 0x4a, 0x25, 0x14, 0x37, 0xa6, 0x9d, 0xec, + 0x13, 0xa6, 0x90, 0x15, 0x05, 0xea, 0x72, 0x59, 0x11, 0x78, 0x8f, 0xdc, 0x20, 0xac, + 0xd4, 0x0f, 0xa8, 0x4f, 0x4d, 0xac, 0x94, 0xd2, 0x9a, 0x9a, 0x34, 0x04, 0x36, 0xb3, + 0x64, 0x2d, 0x1b, 0xc0, 0xdb, 0x3b, 0x5f, 0x90, 0x95, 0x9c, 0x7e, 0x4f}, + {0x2e, 0x30, 0x81, 0x57, 0xbc, 0x4b, 0x67, 0x62, 0x0f, 0xdc, 0xad, 0x89, 0x39, 0x0f, + 0x52, 0xd8, 0xc6, 0xd9, 0xfb, 0x53, 0xae, 0x99, 0x29, 0x8c, 0x4c, 0x8e, 0x63, 0x2e, + 0xd9, 0x3a, 0x99, 0x31, 0xfe, 0x99, 0x52, 0x35, 0x3d, 0x44, 0xc8, 0x71, 0xd7, 0xea, + 0xeb, 0xdb, 0x1c, 0x3b, 0xcd, 0x8b, 0x66, 0x94, 0xa4, 0xf1, 0x9e, 0x49, 0x92, 0x80, + 0xc8, 0xad, 0x44, 0xa1, 0xc4, 0xee, 0x42, 0x19, 0x92, 0x49, 0x23, 0xae, 0x19, 0x53, + 0xac, 0x7d, 0x92, 0x3e, 0xea, 0x0c, 0x91, 0x3d, 0x1b, 0x2c, 0x22, 0x11, 0x3c, 0x25, + 0x94, 0xe4, 0x3c, 0x55, 0x75, 0xca, 0xf9, 0x4e, 0x31, 0x65, 0x0a, 0x2a}, + {0xc2, 0x27, 0xf9, 0xf7, 0x7f, 0x93, 0xb7, 0x2d, 0x35, 0xa6, 0xd0, 0x17, 0x06, 0x1f, + 0x74, 0xdb, 0x76, 0xaf, 0x55, 0x11, 0xa2, 0xf3, 0x82, 0x59, 0xed, 0x2d, 0x7c, 0x64, + 0x18, 0xe2, 0xf6, 0x4c, 0x3a, 0x79, 0x1c, 0x3c, 0xcd, 0x1a, 0x36, 0xcf, 0x3b, 0xbc, + 0x35, 0x5a, 0xac, 0xbc, 0x9e, 0x2f, 0xab, 0xa6, 0xcd, 0xa8, 0xe9, 0x60, 0xe8, 0x60, + 0x13, 0x1a, 0xea, 0x6d, 0x9b, 0xc3, 0x5d, 0x05, 0xb6, 0x5b, 0x8d, 0xc2, 0x7c, 0x22, + 0x19, 0xb1, 0xab, 0xff, 0x4d, 0x77, 0xbc, 0x4e, 0xe2, 0x07, 0x89, 0x2c, 0xa3, 0xe4, + 0xce, 0x78, 0x3c, 0xa8, 0xb6, 0x24, 0xaa, 0x10, 0x77, 0x30, 0x1a, 0x12}, + {0x97, 0x4a, 0x03, 0x9f, 0x5e, 0x5d, 0xdb, 0xe4, 0x2d, 0xbc, 0x34, 0x30, 0x09, 0xfc, + 0x53, 0xe1, 0xb1, 0xd3, 0x51, 0x95, 0x91, 0x46, 0x05, 0x46, 0x2d, 0xe5, 0x40, 0x7a, + 0x6c, 0xc7, 0x3f, 0x33, 0xc9, 0x83, 0x74, 0xc7, 0x3e, 0x71, 0x59, 0xd6, 0xaf, 0x96, + 0x2b, 0xb8, 0x77, 0xe0, 0xbf, 0x88, 0xd3, 0xbc, 0x97, 0x10, 0x23, 0x28, 0x9e, 0x28, + 0x9b, 0x3a, 0xed, 0x6c, 0x4a, 0xb9, 0x7b, 0x52, 0x2e, 0x48, 0x5b, 0x99, 0x2a, 0x99, + 0x3d, 0x56, 0x01, 0x38, 0x38, 0x6e, 0x7c, 0xd0, 0x05, 0x34, 0xe5, 0xd8, 0x64, 0x2f, + 0xde, 0x35, 0x50, 0x48, 0xf7, 0xa9, 0xa7, 0x20, 0x9b, 0x06, 0x89, 0x6b}, + {0x0d, 0x22, 0x70, 0x62, 0x41, 0xa0, 0x2a, 0x81, 0x4e, 0x5b, 0x24, 0xf9, 0xfa, 0x89, + 0x5a, 0x99, 0x05, 0xef, 0x72, 0x50, 0xce, 0xc4, 0xad, 0xff, 0x73, 0xeb, 0x73, 0xaa, + 0x03, 0x21, 0xbc, 0x23, 0x77, 0xdb, 0xc7, 0xb5, 0x8c, 0xfa, 0x82, 0x40, 0x55, 0xc1, + 0x34, 0xc7, 0xf8, 0x86, 0x86, 0x06, 0x7e, 0xa5, 0xe7, 0xf6, 0xd9, 0xc8, 0xe6, 0x29, + 0xcf, 0x9b, 0x63, 0xa7, 0x08, 0xd3, 0x73, 0x04, 0x05, 0x9e, 0x58, 0x03, 0x26, 0x79, + 0xee, 0xca, 0x92, 0xc4, 0xdc, 0x46, 0x12, 0x42, 0x4b, 0x2b, 0x4f, 0xa9, 0x01, 0xe6, + 0x74, 0xef, 0xa1, 0x02, 0x1a, 0x34, 0x04, 0xde, 0xbf, 0x73, 0x2f, 0x10}, + {0xc6, 0x45, 0x57, 0x7f, 0xab, 0xb9, 0x18, 0xeb, 0x90, 0xc6, 0x87, 0x57, 0xee, 0x8a, + 0x3a, 0x02, 0xa9, 0xaf, 0xf7, 0x2d, 0xda, 0x12, 0x27, 0xb7, 0x3d, 0x01, 0x5c, 0xea, + 0x25, 0x7d, 0x59, 0x36, 0x9a, 0x1c, 0x51, 0xb5, 0xe0, 0xda, 0xb4, 0xa2, 0x06, 0xff, + 0xff, 0x2b, 0x29, 0x60, 0xc8, 0x7a, 0x34, 0x42, 0x50, 0xf5, 0x5d, 0x37, 0x1f, 0x98, + 0x2d, 0xa1, 0x4e, 0xda, 0x25, 0xd7, 0x6b, 0x3f, 0xac, 0x58, 0x60, 0x10, 0x7b, 0x8d, + 0x4d, 0x73, 0x5f, 0x90, 0xc6, 0x6f, 0x9e, 0x57, 0x40, 0xd9, 0x2d, 0x93, 0x02, 0x92, + 0xf9, 0xf8, 0x66, 0x64, 0xd0, 0xd6, 0x60, 0xda, 0x19, 0xcc, 0x7e, 0x7b}, + {0x0d, 0x69, 0x5c, 0x69, 0x3c, 0x37, 0xc2, 0x78, 0x6e, 0x90, 0x42, 0x06, 0x66, 0x2e, + 0x25, 0xdd, 0xd2, 0x2b, 0xe1, 0x4a, 0x44, 0x44, 0x1d, 0x95, 0x56, 0x39, 0x74, 0x01, + 0x76, 0xad, 0x35, 0x42, 0x9b, 0xfa, 0x7c, 0xa7, 0x51, 0x4a, 0xae, 0x6d, 0x50, 0x86, + 0xa3, 0xe7, 0x54, 0x36, 0x26, 0x82, 0xdb, 0x82, 0x2d, 0x8f, 0xcd, 0xff, 0xbb, 0x09, + 0xba, 0xca, 0xf5, 0x1b, 0x66, 0xdc, 0xbe, 0x03, 0xf5, 0x75, 0x89, 0x07, 0x0d, 0xcb, + 0x58, 0x62, 0x98, 0xf2, 0x89, 0x91, 0x54, 0x42, 0x29, 0x49, 0xe4, 0x6e, 0xe3, 0xe2, + 0x23, 0xb4, 0xca, 0xa0, 0xa1, 0x66, 0xf0, 0xcd, 0xb0, 0xe2, 0x7c, 0x0e}, + {0xa3, 0x85, 0x8c, 0xc4, 0x3a, 0x64, 0x94, 0xc4, 0xad, 0x39, 0x61, 0x3c, 0xf4, 0x1d, + 0x36, 0xfd, 0x48, 0x4d, 0xe9, 0x3a, 0xdd, 0x17, 0xdb, 0x09, 0x4a, 0x67, 0xb4, 0x8f, + 0x5d, 0x0a, 0x6e, 0x66, 0xf9, 0x70, 0x4b, 0xd9, 0xdf, 0xfe, 0xa6, 0xfe, 0x2d, 0xba, + 0xfc, 0xc1, 0x51, 0xc0, 0x30, 0xf1, 0x89, 0xab, 0x2f, 0x7f, 0x7e, 0xd4, 0x82, 0x48, + 0xb5, 0xee, 0xec, 0x8a, 0x13, 0x56, 0x52, 0x61, 0x0d, 0xcb, 0x70, 0x48, 0x4e, 0xf6, + 0xbb, 0x2a, 0x6b, 0x8b, 0x45, 0xaa, 0xf0, 0xbc, 0x65, 0xcd, 0x5d, 0x98, 0xe8, 0x75, + 0xba, 0x4e, 0xbe, 0x9a, 0xe4, 0xde, 0x14, 0xd5, 0x10, 0xc8, 0x0b, 0x7f}, + {0x6f, 0x13, 0xf4, 0x26, 0xa4, 0x6b, 0x00, 0xb9, 0x35, 0x30, 0xe0, 0x57, 0x9e, 0x36, + 0x67, 0x8d, 0x28, 0x3c, 0x46, 0x4f, 0xd9, 0xdf, 0xc8, 0xcb, 0xf5, 0xdb, 0xee, 0xf8, + 0xbc, 0x8d, 0x1f, 0x0d, 0xa0, 0x13, 0x72, 0x73, 0xad, 0x9d, 0xac, 0x83, 0x98, 0x2e, + 0xf7, 0x2e, 0xba, 0xf8, 0xf6, 0x9f, 0x57, 0x69, 0xec, 0x43, 0xdd, 0x2e, 0x1e, 0x31, + 0x75, 0xab, 0xc5, 0xde, 0x7d, 0x90, 0x3a, 0x1d, 0xdc, 0x81, 0xd0, 0x3e, 0x31, 0x93, + 0x16, 0xba, 0x80, 0x34, 0x1b, 0x85, 0xad, 0x9f, 0x32, 0x29, 0xcb, 0x21, 0x03, 0x03, + 0x3c, 0x01, 0x28, 0x01, 0xe3, 0xfd, 0x1b, 0xa3, 0x44, 0x1b, 0x01, 0x00}, + {0x0c, 0x6c, 0xc6, 0x3f, 0x6c, 0xa0, 0xdf, 0x3f, 0xd2, 0x0d, 0xd6, 0x4d, 0x8e, 0xe3, + 0x40, 0x5d, 0x71, 0x4d, 0x8e, 0x26, 0x38, 0x8b, 0xe3, 0x7a, 0xe1, 0x57, 0x83, 0x6e, + 0x91, 0x8d, 0xc4, 0x3a, 0x5c, 0xa7, 0x0a, 0x6a, 0x69, 0x1f, 0x56, 0x16, 0x6a, 0xbd, + 0x52, 0x58, 0x5c, 0x72, 0xbf, 0xc1, 0xad, 0x66, 0x79, 0x9a, 0x7f, 0xdd, 0xa8, 0x11, + 0x26, 0x10, 0x85, 0xd2, 0xa2, 0x88, 0xd9, 0x63, 0x2e, 0x23, 0xbd, 0xaf, 0x53, 0x07, + 0x12, 0x00, 0x83, 0xf6, 0xd8, 0xfd, 0xb8, 0xce, 0x2b, 0xe9, 0x91, 0x2b, 0xe7, 0x84, + 0xb3, 0x69, 0x16, 0xf8, 0x66, 0xa0, 0x68, 0x23, 0x2b, 0xd5, 0xfa, 0x33}, + {0x16, 0x1e, 0xe4, 0xc5, 0xc6, 0x49, 0x06, 0x54, 0x35, 0x77, 0x3f, 0x33, 0x30, 0x64, + 0xf8, 0x0a, 0x46, 0xe7, 0x05, 0xf3, 0xd2, 0xfc, 0xac, 0xb2, 0xa7, 0xdc, 0x56, 0xa2, + 0x29, 0xf4, 0xc0, 0x16, 0xe8, 0xcf, 0x22, 0xc4, 0xd0, 0xc8, 0x2c, 0x8d, 0xcb, 0x3a, + 0xa1, 0x05, 0x7b, 0x4f, 0x2b, 0x07, 0x6f, 0xa5, 0xf6, 0xec, 0xe6, 0xb6, 0xfe, 0xa3, + 0xe2, 0x71, 0x0a, 0xb9, 0xcc, 0x55, 0xc3, 0x3c, 0x31, 0x91, 0x3e, 0x90, 0x43, 0x94, + 0xb6, 0xe9, 0xce, 0x37, 0x56, 0x7a, 0xcb, 0x94, 0xa4, 0xb8, 0x44, 0x92, 0xba, 0xba, + 0xa4, 0xd1, 0x7c, 0xc8, 0x68, 0x75, 0xae, 0x6b, 0x42, 0xaf, 0x1e, 0x63}, + {0x9f, 0xfe, 0x66, 0xda, 0x10, 0x04, 0xe9, 0xb3, 0xa6, 0xe5, 0x16, 0x6c, 0x52, 0x4b, + 0xdd, 0x85, 0x83, 0xbf, 0xf9, 0x1e, 0x61, 0x97, 0x3d, 0xbc, 0xb5, 0x19, 0xa9, 0x1e, + 0x8b, 0x64, 0x99, 0x55, 0xe8, 0x0d, 0x70, 0xa3, 0xb9, 0x75, 0xd9, 0x47, 0x52, 0x05, + 0xf8, 0xe2, 0xfb, 0xc5, 0x80, 0x72, 0xe1, 0x5d, 0xe4, 0x32, 0x27, 0x8f, 0x65, 0x53, + 0xb5, 0x80, 0x5f, 0x66, 0x7f, 0x2c, 0x1f, 0x43, 0x19, 0x7b, 0x8f, 0x85, 0x44, 0x63, + 0x02, 0xd6, 0x4a, 0x51, 0xea, 0xa1, 0x2f, 0x35, 0xab, 0x14, 0xd7, 0xa9, 0x90, 0x20, + 0x1a, 0x44, 0x00, 0x89, 0x26, 0x3b, 0x25, 0x91, 0x5f, 0x71, 0x04, 0x7b}, + {0x43, 0xae, 0xf6, 0xac, 0x28, 0xbd, 0xed, 0x83, 0xb4, 0x7a, 0x5c, 0x7d, 0x8b, 0x7c, + 0x35, 0x86, 0x44, 0x2c, 0xeb, 0xb7, 0x69, 0x47, 0x40, 0xc0, 0x3f, 0x58, 0xf6, 0xc2, + 0xf5, 0x7b, 0xb3, 0x59, 0xc6, 0xba, 0xe6, 0xc4, 0x80, 0xc2, 0x76, 0xb3, 0x0b, 0x9b, + 0x1d, 0x6d, 0xdd, 0xd3, 0x0e, 0x97, 0x44, 0xf9, 0x0b, 0x45, 0x58, 0x95, 0x9a, 0xb0, + 0x23, 0xe2, 0xcd, 0x57, 0xfa, 0xac, 0xd0, 0x48, 0x71, 0xe6, 0xab, 0x7d, 0xe4, 0x26, + 0x0f, 0xb6, 0x37, 0x3a, 0x2f, 0x62, 0x97, 0xa1, 0xd1, 0xf1, 0x94, 0x03, 0x96, 0xe9, + 0x7e, 0xce, 0x08, 0x42, 0xdb, 0x3b, 0x6d, 0x33, 0x91, 0x41, 0x23, 0x16}, + {0xf6, 0x7f, 0x26, 0xf6, 0xde, 0x99, 0xe4, 0xb9, 0x43, 0x08, 0x2c, 0x74, 0x7b, 0xca, + 0x72, 0x77, 0xb1, 0xf2, 0xa4, 0xe9, 0x3f, 0x15, 0xa0, 0x23, 0x06, 0x50, 0xd0, 0xd5, + 0xec, 0xdf, 0xdf, 0x2c, 0x40, 0x86, 0xf3, 0x1f, 0xd6, 0x9c, 0x49, 0xdd, 0xa0, 0x25, + 0x36, 0x06, 0xc3, 0x9b, 0xcd, 0x29, 0xc3, 0x3d, 0xd7, 0x3d, 0x02, 0xd8, 0xe2, 0x51, + 0x31, 0x92, 0x3b, 0x20, 0x7a, 0x70, 0x25, 0x4a, 0x6a, 0xed, 0xf6, 0x53, 0x8a, 0x66, + 0xb7, 0x2a, 0xa1, 0x70, 0xd1, 0x1d, 0x58, 0x42, 0x42, 0x30, 0x61, 0x01, 0xe2, 0x3a, + 0x4c, 0x14, 0x00, 0x40, 0xfc, 0x49, 0x8e, 0x24, 0x6d, 0x89, 0x21, 0x57}, + {0xae, 0x1b, 0x18, 0xfd, 0x17, 0x55, 0x6e, 0x0b, 0xb4, 0x63, 0xb9, 0x2b, 0x9f, 0x62, + 0x22, 0x90, 0x25, 0x46, 0x06, 0x32, 0xe9, 0xbc, 0x09, 0x55, 0xda, 0x13, 0x3c, 0xf6, + 0x74, 0xdd, 0x8e, 0x57, 0x4e, 0xda, 0xd0, 0xa1, 0x91, 0x50, 0x5d, 0x28, 0x08, 0x3e, + 0xfe, 0xb5, 0xa7, 0x6f, 0xaa, 0x4b, 0xb3, 0x93, 0x93, 0xe1, 0x7c, 0x17, 0xe5, 0x63, + 0xfd, 0x30, 0xb0, 0xc4, 0xaf, 0x35, 0xc9, 0x03, 0x3d, 0x0c, 0x2b, 0x49, 0xc6, 0x76, + 0x72, 0x99, 0xfc, 0x05, 0xe2, 0xdf, 0xc4, 0xc2, 0xcc, 0x47, 0x3c, 0x3a, 0x62, 0xdd, + 0x84, 0x9b, 0xd2, 0xdc, 0xa2, 0xc7, 0x88, 0x02, 0x59, 0xab, 0xc2, 0x3e}, + {0xb9, 0x7b, 0xd8, 0xe4, 0x7b, 0xd2, 0xa0, 0xa1, 0xed, 0x1a, 0x39, 0x61, 0xeb, 0x4d, + 0x8b, 0xa9, 0x83, 0x9b, 0xcb, 0x73, 0xd0, 0xdd, 0xa0, 0x99, 0xce, 0xca, 0x0f, 0x20, + 0x5a, 0xc2, 0xd5, 0x2d, 0xcb, 0xd1, 0x32, 0xae, 0x09, 0x3a, 0x21, 0xa7, 0xd5, 0xc2, + 0xf5, 0x40, 0xdf, 0x87, 0x2b, 0x0f, 0x29, 0xab, 0x1e, 0xe8, 0xc6, 0xa4, 0xae, 0x0b, + 0x5e, 0xac, 0xdb, 0x6a, 0x6c, 0xf6, 0x1b, 0x0e, 0x7e, 0x88, 0x2c, 0x79, 0xe9, 0xd5, + 0xab, 0xe2, 0x5d, 0x6d, 0x92, 0xcb, 0x18, 0x00, 0x02, 0x1a, 0x1e, 0x5f, 0xae, 0xba, + 0xcd, 0x69, 0xba, 0xbf, 0x5f, 0x8f, 0xe8, 0x5a, 0xb3, 0x48, 0x05, 0x73}, + {0xee, 0xb8, 0xa8, 0xcb, 0xa3, 0x51, 0x35, 0xc4, 0x16, 0x5f, 0x11, 0xb2, 0x1d, 0x6f, + 0xa2, 0x65, 0x50, 0x38, 0x8c, 0xab, 0x52, 0x4f, 0x0f, 0x76, 0xca, 0xb8, 0x1d, 0x41, + 0x3b, 0x44, 0x43, 0x30, 0x34, 0xe3, 0xd6, 0xa1, 0x4b, 0x09, 0x5b, 0x80, 0x19, 0x3f, + 0x35, 0x09, 0x77, 0xf1, 0x3e, 0xbf, 0x2b, 0x70, 0x22, 0x06, 0xcb, 0x06, 0x3f, 0x42, + 0xdd, 0x45, 0x78, 0xd8, 0x77, 0x22, 0x5a, 0x58, 0x62, 0x89, 0xd4, 0x33, 0x82, 0x5f, + 0x8a, 0xa1, 0x7f, 0x25, 0x78, 0xec, 0xb5, 0xc4, 0x98, 0x66, 0xff, 0x41, 0x3e, 0x37, + 0xa5, 0x6f, 0x8e, 0xa7, 0x1f, 0x98, 0xef, 0x50, 0x89, 0x27, 0x56, 0x76}, + {0xc0, 0xc8, 0x1f, 0xd5, 0x59, 0xcf, 0xc3, 0x38, 0xf2, 0xb6, 0x06, 0x05, 0xfd, 0xd2, + 0xed, 0x9b, 0x8f, 0x0e, 0x57, 0xab, 0x9f, 0x10, 0xbf, 0x26, 0xa6, 0x46, 0xb8, 0xc1, + 0xa8, 0x60, 0x41, 0x3f, 0x9d, 0xcf, 0x86, 0xea, 0xa3, 0x73, 0x70, 0xe1, 0xdc, 0x5f, + 0x15, 0x07, 0xb7, 0xfb, 0x8c, 0x3a, 0x8e, 0x8a, 0x83, 0x31, 0xfc, 0xe7, 0x53, 0x48, + 0x16, 0xf6, 0x13, 0xb6, 0x84, 0xf4, 0xbb, 0x28, 0x7c, 0x6c, 0x13, 0x6f, 0x5c, 0x2f, + 0x61, 0xf2, 0xbe, 0x11, 0xdd, 0xf6, 0x07, 0xd1, 0xea, 0xaf, 0x33, 0x6f, 0xde, 0x13, + 0xd2, 0x9a, 0x7e, 0x52, 0x5d, 0xf7, 0x88, 0x81, 0x35, 0xcb, 0x79, 0x1e}, + {0xf1, 0xe3, 0xf7, 0xee, 0xc3, 0x36, 0x34, 0x01, 0xf8, 0x10, 0x9e, 0xfe, 0x7f, 0x6a, + 0x8b, 0x82, 0xfc, 0xde, 0xf9, 0xbc, 0xe5, 0x08, 0xf9, 0x7f, 0x31, 0x38, 0x3b, 0x3a, + 0x1b, 0x95, 0xd7, 0x65, 0x81, 0x81, 0xe0, 0xf5, 0xd8, 0x53, 0xe9, 0x77, 0xd9, 0xde, + 0x9d, 0x29, 0x44, 0x0c, 0xa5, 0x84, 0xe5, 0x25, 0x45, 0x86, 0x0c, 0x2d, 0x6c, 0xdc, + 0xf4, 0xf2, 0xd1, 0x39, 0x2d, 0xb5, 0x8a, 0x47, 0x59, 0xd1, 0x52, 0x92, 0xd3, 0xa4, + 0xa6, 0x66, 0x07, 0xc8, 0x1a, 0x87, 0xbc, 0xe1, 0xdd, 0xe5, 0x6f, 0xc9, 0xc1, 0xa6, + 0x40, 0x6b, 0x2c, 0xb8, 0x14, 0x22, 0x21, 0x1a, 0x41, 0x7a, 0xd8, 0x16}, + {0x15, 0x62, 0x06, 0x42, 0x5a, 0x7e, 0xbd, 0xb3, 0xc1, 0x24, 0x5a, 0x0c, 0xcd, 0xe3, + 0x9b, 0x87, 0xb7, 0x94, 0xf9, 0xd6, 0xb1, 0x5d, 0xc0, 0x57, 0xa6, 0x8c, 0xf3, 0x65, + 0x81, 0x7c, 0xf8, 0x28, 0x83, 0x05, 0x4e, 0xd5, 0xe2, 0xd5, 0xa4, 0xfb, 0xfa, 0x99, + 0xbd, 0x2e, 0xd7, 0xaf, 0x1f, 0xe2, 0x8f, 0x77, 0xe9, 0x6e, 0x73, 0xc2, 0x7a, 0x49, + 0xde, 0x6d, 0x5a, 0x7a, 0x57, 0x0b, 0x99, 0x1f, 0xd6, 0xf7, 0xe8, 0x1b, 0xad, 0x4e, + 0x34, 0xa3, 0x8f, 0x79, 0xea, 0xac, 0xeb, 0x50, 0x1e, 0x7d, 0x52, 0xe0, 0x0d, 0x52, + 0x9e, 0x56, 0xc6, 0x77, 0x3e, 0x6d, 0x4d, 0x53, 0xe1, 0x2f, 0x88, 0x45}, + {0xd6, 0x83, 0x79, 0x75, 0x5d, 0x34, 0x69, 0x66, 0xa6, 0x11, 0xaa, 0x17, 0x11, 0xed, + 0xb6, 0x62, 0x8f, 0x12, 0x5e, 0x98, 0x57, 0x18, 0xdd, 0x7d, 0xdd, 0xf6, 0x26, 0xf6, + 0xb8, 0xe5, 0x8f, 0x68, 0xe4, 0x6f, 0x3c, 0x94, 0x29, 0x99, 0xac, 0xd8, 0xa2, 0x92, + 0x83, 0xa3, 0x61, 0xf1, 0xf9, 0xb5, 0xf3, 0x9a, 0xc8, 0xbe, 0x13, 0xdb, 0x99, 0x26, + 0x74, 0xf0, 0x05, 0xe4, 0x3c, 0x84, 0xcf, 0x7d, 0xc0, 0x32, 0x47, 0x4a, 0x48, 0xd6, + 0x90, 0x6c, 0x99, 0x32, 0x56, 0xca, 0xfd, 0x43, 0x21, 0xd5, 0xe1, 0xc6, 0x5d, 0x91, + 0xc3, 0x28, 0xbe, 0xb3, 0x1b, 0x19, 0x27, 0x73, 0x7e, 0x68, 0x39, 0x67}, + {0xa6, 0x75, 0x56, 0x38, 0x14, 0x20, 0x78, 0xef, 0xe8, 0xa9, 0xfd, 0xaa, 0x30, 0x9f, + 0x64, 0xa2, 0xcb, 0xa8, 0xdf, 0x5c, 0x50, 0xeb, 0xd1, 0x4c, 0xb3, 0xc0, 0x4d, 0x1d, + 0xba, 0x5a, 0x11, 0x46, 0xc0, 0x1a, 0x0c, 0xc8, 0x9d, 0xcc, 0x6d, 0xa6, 0x36, 0xa4, + 0x38, 0x1b, 0xf4, 0x5c, 0xa0, 0x97, 0xc6, 0xd7, 0xdb, 0x95, 0xbe, 0xf3, 0xeb, 0xa7, + 0xab, 0x7d, 0x7e, 0x8d, 0xf6, 0xb8, 0xa0, 0x7d, 0x76, 0xda, 0xb5, 0xc3, 0x53, 0x19, + 0x0f, 0xd4, 0x9b, 0x9e, 0x11, 0x21, 0x73, 0x6f, 0xac, 0x1d, 0x60, 0x59, 0xb2, 0xfe, + 0x21, 0x60, 0xcc, 0x03, 0x4b, 0x4b, 0x67, 0x83, 0x7e, 0x88, 0x5f, 0x5a}, + {0x11, 0x3d, 0xa1, 0x70, 0xcf, 0x01, 0x63, 0x8f, 0xc4, 0xd0, 0x0d, 0x35, 0x15, 0xb8, + 0xce, 0xcf, 0x7e, 0xa4, 0xbc, 0xa4, 0xd4, 0x97, 0x02, 0xf7, 0x34, 0x14, 0x4d, 0xe4, + 0x56, 0xb6, 0x69, 0x36, 0xb9, 0x43, 0xa6, 0xa0, 0xd3, 0x28, 0x96, 0x9e, 0x64, 0x20, + 0xc3, 0xe6, 0x00, 0xcb, 0xc3, 0xb5, 0x32, 0xec, 0x2d, 0x7c, 0x89, 0x02, 0x53, 0x9b, + 0x0c, 0xc7, 0xd1, 0xd5, 0xe2, 0x7a, 0xe3, 0x43, 0x33, 0xe1, 0xa6, 0xed, 0x06, 0x3f, + 0x7e, 0x38, 0xc0, 0x3a, 0xa1, 0x99, 0x51, 0x1d, 0x30, 0x67, 0x11, 0x38, 0x26, 0x36, + 0xf8, 0xd8, 0x5a, 0xbd, 0xbe, 0xe9, 0xd5, 0x4f, 0xcd, 0xe6, 0x21, 0x6a}, + {0x5f, 0xe6, 0x46, 0x30, 0x0a, 0x17, 0xc6, 0xf1, 0x24, 0x35, 0xd2, 0x00, 0x2a, 0x2a, + 0x71, 0x58, 0x55, 0xb7, 0x82, 0x8c, 0x3c, 0xbd, 0xdb, 0x69, 0x57, 0xff, 0x95, 0xa1, + 0xf1, 0xf9, 0x6b, 0x58, 0xe3, 0xb2, 0x99, 0x66, 0x12, 0x29, 0x41, 0xef, 0x01, 0x13, + 0x8d, 0x70, 0x47, 0x08, 0xd3, 0x71, 0xbd, 0xb0, 0x82, 0x11, 0xd0, 0x32, 0x54, 0x32, + 0x36, 0x8b, 0x1e, 0x00, 0x07, 0x1b, 0x37, 0x45, 0x0b, 0x79, 0xf8, 0x5e, 0x8d, 0x08, + 0xdb, 0xa6, 0xe5, 0x37, 0x09, 0x61, 0xdc, 0xf0, 0x78, 0x52, 0xb8, 0x6e, 0xa1, 0x61, + 0xd2, 0x49, 0x03, 0xac, 0x79, 0x21, 0xe5, 0x90, 0x37, 0xb0, 0xaf, 0x0e}, + {0x2f, 0x04, 0x48, 0x37, 0xc1, 0x55, 0x05, 0x96, 0x11, 0xaa, 0x0b, 0x82, 0xe6, 0x41, + 0x9a, 0x21, 0x0c, 0x6d, 0x48, 0x73, 0x38, 0xf7, 0x81, 0x1c, 0x61, 0xc6, 0x02, 0x5a, + 0x67, 0xcc, 0x9a, 0x30, 0x1d, 0xae, 0x75, 0x0f, 0x5e, 0x80, 0x40, 0x51, 0x30, 0xcc, + 0x62, 0x26, 0xe3, 0xfb, 0x02, 0xec, 0x6d, 0x39, 0x92, 0xea, 0x1e, 0xdf, 0xeb, 0x2c, + 0xb3, 0x5b, 0x43, 0xc5, 0x44, 0x33, 0xae, 0x44, 0xee, 0x43, 0xa5, 0xbb, 0xb9, 0x89, + 0xf2, 0x9c, 0x42, 0x71, 0xc9, 0x5a, 0x9d, 0x0e, 0x76, 0xf3, 0xaa, 0x60, 0x93, 0x4f, + 0xc6, 0xe5, 0x82, 0x1d, 0x8f, 0x67, 0x94, 0x7f, 0x1b, 0x22, 0xd5, 0x62}, + {0x6d, 0x93, 0xd0, 0x18, 0x9c, 0x29, 0x4c, 0x52, 0x0c, 0x1a, 0x0c, 0x8a, 0x6c, 0xb5, + 0x6b, 0xc8, 0x31, 0x86, 0x4a, 0xdb, 0x2e, 0x05, 0x75, 0xa3, 0x62, 0x45, 0x75, 0xbc, + 0xe4, 0xfd, 0x0e, 0x5c, 0x3c, 0x7a, 0xf7, 0x3a, 0x26, 0xd4, 0x85, 0x75, 0x4d, 0x14, + 0xe9, 0xfe, 0x11, 0x7b, 0xae, 0xdf, 0x3d, 0x19, 0xf7, 0x59, 0x80, 0x70, 0x06, 0xa5, + 0x37, 0x20, 0x92, 0x83, 0x53, 0x9a, 0xf2, 0x14, 0xf5, 0xd7, 0xb2, 0x25, 0xdc, 0x7e, + 0x71, 0xdf, 0x40, 0x30, 0xb5, 0x99, 0xdb, 0x70, 0xf9, 0x21, 0x62, 0x4c, 0xed, 0xc3, + 0xb7, 0x34, 0x92, 0xda, 0x3e, 0x09, 0xee, 0x7b, 0x5c, 0x36, 0x72, 0x5e}, + {0x7f, 0x21, 0x71, 0x45, 0x07, 0xfc, 0x5b, 0x57, 0x5b, 0xd9, 0x94, 0x06, 0x5d, 0x67, + 0x79, 0x37, 0x33, 0x1e, 0x19, 0xf4, 0xbb, 0x37, 0x0a, 0x9a, 0xbc, 0xea, 0xb4, 0x47, + 0x4c, 0x10, 0xf1, 0x77, 0x3e, 0xb3, 0x08, 0x2f, 0x06, 0x39, 0x93, 0x7d, 0xbe, 0x32, + 0x9f, 0xdf, 0xe5, 0x59, 0x96, 0x5b, 0xfd, 0xbd, 0x9e, 0x1f, 0xad, 0x3d, 0xff, 0xac, + 0xb7, 0x49, 0x73, 0xcb, 0x55, 0x05, 0xb2, 0x70, 0x4c, 0x2c, 0x11, 0x55, 0xc5, 0x13, + 0x51, 0xbe, 0xcd, 0x1f, 0x88, 0x9a, 0x3a, 0x42, 0x88, 0x66, 0x47, 0x3b, 0x50, 0x5e, + 0x85, 0x77, 0x66, 0x44, 0x4a, 0x40, 0x06, 0x4a, 0x8f, 0x39, 0x34, 0x0e}, + {0xe8, 0xbd, 0xce, 0x3e, 0xd9, 0x22, 0x7d, 0xb6, 0x07, 0x2f, 0x82, 0x27, 0x41, 0xe8, + 0xb3, 0x09, 0x8d, 0x6d, 0x5b, 0xb0, 0x1f, 0xa6, 0x3f, 0x74, 0x72, 0x23, 0x36, 0x8a, + 0x36, 0x05, 0x54, 0x5e, 0x28, 0x19, 0x4b, 0x3e, 0x09, 0x0b, 0x93, 0x18, 0x40, 0xf6, + 0xf3, 0x73, 0x0e, 0xe1, 0xe3, 0x7d, 0x6f, 0x5d, 0x39, 0x73, 0xda, 0x17, 0x32, 0xf4, + 0x3e, 0x9c, 0x37, 0xca, 0xd6, 0xde, 0x8a, 0x6f, 0x9a, 0xb2, 0xb7, 0xfd, 0x3d, 0x12, + 0x40, 0xe3, 0x91, 0xb2, 0x1a, 0xa2, 0xe1, 0x97, 0x7b, 0x48, 0x9e, 0x94, 0xe6, 0xfd, + 0x02, 0x7d, 0x96, 0xf9, 0x97, 0xde, 0xd3, 0xc8, 0x2e, 0xe7, 0x0d, 0x78}, + {0xbc, 0xe7, 0x9a, 0x08, 0x45, 0x85, 0xe2, 0x0a, 0x06, 0x4d, 0x7f, 0x1c, 0xcf, 0xde, + 0x8d, 0x38, 0xb8, 0x11, 0x48, 0x0a, 0x51, 0x15, 0xac, 0x38, 0xe4, 0x8c, 0x92, 0x71, + 0xf6, 0x8b, 0xb2, 0x0e, 0x72, 0x27, 0xf4, 0x00, 0xf3, 0xea, 0x1f, 0x67, 0xaa, 0x41, + 0x8c, 0x2a, 0x2a, 0xeb, 0x72, 0x8f, 0x92, 0x32, 0x37, 0x97, 0xd7, 0x7f, 0xa1, 0x29, + 0xa6, 0x87, 0xb5, 0x32, 0xad, 0xc6, 0xef, 0x1d, 0xa7, 0x95, 0x51, 0xef, 0x1a, 0xbe, + 0x5b, 0xaf, 0xed, 0x15, 0x7b, 0x91, 0x77, 0x12, 0x8c, 0x14, 0x2e, 0xda, 0xe5, 0x7a, + 0xfb, 0xf7, 0x91, 0x29, 0x67, 0x28, 0xdd, 0xf8, 0x1b, 0x20, 0x7d, 0x46}, + {0xad, 0x4f, 0xef, 0x74, 0x9a, 0x91, 0xfe, 0x95, 0xa2, 0x08, 0xa3, 0xf6, 0xec, 0x7b, + 0x82, 0x3a, 0x01, 0x7b, 0xa4, 0x09, 0xd3, 0x01, 0x4e, 0x96, 0x97, 0xc7, 0xa3, 0x5b, + 0x4f, 0x3c, 0xc4, 0x71, 0xa9, 0xe7, 0x7a, 0x56, 0xbd, 0xf4, 0x1e, 0xbc, 0xbd, 0x98, + 0x44, 0xd6, 0xb2, 0x4c, 0x62, 0x3f, 0xc8, 0x4e, 0x1f, 0x2c, 0xd2, 0x64, 0x10, 0xe4, + 0x01, 0x40, 0x38, 0xba, 0xa5, 0xc5, 0xf9, 0x2e, 0xcd, 0x74, 0x9e, 0xfa, 0xf6, 0x6d, + 0xfd, 0xb6, 0x7a, 0x26, 0xaf, 0xe4, 0xbc, 0x78, 0x82, 0xf1, 0x0e, 0x99, 0xef, 0xf1, + 0xd0, 0xb3, 0x55, 0x82, 0x93, 0xf2, 0xc5, 0x90, 0xa3, 0x8c, 0x75, 0x5a}, + {0x95, 0x24, 0x46, 0xd9, 0x10, 0x27, 0xb7, 0xa2, 0x03, 0x50, 0x7d, 0xd5, 0xd2, 0xc6, + 0xa8, 0x3a, 0xca, 0x87, 0xb4, 0xa0, 0xbf, 0x00, 0xd4, 0xe3, 0xec, 0x72, 0xeb, 0xb3, + 0x44, 0xe2, 0xba, 0x2d, 0x94, 0xdc, 0x61, 0x1d, 0x8b, 0x91, 0xe0, 0x8c, 0x66, 0x30, + 0x81, 0x9a, 0x46, 0x36, 0xed, 0x8d, 0xd3, 0xaa, 0xe8, 0xaf, 0x29, 0xa8, 0xe6, 0xd4, + 0x3f, 0xd4, 0x39, 0xf6, 0x27, 0x80, 0x73, 0x0a, 0xcc, 0xe1, 0xff, 0x57, 0x2f, 0x4a, + 0x0f, 0x98, 0x43, 0x98, 0x83, 0xe1, 0x0d, 0x0d, 0x67, 0x00, 0xfd, 0x15, 0xfb, 0x49, + 0x4a, 0x3f, 0x5c, 0x10, 0x9c, 0xa6, 0x26, 0x51, 0x63, 0xca, 0x98, 0x26}, + {0x78, 0xba, 0xb0, 0x32, 0x88, 0x31, 0x65, 0xe7, 0x8b, 0xff, 0x5c, 0x92, 0xf7, 0x31, + 0x18, 0x38, 0xcc, 0x1f, 0x29, 0xa0, 0x91, 0x1b, 0xa8, 0x08, 0x07, 0xeb, 0xca, 0x49, + 0xcc, 0x3d, 0xb4, 0x1f, 0x0e, 0xd9, 0x3d, 0x5e, 0x2f, 0x70, 0x3d, 0x2e, 0x86, 0x53, + 0xd2, 0xe4, 0x18, 0x09, 0x3f, 0x9e, 0x6a, 0xa9, 0x4d, 0x02, 0xf6, 0x3e, 0x77, 0x5e, + 0x32, 0x33, 0xfa, 0x4a, 0x0c, 0x4b, 0x00, 0x3c, 0x2b, 0xb8, 0xf4, 0x06, 0xac, 0x46, + 0xa9, 0x9a, 0xf3, 0xc4, 0x06, 0xa8, 0xa5, 0x84, 0xa2, 0x1c, 0x87, 0x47, 0xcd, 0xc6, + 0x5f, 0x26, 0xd3, 0x3e, 0x17, 0xd2, 0x1f, 0xcd, 0x01, 0xfd, 0x43, 0x6b}, + {0x44, 0xc5, 0x97, 0x46, 0x4b, 0x5d, 0xa7, 0xc7, 0xbf, 0xff, 0x0f, 0xdf, 0x48, 0xf8, + 0xfd, 0x15, 0x5a, 0x78, 0x46, 0xaa, 0xeb, 0xb9, 0x68, 0x28, 0x14, 0xf7, 0x52, 0x5b, + 0x10, 0xd7, 0x68, 0x5a, 0xf3, 0x0e, 0x76, 0x3e, 0x58, 0x42, 0xc7, 0xb5, 0x90, 0xb9, + 0x0a, 0xee, 0xb9, 0x52, 0xdc, 0x75, 0x3f, 0x92, 0x2b, 0x07, 0xc2, 0x27, 0x14, 0xbf, + 0xf0, 0xd9, 0xf0, 0x6f, 0x2d, 0x0b, 0x42, 0x73, 0x06, 0x1e, 0x85, 0x9e, 0xcb, 0xf6, + 0x2c, 0xaf, 0xc4, 0x38, 0x22, 0xc6, 0x13, 0x39, 0x59, 0x8f, 0x73, 0xf3, 0xfb, 0x99, + 0x96, 0xb8, 0x8a, 0xda, 0x9e, 0xbc, 0x34, 0xea, 0x2f, 0x63, 0xb5, 0x3d}, + {0xd8, 0xd9, 0x5d, 0xf7, 0x2b, 0xee, 0x6e, 0xf4, 0xa5, 0x59, 0x67, 0x39, 0xf6, 0xb1, + 0x17, 0x0d, 0x73, 0x72, 0x9e, 0x49, 0x31, 0xd1, 0xf2, 0x1b, 0x13, 0x5f, 0xd7, 0x49, + 0xdf, 0x1a, 0x32, 0x04, 0xd5, 0x25, 0x98, 0x82, 0xb1, 0x90, 0x49, 0x2e, 0x91, 0x89, + 0x9a, 0x3e, 0x87, 0xeb, 0xea, 0xed, 0xf8, 0x4a, 0x70, 0x4c, 0x39, 0x3d, 0xf0, 0xee, + 0x0e, 0x2b, 0xdf, 0x95, 0xa4, 0x7e, 0x19, 0x59, 0xae, 0x5a, 0xe5, 0xe4, 0x19, 0x60, + 0xe1, 0x04, 0xe9, 0x92, 0x2f, 0x7e, 0x7a, 0x43, 0x7b, 0xe7, 0xa4, 0x9a, 0x15, 0x6f, + 0xc1, 0x2d, 0xce, 0xc7, 0xc0, 0x0c, 0xd7, 0xf4, 0xc1, 0xfd, 0xea, 0x45}, + {0x2b, 0xd7, 0x45, 0x80, 0x85, 0x01, 0x84, 0x69, 0x51, 0x06, 0x2f, 0xcf, 0xa2, 0xfa, + 0x22, 0x4c, 0xc6, 0x2d, 0x22, 0x6b, 0x65, 0x36, 0x1a, 0x94, 0xde, 0xda, 0x62, 0x03, + 0xc8, 0xeb, 0x5e, 0x5a, 0xed, 0xb1, 0xcc, 0xcf, 0x24, 0x46, 0x0e, 0xb6, 0x95, 0x03, + 0x5c, 0xbd, 0x92, 0xc2, 0xdb, 0x59, 0xc9, 0x81, 0x04, 0xdc, 0x1d, 0x9d, 0xa0, 0x31, + 0x40, 0xd9, 0x56, 0x5d, 0xea, 0xce, 0x73, 0x3f, 0xc6, 0x8d, 0x4e, 0x0a, 0xd1, 0xbf, + 0xa7, 0xb7, 0x39, 0xb3, 0xc9, 0x44, 0x7e, 0x00, 0x57, 0xbe, 0xfa, 0xae, 0x57, 0x15, + 0x7f, 0x20, 0xc1, 0x60, 0xdb, 0x18, 0x62, 0x26, 0x91, 0x88, 0x05, 0x26}, + {0x04, 0xff, 0x60, 0x83, 0xa6, 0x04, 0xf7, 0x59, 0xf4, 0xe6, 0x61, 0x76, 0xde, 0x3f, + 0xd9, 0xc3, 0x51, 0x35, 0x87, 0x12, 0x73, 0x2a, 0x1b, 0x83, 0x57, 0x5d, 0x61, 0x4e, + 0x2e, 0x0c, 0xad, 0x54, 0x42, 0xe5, 0x76, 0xc6, 0x3c, 0x8e, 0x81, 0x4c, 0xad, 0xcc, + 0xce, 0x03, 0x93, 0x2c, 0x42, 0x5e, 0x08, 0x9f, 0x12, 0xb4, 0xca, 0xcc, 0x07, 0xec, + 0xb8, 0x43, 0x44, 0xb2, 0x10, 0xfa, 0xed, 0x0d, 0x2a, 0x52, 0x2b, 0xb8, 0xd5, 0x67, + 0x3b, 0xee, 0xeb, 0xc1, 0xa5, 0x9f, 0x46, 0x63, 0xf1, 0x36, 0xd3, 0x9f, 0xc1, 0x6e, + 0xf2, 0xd2, 0xb4, 0xa5, 0x08, 0x94, 0x7a, 0xa7, 0xba, 0xb2, 0xec, 0x62}, + {0x3d, 0x2b, 0x15, 0x61, 0x52, 0x79, 0xed, 0xe5, 0xd1, 0xd7, 0xdd, 0x0e, 0x7d, 0x35, + 0x62, 0x49, 0x71, 0x4c, 0x6b, 0xb9, 0xd0, 0xc8, 0x82, 0x74, 0xbe, 0xd8, 0x66, 0xa9, + 0x19, 0xf9, 0x59, 0x2e, 0x74, 0x28, 0xb6, 0xaf, 0x36, 0x28, 0x07, 0x92, 0xa5, 0x04, + 0xe1, 0x79, 0x85, 0x5e, 0xcd, 0x5f, 0x4a, 0xa1, 0x30, 0xc6, 0xad, 0x01, 0xad, 0x5a, + 0x98, 0x3f, 0x66, 0x75, 0x50, 0x3d, 0x91, 0x61, 0xda, 0x31, 0x32, 0x1a, 0x36, 0x2d, + 0xc6, 0x0d, 0x70, 0x02, 0x20, 0x94, 0x32, 0x58, 0x47, 0xfa, 0xce, 0x94, 0x95, 0x3f, + 0x51, 0x01, 0xd8, 0x02, 0x5c, 0x5d, 0xc0, 0x31, 0xa1, 0xc2, 0xdb, 0x3d}, + {0x4b, 0xc5, 0x5e, 0xce, 0xf9, 0x0f, 0xdc, 0x9a, 0x0d, 0x13, 0x2f, 0x8c, 0x6b, 0x2a, + 0x9c, 0x03, 0x15, 0x95, 0xf8, 0xf0, 0xc7, 0x07, 0x80, 0x02, 0x6b, 0xb3, 0x04, 0xac, + 0x14, 0x83, 0x96, 0x78, 0x14, 0xbb, 0x96, 0x27, 0xa2, 0x57, 0xaa, 0xf3, 0x21, 0xda, + 0x07, 0x9b, 0xb7, 0xba, 0x3a, 0x88, 0x1c, 0x39, 0xa0, 0x31, 0x18, 0xe2, 0x4b, 0xe5, + 0xf9, 0x05, 0x32, 0xd8, 0x38, 0xfb, 0xe7, 0x5e, 0x8e, 0x6a, 0x44, 0x41, 0xcb, 0xfd, + 0x8d, 0x53, 0xf9, 0x37, 0x49, 0x43, 0xa9, 0xfd, 0xac, 0xa5, 0x78, 0x8c, 0x3c, 0x26, + 0x8d, 0x90, 0xaf, 0x46, 0x09, 0x0d, 0xca, 0x9b, 0x3c, 0x63, 0xd0, 0x61}, + {0x66, 0x25, 0xdb, 0xff, 0x35, 0x49, 0x74, 0x63, 0xbb, 0x68, 0x0b, 0x78, 0x89, 0x6b, + 0xbd, 0xc5, 0x03, 0xec, 0x3e, 0x55, 0x80, 0x32, 0x1b, 0x6f, 0xf5, 0xd7, 0xae, 0x47, + 0xd8, 0x5f, 0x96, 0x6e, 0xdf, 0x73, 0xfc, 0xf8, 0xbc, 0x28, 0xa3, 0xad, 0xfc, 0x37, + 0xf0, 0xa6, 0x5d, 0x69, 0x84, 0xee, 0x09, 0xa9, 0xc2, 0x38, 0xdb, 0xb4, 0x7f, 0x63, + 0xdc, 0x7b, 0x06, 0xf8, 0x2d, 0xac, 0x23, 0x5b, 0x7b, 0x52, 0x80, 0xee, 0x53, 0xb9, + 0xd2, 0x9a, 0x8d, 0x6d, 0xde, 0xfa, 0xaa, 0x19, 0x8f, 0xe8, 0xcf, 0x82, 0x0e, 0x15, + 0x04, 0x17, 0x71, 0x0e, 0xdc, 0xde, 0x95, 0xdd, 0xb9, 0xbb, 0xb9, 0x79}, + {0xc2, 0x26, 0x31, 0x6a, 0x40, 0x55, 0xb3, 0xeb, 0x93, 0xc3, 0xc8, 0x68, 0xa8, 0x83, + 0x63, 0xd2, 0x82, 0x7a, 0xb9, 0xe5, 0x29, 0x64, 0x0c, 0x6c, 0x47, 0x21, 0xfd, 0xc9, + 0x58, 0xf1, 0x65, 0x50, 0x74, 0x73, 0x9f, 0x8e, 0xae, 0x7d, 0x99, 0xd1, 0x16, 0x08, + 0xbb, 0xcf, 0xf8, 0xa2, 0x32, 0xa0, 0x0a, 0x5f, 0x44, 0x6d, 0x12, 0xba, 0x6c, 0xcd, + 0x34, 0xb8, 0xcc, 0x0a, 0x46, 0x11, 0xa8, 0x1b, 0x54, 0x99, 0x42, 0x0c, 0xfb, 0x69, + 0x81, 0x70, 0x67, 0xcf, 0x6e, 0xd7, 0xac, 0x00, 0x46, 0xe1, 0xba, 0x45, 0xe6, 0x70, + 0x8a, 0xb9, 0xaa, 0x2e, 0xf2, 0xfa, 0xa4, 0x58, 0x9e, 0xf3, 0x81, 0x39}, + {0x93, 0x0a, 0x23, 0x59, 0x75, 0x8a, 0xfb, 0x18, 0x5d, 0xf4, 0xe6, 0x60, 0x69, 0x8f, + 0x16, 0x1d, 0xb5, 0x3c, 0xa9, 0x14, 0x45, 0xa9, 0x85, 0x3a, 0xfd, 0xd0, 0xac, 0x05, + 0x37, 0x08, 0xdc, 0x38, 0xde, 0x6f, 0xe6, 0x6d, 0xa5, 0xdf, 0x45, 0xc8, 0x3a, 0x48, + 0x40, 0x2c, 0x00, 0xa5, 0x52, 0xe1, 0x32, 0xf6, 0xb4, 0xc7, 0x63, 0xe1, 0xd2, 0xe9, + 0x65, 0x1b, 0xbc, 0xdc, 0x2e, 0x45, 0xf4, 0x30, 0x40, 0x97, 0x75, 0xc5, 0x82, 0x27, + 0x6d, 0x85, 0xcc, 0xbe, 0x9c, 0xf9, 0x69, 0x45, 0x13, 0xfa, 0x71, 0x4e, 0xea, 0xc0, + 0x73, 0xfc, 0x44, 0x88, 0x69, 0x24, 0x3f, 0x59, 0x1a, 0x9a, 0x2d, 0x63}, + {0xa6, 0xcb, 0x07, 0xb8, 0x15, 0x6b, 0xbb, 0xf6, 0xd7, 0xf0, 0x54, 0xbc, 0xdf, 0xc7, + 0x23, 0x18, 0x0b, 0x67, 0x29, 0x6e, 0x03, 0x97, 0x1d, 0xbb, 0x57, 0x4a, 0xed, 0x47, + 0x88, 0xf4, 0x24, 0x0b, 0xa7, 0x84, 0x0c, 0xed, 0x11, 0xfd, 0x09, 0xbf, 0x3a, 0x69, + 0x9f, 0x0d, 0x81, 0x71, 0xf0, 0x63, 0x79, 0x87, 0xcf, 0x57, 0x2d, 0x8c, 0x90, 0x21, + 0xa2, 0x4b, 0xf6, 0x8a, 0xf2, 0x7d, 0x5a, 0x3a, 0xc7, 0xea, 0x1b, 0x51, 0xbe, 0xd4, + 0xda, 0xdc, 0xf2, 0xcc, 0x26, 0xed, 0x75, 0x80, 0x53, 0xa4, 0x65, 0x9a, 0x5f, 0x00, + 0x9f, 0xff, 0x9c, 0xe1, 0x63, 0x1f, 0x48, 0x75, 0x44, 0xf7, 0xfc, 0x34}, + {0xca, 0x67, 0x97, 0x78, 0x4c, 0xe0, 0x97, 0xc1, 0x7d, 0x46, 0xd9, 0x38, 0xcb, 0x4d, + 0x71, 0xb8, 0xa8, 0x5f, 0xf9, 0x83, 0x82, 0x88, 0xde, 0x55, 0xf7, 0x63, 0xfa, 0x4d, + 0x16, 0xdc, 0x3b, 0x3d, 0x98, 0xaa, 0xcf, 0x78, 0xab, 0x1d, 0xbb, 0xa5, 0xf2, 0x72, + 0x0b, 0x19, 0x67, 0xa2, 0xed, 0x5c, 0x8e, 0x60, 0x92, 0x0a, 0x11, 0xc9, 0x09, 0x93, + 0xb0, 0x74, 0xb3, 0x2f, 0x04, 0xa3, 0x19, 0x01, 0x7d, 0x17, 0xc2, 0xe8, 0x9c, 0xd8, + 0xa2, 0x67, 0xc1, 0xd0, 0x95, 0x68, 0xf6, 0xa5, 0x9d, 0x66, 0xb0, 0xa2, 0x82, 0xb2, + 0xe5, 0x98, 0x65, 0xf5, 0x73, 0x0a, 0xe2, 0xed, 0xf1, 0x88, 0xc0, 0x56}, + {0x17, 0x6e, 0xa8, 0x10, 0x11, 0x3d, 0x6d, 0x33, 0xfa, 0xb2, 0x75, 0x0b, 0x32, 0x88, + 0xf3, 0xd7, 0x88, 0x29, 0x07, 0x25, 0x76, 0x33, 0x15, 0xf9, 0x87, 0x8b, 0x10, 0x99, + 0x6b, 0x4c, 0x67, 0x09, 0x02, 0x8f, 0xf3, 0x24, 0xac, 0x5f, 0x1b, 0x58, 0xbd, 0x0c, + 0xe3, 0xba, 0xfe, 0xe9, 0x0b, 0xa9, 0xf0, 0x92, 0xcf, 0x8a, 0x02, 0x69, 0x21, 0x9a, + 0x8f, 0x03, 0x59, 0x83, 0xa4, 0x7e, 0x8b, 0x03, 0xf8, 0x6f, 0x31, 0x99, 0x21, 0xf8, + 0x4e, 0x9f, 0x4f, 0x8d, 0xa7, 0xea, 0x82, 0xd2, 0x49, 0x2f, 0x74, 0x31, 0xef, 0x5a, + 0xab, 0xa5, 0x71, 0x09, 0x65, 0xeb, 0x69, 0x59, 0x02, 0x31, 0x5e, 0x6e}, + {0xfb, 0x93, 0xe5, 0x87, 0xf5, 0x62, 0x6c, 0xb1, 0x71, 0x3e, 0x5d, 0xca, 0xde, 0xed, + 0x99, 0x49, 0x6d, 0x3e, 0xcc, 0x14, 0xe0, 0xc1, 0x91, 0xb4, 0xa8, 0xdb, 0xa8, 0x89, + 0x47, 0x11, 0xf5, 0x08, 0x22, 0x62, 0x06, 0x63, 0x0e, 0xfb, 0x04, 0x33, 0x3f, 0xba, + 0xac, 0x87, 0x89, 0x06, 0x35, 0xfb, 0xa3, 0x61, 0x10, 0x8c, 0x77, 0x24, 0x19, 0xbd, + 0x20, 0x86, 0x83, 0xd1, 0x43, 0xad, 0x58, 0x30, 0xd0, 0x63, 0x76, 0xe5, 0xfd, 0x0f, + 0x3c, 0x32, 0x10, 0xa6, 0x2e, 0xa2, 0x38, 0xdf, 0xc3, 0x05, 0x9a, 0x4f, 0x99, 0xac, + 0xbd, 0x8a, 0xc7, 0xbd, 0x99, 0xdc, 0xe3, 0xef, 0xa4, 0x9f, 0x54, 0x26}, + {0xd6, 0xf9, 0x6b, 0x1e, 0x46, 0x5a, 0x1d, 0x74, 0x81, 0xa5, 0x77, 0x77, 0xfc, 0xb3, + 0x05, 0x23, 0xd9, 0xd3, 0x74, 0x64, 0xa2, 0x74, 0x55, 0xd4, 0xff, 0xe0, 0x01, 0x64, + 0xdc, 0xe1, 0x26, 0x19, 0x6e, 0x66, 0x3f, 0xaf, 0x49, 0x85, 0x46, 0xdb, 0xa5, 0x0e, + 0x4a, 0xf1, 0x04, 0xcf, 0x7f, 0xd7, 0x47, 0x0c, 0xba, 0xa4, 0xf7, 0x3f, 0xf2, 0x3d, + 0x85, 0x3c, 0xce, 0x32, 0xe1, 0xdf, 0x10, 0x3a, 0xa0, 0xce, 0x17, 0xea, 0x8a, 0x4e, + 0x7f, 0xe0, 0xfd, 0xc1, 0x1f, 0x3a, 0x46, 0x15, 0xd5, 0x2f, 0xf1, 0xc0, 0xf2, 0x31, + 0xfd, 0x22, 0x53, 0x17, 0x15, 0x5d, 0x1e, 0x86, 0x1d, 0xd0, 0xa1, 0x1f}, + {0x32, 0x98, 0x59, 0x7d, 0x94, 0x55, 0x80, 0xcc, 0x20, 0x55, 0xf1, 0x37, 0xda, 0x56, + 0x46, 0x1e, 0x20, 0x93, 0x05, 0x4e, 0x74, 0xf7, 0xf6, 0x99, 0x33, 0xcf, 0x75, 0x6a, + 0xbc, 0x63, 0x35, 0x77, 0xab, 0x94, 0xdf, 0xd1, 0x00, 0xac, 0xdc, 0x38, 0xe9, 0x0d, + 0x08, 0xd1, 0xdd, 0x2b, 0x71, 0x2e, 0x62, 0xe2, 0xd5, 0xfd, 0x3e, 0xe9, 0x13, 0x7f, + 0xe5, 0x01, 0x9a, 0xee, 0x18, 0xed, 0xfc, 0x73, 0xb3, 0x9c, 0x13, 0x63, 0x08, 0xe9, + 0xb1, 0x06, 0xcd, 0x3e, 0xa0, 0xc5, 0x67, 0xda, 0x93, 0xa4, 0x32, 0x89, 0x63, 0xad, + 0xc8, 0xce, 0x77, 0x8d, 0x44, 0x4f, 0x86, 0x1b, 0x70, 0x6b, 0x42, 0x1f}, + {0x01, 0x1c, 0x91, 0x41, 0x4c, 0x26, 0xc9, 0xef, 0x25, 0x2c, 0xa2, 0x17, 0xb8, 0xb7, + 0xa3, 0xf1, 0x47, 0x14, 0x0f, 0xf3, 0x6b, 0xda, 0x75, 0x58, 0x90, 0xb0, 0x31, 0x1d, + 0x27, 0xf5, 0x1a, 0x4e, 0x52, 0x25, 0xa1, 0x91, 0xc8, 0x35, 0x7e, 0xf1, 0x76, 0x9c, + 0x5e, 0x57, 0x53, 0x81, 0x6b, 0xb7, 0x3e, 0x72, 0x9b, 0x0d, 0x6f, 0x40, 0x83, 0xfa, + 0x38, 0xe4, 0xa7, 0x3f, 0x1b, 0xbb, 0x76, 0x0b, 0x9b, 0x93, 0x92, 0x7f, 0xf9, 0xc1, + 0xb8, 0x08, 0x6e, 0xab, 0x44, 0xd4, 0xcb, 0x71, 0x67, 0xbe, 0x17, 0x80, 0xbb, 0x99, + 0x63, 0x64, 0xe5, 0x22, 0x55, 0xa9, 0x72, 0xb7, 0x1e, 0xd6, 0x6d, 0x7b}, + {0x92, 0x3d, 0xf3, 0x50, 0xe8, 0xc1, 0xad, 0xb7, 0xcf, 0xd5, 0x8c, 0x60, 0x4f, 0xfa, + 0x98, 0x79, 0xdb, 0x5b, 0xfc, 0x8d, 0xbd, 0x2d, 0x96, 0xad, 0x4f, 0x2f, 0x1d, 0xaf, + 0xce, 0x9b, 0x3e, 0x70, 0xc7, 0xd2, 0x01, 0xab, 0xf9, 0xab, 0x30, 0x57, 0x18, 0x3b, + 0x14, 0x40, 0xdc, 0x76, 0xfb, 0x16, 0x81, 0xb2, 0xcb, 0xa0, 0x65, 0xbe, 0x6c, 0x86, + 0xfe, 0x6a, 0xff, 0x9b, 0x65, 0x9b, 0xfa, 0x53, 0x55, 0x54, 0x88, 0x94, 0xe9, 0xc8, + 0x14, 0x6c, 0xe5, 0xd4, 0xae, 0x65, 0x66, 0x5d, 0x3a, 0x84, 0xf1, 0x5a, 0xd6, 0xbc, + 0x3e, 0xb7, 0x1b, 0x18, 0x50, 0x1f, 0xc6, 0xc4, 0xe5, 0x93, 0x8d, 0x39}, + {0xf3, 0x48, 0xe2, 0x33, 0x67, 0xd1, 0x4b, 0x1c, 0x5f, 0x0a, 0xbf, 0x15, 0x87, 0x12, + 0x9e, 0xbd, 0x76, 0x03, 0x0b, 0xa1, 0xf0, 0x8c, 0x3f, 0xd4, 0x13, 0x1b, 0x19, 0xdf, + 0x5d, 0x9b, 0xb0, 0x53, 0xf2, 0xe3, 0xe7, 0xd2, 0x60, 0x7c, 0x87, 0xc3, 0xb1, 0x8b, + 0x82, 0x30, 0xa0, 0xaa, 0x34, 0x3b, 0x38, 0xf1, 0x9e, 0x73, 0xe7, 0x26, 0x3e, 0x28, + 0x77, 0x05, 0xc3, 0x02, 0x90, 0x9c, 0x9c, 0x69, 0xcc, 0xf1, 0x46, 0x59, 0x23, 0xa7, + 0x06, 0xf3, 0x7d, 0xd9, 0xe5, 0xcc, 0xb5, 0x18, 0x17, 0x92, 0x75, 0xe9, 0xb4, 0x81, + 0x47, 0xd2, 0xcd, 0x28, 0x07, 0xd9, 0xcd, 0x6f, 0x0c, 0xf3, 0xca, 0x51}, + {0x0a, 0xe0, 0x74, 0x76, 0x42, 0xa7, 0x0b, 0xa6, 0xf3, 0x7b, 0x7a, 0xa1, 0x70, 0x85, + 0x0e, 0x63, 0xcc, 0x24, 0x33, 0xcf, 0x3d, 0x56, 0x58, 0x37, 0xaa, 0xfd, 0x83, 0x23, + 0x29, 0xaa, 0x04, 0x55, 0xc7, 0x54, 0xac, 0x18, 0x9a, 0xf9, 0x7a, 0x73, 0x0f, 0xb3, + 0x1c, 0xc5, 0xdc, 0x78, 0x33, 0x90, 0xc7, 0x0c, 0xe1, 0x4c, 0x33, 0xbc, 0x89, 0x2b, + 0x9a, 0xe9, 0xf8, 0x89, 0xc1, 0x29, 0xae, 0x12, 0xcf, 0x01, 0x0d, 0x1f, 0xcb, 0xc0, + 0x9e, 0xa9, 0xae, 0xf7, 0x34, 0x3a, 0xcc, 0xef, 0xd1, 0x0d, 0x22, 0x4e, 0x9c, 0xd0, + 0x21, 0x75, 0xca, 0x55, 0xea, 0xa5, 0xeb, 0x58, 0xe9, 0x4f, 0xd1, 0x5f}, + {0x2c, 0xab, 0x45, 0x28, 0xdf, 0x2d, 0xdc, 0xb5, 0x93, 0xe9, 0x7f, 0x0a, 0xb1, 0x91, + 0x94, 0x06, 0x46, 0xe3, 0x02, 0x40, 0xd6, 0xf3, 0xaa, 0x4d, 0xd1, 0x74, 0x64, 0x58, + 0x6e, 0xf2, 0x3f, 0x09, 0x8e, 0xcb, 0x93, 0xbf, 0x5e, 0xfe, 0x42, 0x3c, 0x5f, 0x56, + 0xd4, 0x36, 0x51, 0xa8, 0xdf, 0xbe, 0xe8, 0x20, 0x42, 0x88, 0x9e, 0x85, 0xf0, 0xe0, + 0x28, 0xd1, 0x25, 0x07, 0x96, 0x3f, 0xd7, 0x7d, 0x29, 0x98, 0x05, 0x68, 0xfe, 0x24, + 0x0d, 0xb1, 0xe5, 0x23, 0xaf, 0xdb, 0x72, 0x06, 0x73, 0x75, 0x29, 0xac, 0x57, 0xb4, + 0x3a, 0x25, 0x67, 0x13, 0xa4, 0x70, 0xb4, 0x86, 0xbc, 0xbc, 0x59, 0x2f}, + {0x5f, 0x13, 0x17, 0x99, 0x42, 0x7d, 0x84, 0x83, 0xd7, 0x03, 0x7d, 0x56, 0x1f, 0x91, + 0x1b, 0xad, 0xd1, 0xaa, 0x77, 0xbe, 0xd9, 0x48, 0x77, 0x7e, 0x4a, 0xaf, 0x51, 0x2e, + 0x2e, 0xb4, 0x58, 0x54, 0x01, 0xc3, 0x91, 0xb6, 0x60, 0xd5, 0x41, 0x70, 0x1e, 0xe7, + 0xd7, 0xad, 0x3f, 0x1b, 0x20, 0x85, 0x85, 0x55, 0x33, 0x11, 0x63, 0xe1, 0xc2, 0x16, + 0xb1, 0x28, 0x08, 0x01, 0x3d, 0x5e, 0xa5, 0x2a, 0x4f, 0x44, 0x07, 0x0c, 0xe6, 0x92, + 0x51, 0xed, 0x10, 0x1d, 0x42, 0x74, 0x2d, 0x4e, 0xc5, 0x42, 0x64, 0xc8, 0xb5, 0xfd, + 0x82, 0x4c, 0x2b, 0x35, 0x64, 0x86, 0x76, 0x8a, 0x4a, 0x00, 0xe9, 0x13}, + {0xdb, 0xce, 0x2f, 0x83, 0x45, 0x88, 0x9d, 0x73, 0x63, 0xf8, 0x6b, 0xae, 0xc9, 0xd6, + 0x38, 0xfa, 0xf7, 0xfe, 0x4f, 0xb7, 0xca, 0x0d, 0xbc, 0x32, 0x5e, 0xe4, 0xbc, 0x14, + 0x88, 0x7e, 0x93, 0x73, 0x7f, 0x87, 0x3b, 0x19, 0xc9, 0x00, 0x2e, 0xbb, 0x6b, 0x50, + 0xdc, 0xe0, 0x90, 0xa8, 0xe3, 0xec, 0x9f, 0x64, 0xde, 0x36, 0xc0, 0xb7, 0xf3, 0xec, + 0x1a, 0x9e, 0xde, 0x98, 0x08, 0x04, 0x46, 0x5f, 0x8d, 0xf4, 0x7b, 0x29, 0x16, 0x71, + 0x03, 0xb9, 0x34, 0x68, 0xf0, 0xd4, 0x22, 0x3b, 0xd1, 0xa9, 0xc6, 0xbd, 0x96, 0x46, + 0x57, 0x15, 0x97, 0xe1, 0x35, 0xe8, 0xd5, 0x91, 0xe8, 0xa4, 0xf8, 0x2c}, + {0x67, 0x0f, 0x11, 0x07, 0x87, 0xfd, 0x93, 0x6d, 0x49, 0xb5, 0x38, 0x7c, 0xd3, 0x09, + 0x4c, 0xdd, 0x86, 0x6a, 0x73, 0xc2, 0x4c, 0x6a, 0xb1, 0x7c, 0x09, 0x2a, 0x25, 0x58, + 0x6e, 0xbd, 0x49, 0x20, 0xa2, 0x6b, 0xd0, 0x17, 0x7e, 0x48, 0xb5, 0x2c, 0x6b, 0x19, + 0x50, 0x39, 0x1c, 0x38, 0xd2, 0x24, 0x30, 0x8a, 0x97, 0x85, 0x81, 0x9c, 0x65, 0xd7, + 0xf6, 0xa4, 0xd6, 0x91, 0x28, 0x7f, 0x6f, 0x7a, 0x49, 0xef, 0x9a, 0x6a, 0x8d, 0xfd, + 0x09, 0x7d, 0x0b, 0xb9, 0x3d, 0x5b, 0xbe, 0x60, 0xee, 0xf0, 0xd4, 0xbf, 0x9e, 0x51, + 0x2c, 0xb5, 0x21, 0x4c, 0x1d, 0x94, 0x45, 0xc5, 0xdf, 0xaa, 0x11, 0x60}, + {0x3c, 0xf8, 0x95, 0xcf, 0x6d, 0x92, 0x67, 0x5f, 0x71, 0x90, 0x28, 0x71, 0x61, 0x85, + 0x7e, 0x7c, 0x5b, 0x7a, 0x8f, 0x99, 0xf3, 0xe7, 0xa1, 0xd6, 0xe0, 0xf9, 0x62, 0x0b, + 0x1b, 0xcc, 0xc5, 0x6f, 0x90, 0xf8, 0xcb, 0x02, 0xc8, 0xd0, 0xde, 0x63, 0xaa, 0x6a, + 0xff, 0x0d, 0xca, 0x98, 0xd0, 0xfb, 0x99, 0xed, 0xb6, 0xb9, 0xfd, 0x0a, 0x4d, 0x62, + 0x1e, 0x0b, 0x34, 0x79, 0xb7, 0x18, 0xce, 0x69, 0xcb, 0x79, 0x98, 0xb2, 0x28, 0x55, + 0xef, 0xd1, 0x92, 0x90, 0x7e, 0xd4, 0x3c, 0xae, 0x1a, 0xdd, 0x52, 0x23, 0x9f, 0x18, + 0x42, 0x04, 0x7e, 0x12, 0xf1, 0x01, 0x71, 0xe5, 0x3a, 0x6b, 0x59, 0x15}, + {0xa2, 0x79, 0x91, 0x3f, 0xd2, 0x39, 0x27, 0x46, 0xcf, 0xdd, 0xd6, 0x97, 0x31, 0x12, + 0x83, 0xff, 0x8a, 0x14, 0xf2, 0x53, 0xb5, 0xde, 0x07, 0x13, 0xda, 0x4d, 0x5f, 0x7b, + 0x68, 0x37, 0x22, 0x0d, 0xca, 0x24, 0x51, 0x7e, 0x16, 0x31, 0xff, 0x09, 0xdf, 0x45, + 0xc7, 0xd9, 0x8b, 0x15, 0xe4, 0x0b, 0xe5, 0x56, 0xf5, 0x7e, 0x22, 0x7d, 0x2b, 0x29, + 0x38, 0xd1, 0xb6, 0xaf, 0x41, 0xe2, 0xa4, 0x3a, 0xf5, 0x05, 0x33, 0x2a, 0xbf, 0x38, + 0xc1, 0x2c, 0xc3, 0x26, 0xe9, 0xa2, 0x8f, 0x3f, 0x58, 0x48, 0xeb, 0xd2, 0x49, 0x55, + 0xa2, 0xb1, 0x3a, 0x08, 0x6c, 0xa3, 0x87, 0x46, 0x6e, 0xaa, 0xfc, 0x32}, + {0xf5, 0x9a, 0x7d, 0xc5, 0x8d, 0x6e, 0xc5, 0x7b, 0xf2, 0xbd, 0xf0, 0x9d, 0xed, 0xd2, + 0x0b, 0x3e, 0xa3, 0xe4, 0xef, 0x22, 0xde, 0x14, 0xc0, 0xaa, 0x5c, 0x6a, 0xbd, 0xfe, + 0xce, 0xe9, 0x27, 0x46, 0xdf, 0xcc, 0x87, 0x27, 0x73, 0xa4, 0x07, 0x32, 0xf8, 0xe3, + 0x13, 0xf2, 0x08, 0x19, 0xe3, 0x17, 0x4e, 0x96, 0x0d, 0xf6, 0xd7, 0xec, 0xb2, 0xd5, + 0xe9, 0x0b, 0x60, 0xc2, 0x36, 0x63, 0x6f, 0x74, 0x1c, 0x97, 0x6c, 0xab, 0x45, 0xf3, + 0x4a, 0x3f, 0x1f, 0x73, 0x43, 0x99, 0x72, 0xeb, 0x88, 0xe2, 0x6d, 0x18, 0x44, 0x03, + 0x8a, 0x6a, 0x59, 0x33, 0x93, 0x62, 0xd6, 0x7e, 0x00, 0x17, 0x49, 0x7b}, + {0x64, 0xb0, 0x84, 0xab, 0x5c, 0xfb, 0x85, 0x2d, 0x14, 0xbc, 0xf3, 0x89, 0xd2, 0x10, + 0x78, 0x49, 0x0c, 0xce, 0x15, 0x7b, 0x44, 0xdc, 0x6a, 0x47, 0x7b, 0xfd, 0x44, 0xf8, + 0x76, 0xa3, 0x2b, 0x12, 0xdd, 0xa2, 0x53, 0xdd, 0x28, 0x1b, 0x34, 0x54, 0x3f, 0xfc, + 0x42, 0xdf, 0x5b, 0x90, 0x17, 0xaa, 0xf4, 0xf8, 0xd2, 0x4d, 0xd9, 0x92, 0xf5, 0x0f, + 0x7d, 0xd3, 0x8c, 0xe0, 0x0f, 0x62, 0x03, 0x1d, 0x54, 0xe5, 0xb4, 0xa2, 0xcd, 0x32, + 0x02, 0xc2, 0x7f, 0x18, 0x5d, 0x11, 0x42, 0xfd, 0xd0, 0x9e, 0xd9, 0x79, 0xd4, 0x7d, + 0xbe, 0xb4, 0xab, 0x2e, 0x4c, 0xec, 0x68, 0x2b, 0xf5, 0x0b, 0xc7, 0x02}, + {0xbb, 0x2f, 0x0b, 0x5d, 0x4b, 0xec, 0x87, 0xa2, 0xca, 0x82, 0x48, 0x07, 0x90, 0x57, + 0x5c, 0x41, 0x5c, 0x81, 0xd0, 0xc1, 0x1e, 0xa6, 0x44, 0xe0, 0xe0, 0xf5, 0x9e, 0x40, + 0x0a, 0x4f, 0x33, 0x26, 0xe1, 0x72, 0x8d, 0x45, 0xbf, 0x32, 0xe5, 0xac, 0xb5, 0x3c, + 0xb7, 0x7c, 0xe0, 0x68, 0xe7, 0x5b, 0xe7, 0xbd, 0x8b, 0xee, 0x94, 0x7d, 0xcf, 0x56, + 0x03, 0x3a, 0xb4, 0xfe, 0xe3, 0x97, 0x06, 0x6b, 0xc0, 0xa3, 0x62, 0xdf, 0x4a, 0xf0, + 0xc8, 0xb6, 0x5d, 0xa4, 0x6d, 0x07, 0xef, 0x00, 0xf0, 0x3e, 0xa9, 0xd2, 0xf0, 0x49, + 0x58, 0xb9, 0x9c, 0x9c, 0xae, 0x2f, 0x1b, 0x44, 0x43, 0x7f, 0xc3, 0x1c}, + {0x4f, 0x32, 0xc7, 0x5c, 0x5a, 0x56, 0x8f, 0x50, 0x22, 0xa9, 0x06, 0xe5, 0xc0, 0xc4, + 0x61, 0xd0, 0x19, 0xac, 0x45, 0x5c, 0xdb, 0xab, 0x18, 0xfb, 0x4a, 0x31, 0x80, 0x03, + 0xc1, 0x09, 0x68, 0x6c, 0xb9, 0xae, 0xce, 0xc9, 0xf1, 0x56, 0x66, 0xd7, 0x6a, 0x65, + 0xe5, 0x18, 0xf8, 0x15, 0x5b, 0x1c, 0x34, 0x23, 0x4c, 0x84, 0x32, 0x28, 0xe7, 0x26, + 0x38, 0x68, 0x19, 0x2f, 0x77, 0x6f, 0x34, 0x3a, 0xc8, 0x6a, 0xda, 0xe2, 0x12, 0x51, + 0xd5, 0xd2, 0xed, 0x51, 0xe8, 0xb1, 0x31, 0x03, 0xbd, 0xe9, 0x62, 0x72, 0xc6, 0x8e, + 0xdd, 0x46, 0x07, 0x96, 0xd0, 0xc5, 0xf7, 0x6e, 0x9f, 0x1b, 0x91, 0x05}, + {0xbb, 0x0e, 0xdf, 0xf5, 0x83, 0x99, 0x33, 0xc1, 0xac, 0x4c, 0x2c, 0x51, 0x8f, 0x75, + 0xf3, 0xc0, 0xe1, 0x98, 0xb3, 0x0b, 0x0a, 0x13, 0xf1, 0x2c, 0x62, 0x0c, 0x27, 0xaa, + 0xf9, 0xec, 0x3c, 0x6b, 0xef, 0xea, 0x2e, 0x51, 0xf3, 0xac, 0x49, 0x53, 0x49, 0xcb, + 0xc1, 0x1c, 0xd3, 0x41, 0xc1, 0x20, 0x8d, 0x68, 0x9a, 0xa9, 0x07, 0x0c, 0x18, 0x24, + 0x17, 0x2d, 0x4b, 0xc6, 0xd1, 0xf9, 0x5e, 0x55, 0x08, 0xbd, 0x73, 0x3b, 0xba, 0x70, + 0xa7, 0x36, 0x0c, 0xbf, 0xaf, 0xa3, 0x08, 0xef, 0x4a, 0x62, 0xf2, 0x46, 0x09, 0xb4, + 0x98, 0xff, 0x37, 0x57, 0x9d, 0x74, 0x81, 0x33, 0xe1, 0x4d, 0x5f, 0x67}, + {0xfc, 0x82, 0x17, 0x6b, 0x03, 0x52, 0x2c, 0x0e, 0xb4, 0x83, 0xad, 0x6c, 0x81, 0x6c, + 0x81, 0x64, 0x3e, 0x07, 0x64, 0x69, 0xd9, 0xbd, 0xdc, 0xd0, 0x20, 0xc5, 0x64, 0x01, + 0xf7, 0x9d, 0xd9, 0x13, 0x1d, 0xb3, 0xda, 0x3b, 0xd9, 0xf6, 0x2f, 0xa1, 0xfe, 0x2d, + 0x65, 0x9d, 0x0f, 0xd8, 0x25, 0x07, 0x87, 0x94, 0xbe, 0x9a, 0xf3, 0x4f, 0x9c, 0x01, + 0x43, 0x3c, 0xcd, 0x82, 0xb8, 0x50, 0xf4, 0x60, 0xca, 0xc0, 0xe5, 0x21, 0xc3, 0x5e, + 0x4b, 0x01, 0xa2, 0xbf, 0x19, 0xd7, 0xc9, 0x69, 0xcb, 0x4f, 0xa0, 0x23, 0x00, 0x75, + 0x18, 0x1c, 0x5f, 0x4e, 0x80, 0xac, 0xed, 0x55, 0x9e, 0xde, 0x06, 0x1c}, + {0xe2, 0xc4, 0x3e, 0xa3, 0xd6, 0x7a, 0x0f, 0x99, 0x8e, 0xe0, 0x2e, 0xbe, 0x38, 0xf9, + 0x08, 0x66, 0x15, 0x45, 0x28, 0x63, 0xc5, 0x43, 0xa1, 0x9c, 0x0d, 0xb6, 0x2d, 0xec, + 0x1f, 0x8a, 0xf3, 0x4c, 0xaa, 0x69, 0x6d, 0xff, 0x40, 0x2b, 0xd5, 0xff, 0xbb, 0x49, + 0x40, 0xdc, 0x18, 0x0b, 0x53, 0x34, 0x97, 0x98, 0x4d, 0xa3, 0x2f, 0x5c, 0x4a, 0x5e, + 0x2d, 0xba, 0x32, 0x7d, 0x8e, 0x6f, 0x09, 0x78, 0xe7, 0x5c, 0xfa, 0x0d, 0x65, 0xaa, + 0xaa, 0xa0, 0x8c, 0x47, 0xb5, 0x48, 0x2a, 0x9e, 0xc4, 0xf9, 0x5b, 0x72, 0x03, 0x70, + 0x7d, 0xcc, 0x09, 0x4f, 0xbe, 0x1a, 0x09, 0x26, 0x3a, 0xad, 0x3c, 0x37}, + {0x7c, 0xf5, 0xc9, 0x82, 0x4d, 0x63, 0x94, 0xb2, 0x36, 0x45, 0x93, 0x24, 0xe1, 0xfd, + 0xcb, 0x1f, 0x5a, 0xdb, 0x8c, 0x41, 0xb3, 0x4d, 0x9c, 0x9e, 0xfc, 0x19, 0x44, 0x45, + 0xd9, 0xf3, 0x40, 0x00, 0xad, 0xbb, 0xdd, 0x89, 0xfb, 0xa8, 0xbe, 0xf1, 0xcb, 0xae, + 0xae, 0x61, 0xbc, 0x2c, 0xcb, 0x3b, 0x9d, 0x8d, 0x9b, 0x1f, 0xbb, 0xa7, 0x58, 0x8f, + 0x86, 0xa6, 0x12, 0x51, 0xda, 0x7e, 0x54, 0x21, 0xd3, 0x86, 0x59, 0xfd, 0x39, 0xe9, + 0xfd, 0xde, 0x0c, 0x38, 0x0a, 0x51, 0x89, 0x2c, 0x27, 0xf4, 0xb9, 0x19, 0x31, 0xbb, + 0x07, 0xa4, 0x2b, 0xb7, 0xf4, 0x4d, 0x25, 0x4a, 0x33, 0x0a, 0x55, 0x63}, + {0x37, 0xcf, 0x69, 0xb5, 0xed, 0xd6, 0x07, 0x65, 0xe1, 0x2e, 0xa5, 0x0c, 0xb0, 0x29, + 0x84, 0x17, 0x5d, 0xd6, 0x6b, 0xeb, 0x90, 0x00, 0x7c, 0xea, 0x51, 0x8f, 0xf7, 0xda, + 0xc7, 0x62, 0xea, 0x3e, 0x49, 0x7b, 0x54, 0x72, 0x45, 0x58, 0xba, 0x9b, 0xe0, 0x08, + 0xc4, 0xe2, 0xfa, 0xc6, 0x05, 0xf3, 0x8d, 0xf1, 0x34, 0xc7, 0x69, 0xfa, 0xe8, 0x60, + 0x7a, 0x76, 0x7d, 0xaa, 0xaf, 0x2b, 0xa9, 0x39, 0x4e, 0x27, 0x93, 0xe6, 0x13, 0xc7, + 0x24, 0x9d, 0x75, 0xd3, 0xdb, 0x68, 0x77, 0x85, 0x63, 0x5f, 0x9a, 0xb3, 0x8a, 0xeb, + 0x60, 0x55, 0x52, 0x70, 0xcd, 0xc4, 0xc9, 0x65, 0x06, 0x6a, 0x43, 0x68}, + {0x27, 0x3f, 0x2f, 0x20, 0xe8, 0x35, 0x02, 0xbc, 0xb0, 0x75, 0xf9, 0x64, 0xe2, 0x00, + 0x5c, 0xc7, 0x16, 0x24, 0x8c, 0xa3, 0xd5, 0xe9, 0xa4, 0x91, 0xf9, 0x89, 0xb7, 0x8a, + 0xf6, 0xe7, 0xb6, 0x17, 0x7c, 0x10, 0x20, 0xe8, 0x17, 0xd3, 0x56, 0x1e, 0x65, 0xe9, + 0x0a, 0x84, 0x44, 0x68, 0x26, 0xc5, 0x7a, 0xfc, 0x0f, 0x32, 0xc6, 0xa1, 0xe0, 0xc1, + 0x72, 0x14, 0x61, 0x91, 0x9c, 0x66, 0x73, 0x53, 0x57, 0x52, 0x0e, 0x9a, 0xab, 0x14, + 0x28, 0x5d, 0xfc, 0xb3, 0xca, 0xc9, 0x84, 0x20, 0x8f, 0x90, 0xca, 0x1e, 0x2d, 0x5b, + 0x88, 0xf5, 0xca, 0xaf, 0x11, 0x7d, 0xf8, 0x78, 0xa6, 0xb5, 0xb4, 0x1c}, + {0x6c, 0xfc, 0x4a, 0x39, 0x6b, 0xc0, 0x64, 0xb6, 0xb1, 0x5f, 0xda, 0x98, 0x24, 0xde, + 0x88, 0x0c, 0x34, 0xd8, 0xca, 0x4b, 0x16, 0x03, 0x8d, 0x4f, 0xa2, 0x34, 0x74, 0xde, + 0x78, 0xca, 0x0b, 0x33, 0xe7, 0x07, 0xa0, 0xa2, 0x62, 0xaa, 0x74, 0x6b, 0xb1, 0xc7, + 0x71, 0xf0, 0xb0, 0xe0, 0x11, 0xf3, 0x23, 0xe2, 0x0b, 0x00, 0x38, 0xe4, 0x07, 0x57, + 0xac, 0x6e, 0xef, 0x82, 0x2d, 0xfd, 0xc0, 0x2d, 0x4e, 0x74, 0x19, 0x11, 0x84, 0xff, + 0x2e, 0x98, 0x24, 0x47, 0x07, 0x2b, 0x96, 0x5e, 0x69, 0xf9, 0xfb, 0x53, 0xc9, 0xbf, + 0x4f, 0xc1, 0x8a, 0xc5, 0xf5, 0x1c, 0x9f, 0x36, 0x1b, 0xbe, 0x31, 0x3c}, + {0xee, 0x8a, 0x94, 0x08, 0x4d, 0x86, 0xf4, 0xb0, 0x6f, 0x1c, 0xba, 0x91, 0xee, 0x19, + 0xdc, 0x07, 0x58, 0xa1, 0xac, 0xa6, 0xae, 0xcd, 0x75, 0x79, 0xbb, 0xd4, 0x62, 0x42, + 0x13, 0x61, 0x0b, 0x33, 0x72, 0x42, 0xcb, 0xf9, 0x93, 0xbc, 0x68, 0xc1, 0x98, 0xdb, + 0xce, 0xc7, 0x1f, 0x71, 0xb8, 0xae, 0x7a, 0x8d, 0xac, 0x34, 0xaa, 0x52, 0x0e, 0x7f, + 0xbb, 0x55, 0x7d, 0x7e, 0x09, 0xc1, 0xce, 0x41, 0x8a, 0x80, 0x6d, 0xa2, 0xd7, 0x19, + 0x96, 0xf7, 0x6d, 0x15, 0x9e, 0x1d, 0x9e, 0xd4, 0x1f, 0xbb, 0x27, 0xdf, 0xa1, 0xdb, + 0x6c, 0xc3, 0xd7, 0x73, 0x7d, 0x77, 0x28, 0x1f, 0xd9, 0x4c, 0xb4, 0x26}, + {0x75, 0x74, 0x38, 0x8f, 0x47, 0x48, 0xf0, 0x51, 0x3c, 0xcb, 0xbe, 0x9c, 0xf4, 0xbc, + 0x5d, 0xb2, 0x55, 0x20, 0x9f, 0xd9, 0x44, 0x12, 0xab, 0x9a, 0xd6, 0xa5, 0x10, 0x1c, + 0x6c, 0x9e, 0x70, 0x2c, 0x83, 0x03, 0x73, 0x62, 0x93, 0xf2, 0xb7, 0xe1, 0x2c, 0x8a, + 0xca, 0xeb, 0xff, 0x79, 0x52, 0x4b, 0x14, 0x13, 0xd4, 0xbf, 0x8a, 0x77, 0xfc, 0xda, + 0x0f, 0x61, 0x72, 0x9c, 0x14, 0x10, 0xeb, 0x7d, 0x7a, 0xee, 0x66, 0x87, 0x6a, 0xaf, + 0x62, 0xcb, 0x0e, 0xcd, 0x53, 0x55, 0x04, 0xec, 0xcb, 0x66, 0xb5, 0xe4, 0x0b, 0x0f, + 0x38, 0x01, 0x80, 0x58, 0xea, 0xe2, 0x2c, 0xf6, 0x9f, 0x8e, 0xe6, 0x08}, + {0xad, 0x30, 0xc1, 0x4b, 0x0a, 0x50, 0xad, 0x34, 0x9c, 0xd4, 0x0b, 0x3d, 0x49, 0xdb, + 0x38, 0x8d, 0xbe, 0x89, 0x0a, 0x50, 0x98, 0x3d, 0x5c, 0xa2, 0x09, 0x3b, 0xba, 0xee, + 0x87, 0x3f, 0x1f, 0x2f, 0xf9, 0xf2, 0xb8, 0x0a, 0xd5, 0x09, 0x2d, 0x2f, 0xdf, 0x23, + 0x59, 0xc5, 0x8d, 0x21, 0xb9, 0xac, 0xb9, 0x6c, 0x76, 0x73, 0x26, 0x34, 0x8f, 0x4a, + 0xf5, 0x19, 0xf7, 0x38, 0xd7, 0x3b, 0xb1, 0x4c, 0x4a, 0xb6, 0x15, 0xe5, 0x75, 0x8c, + 0x84, 0xf7, 0x38, 0x90, 0x4a, 0xdb, 0xba, 0x01, 0x95, 0xa5, 0x50, 0x1b, 0x75, 0x3f, + 0x3f, 0x31, 0x0d, 0xc2, 0xe8, 0x2e, 0xae, 0xc0, 0x53, 0xe3, 0xa1, 0x19}, + {0xc3, 0x05, 0xfa, 0xba, 0x60, 0x75, 0x1c, 0x7d, 0x61, 0x5e, 0xe5, 0xc6, 0xa0, 0xa0, + 0xe1, 0xb3, 0x73, 0x64, 0xd6, 0xc0, 0x18, 0x97, 0x52, 0xe3, 0x86, 0x34, 0x0c, 0xc2, + 0x11, 0x6b, 0x54, 0x41, 0xbd, 0xbd, 0x96, 0xd5, 0xcd, 0x72, 0x21, 0xb4, 0x40, 0xfc, + 0xee, 0x98, 0x43, 0x45, 0xe0, 0x93, 0xb5, 0x09, 0x41, 0xb4, 0x47, 0x53, 0xb1, 0x9f, + 0x34, 0xae, 0x66, 0x02, 0x99, 0xd3, 0x6b, 0x73, 0xb4, 0xb3, 0x34, 0x93, 0x50, 0x2d, + 0x53, 0x85, 0x73, 0x65, 0x81, 0x60, 0x4b, 0x11, 0xfd, 0x46, 0x75, 0x83, 0x5c, 0x42, + 0x30, 0x5f, 0x5f, 0xcc, 0x5c, 0xab, 0x7f, 0xb8, 0xa2, 0x95, 0x22, 0x41}, + {0xe9, 0xd6, 0x7e, 0xf5, 0x88, 0x9b, 0xc9, 0x19, 0x25, 0xc8, 0xf8, 0x6d, 0x26, 0xcb, + 0x93, 0x53, 0x73, 0xd2, 0x0a, 0xb3, 0x13, 0x32, 0xee, 0x5c, 0x34, 0x2e, 0x2d, 0xb5, + 0xeb, 0x53, 0xe1, 0x14, 0xc6, 0xea, 0x93, 0xe2, 0x61, 0x52, 0x65, 0x2e, 0xdb, 0xac, + 0x33, 0x21, 0x03, 0x92, 0x5a, 0x84, 0x6b, 0x99, 0x00, 0x79, 0xcb, 0x75, 0x09, 0x46, + 0x80, 0xdd, 0x5a, 0x19, 0x8d, 0xbb, 0x60, 0x07, 0x8a, 0x81, 0xe6, 0xcd, 0x17, 0x1a, + 0x3e, 0x41, 0x84, 0xa0, 0x69, 0xed, 0xa9, 0x6d, 0x15, 0x57, 0xb1, 0xcc, 0xca, 0x46, + 0x8f, 0x26, 0xbf, 0x2c, 0xf2, 0xc5, 0x3a, 0xc3, 0x9b, 0xbe, 0x34, 0x6b}, + {0xb2, 0xc0, 0x78, 0x3a, 0x64, 0x2f, 0xdf, 0xf3, 0x7c, 0x02, 0x2e, 0xf2, 0x1e, 0x97, + 0x3e, 0x4c, 0xa3, 0xb5, 0xc1, 0x49, 0x5e, 0x1c, 0x7d, 0xec, 0x2d, 0xdd, 0x22, 0x09, + 0x8f, 0xc1, 0x12, 0x20, 0xd3, 0xf2, 0x71, 0x65, 0x65, 0x69, 0xfc, 0x11, 0x7a, 0x73, + 0x0e, 0x53, 0x45, 0xe8, 0xc9, 0xc6, 0x35, 0x50, 0xfe, 0xd4, 0xa2, 0xe7, 0x3a, 0xe3, + 0x0b, 0xd3, 0x6d, 0x2e, 0xb6, 0xc7, 0xb9, 0x01, 0x29, 0x9d, 0xc8, 0x5a, 0xe5, 0x55, + 0x0b, 0x88, 0x63, 0xa7, 0xa0, 0x45, 0x1f, 0x24, 0x83, 0x14, 0x1f, 0x6c, 0xe7, 0xc2, + 0xdf, 0xef, 0x36, 0x3d, 0xe8, 0xad, 0x4b, 0x4e, 0x78, 0x5b, 0xaf, 0x08}, + {0x33, 0x25, 0x1f, 0x88, 0xdc, 0x99, 0x34, 0x28, 0xb6, 0x23, 0x93, 0x77, 0xda, 0x25, + 0x05, 0x9d, 0xf4, 0x41, 0x34, 0x67, 0xfb, 0xdd, 0x7a, 0x89, 0x8d, 0x16, 0x3a, 0x16, + 0x71, 0x9d, 0xb7, 0x32, 0x4b, 0x2c, 0xcc, 0x89, 0xd2, 0x14, 0x73, 0xe2, 0x8d, 0x17, + 0x87, 0xa2, 0x11, 0xbd, 0xe4, 0x4b, 0xce, 0x64, 0x33, 0xfa, 0xd6, 0x28, 0xd5, 0x18, + 0x6e, 0x82, 0xd9, 0xaf, 0xd5, 0xc1, 0x23, 0x64, 0x6a, 0xb3, 0xfc, 0xed, 0xd9, 0xf8, + 0x85, 0xcc, 0xf9, 0xe5, 0x46, 0x37, 0x8f, 0xc2, 0xbc, 0x22, 0xcd, 0xd3, 0xe5, 0xf9, + 0x38, 0xe3, 0x9d, 0xe4, 0xcc, 0x2d, 0x3e, 0xc1, 0xfb, 0x5e, 0x0a, 0x48}, + {0x71, 0x20, 0x62, 0x01, 0x0b, 0xe7, 0x51, 0x0b, 0xc5, 0xaf, 0x1d, 0x8b, 0xcf, 0x05, + 0xb5, 0x06, 0xcd, 0xab, 0x5a, 0xef, 0x61, 0xb0, 0x6b, 0x2c, 0x31, 0xbf, 0xb7, 0x0c, + 0x60, 0x27, 0xaa, 0x47, 0x1f, 0x22, 0xce, 0x42, 0xe4, 0x4c, 0x61, 0xb6, 0x28, 0x39, + 0x05, 0x4c, 0xcc, 0x9d, 0x19, 0x6e, 0x03, 0xbe, 0x1c, 0xdc, 0xa4, 0xb4, 0x3f, 0x66, + 0x06, 0x8e, 0x1c, 0x69, 0x47, 0x1d, 0xb3, 0x24, 0xc3, 0xf8, 0x15, 0xc0, 0xed, 0x1e, + 0x54, 0x2a, 0x7c, 0x3f, 0x69, 0x7c, 0x7e, 0xfe, 0xa4, 0x11, 0xd6, 0x78, 0xa2, 0x4e, + 0x13, 0x66, 0xaf, 0xf0, 0x94, 0xa0, 0xdd, 0x14, 0x5d, 0x58, 0x5b, 0x54}, + {0x0f, 0x3a, 0xd4, 0xa0, 0x5e, 0x27, 0xbf, 0x67, 0xbe, 0xee, 0x9b, 0x08, 0x34, 0x8e, + 0xe6, 0xad, 0x2e, 0xe7, 0x79, 0xd4, 0x4c, 0x13, 0x89, 0x42, 0x54, 0x54, 0xba, 0x32, + 0xc3, 0xf9, 0x62, 0x0f, 0xe1, 0x21, 0xb3, 0xe3, 0xd0, 0xe4, 0x04, 0x62, 0x95, 0x1e, + 0xff, 0x28, 0x7a, 0x63, 0xaa, 0x3b, 0x9e, 0xbd, 0x99, 0x5b, 0xfd, 0xcf, 0x0c, 0x0b, + 0x71, 0xd0, 0xc8, 0x64, 0x3e, 0xdc, 0x22, 0x4d, 0x39, 0x5f, 0x3b, 0xd6, 0x89, 0x65, + 0xb4, 0xfc, 0x61, 0xcf, 0xcb, 0x57, 0x3f, 0x6a, 0xae, 0x5c, 0x05, 0xfa, 0x3a, 0x95, + 0xd2, 0xc2, 0xba, 0xfe, 0x36, 0x14, 0x37, 0x36, 0x1a, 0xa0, 0x0f, 0x1c}, + {0xff, 0x3d, 0x94, 0x22, 0xb6, 0x04, 0xc6, 0xd2, 0xa0, 0xb3, 0xcf, 0x44, 0xce, 0xbe, + 0x8c, 0xbc, 0x78, 0x86, 0x80, 0x97, 0xf3, 0x4f, 0x25, 0x5d, 0xbf, 0xa6, 0x1c, 0x3b, + 0x4f, 0x61, 0xa3, 0x0f, 0x50, 0x6a, 0x93, 0x8c, 0x0e, 0x2b, 0x08, 0x69, 0xb6, 0xc5, + 0xda, 0xc1, 0x35, 0xa0, 0xc9, 0xf9, 0x34, 0xb6, 0xdf, 0xc4, 0x54, 0x3e, 0xb7, 0x6f, + 0x40, 0xc1, 0x2b, 0x1d, 0x9b, 0x41, 0x05, 0x40, 0xf0, 0x82, 0xbe, 0xb9, 0xbd, 0xfe, + 0x03, 0xa0, 0x90, 0xac, 0x44, 0x3a, 0xaf, 0xc1, 0x89, 0x20, 0x8e, 0xfa, 0x54, 0x19, + 0x91, 0x9f, 0x49, 0xf8, 0x42, 0xab, 0x40, 0xef, 0x8a, 0x21, 0xba, 0x1f}, + {0x3e, 0xf5, 0xc8, 0xfa, 0x48, 0x94, 0x54, 0xab, 0x41, 0x37, 0xa6, 0x7b, 0x9a, 0xe8, + 0xf6, 0x81, 0x01, 0x5e, 0x2b, 0x6c, 0x7d, 0x6c, 0xfd, 0x74, 0x42, 0x6e, 0xc8, 0xa8, + 0xca, 0x3a, 0x2e, 0x39, 0x94, 0x01, 0x7b, 0x3e, 0x04, 0x57, 0x3e, 0x4f, 0x7f, 0xaf, + 0xda, 0x08, 0xee, 0x3e, 0x1d, 0xa8, 0xf1, 0xde, 0xdc, 0x99, 0xab, 0xc6, 0x39, 0xc8, + 0xd5, 0x61, 0x77, 0xff, 0x13, 0x5d, 0x53, 0x6c, 0xaf, 0x35, 0x8a, 0x3e, 0xe9, 0x34, + 0xbd, 0x4c, 0x16, 0xe8, 0x87, 0x58, 0x44, 0x81, 0x07, 0x2e, 0xab, 0xb0, 0x9a, 0xf2, + 0x76, 0x9c, 0x31, 0x19, 0x3b, 0xc1, 0x0a, 0xd5, 0xe4, 0x7f, 0xe1, 0x25}, + {0x76, 0xf6, 0x04, 0x1e, 0xd7, 0x9b, 0x28, 0x0a, 0x95, 0x0f, 0x42, 0xd6, 0x52, 0x1c, + 0x8e, 0x20, 0xab, 0x1f, 0x69, 0x34, 0xb0, 0xd8, 0x86, 0x51, 0x51, 0xb3, 0x9f, 0x2a, + 0x44, 0x51, 0x57, 0x25, 0xa7, 0x21, 0xf1, 0x76, 0xf5, 0x7f, 0x5f, 0x91, 0xe3, 0x87, + 0xcd, 0x2f, 0x27, 0x32, 0x4a, 0xc3, 0x26, 0xe5, 0x1b, 0x4d, 0xde, 0x2f, 0xba, 0xcc, + 0x9b, 0x89, 0x69, 0x89, 0x8f, 0x82, 0xba, 0x6b, 0x01, 0x39, 0xfe, 0x90, 0x66, 0xbc, + 0xd1, 0xe2, 0xd5, 0x7a, 0x99, 0xa0, 0x18, 0x4a, 0xb5, 0x4c, 0xd4, 0x60, 0x84, 0xaf, + 0x14, 0x69, 0x1d, 0x97, 0xe4, 0x7b, 0x6b, 0x7f, 0x4f, 0x50, 0x9d, 0x55}, + {0xd5, 0x54, 0xeb, 0xb3, 0x78, 0x83, 0x73, 0xa7, 0x7c, 0x3c, 0x55, 0xa5, 0x66, 0xd3, + 0x69, 0x1d, 0xba, 0x00, 0x28, 0xf9, 0x62, 0xcf, 0x26, 0x0a, 0x17, 0x32, 0x7e, 0x80, + 0xd5, 0x12, 0xab, 0x01, 0xfd, 0x66, 0xd2, 0xf6, 0xe7, 0x91, 0x48, 0x9c, 0x1b, 0x78, + 0x07, 0x03, 0x9b, 0xa1, 0x44, 0x07, 0x3b, 0xe2, 0x61, 0x60, 0x1d, 0x8f, 0x38, 0x88, + 0x0e, 0xd5, 0x4b, 0x35, 0xa3, 0xa6, 0x3e, 0x12, 0x96, 0x2d, 0xe3, 0x41, 0x90, 0x18, + 0x8d, 0x11, 0x48, 0x58, 0x31, 0xd8, 0xc2, 0xe3, 0xed, 0xb9, 0xd9, 0x45, 0x32, 0xd8, + 0x71, 0x42, 0xab, 0x1e, 0x54, 0xa1, 0x18, 0xc9, 0xe2, 0x61, 0x39, 0x4a}, + {0xa0, 0xbb, 0xe6, 0xf8, 0xe0, 0x3b, 0xdc, 0x71, 0x0a, 0xe3, 0xff, 0x7e, 0x34, 0xf8, + 0xce, 0xd6, 0x6a, 0x47, 0x3a, 0xe1, 0x5f, 0x42, 0x92, 0xa9, 0x63, 0xb7, 0x1d, 0xfb, + 0xe3, 0xbc, 0xd6, 0x2c, 0x1e, 0x3f, 0x23, 0xf3, 0x44, 0xd6, 0x27, 0x03, 0x16, 0xf0, + 0xfc, 0x34, 0x0e, 0x26, 0x9a, 0x49, 0x79, 0xb9, 0xda, 0xf2, 0x16, 0xa7, 0xb5, 0x83, + 0x1f, 0x11, 0xd4, 0x9b, 0xad, 0xee, 0xac, 0x68, 0x10, 0xc2, 0xd7, 0xf3, 0x0e, 0xc9, + 0xb4, 0x38, 0x0c, 0x04, 0xad, 0xb7, 0x24, 0x6e, 0x8e, 0x30, 0x23, 0x3e, 0xe7, 0xb7, + 0xf1, 0xd9, 0x60, 0x38, 0x97, 0xf5, 0x08, 0xb5, 0xd5, 0x60, 0x57, 0x59}, + {0x97, 0x63, 0xaa, 0x04, 0xe1, 0xbf, 0x29, 0x61, 0xcb, 0xfc, 0xa7, 0xa4, 0x08, 0x00, + 0x96, 0x8f, 0x58, 0x94, 0x90, 0x7d, 0x89, 0xc0, 0x8b, 0x3f, 0xa9, 0x91, 0xb2, 0xdc, + 0x3e, 0xa4, 0x9f, 0x70, 0x90, 0x27, 0x02, 0xfd, 0xeb, 0xcb, 0x2a, 0x88, 0x60, 0x57, + 0x11, 0xc4, 0x05, 0x33, 0xaf, 0x89, 0xf4, 0x73, 0x34, 0x7d, 0xe3, 0x92, 0xf4, 0x65, + 0x2b, 0x5a, 0x51, 0x54, 0xdf, 0xc5, 0xb2, 0x2c, 0xca, 0x2a, 0xfd, 0x63, 0x8c, 0x5d, + 0x0a, 0xeb, 0xff, 0x4e, 0x69, 0x2e, 0x66, 0xc1, 0x2b, 0xd2, 0x3a, 0xb0, 0xcb, 0xf8, + 0x6e, 0xf3, 0x23, 0x27, 0x1f, 0x13, 0xc8, 0xf0, 0xec, 0x29, 0xf0, 0x70}, + {0x33, 0x3e, 0xed, 0x2e, 0xb3, 0x07, 0x13, 0x46, 0xe7, 0x81, 0x55, 0xa4, 0x33, 0x2f, + 0x04, 0xae, 0x66, 0x03, 0x5f, 0x19, 0xd3, 0x49, 0x44, 0xc9, 0x58, 0x48, 0x31, 0x6c, + 0x8a, 0x5d, 0x7d, 0x0b, 0xb9, 0xb0, 0x10, 0x5e, 0xaa, 0xaf, 0x6a, 0x2a, 0xa9, 0x1a, + 0x04, 0xef, 0x70, 0xa3, 0xf0, 0x78, 0x1f, 0xd6, 0x3a, 0xaa, 0x77, 0xfb, 0x3e, 0x77, + 0xe1, 0xd9, 0x4b, 0xa7, 0xa2, 0xa5, 0xec, 0x44, 0x43, 0xd5, 0x95, 0x7b, 0x32, 0x48, + 0xd4, 0x25, 0x1d, 0x0f, 0x34, 0xa3, 0x00, 0x83, 0xd3, 0x70, 0x2b, 0xc5, 0xe1, 0x60, + 0x1c, 0x53, 0x1c, 0xde, 0xe4, 0xe9, 0x7d, 0x2c, 0x51, 0x24, 0x22, 0x27}, + {0x2e, 0x34, 0xc5, 0x49, 0xaf, 0x92, 0xbc, 0x1a, 0xd0, 0xfa, 0xe6, 0xb2, 0x11, 0xd8, + 0xee, 0xff, 0x29, 0x4e, 0xc8, 0xfc, 0x8d, 0x8c, 0xa2, 0xef, 0x43, 0xc5, 0x4c, 0xa4, + 0x18, 0xdf, 0xb5, 0x11, 0xfc, 0x75, 0xa9, 0x42, 0x8a, 0xbb, 0x7b, 0xbf, 0x58, 0xa3, + 0xad, 0x96, 0x77, 0x39, 0x5c, 0x8c, 0x48, 0xaa, 0xed, 0xcd, 0x6f, 0xc7, 0x7f, 0xe2, + 0xa6, 0x20, 0xbc, 0xf6, 0xd7, 0x5f, 0x73, 0x19, 0x66, 0x42, 0xc8, 0x42, 0xd0, 0x90, + 0xab, 0xe3, 0x7e, 0x54, 0x19, 0x7f, 0x0f, 0x8e, 0x84, 0xeb, 0xb9, 0x97, 0xa4, 0x65, + 0xd0, 0xa1, 0x03, 0x25, 0x5f, 0x89, 0xdf, 0x91, 0x11, 0x91, 0xef, 0x0f}}; diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.h new file mode 100644 index 000000000..9c0cdfc0c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_basepoint_table.h @@ -0,0 +1,2 @@ +/* multiples of the base point in packed {ysubx, xaddy, t2d} form */ +extern const uint8_t ALIGN(16) ge25519_niels_base_multiples[256][96]; diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.c new file mode 100644 index 000000000..778f24578 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.c @@ -0,0 +1,829 @@ +#include +#include "ed25519_donna.h" +#include "../memzero.h" + +/* sqrt(x) is such an integer y that 0 <= y <= p - 1, y % 2 = 0, and y^2 = x (mod p). */ +/* d = -121665 / 121666 */ +#if !defined(NDEBUG) +static const bignum25519 ALIGN(16) fe_d = { + 0x35978a3, + 0x0d37284, + 0x3156ebd, + 0x06a0a0e, + 0x001c029, + 0x179e898, + 0x3a03cbb, + 0x1ce7198, + 0x2e2b6ff, + 0x1480db3}; /* d */ +#endif +static const bignum25519 ALIGN(16) fe_sqrtm1 = { + 0x20ea0b0, + 0x186c9d2, + 0x08f189d, + 0x035697f, + 0x0bd0c60, + 0x1fbd7a7, + 0x2804c9e, + 0x1e16569, + 0x004fc1d, + 0x0ae0c92}; /* sqrt(-1) */ +//static const bignum25519 ALIGN(16) fe_d2 = { +// 0x2b2f159, 0x1a6e509, 0x22add7a, 0x0d4141d, 0x0038052, 0x0f3d130, 0x3407977, 0x19ce331, 0x1c56dff, 0x0901b67}; /* 2 * d */ + +/* A = 2 * (1 - d) / (1 + d) = 486662 */ +static const bignum25519 ALIGN(16) fe_ma2 = { + 0x33de3c9, + 0x1fff236, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff}; /* -A^2 */ +static const bignum25519 ALIGN(16) fe_ma = { + 0x3f892e7, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff, + 0x3ffffff, + 0x1ffffff}; /* -A */ +static const bignum25519 ALIGN(16) fe_fffb1 = { + 0x1e3bdff, + 0x025a2b3, + 0x18e5bab, + 0x0ba36ac, + 0x0b9afed, + 0x004e61c, + 0x31d645f, + 0x09d1bea, + 0x102529e, + 0x0063810}; /* sqrt(-2 * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb2 = { + 0x383650d, + 0x066df27, + 0x10405a4, + 0x1cfdd48, + 0x2b887f2, + 0x1e9a041, + 0x1d7241f, + 0x0612dc5, + 0x35fba5d, + 0x0cbe787}; /* sqrt(2 * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb3 = { + 0x0cfd387, + 0x1209e3a, + 0x3bad4fc, + 0x18ad34d, + 0x2ff6c02, + 0x0f25d12, + 0x15cdfe0, + 0x0e208ed, + 0x32eb3df, + 0x062d7bb}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ +static const bignum25519 ALIGN(16) fe_fffb4 = { + 0x2b39186, + 0x14640ed, + 0x14930a7, + 0x04509fa, + 0x3b91bf0, + 0x0f7432e, + 0x07a443f, + 0x17f24d8, + 0x031067d, + 0x0690fcc}; /* sqrt(sqrt(-1) * A * (A + 2)) */ + +/* + Timing safe memory compare +*/ +int ed25519_verify(const unsigned char* x, const unsigned char* y, size_t len) { + size_t differentbits = 0; + while(len--) differentbits |= (*x++ ^ *y++); + return (int)(1 & ((differentbits - 1) >> 8)); +} + +/* + conversions +*/ + +void ge25519_p1p1_to_partial(ge25519* r, const ge25519_p1p1* p) { + curve25519_mul(r->x, p->x, p->t); + curve25519_mul(r->y, p->y, p->z); + curve25519_mul(r->z, p->z, p->t); +} + +void ge25519_p1p1_to_full(ge25519* r, const ge25519_p1p1* p) { + curve25519_mul(r->x, p->x, p->t); + curve25519_mul(r->y, p->y, p->z); + curve25519_mul(r->z, p->z, p->t); + curve25519_mul(r->t, p->x, p->y); +} + +void ge25519_full_to_pniels(ge25519_pniels* p, const ge25519* r) { + curve25519_sub(p->ysubx, r->y, r->x); + curve25519_add(p->xaddy, r->y, r->x); + curve25519_copy(p->z, r->z); + curve25519_mul(p->t2d, r->t, ge25519_ec2d); +} + +/* + adding & doubling +*/ + +void ge25519_double_p1p1(ge25519_p1p1* r, const ge25519* p) { + bignum25519 a = {0}, b = {0}, c = {0}; + + curve25519_square(a, p->x); + curve25519_square(b, p->y); + curve25519_square(c, p->z); + curve25519_add_reduce(c, c, c); + curve25519_add(r->x, p->x, p->y); + curve25519_square(r->x, r->x); + curve25519_add(r->y, b, a); + curve25519_sub(r->z, b, a); + curve25519_sub_after_basic(r->x, r->x, r->y); + curve25519_sub_after_basic(r->t, c, r->z); +} + +#ifndef ED25519_NO_PRECOMP +void ge25519_nielsadd2_p1p1( + ge25519_p1p1* r, + const ge25519* p, + const ge25519_niels* q, + unsigned char signbit) { + const bignum25519* qb = (const bignum25519*)q; + bignum25519* rb = (bignum25519*)r; + bignum25519 a = {0}, b = {0}, c = {0}; + + curve25519_sub(a, p->y, p->x); + curve25519_add(b, p->y, p->x); + curve25519_mul(a, a, qb[signbit]); /* x for +, y for - */ + curve25519_mul(r->x, b, qb[signbit ^ 1]); /* y for +, x for - */ + curve25519_add(r->y, r->x, a); + curve25519_sub(r->x, r->x, a); + curve25519_mul(c, p->t, q->t2d); + curve25519_add_reduce(r->t, p->z, p->z); + curve25519_copy(r->z, r->t); + curve25519_add(rb[2 + signbit], rb[2 + signbit], c); /* z for +, t for - */ + curve25519_sub(rb[2 + (signbit ^ 1)], rb[2 + (signbit ^ 1)], c); /* t for +, z for - */ +} +#endif + +void ge25519_pnielsadd_p1p1( + ge25519_p1p1* r, + const ge25519* p, + const ge25519_pniels* q, + unsigned char signbit) { + const bignum25519* qb = (const bignum25519*)q; + bignum25519* rb = (bignum25519*)r; + bignum25519 a = {0}, b = {0}, c = {0}; + + curve25519_sub(a, p->y, p->x); + curve25519_add(b, p->y, p->x); + curve25519_mul(a, a, qb[signbit]); /* ysubx for +, xaddy for - */ + curve25519_mul(r->x, b, qb[signbit ^ 1]); /* xaddy for +, ysubx for - */ + curve25519_add(r->y, r->x, a); + curve25519_sub(r->x, r->x, a); + curve25519_mul(c, p->t, q->t2d); + curve25519_mul(r->t, p->z, q->z); + curve25519_add_reduce(r->t, r->t, r->t); + curve25519_copy(r->z, r->t); + curve25519_add(rb[2 + signbit], rb[2 + signbit], c); /* z for +, t for - */ + curve25519_sub(rb[2 + (signbit ^ 1)], rb[2 + (signbit ^ 1)], c); /* t for +, z for - */ +} + +void ge25519_double_partial(ge25519* r, const ge25519* p) { + ge25519_p1p1 t = {0}; + ge25519_double_p1p1(&t, p); + ge25519_p1p1_to_partial(r, &t); +} + +void ge25519_double(ge25519* r, const ge25519* p) { + ge25519_p1p1 t = {0}; + ge25519_double_p1p1(&t, p); + ge25519_p1p1_to_full(r, &t); +} + +void ge25519_nielsadd2(ge25519* r, const ge25519_niels* q) { + bignum25519 a = {0}, b = {0}, c = {0}, e = {0}, f = {0}, g = {0}, h = {0}; + + curve25519_sub(a, r->y, r->x); + curve25519_add(b, r->y, r->x); + curve25519_mul(a, a, q->ysubx); + curve25519_mul(e, b, q->xaddy); + curve25519_add(h, e, a); + curve25519_sub(e, e, a); + curve25519_mul(c, r->t, q->t2d); + curve25519_add(f, r->z, r->z); + curve25519_add_after_basic(g, f, c); + curve25519_sub_after_basic(f, f, c); + curve25519_mul(r->x, e, f); + curve25519_mul(r->y, h, g); + curve25519_mul(r->z, g, f); + curve25519_mul(r->t, e, h); +} + +void ge25519_pnielsadd(ge25519_pniels* r, const ge25519* p, const ge25519_pniels* q) { + bignum25519 a = {0}, b = {0}, c = {0}, x = {0}, y = {0}, z = {0}, t = {0}; + + curve25519_sub(a, p->y, p->x); + curve25519_add(b, p->y, p->x); + curve25519_mul(a, a, q->ysubx); + curve25519_mul(x, b, q->xaddy); + curve25519_add(y, x, a); + curve25519_sub(x, x, a); + curve25519_mul(c, p->t, q->t2d); + curve25519_mul(t, p->z, q->z); + curve25519_add(t, t, t); + curve25519_add_after_basic(z, t, c); + curve25519_sub_after_basic(t, t, c); + curve25519_mul(r->xaddy, x, t); + curve25519_mul(r->ysubx, y, z); + curve25519_mul(r->z, z, t); + curve25519_mul(r->t2d, x, y); + curve25519_copy(y, r->ysubx); + curve25519_sub(r->ysubx, r->ysubx, r->xaddy); + curve25519_add(r->xaddy, r->xaddy, y); + curve25519_mul(r->t2d, r->t2d, ge25519_ec2d); +} + +/* + pack & unpack +*/ + +void ge25519_pack(unsigned char r[32], const ge25519* p) { + bignum25519 tx = {0}, ty = {0}, zi = {0}; + unsigned char parity[32] = {0}; + curve25519_recip(zi, p->z); + curve25519_mul(tx, p->x, zi); + curve25519_mul(ty, p->y, zi); + curve25519_contract(r, ty); + curve25519_contract(parity, tx); + r[31] ^= ((parity[0] & 1) << 7); +} + +int ge25519_unpack_negative_vartime(ge25519* r, const unsigned char p[32]) { + const unsigned char zero[32] = {0}; + const bignum25519 one = {1}; + unsigned char parity = p[31] >> 7; + unsigned char check[32] = {0}; + bignum25519 t = {0}, root = {0}, num = {0}, den = {0}, d3 = {0}; + + curve25519_expand(r->y, p); + curve25519_copy(r->z, one); + curve25519_square(num, r->y); /* x = y^2 */ + curve25519_mul(den, num, ge25519_ecd); /* den = dy^2 */ + curve25519_sub_reduce(num, num, r->z); /* x = y^1 - 1 */ + curve25519_add(den, den, r->z); /* den = dy^2 + 1 */ + + /* Computation of sqrt(num/den) */ + /* 1.: computation of num^((p-5)/8)*den^((7p-35)/8) = (num*den^7)^((p-5)/8) */ + curve25519_square(t, den); + curve25519_mul(d3, t, den); + curve25519_square(r->x, d3); + curve25519_mul(r->x, r->x, den); + curve25519_mul(r->x, r->x, num); + curve25519_pow_two252m3(r->x, r->x); + + /* 2. computation of r->x = num * den^3 * (num*den^7)^((p-5)/8) */ + curve25519_mul(r->x, r->x, d3); + curve25519_mul(r->x, r->x, num); + + /* 3. Check if either of the roots works: */ + curve25519_square(t, r->x); + curve25519_mul(t, t, den); + curve25519_sub_reduce(root, t, num); + curve25519_contract(check, root); + if(!ed25519_verify(check, zero, 32)) { + curve25519_add_reduce(t, t, num); + curve25519_contract(check, t); + if(!ed25519_verify(check, zero, 32)) return 0; + curve25519_mul(r->x, r->x, ge25519_sqrtneg1); + } + + curve25519_contract(check, r->x); + if((check[0] & 1) == parity) { + curve25519_copy(t, r->x); + curve25519_neg(r->x, t); + } + curve25519_mul(r->t, r->x, r->y); + return 1; +} + +/* + scalarmults +*/ + +void ge25519_set_neutral(ge25519* r) { + memzero(r, sizeof(ge25519)); + r->y[0] = 1; + r->z[0] = 1; +} + +#define S1_SWINDOWSIZE 5 +#define S1_TABLE_SIZE (1 << (S1_SWINDOWSIZE - 2)) +#ifdef ED25519_NO_PRECOMP +#define S2_SWINDOWSIZE 5 +#else +#define S2_SWINDOWSIZE 7 +#endif +#define S2_TABLE_SIZE (1 << (S2_SWINDOWSIZE - 2)) + +/* computes [s1]p1 + [s2]base */ +void ge25519_double_scalarmult_vartime( + ge25519* r, + const ge25519* p1, + const bignum256modm s1, + const bignum256modm s2) { + signed char slide1[256] = {0}, slide2[256] = {0}; + ge25519_pniels pre1[S1_TABLE_SIZE] = {0}; +#ifdef ED25519_NO_PRECOMP + ge25519_pniels pre2[S2_TABLE_SIZE] = {0}; +#endif + ge25519 dp = {0}; + ge25519_p1p1 t = {0}; + int32_t i = 0; + + memzero(&t, sizeof(ge25519_p1p1)); + contract256_slidingwindow_modm(slide1, s1, S1_SWINDOWSIZE); + contract256_slidingwindow_modm(slide2, s2, S2_SWINDOWSIZE); + + ge25519_double(&dp, p1); + ge25519_full_to_pniels(pre1, p1); + for(i = 0; i < S1_TABLE_SIZE - 1; i++) ge25519_pnielsadd(&pre1[i + 1], &dp, &pre1[i]); + +#ifdef ED25519_NO_PRECOMP + ge25519_double(&dp, &ge25519_basepoint); + ge25519_full_to_pniels(pre2, &ge25519_basepoint); + for(i = 0; i < S2_TABLE_SIZE - 1; i++) ge25519_pnielsadd(&pre2[i + 1], &dp, &pre2[i]); +#endif + + ge25519_set_neutral(r); + + i = 255; + while((i >= 0) && !(slide1[i] | slide2[i])) i--; + + for(; i >= 0; i--) { + ge25519_double_p1p1(&t, r); + + if(slide1[i]) { + ge25519_p1p1_to_full(r, &t); + ge25519_pnielsadd_p1p1( + &t, r, &pre1[abs(slide1[i]) / 2], (unsigned char)slide1[i] >> 7); + } + + if(slide2[i]) { + ge25519_p1p1_to_full(r, &t); +#ifdef ED25519_NO_PRECOMP + ge25519_pnielsadd_p1p1( + &t, r, &pre2[abs(slide2[i]) / 2], (unsigned char)slide2[i] >> 7); +#else + ge25519_nielsadd2_p1p1( + &t, + r, + &ge25519_niels_sliding_multiples[abs(slide2[i]) / 2], + (unsigned char)slide2[i] >> 7); +#endif + } + + ge25519_p1p1_to_partial(r, &t); + } + curve25519_mul(r->t, t.x, t.y); + memzero(slide1, sizeof(slide1)); + memzero(slide2, sizeof(slide2)); +} + +/* computes [s1]p1 + [s2]p2 */ +#if USE_MONERO +void ge25519_double_scalarmult_vartime2( + ge25519* r, + const ge25519* p1, + const bignum256modm s1, + const ge25519* p2, + const bignum256modm s2) { + signed char slide1[256] = {0}, slide2[256] = {0}; + ge25519_pniels pre1[S1_TABLE_SIZE] = {0}; + ge25519_pniels pre2[S1_TABLE_SIZE] = {0}; + ge25519 dp = {0}; + ge25519_p1p1 t = {0}; + int32_t i = 0; + + memzero(&t, sizeof(ge25519_p1p1)); + contract256_slidingwindow_modm(slide1, s1, S1_SWINDOWSIZE); + contract256_slidingwindow_modm(slide2, s2, S1_SWINDOWSIZE); + + ge25519_double(&dp, p1); + ge25519_full_to_pniels(pre1, p1); + for(i = 0; i < S1_TABLE_SIZE - 1; i++) ge25519_pnielsadd(&pre1[i + 1], &dp, &pre1[i]); + + ge25519_double(&dp, p2); + ge25519_full_to_pniels(pre2, p2); + for(i = 0; i < S1_TABLE_SIZE - 1; i++) ge25519_pnielsadd(&pre2[i + 1], &dp, &pre2[i]); + + ge25519_set_neutral(r); + + i = 255; + while((i >= 0) && !(slide1[i] | slide2[i])) i--; + + for(; i >= 0; i--) { + ge25519_double_p1p1(&t, r); + + if(slide1[i]) { + ge25519_p1p1_to_full(r, &t); + ge25519_pnielsadd_p1p1( + &t, r, &pre1[abs(slide1[i]) / 2], (unsigned char)slide1[i] >> 7); + } + + if(slide2[i]) { + ge25519_p1p1_to_full(r, &t); + ge25519_pnielsadd_p1p1( + &t, r, &pre2[abs(slide2[i]) / 2], (unsigned char)slide2[i] >> 7); + } + + ge25519_p1p1_to_partial(r, &t); + } + curve25519_mul(r->t, t.x, t.y); + memzero(slide1, sizeof(slide1)); + memzero(slide2, sizeof(slide2)); +} +#endif + +/* + * The following conditional move stuff uses conditional moves. + * I will check on which compilers this works, and provide suitable + * workarounds for those where it doesn't. + * + * This works on gcc 4.x and above with -O3. Don't use -O2, this will + * cause the code to not generate conditional moves. Don't use any -march= + * with less than i686 on x86 + */ +static void ge25519_cmove_stride4(long* r, long* p, long* pos, long* n, int stride) { + long x0 = r[0], x1 = r[1], x2 = r[2], x3 = r[3], y0 = 0, y1 = 0, y2 = 0, y3 = 0; + for(; p < n; p += stride) { + volatile int flag = (p == pos); + y0 = p[0]; + y1 = p[1]; + y2 = p[2]; + y3 = p[3]; + x0 = flag ? y0 : x0; + x1 = flag ? y1 : x1; + x2 = flag ? y2 : x2; + x3 = flag ? y3 : x3; + } + r[0] = x0; + r[1] = x1; + r[2] = x2; + r[3] = x3; +} +#define HAS_CMOVE_STRIDE4 + +static void ge25519_cmove_stride4b(long* r, long* p, long* pos, long* n, int stride) { + long x0 = p[0], x1 = p[1], x2 = p[2], x3 = p[3], y0 = 0, y1 = 0, y2 = 0, y3 = 0; + for(p += stride; p < n; p += stride) { + volatile int flag = (p == pos); + y0 = p[0]; + y1 = p[1]; + y2 = p[2]; + y3 = p[3]; + x0 = flag ? y0 : x0; + x1 = flag ? y1 : x1; + x2 = flag ? y2 : x2; + x3 = flag ? y3 : x3; + } + r[0] = x0; + r[1] = x1; + r[2] = x2; + r[3] = x3; +} +#define HAS_CMOVE_STRIDE4B + +void ge25519_move_conditional_pniels_array( + ge25519_pniels* r, + const ge25519_pniels* p, + int pos, + int n) { +#ifdef HAS_CMOVE_STRIDE4B + size_t i = 0; + for(i = 0; i < sizeof(ge25519_pniels) / sizeof(long); i += 4) { + ge25519_cmove_stride4b( + ((long*)r) + i, + ((long*)p) + i, + ((long*)(p + pos)) + i, + ((long*)(p + n)) + i, + sizeof(ge25519_pniels) / sizeof(long)); + } +#else + size_t i = 0; + for(i = 0; i < n; i++) { + ge25519_move_conditional_pniels(r, p + i, pos == i); + } +#endif +} + +void ge25519_move_conditional_niels_array(ge25519_niels* r, const uint8_t p[8][96], int pos, int n) { + size_t i = 0; + for(i = 0; i < 96 / sizeof(long); i += 4) { + ge25519_cmove_stride4( + ((long*)r) + i, + ((long*)p) + i, + ((long*)(p + pos)) + i, + ((long*)(p + n)) + i, + 96 / sizeof(long)); + } +} + +/* computes [s1]p1, constant time */ +void ge25519_scalarmult(ge25519* r, const ge25519* p1, const bignum256modm s1) { + signed char slide1[64] = {0}; + ge25519_pniels pre1[9] = {0}; + ge25519_pniels pre = {0}; + ge25519 d1 = {0}; + ge25519_p1p1 t = {0}; + int32_t i = 0; + + contract256_window4_modm(slide1, s1); + + ge25519_full_to_pniels(pre1 + 1, p1); + ge25519_double(&d1, p1); + + ge25519_set_neutral(r); + ge25519_full_to_pniels(pre1, r); + + ge25519_full_to_pniels(pre1 + 2, &d1); + for(i = 1; i < 7; i++) { + ge25519_pnielsadd(&pre1[i + 2], &d1, &pre1[i]); + } + + for(i = 63; i >= 0; i--) { + int k = abs(slide1[i]); + ge25519_double_partial(r, r); + ge25519_double_partial(r, r); + ge25519_double_partial(r, r); + ge25519_double_p1p1(&t, r); + ge25519_move_conditional_pniels_array(&pre, pre1, k, 9); + ge25519_p1p1_to_full(r, &t); + ge25519_pnielsadd_p1p1(&t, r, &pre, (unsigned char)slide1[i] >> 7); + ge25519_p1p1_to_partial(r, &t); + } + curve25519_mul(r->t, t.x, t.y); + memzero(slide1, sizeof(slide1)); +} + +void ge25519_scalarmult_base_choose_niels( + ge25519_niels* t, + const uint8_t table[256][96], + uint32_t pos, + signed char b) { + bignum25519 neg = {0}; + uint32_t sign = (uint32_t)((unsigned char)b >> 7); + uint32_t mask = ~(sign - 1); + uint32_t u = (b + mask) ^ mask; + + /* ysubx, xaddy, t2d in packed form. initialize to ysubx = 1, xaddy = 1, t2d = 0 */ + uint8_t packed[96] = {0}; + packed[0] = 1; + packed[32] = 1; + + ge25519_move_conditional_niels_array((ge25519_niels*)packed, &table[pos * 8], u - 1, 8); + + /* expand in to t */ + curve25519_expand(t->ysubx, packed + 0); + curve25519_expand(t->xaddy, packed + 32); + curve25519_expand(t->t2d, packed + 64); + + /* adjust for sign */ + curve25519_swap_conditional(t->ysubx, t->xaddy, sign); + curve25519_neg(neg, t->t2d); + curve25519_swap_conditional(t->t2d, neg, sign); +} + +/* computes [s]basepoint */ +void ge25519_scalarmult_base_niels( + ge25519* r, + const uint8_t basepoint_table[256][96], + const bignum256modm s) { + signed char b[64] = {0}; + uint32_t i = 0; + ge25519_niels t = {0}; + + contract256_window4_modm(b, s); + + ge25519_scalarmult_base_choose_niels(&t, basepoint_table, 0, b[1]); + curve25519_sub_reduce(r->x, t.xaddy, t.ysubx); + curve25519_add_reduce(r->y, t.xaddy, t.ysubx); + memzero(r->z, sizeof(bignum25519)); + curve25519_copy(r->t, t.t2d); + r->z[0] = 2; + for(i = 3; i < 64; i += 2) { + ge25519_scalarmult_base_choose_niels(&t, basepoint_table, i / 2, b[i]); + ge25519_nielsadd2(r, &t); + } + ge25519_double_partial(r, r); + ge25519_double_partial(r, r); + ge25519_double_partial(r, r); + ge25519_double(r, r); + ge25519_scalarmult_base_choose_niels(&t, basepoint_table, 0, b[0]); + curve25519_mul(t.t2d, t.t2d, ge25519_ecd); + ge25519_nielsadd2(r, &t); + for(i = 2; i < 64; i += 2) { + ge25519_scalarmult_base_choose_niels(&t, basepoint_table, i / 2, b[i]); + ge25519_nielsadd2(r, &t); + } +} + +int ge25519_check(const ge25519* r) { + /* return (z % q != 0 and + x * y % q == z * t % q and + (y * y - x * x - z * z - ed25519.d * t * t) % q == 0) + */ + + bignum25519 z = {0}, lhs = {0}, rhs = {0}, tmp = {0}, res = {0}; + curve25519_reduce(z, r->z); + + curve25519_mul(lhs, r->x, r->y); + curve25519_mul(rhs, r->z, r->t); + curve25519_sub_reduce(lhs, lhs, rhs); + + curve25519_square(res, r->y); + curve25519_square(tmp, r->x); + curve25519_sub_reduce(res, res, tmp); + curve25519_square(tmp, r->z); + curve25519_sub_reduce(res, res, tmp); + curve25519_square(tmp, r->t); + curve25519_mul(tmp, tmp, ge25519_ecd); + curve25519_sub_reduce(res, res, tmp); + + const int c1 = curve25519_isnonzero(z); + const int c2 = curve25519_isnonzero(lhs); + const int c3 = curve25519_isnonzero(res); + return c1 & (c2 ^ 0x1) & (c3 ^ 0x1); +} + +int ge25519_eq(const ge25519* a, const ge25519* b) { + int eq = 1; + bignum25519 t1 = {0}, t2 = {0}; + + eq &= ge25519_check(a); + eq &= ge25519_check(b); + + curve25519_mul(t1, a->x, b->z); + curve25519_mul(t2, b->x, a->z); + curve25519_sub_reduce(t1, t1, t2); + eq &= curve25519_isnonzero(t1) ^ 1; + + curve25519_mul(t1, a->y, b->z); + curve25519_mul(t2, b->y, a->z); + curve25519_sub_reduce(t1, t1, t2); + eq &= curve25519_isnonzero(t1) ^ 1; + + return eq; +} + +void ge25519_copy(ge25519* dst, const ge25519* src) { + curve25519_copy(dst->x, src->x); + curve25519_copy(dst->y, src->y); + curve25519_copy(dst->z, src->z); + curve25519_copy(dst->t, src->t); +} + +void ge25519_set_base(ge25519* r) { + ge25519_copy(r, &ge25519_basepoint); +} + +void ge25519_mul8(ge25519* r, const ge25519* t) { + ge25519_double_partial(r, t); + ge25519_double_partial(r, r); + ge25519_double(r, r); +} + +void ge25519_neg_partial(ge25519* r) { + curve25519_neg(r->x, r->x); +} + +void ge25519_neg_full(ge25519* r) { + curve25519_neg(r->x, r->x); + curve25519_neg(r->t, r->t); +} + +void ge25519_reduce(ge25519* r, const ge25519* t) { + curve25519_reduce(r->x, t->x); + curve25519_reduce(r->y, t->y); + curve25519_reduce(r->z, t->z); + curve25519_reduce(r->t, t->t); +} + +void ge25519_norm(ge25519* r, const ge25519* t) { + bignum25519 zinv = {0}; + curve25519_recip(zinv, t->z); + curve25519_mul(r->x, t->x, zinv); + curve25519_mul(r->y, t->y, zinv); + curve25519_mul(r->t, r->x, r->y); + curve25519_set(r->z, 1); +} + +void ge25519_add(ge25519* r, const ge25519* p, const ge25519* q, unsigned char signbit) { + ge25519_pniels P_ni = {0}; + ge25519_p1p1 P_11 = {0}; + + ge25519_full_to_pniels(&P_ni, q); + ge25519_pnielsadd_p1p1(&P_11, p, &P_ni, signbit); + ge25519_p1p1_to_full(r, &P_11); +} + +void ge25519_fromfe_frombytes_vartime(ge25519* r, const unsigned char* s) { + bignum25519 u = {0}, v = {0}, w = {0}, x = {0}, y = {0}, z = {0}; + unsigned char sign = 0; + + curve25519_expand_reduce(u, s); + + curve25519_square(v, u); + curve25519_add_reduce(v, v, v); /* 2 * u^2 */ + curve25519_set(w, 1); + curve25519_add_reduce(w, v, w); /* w = 2 * u^2 + 1 */ + + curve25519_square(x, w); /* w^2 */ + curve25519_mul(y, fe_ma2, v); /* -2 * A^2 * u^2 */ + curve25519_add_reduce(x, x, y); /* x = w^2 - 2 * A^2 * u^2 */ + + curve25519_divpowm1(r->x, w, x); /* (w / x)^(m + 1) */ + curve25519_square(y, r->x); + curve25519_mul(x, y, x); + curve25519_sub_reduce(y, w, x); + curve25519_copy(z, fe_ma); + + if(curve25519_isnonzero(y)) { + curve25519_add_reduce(y, w, x); + if(curve25519_isnonzero(y)) { + goto negative; + } else { + curve25519_mul(r->x, r->x, fe_fffb1); + } + } else { + curve25519_mul(r->x, r->x, fe_fffb2); + } + curve25519_mul(r->x, r->x, u); /* u * sqrt(2 * A * (A + 2) * w / x) */ + curve25519_mul(z, z, v); /* -2 * A * u^2 */ + sign = 0; + goto setsign; +negative: + curve25519_mul(x, x, fe_sqrtm1); + curve25519_sub_reduce(y, w, x); + if(curve25519_isnonzero(y)) { + assert((curve25519_add_reduce(y, w, x), !curve25519_isnonzero(y))); + curve25519_mul(r->x, r->x, fe_fffb3); + } else { + curve25519_mul(r->x, r->x, fe_fffb4); + } + /* r->x = sqrt(A * (A + 2) * w / x) */ + /* z = -A */ + sign = 1; +setsign: + if(curve25519_isnegative(r->x) != sign) { + assert(curve25519_isnonzero(r->x)); + curve25519_neg(r->x, r->x); + } + curve25519_add_reduce(r->z, z, w); + curve25519_sub_reduce(r->y, z, w); + curve25519_mul(r->x, r->x, r->z); + + // Partial form, saving from T coord computation . + // Later is mul8 discarding T anyway. + // rt = ((rx * ry % q) * inv(rz)) % q + // curve25519_mul(x, r->x, r->y); + // curve25519_recip(z, r->z); + // curve25519_mul(r->t, x, z); + +#if !defined(NDEBUG) + { + bignum25519 check_x = {0}, check_y = {0}, check_iz = {0}, check_v = {0}; + curve25519_recip(check_iz, r->z); + curve25519_mul(check_x, r->x, check_iz); + curve25519_mul(check_y, r->y, check_iz); + curve25519_square(check_x, check_x); + curve25519_square(check_y, check_y); + curve25519_mul(check_v, check_x, check_y); + curve25519_mul(check_v, fe_d, check_v); + curve25519_add_reduce(check_v, check_v, check_x); + curve25519_sub_reduce(check_v, check_v, check_y); + curve25519_set(check_x, 1); + curve25519_add_reduce(check_v, check_v, check_x); + assert(!curve25519_isnonzero(check_v)); + } +#endif +} + +int ge25519_unpack_vartime(ge25519* r, const unsigned char* s) { + int res = ge25519_unpack_negative_vartime(r, s); + ge25519_neg_full(r); + return res; +} + +void ge25519_scalarmult_base_wrapper(ge25519* r, const bignum256modm s) { + ge25519_scalarmult_base_niels(r, ge25519_niels_base_multiples, s); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.h new file mode 100644 index 000000000..8e690ef80 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_impl_base.h @@ -0,0 +1,127 @@ +/* + Timing safe memory compare +*/ +int ed25519_verify(const unsigned char* x, const unsigned char* y, size_t len); + +/* + conversions +*/ + +void ge25519_p1p1_to_partial(ge25519* r, const ge25519_p1p1* p); + +void ge25519_p1p1_to_full(ge25519* r, const ge25519_p1p1* p); + +void ge25519_full_to_pniels(ge25519_pniels* p, const ge25519* r); + +/* + adding & doubling +*/ + +void ge25519_double_p1p1(ge25519_p1p1* r, const ge25519* p); + +#ifndef ED25519_NO_PRECOMP +void ge25519_nielsadd2_p1p1( + ge25519_p1p1* r, + const ge25519* p, + const ge25519_niels* q, + unsigned char signbit); +#endif + +/* computes [s1]p1 + [s2]p2 */ +//#if USE_MONERO +void ge25519_double_scalarmult_vartime2( + ge25519* r, + const ge25519* p1, + const bignum256modm s1, + const ge25519* p2, + const bignum256modm s2); +//#endif + +void ge25519_pnielsadd_p1p1( + ge25519_p1p1* r, + const ge25519* p, + const ge25519_pniels* q, + unsigned char signbit); + +void ge25519_double_partial(ge25519* r, const ge25519* p); + +void ge25519_double(ge25519* r, const ge25519* p); + +void ge25519_nielsadd2(ge25519* r, const ge25519_niels* q); + +void ge25519_pnielsadd(ge25519_pniels* r, const ge25519* p, const ge25519_pniels* q); + +/* + pack & unpack +*/ + +void ge25519_pack(unsigned char r[32], const ge25519* p); + +int ge25519_unpack_negative_vartime(ge25519* r, const unsigned char p[32]); + +/* + scalarmults +*/ + +void ge25519_set_neutral(ge25519* r); + +/* computes [s1]p1 + [s2]base */ +void ge25519_double_scalarmult_vartime( + ge25519* r, + const ge25519* p1, + const bignum256modm s1, + const bignum256modm s2); + +/* computes [s1]p1, constant time */ +void ge25519_scalarmult(ge25519* r, const ge25519* p1, const bignum256modm s1); + +void ge25519_scalarmult_base_choose_niels( + ge25519_niels* t, + const uint8_t table[256][96], + uint32_t pos, + signed char b); + +/* computes [s]basepoint */ +void ge25519_scalarmult_base_niels( + ge25519* r, + const uint8_t basepoint_table[256][96], + const bignum256modm s); + +/* check if r is on curve */ +int ge25519_check(const ge25519* r); + +/* a == b */ +int ge25519_eq(const ge25519* a, const ge25519* b); + +/* copies one point to another */ +void ge25519_copy(ge25519* dst, const ge25519* src); + +/* sets B point to r */ +void ge25519_set_base(ge25519* r); + +/* 8*P */ +void ge25519_mul8(ge25519* r, const ge25519* t); + +/* -P */ +void ge25519_neg_partial(ge25519* r); + +/* -P */ +void ge25519_neg_full(ge25519* r); + +/* reduce all coords */ +void ge25519_reduce(ge25519* r, const ge25519* t); + +/* normalizes coords. (x, y, 1, x*y) */ +void ge25519_norm(ge25519* r, const ge25519* t); + +/* Simple addition */ +void ge25519_add(ge25519* r, const ge25519* a, const ge25519* b, unsigned char signbit); + +/* point from bytes, used in H_p() */ +void ge25519_fromfe_frombytes_vartime(ge25519* r, const unsigned char* s); + +/* point from bytes */ +int ge25519_unpack_vartime(ge25519* r, const unsigned char* s); + +/* aG, wrapper for niels base mult. */ +void ge25519_scalarmult_base_wrapper(ge25519* r, const bignum256modm s); diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_portable.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_portable.h new file mode 100644 index 000000000..d344c9e2a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_donna_portable.h @@ -0,0 +1,22 @@ +#define mul32x32_64(a, b) (((uint64_t)(a)) * (b)) + +#include +#include +#include + +#define DONNA_INLINE +#undef ALIGN +#define ALIGN(x) __attribute__((aligned(x))) + +static inline void U32TO8_LE(unsigned char* p, const uint32_t v) { + p[0] = (unsigned char)(v); + p[1] = (unsigned char)(v >> 8); + p[2] = (unsigned char)(v >> 16); + p[3] = (unsigned char)(v >> 24); +} + +static inline uint32_t U8TO32_LE(const unsigned char* p) { + return ( + ((uint32_t)(p[0])) | ((uint32_t)(p[1]) << 8) | ((uint32_t)(p[2]) << 16) | + ((uint32_t)(p[3]) << 24)); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom.h new file mode 100644 index 000000000..7bae63ed1 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom.h @@ -0,0 +1,23 @@ +/* + a custom hash must have a 512bit digest and implement: + + struct ed25519_hash_context; + + void ed25519_hash_init(ed25519_hash_context *ctx); + void ed25519_hash_update(ed25519_hash_context *ctx, const uint8_t *in, size_t inlen); + void ed25519_hash_final(ed25519_hash_context *ctx, uint8_t *hash); + void ed25519_hash(uint8_t *hash, const uint8_t *in, size_t inlen); +*/ + +#ifndef ED25519_HASH_CUSTOM +#define ED25519_HASH_CUSTOM + +#include "../sha2.h" + +#define ed25519_hash_context SHA512_CTX +#define ed25519_hash_init(ctx) sha512_Init(ctx) +#define ed25519_hash_update(ctx, in, inlen) sha512_Update((ctx), (in), (inlen)) +#define ed25519_hash_final(ctx, hash) sha512_Final((ctx), (hash)) +#define ed25519_hash(hash, in, inlen) sha512_Raw((in), (inlen), (hash)) + +#endif // ED25519_HASH_CUSTOM diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_keccak.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_keccak.h new file mode 100644 index 000000000..74a986824 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_keccak.h @@ -0,0 +1,29 @@ +/* + a custom hash must have a 512bit digest and implement: + + struct ed25519_hash_context; + + void ed25519_hash_init(ed25519_hash_context *ctx); + void ed25519_hash_update(ed25519_hash_context *ctx, const uint8_t *in, size_t inlen); + void ed25519_hash_final(ed25519_hash_context *ctx, uint8_t *hash); + void ed25519_hash(uint8_t *hash, const uint8_t *in, size_t inlen); +*/ + +#include "../options.h" + +#if USE_KECCAK + +#ifndef ED25519_HASH_CUSTOM +#define ED25519_HASH_CUSTOM + +#include "../sha3.h" + +#define ed25519_hash_context SHA3_CTX +#define ed25519_hash_init(ctx) keccak_512_Init(ctx) +#define ed25519_hash_update(ctx, in, inlen) keccak_Update((ctx), (in), (inlen)) +#define ed25519_hash_final(ctx, hash) keccak_Final((ctx), (hash)) +#define ed25519_hash(hash, in, inlen) keccak_512((in), (inlen), (hash)) + +#endif // ED25519_HASH_CUSTOM + +#endif // USE_KECCAK diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_sha3.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_sha3.h new file mode 100644 index 000000000..f857821fa --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_hash_custom_sha3.h @@ -0,0 +1,23 @@ +/* + a custom hash must have a 512bit digest and implement: + + struct ed25519_hash_context; + + void ed25519_hash_init(ed25519_hash_context *ctx); + void ed25519_hash_update(ed25519_hash_context *ctx, const uint8_t *in, size_t inlen); + void ed25519_hash_final(ed25519_hash_context *ctx, uint8_t *hash); + void ed25519_hash(uint8_t *hash, const uint8_t *in, size_t inlen); +*/ + +#ifndef ED25519_HASH_CUSTOM +#define ED25519_HASH_CUSTOM + +#include "../sha3.h" + +#define ed25519_hash_context SHA3_CTX +#define ed25519_hash_init(ctx) sha3_512_Init(ctx) +#define ed25519_hash_update(ctx, in, inlen) sha3_Update((ctx), (in), (inlen)) +#define ed25519_hash_final(ctx, hash) sha3_Final((ctx), (hash)) +#define ed25519_hash(hash, in, inlen) sha3_512((in), (inlen), (hash)) + +#endif // ED25519_HASH_CUSTOM diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.c new file mode 100644 index 000000000..7b33b6bfe --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.c @@ -0,0 +1,14 @@ +#include "../options.h" + +#if USE_KECCAK + +#include + +#include "ed25519_keccak.h" +#include "ed25519_hash_custom_keccak.h" + +#define ED25519_SUFFIX _keccak + +#include "ed25519.c" + +#endif // USE_KECCAK \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.h new file mode 100644 index 000000000..4c315a75e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_keccak.h @@ -0,0 +1,38 @@ +#include "../options.h" + +#if USE_KECCAK + +#ifndef ED25519_KECCAK_H +#define ED25519_KECCAK_H + +#include "ed25519.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +void ed25519_publickey_keccak(const ed25519_secret_key sk, ed25519_public_key pk); + +int ed25519_sign_open_keccak( + const unsigned char* m, + size_t mlen, + const ed25519_public_key pk, + const ed25519_signature RS); +void ed25519_sign_keccak( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + ed25519_signature RS); + +int ed25519_scalarmult_keccak( + ed25519_public_key res, + const ed25519_secret_key sk, + const ed25519_public_key pk); + +#if defined(__cplusplus) +} +#endif + +#endif // ED25519_KECCAK_H + +#endif // USE_KECCAK \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.c b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.c new file mode 100644 index 000000000..e172ef874 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.c @@ -0,0 +1,8 @@ +#include + +#include "ed25519_sha3.h" +#include "ed25519_hash_custom_sha3.h" + +#define ED25519_SUFFIX _sha3 + +#include "ed25519.c" diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.h b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.h new file mode 100644 index 000000000..1085c1b20 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/ed25519_sha3.h @@ -0,0 +1,32 @@ +#ifndef ED25519_SHA3_H +#define ED25519_SHA3_H + +#include "ed25519.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +void ed25519_publickey_sha3(const ed25519_secret_key sk, ed25519_public_key pk); + +int ed25519_sign_open_sha3( + const unsigned char* m, + size_t mlen, + const ed25519_public_key pk, + const ed25519_signature RS); +void ed25519_sign_sha3( + const unsigned char* m, + size_t mlen, + const ed25519_secret_key sk, + ed25519_signature RS); + +int ed25519_scalarmult_sha3( + ed25519_public_key res, + const ed25519_secret_key sk, + const ed25519_public_key pk); + +#if defined(__cplusplus) +} +#endif + +#endif // ED25519_SHA3_H diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.c b/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.c new file mode 100644 index 000000000..6489ea20c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.c @@ -0,0 +1,800 @@ +/* + Public domain by Andrew M. +*/ + +#include "ed25519_donna.h" + +/* + Arithmetic modulo the group order n = 2^252 + 27742317777372353535851937790883648493 = 7237005577332262213973186563042994240857116359379907606001950938285454250989 + + k = 32 + b = 1 << 8 = 256 + m = 2^252 + 27742317777372353535851937790883648493 = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed + mu = floor( b^(k*2) / m ) = 0xfffffffffffffffffffffffffffffffeb2106215d086329a7ed9ce5a30a2c131b +*/ + +static const bignum256modm modm_m = { + 0x1cf5d3ed, + 0x20498c69, + 0x2f79cd65, + 0x37be77a8, + 0x00000014, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00001000}; + +static const bignum256modm modm_mu = { + 0x0a2c131b, + 0x3673968c, + 0x06329a7e, + 0x01885742, + 0x3fffeb21, + 0x3fffffff, + 0x3fffffff, + 0x3fffffff, + 0x000fffff}; + +static bignum256modm_element_t lt_modm(bignum256modm_element_t a, bignum256modm_element_t b) { + return (a - b) >> 31; +} + +/* see HAC, Alg. 14.42 Step 4 */ +void reduce256_modm(bignum256modm r) { + bignum256modm t = {0}; + bignum256modm_element_t b = 0, pb = 0, mask = 0; + + /* t = r - m */ + pb = 0; + pb += modm_m[0]; + b = lt_modm(r[0], pb); + t[0] = (r[0] - pb + (b << 30)); + pb = b; + pb += modm_m[1]; + b = lt_modm(r[1], pb); + t[1] = (r[1] - pb + (b << 30)); + pb = b; + pb += modm_m[2]; + b = lt_modm(r[2], pb); + t[2] = (r[2] - pb + (b << 30)); + pb = b; + pb += modm_m[3]; + b = lt_modm(r[3], pb); + t[3] = (r[3] - pb + (b << 30)); + pb = b; + pb += modm_m[4]; + b = lt_modm(r[4], pb); + t[4] = (r[4] - pb + (b << 30)); + pb = b; + pb += modm_m[5]; + b = lt_modm(r[5], pb); + t[5] = (r[5] - pb + (b << 30)); + pb = b; + pb += modm_m[6]; + b = lt_modm(r[6], pb); + t[6] = (r[6] - pb + (b << 30)); + pb = b; + pb += modm_m[7]; + b = lt_modm(r[7], pb); + t[7] = (r[7] - pb + (b << 30)); + pb = b; + pb += modm_m[8]; + b = lt_modm(r[8], pb); + t[8] = (r[8] - pb + (b << 16)); + + /* keep r if r was smaller than m */ + mask = b - 1; + r[0] ^= mask & (r[0] ^ t[0]); + r[1] ^= mask & (r[1] ^ t[1]); + r[2] ^= mask & (r[2] ^ t[2]); + r[3] ^= mask & (r[3] ^ t[3]); + r[4] ^= mask & (r[4] ^ t[4]); + r[5] ^= mask & (r[5] ^ t[5]); + r[6] ^= mask & (r[6] ^ t[6]); + r[7] ^= mask & (r[7] ^ t[7]); + r[8] ^= mask & (r[8] ^ t[8]); +} + +/* + Barrett reduction, see HAC, Alg. 14.42 + + Instead of passing in x, pre-process in to q1 and r1 for efficiency +*/ +void barrett_reduce256_modm(bignum256modm r, const bignum256modm q1, const bignum256modm r1) { + bignum256modm q3 = {0}, r2 = {0}; + uint64_t c = 0; + bignum256modm_element_t f = 0, b = 0, pb = 0; + + /* q1 = x >> 248 = 264 bits = 9 30 bit elements + q2 = mu * q1 + q3 = (q2 / 256(32+1)) = q2 / (2^8)^(32+1) = q2 >> 264 */ + c = mul32x32_64(modm_mu[0], q1[7]) + mul32x32_64(modm_mu[1], q1[6]) + + mul32x32_64(modm_mu[2], q1[5]) + mul32x32_64(modm_mu[3], q1[4]) + + mul32x32_64(modm_mu[4], q1[3]) + mul32x32_64(modm_mu[5], q1[2]) + + mul32x32_64(modm_mu[6], q1[1]) + mul32x32_64(modm_mu[7], q1[0]); + c >>= 30; + c += mul32x32_64(modm_mu[0], q1[8]) + mul32x32_64(modm_mu[1], q1[7]) + + mul32x32_64(modm_mu[2], q1[6]) + mul32x32_64(modm_mu[3], q1[5]) + + mul32x32_64(modm_mu[4], q1[4]) + mul32x32_64(modm_mu[5], q1[3]) + + mul32x32_64(modm_mu[6], q1[2]) + mul32x32_64(modm_mu[7], q1[1]) + + mul32x32_64(modm_mu[8], q1[0]); + f = (bignum256modm_element_t)c; + q3[0] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[1], q1[8]) + mul32x32_64(modm_mu[2], q1[7]) + + mul32x32_64(modm_mu[3], q1[6]) + mul32x32_64(modm_mu[4], q1[5]) + + mul32x32_64(modm_mu[5], q1[4]) + mul32x32_64(modm_mu[6], q1[3]) + + mul32x32_64(modm_mu[7], q1[2]) + mul32x32_64(modm_mu[8], q1[1]); + f = (bignum256modm_element_t)c; + q3[0] |= (f << 6) & 0x3fffffff; + q3[1] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[2], q1[8]) + mul32x32_64(modm_mu[3], q1[7]) + + mul32x32_64(modm_mu[4], q1[6]) + mul32x32_64(modm_mu[5], q1[5]) + + mul32x32_64(modm_mu[6], q1[4]) + mul32x32_64(modm_mu[7], q1[3]) + + mul32x32_64(modm_mu[8], q1[2]); + f = (bignum256modm_element_t)c; + q3[1] |= (f << 6) & 0x3fffffff; + q3[2] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[3], q1[8]) + mul32x32_64(modm_mu[4], q1[7]) + + mul32x32_64(modm_mu[5], q1[6]) + mul32x32_64(modm_mu[6], q1[5]) + + mul32x32_64(modm_mu[7], q1[4]) + mul32x32_64(modm_mu[8], q1[3]); + f = (bignum256modm_element_t)c; + q3[2] |= (f << 6) & 0x3fffffff; + q3[3] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[4], q1[8]) + mul32x32_64(modm_mu[5], q1[7]) + + mul32x32_64(modm_mu[6], q1[6]) + mul32x32_64(modm_mu[7], q1[5]) + + mul32x32_64(modm_mu[8], q1[4]); + f = (bignum256modm_element_t)c; + q3[3] |= (f << 6) & 0x3fffffff; + q3[4] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[5], q1[8]) + mul32x32_64(modm_mu[6], q1[7]) + + mul32x32_64(modm_mu[7], q1[6]) + mul32x32_64(modm_mu[8], q1[5]); + f = (bignum256modm_element_t)c; + q3[4] |= (f << 6) & 0x3fffffff; + q3[5] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[6], q1[8]) + mul32x32_64(modm_mu[7], q1[7]) + + mul32x32_64(modm_mu[8], q1[6]); + f = (bignum256modm_element_t)c; + q3[5] |= (f << 6) & 0x3fffffff; + q3[6] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[7], q1[8]) + mul32x32_64(modm_mu[8], q1[7]); + f = (bignum256modm_element_t)c; + q3[6] |= (f << 6) & 0x3fffffff; + q3[7] = (f >> 24) & 0x3f; + c >>= 30; + c += mul32x32_64(modm_mu[8], q1[8]); + f = (bignum256modm_element_t)c; + q3[7] |= (f << 6) & 0x3fffffff; + q3[8] = (bignum256modm_element_t)(c >> 24); + + /* r1 = (x mod 256^(32+1)) = x mod (2^8)(32+1) = x & ((1 << 264) - 1) + r2 = (q3 * m) mod (256^(32+1)) = (q3 * m) & ((1 << 264) - 1) */ + c = mul32x32_64(modm_m[0], q3[0]); + r2[0] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[1]) + mul32x32_64(modm_m[1], q3[0]); + r2[1] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[2]) + mul32x32_64(modm_m[1], q3[1]) + + mul32x32_64(modm_m[2], q3[0]); + r2[2] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[3]) + mul32x32_64(modm_m[1], q3[2]) + + mul32x32_64(modm_m[2], q3[1]) + mul32x32_64(modm_m[3], q3[0]); + r2[3] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[4]) + mul32x32_64(modm_m[1], q3[3]) + + mul32x32_64(modm_m[2], q3[2]) + mul32x32_64(modm_m[3], q3[1]) + + mul32x32_64(modm_m[4], q3[0]); + r2[4] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[5]) + mul32x32_64(modm_m[1], q3[4]) + + mul32x32_64(modm_m[2], q3[3]) + mul32x32_64(modm_m[3], q3[2]) + + mul32x32_64(modm_m[4], q3[1]) + mul32x32_64(modm_m[5], q3[0]); + r2[5] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[6]) + mul32x32_64(modm_m[1], q3[5]) + + mul32x32_64(modm_m[2], q3[4]) + mul32x32_64(modm_m[3], q3[3]) + + mul32x32_64(modm_m[4], q3[2]) + mul32x32_64(modm_m[5], q3[1]) + + mul32x32_64(modm_m[6], q3[0]); + r2[6] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[7]) + mul32x32_64(modm_m[1], q3[6]) + + mul32x32_64(modm_m[2], q3[5]) + mul32x32_64(modm_m[3], q3[4]) + + mul32x32_64(modm_m[4], q3[3]) + mul32x32_64(modm_m[5], q3[2]) + + mul32x32_64(modm_m[6], q3[1]) + mul32x32_64(modm_m[7], q3[0]); + r2[7] = (bignum256modm_element_t)(c & 0x3fffffff); + c >>= 30; + c += mul32x32_64(modm_m[0], q3[8]) + mul32x32_64(modm_m[1], q3[7]) + + mul32x32_64(modm_m[2], q3[6]) + mul32x32_64(modm_m[3], q3[5]) + + mul32x32_64(modm_m[4], q3[4]) + mul32x32_64(modm_m[5], q3[3]) + + mul32x32_64(modm_m[6], q3[2]) + mul32x32_64(modm_m[7], q3[1]) + + mul32x32_64(modm_m[8], q3[0]); + r2[8] = (bignum256modm_element_t)(c & 0xffffff); + + /* r = r1 - r2 + if (r < 0) r += (1 << 264) */ + pb = 0; + pb += r2[0]; + b = lt_modm(r1[0], pb); + r[0] = (r1[0] - pb + (b << 30)); + pb = b; + pb += r2[1]; + b = lt_modm(r1[1], pb); + r[1] = (r1[1] - pb + (b << 30)); + pb = b; + pb += r2[2]; + b = lt_modm(r1[2], pb); + r[2] = (r1[2] - pb + (b << 30)); + pb = b; + pb += r2[3]; + b = lt_modm(r1[3], pb); + r[3] = (r1[3] - pb + (b << 30)); + pb = b; + pb += r2[4]; + b = lt_modm(r1[4], pb); + r[4] = (r1[4] - pb + (b << 30)); + pb = b; + pb += r2[5]; + b = lt_modm(r1[5], pb); + r[5] = (r1[5] - pb + (b << 30)); + pb = b; + pb += r2[6]; + b = lt_modm(r1[6], pb); + r[6] = (r1[6] - pb + (b << 30)); + pb = b; + pb += r2[7]; + b = lt_modm(r1[7], pb); + r[7] = (r1[7] - pb + (b << 30)); + pb = b; + pb += r2[8]; + b = lt_modm(r1[8], pb); + r[8] = (r1[8] - pb + (b << 24)); + + reduce256_modm(r); + reduce256_modm(r); +} + +/* addition modulo m */ +void add256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y) { + bignum256modm_element_t c = 0; + + c = x[0] + y[0]; + r[0] = c & 0x3fffffff; + c >>= 30; + c += x[1] + y[1]; + r[1] = c & 0x3fffffff; + c >>= 30; + c += x[2] + y[2]; + r[2] = c & 0x3fffffff; + c >>= 30; + c += x[3] + y[3]; + r[3] = c & 0x3fffffff; + c >>= 30; + c += x[4] + y[4]; + r[4] = c & 0x3fffffff; + c >>= 30; + c += x[5] + y[5]; + r[5] = c & 0x3fffffff; + c >>= 30; + c += x[6] + y[6]; + r[6] = c & 0x3fffffff; + c >>= 30; + c += x[7] + y[7]; + r[7] = c & 0x3fffffff; + c >>= 30; + c += x[8] + y[8]; + r[8] = c; + + reduce256_modm(r); +} + +/* -x modulo m */ +void neg256_modm(bignum256modm r, const bignum256modm x) { + bignum256modm_element_t b = 0, pb = 0; + + /* r = m - x */ + pb = 0; + pb += x[0]; + b = lt_modm(modm_m[0], pb); + r[0] = (modm_m[0] - pb + (b << 30)); + pb = b; + pb += x[1]; + b = lt_modm(modm_m[1], pb); + r[1] = (modm_m[1] - pb + (b << 30)); + pb = b; + pb += x[2]; + b = lt_modm(modm_m[2], pb); + r[2] = (modm_m[2] - pb + (b << 30)); + pb = b; + pb += x[3]; + b = lt_modm(modm_m[3], pb); + r[3] = (modm_m[3] - pb + (b << 30)); + pb = b; + pb += x[4]; + b = lt_modm(modm_m[4], pb); + r[4] = (modm_m[4] - pb + (b << 30)); + pb = b; + pb += x[5]; + b = lt_modm(modm_m[5], pb); + r[5] = (modm_m[5] - pb + (b << 30)); + pb = b; + pb += x[6]; + b = lt_modm(modm_m[6], pb); + r[6] = (modm_m[6] - pb + (b << 30)); + pb = b; + pb += x[7]; + b = lt_modm(modm_m[7], pb); + r[7] = (modm_m[7] - pb + (b << 30)); + pb = b; + pb += x[8]; + b = lt_modm(modm_m[8], pb); + r[8] = (modm_m[8] - pb + (b << 16)); + + // if x==0, reduction is required + reduce256_modm(r); +} + +/* consts for subtraction, > p */ +/* Emilia Kasper trick, https://www.imperialviolet.org/2010/12/04/ecc.html */ +static const uint32_t twoP[] = { + 0x5cf5d3ed, + 0x60498c68, + 0x6f79cd64, + 0x77be77a7, + 0x40000013, + 0x3fffffff, + 0x3fffffff, + 0x3fffffff, + 0xfff}; + +/* subtraction x-y % m */ +void sub256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y) { + bignum256modm_element_t c = 0; + c = twoP[0] + x[0] - y[0]; + r[0] = c & 0x3fffffff; + c >>= 30; + c += twoP[1] + x[1] - y[1]; + r[1] = c & 0x3fffffff; + c >>= 30; + c += twoP[2] + x[2] - y[2]; + r[2] = c & 0x3fffffff; + c >>= 30; + c += twoP[3] + x[3] - y[3]; + r[3] = c & 0x3fffffff; + c >>= 30; + c += twoP[4] + x[4] - y[4]; + r[4] = c & 0x3fffffff; + c >>= 30; + c += twoP[5] + x[5] - y[5]; + r[5] = c & 0x3fffffff; + c >>= 30; + c += twoP[6] + x[6] - y[6]; + r[6] = c & 0x3fffffff; + c >>= 30; + c += twoP[7] + x[7] - y[7]; + r[7] = c & 0x3fffffff; + c >>= 30; + c += twoP[8] + x[8] - y[8]; + r[8] = c; + reduce256_modm(r); +} + +/* multiplication modulo m */ +void mul256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y) { + bignum256modm r1 = {0}, q1 = {0}; + uint64_t c = 0; + bignum256modm_element_t f = 0; + + /* r1 = (x mod 256^(32+1)) = x mod (2^8)(31+1) = x & ((1 << 264) - 1) + q1 = x >> 248 = 264 bits = 9 30 bit elements */ + c = mul32x32_64(x[0], y[0]); + f = (bignum256modm_element_t)c; + r1[0] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[1]) + mul32x32_64(x[1], y[0]); + f = (bignum256modm_element_t)c; + r1[1] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[2]) + mul32x32_64(x[1], y[1]) + mul32x32_64(x[2], y[0]); + f = (bignum256modm_element_t)c; + r1[2] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[3]) + mul32x32_64(x[1], y[2]) + mul32x32_64(x[2], y[1]) + + mul32x32_64(x[3], y[0]); + f = (bignum256modm_element_t)c; + r1[3] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[4]) + mul32x32_64(x[1], y[3]) + mul32x32_64(x[2], y[2]) + + mul32x32_64(x[3], y[1]) + mul32x32_64(x[4], y[0]); + f = (bignum256modm_element_t)c; + r1[4] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[5]) + mul32x32_64(x[1], y[4]) + mul32x32_64(x[2], y[3]) + + mul32x32_64(x[3], y[2]) + mul32x32_64(x[4], y[1]) + mul32x32_64(x[5], y[0]); + f = (bignum256modm_element_t)c; + r1[5] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[6]) + mul32x32_64(x[1], y[5]) + mul32x32_64(x[2], y[4]) + + mul32x32_64(x[3], y[3]) + mul32x32_64(x[4], y[2]) + mul32x32_64(x[5], y[1]) + + mul32x32_64(x[6], y[0]); + f = (bignum256modm_element_t)c; + r1[6] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[7]) + mul32x32_64(x[1], y[6]) + mul32x32_64(x[2], y[5]) + + mul32x32_64(x[3], y[4]) + mul32x32_64(x[4], y[3]) + mul32x32_64(x[5], y[2]) + + mul32x32_64(x[6], y[1]) + mul32x32_64(x[7], y[0]); + f = (bignum256modm_element_t)c; + r1[7] = (f & 0x3fffffff); + c >>= 30; + c += mul32x32_64(x[0], y[8]) + mul32x32_64(x[1], y[7]) + mul32x32_64(x[2], y[6]) + + mul32x32_64(x[3], y[5]) + mul32x32_64(x[4], y[4]) + mul32x32_64(x[5], y[3]) + + mul32x32_64(x[6], y[2]) + mul32x32_64(x[7], y[1]) + mul32x32_64(x[8], y[0]); + f = (bignum256modm_element_t)c; + r1[8] = (f & 0x00ffffff); + q1[0] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[1], y[8]) + mul32x32_64(x[2], y[7]) + mul32x32_64(x[3], y[6]) + + mul32x32_64(x[4], y[5]) + mul32x32_64(x[5], y[4]) + mul32x32_64(x[6], y[3]) + + mul32x32_64(x[7], y[2]) + mul32x32_64(x[8], y[1]); + f = (bignum256modm_element_t)c; + q1[0] = (q1[0] | (f << 22)) & 0x3fffffff; + q1[1] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[2], y[8]) + mul32x32_64(x[3], y[7]) + mul32x32_64(x[4], y[6]) + + mul32x32_64(x[5], y[5]) + mul32x32_64(x[6], y[4]) + mul32x32_64(x[7], y[3]) + + mul32x32_64(x[8], y[2]); + f = (bignum256modm_element_t)c; + q1[1] = (q1[1] | (f << 22)) & 0x3fffffff; + q1[2] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[3], y[8]) + mul32x32_64(x[4], y[7]) + mul32x32_64(x[5], y[6]) + + mul32x32_64(x[6], y[5]) + mul32x32_64(x[7], y[4]) + mul32x32_64(x[8], y[3]); + f = (bignum256modm_element_t)c; + q1[2] = (q1[2] | (f << 22)) & 0x3fffffff; + q1[3] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[4], y[8]) + mul32x32_64(x[5], y[7]) + mul32x32_64(x[6], y[6]) + + mul32x32_64(x[7], y[5]) + mul32x32_64(x[8], y[4]); + f = (bignum256modm_element_t)c; + q1[3] = (q1[3] | (f << 22)) & 0x3fffffff; + q1[4] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[5], y[8]) + mul32x32_64(x[6], y[7]) + mul32x32_64(x[7], y[6]) + + mul32x32_64(x[8], y[5]); + f = (bignum256modm_element_t)c; + q1[4] = (q1[4] | (f << 22)) & 0x3fffffff; + q1[5] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[6], y[8]) + mul32x32_64(x[7], y[7]) + mul32x32_64(x[8], y[6]); + f = (bignum256modm_element_t)c; + q1[5] = (q1[5] | (f << 22)) & 0x3fffffff; + q1[6] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[7], y[8]) + mul32x32_64(x[8], y[7]); + f = (bignum256modm_element_t)c; + q1[6] = (q1[6] | (f << 22)) & 0x3fffffff; + q1[7] = (f >> 8) & 0x3fffff; + c >>= 30; + c += mul32x32_64(x[8], y[8]); + f = (bignum256modm_element_t)c; + q1[7] = (q1[7] | (f << 22)) & 0x3fffffff; + q1[8] = (f >> 8) & 0x3fffff; + + barrett_reduce256_modm(r, q1, r1); +} + +void expand256_modm(bignum256modm out, const unsigned char* in, size_t len) { + unsigned char work[64] = {0}; + bignum256modm_element_t x[16] = {0}; + bignum256modm q1 = {0}; + + memcpy(work, in, len); + x[0] = U8TO32_LE(work + 0); + x[1] = U8TO32_LE(work + 4); + x[2] = U8TO32_LE(work + 8); + x[3] = U8TO32_LE(work + 12); + x[4] = U8TO32_LE(work + 16); + x[5] = U8TO32_LE(work + 20); + x[6] = U8TO32_LE(work + 24); + x[7] = U8TO32_LE(work + 28); + x[8] = U8TO32_LE(work + 32); + x[9] = U8TO32_LE(work + 36); + x[10] = U8TO32_LE(work + 40); + x[11] = U8TO32_LE(work + 44); + x[12] = U8TO32_LE(work + 48); + x[13] = U8TO32_LE(work + 52); + x[14] = U8TO32_LE(work + 56); + x[15] = U8TO32_LE(work + 60); + + /* r1 = (x mod 256^(32+1)) = x mod (2^8)(31+1) = x & ((1 << 264) - 1) */ + out[0] = (x[0]) & 0x3fffffff; + out[1] = ((x[0] >> 30) | (x[1] << 2)) & 0x3fffffff; + out[2] = ((x[1] >> 28) | (x[2] << 4)) & 0x3fffffff; + out[3] = ((x[2] >> 26) | (x[3] << 6)) & 0x3fffffff; + out[4] = ((x[3] >> 24) | (x[4] << 8)) & 0x3fffffff; + out[5] = ((x[4] >> 22) | (x[5] << 10)) & 0x3fffffff; + out[6] = ((x[5] >> 20) | (x[6] << 12)) & 0x3fffffff; + out[7] = ((x[6] >> 18) | (x[7] << 14)) & 0x3fffffff; + out[8] = ((x[7] >> 16) | (x[8] << 16)) & 0x00ffffff; + + /* 8*31 = 248 bits, no need to reduce */ + if(len < 32) return; + + /* q1 = x >> 248 = 264 bits = 9 30 bit elements */ + q1[0] = ((x[7] >> 24) | (x[8] << 8)) & 0x3fffffff; + q1[1] = ((x[8] >> 22) | (x[9] << 10)) & 0x3fffffff; + q1[2] = ((x[9] >> 20) | (x[10] << 12)) & 0x3fffffff; + q1[3] = ((x[10] >> 18) | (x[11] << 14)) & 0x3fffffff; + q1[4] = ((x[11] >> 16) | (x[12] << 16)) & 0x3fffffff; + q1[5] = ((x[12] >> 14) | (x[13] << 18)) & 0x3fffffff; + q1[6] = ((x[13] >> 12) | (x[14] << 20)) & 0x3fffffff; + q1[7] = ((x[14] >> 10) | (x[15] << 22)) & 0x3fffffff; + q1[8] = ((x[15] >> 8)); + + barrett_reduce256_modm(out, q1, out); +} + +void expand_raw256_modm(bignum256modm out, const unsigned char in[32]) { + bignum256modm_element_t x[8] = {0}; + + x[0] = U8TO32_LE(in + 0); + x[1] = U8TO32_LE(in + 4); + x[2] = U8TO32_LE(in + 8); + x[3] = U8TO32_LE(in + 12); + x[4] = U8TO32_LE(in + 16); + x[5] = U8TO32_LE(in + 20); + x[6] = U8TO32_LE(in + 24); + x[7] = U8TO32_LE(in + 28); + + out[0] = (x[0]) & 0x3fffffff; + out[1] = ((x[0] >> 30) | (x[1] << 2)) & 0x3fffffff; + out[2] = ((x[1] >> 28) | (x[2] << 4)) & 0x3fffffff; + out[3] = ((x[2] >> 26) | (x[3] << 6)) & 0x3fffffff; + out[4] = ((x[3] >> 24) | (x[4] << 8)) & 0x3fffffff; + out[5] = ((x[4] >> 22) | (x[5] << 10)) & 0x3fffffff; + out[6] = ((x[5] >> 20) | (x[6] << 12)) & 0x3fffffff; + out[7] = ((x[6] >> 18) | (x[7] << 14)) & 0x3fffffff; + out[8] = ((x[7] >> 16)) & 0x0000ffff; +} + +int is_reduced256_modm(const bignum256modm in) { + int i = 0; + uint32_t res1 = 0; + uint32_t res2 = 0; + for(i = 8; i >= 0; i--) { + res1 = (res1 << 1) | (in[i] < modm_m[i]); + res2 = (res2 << 1) | (in[i] > modm_m[i]); + } + return res1 > res2; +} + +void contract256_modm(unsigned char out[32], const bignum256modm in) { + U32TO8_LE(out + 0, (in[0]) | (in[1] << 30)); + U32TO8_LE(out + 4, (in[1] >> 2) | (in[2] << 28)); + U32TO8_LE(out + 8, (in[2] >> 4) | (in[3] << 26)); + U32TO8_LE(out + 12, (in[3] >> 6) | (in[4] << 24)); + U32TO8_LE(out + 16, (in[4] >> 8) | (in[5] << 22)); + U32TO8_LE(out + 20, (in[5] >> 10) | (in[6] << 20)); + U32TO8_LE(out + 24, (in[6] >> 12) | (in[7] << 18)); + U32TO8_LE(out + 28, (in[7] >> 14) | (in[8] << 16)); +} + +void contract256_window4_modm(signed char r[64], const bignum256modm in) { + char carry = 0; + signed char* quads = r; + bignum256modm_element_t i = 0, j = 0, v = 0; + + for(i = 0; i < 8; i += 2) { + v = in[i]; + for(j = 0; j < 7; j++) { + *quads++ = (v & 15); + v >>= 4; + } + v |= (in[i + 1] << 2); + for(j = 0; j < 8; j++) { + *quads++ = (v & 15); + v >>= 4; + } + } + v = in[8]; + *quads++ = (v & 15); + v >>= 4; + *quads++ = (v & 15); + v >>= 4; + *quads++ = (v & 15); + v >>= 4; + *quads++ = (v & 15); + v >>= 4; + + /* making it signed */ + carry = 0; + for(i = 0; i < 63; i++) { + r[i] += carry; + r[i + 1] += (r[i] >> 4); + r[i] &= 15; + carry = (r[i] >> 3); + r[i] -= (carry << 4); + } + r[63] += carry; +} + +void contract256_slidingwindow_modm(signed char r[256], const bignum256modm s, int windowsize) { + int i = 0, j = 0, k = 0, b = 0; + int m = (1 << (windowsize - 1)) - 1, soplen = 256; + signed char* bits = r; + bignum256modm_element_t v = 0; + + /* first put the binary expansion into r */ + for(i = 0; i < 8; i++) { + v = s[i]; + for(j = 0; j < 30; j++, v >>= 1) *bits++ = (v & 1); + } + v = s[8]; + for(j = 0; j < 16; j++, v >>= 1) *bits++ = (v & 1); + + /* Making it sliding window */ + for(j = 0; j < soplen; j++) { + if(!r[j]) continue; + + for(b = 1; (b < (soplen - j)) && (b <= 6); b++) { + if((r[j] + (r[j + b] << b)) <= m) { + r[j] += r[j + b] << b; + r[j + b] = 0; + } else if((r[j] - (r[j + b] << b)) >= -m) { + r[j] -= r[j + b] << b; + for(k = j + b; k < soplen; k++) { + if(!r[k]) { + r[k] = 1; + break; + } + r[k] = 0; + } + } else if(r[j + b]) { + break; + } + } + } +} + +void set256_modm(bignum256modm r, uint64_t v) { + r[0] = (bignum256modm_element_t)(v & 0x3fffffff); + v >>= 30; + r[1] = (bignum256modm_element_t)(v & 0x3fffffff); + v >>= 30; + r[2] = (bignum256modm_element_t)(v & 0x3fffffff); + r[3] = 0; + r[4] = 0; + r[5] = 0; + r[6] = 0; + r[7] = 0; + r[8] = 0; +} + +int get256_modm(uint64_t* v, const bignum256modm r) { + *v = 0; + int con1 = 0; + +#define NONZ(x) ((((((int64_t)(x)) - 1) >> 32) + 1) & 1) + bignum256modm_element_t c = 0; + c = r[0]; + *v += (uint64_t)c & 0x3fffffff; + c >>= 30; // 30 + c += r[1]; + *v += ((uint64_t)c & 0x3fffffff) << 30; + c >>= 30; // 60 + c += r[2]; + *v += ((uint64_t)c & 0xf) << 60; + con1 |= NONZ(c >> 4); + c >>= 30; // 64 bits + c += r[3]; + con1 |= NONZ(c); + c >>= 30; + c += r[4]; + con1 |= NONZ(c); + c >>= 30; + c += r[5]; + con1 |= NONZ(c); + c >>= 30; + c += r[6]; + con1 |= NONZ(c); + c >>= 30; + c += r[7]; + con1 |= NONZ(c); + c >>= 30; + c += r[8]; + con1 |= NONZ(c); + c >>= 30; + con1 |= NONZ(c); +#undef NONZ + + return con1 ^ 1; +} + +int eq256_modm(const bignum256modm x, const bignum256modm y) { + size_t differentbits = 0; + int len = bignum256modm_limb_size; + while(len--) { + differentbits |= (*x++ ^ *y++); + } + return (int)(1 & ((differentbits - 1) >> bignum256modm_bits_per_limb)); +} + +int cmp256_modm(const bignum256modm x, const bignum256modm y) { + int len = 2 * bignum256modm_limb_size; + uint32_t a_gt = 0; + uint32_t b_gt = 0; + + // 16B chunks + while(len--) { + const uint32_t ln = (const uint32_t)len; + const uint32_t a = (x[ln >> 1] >> 16 * (ln & 1)) & 0xffff; + const uint32_t b = (y[ln >> 1] >> 16 * (ln & 1)) & 0xffff; + + const uint32_t limb_a_gt = ((b - a) >> 16) & 1; + const uint32_t limb_b_gt = ((a - b) >> 16) & 1; + a_gt |= limb_a_gt & ~b_gt; + b_gt |= limb_b_gt & ~a_gt; + } + + return a_gt - b_gt; +} + +int iszero256_modm(const bignum256modm x) { + size_t differentbits = 0; + int len = bignum256modm_limb_size; + while(len--) { + differentbits |= (*x++); + } + return (int)(1 & ((differentbits - 1) >> bignum256modm_bits_per_limb)); +} + +void copy256_modm(bignum256modm r, const bignum256modm x) { + r[0] = x[0]; + r[1] = x[1]; + r[2] = x[2]; + r[3] = x[3]; + r[4] = x[4]; + r[5] = x[5]; + r[6] = x[6]; + r[7] = x[7]; + r[8] = x[8]; +} + +int check256_modm(const bignum256modm x) { + int ok = 1; + bignum256modm t = {0}, z = {0}; + + ok &= iszero256_modm(x) ^ 1; + barrett_reduce256_modm(t, z, x); + ok &= eq256_modm(t, x); + return ok; +} + +void mulsub256_modm( + bignum256modm r, + const bignum256modm a, + const bignum256modm b, + const bignum256modm c) { + //(cc - aa * bb) % l + bignum256modm t = {0}; + mul256_modm(t, a, b); + sub256_modm(r, c, t); +} + +void muladd256_modm( + bignum256modm r, + const bignum256modm a, + const bignum256modm b, + const bignum256modm c) { + //(cc + aa * bb) % l + bignum256modm t = {0}; + mul256_modm(t, a, b); + add256_modm(r, c, t); +} diff --git a/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.h b/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.h new file mode 100644 index 000000000..021edc19e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ed25519_donna/modm_donna_32bit.h @@ -0,0 +1,87 @@ +/* + Public domain by Andrew M. +*/ + +/* + Arithmetic modulo the group order n = 2^252 + 27742317777372353535851937790883648493 = 7237005577332262213973186563042994240857116359379907606001950938285454250989 + + k = 32 + b = 1 << 8 = 256 + m = 2^252 + 27742317777372353535851937790883648493 = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed + mu = floor( b^(k*2) / m ) = 0xfffffffffffffffffffffffffffffffeb2106215d086329a7ed9ce5a30a2c131b +*/ + +#define bignum256modm_bits_per_limb 30 +#define bignum256modm_limb_size 9 + +typedef uint32_t bignum256modm_element_t; +typedef bignum256modm_element_t bignum256modm[9]; + +/* see HAC, Alg. 14.42 Step 4 */ +void reduce256_modm(bignum256modm r); + +/* + Barrett reduction, see HAC, Alg. 14.42 + + Instead of passing in x, pre-process in to q1 and r1 for efficiency +*/ +void barrett_reduce256_modm(bignum256modm r, const bignum256modm q1, const bignum256modm r1); + +/* addition modulo m */ +void add256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y); + +/* -x modulo m */ +void neg256_modm(bignum256modm r, const bignum256modm x); + +/* subtraction x-y modulo m */ +void sub256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y); + +/* multiplication modulo m */ +void mul256_modm(bignum256modm r, const bignum256modm x, const bignum256modm y); + +void expand256_modm(bignum256modm out, const unsigned char* in, size_t len); + +void expand_raw256_modm(bignum256modm out, const unsigned char in[32]); + +int is_reduced256_modm(const bignum256modm in); + +void contract256_modm(unsigned char out[32], const bignum256modm in); + +void contract256_window4_modm(signed char r[64], const bignum256modm in); + +void contract256_slidingwindow_modm(signed char r[256], const bignum256modm s, int windowsize); + +/* 64bit uint to scalar value */ +void set256_modm(bignum256modm r, uint64_t v); + +/* scalar value to 64bit uint */ +int get256_modm(uint64_t* v, const bignum256modm r); + +/* equality test on two reduced scalar values */ +int eq256_modm(const bignum256modm x, const bignum256modm y); + +/* comparison of two reduced scalar values */ +int cmp256_modm(const bignum256modm x, const bignum256modm y); + +/* scalar null check, has to be reduced */ +int iszero256_modm(const bignum256modm x); + +/* simple copy, no reduction */ +void copy256_modm(bignum256modm r, const bignum256modm x); + +/* check if nonzero && same after reduction */ +int check256_modm(const bignum256modm x); + +/* (cc - aa * bb) % l */ +void mulsub256_modm( + bignum256modm r, + const bignum256modm a, + const bignum256modm b, + const bignum256modm c); + +/* (cc + aa * bb) % l */ +void muladd256_modm( + bignum256modm r, + const bignum256modm a, + const bignum256modm b, + const bignum256modm c); diff --git a/applications/external/flipbip/lib/crypto/groestl.c b/applications/external/flipbip/lib/crypto/groestl.c new file mode 100644 index 000000000..a77da80f4 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/groestl.c @@ -0,0 +1,755 @@ +/* Groestl hash from https://github.com/Groestlcoin/vanitygen + * Trezor adaptation by Yura Pakhuchiy . */ +/* + * Groestl implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "groestl_internal.h" +#include "groestl.h" +#include "memzero.h" + +#define C32e(x) \ + ((SPH_C32(x) >> 24) | ((SPH_C32(x) >> 8) & SPH_C32(0x0000FF00)) | \ + ((SPH_C32(x) << 8) & SPH_C32(0x00FF0000)) | ((SPH_C32(x) << 24) & SPH_C32(0xFF000000))) +#define dec32e_aligned sph_dec32le_aligned +#define enc32e sph_enc32le +#define B32_0(x) ((x)&0xFF) +#define B32_1(x) (((x) >> 8) & 0xFF) +#define B32_2(x) (((x) >> 16) & 0xFF) +#define B32_3(x) ((x) >> 24) + +#define R32u(u, d) SPH_T32(((u) << 16) | ((d) >> 16)) +#define R32d(u, d) SPH_T32(((u) >> 16) | ((d) << 16)) + +#define PC32up(j, r) ((sph_u32)((j) + (r))) +#define PC32dn(j, r) 0 +#define QC32up(j, r) SPH_C32(0xFFFFFFFF) +#define QC32dn(j, r) (((sph_u32)(r) << 24) ^ SPH_T32(~((sph_u32)(j) << 24))) + +#define C64e(x) \ + ((SPH_C64(x) >> 56) | ((SPH_C64(x) >> 40) & SPH_C64(0x000000000000FF00)) | \ + ((SPH_C64(x) >> 24) & SPH_C64(0x0000000000FF0000)) | \ + ((SPH_C64(x) >> 8) & SPH_C64(0x00000000FF000000)) | \ + ((SPH_C64(x) << 8) & SPH_C64(0x000000FF00000000)) | \ + ((SPH_C64(x) << 24) & SPH_C64(0x0000FF0000000000)) | \ + ((SPH_C64(x) << 40) & SPH_C64(0x00FF000000000000)) | \ + ((SPH_C64(x) << 56) & SPH_C64(0xFF00000000000000))) +#define dec64e_aligned sph_dec64le_aligned +#define enc64e sph_enc64le +#define B64_0(x) ((x)&0xFF) +#define B64_1(x) (((x) >> 8) & 0xFF) +#define B64_2(x) (((x) >> 16) & 0xFF) +#define B64_3(x) (((x) >> 24) & 0xFF) +#define B64_4(x) (((x) >> 32) & 0xFF) +#define B64_5(x) (((x) >> 40) & 0xFF) +#define B64_6(x) (((x) >> 48) & 0xFF) +#define B64_7(x) ((x) >> 56) +#define R64 SPH_ROTL64 +#define PC64(j, r) ((sph_u64)((j) + (r))) +#define QC64(j, r) (((sph_u64)(r) << 56) ^ SPH_T64(~((sph_u64)(j) << 56))) + +static const sph_u32 T0up[] = { + C32e(0xc632f4a5), C32e(0xf86f9784), C32e(0xee5eb099), C32e(0xf67a8c8d), C32e(0xffe8170d), + C32e(0xd60adcbd), C32e(0xde16c8b1), C32e(0x916dfc54), C32e(0x6090f050), C32e(0x02070503), + C32e(0xce2ee0a9), C32e(0x56d1877d), C32e(0xe7cc2b19), C32e(0xb513a662), C32e(0x4d7c31e6), + C32e(0xec59b59a), C32e(0x8f40cf45), C32e(0x1fa3bc9d), C32e(0x8949c040), C32e(0xfa689287), + C32e(0xefd03f15), C32e(0xb29426eb), C32e(0x8ece40c9), C32e(0xfbe61d0b), C32e(0x416e2fec), + C32e(0xb31aa967), C32e(0x5f431cfd), C32e(0x456025ea), C32e(0x23f9dabf), C32e(0x535102f7), + C32e(0xe445a196), C32e(0x9b76ed5b), C32e(0x75285dc2), C32e(0xe1c5241c), C32e(0x3dd4e9ae), + C32e(0x4cf2be6a), C32e(0x6c82ee5a), C32e(0x7ebdc341), C32e(0xf5f30602), C32e(0x8352d14f), + C32e(0x688ce45c), C32e(0x515607f4), C32e(0xd18d5c34), C32e(0xf9e11808), C32e(0xe24cae93), + C32e(0xab3e9573), C32e(0x6297f553), C32e(0x2a6b413f), C32e(0x081c140c), C32e(0x9563f652), + C32e(0x46e9af65), C32e(0x9d7fe25e), C32e(0x30487828), C32e(0x37cff8a1), C32e(0x0a1b110f), + C32e(0x2febc4b5), C32e(0x0e151b09), C32e(0x247e5a36), C32e(0x1badb69b), C32e(0xdf98473d), + C32e(0xcda76a26), C32e(0x4ef5bb69), C32e(0x7f334ccd), C32e(0xea50ba9f), C32e(0x123f2d1b), + C32e(0x1da4b99e), C32e(0x58c49c74), C32e(0x3446722e), C32e(0x3641772d), C32e(0xdc11cdb2), + C32e(0xb49d29ee), C32e(0x5b4d16fb), C32e(0xa4a501f6), C32e(0x76a1d74d), C32e(0xb714a361), + C32e(0x7d3449ce), C32e(0x52df8d7b), C32e(0xdd9f423e), C32e(0x5ecd9371), C32e(0x13b1a297), + C32e(0xa6a204f5), C32e(0xb901b868), C32e(0x00000000), C32e(0xc1b5742c), C32e(0x40e0a060), + C32e(0xe3c2211f), C32e(0x793a43c8), C32e(0xb69a2ced), C32e(0xd40dd9be), C32e(0x8d47ca46), + C32e(0x671770d9), C32e(0x72afdd4b), C32e(0x94ed79de), C32e(0x98ff67d4), C32e(0xb09323e8), + C32e(0x855bde4a), C32e(0xbb06bd6b), C32e(0xc5bb7e2a), C32e(0x4f7b34e5), C32e(0xedd73a16), + C32e(0x86d254c5), C32e(0x9af862d7), C32e(0x6699ff55), C32e(0x11b6a794), C32e(0x8ac04acf), + C32e(0xe9d93010), C32e(0x040e0a06), C32e(0xfe669881), C32e(0xa0ab0bf0), C32e(0x78b4cc44), + C32e(0x25f0d5ba), C32e(0x4b753ee3), C32e(0xa2ac0ef3), C32e(0x5d4419fe), C32e(0x80db5bc0), + C32e(0x0580858a), C32e(0x3fd3ecad), C32e(0x21fedfbc), C32e(0x70a8d848), C32e(0xf1fd0c04), + C32e(0x63197adf), C32e(0x772f58c1), C32e(0xaf309f75), C32e(0x42e7a563), C32e(0x20705030), + C32e(0xe5cb2e1a), C32e(0xfdef120e), C32e(0xbf08b76d), C32e(0x8155d44c), C32e(0x18243c14), + C32e(0x26795f35), C32e(0xc3b2712f), C32e(0xbe8638e1), C32e(0x35c8fda2), C32e(0x88c74fcc), + C32e(0x2e654b39), C32e(0x936af957), C32e(0x55580df2), C32e(0xfc619d82), C32e(0x7ab3c947), + C32e(0xc827efac), C32e(0xba8832e7), C32e(0x324f7d2b), C32e(0xe642a495), C32e(0xc03bfba0), + C32e(0x19aab398), C32e(0x9ef668d1), C32e(0xa322817f), C32e(0x44eeaa66), C32e(0x54d6827e), + C32e(0x3bdde6ab), C32e(0x0b959e83), C32e(0x8cc945ca), C32e(0xc7bc7b29), C32e(0x6b056ed3), + C32e(0x286c443c), C32e(0xa72c8b79), C32e(0xbc813de2), C32e(0x1631271d), C32e(0xad379a76), + C32e(0xdb964d3b), C32e(0x649efa56), C32e(0x74a6d24e), C32e(0x1436221e), C32e(0x92e476db), + C32e(0x0c121e0a), C32e(0x48fcb46c), C32e(0xb88f37e4), C32e(0x9f78e75d), C32e(0xbd0fb26e), + C32e(0x43692aef), C32e(0xc435f1a6), C32e(0x39dae3a8), C32e(0x31c6f7a4), C32e(0xd38a5937), + C32e(0xf274868b), C32e(0xd5835632), C32e(0x8b4ec543), C32e(0x6e85eb59), C32e(0xda18c2b7), + C32e(0x018e8f8c), C32e(0xb11dac64), C32e(0x9cf16dd2), C32e(0x49723be0), C32e(0xd81fc7b4), + C32e(0xacb915fa), C32e(0xf3fa0907), C32e(0xcfa06f25), C32e(0xca20eaaf), C32e(0xf47d898e), + C32e(0x476720e9), C32e(0x10382818), C32e(0x6f0b64d5), C32e(0xf0738388), C32e(0x4afbb16f), + C32e(0x5cca9672), C32e(0x38546c24), C32e(0x575f08f1), C32e(0x732152c7), C32e(0x9764f351), + C32e(0xcbae6523), C32e(0xa125847c), C32e(0xe857bf9c), C32e(0x3e5d6321), C32e(0x96ea7cdd), + C32e(0x611e7fdc), C32e(0x0d9c9186), C32e(0x0f9b9485), C32e(0xe04bab90), C32e(0x7cbac642), + C32e(0x712657c4), C32e(0xcc29e5aa), C32e(0x90e373d8), C32e(0x06090f05), C32e(0xf7f40301), + C32e(0x1c2a3612), C32e(0xc23cfea3), C32e(0x6a8be15f), C32e(0xaebe10f9), C32e(0x69026bd0), + C32e(0x17bfa891), C32e(0x9971e858), C32e(0x3a536927), C32e(0x27f7d0b9), C32e(0xd9914838), + C32e(0xebde3513), C32e(0x2be5ceb3), C32e(0x22775533), C32e(0xd204d6bb), C32e(0xa9399070), + C32e(0x07878089), C32e(0x33c1f2a7), C32e(0x2decc1b6), C32e(0x3c5a6622), C32e(0x15b8ad92), + C32e(0xc9a96020), C32e(0x875cdb49), C32e(0xaab01aff), C32e(0x50d88878), C32e(0xa52b8e7a), + C32e(0x03898a8f), C32e(0x594a13f8), C32e(0x09929b80), C32e(0x1a233917), C32e(0x651075da), + C32e(0xd7845331), C32e(0x84d551c6), C32e(0xd003d3b8), C32e(0x82dc5ec3), C32e(0x29e2cbb0), + C32e(0x5ac39977), C32e(0x1e2d3311), C32e(0x7b3d46cb), C32e(0xa8b71ffc), C32e(0x6d0c61d6), + C32e(0x2c624e3a)}; + +static const sph_u32 T0dn[] = { + C32e(0xf497a5c6), C32e(0x97eb84f8), C32e(0xb0c799ee), C32e(0x8cf78df6), C32e(0x17e50dff), + C32e(0xdcb7bdd6), C32e(0xc8a7b1de), C32e(0xfc395491), C32e(0xf0c05060), C32e(0x05040302), + C32e(0xe087a9ce), C32e(0x87ac7d56), C32e(0x2bd519e7), C32e(0xa67162b5), C32e(0x319ae64d), + C32e(0xb5c39aec), C32e(0xcf05458f), C32e(0xbc3e9d1f), C32e(0xc0094089), C32e(0x92ef87fa), + C32e(0x3fc515ef), C32e(0x267febb2), C32e(0x4007c98e), C32e(0x1ded0bfb), C32e(0x2f82ec41), + C32e(0xa97d67b3), C32e(0x1cbefd5f), C32e(0x258aea45), C32e(0xda46bf23), C32e(0x02a6f753), + C32e(0xa1d396e4), C32e(0xed2d5b9b), C32e(0x5deac275), C32e(0x24d91ce1), C32e(0xe97aae3d), + C32e(0xbe986a4c), C32e(0xeed85a6c), C32e(0xc3fc417e), C32e(0x06f102f5), C32e(0xd11d4f83), + C32e(0xe4d05c68), C32e(0x07a2f451), C32e(0x5cb934d1), C32e(0x18e908f9), C32e(0xaedf93e2), + C32e(0x954d73ab), C32e(0xf5c45362), C32e(0x41543f2a), C32e(0x14100c08), C32e(0xf6315295), + C32e(0xaf8c6546), C32e(0xe2215e9d), C32e(0x78602830), C32e(0xf86ea137), C32e(0x11140f0a), + C32e(0xc45eb52f), C32e(0x1b1c090e), C32e(0x5a483624), C32e(0xb6369b1b), C32e(0x47a53ddf), + C32e(0x6a8126cd), C32e(0xbb9c694e), C32e(0x4cfecd7f), C32e(0xbacf9fea), C32e(0x2d241b12), + C32e(0xb93a9e1d), C32e(0x9cb07458), C32e(0x72682e34), C32e(0x776c2d36), C32e(0xcda3b2dc), + C32e(0x2973eeb4), C32e(0x16b6fb5b), C32e(0x0153f6a4), C32e(0xd7ec4d76), C32e(0xa37561b7), + C32e(0x49face7d), C32e(0x8da47b52), C32e(0x42a13edd), C32e(0x93bc715e), C32e(0xa2269713), + C32e(0x0457f5a6), C32e(0xb86968b9), C32e(0x00000000), C32e(0x74992cc1), C32e(0xa0806040), + C32e(0x21dd1fe3), C32e(0x43f2c879), C32e(0x2c77edb6), C32e(0xd9b3bed4), C32e(0xca01468d), + C32e(0x70ced967), C32e(0xdde44b72), C32e(0x7933de94), C32e(0x672bd498), C32e(0x237be8b0), + C32e(0xde114a85), C32e(0xbd6d6bbb), C32e(0x7e912ac5), C32e(0x349ee54f), C32e(0x3ac116ed), + C32e(0x5417c586), C32e(0x622fd79a), C32e(0xffcc5566), C32e(0xa7229411), C32e(0x4a0fcf8a), + C32e(0x30c910e9), C32e(0x0a080604), C32e(0x98e781fe), C32e(0x0b5bf0a0), C32e(0xccf04478), + C32e(0xd54aba25), C32e(0x3e96e34b), C32e(0x0e5ff3a2), C32e(0x19bafe5d), C32e(0x5b1bc080), + C32e(0x850a8a05), C32e(0xec7ead3f), C32e(0xdf42bc21), C32e(0xd8e04870), C32e(0x0cf904f1), + C32e(0x7ac6df63), C32e(0x58eec177), C32e(0x9f4575af), C32e(0xa5846342), C32e(0x50403020), + C32e(0x2ed11ae5), C32e(0x12e10efd), C32e(0xb7656dbf), C32e(0xd4194c81), C32e(0x3c301418), + C32e(0x5f4c3526), C32e(0x719d2fc3), C32e(0x3867e1be), C32e(0xfd6aa235), C32e(0x4f0bcc88), + C32e(0x4b5c392e), C32e(0xf93d5793), C32e(0x0daaf255), C32e(0x9de382fc), C32e(0xc9f4477a), + C32e(0xef8bacc8), C32e(0x326fe7ba), C32e(0x7d642b32), C32e(0xa4d795e6), C32e(0xfb9ba0c0), + C32e(0xb3329819), C32e(0x6827d19e), C32e(0x815d7fa3), C32e(0xaa886644), C32e(0x82a87e54), + C32e(0xe676ab3b), C32e(0x9e16830b), C32e(0x4503ca8c), C32e(0x7b9529c7), C32e(0x6ed6d36b), + C32e(0x44503c28), C32e(0x8b5579a7), C32e(0x3d63e2bc), C32e(0x272c1d16), C32e(0x9a4176ad), + C32e(0x4dad3bdb), C32e(0xfac85664), C32e(0xd2e84e74), C32e(0x22281e14), C32e(0x763fdb92), + C32e(0x1e180a0c), C32e(0xb4906c48), C32e(0x376be4b8), C32e(0xe7255d9f), C32e(0xb2616ebd), + C32e(0x2a86ef43), C32e(0xf193a6c4), C32e(0xe372a839), C32e(0xf762a431), C32e(0x59bd37d3), + C32e(0x86ff8bf2), C32e(0x56b132d5), C32e(0xc50d438b), C32e(0xebdc596e), C32e(0xc2afb7da), + C32e(0x8f028c01), C32e(0xac7964b1), C32e(0x6d23d29c), C32e(0x3b92e049), C32e(0xc7abb4d8), + C32e(0x1543faac), C32e(0x09fd07f3), C32e(0x6f8525cf), C32e(0xea8fafca), C32e(0x89f38ef4), + C32e(0x208ee947), C32e(0x28201810), C32e(0x64ded56f), C32e(0x83fb88f0), C32e(0xb1946f4a), + C32e(0x96b8725c), C32e(0x6c702438), C32e(0x08aef157), C32e(0x52e6c773), C32e(0xf3355197), + C32e(0x658d23cb), C32e(0x84597ca1), C32e(0xbfcb9ce8), C32e(0x637c213e), C32e(0x7c37dd96), + C32e(0x7fc2dc61), C32e(0x911a860d), C32e(0x941e850f), C32e(0xabdb90e0), C32e(0xc6f8427c), + C32e(0x57e2c471), C32e(0xe583aacc), C32e(0x733bd890), C32e(0x0f0c0506), C32e(0x03f501f7), + C32e(0x3638121c), C32e(0xfe9fa3c2), C32e(0xe1d45f6a), C32e(0x1047f9ae), C32e(0x6bd2d069), + C32e(0xa82e9117), C32e(0xe8295899), C32e(0x6974273a), C32e(0xd04eb927), C32e(0x48a938d9), + C32e(0x35cd13eb), C32e(0xce56b32b), C32e(0x55443322), C32e(0xd6bfbbd2), C32e(0x904970a9), + C32e(0x800e8907), C32e(0xf266a733), C32e(0xc15ab62d), C32e(0x6678223c), C32e(0xad2a9215), + C32e(0x608920c9), C32e(0xdb154987), C32e(0x1a4fffaa), C32e(0x88a07850), C32e(0x8e517aa5), + C32e(0x8a068f03), C32e(0x13b2f859), C32e(0x9b128009), C32e(0x3934171a), C32e(0x75cada65), + C32e(0x53b531d7), C32e(0x5113c684), C32e(0xd3bbb8d0), C32e(0x5e1fc382), C32e(0xcb52b029), + C32e(0x99b4775a), C32e(0x333c111e), C32e(0x46f6cb7b), C32e(0x1f4bfca8), C32e(0x61dad66d), + C32e(0x4e583a2c)}; + +static const sph_u32 T1up[] = { + C32e(0xc6c632f4), C32e(0xf8f86f97), C32e(0xeeee5eb0), C32e(0xf6f67a8c), C32e(0xffffe817), + C32e(0xd6d60adc), C32e(0xdede16c8), C32e(0x91916dfc), C32e(0x606090f0), C32e(0x02020705), + C32e(0xcece2ee0), C32e(0x5656d187), C32e(0xe7e7cc2b), C32e(0xb5b513a6), C32e(0x4d4d7c31), + C32e(0xecec59b5), C32e(0x8f8f40cf), C32e(0x1f1fa3bc), C32e(0x898949c0), C32e(0xfafa6892), + C32e(0xefefd03f), C32e(0xb2b29426), C32e(0x8e8ece40), C32e(0xfbfbe61d), C32e(0x41416e2f), + C32e(0xb3b31aa9), C32e(0x5f5f431c), C32e(0x45456025), C32e(0x2323f9da), C32e(0x53535102), + C32e(0xe4e445a1), C32e(0x9b9b76ed), C32e(0x7575285d), C32e(0xe1e1c524), C32e(0x3d3dd4e9), + C32e(0x4c4cf2be), C32e(0x6c6c82ee), C32e(0x7e7ebdc3), C32e(0xf5f5f306), C32e(0x838352d1), + C32e(0x68688ce4), C32e(0x51515607), C32e(0xd1d18d5c), C32e(0xf9f9e118), C32e(0xe2e24cae), + C32e(0xabab3e95), C32e(0x626297f5), C32e(0x2a2a6b41), C32e(0x08081c14), C32e(0x959563f6), + C32e(0x4646e9af), C32e(0x9d9d7fe2), C32e(0x30304878), C32e(0x3737cff8), C32e(0x0a0a1b11), + C32e(0x2f2febc4), C32e(0x0e0e151b), C32e(0x24247e5a), C32e(0x1b1badb6), C32e(0xdfdf9847), + C32e(0xcdcda76a), C32e(0x4e4ef5bb), C32e(0x7f7f334c), C32e(0xeaea50ba), C32e(0x12123f2d), + C32e(0x1d1da4b9), C32e(0x5858c49c), C32e(0x34344672), C32e(0x36364177), C32e(0xdcdc11cd), + C32e(0xb4b49d29), C32e(0x5b5b4d16), C32e(0xa4a4a501), C32e(0x7676a1d7), C32e(0xb7b714a3), + C32e(0x7d7d3449), C32e(0x5252df8d), C32e(0xdddd9f42), C32e(0x5e5ecd93), C32e(0x1313b1a2), + C32e(0xa6a6a204), C32e(0xb9b901b8), C32e(0x00000000), C32e(0xc1c1b574), C32e(0x4040e0a0), + C32e(0xe3e3c221), C32e(0x79793a43), C32e(0xb6b69a2c), C32e(0xd4d40dd9), C32e(0x8d8d47ca), + C32e(0x67671770), C32e(0x7272afdd), C32e(0x9494ed79), C32e(0x9898ff67), C32e(0xb0b09323), + C32e(0x85855bde), C32e(0xbbbb06bd), C32e(0xc5c5bb7e), C32e(0x4f4f7b34), C32e(0xededd73a), + C32e(0x8686d254), C32e(0x9a9af862), C32e(0x666699ff), C32e(0x1111b6a7), C32e(0x8a8ac04a), + C32e(0xe9e9d930), C32e(0x04040e0a), C32e(0xfefe6698), C32e(0xa0a0ab0b), C32e(0x7878b4cc), + C32e(0x2525f0d5), C32e(0x4b4b753e), C32e(0xa2a2ac0e), C32e(0x5d5d4419), C32e(0x8080db5b), + C32e(0x05058085), C32e(0x3f3fd3ec), C32e(0x2121fedf), C32e(0x7070a8d8), C32e(0xf1f1fd0c), + C32e(0x6363197a), C32e(0x77772f58), C32e(0xafaf309f), C32e(0x4242e7a5), C32e(0x20207050), + C32e(0xe5e5cb2e), C32e(0xfdfdef12), C32e(0xbfbf08b7), C32e(0x818155d4), C32e(0x1818243c), + C32e(0x2626795f), C32e(0xc3c3b271), C32e(0xbebe8638), C32e(0x3535c8fd), C32e(0x8888c74f), + C32e(0x2e2e654b), C32e(0x93936af9), C32e(0x5555580d), C32e(0xfcfc619d), C32e(0x7a7ab3c9), + C32e(0xc8c827ef), C32e(0xbaba8832), C32e(0x32324f7d), C32e(0xe6e642a4), C32e(0xc0c03bfb), + C32e(0x1919aab3), C32e(0x9e9ef668), C32e(0xa3a32281), C32e(0x4444eeaa), C32e(0x5454d682), + C32e(0x3b3bdde6), C32e(0x0b0b959e), C32e(0x8c8cc945), C32e(0xc7c7bc7b), C32e(0x6b6b056e), + C32e(0x28286c44), C32e(0xa7a72c8b), C32e(0xbcbc813d), C32e(0x16163127), C32e(0xadad379a), + C32e(0xdbdb964d), C32e(0x64649efa), C32e(0x7474a6d2), C32e(0x14143622), C32e(0x9292e476), + C32e(0x0c0c121e), C32e(0x4848fcb4), C32e(0xb8b88f37), C32e(0x9f9f78e7), C32e(0xbdbd0fb2), + C32e(0x4343692a), C32e(0xc4c435f1), C32e(0x3939dae3), C32e(0x3131c6f7), C32e(0xd3d38a59), + C32e(0xf2f27486), C32e(0xd5d58356), C32e(0x8b8b4ec5), C32e(0x6e6e85eb), C32e(0xdada18c2), + C32e(0x01018e8f), C32e(0xb1b11dac), C32e(0x9c9cf16d), C32e(0x4949723b), C32e(0xd8d81fc7), + C32e(0xacacb915), C32e(0xf3f3fa09), C32e(0xcfcfa06f), C32e(0xcaca20ea), C32e(0xf4f47d89), + C32e(0x47476720), C32e(0x10103828), C32e(0x6f6f0b64), C32e(0xf0f07383), C32e(0x4a4afbb1), + C32e(0x5c5cca96), C32e(0x3838546c), C32e(0x57575f08), C32e(0x73732152), C32e(0x979764f3), + C32e(0xcbcbae65), C32e(0xa1a12584), C32e(0xe8e857bf), C32e(0x3e3e5d63), C32e(0x9696ea7c), + C32e(0x61611e7f), C32e(0x0d0d9c91), C32e(0x0f0f9b94), C32e(0xe0e04bab), C32e(0x7c7cbac6), + C32e(0x71712657), C32e(0xcccc29e5), C32e(0x9090e373), C32e(0x0606090f), C32e(0xf7f7f403), + C32e(0x1c1c2a36), C32e(0xc2c23cfe), C32e(0x6a6a8be1), C32e(0xaeaebe10), C32e(0x6969026b), + C32e(0x1717bfa8), C32e(0x999971e8), C32e(0x3a3a5369), C32e(0x2727f7d0), C32e(0xd9d99148), + C32e(0xebebde35), C32e(0x2b2be5ce), C32e(0x22227755), C32e(0xd2d204d6), C32e(0xa9a93990), + C32e(0x07078780), C32e(0x3333c1f2), C32e(0x2d2decc1), C32e(0x3c3c5a66), C32e(0x1515b8ad), + C32e(0xc9c9a960), C32e(0x87875cdb), C32e(0xaaaab01a), C32e(0x5050d888), C32e(0xa5a52b8e), + C32e(0x0303898a), C32e(0x59594a13), C32e(0x0909929b), C32e(0x1a1a2339), C32e(0x65651075), + C32e(0xd7d78453), C32e(0x8484d551), C32e(0xd0d003d3), C32e(0x8282dc5e), C32e(0x2929e2cb), + C32e(0x5a5ac399), C32e(0x1e1e2d33), C32e(0x7b7b3d46), C32e(0xa8a8b71f), C32e(0x6d6d0c61), + C32e(0x2c2c624e)}; + +static const sph_u32 T1dn[] = { + C32e(0xa5f497a5), C32e(0x8497eb84), C32e(0x99b0c799), C32e(0x8d8cf78d), C32e(0x0d17e50d), + C32e(0xbddcb7bd), C32e(0xb1c8a7b1), C32e(0x54fc3954), C32e(0x50f0c050), C32e(0x03050403), + C32e(0xa9e087a9), C32e(0x7d87ac7d), C32e(0x192bd519), C32e(0x62a67162), C32e(0xe6319ae6), + C32e(0x9ab5c39a), C32e(0x45cf0545), C32e(0x9dbc3e9d), C32e(0x40c00940), C32e(0x8792ef87), + C32e(0x153fc515), C32e(0xeb267feb), C32e(0xc94007c9), C32e(0x0b1ded0b), C32e(0xec2f82ec), + C32e(0x67a97d67), C32e(0xfd1cbefd), C32e(0xea258aea), C32e(0xbfda46bf), C32e(0xf702a6f7), + C32e(0x96a1d396), C32e(0x5bed2d5b), C32e(0xc25deac2), C32e(0x1c24d91c), C32e(0xaee97aae), + C32e(0x6abe986a), C32e(0x5aeed85a), C32e(0x41c3fc41), C32e(0x0206f102), C32e(0x4fd11d4f), + C32e(0x5ce4d05c), C32e(0xf407a2f4), C32e(0x345cb934), C32e(0x0818e908), C32e(0x93aedf93), + C32e(0x73954d73), C32e(0x53f5c453), C32e(0x3f41543f), C32e(0x0c14100c), C32e(0x52f63152), + C32e(0x65af8c65), C32e(0x5ee2215e), C32e(0x28786028), C32e(0xa1f86ea1), C32e(0x0f11140f), + C32e(0xb5c45eb5), C32e(0x091b1c09), C32e(0x365a4836), C32e(0x9bb6369b), C32e(0x3d47a53d), + C32e(0x266a8126), C32e(0x69bb9c69), C32e(0xcd4cfecd), C32e(0x9fbacf9f), C32e(0x1b2d241b), + C32e(0x9eb93a9e), C32e(0x749cb074), C32e(0x2e72682e), C32e(0x2d776c2d), C32e(0xb2cda3b2), + C32e(0xee2973ee), C32e(0xfb16b6fb), C32e(0xf60153f6), C32e(0x4dd7ec4d), C32e(0x61a37561), + C32e(0xce49face), C32e(0x7b8da47b), C32e(0x3e42a13e), C32e(0x7193bc71), C32e(0x97a22697), + C32e(0xf50457f5), C32e(0x68b86968), C32e(0x00000000), C32e(0x2c74992c), C32e(0x60a08060), + C32e(0x1f21dd1f), C32e(0xc843f2c8), C32e(0xed2c77ed), C32e(0xbed9b3be), C32e(0x46ca0146), + C32e(0xd970ced9), C32e(0x4bdde44b), C32e(0xde7933de), C32e(0xd4672bd4), C32e(0xe8237be8), + C32e(0x4ade114a), C32e(0x6bbd6d6b), C32e(0x2a7e912a), C32e(0xe5349ee5), C32e(0x163ac116), + C32e(0xc55417c5), C32e(0xd7622fd7), C32e(0x55ffcc55), C32e(0x94a72294), C32e(0xcf4a0fcf), + C32e(0x1030c910), C32e(0x060a0806), C32e(0x8198e781), C32e(0xf00b5bf0), C32e(0x44ccf044), + C32e(0xbad54aba), C32e(0xe33e96e3), C32e(0xf30e5ff3), C32e(0xfe19bafe), C32e(0xc05b1bc0), + C32e(0x8a850a8a), C32e(0xadec7ead), C32e(0xbcdf42bc), C32e(0x48d8e048), C32e(0x040cf904), + C32e(0xdf7ac6df), C32e(0xc158eec1), C32e(0x759f4575), C32e(0x63a58463), C32e(0x30504030), + C32e(0x1a2ed11a), C32e(0x0e12e10e), C32e(0x6db7656d), C32e(0x4cd4194c), C32e(0x143c3014), + C32e(0x355f4c35), C32e(0x2f719d2f), C32e(0xe13867e1), C32e(0xa2fd6aa2), C32e(0xcc4f0bcc), + C32e(0x394b5c39), C32e(0x57f93d57), C32e(0xf20daaf2), C32e(0x829de382), C32e(0x47c9f447), + C32e(0xacef8bac), C32e(0xe7326fe7), C32e(0x2b7d642b), C32e(0x95a4d795), C32e(0xa0fb9ba0), + C32e(0x98b33298), C32e(0xd16827d1), C32e(0x7f815d7f), C32e(0x66aa8866), C32e(0x7e82a87e), + C32e(0xabe676ab), C32e(0x839e1683), C32e(0xca4503ca), C32e(0x297b9529), C32e(0xd36ed6d3), + C32e(0x3c44503c), C32e(0x798b5579), C32e(0xe23d63e2), C32e(0x1d272c1d), C32e(0x769a4176), + C32e(0x3b4dad3b), C32e(0x56fac856), C32e(0x4ed2e84e), C32e(0x1e22281e), C32e(0xdb763fdb), + C32e(0x0a1e180a), C32e(0x6cb4906c), C32e(0xe4376be4), C32e(0x5de7255d), C32e(0x6eb2616e), + C32e(0xef2a86ef), C32e(0xa6f193a6), C32e(0xa8e372a8), C32e(0xa4f762a4), C32e(0x3759bd37), + C32e(0x8b86ff8b), C32e(0x3256b132), C32e(0x43c50d43), C32e(0x59ebdc59), C32e(0xb7c2afb7), + C32e(0x8c8f028c), C32e(0x64ac7964), C32e(0xd26d23d2), C32e(0xe03b92e0), C32e(0xb4c7abb4), + C32e(0xfa1543fa), C32e(0x0709fd07), C32e(0x256f8525), C32e(0xafea8faf), C32e(0x8e89f38e), + C32e(0xe9208ee9), C32e(0x18282018), C32e(0xd564ded5), C32e(0x8883fb88), C32e(0x6fb1946f), + C32e(0x7296b872), C32e(0x246c7024), C32e(0xf108aef1), C32e(0xc752e6c7), C32e(0x51f33551), + C32e(0x23658d23), C32e(0x7c84597c), C32e(0x9cbfcb9c), C32e(0x21637c21), C32e(0xdd7c37dd), + C32e(0xdc7fc2dc), C32e(0x86911a86), C32e(0x85941e85), C32e(0x90abdb90), C32e(0x42c6f842), + C32e(0xc457e2c4), C32e(0xaae583aa), C32e(0xd8733bd8), C32e(0x050f0c05), C32e(0x0103f501), + C32e(0x12363812), C32e(0xa3fe9fa3), C32e(0x5fe1d45f), C32e(0xf91047f9), C32e(0xd06bd2d0), + C32e(0x91a82e91), C32e(0x58e82958), C32e(0x27697427), C32e(0xb9d04eb9), C32e(0x3848a938), + C32e(0x1335cd13), C32e(0xb3ce56b3), C32e(0x33554433), C32e(0xbbd6bfbb), C32e(0x70904970), + C32e(0x89800e89), C32e(0xa7f266a7), C32e(0xb6c15ab6), C32e(0x22667822), C32e(0x92ad2a92), + C32e(0x20608920), C32e(0x49db1549), C32e(0xff1a4fff), C32e(0x7888a078), C32e(0x7a8e517a), + C32e(0x8f8a068f), C32e(0xf813b2f8), C32e(0x809b1280), C32e(0x17393417), C32e(0xda75cada), + C32e(0x3153b531), C32e(0xc65113c6), C32e(0xb8d3bbb8), C32e(0xc35e1fc3), C32e(0xb0cb52b0), + C32e(0x7799b477), C32e(0x11333c11), C32e(0xcb46f6cb), C32e(0xfc1f4bfc), C32e(0xd661dad6), + C32e(0x3a4e583a)}; + +#define DECL_STATE_SMALL sph_u32 H[16] = {0}; + +#define READ_STATE_SMALL(sc) \ + do { \ + memcpy(H, (sc)->state.narrow, sizeof H); \ + } while(0) + +#define WRITE_STATE_SMALL(sc) \ + do { \ + memcpy((sc)->state.narrow, H, sizeof H); \ + } while(0) + +#define XCAT(x, y) XCAT_(x, y) +#define XCAT_(x, y) x##y + +#define RSTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) \ + do { \ + t[d0] = T0up[B32_0(a[b0])] ^ T1up[B32_1(a[b1])] ^ T2up[B32_2(a[b2])] ^ \ + T3up[B32_3(a[b3])] ^ T0dn[B32_0(a[b4])] ^ T1dn[B32_1(a[b5])] ^ \ + T2dn[B32_2(a[b6])] ^ T3dn[B32_3(a[b7])]; \ + t[d1] = T0dn[B32_0(a[b0])] ^ T1dn[B32_1(a[b1])] ^ T2dn[B32_2(a[b2])] ^ \ + T3dn[B32_3(a[b3])] ^ T0up[B32_0(a[b4])] ^ T1up[B32_1(a[b5])] ^ \ + T2up[B32_2(a[b6])] ^ T3up[B32_3(a[b7])]; \ + } while(0) + +#define ROUND_SMALL_P(a, r) \ + do { \ + sph_u32 t[16]; \ + a[0x0] ^= PC32up(0x00, r); \ + a[0x1] ^= PC32dn(0x00, r); \ + a[0x2] ^= PC32up(0x10, r); \ + a[0x3] ^= PC32dn(0x10, r); \ + a[0x4] ^= PC32up(0x20, r); \ + a[0x5] ^= PC32dn(0x20, r); \ + a[0x6] ^= PC32up(0x30, r); \ + a[0x7] ^= PC32dn(0x30, r); \ + a[0x8] ^= PC32up(0x40, r); \ + a[0x9] ^= PC32dn(0x40, r); \ + a[0xA] ^= PC32up(0x50, r); \ + a[0xB] ^= PC32dn(0x50, r); \ + a[0xC] ^= PC32up(0x60, r); \ + a[0xD] ^= PC32dn(0x60, r); \ + a[0xE] ^= PC32up(0x70, r); \ + a[0xF] ^= PC32dn(0x70, r); \ + RSTT(0x0, 0x1, a, 0x0, 0x2, 0x4, 0x6, 0x9, 0xB, 0xD, 0xF); \ + RSTT(0x2, 0x3, a, 0x2, 0x4, 0x6, 0x8, 0xB, 0xD, 0xF, 0x1); \ + RSTT(0x4, 0x5, a, 0x4, 0x6, 0x8, 0xA, 0xD, 0xF, 0x1, 0x3); \ + RSTT(0x6, 0x7, a, 0x6, 0x8, 0xA, 0xC, 0xF, 0x1, 0x3, 0x5); \ + RSTT(0x8, 0x9, a, 0x8, 0xA, 0xC, 0xE, 0x1, 0x3, 0x5, 0x7); \ + RSTT(0xA, 0xB, a, 0xA, 0xC, 0xE, 0x0, 0x3, 0x5, 0x7, 0x9); \ + RSTT(0xC, 0xD, a, 0xC, 0xE, 0x0, 0x2, 0x5, 0x7, 0x9, 0xB); \ + RSTT(0xE, 0xF, a, 0xE, 0x0, 0x2, 0x4, 0x7, 0x9, 0xB, 0xD); \ + memcpy(a, t, sizeof t); \ + } while(0) + +#define ROUND_SMALL_Q(a, r) \ + do { \ + sph_u32 t[16]; \ + a[0x0] ^= QC32up(0x00, r); \ + a[0x1] ^= QC32dn(0x00, r); \ + a[0x2] ^= QC32up(0x10, r); \ + a[0x3] ^= QC32dn(0x10, r); \ + a[0x4] ^= QC32up(0x20, r); \ + a[0x5] ^= QC32dn(0x20, r); \ + a[0x6] ^= QC32up(0x30, r); \ + a[0x7] ^= QC32dn(0x30, r); \ + a[0x8] ^= QC32up(0x40, r); \ + a[0x9] ^= QC32dn(0x40, r); \ + a[0xA] ^= QC32up(0x50, r); \ + a[0xB] ^= QC32dn(0x50, r); \ + a[0xC] ^= QC32up(0x60, r); \ + a[0xD] ^= QC32dn(0x60, r); \ + a[0xE] ^= QC32up(0x70, r); \ + a[0xF] ^= QC32dn(0x70, r); \ + RSTT(0x0, 0x1, a, 0x2, 0x6, 0xA, 0xE, 0x1, 0x5, 0x9, 0xD); \ + RSTT(0x2, 0x3, a, 0x4, 0x8, 0xC, 0x0, 0x3, 0x7, 0xB, 0xF); \ + RSTT(0x4, 0x5, a, 0x6, 0xA, 0xE, 0x2, 0x5, 0x9, 0xD, 0x1); \ + RSTT(0x6, 0x7, a, 0x8, 0xC, 0x0, 0x4, 0x7, 0xB, 0xF, 0x3); \ + RSTT(0x8, 0x9, a, 0xA, 0xE, 0x2, 0x6, 0x9, 0xD, 0x1, 0x5); \ + RSTT(0xA, 0xB, a, 0xC, 0x0, 0x4, 0x8, 0xB, 0xF, 0x3, 0x7); \ + RSTT(0xC, 0xD, a, 0xE, 0x2, 0x6, 0xA, 0xD, 0x1, 0x5, 0x9); \ + RSTT(0xE, 0xF, a, 0x0, 0x4, 0x8, 0xC, 0xF, 0x3, 0x7, 0xB); \ + memcpy(a, t, sizeof t); \ + } while(0) + +#define PERM_SMALL_P(a) \ + do { \ + int r; \ + for(r = 0; r < 10; r++) ROUND_SMALL_P(a, r); \ + } while(0) + +#define PERM_SMALL_Q(a) \ + do { \ + int r; \ + for(r = 0; r < 10; r++) ROUND_SMALL_Q(a, r); \ + } while(0) + +#define COMPRESS_SMALL \ + do { \ + sph_u32 g[16], m[16]; \ + size_t u; \ + for(u = 0; u < 16; u++) { \ + m[u] = dec32e_aligned(buf + (u << 2)); \ + g[u] = m[u] ^ H[u]; \ + } \ + PERM_SMALL_P(g); \ + PERM_SMALL_Q(m); \ + for(u = 0; u < 16; u++) H[u] ^= g[u] ^ m[u]; \ + } while(0) + +#define FINAL_SMALL \ + do { \ + sph_u32 x[16]; \ + size_t u; \ + memcpy(x, H, sizeof x); \ + PERM_SMALL_P(x); \ + for(u = 0; u < 16; u++) H[u] ^= x[u]; \ + } while(0) + +#define DECL_STATE_BIG sph_u32 H[32] = {0}; + +#define READ_STATE_BIG(sc) \ + do { \ + memcpy(H, (sc)->state.narrow, sizeof H); \ + } while(0) + +#define WRITE_STATE_BIG(sc) \ + do { \ + memcpy((sc)->state.narrow, H, sizeof H); \ + } while(0) + +#define RBTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) \ + do { \ + sph_u32 fu2 = T0up[B32_2(a[b2])]; \ + sph_u32 fd2 = T0dn[B32_2(a[b2])]; \ + sph_u32 fu3 = T1up[B32_3(a[b3])]; \ + sph_u32 fd3 = T1dn[B32_3(a[b3])]; \ + sph_u32 fu6 = T0up[B32_2(a[b6])]; \ + sph_u32 fd6 = T0dn[B32_2(a[b6])]; \ + sph_u32 fu7 = T1up[B32_3(a[b7])]; \ + sph_u32 fd7 = T1dn[B32_3(a[b7])]; \ + t[d0] = T0up[B32_0(a[b0])] ^ T1up[B32_1(a[b1])] ^ R32u(fu2, fd2) ^ R32u(fu3, fd3) ^ \ + T0dn[B32_0(a[b4])] ^ T1dn[B32_1(a[b5])] ^ R32d(fu6, fd6) ^ R32d(fu7, fd7); \ + t[d1] = T0dn[B32_0(a[b0])] ^ T1dn[B32_1(a[b1])] ^ R32d(fu2, fd2) ^ R32d(fu3, fd3) ^ \ + T0up[B32_0(a[b4])] ^ T1up[B32_1(a[b5])] ^ R32u(fu6, fd6) ^ R32u(fu7, fd7); \ + } while(0) + +#define ROUND_BIG_P(a, r) \ + do { \ + sph_u32 t[32]; \ + size_t u; \ + a[0x00] ^= PC32up(0x00, r); \ + a[0x01] ^= PC32dn(0x00, r); \ + a[0x02] ^= PC32up(0x10, r); \ + a[0x03] ^= PC32dn(0x10, r); \ + a[0x04] ^= PC32up(0x20, r); \ + a[0x05] ^= PC32dn(0x20, r); \ + a[0x06] ^= PC32up(0x30, r); \ + a[0x07] ^= PC32dn(0x30, r); \ + a[0x08] ^= PC32up(0x40, r); \ + a[0x09] ^= PC32dn(0x40, r); \ + a[0x0A] ^= PC32up(0x50, r); \ + a[0x0B] ^= PC32dn(0x50, r); \ + a[0x0C] ^= PC32up(0x60, r); \ + a[0x0D] ^= PC32dn(0x60, r); \ + a[0x0E] ^= PC32up(0x70, r); \ + a[0x0F] ^= PC32dn(0x70, r); \ + a[0x10] ^= PC32up(0x80, r); \ + a[0x11] ^= PC32dn(0x80, r); \ + a[0x12] ^= PC32up(0x90, r); \ + a[0x13] ^= PC32dn(0x90, r); \ + a[0x14] ^= PC32up(0xA0, r); \ + a[0x15] ^= PC32dn(0xA0, r); \ + a[0x16] ^= PC32up(0xB0, r); \ + a[0x17] ^= PC32dn(0xB0, r); \ + a[0x18] ^= PC32up(0xC0, r); \ + a[0x19] ^= PC32dn(0xC0, r); \ + a[0x1A] ^= PC32up(0xD0, r); \ + a[0x1B] ^= PC32dn(0xD0, r); \ + a[0x1C] ^= PC32up(0xE0, r); \ + a[0x1D] ^= PC32dn(0xE0, r); \ + a[0x1E] ^= PC32up(0xF0, r); \ + a[0x1F] ^= PC32dn(0xF0, r); \ + for(u = 0; u < 32; u += 8) { \ + RBTT( \ + u + 0x00, \ + (u + 0x01) & 0x1F, \ + a, \ + u + 0x00, \ + (u + 0x02) & 0x1F, \ + (u + 0x04) & 0x1F, \ + (u + 0x06) & 0x1F, \ + (u + 0x09) & 0x1F, \ + (u + 0x0B) & 0x1F, \ + (u + 0x0D) & 0x1F, \ + (u + 0x17) & 0x1F); \ + RBTT( \ + u + 0x02, \ + (u + 0x03) & 0x1F, \ + a, \ + u + 0x02, \ + (u + 0x04) & 0x1F, \ + (u + 0x06) & 0x1F, \ + (u + 0x08) & 0x1F, \ + (u + 0x0B) & 0x1F, \ + (u + 0x0D) & 0x1F, \ + (u + 0x0F) & 0x1F, \ + (u + 0x19) & 0x1F); \ + RBTT( \ + u + 0x04, \ + (u + 0x05) & 0x1F, \ + a, \ + u + 0x04, \ + (u + 0x06) & 0x1F, \ + (u + 0x08) & 0x1F, \ + (u + 0x0A) & 0x1F, \ + (u + 0x0D) & 0x1F, \ + (u + 0x0F) & 0x1F, \ + (u + 0x11) & 0x1F, \ + (u + 0x1B) & 0x1F); \ + RBTT( \ + u + 0x06, \ + (u + 0x07) & 0x1F, \ + a, \ + u + 0x06, \ + (u + 0x08) & 0x1F, \ + (u + 0x0A) & 0x1F, \ + (u + 0x0C) & 0x1F, \ + (u + 0x0F) & 0x1F, \ + (u + 0x11) & 0x1F, \ + (u + 0x13) & 0x1F, \ + (u + 0x1D) & 0x1F); \ + } \ + memcpy(a, t, sizeof t); \ + } while(0) + +#define ROUND_BIG_Q(a, r) \ + do { \ + sph_u32 t[32]; \ + size_t u; \ + a[0x00] ^= QC32up(0x00, r); \ + a[0x01] ^= QC32dn(0x00, r); \ + a[0x02] ^= QC32up(0x10, r); \ + a[0x03] ^= QC32dn(0x10, r); \ + a[0x04] ^= QC32up(0x20, r); \ + a[0x05] ^= QC32dn(0x20, r); \ + a[0x06] ^= QC32up(0x30, r); \ + a[0x07] ^= QC32dn(0x30, r); \ + a[0x08] ^= QC32up(0x40, r); \ + a[0x09] ^= QC32dn(0x40, r); \ + a[0x0A] ^= QC32up(0x50, r); \ + a[0x0B] ^= QC32dn(0x50, r); \ + a[0x0C] ^= QC32up(0x60, r); \ + a[0x0D] ^= QC32dn(0x60, r); \ + a[0x0E] ^= QC32up(0x70, r); \ + a[0x0F] ^= QC32dn(0x70, r); \ + a[0x10] ^= QC32up(0x80, r); \ + a[0x11] ^= QC32dn(0x80, r); \ + a[0x12] ^= QC32up(0x90, r); \ + a[0x13] ^= QC32dn(0x90, r); \ + a[0x14] ^= QC32up(0xA0, r); \ + a[0x15] ^= QC32dn(0xA0, r); \ + a[0x16] ^= QC32up(0xB0, r); \ + a[0x17] ^= QC32dn(0xB0, r); \ + a[0x18] ^= QC32up(0xC0, r); \ + a[0x19] ^= QC32dn(0xC0, r); \ + a[0x1A] ^= QC32up(0xD0, r); \ + a[0x1B] ^= QC32dn(0xD0, r); \ + a[0x1C] ^= QC32up(0xE0, r); \ + a[0x1D] ^= QC32dn(0xE0, r); \ + a[0x1E] ^= QC32up(0xF0, r); \ + a[0x1F] ^= QC32dn(0xF0, r); \ + for(u = 0; u < 32; u += 8) { \ + RBTT( \ + u + 0x00, \ + (u + 0x01) & 0x1F, \ + a, \ + (u + 0x02) & 0x1F, \ + (u + 0x06) & 0x1F, \ + (u + 0x0A) & 0x1F, \ + (u + 0x16) & 0x1F, \ + (u + 0x01) & 0x1F, \ + (u + 0x05) & 0x1F, \ + (u + 0x09) & 0x1F, \ + (u + 0x0D) & 0x1F); \ + RBTT( \ + u + 0x02, \ + (u + 0x03) & 0x1F, \ + a, \ + (u + 0x04) & 0x1F, \ + (u + 0x08) & 0x1F, \ + (u + 0x0C) & 0x1F, \ + (u + 0x18) & 0x1F, \ + (u + 0x03) & 0x1F, \ + (u + 0x07) & 0x1F, \ + (u + 0x0B) & 0x1F, \ + (u + 0x0F) & 0x1F); \ + RBTT( \ + u + 0x04, \ + (u + 0x05) & 0x1F, \ + a, \ + (u + 0x06) & 0x1F, \ + (u + 0x0A) & 0x1F, \ + (u + 0x0E) & 0x1F, \ + (u + 0x1A) & 0x1F, \ + (u + 0x05) & 0x1F, \ + (u + 0x09) & 0x1F, \ + (u + 0x0D) & 0x1F, \ + (u + 0x11) & 0x1F); \ + RBTT( \ + u + 0x06, \ + (u + 0x07) & 0x1F, \ + a, \ + (u + 0x08) & 0x1F, \ + (u + 0x0C) & 0x1F, \ + (u + 0x10) & 0x1F, \ + (u + 0x1C) & 0x1F, \ + (u + 0x07) & 0x1F, \ + (u + 0x0B) & 0x1F, \ + (u + 0x0F) & 0x1F, \ + (u + 0x13) & 0x1F); \ + } \ + memcpy(a, t, sizeof t); \ + } while(0) + +#define PERM_BIG_P(a) \ + do { \ + int r; \ + for(r = 0; r < 14; r++) ROUND_BIG_P(a, r); \ + } while(0) + +#define PERM_BIG_Q(a) \ + do { \ + int r; \ + for(r = 0; r < 14; r++) ROUND_BIG_Q(a, r); \ + } while(0) + +#define COMPRESS_BIG \ + do { \ + sph_u32 g[32], m[32]; \ + size_t uu; \ + for(uu = 0; uu < 32; uu++) { \ + m[uu] = dec32e_aligned(buf + (uu << 2)); \ + g[uu] = m[uu] ^ H[uu]; \ + } \ + PERM_BIG_P(g); \ + PERM_BIG_Q(m); \ + for(uu = 0; uu < 32; uu++) H[uu] ^= g[uu] ^ m[uu]; \ + } while(0) + +#define FINAL_BIG \ + do { \ + sph_u32 x[32]; \ + size_t uu; \ + memcpy(x, H, sizeof x); \ + PERM_BIG_P(x); \ + for(uu = 0; uu < 32; uu++) H[uu] ^= x[uu]; \ + } while(0) + +static void groestl_big_init(sph_groestl_big_context* sc, unsigned out_size) { + size_t u = 0; + + sc->ptr = 0; + for(u = 0; u < 31; u++) sc->state.narrow[u] = 0; + sc->state.narrow[31] = ((sph_u32)(out_size & 0xFF) << 24) | + ((sph_u32)(out_size & 0xFF00) << 8); + sc->count = 0; +} + +static void groestl_big_core(sph_groestl_big_context* sc, const void* data, size_t len) { + if(len == 0) { + return; + } + + unsigned char* buf = NULL; + size_t ptr = 0; + DECL_STATE_BIG + + buf = sc->buf; + ptr = sc->ptr; + if(len < (sizeof sc->buf) - ptr) { + memcpy(buf + ptr, data, len); + ptr += len; + sc->ptr = ptr; + return; + } + + READ_STATE_BIG(sc); + while(len > 0) { + size_t clen = 0; + + clen = (sizeof sc->buf) - ptr; + if(clen > len) clen = len; + memcpy(buf + ptr, data, clen); + ptr += clen; + data = (const unsigned char*)data + clen; + len -= clen; + if(ptr == sizeof sc->buf) { + COMPRESS_BIG; + sc->count++; + ptr = 0; + } + } + WRITE_STATE_BIG(sc); + sc->ptr = ptr; +} + +static void groestl_big_close( + sph_groestl_big_context* sc, + unsigned ub, + unsigned n, + void* dst, + size_t out_len) { + unsigned char pad[136] = {0}; + size_t ptr = 0, pad_len = 0, u2 = 0; + sph_u64 count = 0; + unsigned z = 0; + DECL_STATE_BIG + + ptr = sc->ptr; + z = 0x80 >> n; + pad[0] = ((ub & -z) | z) & 0xFF; + if(ptr < 120) { + pad_len = 128 - ptr; + count = SPH_T64(sc->count + 1); + } else { + pad_len = 256 - ptr; + count = SPH_T64(sc->count + 2); + } + memzero(pad + 1, pad_len - 9); + sph_enc64be(pad + pad_len - 8, count); + groestl_big_core(sc, pad, pad_len); + READ_STATE_BIG(sc); + FINAL_BIG; + for(u2 = 0; u2 < 16; u2++) enc32e(pad + (u2 << 2), H[u2 + 16]); + memcpy(dst, pad + 64 - out_len, out_len); + groestl_big_init(sc, (unsigned)out_len << 3); +} + +void groestl512_Init(void* cc) { + groestl_big_init((sph_groestl_big_context*)cc, 512); +} + +void groestl512_Update(void* cc, const void* data, size_t len) { + groestl_big_core((sph_groestl_big_context*)cc, data, len); +} + +void groestl512_Final(void* cc, void* dst) { + groestl_big_close((sph_groestl_big_context*)cc, 0, 0, dst, 64); +} + +void groestl512_DoubleTrunc(void* cc, void* dst) { + char buf[64] = {0}; + + groestl512_Final(cc, buf); + groestl512_Update(cc, buf, sizeof(buf)); + groestl512_Final(cc, buf); + memcpy(dst, buf, 32); +} diff --git a/applications/external/flipbip/lib/crypto/groestl.h b/applications/external/flipbip/lib/crypto/groestl.h new file mode 100644 index 000000000..a4871deff --- /dev/null +++ b/applications/external/flipbip/lib/crypto/groestl.h @@ -0,0 +1,95 @@ +/* Groestl hash from https://github.com/Groestlcoin/vanitygen + * Trezor adaptation by Yura Pakhuchiy . */ +/** + * Groestl interface. This code implements Groestl with the recommended + * parameters for SHA-3, with outputs of 224, 256, 384 and 512 bits. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_groestl.h + * @author Thomas Pornin + */ + +#ifndef GROESTL_H__ +#define GROESTL_H__ + +#include + +/** + * This structure is a context for Groestl-384 and Groestl-512 computations: + * it contains the intermediate values and some data from the last + * entered block. Once a Groestl computation has been performed, the + * context can be reused for another computation. + * + * The contents of this structure are private. A running Groestl + * computation can be cloned by copying the context (e.g. with a simple + * memcpy()). + */ +typedef struct { + unsigned char buf[128]; /* first field, for alignment */ + size_t ptr; + union { + uint64_t wide[16]; + uint32_t narrow[32]; + } state; + uint64_t count; +} sph_groestl_big_context; + +typedef sph_groestl_big_context GROESTL512_CTX; + +/** + * Initialize a Groestl-512 context. This process performs no memory allocation. + * + * @param cc the Groestl-512 context (pointer to a + * GROESTL512_CTX) + */ +void groestl512_Init(void* cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the Groestl-512 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void groestl512_Update(void* cc, const void* data, size_t len); + +/** + * Terminate the current Groestl-512 computation and output the result into + * the provided buffer. The destination buffer must be wide enough to + * accomodate the result (64 bytes). The context is automatically + * reinitialized. + * + * @param cc the Groestl-512 context + * @param dst the destination buffer + */ +void groestl512_Final(void* cc, void* dst); + +/* Calculate double Groestl-512 hash and truncate it to 256-bits. */ +void groestl512_DoubleTrunc(void* cc, void* dst); + +#endif diff --git a/applications/external/flipbip/lib/crypto/groestl_internal.h b/applications/external/flipbip/lib/crypto/groestl_internal.h new file mode 100644 index 000000000..3859ff532 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/groestl_internal.h @@ -0,0 +1,461 @@ +/* Groestl hash from https://github.com/Groestlcoin/vanitygen + * Trezor adaptation by Yura Pakhuchiy . */ +/** + * Basic type definitions. + * + * This header file defines the generic integer types that will be used + * for the implementation of hash functions; it also contains helper + * functions which encode and decode multi-byte integer values, using + * either little-endian or big-endian conventions. + * + * This file contains a compile-time test on the size of a byte + * (the unsigned char C type). If bytes are not octets, + * i.e. if they do not have a size of exactly 8 bits, then compilation + * is aborted. Architectures where bytes are not octets are relatively + * rare, even in the embedded devices market. We forbid non-octet bytes + * because there is no clear convention on how octet streams are encoded + * on such systems. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_types.h + * @author Thomas Pornin + */ + +#ifndef GROESTL_INTERNAL_H__ +#define GROESTL_INTERNAL_H__ + +#include + +/* + * All our I/O functions are defined over octet streams. We do not know + * how to handle input data if bytes are not octets. + */ +#if CHAR_BIT != 8 +#error This code requires 8-bit bytes +#endif + +#if defined __STDC__ && __STDC_VERSION__ >= 199901L + +#include + +typedef uint32_t sph_u32; +typedef int32_t sph_s32; +typedef uint64_t sph_u64; +typedef int64_t sph_s64; + +#define SPH_C32(x) ((sph_u32)(x)) +#define SPH_C64(x) ((sph_u64)(x)) + +#else +#error We need at least C99 compiler +#endif + +#define SPH_T32(x) ((x)&SPH_C32(0xFFFFFFFF)) +#define SPH_ROTL32(x, n) SPH_T32(((x) << (n)) | ((x) >> (32 - (n)))) +#define SPH_ROTR32(x, n) SPH_ROTL32(x, (32 - (n))) + +#define SPH_T64(x) ((x)&SPH_C64(0xFFFFFFFFFFFFFFFF)) +#define SPH_ROTL64(x, n) SPH_T64(((x) << (n)) | ((x) >> (64 - (n)))) +#define SPH_ROTR64(x, n) SPH_ROTL64(x, (64 - (n))) + +/* + * 32-bit x86, aka "i386 compatible". + */ +#if defined __i386__ || defined _M_IX86 + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * 64-bit x86, hereafter known as "amd64". + */ +#elif defined __x86_64 || defined _M_X64 + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * ARM, little-endian. + */ +#elif defined __arm__ && __ARMEL__ + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * ARM64, little-endian. + */ +#elif defined __aarch64__ + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +#endif + +#if defined SPH_DETECT_LITTLE_ENDIAN && !defined SPH_LITTLE_ENDIAN +#define SPH_LITTLE_ENDIAN SPH_DETECT_LITTLE_ENDIAN +#endif +#if defined SPH_DETECT_BIG_ENDIAN && !defined SPH_BIG_ENDIAN +#define SPH_BIG_ENDIAN SPH_DETECT_BIG_ENDIAN +#endif + +static inline sph_u32 sph_bswap32(sph_u32 x) { + x = SPH_T32((x << 16) | (x >> 16)); + x = ((x & SPH_C32(0xFF00FF00)) >> 8) | ((x & SPH_C32(0x00FF00FF)) << 8); + return x; +} + +/** + * Byte-swap a 64-bit value. + * + * @param x the input value + * @return the byte-swapped value + */ +static inline sph_u64 sph_bswap64(sph_u64 x) { + x = SPH_T64((x << 32) | (x >> 32)); + x = ((x & SPH_C64(0xFFFF0000FFFF0000)) >> 16) | ((x & SPH_C64(0x0000FFFF0000FFFF)) << 16); + x = ((x & SPH_C64(0xFF00FF00FF00FF00)) >> 8) | ((x & SPH_C64(0x00FF00FF00FF00FF)) << 8); + return x; +} + +static inline void sph_enc16be(void* dst, unsigned val) { + ((unsigned char*)dst)[0] = (val >> 8); + ((unsigned char*)dst)[1] = val; +} + +static inline unsigned sph_dec16be(const void* src) { + return ((unsigned)(((const unsigned char*)src)[0]) << 8) | + (unsigned)(((const unsigned char*)src)[1]); +} + +static inline void sph_enc16le(void* dst, unsigned val) { + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = val >> 8; +} + +static inline unsigned sph_dec16le(const void* src) { + return (unsigned)(((const unsigned char*)src)[0]) | + ((unsigned)(((const unsigned char*)src)[1]) << 8); +} + +/** + * Encode a 32-bit value into the provided buffer (big endian convention). + * + * @param dst the destination buffer + * @param val the 32-bit value to encode + */ +static inline void sph_enc32be(void* dst, sph_u32 val) { + ((unsigned char*)dst)[0] = (val >> 24); + ((unsigned char*)dst)[1] = (val >> 16); + ((unsigned char*)dst)[2] = (val >> 8); + ((unsigned char*)dst)[3] = val; +} + +/** + * Encode a 32-bit value into the provided buffer (big endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (32-bit aligned) + * @param val the value to encode + */ +static inline void sph_enc32be_aligned(void* dst, sph_u32 val) { +#if SPH_LITTLE_ENDIAN + *(sph_u32*)dst = sph_bswap32(val); +#elif SPH_BIG_ENDIAN + *(sph_u32*)dst = val; +#else + ((unsigned char*)dst)[0] = (val >> 24); + ((unsigned char*)dst)[1] = (val >> 16); + ((unsigned char*)dst)[2] = (val >> 8); + ((unsigned char*)dst)[3] = val; +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (big endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u32 sph_dec32be(const void* src) { + return ((sph_u32)(((const unsigned char*)src)[0]) << 24) | + ((sph_u32)(((const unsigned char*)src)[1]) << 16) | + ((sph_u32)(((const unsigned char*)src)[2]) << 8) | + (sph_u32)(((const unsigned char*)src)[3]); +} + +/** + * Decode a 32-bit value from the provided buffer (big endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (32-bit aligned) + * @return the decoded value + */ +static inline sph_u32 sph_dec32be_aligned(const void* src) { +#if SPH_LITTLE_ENDIAN + return sph_bswap32(*(const sph_u32*)src); +#elif SPH_BIG_ENDIAN + return *(const sph_u32*)src; +#else + return ((sph_u32)(((const unsigned char*)src)[0]) << 24) | + ((sph_u32)(((const unsigned char*)src)[1]) << 16) | + ((sph_u32)(((const unsigned char*)src)[2]) << 8) | + (sph_u32)(((const unsigned char*)src)[3]); +#endif +} + +/** + * Encode a 32-bit value into the provided buffer (little endian convention). + * + * @param dst the destination buffer + * @param val the 32-bit value to encode + */ +static inline void sph_enc32le(void* dst, sph_u32 val) { + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = (val >> 8); + ((unsigned char*)dst)[2] = (val >> 16); + ((unsigned char*)dst)[3] = (val >> 24); +} + +/** + * Encode a 32-bit value into the provided buffer (little endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (32-bit aligned) + * @param val the value to encode + */ +static inline void sph_enc32le_aligned(void* dst, sph_u32 val) { +#if SPH_LITTLE_ENDIAN + *(sph_u32*)dst = val; +#elif SPH_BIG_ENDIAN + *(sph_u32*)dst = sph_bswap32(val); +#else + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = (val >> 8); + ((unsigned char*)dst)[2] = (val >> 16); + ((unsigned char*)dst)[3] = (val >> 24); +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (little endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u32 sph_dec32le(const void* src) { + return (sph_u32)(((const unsigned char*)src)[0]) | + ((sph_u32)(((const unsigned char*)src)[1]) << 8) | + ((sph_u32)(((const unsigned char*)src)[2]) << 16) | + ((sph_u32)(((const unsigned char*)src)[3]) << 24); +} + +/** + * Decode a 32-bit value from the provided buffer (little endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (32-bit aligned) + * @return the decoded value + */ +static inline sph_u32 sph_dec32le_aligned(const void* src) { +#if SPH_LITTLE_ENDIAN + return *(const sph_u32*)src; +#elif SPH_BIG_ENDIAN + return sph_bswap32(*(const sph_u32*)src); +#else + return (sph_u32)(((const unsigned char*)src)[0]) | + ((sph_u32)(((const unsigned char*)src)[1]) << 8) | + ((sph_u32)(((const unsigned char*)src)[2]) << 16) | + ((sph_u32)(((const unsigned char*)src)[3]) << 24); +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (big endian convention). + * + * @param dst the destination buffer + * @param val the 64-bit value to encode + */ +static inline void sph_enc64be(void* dst, sph_u64 val) { + ((unsigned char*)dst)[0] = (val >> 56); + ((unsigned char*)dst)[1] = (val >> 48); + ((unsigned char*)dst)[2] = (val >> 40); + ((unsigned char*)dst)[3] = (val >> 32); + ((unsigned char*)dst)[4] = (val >> 24); + ((unsigned char*)dst)[5] = (val >> 16); + ((unsigned char*)dst)[6] = (val >> 8); + ((unsigned char*)dst)[7] = val; +} + +/** + * Encode a 64-bit value into the provided buffer (big endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (64-bit aligned) + * @param val the value to encode + */ +static inline void sph_enc64be_aligned(void* dst, sph_u64 val) { +#if SPH_LITTLE_ENDIAN + *(sph_u64*)dst = sph_bswap64(val); +#elif SPH_BIG_ENDIAN + *(sph_u64*)dst = val; +#else + ((unsigned char*)dst)[0] = (val >> 56); + ((unsigned char*)dst)[1] = (val >> 48); + ((unsigned char*)dst)[2] = (val >> 40); + ((unsigned char*)dst)[3] = (val >> 32); + ((unsigned char*)dst)[4] = (val >> 24); + ((unsigned char*)dst)[5] = (val >> 16); + ((unsigned char*)dst)[6] = (val >> 8); + ((unsigned char*)dst)[7] = val; +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (big endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u64 sph_dec64be(const void* src) { + return ((sph_u64)(((const unsigned char*)src)[0]) << 56) | + ((sph_u64)(((const unsigned char*)src)[1]) << 48) | + ((sph_u64)(((const unsigned char*)src)[2]) << 40) | + ((sph_u64)(((const unsigned char*)src)[3]) << 32) | + ((sph_u64)(((const unsigned char*)src)[4]) << 24) | + ((sph_u64)(((const unsigned char*)src)[5]) << 16) | + ((sph_u64)(((const unsigned char*)src)[6]) << 8) | + (sph_u64)(((const unsigned char*)src)[7]); +} + +/** + * Decode a 64-bit value from the provided buffer (big endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (64-bit aligned) + * @return the decoded value + */ +static inline sph_u64 sph_dec64be_aligned(const void* src) { +#if SPH_LITTLE_ENDIAN + return sph_bswap64(*(const sph_u64*)src); +#elif SPH_BIG_ENDIAN + return *(const sph_u64*)src; +#else + return ((sph_u64)(((const unsigned char*)src)[0]) << 56) | + ((sph_u64)(((const unsigned char*)src)[1]) << 48) | + ((sph_u64)(((const unsigned char*)src)[2]) << 40) | + ((sph_u64)(((const unsigned char*)src)[3]) << 32) | + ((sph_u64)(((const unsigned char*)src)[4]) << 24) | + ((sph_u64)(((const unsigned char*)src)[5]) << 16) | + ((sph_u64)(((const unsigned char*)src)[6]) << 8) | + (sph_u64)(((const unsigned char*)src)[7]); +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (little endian convention). + * + * @param dst the destination buffer + * @param val the 64-bit value to encode + */ +static inline void sph_enc64le(void* dst, sph_u64 val) { + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = (val >> 8); + ((unsigned char*)dst)[2] = (val >> 16); + ((unsigned char*)dst)[3] = (val >> 24); + ((unsigned char*)dst)[4] = (val >> 32); + ((unsigned char*)dst)[5] = (val >> 40); + ((unsigned char*)dst)[6] = (val >> 48); + ((unsigned char*)dst)[7] = (val >> 56); +} + +/** + * Encode a 64-bit value into the provided buffer (little endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (64-bit aligned) + * @param val the value to encode + */ +static inline void sph_enc64le_aligned(void* dst, sph_u64 val) { +#if SPH_LITTLE_ENDIAN + *(sph_u64*)dst = val; +#elif SPH_BIG_ENDIAN + *(sph_u64*)dst = sph_bswap64(val); +#else + ((unsigned char*)dst)[0] = val; + ((unsigned char*)dst)[1] = (val >> 8); + ((unsigned char*)dst)[2] = (val >> 16); + ((unsigned char*)dst)[3] = (val >> 24); + ((unsigned char*)dst)[4] = (val >> 32); + ((unsigned char*)dst)[5] = (val >> 40); + ((unsigned char*)dst)[6] = (val >> 48); + ((unsigned char*)dst)[7] = (val >> 56); +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (little endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u64 sph_dec64le(const void* src) { + return (sph_u64)(((const unsigned char*)src)[0]) | + ((sph_u64)(((const unsigned char*)src)[1]) << 8) | + ((sph_u64)(((const unsigned char*)src)[2]) << 16) | + ((sph_u64)(((const unsigned char*)src)[3]) << 24) | + ((sph_u64)(((const unsigned char*)src)[4]) << 32) | + ((sph_u64)(((const unsigned char*)src)[5]) << 40) | + ((sph_u64)(((const unsigned char*)src)[6]) << 48) | + ((sph_u64)(((const unsigned char*)src)[7]) << 56); +} + +/** + * Decode a 64-bit value from the provided buffer (little endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (64-bit aligned) + * @return the decoded value + */ +static inline sph_u64 sph_dec64le_aligned(const void* src) { +#if SPH_LITTLE_ENDIAN + return *(const sph_u64*)src; +#elif SPH_BIG_ENDIAN + return sph_bswap64(*(const sph_u64*)src); +#else + return (sph_u64)(((const unsigned char*)src)[0]) | + ((sph_u64)(((const unsigned char*)src)[1]) << 8) | + ((sph_u64)(((const unsigned char*)src)[2]) << 16) | + ((sph_u64)(((const unsigned char*)src)[3]) << 24) | + ((sph_u64)(((const unsigned char*)src)[4]) << 32) | + ((sph_u64)(((const unsigned char*)src)[5]) << 40) | + ((sph_u64)(((const unsigned char*)src)[6]) << 48) | + ((sph_u64)(((const unsigned char*)src)[7]) << 56); +#endif +} + +#endif diff --git a/applications/external/flipbip/lib/crypto/hasher.c b/applications/external/flipbip/lib/crypto/hasher.c new file mode 100644 index 000000000..799ea6335 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hasher.c @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "hasher.h" +#include "ripemd160.h" + +const uint32_t sha256_initial_tapsighash_state[8] = { + 0xf504a425UL, + 0xd7f8783bUL, + 0x1363868aUL, + 0xe3e55658UL, + 0x6eee945dUL, + 0xbc7888ddUL, + 0x02a6e2c3UL, + 0x1873fe9fUL, +}; + +void hasher_InitParam(Hasher* hasher, HasherType type, const void* param, uint32_t param_size) { + hasher->type = type; + hasher->param = param; + hasher->param_size = param_size; + + switch(hasher->type) { + case HASHER_SHA2: + case HASHER_SHA2D: + case HASHER_SHA2_RIPEMD: + sha256_Init(&hasher->ctx.sha2); + break; + case HASHER_SHA2_TAPSIGHASH: + sha256_Init_ex(&hasher->ctx.sha2, sha256_initial_tapsighash_state, 512); + break; + case HASHER_SHA3: +#if USE_KECCAK + case HASHER_SHA3K: +#endif + sha3_256_Init(&hasher->ctx.sha3); + break; + case HASHER_BLAKE: + case HASHER_BLAKED: + case HASHER_BLAKE_RIPEMD: + blake256_Init(&hasher->ctx.blake); + break; + case HASHER_GROESTLD_TRUNC: + groestl512_Init(&hasher->ctx.groestl); + break; + case HASHER_BLAKE2B: + blake2b_Init(&hasher->ctx.blake2b, 32); + break; + case HASHER_BLAKE2B_PERSONAL: + blake2b_InitPersonal(&hasher->ctx.blake2b, 32, hasher->param, hasher->param_size); + break; + } +} + +void hasher_Init(Hasher* hasher, HasherType type) { + hasher_InitParam(hasher, type, NULL, 0); +} + +void hasher_Reset(Hasher* hasher) { + hasher_InitParam(hasher, hasher->type, hasher->param, hasher->param_size); +} + +void hasher_Update(Hasher* hasher, const uint8_t* data, size_t length) { + switch(hasher->type) { + case HASHER_SHA2: + case HASHER_SHA2D: + case HASHER_SHA2_RIPEMD: + case HASHER_SHA2_TAPSIGHASH: + sha256_Update(&hasher->ctx.sha2, data, length); + break; + case HASHER_SHA3: +#if USE_KECCAK + case HASHER_SHA3K: +#endif + sha3_Update(&hasher->ctx.sha3, data, length); + break; + case HASHER_BLAKE: + case HASHER_BLAKED: + case HASHER_BLAKE_RIPEMD: + blake256_Update(&hasher->ctx.blake, data, length); + break; + case HASHER_GROESTLD_TRUNC: + groestl512_Update(&hasher->ctx.groestl, data, length); + break; + case HASHER_BLAKE2B: + case HASHER_BLAKE2B_PERSONAL: + blake2b_Update(&hasher->ctx.blake2b, data, length); + break; + } +} + +void hasher_Final(Hasher* hasher, uint8_t hash[HASHER_DIGEST_LENGTH]) { + switch(hasher->type) { + case HASHER_SHA2: + case HASHER_SHA2_TAPSIGHASH: + sha256_Final(&hasher->ctx.sha2, hash); + break; + case HASHER_SHA2D: + sha256_Final(&hasher->ctx.sha2, hash); + hasher_Raw(HASHER_SHA2, hash, HASHER_DIGEST_LENGTH, hash); + break; + case HASHER_SHA2_RIPEMD: + sha256_Final(&hasher->ctx.sha2, hash); + ripemd160(hash, HASHER_DIGEST_LENGTH, hash); + break; + case HASHER_SHA3: + sha3_Final(&hasher->ctx.sha3, hash); + break; +#if USE_KECCAK + case HASHER_SHA3K: + keccak_Final(&hasher->ctx.sha3, hash); + break; +#endif + case HASHER_BLAKE: + blake256_Final(&hasher->ctx.blake, hash); + break; + case HASHER_BLAKED: + blake256_Final(&hasher->ctx.blake, hash); + hasher_Raw(HASHER_BLAKE, hash, HASHER_DIGEST_LENGTH, hash); + break; + case HASHER_BLAKE_RIPEMD: + blake256_Final(&hasher->ctx.blake, hash); + ripemd160(hash, HASHER_DIGEST_LENGTH, hash); + break; + case HASHER_GROESTLD_TRUNC: + groestl512_DoubleTrunc(&hasher->ctx.groestl, hash); + break; + case HASHER_BLAKE2B: + case HASHER_BLAKE2B_PERSONAL: + blake2b_Final(&hasher->ctx.blake2b, hash, 32); + break; + } +} + +void hasher_Raw( + HasherType type, + const uint8_t* data, + size_t length, + uint8_t hash[HASHER_DIGEST_LENGTH]) { + Hasher hasher = {0}; + + hasher_Init(&hasher, type); + hasher_Update(&hasher, data, length); + hasher_Final(&hasher, hash); +} diff --git a/applications/external/flipbip/lib/crypto/hasher.h b/applications/external/flipbip/lib/crypto/hasher.h new file mode 100644 index 000000000..5130df416 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hasher.h @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __HASHER_H__ +#define __HASHER_H__ + +#include +#include + +#include "blake256.h" +#include "blake2b.h" +#include "groestl.h" +#include "sha2.h" +#include "sha3.h" + +#define HASHER_DIGEST_LENGTH 32 + +typedef enum { + HASHER_SHA2, + HASHER_SHA2D, + HASHER_SHA2_RIPEMD, + HASHER_SHA2_TAPSIGHASH, + + HASHER_SHA3, +#if USE_KECCAK + HASHER_SHA3K, +#endif + + HASHER_BLAKE, + HASHER_BLAKED, + HASHER_BLAKE_RIPEMD, + + HASHER_GROESTLD_TRUNC, /* Double Groestl512 hasher truncated to 256 bits */ + + HASHER_BLAKE2B, + HASHER_BLAKE2B_PERSONAL, +} HasherType; + +typedef struct { + HasherType type; + + union { + SHA256_CTX sha2; // for HASHER_SHA2{,D} + SHA3_CTX sha3; // for HASHER_SHA3{,K} + BLAKE256_CTX blake; // for HASHER_BLAKE{,D} + GROESTL512_CTX groestl; // for HASHER_GROESTLD_TRUNC + BLAKE2B_CTX blake2b; // for HASHER_BLAKE2B{,_PERSONAL} + } ctx; + + const void* param; + uint32_t param_size; +} Hasher; + +void hasher_InitParam(Hasher* hasher, HasherType type, const void* param, uint32_t param_size); +void hasher_Init(Hasher* hasher, HasherType type); +void hasher_Reset(Hasher* hasher); +void hasher_Update(Hasher* hasher, const uint8_t* data, size_t length); +void hasher_Final(Hasher* hasher, uint8_t hash[HASHER_DIGEST_LENGTH]); + +void hasher_Raw( + HasherType type, + const uint8_t* data, + size_t length, + uint8_t hash[HASHER_DIGEST_LENGTH]); + +#endif diff --git a/applications/external/flipbip/lib/crypto/hmac.c b/applications/external/flipbip/lib/crypto/hmac.c new file mode 100644 index 000000000..1ee02711a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hmac.c @@ -0,0 +1,186 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "hmac.h" +#include "memzero.h" +#include "options.h" + +void hmac_sha256_Init(HMAC_SHA256_CTX* hctx, const uint8_t* key, const uint32_t keylen) { + static CONFIDENTIAL uint8_t i_key_pad[SHA256_BLOCK_LENGTH]; + memzero(i_key_pad, SHA256_BLOCK_LENGTH); + if(keylen > SHA256_BLOCK_LENGTH) { + sha256_Raw(key, keylen, i_key_pad); + } else { + memcpy(i_key_pad, key, keylen); + } + for(int i = 0; i < SHA256_BLOCK_LENGTH; i++) { + hctx->o_key_pad[i] = i_key_pad[i] ^ 0x5c; + i_key_pad[i] ^= 0x36; + } + sha256_Init(&(hctx->ctx)); + sha256_Update(&(hctx->ctx), i_key_pad, SHA256_BLOCK_LENGTH); + memzero(i_key_pad, sizeof(i_key_pad)); +} + +void hmac_sha256_Update(HMAC_SHA256_CTX* hctx, const uint8_t* msg, const uint32_t msglen) { + sha256_Update(&(hctx->ctx), msg, msglen); +} + +void hmac_sha256_Final(HMAC_SHA256_CTX* hctx, uint8_t* hmac) { + sha256_Final(&(hctx->ctx), hmac); + sha256_Init(&(hctx->ctx)); + sha256_Update(&(hctx->ctx), hctx->o_key_pad, SHA256_BLOCK_LENGTH); + sha256_Update(&(hctx->ctx), hmac, SHA256_DIGEST_LENGTH); + sha256_Final(&(hctx->ctx), hmac); + memzero(hctx, sizeof(HMAC_SHA256_CTX)); +} + +void hmac_sha256( + const uint8_t* key, + const uint32_t keylen, + const uint8_t* msg, + const uint32_t msglen, + uint8_t* hmac) { + static CONFIDENTIAL HMAC_SHA256_CTX hctx; + hmac_sha256_Init(&hctx, key, keylen); + hmac_sha256_Update(&hctx, msg, msglen); + hmac_sha256_Final(&hctx, hmac); +} + +void hmac_sha256_prepare( + const uint8_t* key, + const uint32_t keylen, + uint32_t* opad_digest, + uint32_t* ipad_digest) { + static CONFIDENTIAL uint32_t key_pad[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; + + memzero(key_pad, sizeof(key_pad)); + if(keylen > SHA256_BLOCK_LENGTH) { + static CONFIDENTIAL SHA256_CTX context; + sha256_Init(&context); + sha256_Update(&context, key, keylen); + sha256_Final(&context, (uint8_t*)key_pad); + } else { + memcpy(key_pad, key, keylen); + } + + /* compute o_key_pad and its digest */ + for(int i = 0; i < SHA256_BLOCK_LENGTH / (int)sizeof(uint32_t); i++) { + uint32_t data = 0; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(key_pad[i], data); +#else + data = key_pad[i]; +#endif + key_pad[i] = data ^ 0x5c5c5c5c; + } + sha256_Transform(sha256_initial_hash_value, key_pad, opad_digest); + + /* convert o_key_pad to i_key_pad and compute its digest */ + for(int i = 0; i < SHA256_BLOCK_LENGTH / (int)sizeof(uint32_t); i++) { + key_pad[i] = key_pad[i] ^ 0x5c5c5c5c ^ 0x36363636; + } + sha256_Transform(sha256_initial_hash_value, key_pad, ipad_digest); + memzero(key_pad, sizeof(key_pad)); +} + +void hmac_sha512_Init(HMAC_SHA512_CTX* hctx, const uint8_t* key, const uint32_t keylen) { + static CONFIDENTIAL uint8_t i_key_pad[SHA512_BLOCK_LENGTH]; + memzero(i_key_pad, SHA512_BLOCK_LENGTH); + if(keylen > SHA512_BLOCK_LENGTH) { + sha512_Raw(key, keylen, i_key_pad); + } else { + memcpy(i_key_pad, key, keylen); + } + for(int i = 0; i < SHA512_BLOCK_LENGTH; i++) { + hctx->o_key_pad[i] = i_key_pad[i] ^ 0x5c; + i_key_pad[i] ^= 0x36; + } + sha512_Init(&(hctx->ctx)); + sha512_Update(&(hctx->ctx), i_key_pad, SHA512_BLOCK_LENGTH); + memzero(i_key_pad, sizeof(i_key_pad)); +} + +void hmac_sha512_Update(HMAC_SHA512_CTX* hctx, const uint8_t* msg, const uint32_t msglen) { + sha512_Update(&(hctx->ctx), msg, msglen); +} + +void hmac_sha512_Final(HMAC_SHA512_CTX* hctx, uint8_t* hmac) { + sha512_Final(&(hctx->ctx), hmac); + sha512_Init(&(hctx->ctx)); + sha512_Update(&(hctx->ctx), hctx->o_key_pad, SHA512_BLOCK_LENGTH); + sha512_Update(&(hctx->ctx), hmac, SHA512_DIGEST_LENGTH); + sha512_Final(&(hctx->ctx), hmac); + memzero(hctx, sizeof(HMAC_SHA512_CTX)); +} + +void hmac_sha512( + const uint8_t* key, + const uint32_t keylen, + const uint8_t* msg, + const uint32_t msglen, + uint8_t* hmac) { + HMAC_SHA512_CTX hctx = {0}; + hmac_sha512_Init(&hctx, key, keylen); + hmac_sha512_Update(&hctx, msg, msglen); + hmac_sha512_Final(&hctx, hmac); +} + +void hmac_sha512_prepare( + const uint8_t* key, + const uint32_t keylen, + uint64_t* opad_digest, + uint64_t* ipad_digest) { + static CONFIDENTIAL uint64_t key_pad[SHA512_BLOCK_LENGTH / sizeof(uint64_t)]; + + memzero(key_pad, sizeof(key_pad)); + if(keylen > SHA512_BLOCK_LENGTH) { + static CONFIDENTIAL SHA512_CTX context; + sha512_Init(&context); + sha512_Update(&context, key, keylen); + sha512_Final(&context, (uint8_t*)key_pad); + } else { + memcpy(key_pad, key, keylen); + } + + /* compute o_key_pad and its digest */ + for(int i = 0; i < SHA512_BLOCK_LENGTH / (int)sizeof(uint64_t); i++) { + uint64_t data = 0; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE64(key_pad[i], data); +#else + data = key_pad[i]; +#endif + key_pad[i] = data ^ 0x5c5c5c5c5c5c5c5c; + } + sha512_Transform(sha512_initial_hash_value, key_pad, opad_digest); + + /* convert o_key_pad to i_key_pad and compute its digest */ + for(int i = 0; i < SHA512_BLOCK_LENGTH / (int)sizeof(uint64_t); i++) { + key_pad[i] = key_pad[i] ^ 0x5c5c5c5c5c5c5c5c ^ 0x3636363636363636; + } + sha512_Transform(sha512_initial_hash_value, key_pad, ipad_digest); + memzero(key_pad, sizeof(key_pad)); +} diff --git a/applications/external/flipbip/lib/crypto/hmac.h b/applications/external/flipbip/lib/crypto/hmac.h new file mode 100644 index 000000000..21a4f8da6 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hmac.h @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT HMAC_SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __HMAC_H__ +#define __HMAC_H__ + +#include +#include "sha2.h" + +typedef struct _HMAC_SHA256_CTX { + uint8_t o_key_pad[SHA256_BLOCK_LENGTH]; + SHA256_CTX ctx; +} HMAC_SHA256_CTX; + +typedef struct _HMAC_SHA512_CTX { + uint8_t o_key_pad[SHA512_BLOCK_LENGTH]; + SHA512_CTX ctx; +} HMAC_SHA512_CTX; + +void hmac_sha256_Init(HMAC_SHA256_CTX* hctx, const uint8_t* key, const uint32_t keylen); +void hmac_sha256_Update(HMAC_SHA256_CTX* hctx, const uint8_t* msg, const uint32_t msglen); +void hmac_sha256_Final(HMAC_SHA256_CTX* hctx, uint8_t* hmac); +void hmac_sha256( + const uint8_t* key, + const uint32_t keylen, + const uint8_t* msg, + const uint32_t msglen, + uint8_t* hmac); +void hmac_sha256_prepare( + const uint8_t* key, + const uint32_t keylen, + uint32_t* opad_digest, + uint32_t* ipad_digest); + +void hmac_sha512_Init(HMAC_SHA512_CTX* hctx, const uint8_t* key, const uint32_t keylen); +void hmac_sha512_Update(HMAC_SHA512_CTX* hctx, const uint8_t* msg, const uint32_t msglen); +void hmac_sha512_Final(HMAC_SHA512_CTX* hctx, uint8_t* hmac); +void hmac_sha512( + const uint8_t* key, + const uint32_t keylen, + const uint8_t* msg, + const uint32_t msglen, + uint8_t* hmac); +void hmac_sha512_prepare( + const uint8_t* key, + const uint32_t keylen, + uint64_t* opad_digest, + uint64_t* ipad_digest); + +#endif diff --git a/applications/external/flipbip/lib/crypto/hmac_drbg.c b/applications/external/flipbip/lib/crypto/hmac_drbg.c new file mode 100644 index 000000000..3115c11f2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hmac_drbg.c @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2019 Andrew R. Kozlik + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "hmac_drbg.h" +#include +#include "memzero.h" +#include "sha2.h" + +static void update_k( + HMAC_DRBG_CTX* ctx, + uint8_t domain, + const uint8_t* data1, + size_t len1, + const uint8_t* data2, + size_t len2) { + // Computes K = HMAC(K, V || domain || data1 || data 2). + + // First hash operation of HMAC. + uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; + if(len1 + len2 == 0) { + ctx->v[8] = 0x00800000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH + 1) * 8; + sha256_Transform(ctx->idig, ctx->v, h); + ctx->v[8] = 0x80000000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + } else { + SHA256_CTX sha_ctx = {0}; + memcpy(sha_ctx.state, ctx->idig, SHA256_DIGEST_LENGTH); + for(size_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) { +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(ctx->v[i], sha_ctx.buffer[i]); +#else + sha_ctx.buffer[i] = ctx->v[i]; +#endif + } + ((uint8_t*)sha_ctx.buffer)[SHA256_DIGEST_LENGTH] = domain; + sha_ctx.bitcount = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH + 1) * 8; + sha256_Update(&sha_ctx, data1, len1); + sha256_Update(&sha_ctx, data2, len2); + sha256_Final(&sha_ctx, (uint8_t*)h); +#if BYTE_ORDER == LITTLE_ENDIAN + for(size_t i = 0; i < SHA256_DIGEST_LENGTH / sizeof(uint32_t); i++) REVERSE32(h[i], h[i]); +#endif + } + + // Second hash operation of HMAC. + h[8] = 0x80000000; + h[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + sha256_Transform(ctx->odig, h, h); + + // Precompute the inner digest and outer digest of K. + h[8] = 0; + h[15] = 0; + for(size_t i = 0; i < SHA256_BLOCK_LENGTH / sizeof(uint32_t); i++) { + h[i] ^= 0x36363636; + } + sha256_Transform(sha256_initial_hash_value, h, ctx->idig); + + for(size_t i = 0; i < SHA256_BLOCK_LENGTH / sizeof(uint32_t); i++) { + h[i] = h[i] ^ 0x36363636 ^ 0x5c5c5c5c; + } + sha256_Transform(sha256_initial_hash_value, h, ctx->odig); + memzero(h, sizeof(h)); +} + +static void update_v(HMAC_DRBG_CTX* ctx) { + sha256_Transform(ctx->idig, ctx->v, ctx->v); + sha256_Transform(ctx->odig, ctx->v, ctx->v); +} + +void hmac_drbg_init( + HMAC_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t entropy_len, + const uint8_t* nonce, + size_t nonce_len) { + uint32_t h[SHA256_BLOCK_LENGTH / sizeof(uint32_t)] = {0}; + + // Precompute the inner digest and outer digest of K = 0x00 ... 0x00. + memset(h, 0x36, sizeof(h)); + sha256_Transform(sha256_initial_hash_value, h, ctx->idig); + memset(h, 0x5c, sizeof(h)); + sha256_Transform(sha256_initial_hash_value, h, ctx->odig); + + // Let V = 0x01 ... 0x01. + memset(ctx->v, 1, SHA256_DIGEST_LENGTH); + for(size_t i = 9; i < 15; i++) ctx->v[i] = 0; + ctx->v[8] = 0x80000000; + ctx->v[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + + hmac_drbg_reseed(ctx, entropy, entropy_len, nonce, nonce_len); + + memzero(h, sizeof(h)); +} + +void hmac_drbg_reseed( + HMAC_DRBG_CTX* ctx, + const uint8_t* entropy, + size_t len, + const uint8_t* addin, + size_t addin_len) { + update_k(ctx, 0, entropy, len, addin, addin_len); + update_v(ctx); + if(len == 0) return; + update_k(ctx, 1, entropy, len, addin, addin_len); + update_v(ctx); +} + +void hmac_drbg_generate(HMAC_DRBG_CTX* ctx, uint8_t* buf, size_t len) { + size_t i = 0; + while(i < len) { + update_v(ctx); + for(size_t j = 0; j < 8 && i < len; j++) { + uint32_t r = ctx->v[j]; + for(int k = 24; k >= 0 && i < len; k -= 8) { + buf[i++] = (r >> k) & 0xFF; + } + } + } + update_k(ctx, 0, NULL, 0, NULL, 0); + update_v(ctx); +} diff --git a/applications/external/flipbip/lib/crypto/hmac_drbg.h b/applications/external/flipbip/lib/crypto/hmac_drbg.h new file mode 100644 index 000000000..f6d3f3732 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/hmac_drbg.h @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2019 Andrew R. Kozlik + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __HMAC_DRBG_H__ +#define __HMAC_DRBG_H__ + +#include "sha2.h" +#include + +// HMAC based Deterministic Random Bit Generator with SHA-256 + +typedef struct _HMAC_DRBG_CTX { + uint32_t odig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t idig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t v[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; +} HMAC_DRBG_CTX; + +void hmac_drbg_init( + HMAC_DRBG_CTX* ctx, + const uint8_t* buf, + size_t len, + const uint8_t* nonce, + size_t nonce_len); +void hmac_drbg_reseed( + HMAC_DRBG_CTX* ctx, + const uint8_t* buf, + size_t len, + const uint8_t* addin, + size_t addin_len); +void hmac_drbg_generate(HMAC_DRBG_CTX* ctx, uint8_t* buf, size_t len); + +#endif diff --git a/applications/external/flipbip/lib/crypto/memzero.c b/applications/external/flipbip/lib/crypto/memzero.c new file mode 100644 index 000000000..64866ee56 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/memzero.c @@ -0,0 +1,74 @@ +#ifndef __STDC_WANT_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 // C11's bounds-checking interface. +#endif +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef __unix__ +#include +#include +#endif + +// C11's bounds-checking interface. +#if defined(__STDC_LIB_EXT1__) +#define HAVE_MEMSET_S 1 +#endif + +// GNU C Library version 2.25 or later. +#if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// Newlib +#if defined(__NEWLIB__) +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// FreeBSD version 11.0 or later. +#if defined(__FreeBSD__) && __FreeBSD_version >= 1100037 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// OpenBSD version 5.5 or later. +#if defined(__OpenBSD__) && OpenBSD >= 201405 +#define HAVE_EXPLICIT_BZERO 1 +#endif + +// NetBSD version 7.2 or later. +#if defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 +#define HAVE_EXPLICIT_MEMSET 1 +#endif + +// Adapted from +// https://github.com/jedisct1/libsodium/blob/1647f0d53ae0e370378a9195477e3df0a792408f/src/libsodium/sodium/utils.c#L102-L130 + +void memzero(void* const pnt, const size_t len) { +#ifdef _WIN32 + SecureZeroMemory(pnt, len); +#elif defined(HAVE_MEMSET_S) + memset_s(pnt, (rsize_t)len, 0, (rsize_t)len); +// #elif defined(HAVE_EXPLICIT_BZERO) +// explicit_bzero(pnt, len); +#elif defined(HAVE_EXPLICIT_MEMSET) + explicit_memset(pnt, 0, len); +#else + volatile unsigned char* volatile pnt_ = (volatile unsigned char* volatile)pnt; + size_t i = (size_t)0U; + + while(i < len) { + pnt_[i++] = 0U; + } +#endif + + // explicitly mark the memory as overwritten for the Clang MemorySanitizer + // this is only included at compile time if MemorySanitizer is enabled and + // should not come with any downsides during regular builds +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) + memset(pnt, 0, len); +#endif +#endif +} diff --git a/applications/external/flipbip/lib/crypto/memzero.h b/applications/external/flipbip/lib/crypto/memzero.h new file mode 100644 index 000000000..0a959fbc2 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/memzero.h @@ -0,0 +1,8 @@ +#ifndef __MEMZERO_H__ +#define __MEMZERO_H__ + +#include + +void memzero(void* const pnt, const size_t len); + +#endif diff --git a/applications/external/flipbip/lib/crypto/monero/base58.c b/applications/external/flipbip/lib/crypto/monero/base58.c new file mode 100644 index 000000000..1ca9dfafd --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/base58.c @@ -0,0 +1,297 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote +// developers + +#if USE_MONERO + +#include "base58.h" +#include +#include +#include +#include +#include "../base58.h" +#include "../byte_order.h" +#include "int_util.h" +#include "../sha2.h" + +const size_t alphabet_size = 58; // sizeof(b58digits_ordered) - 1; +const size_t full_encoded_block_size = 11; +const size_t encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, full_encoded_block_size}; +const size_t full_block_size = sizeof(encoded_block_sizes) / sizeof(encoded_block_sizes[0]) - 1; +const size_t addr_checksum_size = 4; +const size_t max_bin_data_size = 72; +const int decoded_block_sizes[] = {0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8}; +#define reverse_alphabet(letter) ((int8_t)b58digits_map[(int)letter]) + +uint64_t uint_8be_to_64(const uint8_t* data, size_t size) { + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t res = 0; + switch(9 - size) { + case 1: + res |= *data++; /* FALLTHRU */ + case 2: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 3: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 4: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 5: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 6: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 7: + res <<= 8; + res |= *data++; /* FALLTHRU */ + case 8: + res <<= 8; + res |= *data; + break; + default: + assert(false); + } + + return res; +} + +void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data) { + assert(1 <= size && size <= sizeof(uint64_t)); + +#if BYTE_ORDER == LITTLE_ENDIAN + uint64_t num_be = SWAP64(num); +#else + uint64_t num_be = num; +#endif + memcpy(data, (uint8_t*)(&num_be) + sizeof(uint64_t) - size, size); +} + +void encode_block(const char* block, size_t size, char* res) { + assert(1 <= size && size <= full_block_size); + + uint64_t num = uint_8be_to_64((uint8_t*)(block), size); + int i = ((int)(encoded_block_sizes[size])) - 1; + while(0 <= i) { + uint64_t remainder = num % alphabet_size; + num /= alphabet_size; + res[i] = b58digits_ordered[remainder]; + --i; + } +} + +bool decode_block(const char* block, size_t size, char* res) { + assert(1 <= size && size <= full_encoded_block_size); + + int res_size = decoded_block_sizes[size]; + if(res_size <= 0) { + return false; // Invalid block size + } + + uint64_t res_num = 0; + uint64_t order = 1; + for(size_t i = size - 1; i < size; --i) { + if(block[i] & 0x80) { + return false; // Invalid symbol + } + int digit = reverse_alphabet(block[i]); + if(digit < 0) { + return false; // Invalid symbol + } + + uint64_t product_hi = 0; + uint64_t tmp = res_num + mul128(order, (uint64_t)digit, &product_hi); + if(tmp < res_num || 0 != product_hi) { + return false; // Overflow + } + + res_num = tmp; + // The original code comment for the order multiplication says + // "Never overflows, 58^10 < 2^64" + // This is incorrect since it overflows on the 11th iteration + // However, there is no negative impact since the result is unused + order *= alphabet_size; + } + + if((size_t)res_size < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num) + return false; // Overflow + + uint_64_to_8be(res_num, res_size, (uint8_t*)(res)); + + return true; +} + +bool xmr_base58_encode(char* b58, size_t* b58sz, const void* data, size_t binsz) { + if(binsz == 0) { + if(b58sz) { + *b58sz = 0; + } + return true; + } + + const char* data_bin = data; + size_t full_block_count = binsz / full_block_size; + size_t last_block_size = binsz % full_block_size; + size_t res_size = + full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size]; + + if(b58sz) { + if(res_size > *b58sz) { + return false; + } + *b58sz = res_size; + } + + for(size_t i = 0; i < full_block_count; ++i) { + encode_block( + data_bin + i * full_block_size, full_block_size, b58 + i * full_encoded_block_size); + } + + if(0 < last_block_size) { + encode_block( + data_bin + full_block_count * full_block_size, + last_block_size, + b58 + full_block_count * full_encoded_block_size); + } + + return true; +} + +bool xmr_base58_decode(const char* b58, size_t b58sz, void* data, size_t* binsz) { + if(b58sz == 0) { + *binsz = 0; + return true; + } + + size_t full_block_count = b58sz / full_encoded_block_size; + size_t last_block_size = b58sz % full_encoded_block_size; + int last_block_decoded_size = decoded_block_sizes[last_block_size]; + if(last_block_decoded_size < 0) { + *binsz = 0; + return false; // Invalid enc length + } + + size_t data_size = full_block_count * full_block_size + last_block_decoded_size; + if(*binsz < data_size) { + *binsz = 0; + return false; + } + + char* data_bin = data; + for(size_t i = 0; i < full_block_count; ++i) { + if(!decode_block( + b58 + i * full_encoded_block_size, + full_encoded_block_size, + data_bin + i * full_block_size)) { + *binsz = 0; + return false; + } + } + + if(0 < last_block_size) { + if(!decode_block( + b58 + full_block_count * full_encoded_block_size, + last_block_size, + data_bin + full_block_count * full_block_size)) { + *binsz = 0; + return false; + } + } + + *binsz = data_size; + return true; +} + +int xmr_base58_addr_encode_check( + uint64_t tag, + const uint8_t* data, + size_t binsz, + char* b58, + size_t b58sz) { + if(binsz > max_bin_data_size || tag > 127) { // tag varint + return false; + } + + size_t b58size = b58sz; + uint8_t buf[(binsz + 1) + HASHER_DIGEST_LENGTH]; + memset(buf, 0, sizeof(buf)); + uint8_t* hash = buf + binsz + 1; + buf[0] = (uint8_t)tag; + memcpy(buf + 1, data, binsz); + hasher_Raw(HASHER_SHA3K, buf, binsz + 1, hash); + + bool r = xmr_base58_encode(b58, &b58size, buf, binsz + 1 + addr_checksum_size); + return (int)(!r ? 0 : b58size); +} + +int xmr_base58_addr_decode_check( + const char* addr, + size_t sz, + uint64_t* tag, + void* data, + size_t datalen) { + size_t buflen = 1 + max_bin_data_size + addr_checksum_size; + uint8_t buf[buflen]; + memset(buf, 0, sizeof(buf)); + uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; + + if(!xmr_base58_decode(addr, sz, buf, &buflen)) { + return 0; + } + + if(buflen <= addr_checksum_size + 1) { + return 0; + } + + size_t res_size = buflen - addr_checksum_size - 1; + if(datalen < res_size) { + return 0; + } + + hasher_Raw(HASHER_SHA3K, buf, buflen - addr_checksum_size, hash); + if(memcmp(hash, buf + buflen - addr_checksum_size, addr_checksum_size) != 0) { + return 0; + } + + *tag = buf[0]; + if(*tag > 127) { + return false; // varint + } + + memcpy(data, buf + 1, res_size); + return (int)res_size; +} + +#endif // USE_MONERO \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/monero/base58.h b/applications/external/flipbip/lib/crypto/monero/base58.h new file mode 100644 index 000000000..adcafadd1 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/base58.h @@ -0,0 +1,60 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote +// developers + +#if USE_MONERO + +#ifndef __XMR_BASE58_H__ +#define __XMR_BASE58_H__ + +#include +#include "../hasher.h" +#include "../options.h" + +int xmr_base58_addr_encode_check( + uint64_t tag, + const uint8_t* data, + size_t binsz, + char* b58, + size_t b58sz); +int xmr_base58_addr_decode_check( + const char* addr, + size_t sz, + uint64_t* tag, + void* data, + size_t datalen); +bool xmr_base58_encode(char* b58, size_t* b58sz, const void* data, size_t binsz); +bool xmr_base58_decode(const char* b58, size_t b58sz, void* data, size_t* binsz); + +#endif + +#endif // USE_MONERO \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/monero/int_util.h b/applications/external/flipbip/lib/crypto/monero/int_util.h new file mode 100644 index 000000000..fb627b62e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/int_util.h @@ -0,0 +1,81 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote +// developers + +#if USE_MONERO + +#pragma once + +#include +#include + +static inline uint64_t hi_dword(uint64_t val) { + return val >> 32; +} + +static inline uint64_t lo_dword(uint64_t val) { + return val & 0xFFFFFFFF; +} + +static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = hi_dword(multiplier); + uint64_t b = lo_dword(multiplier); + uint64_t c = hi_dword(multiplicand); + uint64_t d = lo_dword(multiplicand); + + uint64_t ac = a * c; + uint64_t ad = a * d; + uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + bc; + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + assert(ac <= *product_hi); + + return product_lo; +} + +#define SWAP64(x) \ + ((((uint64_t)(x)&0x00000000000000ff) << 56) | (((uint64_t)(x)&0x000000000000ff00) << 40) | \ + (((uint64_t)(x)&0x0000000000ff0000) << 24) | (((uint64_t)(x)&0x00000000ff000000) << 8) | \ + (((uint64_t)(x)&0x000000ff00000000) >> 8) | (((uint64_t)(x)&0x0000ff0000000000) >> 24) | \ + (((uint64_t)(x)&0x00ff000000000000) >> 40) | (((uint64_t)(x)&0xff00000000000000) >> 56)) + +#endif // USE_MONERO \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/monero/monero.h b/applications/external/flipbip/lib/crypto/monero/monero.h new file mode 100644 index 000000000..96ff9dd5c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/monero.h @@ -0,0 +1,24 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#if USE_MONERO + +#ifndef TREZOR_CRYPTO_MONERO_H +#define TREZOR_CRYPTO_MONERO_H + +#if !USE_MONERO +#error "Compile with -DUSE_MONERO=1" +#endif + +#if !USE_KECCAK +#error "Compile with -DUSE_KECCAK=1" +#endif + +#include "base58.h" +#include "serialize.h" +#include "xmr.h" + +#endif // TREZOR_CRYPTO_MONERO_H + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/monero/serialize.c b/applications/external/flipbip/lib/crypto/monero/serialize.c new file mode 100644 index 000000000..0023c9699 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/serialize.c @@ -0,0 +1,57 @@ +// +// Created by Dusan Klinec on 02/05/2018. +// + +#if USE_MONERO + +#include "serialize.h" + +int xmr_size_varint(uint64_t num) { + int ctr = 1; + while(num >= 0x80) { + ++ctr; + num >>= 7; + } + return ctr; +} + +int xmr_write_varint(uint8_t* buff, size_t buff_size, uint64_t num) { + unsigned ctr = 0; + while(num >= 0x80 && ctr < buff_size) { + *buff = (uint8_t)(((num)&0x7f) | 0x80); + ++buff; + ++ctr; + num >>= 7; + } + + /* writes the last one to dest */ + if(ctr < buff_size) { + *buff = (uint8_t)num; + ++ctr; + } + return ctr <= buff_size ? (int)ctr : -1; +} + +int xmr_read_varint(uint8_t* buff, size_t buff_size, uint64_t* val) { + unsigned read = 0; + int finished_ok = 0; + *val = 0; + + for(int shift = 0; read < buff_size; shift += 7, ++read) { + uint8_t byte = buff[read]; + if((byte == 0 && shift != 0) || (shift >= 63 && byte > 1)) { + return -1; + } + + *val |= (uint64_t)(byte & 0x7f) << shift; + + /* If there is no next */ + if((byte & 0x80) == 0) { + finished_ok = 1; + break; + } + } + return finished_ok ? (int)read + 1 : -2; +} + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/monero/serialize.h b/applications/external/flipbip/lib/crypto/monero/serialize.h new file mode 100644 index 000000000..9f8903694 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/serialize.h @@ -0,0 +1,19 @@ +// +// Created by Dusan Klinec on 02/05/2018. +// + +#if USE_MONERO + +#ifndef TREZOR_XMR_SERIALIZE_H +#define TREZOR_XMR_SERIALIZE_H + +#include +#include + +int xmr_size_varint(uint64_t num); +int xmr_write_varint(uint8_t* buff, size_t buff_size, uint64_t num); +int xmr_read_varint(uint8_t* buff, size_t buff_size, uint64_t* val); + +#endif // TREZOR_XMR_SERIALIZE_H + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/monero/xmr.c b/applications/external/flipbip/lib/crypto/monero/xmr.c new file mode 100644 index 000000000..917200624 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/xmr.c @@ -0,0 +1,202 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#if USE_MONERO + +#include "xmr.h" +#include "../byte_order.h" +#include "int_util.h" +#include "../rand.h" +#include "serialize.h" + +const ge25519 ALIGN(16) xmr_h = { + {0x1861ec7, + 0x1ceac77, + 0x2f11626, + 0x1f261d3, + 0x346107c, + 0x06d8c4a, + 0x254201d, + 0x1675c09, + 0x1301c3f, + 0x0211d73}, + {0x326feb4, + 0x12e30cc, + 0x0cf54b4, + 0x1117305, + 0x318f5d5, + 0x06cf754, + 0x2e578a1, + 0x1daf058, + 0x34430a1, + 0x04410e9}, + {0x0fde4d2, + 0x0774049, + 0x22ca951, + 0x05aec2b, + 0x07a36a5, + 0x1394f13, + 0x3c5385c, + 0x1adb924, + 0x2b6c581, + 0x0a55fa4}, + {0x24517f7, + 0x05ee936, + 0x3acf5d9, + 0x14b08aa, + 0x3363738, + 0x1051745, + 0x360601e, + 0x0f3f2c9, + 0x1ead2cd, + 0x1d3e3df}}; + +void ge25519_set_xmr_h(ge25519* r) { + ge25519_copy(r, &xmr_h); +} + +void xmr_random_scalar(bignum256modm m) { + unsigned char buff[32] = {0}; + random_buffer(buff, sizeof(buff)); + expand256_modm(m, buff, sizeof(buff)); +} + +void xmr_fast_hash(uint8_t* hash, const void* data, size_t length) { + hasher_Raw(HASHER_SHA3K, data, length, hash); +} + +void xmr_hasher_init(Hasher* hasher) { + hasher_Init(hasher, HASHER_SHA3K); +} + +void xmr_hasher_update(Hasher* hasher, const void* data, size_t length) { + hasher_Update(hasher, data, length); +} + +void xmr_hasher_final(Hasher* hasher, uint8_t* hash) { + hasher_Final(hasher, hash); +} + +void xmr_hasher_copy(Hasher* dst, const Hasher* src) { + memcpy(dst, src, sizeof(Hasher)); +} + +void xmr_hash_to_scalar(bignum256modm r, const void* data, size_t length) { + uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; + hasher_Raw(HASHER_SHA3K, data, length, hash); + expand256_modm(r, hash, HASHER_DIGEST_LENGTH); +} + +void xmr_hash_to_ec(ge25519* P, const void* data, size_t length) { + ge25519 point2 = {0}; + uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; + hasher_Raw(HASHER_SHA3K, data, length, hash); + + ge25519_fromfe_frombytes_vartime(&point2, hash); + ge25519_mul8(P, &point2); +} + +void xmr_derivation_to_scalar(bignum256modm s, const ge25519* p, uint32_t output_index) { + uint8_t buff[32 + 8] = {0}; + ge25519_pack(buff, p); + int written = xmr_write_varint(buff + 32, 8, output_index); + xmr_hash_to_scalar(s, buff, 32u + written); +} + +void xmr_generate_key_derivation(ge25519* r, const ge25519* A, const bignum256modm b) { + ge25519 bA = {0}; + ge25519_scalarmult(&bA, A, b); + ge25519_mul8(r, &bA); +} + +void xmr_derive_private_key( + bignum256modm s, + const ge25519* deriv, + uint32_t idx, + const bignum256modm base) { + xmr_derivation_to_scalar(s, deriv, idx); + add256_modm(s, s, base); +} + +void xmr_derive_public_key(ge25519* r, const ge25519* deriv, uint32_t idx, const ge25519* base) { + bignum256modm s = {0}; + ge25519 p2 = {0}; + + xmr_derivation_to_scalar(s, deriv, idx); + ge25519_scalarmult_base_niels(&p2, ge25519_niels_base_multiples, s); + ge25519_add(r, base, &p2, 0); +} + +void xmr_add_keys2(ge25519* r, const bignum256modm a, const bignum256modm b, const ge25519* B) { + // aG + bB, G is basepoint + ge25519 aG = {0}, bB = {0}; + ge25519_scalarmult_base_niels(&aG, ge25519_niels_base_multiples, a); + ge25519_scalarmult(&bB, B, b); + ge25519_add(r, &aG, &bB, 0); +} + +void xmr_add_keys2_vartime( + ge25519* r, + const bignum256modm a, + const bignum256modm b, + const ge25519* B) { + // aG + bB, G is basepoint + ge25519_double_scalarmult_vartime(r, B, b, a); +} + +void xmr_add_keys3( + ge25519* r, + const bignum256modm a, + const ge25519* A, + const bignum256modm b, + const ge25519* B) { + // aA + bB + ge25519 aA = {0}, bB = {0}; + ge25519_scalarmult(&aA, A, a); + ge25519_scalarmult(&bB, B, b); + ge25519_add(r, &aA, &bB, 0); +} + +void xmr_add_keys3_vartime( + ge25519* r, + const bignum256modm a, + const ge25519* A, + const bignum256modm b, + const ge25519* B) { + // aA + bB + ge25519_double_scalarmult_vartime2(r, A, a, B, b); +} + +void xmr_get_subaddress_secret_key( + bignum256modm r, + uint32_t major, + uint32_t minor, + const bignum256modm m) { + const char prefix[] = "SubAddr"; + unsigned char buff[32] = {0}; + contract256_modm(buff, m); + + char data[sizeof(prefix) + sizeof(buff) + 2 * sizeof(uint32_t)] = {0}; + memcpy(data, prefix, sizeof(prefix)); + memcpy(data + sizeof(prefix), buff, sizeof(buff)); + +#if BYTE_ORDER == BIG_ENDIAN + REVERSE32(major, major); + REVERSE32(minor, minor); +#endif + + memcpy(data + sizeof(prefix) + sizeof(buff), &major, sizeof(uint32_t)); + memcpy(data + sizeof(prefix) + sizeof(buff) + sizeof(uint32_t), &minor, sizeof(uint32_t)); + + xmr_hash_to_scalar(r, data, sizeof(data)); +} + +void xmr_gen_c(ge25519* r, const bignum256modm a, uint64_t amount) { + // C = aG + bH + bignum256modm b = {0}; + set256_modm(b, amount); + xmr_add_keys2(r, a, b, &xmr_h); +} + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/monero/xmr.h b/applications/external/flipbip/lib/crypto/monero/xmr.h new file mode 100644 index 000000000..16c2aec5e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/monero/xmr.h @@ -0,0 +1,93 @@ +// +// Created by Dusan Klinec on 10/05/2018. +// + +#if USE_MONERO + +#ifndef TREZOR_CRYPTO_XMR_H +#define TREZOR_CRYPTO_XMR_H + +#include "../ed25519_donna/ed25519_donna.h" +#include "../hasher.h" + +extern const ge25519 ALIGN(16) xmr_h; + +typedef unsigned char xmr_key_t[32]; + +typedef struct xmr_ctkey { + xmr_key_t dest; + xmr_key_t mask; +} xmr_ctkey_t; + +/* sets H point to r */ +void ge25519_set_xmr_h(ge25519* r); + +/* random scalar value */ +void xmr_random_scalar(bignum256modm m); + +/* cn_fast_hash */ +void xmr_fast_hash(uint8_t* hash, const void* data, size_t length); + +/* incremental hashing wrappers */ +void xmr_hasher_init(Hasher* hasher); +void xmr_hasher_update(Hasher* hasher, const void* data, size_t length); +void xmr_hasher_final(Hasher* hasher, uint8_t* hash); +void xmr_hasher_copy(Hasher* dst, const Hasher* src); + +/* H_s(buffer) */ +void xmr_hash_to_scalar(bignum256modm r, const void* data, size_t length); + +/* H_p(buffer) */ +void xmr_hash_to_ec(ge25519* P, const void* data, size_t length); + +/* derivation to scalar value */ +void xmr_derivation_to_scalar(bignum256modm s, const ge25519* p, uint32_t output_index); + +/* derivation */ +void xmr_generate_key_derivation(ge25519* r, const ge25519* A, const bignum256modm b); + +/* H_s(derivation || varint(output_index)) + base */ +void xmr_derive_private_key( + bignum256modm s, + const ge25519* deriv, + uint32_t idx, + const bignum256modm base); + +/* H_s(derivation || varint(output_index))G + base */ +void xmr_derive_public_key(ge25519* r, const ge25519* deriv, uint32_t idx, const ge25519* base); + +/* aG + bB, G is basepoint */ +void xmr_add_keys2(ge25519* r, const bignum256modm a, const bignum256modm b, const ge25519* B); +void xmr_add_keys2_vartime( + ge25519* r, + const bignum256modm a, + const bignum256modm b, + const ge25519* B); + +/* aA + bB */ +void xmr_add_keys3( + ge25519* r, + const bignum256modm a, + const ge25519* A, + const bignum256modm b, + const ge25519* B); +void xmr_add_keys3_vartime( + ge25519* r, + const bignum256modm a, + const ge25519* A, + const bignum256modm b, + const ge25519* B); + +/* subaddress secret */ +void xmr_get_subaddress_secret_key( + bignum256modm r, + uint32_t major, + uint32_t minor, + const bignum256modm m); + +/* Generates Pedersen commitment C = aG + bH */ +void xmr_gen_c(ge25519* r, const bignum256modm a, uint64_t amount); + +#endif // TREZOR_CRYPTO_XMR_H + +#endif // USE_MONERO diff --git a/applications/external/flipbip/lib/crypto/nem.c b/applications/external/flipbip/lib/crypto/nem.c new file mode 100644 index 000000000..391c1e6fc --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nem.c @@ -0,0 +1,603 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E1PRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "options.h" + +#if USE_NEM + +#include "nem.h" + +#include + +#include "base32.h" +#include "ed25519_donna/ed25519_keccak.h" +#include "memzero.h" +#include "ripemd160.h" +#include "sha3.h" + +#define CAN_WRITE(NEEDED) ((ctx->offset + (NEEDED)) <= ctx->size) + +#define SERIALIZE_U32(DATA) \ + do { \ + if(!nem_write_u32(ctx, (DATA))) return false; \ + } while(0) +#define SERIALIZE_U64(DATA) \ + do { \ + if(!nem_write_u64(ctx, (DATA))) return false; \ + } while(0) +#define SERIALIZE_TAGGED(DATA, LENGTH) \ + do { \ + if(!nem_write_tagged(ctx, (DATA), (LENGTH))) return false; \ + } while(0) + +const char* nem_network_name(uint8_t network) { + switch(network) { + case NEM_NETWORK_MAINNET: + return "NEM Mainnet"; + case NEM_NETWORK_TESTNET: + return "NEM Testnet"; + case NEM_NETWORK_MIJIN: + return "Mijin"; + default: + return NULL; + } +} + +static inline bool + nem_write_checked(nem_transaction_ctx* ctx, const uint8_t* data, uint32_t length) { + if(!CAN_WRITE(length)) { + return false; + } + + memcpy(&ctx->buffer[ctx->offset], data, length); + ctx->offset += length; + return true; +} + +static inline bool nem_write_u32(nem_transaction_ctx* ctx, uint32_t data) { + if(!CAN_WRITE(4)) { + return false; + } + + ctx->buffer[ctx->offset++] = (data >> 0) & 0xff; + ctx->buffer[ctx->offset++] = (data >> 8) & 0xff; + ctx->buffer[ctx->offset++] = (data >> 16) & 0xff; + ctx->buffer[ctx->offset++] = (data >> 24) & 0xff; + + return true; +} + +static inline bool nem_write_u64(nem_transaction_ctx* ctx, uint64_t data) { + SERIALIZE_U32((data >> 0) & 0xffffffff); + SERIALIZE_U32((data >> 32) & 0xffffffff); + + return true; +} + +static inline bool + nem_write_tagged(nem_transaction_ctx* ctx, const uint8_t* data, uint32_t length) { + SERIALIZE_U32(length); + + return nem_write_checked(ctx, data, length); +} + +static inline bool + nem_write_mosaic_str(nem_transaction_ctx* ctx, const char* name, const char* value) { + uint32_t name_length = strlen(name); + uint32_t value_length = strlen(value); + + SERIALIZE_U32(sizeof(uint32_t) + name_length + sizeof(uint32_t) + value_length); + SERIALIZE_TAGGED((const uint8_t*)name, name_length); + SERIALIZE_TAGGED((const uint8_t*)value, value_length); + + return true; +} + +static inline bool nem_write_mosaic_bool(nem_transaction_ctx* ctx, const char* name, bool value) { + return nem_write_mosaic_str(ctx, name, value ? "true" : "false"); +} + +static inline bool + nem_write_mosaic_u64(nem_transaction_ctx* ctx, const char* name, uint64_t value) { + char buffer[21] = {0}; + + if(bn_format_uint64(value, NULL, NULL, 0, 0, false, 0, buffer, sizeof(buffer)) == 0) { + return false; + } + + return nem_write_mosaic_str(ctx, name, buffer); +} + +void nem_get_address_raw(const ed25519_public_key public_key, uint8_t version, uint8_t* address) { + uint8_t hash[SHA3_256_DIGEST_LENGTH] = {0}; + + /* 1. Perform 256-bit Sha3 on the public key */ + keccak_256(public_key, sizeof(ed25519_public_key), hash); + + /* 2. Perform 160-bit Ripemd of hash resulting from step 1. */ + ripemd160(hash, SHA3_256_DIGEST_LENGTH, &address[1]); + + /* 3. Prepend version byte to Ripemd hash (either 0x68 or 0x98) */ + address[0] = version; + + /* 4. Perform 256-bit Sha3 on the result, take the first four bytes as a + * checksum */ + keccak_256(address, 1 + RIPEMD160_DIGEST_LENGTH, hash); + + /* 5. Concatenate output of step 3 and the checksum from step 4 */ + memcpy(&address[1 + RIPEMD160_DIGEST_LENGTH], hash, 4); + + memzero(hash, sizeof(hash)); +} + +bool nem_get_address(const ed25519_public_key public_key, uint8_t version, char* address) { + uint8_t pubkeyhash[NEM_ADDRESS_SIZE_RAW] = {0}; + + nem_get_address_raw(public_key, version, pubkeyhash); + + char* ret = base32_encode( + pubkeyhash, sizeof(pubkeyhash), address, NEM_ADDRESS_SIZE + 1, BASE32_ALPHABET_RFC4648); + + memzero(pubkeyhash, sizeof(pubkeyhash)); + return (ret != NULL); +} + +bool nem_validate_address_raw(const uint8_t* address, uint8_t network) { + if(!nem_network_name(network) || address[0] != network) { + return false; + } + + uint8_t hash[SHA3_256_DIGEST_LENGTH] = {0}; + + keccak_256(address, 1 + RIPEMD160_DIGEST_LENGTH, hash); + bool valid = (memcmp(&address[1 + RIPEMD160_DIGEST_LENGTH], hash, 4) == 0); + + memzero(hash, sizeof(hash)); + return valid; +} + +bool nem_validate_address(const char* address, uint8_t network) { + uint8_t pubkeyhash[NEM_ADDRESS_SIZE_RAW] = {0}; + + if(strlen(address) != NEM_ADDRESS_SIZE) { + return false; + } + + uint8_t* ret = base32_decode( + address, NEM_ADDRESS_SIZE, pubkeyhash, sizeof(pubkeyhash), BASE32_ALPHABET_RFC4648); + bool valid = (ret != NULL) && nem_validate_address_raw(pubkeyhash, network); + + memzero(pubkeyhash, sizeof(pubkeyhash)); + return valid; +} + +void nem_transaction_start( + nem_transaction_ctx* ctx, + const ed25519_public_key public_key, + uint8_t* buffer, + size_t size) { + memcpy(ctx->public_key, public_key, sizeof(ctx->public_key)); + + ctx->buffer = buffer; + ctx->offset = 0; + ctx->size = size; +} + +size_t nem_transaction_end( + nem_transaction_ctx* ctx, + const ed25519_secret_key private_key, + ed25519_signature signature) { + if(private_key != NULL && signature != NULL) { + ed25519_sign_keccak(ctx->buffer, ctx->offset, private_key, signature); + } + + return ctx->offset; +} + +bool nem_transaction_write_common( + nem_transaction_ctx* ctx, + uint32_t type, + uint32_t version, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline) { + SERIALIZE_U32(type); + SERIALIZE_U32(version); + SERIALIZE_U32(timestamp); + SERIALIZE_TAGGED(signer, sizeof(ed25519_public_key)); + SERIALIZE_U64(fee); + SERIALIZE_U32(deadline); + + return true; +} + +bool nem_transaction_create_transfer( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* recipient, + uint64_t amount, + const uint8_t* payload, + uint32_t length, + bool encrypted, + uint32_t mosaics) { + if(!signer) { + signer = ctx->public_key; + } + + if(!payload) { + length = 0; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_TRANSFER, + (uint32_t)network << 24 | (mosaics ? 2 : 1), + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + SERIALIZE_TAGGED((const uint8_t*)recipient, NEM_ADDRESS_SIZE); + SERIALIZE_U64(amount); + + if(length) { + SERIALIZE_U32(sizeof(uint32_t) + sizeof(uint32_t) + length); + SERIALIZE_U32(encrypted ? 0x02 : 0x01); + SERIALIZE_TAGGED(payload, length); + } else { + SERIALIZE_U32(0); + } + + if(mosaics) { + SERIALIZE_U32(mosaics); + } + + return true; +} + +bool nem_transaction_write_mosaic( + nem_transaction_ctx* ctx, + const char* namespace, + const char* mosaic, + uint64_t quantity) { + size_t namespace_length = strlen(namespace); + size_t mosaic_length = strlen(mosaic); + size_t identifier_length = + sizeof(uint32_t) + namespace_length + sizeof(uint32_t) + mosaic_length; + + SERIALIZE_U32(sizeof(uint32_t) + sizeof(uint64_t) + identifier_length); + SERIALIZE_U32(identifier_length); + SERIALIZE_TAGGED((const uint8_t*)namespace, namespace_length); + SERIALIZE_TAGGED((const uint8_t*)mosaic, mosaic_length); + SERIALIZE_U64(quantity); + + return true; +} + +bool nem_transaction_create_multisig( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const nem_transaction_ctx* inner) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_MULTISIG, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + SERIALIZE_TAGGED(inner->buffer, inner->offset); + + return true; +} + +bool nem_transaction_create_multisig_signature( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const nem_transaction_ctx* inner) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + char address[NEM_ADDRESS_SIZE + 1] = {0}; + nem_get_address(inner->public_key, network, address); + + uint8_t hash[SHA3_256_DIGEST_LENGTH] = {0}; + keccak_256(inner->buffer, inner->offset, hash); + + SERIALIZE_U32(sizeof(uint32_t) + SHA3_256_DIGEST_LENGTH); + SERIALIZE_TAGGED(hash, SHA3_256_DIGEST_LENGTH); + SERIALIZE_TAGGED((const uint8_t*)address, NEM_ADDRESS_SIZE); + + return true; +} + +bool nem_transaction_create_provision_namespace( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* parent, + const char* rental_sink, + uint64_t rental_fee) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + if(parent) { + SERIALIZE_TAGGED((const uint8_t*)rental_sink, NEM_ADDRESS_SIZE); + SERIALIZE_U64(rental_fee); + SERIALIZE_TAGGED((const uint8_t*)namespace, strlen(namespace)); + SERIALIZE_TAGGED((const uint8_t*)parent, strlen(parent)); + } else { + SERIALIZE_TAGGED((const uint8_t*)rental_sink, NEM_ADDRESS_SIZE); + SERIALIZE_U64(rental_fee); + SERIALIZE_TAGGED((const uint8_t*)namespace, strlen(namespace)); + SERIALIZE_U32(0xffffffff); + } + + return true; +} + +bool nem_transaction_create_mosaic_creation( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* mosaic, + const char* description, + uint32_t divisibility, + uint64_t supply, + bool mutable_supply, + bool transferable, + uint32_t levy_type, + uint64_t levy_fee, + const char* levy_address, + const char* levy_namespace, + const char* levy_mosaic, + const char* creation_sink, + uint64_t creation_fee) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_MOSAIC_CREATION, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + size_t namespace_length = strlen(namespace); + size_t mosaic_length = strlen(mosaic); + size_t identifier_length = + sizeof(uint32_t) + namespace_length + sizeof(uint32_t) + mosaic_length; + + // This length will be rewritten later on + nem_transaction_ctx state = {0}; + memcpy(&state, ctx, sizeof(state)); + + SERIALIZE_U32(0); + SERIALIZE_TAGGED(signer, sizeof(ed25519_public_key)); + SERIALIZE_U32(identifier_length); + SERIALIZE_TAGGED((const uint8_t*)namespace, namespace_length); + SERIALIZE_TAGGED((const uint8_t*)mosaic, mosaic_length); + SERIALIZE_TAGGED((const uint8_t*)description, strlen(description)); + SERIALIZE_U32(4); // Number of properties + + if(!nem_write_mosaic_u64(ctx, "divisibility", divisibility)) return false; + if(!nem_write_mosaic_u64(ctx, "initialSupply", supply)) return false; + if(!nem_write_mosaic_bool(ctx, "supplyMutable", mutable_supply)) return false; + if(!nem_write_mosaic_bool(ctx, "transferable", transferable)) return false; + + if(levy_type) { + size_t levy_namespace_length = strlen(levy_namespace); + size_t levy_mosaic_length = strlen(levy_mosaic); + size_t levy_identifier_length = + sizeof(uint32_t) + levy_namespace_length + sizeof(uint32_t) + levy_mosaic_length; + + SERIALIZE_U32( + sizeof(uint32_t) + sizeof(uint32_t) + NEM_ADDRESS_SIZE + sizeof(uint32_t) + + levy_identifier_length + sizeof(uint64_t)); + SERIALIZE_U32(levy_type); + SERIALIZE_TAGGED((const uint8_t*)levy_address, NEM_ADDRESS_SIZE); + SERIALIZE_U32(levy_identifier_length); + SERIALIZE_TAGGED((const uint8_t*)levy_namespace, levy_namespace_length); + SERIALIZE_TAGGED((const uint8_t*)levy_mosaic, levy_mosaic_length); + SERIALIZE_U64(levy_fee); + } else { + SERIALIZE_U32(0); + } + + // Rewrite length + nem_write_u32(&state, ctx->offset - state.offset - sizeof(uint32_t)); + + SERIALIZE_TAGGED((const uint8_t*)creation_sink, NEM_ADDRESS_SIZE); + SERIALIZE_U64(creation_fee); + + return true; +} + +bool nem_transaction_create_mosaic_supply_change( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* mosaic, + uint32_t type, + uint64_t delta) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + size_t namespace_length = strlen(namespace); + size_t mosaic_length = strlen(mosaic); + size_t identifier_length = + sizeof(uint32_t) + namespace_length + sizeof(uint32_t) + mosaic_length; + + SERIALIZE_U32(identifier_length); + SERIALIZE_TAGGED((const uint8_t*)namespace, namespace_length); + SERIALIZE_TAGGED((const uint8_t*)mosaic, mosaic_length); + SERIALIZE_U32(type); + SERIALIZE_U64(delta); + + return true; +} + +bool nem_transaction_create_aggregate_modification( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + uint32_t modifications, + bool relative_change) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION, + (uint32_t)network << 24 | (relative_change ? 2 : 1), + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + SERIALIZE_U32(modifications); + + return true; +} + +bool nem_transaction_write_cosignatory_modification( + nem_transaction_ctx* ctx, + uint32_t type, + const ed25519_public_key cosignatory) { + SERIALIZE_U32(sizeof(uint32_t) + sizeof(uint32_t) + sizeof(ed25519_public_key)); + SERIALIZE_U32(type); + SERIALIZE_TAGGED(cosignatory, sizeof(ed25519_public_key)); + + return true; +} + +bool nem_transaction_write_minimum_cosignatories(nem_transaction_ctx* ctx, int32_t relative_change) { + SERIALIZE_U32(sizeof(uint32_t)); + SERIALIZE_U32((uint32_t)relative_change); + + return true; +} + +bool nem_transaction_create_importance_transfer( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + uint32_t mode, + const ed25519_public_key remote) { + if(!signer) { + signer = ctx->public_key; + } + + bool ret = nem_transaction_write_common( + ctx, + NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER, + (uint32_t)network << 24 | 1, + timestamp, + signer, + fee, + deadline); + if(!ret) return false; + + SERIALIZE_U32(mode); + SERIALIZE_TAGGED(remote, sizeof(ed25519_public_key)); + + return true; +} + +#endif // USE_NEM diff --git a/applications/external/flipbip/lib/crypto/nem.h b/applications/external/flipbip/lib/crypto/nem.h new file mode 100644 index 000000000..38b7530d7 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nem.h @@ -0,0 +1,216 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "../options.h" + +#if USE_NEM + +#ifndef __NEM_H__ +#define __NEM_H__ + +#include +#include +#include + +#include "bip32.h" +#include "ed25519_donna/ed25519.h" + +#define NEM_LEVY_PERCENTILE_DIVISOR 4 +#define NEM_MAX_DIVISIBILITY 6 +#define NEM_MAX_SUPPLY 9000000000 + +#define NEM_NETWORK_MAINNET 0x68 +#define NEM_NETWORK_TESTNET 0x98 +#define NEM_NETWORK_MIJIN 0x60 + +#define NEM_ADDRESS_SIZE 40 +#define NEM_ADDRESS_SIZE_RAW 25 + +#define NEM_TRANSACTION_TYPE_TRANSFER 0x0101 +#define NEM_TRANSACTION_TYPE_IMPORTANCE_TRANSFER 0x0801 +#define NEM_TRANSACTION_TYPE_AGGREGATE_MODIFICATION 0x1001 +#define NEM_TRANSACTION_TYPE_MULTISIG_SIGNATURE 0x1002 +#define NEM_TRANSACTION_TYPE_MULTISIG 0x1004 +#define NEM_TRANSACTION_TYPE_PROVISION_NAMESPACE 0x2001 +#define NEM_TRANSACTION_TYPE_MOSAIC_CREATION 0x4001 +#define NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE 0x4002 + +#define NEM_SALT_SIZE sizeof(ed25519_public_key) + +#define NEM_ENCRYPTED_SIZE(size) (((size) + AES_BLOCK_SIZE) / AES_BLOCK_SIZE * AES_BLOCK_SIZE) +#define NEM_ENCRYPTED_PAYLOAD_SIZE(size) \ + (AES_BLOCK_SIZE + NEM_SALT_SIZE + NEM_ENCRYPTED_SIZE(size)) + +#define _NEM_PADDING_SIZE(buffer, size) ((buffer)[(size)-1]) +#define NEM_PADDING_SIZE(buffer, size) \ + (_NEM_PADDING_SIZE(buffer, size) > (size) ? (size) : _NEM_PADDING_SIZE(buffer, size)) + +#define NEM_DECRYPTED_SIZE(buffer, size) ((size)-NEM_PADDING_SIZE(buffer, size)) + +typedef struct { + ed25519_public_key public_key; + uint8_t* buffer; + size_t offset; + size_t size; +} nem_transaction_ctx; + +const char* nem_network_name(uint8_t network); + +void nem_get_address_raw(const ed25519_public_key public_key, uint8_t version, uint8_t* address); +bool nem_get_address(const ed25519_public_key public_key, uint8_t version, char* address); + +bool nem_validate_address_raw(const uint8_t* address, uint8_t network); +bool nem_validate_address(const char* address, uint8_t network); + +void nem_transaction_start( + nem_transaction_ctx* ctx, + const ed25519_public_key public_key, + uint8_t* buffer, + size_t size); +size_t nem_transaction_end( + nem_transaction_ctx* ctx, + const ed25519_secret_key private_key, + ed25519_signature signature); + +bool nem_transaction_write_common( + nem_transaction_ctx* context, + uint32_t type, + uint32_t version, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline); + +bool nem_transaction_create_transfer( + nem_transaction_ctx* context, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* recipient, + uint64_t amount, + const uint8_t* payload, + uint32_t length, + bool encrypted, + uint32_t mosaics); + +bool nem_transaction_write_mosaic( + nem_transaction_ctx* ctx, + const char* namespace, + const char* mosaic, + uint64_t quantity); + +bool nem_transaction_create_multisig( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const nem_transaction_ctx* inner); + +bool nem_transaction_create_multisig_signature( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const nem_transaction_ctx* inner); + +bool nem_transaction_create_provision_namespace( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* parent, + const char* rental_sink, + uint64_t rental_fee); + +bool nem_transaction_create_mosaic_creation( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* mosaic, + const char* description, + uint32_t divisibility, + uint64_t supply, + bool mutable_supply, + bool transferable, + uint32_t levy_type, + uint64_t levy_fee, + const char* levy_address, + const char* levy_namespace, + const char* levy_mosaic, + const char* creation_sink, + uint64_t creation_fee); + +bool nem_transaction_create_mosaic_supply_change( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + const char* namespace, + const char* mosaic, + uint32_t type, + uint64_t delta); + +bool nem_transaction_create_aggregate_modification( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + uint32_t modifications, + bool relative_change); + +bool nem_transaction_write_cosignatory_modification( + nem_transaction_ctx* ctx, + uint32_t type, + const ed25519_public_key cosignatory); + +bool nem_transaction_write_minimum_cosignatories(nem_transaction_ctx* ctx, int32_t relative_change); + +bool nem_transaction_create_importance_transfer( + nem_transaction_ctx* ctx, + uint8_t network, + uint32_t timestamp, + const ed25519_public_key signer, + uint64_t fee, + uint32_t deadline, + uint32_t mode, + const ed25519_public_key remote); + +#endif + +#endif // USE_NEM diff --git a/applications/external/flipbip/lib/crypto/nist256p1.c b/applications/external/flipbip/lib/crypto/nist256p1.c new file mode 100644 index 000000000..51dc569cd --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nist256p1.c @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "nist256p1.h" + +const ecdsa_curve nist256p1 = { + /* .prime */ {/*.val =*/{ + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x000001ff, + 0x00000000, + 0x00000000, + 0x00040000, + 0x1fe00000, + 0xffffff}}, + + /* G */ + {/*.x =*/{/*.val =*/{ + 0x1898c296, + 0x0509ca2e, + 0x1acce83d, + 0x06fb025b, + 0x040f2770, + 0x1372b1d2, + 0x091fe2f3, + 0x1e5c2588, + 0x6b17d1}}, + /*.y =*/{/*.val =*/{ + 0x17bf51f5, + 0x1db20341, + 0x0c57b3b2, + 0x1c66aed6, + 0x19e162bc, + 0x15a53e07, + 0x1e6e3b9f, + 0x1c5fc34f, + 0x4fe342}}}, + + /* order */ + {/*.val =*/{ + 0x1c632551, + 0x1dce5617, + 0x05e7a13c, + 0x0df55b4e, + 0x1ffffbce, + 0x1fffffff, + 0x0003ffff, + 0x1fe00000, + 0xffffff}}, + + /* order_half */ + {/*.val =*/{ + 0x1e3192a8, + 0x0ee72b0b, + 0x02f3d09e, + 0x06faada7, + 0x1ffffde7, + 0x1fffffff, + 0x0001ffff, + 0x1ff00000, + 0x7fffff}}, + + /* a */ -3, + + /* b */ + {/*.val =*/{ + 0x07d2604b, + 0x1e71e1f1, + 0x14ec3d8e, + 0x1a0d6198, + 0x086bc651, + 0x1eaabb4c, + 0x0f9ecfae, + 0x1b154752, + 0x005ac635}} + +#if USE_PRECOMPUTED_CP + , + /* cp */ + { +#include "nist256p1.table" + } +#endif +}; + +const curve_info nist256p1_info = { + .bip32_name = "Nist256p1 seed", + .params = &nist256p1, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; diff --git a/applications/external/flipbip/lib/crypto/nist256p1.h b/applications/external/flipbip/lib/crypto/nist256p1.h new file mode 100644 index 000000000..02d04025a --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nist256p1.h @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NIST256P1_H__ +#define __NIST256P1_H__ + +#include + +#include "bip32.h" +#include "ecdsa.h" + +extern const ecdsa_curve nist256p1; +extern const curve_info nist256p1_info; + +#endif diff --git a/applications/external/flipbip/lib/crypto/nist256p1.table b/applications/external/flipbip/lib/crypto/nist256p1.table new file mode 100644 index 000000000..6be01b4d6 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/nist256p1.table @@ -0,0 +1,1664 @@ + { + /* 1*16^0*G: */ + {{{0x1898c296, 0x0509ca2e, 0x1acce83d, 0x06fb025b, 0x040f2770, 0x1372b1d2, 0x091fe2f3, 0x1e5c2588, 0x6b17d1}}, + {{0x17bf51f5, 0x1db20341, 0x0c57b3b2, 0x1c66aed6, 0x19e162bc, 0x15a53e07, 0x1e6e3b9f, 0x1c5fc34f, 0x4fe342}}}, + /* 3*16^0*G: */ + {{{0x06e7fd6c, 0x1a0b30de, 0x0b6a617e, 0x0d6e43df, 0x1f165e6c, 0x17ca8ea5, 0x091323df, 0x1a34c661, 0x5ecbe4}}, + {{0x027d5032, 0x13cd893d, 0x13ee0f66, 0x15606c70, 0x0a2ecd82, 0x03670d32, 0x1df8dd2c, 0x0189331f, 0x873464}}}, + /* 5*16^0*G: */ + {{{0x03d033ed, 0x0aaa506e, 0x16f94908, 0x1905fa3e, 0x08fdfef8, 0x042b0433, 0x034b5e13, 0x0f4a2a28, 0x51590b}}, + {{0x1da16da4, 0x0e85da27, 0x16022234, 0x025e01a9, 0x079260d0, 0x1f9b5fc5, 0x09f62b86, 0x1512094e, 0xe0c17d}}}, + /* 7*16^0*G: */ + {{{0x1187b2a3, 0x00314381, 0x03fbd6cc, 0x13f17150, 0x1fb607ef, 0x18333e00, 0x0d1896ec, 0x0df417ef, 0x8e533b}}, + {{0x01f400b4, 0x0af0d436, 0x0106c871, 0x0e6c6796, 0x1900053c, 0x0fc1d37a, 0x00d9b41a, 0x17bc0663, 0x73eb1d}}}, + /* 9*16^0*G: */ + {{{0x10949ee0, 0x1cf4525c, 0x1b7e2cf5, 0x15971858, 0x1f8729e0, 0x1c6a8eb8, 0x0dc61e24, 0x16dfdbe1, 0xea68d7}}, + {{0x0dd048fa, 0x02d11252, 0x17a08ffa, 0x029fd549, 0x0a0c84d7, 0x054b2547, 0x139e1c05, 0x192e593f, 0x2a2744}}}, + /* 11*16^0*G: */ + {{{0x14bc21d1, 0x199c8e9b, 0x14122fd0, 0x085da04a, 0x01cda167, 0x1bced861, 0x116418e0, 0x16f10769, 0x3ed113}}, + {{0x082a3740, 0x17c777e7, 0x062276b8, 0x1a09b4bd, 0x0c68a090, 0x01d7d27a, 0x02889321, 0x13599899, 0x909920}}}, + /* 13*16^0*G: */ + {{{0x06072c01, 0x070aecea, 0x1ab562a6, 0x1c5096cb, 0x0e2fc792, 0x0ef96c2f, 0x05698601, 0x0f5c1589, 0x177c83}}, + {{0x0fc7bfd8, 0x021ddf17, 0x1ed37ce7, 0x1c298743, 0x14e7226e, 0x08d6da07, 0x15628902, 0x19a9d7d4, 0x63bb58}}}, + /* 15*16^0*G: */ + {{{0x059b9d5f, 0x1b34631f, 0x0e83bc58, 0x075f25bc, 0x08265ae0, 0x1bc4ccc4, 0x0b9eb7ec, 0x18d2e357, 0xf0454d}}, + {{0x0d034f36, 0x1f2ce6f0, 0x0d7e8fd1, 0x16439ceb, 0x043e62a3, 0x0a728fcb, 0x147d3996, 0x1c6b25c5, 0xb5b93e}}} + }, + { + /* 1*16^1*G: */ + {{{0x01277c6e, 0x0f5a3c3f, 0x1b280e29, 0x10725dfe, 0x0315fcd2, 0x0e314c1b, 0x06162e08, 0x02714d68, 0x76a94d}}, + {{0x0b8c5110, 0x14eeeb92, 0x11e2ea83, 0x1340081f, 0x08720859, 0x10daf08f, 0x1839b2c2, 0x0c2683e4, 0xa985fe}}}, + /* 3*16^1*G: */ + {{{0x1e4536ca, 0x1fdcee34, 0x0806986a, 0x1d252c14, 0x0cda11c2, 0x1a2df038, 0x07b23339, 0x01c924a7, 0x9482fb}}, + {{0x158cc1c8, 0x1d6c31df, 0x01efeec7, 0x1abc52ae, 0x14e63f63, 0x11c653a9, 0x1fe46975, 0x14e8be2a, 0x351d9c}}}, + /* 5*16^1*G: */ + {{{0x15ecff13, 0x0d8ed714, 0x1418cf12, 0x0c9439b7, 0x01eeb637, 0x0d28a984, 0x04656e0d, 0x182f5d26, 0xb2e1b7}}, + {{0x12187d44, 0x1dd24c4d, 0x17a8b9a8, 0x1435302f, 0x158c387d, 0x10d1556b, 0x0f33c8ce, 0x0262747d, 0xe6c044}}}, + /* 7*16^1*G: */ + {{{0x0ef87286, 0x05dd99a7, 0x08c2790e, 0x07d0fbf8, 0x0ee34070, 0x0bbee5e3, 0x0f0b2cd0, 0x05cfe36d, 0xb43346}}, + {{0x041496d9, 0x16023fb4, 0x0b3a92de, 0x1c4100d4, 0x12e6f9dc, 0x073960b4, 0x17f8e0fd, 0x0a4dda56, 0xa0da54}}}, + /* 9*16^1*G: */ + {{{0x19794381, 0x1cc35ad8, 0x0c31d806, 0x02b10ebd, 0x07b19189, 0x1482daab, 0x0933061b, 0x19c9a7b4, 0xa47420}}, + {{0x02769c52, 0x0b671d3f, 0x1dab1e1a, 0x0b811b73, 0x07afff3a, 0x1c9b0ccb, 0x1f839cf7, 0x1bef7c37, 0x2ebbff}}}, + /* 11*16^1*G: */ + {{{0x09a5185a, 0x03a23324, 0x179bcafb, 0x0e15913e, 0x1a3195b8, 0x0a009586, 0x05217fb3, 0x03373a12, 0x5ba7a}}, + {{0x01de3a4e, 0x0a1e4470, 0x016b8005, 0x0c441792, 0x1d48e760, 0x011f1271, 0x025b919e, 0x16bd0583, 0x30e0d2}}}, + /* 13*16^1*G: */ + {{{0x199b85fe, 0x151cc5af, 0x0e977f56, 0x1dfd355c, 0x1e0b79c0, 0x06c8c041, 0x0bf5a5e6, 0x193a1bfc, 0xb29f74}}, + {{0x0da604dc, 0x1d3214bb, 0x1d67a036, 0x1ec7a617, 0x1c6d92de, 0x18974b6b, 0x1e175741, 0x1874d852, 0xa3ce5b}}}, + /* 15*16^1*G: */ + {{{0x07e030ef, 0x1b3a2b74, 0x1543a589, 0x16560af9, 0x02b08576, 0x1f81f924, 0x13d41db2, 0x15517237, 0x99299b}}, + {{0x18d9aa9a, 0x0c735bee, 0x10021113, 0x0258933e, 0x0aa7957c, 0x0080d367, 0x1862fcce, 0x1a6667f1, 0x5cc2e3}}} + }, + { + /* 1*16^2*G: */ + {{{0x12d0441b, 0x0d971af8, 0x1a95930b, 0x1a16e21a, 0x1ed61190, 0x08a94301, 0x19661fff, 0x14760122, 0x34a2d4}}, + {{0x1e93b146, 0x01273c22, 0x1776076d, 0x0ecd73bd, 0x17fc0e7d, 0x1882373b, 0x0f08af29, 0x0d4a743c, 0xbeaaed}}}, + /* 3*16^2*G: */ + {{{0x1da08424, 0x0725719d, 0x1d912a77, 0x1f6f56e9, 0x123ed3ab, 0x02ffd4ae, 0x06a86d63, 0x00f5b86b, 0xa98b0a}}, + {{0x0831800c, 0x10943d07, 0x17d60b4d, 0x17d01741, 0x11320752, 0x0f46470e, 0x0d9b94c6, 0x08e44933, 0x4754c6}}}, + /* 5*16^2*G: */ + {{{0x083c2d6a, 0x096bf2f4, 0x0f4a6ced, 0x1f231b03, 0x0696dd22, 0x15bc642d, 0x0b17486d, 0x013911b5, 0xa034f0}}, + {{0x02c30e72, 0x019b7796, 0x099f9f3d, 0x0c06be2c, 0x0eb06be0, 0x1c2a2e76, 0x107ec336, 0x06d95133, 0xfe1b00}}}, + /* 7*16^2*G: */ + {{{0x08be8c78, 0x0b528ee9, 0x1e4f935c, 0x12d9f056, 0x0e9a14a0, 0x115a04b1, 0x100681db, 0x1fe633a8, 0xb52226}}, + {{0x11686903, 0x019b3e52, 0x0d63fcc8, 0x1eff91cb, 0x0851a819, 0x1b34bf49, 0x1503286c, 0x193ea73e, 0x701ee0}}}, + /* 9*16^2*G: */ + {{{0x0058b5a4, 0x0207f205, 0x0c65e6dc, 0x08c9f0f2, 0x04713de4, 0x07667e66, 0x16cf65a3, 0x0c30d1c0, 0xa3e388}}, + {{0x07113aef, 0x19836c87, 0x1c68a523, 0x1d08b76e, 0x1e68f3ce, 0x1359d74b, 0x04b0e123, 0x110fbe1a, 0xcfd457}}}, + /* 11*16^2*G: */ + {{{0x04a08314, 0x02faf648, 0x11f7fde8, 0x1c6db65b, 0x1197a82f, 0x055cd110, 0x14e4ccf3, 0x094263f4, 0x209954}}, + {{0x07573ccc, 0x1fc64e2f, 0x1f3fa5b9, 0x0dcb4e6d, 0x048dfd40, 0x1472e5c8, 0x0c6243ee, 0x1fa55467, 0xa5cf3d}}}, + /* 13*16^2*G: */ + {{{0x09078c18, 0x017fca57, 0x1b7a4a54, 0x018795dd, 0x1d39b99e, 0x1739556f, 0x11c0c23d, 0x0275981d, 0x751d65}}, + {{0x0ece0d67, 0x0c6b74ae, 0x1816b1e6, 0x12918397, 0x02a4ef00, 0x05d7b515, 0x056214d5, 0x1fb94948, 0xba9c3f}}}, + /* 15*16^2*G: */ + {{{0x1e86d900, 0x167e6ae4, 0x08f6d124, 0x1dc13a2f, 0x03b36405, 0x0184501d, 0x00445ed2, 0x044b88f0, 0x5be0f5}}, + {{0x158a5755, 0x066eb4df, 0x0cd3d6b1, 0x02e4a483, 0x1423df92, 0x11d273da, 0x108b4d1f, 0x1cdbfe58, 0x83d1d9}}} + }, + { + /* 1*16^3*G: */ + {{{0x0245573f, 0x03bf46d5, 0x1f42993c, 0x0cede5e9, 0x16bd2508, 0x04b39736, 0x193665de, 0x1a59e0d3, 0xe716ae}}, + {{0x069218d1, 0x02fe135a, 0x01f26cd4, 0x1a1be5f4, 0x1a851d13, 0x183343dc, 0x0aad644a, 0x1cd29f8e, 0x353663}}}, + /* 3*16^3*G: */ + {{{0x057008ac, 0x1cc3c983, 0x0a7b7edf, 0x1c3d9dc6, 0x1a22d19c, 0x0ac5260e, 0x13a19395, 0x11adea0f, 0xd2bf89}}, + {{0x127beb55, 0x088a9a53, 0x1fc1e620, 0x19c2156b, 0x10c56679, 0x0aed9fd4, 0x1ea8fe06, 0x196b4d6e, 0x69c0b1}}}, + /* 5*16^3*G: */ + {{{0x182f1580, 0x086be54b, 0x080fadff, 0x09bbdc48, 0x064f8b0e, 0x0b9c8f98, 0x19821fe9, 0x09dcb079, 0x4d8830}}, + {{0x1616edd7, 0x007d8b6a, 0x1ac78443, 0x19477ad1, 0x16518aaf, 0x00044c7e, 0x0db3a35a, 0x0e13560c, 0x28a94e}}}, + /* 7*16^3*G: */ + {{{0x00c38e11, 0x1c261ed7, 0x1b89ae97, 0x0c8437d4, 0x1af9e715, 0x1cc90172, 0x1bacc99c, 0x04f91f81, 0x17c727}}, + {{0x0e90decd, 0x0c1b35dd, 0x0336b8f3, 0x07328b4c, 0x1802e7e5, 0x17e67480, 0x0cf10180, 0x1e9fccc9, 0x95c5a4}}}, + /* 9*16^3*G: */ + {{{0x032f36ea, 0x0aafec57, 0x0d5070f6, 0x1e8d4e52, 0x0b8b3cd4, 0x18e7e426, 0x02a2118e, 0x15588e8a, 0x7e03fd}}, + {{0x0a68b8af, 0x16611fe4, 0x140275a2, 0x1d2f3498, 0x0af99419, 0x158b80c2, 0x03699a39, 0x1e240245, 0xdcf6d5}}}, + /* 11*16^3*G: */ + {{{0x1953baa9, 0x01f29536, 0x18637a35, 0x0b5d0c93, 0x0b214261, 0x1d60ee50, 0x1c1d858d, 0x0d096379, 0x8ce6da}}, + {{0x152689e5, 0x073ae147, 0x0a4d075e, 0x0730d76d, 0x1b56807f, 0x1de48030, 0x082f9a6c, 0x171f5262, 0x3d9f4f}}}, + /* 13*16^3*G: */ + {{{0x0f22a96a, 0x15c6c29d, 0x1aae3e52, 0x03753b38, 0x0e2b008b, 0x1aa4badd, 0x15288294, 0x06081b80, 0xb570e7}}, + {{0x085d1b00, 0x199fb30e, 0x15732732, 0x1376f4b0, 0x0cfbda7c, 0x1bb4dae0, 0x1dfc0a30, 0x0c71ee3c, 0xdcd8a2}}}, + /* 15*16^3*G: */ + {{{0x189a7509, 0x12075ca2, 0x1ea01527, 0x0e172c83, 0x1f20de12, 0x0d1f3ae3, 0x10ec1284, 0x04797572, 0x44d943}}, + {{0x00437234, 0x10515cdb, 0x137449b8, 0x07c3fbe2, 0x193586f9, 0x1de693bd, 0x1c280d08, 0x10010803, 0xb2c5b3}}} + }, + { + /* 1*16^4*G: */ + {{{0x0eade3c4, 0x1f4232e3, 0x014a8140, 0x156e9392, 0x186b4714, 0x1219a072, 0x04363971, 0x0de9d23d, 0xa01836}}, + {{0x0b26e8d0, 0x02e21013, 0x1853cdfd, 0x0e509c88, 0x18369d5f, 0x12bc1a4e, 0x0c59f1b3, 0x02e28221, 0xe2bbec}}}, + /* 3*16^4*G: */ + {{{0x19e0af06, 0x10e6f562, 0x1b65635b, 0x07314ac5, 0x1055c92f, 0x1dad36a2, 0x059142f4, 0x001711b1, 0x9cdf1f}}, + {{0x0216ade7, 0x0bd196db, 0x1f2951a3, 0x0a45b832, 0x194b0828, 0x05de6e46, 0x15993656, 0x1fda7e55, 0x916d25}}}, + /* 5*16^4*G: */ + {{{0x1305097e, 0x09b95c9c, 0x0ee94660, 0x0c2e1d08, 0x0448338d, 0x0d9682a0, 0x0fb6adac, 0x1aba34cc, 0x45bfd9}}, + {{0x108ddc04, 0x05d02c74, 0x14b05655, 0x173a677f, 0x0e908683, 0x0182a386, 0x14aeeb62, 0x0250ab5f, 0x9d1dc}}}, + /* 7*16^4*G: */ + {{{0x163bd6df, 0x098f8d05, 0x19abb0f3, 0x184785ba, 0x15e6fd1f, 0x0fce0144, 0x04ed9c7a, 0x02ac8ecd, 0xb599ad}}, + {{0x0efa09ac, 0x150e751c, 0x04d2d163, 0x0189ed22, 0x1c4166a4, 0x1b05c591, 0x0cc918dc, 0x1bd89f56, 0x6979d1}}}, + /* 9*16^4*G: */ + {{{0x07142d62, 0x1c309f99, 0x0ef09b6b, 0x14d46bf0, 0x054c11d9, 0x158913f4, 0x103a4998, 0x1e2ce655, 0xf4dca1}}, + {{0x1a618fc9, 0x04f66603, 0x134f5feb, 0x0f359dfb, 0x1c659bb9, 0x1c0dc8d5, 0x151b527c, 0x1cb69699, 0x551167}}}, + /* 11*16^4*G: */ + {{{0x015fae61, 0x1658ffa3, 0x0e57e087, 0x0b8fb89e, 0x0bb5a7f1, 0x1120738b, 0x0d9b6713, 0x141cdc13, 0xf07278}}, + {{0x096376f9, 0x1e865ba4, 0x0dfaaf3d, 0x0e0e2b70, 0x0f7d42c2, 0x10410c4c, 0x1a97df5e, 0x056294e3, 0x1a681b}}}, + /* 13*16^4*G: */ + {{{0x0466591f, 0x027020a9, 0x11a35a83, 0x0e5d51ff, 0x058ccbe1, 0x15a99dd4, 0x16c3c50e, 0x0af5e141, 0x1fc524}}, + {{0x196f8cdb, 0x077f7ab9, 0x1b1e0891, 0x11958465, 0x14e1629f, 0x1b7f2da1, 0x1047a7b1, 0x070113d7, 0x489c18}}}, + /* 15*16^4*G: */ + {{{0x122a8ebc, 0x12e85c61, 0x1e59c544, 0x039b6e31, 0x056d5a29, 0x05ad9865, 0x17d3f5fc, 0x16634918, 0xfa2501}}, + {{0x0a8a775b, 0x06c93dad, 0x0f20cc60, 0x11c11cd9, 0x1a8cbea5, 0x0f330d37, 0x0588ce14, 0x1629f0b1, 0x71c932}}} + }, + { + /* 1*16^5*G: */ + {{{0x1d96dff1, 0x1bee765b, 0x157f3fa3, 0x08638355, 0x198d530e, 0x105ab866, 0x153ffbda, 0x10a283fc, 0xec738}}, + {{0x1c7b552c, 0x16420d63, 0x1b5e2aa7, 0x04c99d0f, 0x052511d5, 0x0277ac03, 0x1d7646b3, 0x09d0f5d0, 0xd6224f}}}, + /* 3*16^5*G: */ + {{{0x088d58e9, 0x0e192558, 0x18c60e14, 0x14b838c9, 0x0a7b6e94, 0x12353e21, 0x0a1ba64a, 0x1fb8e0c9, 0x96dac3}}, + {{0x0ebebc5e, 0x01a49895, 0x01f9b8e0, 0x17d13729, 0x0c439685, 0x024a49c1, 0x06b615b3, 0x1e75a8d8, 0xcb1faf}}}, + /* 5*16^5*G: */ + {{{0x0db29f92, 0x0a956899, 0x11ecb162, 0x03a4e372, 0x18f811d2, 0x0e1bc575, 0x0c4a8417, 0x079d629e, 0xe297b2}}, + {{0x05e58ddb, 0x0794a645, 0x1b505058, 0x079d770b, 0x19149122, 0x0dd5dc66, 0x02d2d203, 0x041f196e, 0xe13725}}}, + /* 7*16^5*G: */ + {{{0x0ad88c33, 0x0ca1dbdc, 0x1d1af2bf, 0x15c729b2, 0x0da97d91, 0x1e490692, 0x12d9ac1a, 0x071f6572, 0x1cd223}}, + {{0x048fb1b2, 0x14753c21, 0x12879258, 0x1ca262bd, 0x0bc2713f, 0x1205589b, 0x02c25b21, 0x1f071569, 0xfc3acd}}}, + /* 9*16^5*G: */ + {{{0x1b26aa73, 0x09d644e1, 0x18e8383d, 0x0fc23618, 0x11ee0cdf, 0x16986ffd, 0x0eff2c72, 0x15b73d3f, 0xf462d7}}, + {{0x18479e73, 0x02f560bb, 0x140b3289, 0x11c14600, 0x13c7a49e, 0x1d253439, 0x0c50354e, 0x034f068a, 0x406a0d}}}, + /* 11*16^5*G: */ + {{{0x1cd015e3, 0x170f0155, 0x194089cf, 0x01d2b2fc, 0x15168af9, 0x1f59e544, 0x12bdd6f6, 0x04ba7ee1, 0xe0f689}}, + {{0x12157cce, 0x15126a16, 0x0a4daef6, 0x116a723c, 0x0c77c55b, 0x14b6393a, 0x0aa54d89, 0x0621c907, 0x8531e}}}, + /* 13*16^5*G: */ + {{{0x0bb76b12, 0x1362188a, 0x0649da47, 0x1cecee7c, 0x15a00ea8, 0x1598957b, 0x15ff0760, 0x182aa57e, 0x28e4ad}}, + {{0x0c4747bd, 0x1f229d3f, 0x058a3fd5, 0x014c1e2e, 0x0a3f703a, 0x1b2db5cf, 0x06cfd392, 0x09dfb340, 0x14d74c}}}, + /* 15*16^5*G: */ + {{{0x076ff697, 0x1fac00ff, 0x01d918a2, 0x16d10ca4, 0x097c6369, 0x16d5d9d0, 0x017b49c7, 0x04f29750, 0x85a0ba}}, + {{0x12142721, 0x04f6a6d2, 0x02962e4c, 0x12fff4f2, 0x1aa551de, 0x0869ee76, 0x0929551e, 0x0c3d587c, 0xadf32e}}} + }, + { + /* 1*16^6*G: */ + {{{0x0392d805, 0x192e2ee7, 0x1501750d, 0x1500d30e, 0x1449aa87, 0x06d57d51, 0x0f5e9295, 0x19e98d52, 0xf8f5dc}}, + {{0x0cda02fa, 0x1330ca8b, 0x0850ee80, 0x07b4c94a, 0x1327351f, 0x1f19b230, 0x0150e274, 0x19ecdac6, 0xe58176}}}, + /* 3*16^6*G: */ + {{{0x1fa046f7, 0x0ea598b3, 0x01cc2746, 0x021e7204, 0x02d45171, 0x05644a37, 0x0ea53821, 0x0950cb10, 0xe12c8e}}, + {{0x012646ad, 0x1d2ad145, 0x0c464d14, 0x1809c226, 0x126f6dd0, 0x1f6a9c98, 0x0bcd0cec, 0x0c21fb34, 0x7cec04}}}, + /* 5*16^6*G: */ + {{{0x02853c43, 0x0d893f46, 0x08b919ae, 0x0cd8af5c, 0x13236481, 0x1177f1e8, 0x0824f423, 0x0e82f2d2, 0x394bd4}}, + {{0x0064469d, 0x0bc14665, 0x03f3c32c, 0x1ece25b2, 0x00767d52, 0x1fe178eb, 0x1ae481f8, 0x0a42a3b8, 0x2b4d6d}}}, + /* 7*16^6*G: */ + {{{0x1c722e94, 0x016eb9cf, 0x0162587b, 0x102072da, 0x004334ed, 0x132b62ca, 0x0ba51171, 0x1be71bd0, 0xca8538}}, + {{0x1049a527, 0x105316f8, 0x02c9a90e, 0x12a75149, 0x19f12f20, 0x189350fd, 0x170c479c, 0x085d73c0, 0x3b27fc}}}, + /* 9*16^6*G: */ + {{{0x1cad6309, 0x18ba314e, 0x0e7fd221, 0x143f85e4, 0x07b3dd31, 0x1a312653, 0x0dd686ed, 0x0b3e46af, 0x663e1a}}, + {{0x09981f28, 0x19435d1c, 0x1ad4af54, 0x0fa88805, 0x0f918b90, 0x00c1e58e, 0x030f040b, 0x07700cd5, 0x292fb7}}}, + /* 11*16^6*G: */ + {{{0x02c23d03, 0x13ad9229, 0x11ffc924, 0x03609b1f, 0x1eeab3ba, 0x1611b83d, 0x19c25b0d, 0x1ce60c6c, 0xa2d9ef}}, + {{0x1085db74, 0x01726027, 0x0e77d144, 0x134fd2b0, 0x01b92ea0, 0x021b6388, 0x0e3c554c, 0x05199083, 0x1cc852}}}, + /* 13*16^6*G: */ + {{{0x07d4c6d1, 0x1aafcb38, 0x167ffec5, 0x059aa335, 0x135d0f66, 0x085a939f, 0x07bf82e4, 0x05691635, 0x5657e1}}, + {{0x0106bdec, 0x0181f94c, 0x14b05062, 0x1346b428, 0x06a0abff, 0x1c8799d7, 0x07e6b3ec, 0x1e971ba9, 0x72b5be}}}, + /* 15*16^6*G: */ + {{{0x1933564a, 0x032a6eaa, 0x18ebd13e, 0x1169f5db, 0x18d2b7a6, 0x16e333b6, 0x00042193, 0x02ba815c, 0x5b6862}}, + {{0x0204ef63, 0x1af822c4, 0x1e21a7ce, 0x15dc4510, 0x195de392, 0x062a68ce, 0x19154b4c, 0x0d39e744, 0x76b5ea}}} + }, + { + /* 1*16^7*G: */ + {{{0x0f922dbd, 0x025118bb, 0x064863e6, 0x0c622c3f, 0x171b91ca, 0x1556c726, 0x1cc4fe17, 0x17ffa9b5, 0x6d28b6}}, + {{0x00ef3cff, 0x055ff815, 0x1368a651, 0x09d3e170, 0x14a1c4c2, 0x10d30e65, 0x0be903ef, 0x00a283ba, 0xaf39d9}}}, + /* 3*16^7*G: */ + {{{0x1b168b64, 0x0c86ffcc, 0x01b2102d, 0x07a0558b, 0x0467ea15, 0x0482fe0a, 0x0f774f7c, 0x0f4ff7fc, 0x7cd315}}, + {{0x1abbb16b, 0x0b60695a, 0x180b8d61, 0x189b36e5, 0x1fafb13a, 0x073c5e6d, 0x1188815a, 0x0422e525, 0x9ca08d}}}, + /* 5*16^7*G: */ + {{{0x06ef29e8, 0x158e37d0, 0x166bb8ec, 0x18aa9613, 0x1356264d, 0x018ccea2, 0x03115ac9, 0x1b6be5bf, 0xed860b}}, + {{0x19b90f8f, 0x00d0e24d, 0x036a8cd4, 0x061eb85b, 0x17bb1e76, 0x1b59a020, 0x1ff5f1af, 0x086ebab4, 0xb837e7}}}, + /* 7*16^7*G: */ + {{{0x1636e617, 0x0fc8c59f, 0x1c73d667, 0x19a07783, 0x12c9d47d, 0x0e173f13, 0x090dcd78, 0x11fa3a42, 0x6868cc}}, + {{0x1cbb3d27, 0x046ee79f, 0x0e1335c7, 0x1c619148, 0x13f0a60f, 0x081c3c98, 0x079acf16, 0x14b7237f, 0xeed008}}}, + /* 9*16^7*G: */ + {{{0x113e2a34, 0x1ced1530, 0x0e8478b1, 0x1a5ccb9c, 0x1d318291, 0x1a4f8857, 0x1880b172, 0x0c1d591f, 0xec03b3}}, + {{0x184566d6, 0x01121f75, 0x084dade9, 0x102adccb, 0x122728aa, 0x0f108090, 0x010defbf, 0x1f6c140b, 0x86020f}}}, + /* 11*16^7*G: */ + {{{0x087c1b18, 0x1570672e, 0x12646897, 0x1c596a90, 0x056fa101, 0x01ad2c88, 0x04379a8c, 0x1473cf17, 0xa79464}}, + {{0x1b94809a, 0x15ac284b, 0x158a086f, 0x11bd38e2, 0x089f60d9, 0x0bc3cf4d, 0x03309269, 0x1c7ee50b, 0x1361f4}}}, + /* 13*16^7*G: */ + {{{0x1ed10d26, 0x0a714636, 0x19657d96, 0x157df053, 0x10ccc5a9, 0x0fdb4a79, 0x131e43a4, 0x101551f7, 0x544ef2}}, + {{0x16dafcc2, 0x1e127b93, 0x0b01ab8b, 0x1adf2edc, 0x170d3683, 0x0eff46c6, 0x0b3a75ac, 0x13b88b58, 0x95b91c}}}, + /* 15*16^7*G: */ + {{{0x17ad18e6, 0x04a3cfc5, 0x0f2b5789, 0x1a7cf31a, 0x0ff9b57a, 0x1e4235a5, 0x18853794, 0x110be698, 0xe2c26c}}, + {{0x0554cfad, 0x0d7f28d5, 0x0607792b, 0x0a2b94e3, 0x1ba113bc, 0x1337508b, 0x06214738, 0x0509e910, 0xb2121b}}} + }, + { + /* 1*16^8*G: */ + {{{0x185a5943, 0x12d4f110, 0x1977ed8e, 0x12326cb8, 0x071da1ab, 0x15991316, 0x1e248595, 0x0815e455, 0x7fe36b}}, + {{0x099ca101, 0x0868a963, 0x02bc84b5, 0x07ab0cf7, 0x0a6f174b, 0x1a0203ee, 0x18927c27, 0x0b04b6c6, 0xe697d4}}}, + /* 3*16^8*G: */ + {{{0x1f0922a8, 0x153c048e, 0x173e365d, 0x01d3a4a8, 0x18b808e9, 0x072b0117, 0x15ce0249, 0x080b69c5, 0x4b656a}}, + {{0x199a80bb, 0x0d6e562c, 0x0143da53, 0x0773c573, 0x0c4d6d47, 0x06612aec, 0x00e48341, 0x03a25cef, 0xee1ea3}}}, + /* 5*16^8*G: */ + {{{0x09d07e9e, 0x0827c443, 0x09830dad, 0x1cf943ed, 0x18e5ea09, 0x15f07d9e, 0x04f64f6c, 0x0dd7acbc, 0x9d7895}}, + {{0x03d8c8ee, 0x06eb0ad6, 0x0531ecbd, 0x0145b477, 0x010ce746, 0x144e0e99, 0x0335950a, 0x0dde91d5, 0xd5149e}}}, + /* 7*16^8*G: */ + {{{0x1dc4950f, 0x13ba8433, 0x0e5081d9, 0x03655b7a, 0x16316574, 0x1423726d, 0x1ea55c8d, 0x0e5c26a7, 0x4acb12}}, + {{0x03d8b4df, 0x16372223, 0x019ab159, 0x1c85016b, 0x1e29fdf9, 0x0c7b6638, 0x16b633ca, 0x01f6e879, 0x726cd2}}}, + /* 9*16^8*G: */ + {{{0x0d7e8cbd, 0x0063d3df, 0x01f605bd, 0x0cff15e0, 0x1464a2a6, 0x09aa8640, 0x1ed08d11, 0x17e34d62, 0x640c5e}}, + {{0x1ec5a5db, 0x171481e5, 0x1fe6c081, 0x0d3f6319, 0x0ab9eba0, 0x0375b80f, 0x14bcaba7, 0x1fb0539f, 0xd6ae88}}}, + /* 11*16^8*G: */ + {{{0x1e86bddc, 0x18502583, 0x1738f57d, 0x04d72f2e, 0x16b9bf1d, 0x116486ed, 0x0cc91455, 0x16368509, 0x8bba04}}, + {{0x09e349c4, 0x111e2d0f, 0x065b97d4, 0x19fbc9e8, 0x1437ac6a, 0x1c90b34c, 0x1afd966f, 0x18138411, 0xc34099}}}, + /* 13*16^8*G: */ + {{{0x0257f582, 0x0b873c02, 0x10e89634, 0x1213d5bd, 0x05fe41f9, 0x0a1fbe2d, 0x1f88429a, 0x089fd9a6, 0x8b0a57}}, + {{0x17372d4b, 0x00bff6f9, 0x0581d992, 0x168d74f6, 0x17d4656d, 0x0ad09bf1, 0x1ce598a8, 0x09d81ad8, 0x9e57a4}}}, + /* 15*16^8*G: */ + {{{0x185e797e, 0x18a24fcc, 0x0f0c5a0f, 0x1c449e5c, 0x078a6f06, 0x17a49ecb, 0x025c7e15, 0x12f84c8f, 0xa13ff}}, + {{0x139d666f, 0x193d5cfa, 0x0030e4a0, 0x1ede14eb, 0x1dc3ff27, 0x00c747d0, 0x0f5645f6, 0x09644b90, 0xeb1a96}}} + }, + { + /* 1*16^9*G: */ + {{{0x1dde3445, 0x17fa7f0f, 0x0ba9e05e, 0x1753bb45, 0x0519e76b, 0x0ff3ff93, 0x079a14dc, 0x0709ae0c, 0x6965b6}}, + {{0x18855113, 0x0621c4f0, 0x17e6765f, 0x19cc0a34, 0x12dbef47, 0x04b31c39, 0x1444c979, 0x1c738904, 0xd1bcda}}}, + /* 3*16^9*G: */ + {{{0x088acfc5, 0x0f207cb4, 0x1a25e27d, 0x1eb1366c, 0x11a71b1e, 0x1eedea04, 0x1f6809f9, 0x03bbceb8, 0xe1cbc5}}, + {{0x0249a225, 0x1e2ff6ad, 0x0fee948e, 0x065257dd, 0x1e23ce7d, 0x1b27fedc, 0x0bb6cc49, 0x12f9bcfb, 0xec6783}}}, + /* 5*16^9*G: */ + {{{0x0107ccc6, 0x0c7529ff, 0x0f208a1d, 0x112fc2a8, 0x100481d3, 0x0e9422d4, 0x0a85cc7a, 0x0f2e2271, 0x9c3f26}}, + {{0x1dea9152, 0x003ca669, 0x1e6c61a1, 0x11fa9d2b, 0x06eca51a, 0x1afa5f9f, 0x16722f8d, 0x036c96a3, 0xdaee0c}}}, + /* 7*16^9*G: */ + {{{0x15a449e7, 0x0e2f5a1c, 0x0f0af908, 0x054069ca, 0x0922b2fa, 0x160bd8a0, 0x15c9e5be, 0x0aef8d65, 0x5c30d9}}, + {{0x0e2677d0, 0x1875240d, 0x139e439d, 0x1c80a787, 0x192d0b75, 0x12fbcce2, 0x0e48592d, 0x16b611c3, 0x8025e3}}}, + /* 9*16^9*G: */ + {{{0x0b8ceb07, 0x12ac6014, 0x1b89c73e, 0x124a57ff, 0x0d995537, 0x11583f54, 0x1a0abf93, 0x046191d2, 0xaa3bbf}}, + {{0x1fd83984, 0x0d423174, 0x0cfe7ae6, 0x036578f5, 0x162ec3b7, 0x187f2648, 0x09a11372, 0x161383d8, 0xe6320c}}}, + /* 11*16^9*G: */ + {{{0x03bfd1f1, 0x00752aac, 0x1595ccb9, 0x0db86a2b, 0x001943a3, 0x07e4c8a0, 0x0605e798, 0x0a226668, 0xab4a32}}, + {{0x08e1c233, 0x089cb086, 0x07a633c8, 0x1b26484f, 0x1e057a57, 0x1de82097, 0x19a2d3ac, 0x17606d90, 0xea6c64}}}, + /* 13*16^9*G: */ + {{{0x0b1d6d3f, 0x02036bc0, 0x1baee242, 0x0ecd149d, 0x1a8105f3, 0x1fae6c52, 0x15dfdccd, 0x0b3bbdab, 0xdba9b2}}, + {{0x09cd8133, 0x01749414, 0x0af64f31, 0x1b3788a2, 0x1871448b, 0x0ca7a7f5, 0x0ded6b2a, 0x07eaea6b, 0xb3862d}}}, + /* 15*16^9*G: */ + {{{0x11fa2a6a, 0x1cd01c01, 0x0392c959, 0x183ab0b6, 0x1cdfe34f, 0x0ba158d1, 0x1d141eb7, 0x1885d304, 0x1998f4}}, + {{0x06415142, 0x03f6da5e, 0x1c962d58, 0x180470f6, 0x0a1e94c6, 0x1c0045bc, 0x08425c03, 0x0da3215f, 0x2ebb41}}} + }, + { + /* 1*16^10*G: */ + {{{0x0d9aefbd, 0x0c7e5362, 0x0e55dd49, 0x1c8f64e7, 0x043dc1ef, 0x0f86a0de, 0x15d8cb2a, 0x03918cd3, 0xfbc34}}, + {{0x13e71ca0, 0x09db754e, 0x02a7fc7b, 0x160d3c40, 0x1d3d3950, 0x058ea4ab, 0x18ff9005, 0x0c65e6c1, 0xbd8022}}}, + /* 3*16^10*G: */ + {{{0x09434837, 0x1cb6fff7, 0x08306ee1, 0x0628170c, 0x1b06dadb, 0x0e37fda7, 0x0bb6c4d0, 0x1578950f, 0xd76d18}}, + {{0x1b215b4c, 0x1d5027cd, 0x0df33093, 0x08b1ceeb, 0x1933290a, 0x010a7bd5, 0x19137839, 0x1465db2c, 0xf6bc4d}}}, + /* 5*16^10*G: */ + {{{0x1c07f222, 0x1744f90c, 0x183f9f40, 0x0438c758, 0x153d1c5e, 0x0e8d0f8b, 0x1d813d20, 0x0a0c2cff, 0xd5c0c6}}, + {{0x0c6ffd57, 0x177b48f0, 0x004ea1b8, 0x07ea34f1, 0x175b9baf, 0x063bfa4f, 0x02143378, 0x10c102f7, 0x15a30b}}}, + /* 7*16^10*G: */ + {{{0x0e93bfa6, 0x1238b512, 0x084d8a92, 0x1a52b413, 0x09fe0d39, 0x05d335a6, 0x18b39527, 0x09c948de, 0x734c36}}, + {{0x18d10774, 0x037d3ccc, 0x00a5f13f, 0x026c4112, 0x05f48eca, 0x00f1a906, 0x141277a6, 0x007554f3, 0x99515}}}, + /* 9*16^10*G: */ + {{{0x1fa194ae, 0x075a8bfa, 0x0152bb3c, 0x00523b34, 0x0b149064, 0x0ece954f, 0x0a24045d, 0x1b40f6cd, 0x79a3d9}}, + {{0x1fcf634b, 0x1e32f4e4, 0x1e6f1353, 0x084be65e, 0x103d86bd, 0x18dc2c57, 0x06cd2cd9, 0x194e4a96, 0x84e1db}}}, + /* 11*16^10*G: */ + {{{0x01e8b7fe, 0x16483001, 0x016c9a9a, 0x01c5c2ef, 0x098e05dd, 0x06556e7e, 0x160a28f9, 0x0129ab60, 0x3393fd}}, + {{0x023c6821, 0x00a12210, 0x06e52dc3, 0x0d661515, 0x0668d8e5, 0x1576ce2d, 0x1ae0babf, 0x17d90cf7, 0x130437}}}, + /* 13*16^10*G: */ + {{{0x01528cf6, 0x04abceab, 0x141a53a8, 0x004a15ec, 0x10a52e6a, 0x02c3772d, 0x1f85786d, 0x10c268c4, 0xa16b28}}, + {{0x1ce93f3b, 0x05c907cf, 0x132004e0, 0x07f79027, 0x150f4349, 0x16bcec08, 0x166644f3, 0x15d0a6f5, 0xf40598}}}, + /* 15*16^10*G: */ + {{{0x0c8db2e0, 0x0885335c, 0x035cd60d, 0x197b82be, 0x074f6473, 0x04d5d4ad, 0x1d32fdb2, 0x04031c68, 0xd317ce}}, + {{0x074bc9a9, 0x0d9a5159, 0x0e183ce9, 0x04e9a045, 0x19136caa, 0x01f471cb, 0x112e670b, 0x01521270, 0xd61a21}}} + }, + { + /* 1*16^11*G: */ + {{{0x03fe67b2, 0x1718cbd5, 0x0b8cce31, 0x117fce14, 0x123b234a, 0x15cdd4b9, 0x1772f199, 0x086ee790, 0x6608c2}}, + {{0x05b47a28, 0x091ff2e7, 0x1150c206, 0x162b3e81, 0x0e03fc22, 0x1206d3a3, 0x05a211bd, 0x17d8a438, 0xa1a916}}}, + /* 3*16^11*G: */ + {{{0x0bc73e02, 0x1f375569, 0x194c4b04, 0x0cc29dd6, 0x18631849, 0x0c08beab, 0x108c81aa, 0x1c54d8bb, 0xf0956a}}, + {{0x0611219d, 0x0967b4cc, 0x0ba515cb, 0x1a091b77, 0x19356672, 0x02c57941, 0x00f1db88, 0x0c02289c, 0x433fe6}}}, + /* 5*16^11*G: */ + {{{0x1c5fe45c, 0x0f116f28, 0x1f03d65f, 0x05035070, 0x1283488b, 0x124ebe7b, 0x183294bc, 0x1d479454, 0xdb98eb}}, + {{0x1ef45a22, 0x05380e21, 0x12038711, 0x06409f54, 0x12c8ce98, 0x094eec48, 0x168b7854, 0x016eb7b0, 0xe0c0f3}}}, + /* 7*16^11*G: */ + {{{0x02128eac, 0x1acf306f, 0x0bc732eb, 0x0eb1ab99, 0x051690c4, 0x159dbeb5, 0x17d086e9, 0x02c15bb4, 0xa73c0}}, + {{0x04f1180d, 0x02c948eb, 0x1576728e, 0x0eb3e2ee, 0x10a7c793, 0x1b6eca7a, 0x0e329572, 0x0ac089d0, 0x443fda}}}, + /* 9*16^11*G: */ + {{{0x03ccbc30, 0x12d4998b, 0x0d258631, 0x1fcba43a, 0x197249ed, 0x1bb33d04, 0x1c8dc3b4, 0x1868f969, 0x378720}}, + {{0x1315729e, 0x110193f8, 0x0d23e11a, 0x19f2aba2, 0x19b6d16b, 0x05605553, 0x0e324c46, 0x1739ee4a, 0x8f1bdd}}}, + /* 11*16^11*G: */ + {{{0x03bb1b6b, 0x12546f2a, 0x1f0afea9, 0x1fde65e8, 0x1a621575, 0x08c6082a, 0x09867660, 0x03b6f163, 0xfe8fd2}}, + {{0x10eaaf93, 0x1daecdd1, 0x0afe2265, 0x09bb9fe1, 0x113c00f1, 0x074afad6, 0x18dda78a, 0x15d864ad, 0x791e22}}}, + /* 13*16^11*G: */ + {{{0x144b7cea, 0x002acce5, 0x1b3e1f7a, 0x08d28122, 0x1dcca474, 0x00113c59, 0x18bba081, 0x1aaf3f6f, 0x2c331f}}, + {{0x15d1c9c2, 0x1e15a356, 0x0c2501df, 0x04a23fe2, 0x1d650619, 0x19ec61de, 0x19dbf3cf, 0x0399c0c3, 0x20567b}}}, + /* 15*16^11*G: */ + {{{0x0112278c, 0x1c94d9bc, 0x03668563, 0x02c93a15, 0x04b66dd0, 0x1a643fc2, 0x0c828d22, 0x0d88ca34, 0x509bc0}}, + {{0x0e178f66, 0x043ae4c4, 0x0d7e517b, 0x0bfd0dce, 0x164570f6, 0x1a78bdc7, 0x1bb05d83, 0x1856e448, 0xb4b168}}} + }, + { + /* 1*16^12*G: */ + {{{0x17e55104, 0x175d7c00, 0x03a71c70, 0x1506bf77, 0x1561cf73, 0x09e1a6c5, 0x1cdd8f7a, 0x0a44f6f0, 0xd8de76}}, + {{0x052e08cd, 0x10177c07, 0x1036c6ca, 0x0e7f9c32, 0x0f924c2f, 0x114568ee, 0x0b457131, 0x0cb7c27e, 0x2fd294}}}, + /* 3*16^12*G: */ + {{{0x0987bfd3, 0x09ce163f, 0x110a131a, 0x0a8e5fe9, 0x1d19187c, 0x1df57d6e, 0x18cd5477, 0x0df7aa31, 0xe3a578}}, + {{0x0cc23e7d, 0x1698db64, 0x0a38205f, 0x19e5d9b2, 0x0210ca3d, 0x1cffa56c, 0x039a11de, 0x012a6b08, 0xf74bad}}}, + /* 5*16^12*G: */ + {{{0x02d5056e, 0x13ca8e91, 0x11e81f2d, 0x196b4fc4, 0x05e480ed, 0x0ffe74b7, 0x03504549, 0x083d94a4, 0x42a970}}, + {{0x10f7db11, 0x020a6e45, 0x07f47f52, 0x1c907add, 0x03b26b64, 0x16e935ab, 0x1c19bb0d, 0x0cd9e010, 0xf2fc7f}}}, + /* 7*16^12*G: */ + {{{0x13dafd3b, 0x13a2fc8e, 0x12ce55b1, 0x1133e05b, 0x179d66fb, 0x16176b29, 0x1933f88c, 0x036384f2, 0xd6ca7}}, + {{0x1a6fe6e8, 0x156f8a10, 0x021dc6b5, 0x049157dd, 0x1de58a02, 0x0a501383, 0x04f67f92, 0x1671fbc5, 0xa85130}}}, + /* 9*16^12*G: */ + {{{0x11131f8b, 0x15fce3ff, 0x0883537d, 0x08d2c19f, 0x11fb4696, 0x116fce21, 0x0f65f530, 0x0f5b0cb4, 0x5edca}}, + {{0x1a6f779f, 0x167858cd, 0x1a275243, 0x04a00be1, 0x071be335, 0x0b8daf81, 0x1af00727, 0x03c91d10, 0x9e5888}}}, + /* 11*16^12*G: */ + {{{0x0b597e4d, 0x047cfa0f, 0x1f468c67, 0x09e64bce, 0x11c11497, 0x02d19dbb, 0x0290486d, 0x0ac178f4, 0x5a13c0}}, + {{0x041856e0, 0x1bf2434f, 0x04d9992b, 0x1d0f56b2, 0x173ce7bd, 0x0c507916, 0x1ffd47f4, 0x027121f2, 0xc9e73c}}}, + /* 13*16^12*G: */ + {{{0x0260faf0, 0x153c4b0a, 0x1a564d76, 0x17a68b2f, 0x1272ea2b, 0x070d3407, 0x19d50c51, 0x0ac02f6d, 0x96b256}}, + {{0x0366d412, 0x07212907, 0x1f53d6b0, 0x177f30e0, 0x199e1890, 0x072b99df, 0x12b002b6, 0x1400366f, 0xdcf8a6}}}, + /* 15*16^12*G: */ + {{{0x0ad13276, 0x04d0df25, 0x1010f1e9, 0x06a1d5d3, 0x171a3ca6, 0x15959c3b, 0x18909bc4, 0x0218839b, 0xb9719}}, + {{0x0a84dadb, 0x19c79a10, 0x1c3eb8ec, 0x1af25304, 0x0811f593, 0x122d9dfb, 0x1bef538b, 0x0dde00dd, 0xac2848}}} + }, + { + /* 1*16^13*G: */ + {{{0x071e5c83, 0x1535e490, 0x10a82fbb, 0x04fe330a, 0x0e5b18bd, 0x02db952c, 0x1cfc82a1, 0x082a04da, 0x54ccc9}}, + {{0x140916a1, 0x1e8477b8, 0x03b925b3, 0x1c1798bb, 0x0bf22929, 0x038aed69, 0x14c8ea3e, 0x08b68a28, 0x1c433f}}}, + /* 3*16^13*G: */ + {{{0x0f76167c, 0x02f1b115, 0x1d19ce53, 0x1e845988, 0x0d9f22f2, 0x163496e6, 0x1e54e708, 0x1b96e7df, 0x87582e}}, + {{0x0f4d7ff6, 0x037ddb0b, 0x065efa1b, 0x051e0cee, 0x036a8880, 0x0161e1b9, 0x09eff784, 0x06a15ac9, 0x75b94}}}, + /* 5*16^13*G: */ + {{{0x14de6611, 0x1391f743, 0x0087ec11, 0x0cae7297, 0x0f11e33f, 0x1b9b1ab3, 0x1b6c7096, 0x1943e4e6, 0x4a107a}}, + {{0x18a805fe, 0x1ac75f72, 0x084cab00, 0x1aa8f4b8, 0x0052e075, 0x12747b80, 0x1ce4d339, 0x0a200c7d, 0x809b26}}}, + /* 7*16^13*G: */ + {{{0x0dcfecb0, 0x05d27683, 0x0a3611bf, 0x07fa3a61, 0x149f6e98, 0x0706a4df, 0x1f259528, 0x166e555b, 0xfd402e}}, + {{0x0da08de8, 0x02c9f9ef, 0x19979e44, 0x1698497b, 0x1a86b1a4, 0x131c4dd5, 0x088ba698, 0x061bb4c7, 0xd6b340}}}, + /* 9*16^13*G: */ + {{{0x154659ed, 0x1a6eb07a, 0x17659b96, 0x1c7a4432, 0x1d0a07fa, 0x0703ff64, 0x145577ee, 0x08c8c30e, 0x467bb3}}, + {{0x0800cf83, 0x1903e859, 0x004f8026, 0x17821b9a, 0x02cd3701, 0x06ac36dc, 0x03a287cb, 0x1a0b552f, 0xd01eff}}}, + /* 11*16^13*G: */ + {{{0x1c97e515, 0x1324c256, 0x021697fb, 0x09ad8673, 0x0c7e5691, 0x0db1874f, 0x0391b21e, 0x11f19bb8, 0xcf66b9}}, + {{0x006c8a51, 0x0c10a754, 0x0400cea5, 0x00e8b1b3, 0x06a7b33f, 0x1a081a76, 0x04847096, 0x0f72088c, 0xa7071}}}, + /* 13*16^13*G: */ + {{{0x186154e5, 0x1d295080, 0x0be8374e, 0x0054cd35, 0x06770864, 0x04a9d6a4, 0x08d1d472, 0x18c1f7f5, 0x3d9252}}, + {{0x1603a8a2, 0x1fe49fb4, 0x1f75c1dd, 0x03f6faf8, 0x14cf3aea, 0x19a144b5, 0x15ff1124, 0x1b9a5f53, 0x3d0d01}}}, + /* 15*16^13*G: */ + {{{0x0a4a8daa, 0x1db1db15, 0x029728f4, 0x00c01de5, 0x0968ce51, 0x1ada47a5, 0x1a71d83a, 0x164d112e, 0x2bbe38}}, + {{0x1236e49b, 0x1043ba74, 0x1adb9f5f, 0x056749c7, 0x00850990, 0x1f7c8fcb, 0x0dbd78b3, 0x1ec01a76, 0xf85707}}} + }, + { + /* 1*16^14*G: */ + {{{0x02d32936, 0x187dbb89, 0x011fc070, 0x19a05c65, 0x17f38e40, 0x03e8a89e, 0x11f67db3, 0x0b2f0294, 0xc5440c}}, + {{0x07cabfd4, 0x1217d1ba, 0x19079766, 0x0da50c63, 0x0f4acd8a, 0x0f0e91f6, 0x1e9cbb70, 0x174707c3, 0xd27ee9}}}, + /* 3*16^14*G: */ + {{{0x1989a8e1, 0x0c280f63, 0x1b92f75f, 0x1b2de6e9, 0x0fe0a7c1, 0x1023cdce, 0x1e6dd403, 0x188b9bf4, 0x3e4d03}}, + {{0x041c240a, 0x03c89c42, 0x0f59d026, 0x1e31abe7, 0x0a719ffa, 0x0be56be5, 0x11d6ab04, 0x1a102b4c, 0x12f744}}}, + /* 5*16^14*G: */ + {{{0x19bb7902, 0x09cd4686, 0x0df1cfda, 0x04d8e50d, 0x05e9fd8c, 0x124f4a24, 0x00d66a68, 0x09367ac2, 0x1b684}}, + {{0x003ee653, 0x0bfa258f, 0x00d9a0b3, 0x164c08e3, 0x11581fce, 0x0c72e1b6, 0x10f82fc6, 0x143d26f3, 0xe59063}}}, + /* 7*16^14*G: */ + {{{0x0371d9fe, 0x14dab6a6, 0x0675aef0, 0x166ad833, 0x13a4cf04, 0x0ad3e1d5, 0x1288cd65, 0x16359993, 0x952c3c}}, + {{0x0189a13f, 0x1b673692, 0x0231d7b5, 0x08c9e230, 0x0e5d0664, 0x089b7b76, 0x1c1a9f7e, 0x08defac5, 0x59b985}}}, + /* 9*16^14*G: */ + {{{0x0bc2c885, 0x03ffe3b4, 0x19395f21, 0x03dceea4, 0x1cde5155, 0x002285cb, 0x1ab21626, 0x1c2b62cd, 0xdfcb4e}}, + {{0x1345d92e, 0x0ea53218, 0x0afa5d5d, 0x0e7128f7, 0x00f411c7, 0x1e136416, 0x0e854f13, 0x12aaa0c2, 0x536a23}}}, + /* 11*16^14*G: */ + {{{0x1510208e, 0x1c5c295f, 0x15e50e1c, 0x199c5b09, 0x097bbdb8, 0x179d023e, 0x00322a1e, 0x18137cca, 0x157966}}, + {{0x0d207357, 0x10777128, 0x1690f7f5, 0x1f8c8865, 0x0be07008, 0x1bdbddc6, 0x193aaf7f, 0x0b56e244, 0x40446d}}}, + /* 13*16^14*G: */ + {{{0x0b96fe13, 0x04fe65f4, 0x014baa07, 0x0495a769, 0x16e23e49, 0x147fc09f, 0x042b5b86, 0x078963d3, 0xc6b21e}}, + {{0x17bb1417, 0x0035cb57, 0x19b4b5a8, 0x0aa122f3, 0x128a2ff6, 0x1c0f1a75, 0x1523952d, 0x1b186669, 0x335bcd}}}, + /* 15*16^14*G: */ + {{{0x03936565, 0x1b2c0cd6, 0x1fe931f0, 0x0f66db2e, 0x122d997a, 0x054ea9ca, 0x05bc2d2f, 0x188f18e5, 0xa231e4}}, + {{0x15ec5f19, 0x172b5031, 0x1c5509a2, 0x0adc012d, 0x1cf942ba, 0x07634401, 0x1a470365, 0x117d8ff7, 0x80e0c9}}} + }, + { + /* 1*16^15*G: */ + {{{0x1066fd48, 0x1cfa0b95, 0x05c560ef, 0x0e203971, 0x0dca61c3, 0x0dcbd35d, 0x07141b1e, 0x0f4844fe, 0x241c56}}, + {{0x12857b08, 0x1be9633c, 0x08d9815f, 0x10e2715d, 0x003a48ea, 0x00be0219, 0x152e4d8e, 0x127a8605, 0x40a62d}}}, + /* 3*16^15*G: */ + {{{0x0df9591d, 0x10fbedc8, 0x0d320aa1, 0x18758485, 0x07218dce, 0x09e25599, 0x03a72e83, 0x07704d2f, 0xde2fd2}}, + {{0x0457ad84, 0x070cb9e8, 0x100da92d, 0x15143f11, 0x12ebbda9, 0x1bf6425c, 0x0fcc17b2, 0x02676c48, 0x400d71}}}, + /* 5*16^15*G: */ + {{{0x1562282c, 0x15412a57, 0x1ef0ddcb, 0x1e75f271, 0x11340f02, 0x04581270, 0x0f7664e5, 0x16060999, 0x8df889}}, + {{0x01d195cd, 0x12b55ecb, 0x1e6ec55c, 0x1ee0899d, 0x0f35e247, 0x0f318c45, 0x1bb5b1d0, 0x0ce640b9, 0x74525b}}}, + /* 7*16^15*G: */ + {{{0x0dddb2dd, 0x0ea944a8, 0x0b0369be, 0x10c99b98, 0x0f245078, 0x1c0678a9, 0x03e0007e, 0x119b3170, 0xa0fd75}}, + {{0x13ede6b2, 0x1eca7fc3, 0x10269f1f, 0x19d2df12, 0x08f311c8, 0x0fe989d8, 0x0357e1a4, 0x06b8266d, 0x53e5d8}}}, + /* 9*16^15*G: */ + {{{0x0f542e36, 0x0465d502, 0x0d0570b8, 0x05ff5f42, 0x135d84e2, 0x0933ca31, 0x03d9f796, 0x108e5a34, 0x3170c5}}, + {{0x163288b5, 0x1623ad77, 0x066f86f1, 0x1eead2b3, 0x1773d006, 0x0849ff5c, 0x1f88dd45, 0x025f874f, 0xb20836}}}, + /* 11*16^15*G: */ + {{{0x1c7548e9, 0x0cd91d2e, 0x04b5531e, 0x1b500e11, 0x03fe5d8d, 0x00b4a783, 0x180a76d2, 0x152145a7, 0x92fab0}}, + {{0x0917758f, 0x03896682, 0x0b421223, 0x01b8d1de, 0x079ffc8a, 0x18a70613, 0x1af3d0c5, 0x0d019648, 0x55e7b4}}}, + /* 13*16^15*G: */ + {{{0x1498f7f8, 0x06c0285c, 0x104588b8, 0x1ecfa64c, 0x08712c4d, 0x108d8c96, 0x145e742f, 0x17c3006a, 0x91b065}}, + {{0x1f23195b, 0x03a06cf1, 0x0258e78f, 0x18f684af, 0x1e264df2, 0x19a4800b, 0x1883fe7f, 0x0eff6ce2, 0x35b6f}}}, + /* 15*16^15*G: */ + {{{0x0060e322, 0x0ee5f712, 0x113452d4, 0x1b8e6f53, 0x0b9923ec, 0x034ba44d, 0x0cea70e4, 0x09995939, 0x8e4a1f}}, + {{0x104619d7, 0x110c1e6c, 0x13eff813, 0x01531b2a, 0x07bc4fb0, 0x0f692037, 0x1dd4bec1, 0x0bd6651a, 0x4936b7}}} + }, + { + /* 1*16^16*G: */ + {{{0x0e14db63, 0x073ae5a4, 0x1947dfa4, 0x1277555a, 0x025de294, 0x0c971937, 0x0a961249, 0x17850235, 0xfa822}}, + {{0x1f462ee7, 0x008922a2, 0x1fa0bd79, 0x034ca0a1, 0x1188b34b, 0x0a5e59ef, 0x0035bd2b, 0x1d1ebb75, 0xbff44a}}}, + /* 3*16^16*G: */ + {{{0x1db3cdec, 0x096229fb, 0x0a3afd5d, 0x1e742564, 0x04bc8dbe, 0x122f3df5, 0x1d739659, 0x0c9ff225, 0x85b2c0}}, + {{0x0a03f81f, 0x1a684102, 0x133e3823, 0x101669cc, 0x06d00dc9, 0x1d1697e6, 0x14d98f1f, 0x11e73a22, 0xf64b27}}}, + /* 5*16^16*G: */ + {{{0x0607b030, 0x0452d724, 0x01359cca, 0x15fec7cb, 0x0f29c24d, 0x1dd6760b, 0x119de147, 0x0ed88042, 0x110b03}}, + {{0x13617c3a, 0x01d50895, 0x0a61d27d, 0x1c2aadf6, 0x1c70c87b, 0x1c4fc230, 0x19cd31ba, 0x10a631dc, 0x84432b}}}, + /* 7*16^16*G: */ + {{{0x15a76f08, 0x076ec0e3, 0x0efb5395, 0x0be4a717, 0x0aaf8329, 0x1092158e, 0x075c53db, 0x0893ec8e, 0x18784e}}, + {{0x0b824993, 0x19cb02eb, 0x02894c82, 0x1ae94f4c, 0x1e671fc9, 0x147ed638, 0x0b9c5dde, 0x0fe943c3, 0x8d76ed}}}, + /* 9*16^16*G: */ + {{{0x1a6fad81, 0x14719e4f, 0x032c0fb3, 0x06cae918, 0x0037d9c3, 0x16ebb81d, 0x1ae6bbd5, 0x1c0fa0bc, 0x58a2f9}}, + {{0x1b109594, 0x00030af1, 0x0c02e095, 0x1765a65a, 0x1a6bd798, 0x017c38bf, 0x038306da, 0x18b58aac, 0x6ab64}}}, + /* 11*16^16*G: */ + {{{0x1eb52583, 0x191788fc, 0x03304ebe, 0x15339d82, 0x1676fea8, 0x16dd93c7, 0x0e8903b9, 0x1b99cfd7, 0x1ef020}}, + {{0x1a3f17ae, 0x0d93c0cb, 0x04b532d5, 0x0601f2e0, 0x095306ac, 0x1607a5cb, 0x12c025fd, 0x164b4f42, 0x594476}}}, + /* 13*16^16*G: */ + {{{0x10a1958f, 0x0e11f60f, 0x0d5990ed, 0x136a48df, 0x1a32c4f3, 0x0a267d5f, 0x084e5774, 0x0c783ad4, 0x4e4a81}}, + {{0x08636f8a, 0x01d9fa8a, 0x110f59a9, 0x0810bf65, 0x06fa8400, 0x051f714e, 0x03440cfd, 0x0b6f3d19, 0x2574a4}}}, + /* 15*16^16*G: */ + {{{0x04935db5, 0x013ad423, 0x1af6c3ba, 0x0f304c38, 0x0519e281, 0x1f076aca, 0x04138cc9, 0x02d5bac2, 0xf6966a}}, + {{0x1d166838, 0x11a41b60, 0x006fc04e, 0x08a74688, 0x0755e2d7, 0x172b961e, 0x16cb0697, 0x00f52063, 0xba0de3}}} + }, + { + /* 1*16^17*G: */ + {{{0x176a6987, 0x07a0982d, 0x1690ffda, 0x10356887, 0x01b3f2b4, 0x14c46bf7, 0x0551f771, 0x1af53313, 0x54bc18}}, + {{0x1b9aae49, 0x122be682, 0x1cec467f, 0x171da976, 0x1e2fd52e, 0x1c28dd39, 0x0bcdce46, 0x02423cdd, 0x4b2c8c}}}, + /* 3*16^17*G: */ + {{{0x1d1fd820, 0x05fa3faf, 0x1d9d400e, 0x0f0a8c90, 0x1271b788, 0x08113158, 0x14aaa18d, 0x1ed01838, 0xbafbbc}}, + {{0x1bf074f0, 0x176d6a90, 0x07f2b5ba, 0x18694246, 0x1c0fde81, 0x1fad644d, 0x1604b39e, 0x0f93b69b, 0x148799}}}, + /* 5*16^17*G: */ + {{{0x14299b7e, 0x08b84094, 0x01b8343a, 0x0f3e5a13, 0x020ec24e, 0x1dfe3ae1, 0x07f8a8f0, 0x1ee20671, 0x9b1938}}, + {{0x047b84de, 0x0bd942ba, 0x1ae08cb1, 0x1bd0a3f0, 0x03ec90ac, 0x14c70e55, 0x0a0cc503, 0x0dde2e20, 0xfb12aa}}}, + /* 7*16^17*G: */ + {{{0x110cc4c4, 0x0a083a06, 0x094f683f, 0x1a6b4589, 0x01b2cb71, 0x0fee033b, 0x1641f0e0, 0x10b9802e, 0xa67719}}, + {{0x1c976570, 0x1079d91b, 0x1fcc2530, 0x08aee74d, 0x19c3dbc7, 0x0b300f2e, 0x1663ca6f, 0x10beea1b, 0x855061}}}, + /* 9*16^17*G: */ + {{{0x14242a28, 0x0084016a, 0x0f29dc55, 0x1796424a, 0x14eca455, 0x17bc25bb, 0x1f0427a2, 0x0a6d61d5, 0x88b7ed}}, + {{0x194ed8ce, 0x1cb40f63, 0x00f8d1fe, 0x0cd4391f, 0x1bd934e3, 0x1cee9ac3, 0x11791fef, 0x040c48a6, 0x22b053}}}, + /* 11*16^17*G: */ + {{{0x1bc1a94c, 0x0b0a2764, 0x1c76be00, 0x0e3a567c, 0x08757516, 0x1958112f, 0x0f4814b5, 0x002550d6, 0x37114}}, + {{0x0fc2073c, 0x00605b67, 0x110920e3, 0x03186c55, 0x05335c85, 0x10d2a568, 0x0e43be30, 0x07aa3bae, 0x4d1d69}}}, + /* 13*16^17*G: */ + {{{0x077f88ba, 0x1de8f04c, 0x06fb4dbb, 0x100ef3dd, 0x02cc3509, 0x11974275, 0x04f8d2d6, 0x09913085, 0xcc9821}}, + {{0x07885278, 0x123177e4, 0x0d531382, 0x0c1cfdde, 0x163abeff, 0x053233be, 0x1ea44531, 0x1d1ca4b5, 0x4ebb4}}}, + /* 15*16^17*G: */ + {{{0x1d70187c, 0x0b98ca6f, 0x19031fc5, 0x1590c536, 0x15bf8751, 0x12f01487, 0x043e6053, 0x00534854, 0xad3ae}}, + {{0x1fc775a7, 0x0ffe7a59, 0x18a83d9f, 0x09f6102c, 0x1b33dd03, 0x0af82fd5, 0x1b8de171, 0x1a8eb108, 0x2af040}}} + }, + { + /* 1*16^18*G: */ + {{{0x10aae231, 0x0557d68b, 0x1e5adf18, 0x0970a4f3, 0x1756f519, 0x0411c933, 0x0fca17c9, 0x0d32ec3c, 0x1d35c9}}, + {{0x0cd6ac71, 0x033831d6, 0x0699bc56, 0x0b1548e5, 0x1f810edf, 0x055b1175, 0x008c7598, 0x16c5bec1, 0xc7226c}}}, + /* 3*16^18*G: */ + {{{0x0cfa3fac, 0x1069ff0c, 0x0ae064f3, 0x1b0d3f86, 0x1803023a, 0x1da2eb06, 0x0d338b3a, 0x08f4da44, 0xdf3d40}}, + {{0x0fc7d419, 0x03136c9c, 0x020a5d7d, 0x0c79c92d, 0x19dbfafd, 0x10f94e07, 0x036c92a8, 0x0a453fa8, 0x48fa6f}}}, + /* 5*16^18*G: */ + {{{0x1f1322d4, 0x16c92d70, 0x13372eeb, 0x14d28095, 0x1f822cb0, 0x16265188, 0x0513a879, 0x0d563f2c, 0xdbb2af}}, + {{0x0a8c5311, 0x12495a7a, 0x1f8e97ea, 0x1f92f0f6, 0x13f9d9a9, 0x068f6b21, 0x186a86d8, 0x1725e26a, 0xebae75}}}, + /* 7*16^18*G: */ + {{{0x01138f7b, 0x034d19e5, 0x1cba3cbc, 0x03927c5b, 0x15f0bc4c, 0x05133fcd, 0x1f532bea, 0x0b2fe4c3, 0x1dba7b}}, + {{0x039bf268, 0x0416c63d, 0x008cb037, 0x0dcd6f33, 0x007f5813, 0x08035dc0, 0x1f18eb86, 0x07cbbcfd, 0x6aaf7d}}}, + /* 9*16^18*G: */ + {{{0x179a70af, 0x0d43bb80, 0x1937ca23, 0x1a853536, 0x105c6ec9, 0x1416afa9, 0x0674c6ef, 0x0884c4d2, 0x348a2e}}, + {{0x07b66e82, 0x142430a4, 0x184f96b2, 0x0f858b6f, 0x10105b2a, 0x1de70011, 0x1a0231d1, 0x0f3eab47, 0xa1b9fa}}}, + /* 11*16^18*G: */ + {{{0x04d3a201, 0x1a3b6bb6, 0x06e7bbf1, 0x124d49fb, 0x0df5f36e, 0x07dfc98d, 0x1872a1a9, 0x0a333393, 0x3a4ec8}}, + {{0x08635f89, 0x0c2cb7bb, 0x0f3556f7, 0x063cb5ee, 0x14a8b27d, 0x1e08d23b, 0x0b79a780, 0x1f05ec4c, 0xc99a2e}}}, + /* 13*16^18*G: */ + {{{0x1c978567, 0x17d7397e, 0x1707e607, 0x1b4d1081, 0x1f60be15, 0x1aeb17f8, 0x115e13a4, 0x1b10669a, 0xb1ba52}}, + {{0x162dc291, 0x1fd24151, 0x1f35029d, 0x08d96175, 0x1b78fd9a, 0x1e7b89ed, 0x0e9df17d, 0x18f50d28, 0xa46bae}}}, + /* 15*16^18*G: */ + {{{0x12350da1, 0x11477528, 0x1d7c10f4, 0x0298ef82, 0x12c2f194, 0x1bdcb4b0, 0x0bf49c62, 0x07b1de55, 0xae6eb}}, + {{0x10a14bc3, 0x19f04f9b, 0x10f692f1, 0x08d1e1da, 0x189b566d, 0x1a7bfe20, 0x12b8b740, 0x13f6c00d, 0x99cdee}}} + }, + { + /* 1*16^19*G: */ + {{{0x150e4f5f, 0x0b957543, 0x19f83995, 0x0b95354a, 0x0f29acbf, 0x187bd501, 0x0bbce23f, 0x0b30896b, 0x55d9a9}}, + {{0x1ca97db0, 0x02c75bb5, 0x0b46c572, 0x10218c67, 0x0ec524c9, 0x0ba7de64, 0x080dd9b5, 0x1354bb5a, 0x69cb7f}}}, + /* 3*16^19*G: */ + {{{0x1eb142e9, 0x0354c43f, 0x01ff3ef0, 0x0dd60c8c, 0x0d480c17, 0x0341c7d7, 0x1845e536, 0x1d7c8de7, 0x4b0043}}, + {{0x02c68552, 0x0596296b, 0x04962201, 0x06521e74, 0x02d870f2, 0x04231b11, 0x106b6c5e, 0x047461b5, 0x173ccf}}}, + /* 5*16^19*G: */ + {{{0x17f1aa96, 0x16ea5738, 0x1fd9207d, 0x0f4bee69, 0x063c513d, 0x06e5db9a, 0x1f08c9ca, 0x0f3255ad, 0x79ead2}}, + {{0x0b0a3fb7, 0x05d38e72, 0x0ce556a0, 0x09ae2223, 0x16542de8, 0x01c9ab12, 0x0afc69b9, 0x19ff755b, 0xa95de9}}}, + /* 7*16^19*G: */ + {{{0x193a9ae6, 0x002e5397, 0x1dfe2b72, 0x01540b38, 0x1743f8ee, 0x0f1b18d6, 0x073ad49e, 0x0a1e49b4, 0x318e00}}, + {{0x1c447d79, 0x025ab4a6, 0x14ea3b86, 0x06dfb75d, 0x04bd0945, 0x1e528f28, 0x0a67d345, 0x022339d2, 0x2792ee}}}, + /* 9*16^19*G: */ + {{{0x1d30585a, 0x1bca2da6, 0x14052544, 0x1f143546, 0x0a4b495f, 0x197a4860, 0x10eee200, 0x0e3a5e3c, 0x6bbc54}}, + {{0x148b05f3, 0x17cbf779, 0x1e7ab65a, 0x00850205, 0x026be09f, 0x1158af91, 0x07c12a7b, 0x136182a5, 0x467b18}}}, + /* 11*16^19*G: */ + {{{0x00136275, 0x1127c046, 0x01dd4d49, 0x0f8002e7, 0x00d78a64, 0x0d487bbb, 0x144626d4, 0x18c2183f, 0x940148}}, + {{0x1997aa47, 0x0b1d088a, 0x13869097, 0x1675cac0, 0x1d6695d4, 0x06255a82, 0x13ddea89, 0x0be26cf4, 0x6967f9}}}, + /* 13*16^19*G: */ + {{{0x1f0fe850, 0x1d09ee9c, 0x0469ef70, 0x1a4617ca, 0x1f98e864, 0x164946d2, 0x127246d5, 0x124c0736, 0xc41833}}, + {{0x1d38666d, 0x1ac7a235, 0x0682bc04, 0x14fd93c0, 0x1d558065, 0x0aea1357, 0x1debae02, 0x19391a81, 0x3537fe}}}, + /* 15*16^19*G: */ + {{{0x14937fbe, 0x1f2209bb, 0x146e9bb3, 0x009bbbe0, 0x0f546c0b, 0x0000d7cb, 0x15c38305, 0x1ec4f8cf, 0x2baba3}}, + {{0x173a1df4, 0x1c872aba, 0x0b204424, 0x1b896321, 0x0abc9ec2, 0x1866c927, 0x0e235e0e, 0x0cb64411, 0x77e5c7}}} + }, + { + /* 1*16^20*G: */ + {{{0x0351964c, 0x069e96f2, 0x1504b075, 0x12486ede, 0x15c08346, 0x1e50c2ba, 0x11feb96a, 0x0b37c518, 0x6e29f9}}, + {{0x163fd88f, 0x0c4125ea, 0x02fed8c4, 0x0818a4f4, 0x0246def6, 0x163660c2, 0x0bd9414b, 0x13ea01e6, 0x34565d}}}, + /* 3*16^20*G: */ + {{{0x0c0e49cc, 0x1ca8081c, 0x1150034f, 0x065b50b7, 0x140ed412, 0x046f65db, 0x1dbb760a, 0x152f12e1, 0xd691d4}}, + {{0x100f4152, 0x085da60e, 0x1410fcb7, 0x17c3a055, 0x00c52ac5, 0x1edabb1f, 0x0e5fdfee, 0x10e96f1e, 0x7a79e7}}}, + /* 5*16^20*G: */ + {{{0x0b9b930a, 0x0c3b5cf9, 0x0c3d63cf, 0x026a548a, 0x1bc49deb, 0x1befbd3d, 0x1f177b96, 0x08d45c1a, 0x2a5d68}}, + {{0x1b2caeca, 0x17f9a2f9, 0x09c5a161, 0x16e686bc, 0x0ab58ea5, 0x181c81e2, 0x1db79733, 0x012d0ec8, 0xdc3d7c}}}, + /* 7*16^20*G: */ + {{{0x016959ef, 0x13ee5e94, 0x076d66be, 0x13a0ace8, 0x15df8767, 0x18a09713, 0x1498bc10, 0x0d471376, 0x876449}}, + {{0x00cd5010, 0x10188e5f, 0x1e78fc59, 0x0d5a82e5, 0x1961f285, 0x0093cb76, 0x1ff6782d, 0x1ac3a005, 0x599b69}}}, + /* 9*16^20*G: */ + {{{0x1aef0f84, 0x1ca04e71, 0x071d6e58, 0x16a0d50e, 0x1b8cab0b, 0x1fd60bd6, 0x06c4cf78, 0x1790248e, 0x94d393}}, + {{0x178ba7c1, 0x0d730dc9, 0x0b3c4aa1, 0x1e804ca1, 0x19a07dd3, 0x1e1c3591, 0x0fc87872, 0x169d696c, 0x5a826a}}}, + /* 11*16^20*G: */ + {{{0x0ffed1b7, 0x0a2abc27, 0x12a8ed3b, 0x17a24cac, 0x0bd2ee2d, 0x04b8169a, 0x18b745d4, 0x141113c9, 0x4a72b5}}, + {{0x1601dc5f, 0x0f94fec4, 0x1366116d, 0x0c971d8e, 0x0ea9685e, 0x1fe023e4, 0x038b230c, 0x1d4943a4, 0x3531e6}}}, + /* 13*16^20*G: */ + {{{0x10467317, 0x1021f92e, 0x16461a80, 0x03b883b1, 0x07900914, 0x13797d6f, 0x18569e19, 0x1f8b46e3, 0xd7f01c}}, + {{0x0f7d014e, 0x05cabeae, 0x1fef6257, 0x002e86d2, 0x1ef5728a, 0x10a0360a, 0x109bb1cd, 0x1b30ee4d, 0x888dbb}}}, + /* 15*16^20*G: */ + {{{0x1dea02c1, 0x1ebac853, 0x1d021f0e, 0x17736f8e, 0x11206e4f, 0x1fcec8f1, 0x1c6efa02, 0x173eef86, 0x7e50a0}}, + {{0x0d45c201, 0x1e4a36ff, 0x0386ca0c, 0x07269e2b, 0x19517742, 0x178eedc5, 0x0a7185b1, 0x0789c1fc, 0xc3405d}}} + }, + { + /* 1*16^21*G: */ + {{{0x0cb71280, 0x053ea088, 0x1158c44a, 0x0384b350, 0x1458ec14, 0x17783cb7, 0x1b67003c, 0x13d657fd, 0xff046a}}, + {{0x1ec33919, 0x161938fa, 0x0a121e9f, 0x16dcdd7a, 0x0fcc9012, 0x16edeea6, 0x085c2807, 0x159812a7, 0x432f55}}}, + /* 3*16^21*G: */ + {{{0x13ca3ce3, 0x193c1551, 0x1a9df897, 0x1630cb56, 0x0231fef2, 0x1e0fa989, 0x134c6a56, 0x0ba29a7b, 0xf717c4}}, + {{0x10ed88e9, 0x04a666da, 0x05db7c1a, 0x07c57bb8, 0x1ee55af5, 0x0a768556, 0x04b72a6d, 0x0ed1932a, 0x422c40}}}, + /* 5*16^21*G: */ + {{{0x1b28cd88, 0x1b42186a, 0x0a6f66b0, 0x1c12ea05, 0x1b32c738, 0x1ab13fc8, 0x0c830bfa, 0x169e66e1, 0x60e269}}, + {{0x0be2066c, 0x14dd4521, 0x1211ac38, 0x0ac2718d, 0x0df76024, 0x08c44b94, 0x02a58248, 0x0b4caf3e, 0xfe0017}}}, + /* 7*16^21*G: */ + {{{0x061cc594, 0x09d8c994, 0x1414dac5, 0x17ba2e09, 0x06855964, 0x1d02fe5a, 0x05242a96, 0x143cce73, 0x799297}}, + {{0x0f8d309e, 0x14888490, 0x0b1ba427, 0x150d5c17, 0x1e50127d, 0x088c3c4f, 0x06c3841a, 0x119aa479, 0x132ff1}}}, + /* 9*16^21*G: */ + {{{0x172d0aba, 0x04623d12, 0x1f86c5b7, 0x03215b8e, 0x1241dd8d, 0x0775aa80, 0x01c1e7b8, 0x09dfc850, 0x2d9be9}}, + {{0x11fa0876, 0x07b98fd9, 0x0dc439be, 0x038cf311, 0x1d91bbad, 0x1c9a5b0c, 0x0288a57b, 0x16bd3d13, 0xa9708d}}}, + /* 11*16^21*G: */ + {{{0x155e8732, 0x0603da41, 0x1b478c78, 0x0c5dbca0, 0x0c243cfa, 0x03298a49, 0x0859832f, 0x13d91048, 0x212703}}, + {{0x00cacc07, 0x0c91afa3, 0x15ef5a35, 0x07e7e775, 0x10a5cd69, 0x1cedddbf, 0x16a80652, 0x0de475c6, 0x85219c}}}, + /* 13*16^21*G: */ + {{{0x12083347, 0x0489a067, 0x0694c898, 0x15c7ccee, 0x0b42b9e1, 0x07ad5d01, 0x03ae5eb4, 0x09657b1c, 0xfab5b5}}, + {{0x1d38fd2f, 0x0664e6c1, 0x09a6bea7, 0x1c6f4c2d, 0x03eafc05, 0x15791305, 0x0540f21e, 0x02c30da8, 0x5758d1}}}, + /* 15*16^21*G: */ + {{{0x1f572554, 0x1b3930a7, 0x1d36adcf, 0x085c8e31, 0x0a56ff38, 0x0b3b85bf, 0x1f295e42, 0x0ae4f2e3, 0x685d27}}, + {{0x00979e03, 0x18d7b685, 0x1d33292d, 0x15545581, 0x0377cc58, 0x0e4020f0, 0x17f01b8e, 0x18441568, 0xa82f89}}} + }, + { + /* 1*16^22*G: */ + {{{0x05852e50, 0x1e859266, 0x15c33171, 0x18fcc79e, 0x07eff8b2, 0x1511a4f7, 0x016307e6, 0x1bffd576, 0xe486c7}}, + {{0x0ecf107d, 0x15d1e56d, 0x04baf619, 0x08c7ac67, 0x1e05a694, 0x052e4fa8, 0x04ba7ba2, 0x1daac0d4, 0x51fd75}}}, + /* 3*16^22*G: */ + {{{0x1c881907, 0x1355322a, 0x002c76b1, 0x190ce6b6, 0x00415142, 0x02e63357, 0x0df25904, 0x1771c5ff, 0x4acf44}}, + {{0x163cdafc, 0x0935a2b7, 0x1612c94a, 0x0a565ef9, 0x05457993, 0x142d6885, 0x01ecd510, 0x1fc1ffab, 0xad99a8}}}, + /* 5*16^22*G: */ + {{{0x1439383f, 0x15d7034b, 0x0068aaa0, 0x1f4ac56c, 0x14eec34f, 0x11496411, 0x192775ea, 0x10683114, 0x4ddf22}}, + {{0x03d52337, 0x197d40c1, 0x07e09a58, 0x0ba08a26, 0x00c6b3dd, 0x174be9c6, 0x00f5d2ae, 0x0f61b231, 0xe71c62}}}, + /* 7*16^22*G: */ + {{{0x1822b4f4, 0x16e440aa, 0x1977e3c9, 0x0a75f9f0, 0x1f39ba53, 0x14f03cbd, 0x1e784cd3, 0x0f226f30, 0x58b065}}, + {{0x0c60fbca, 0x103d4108, 0x13949659, 0x0940edf8, 0x0836c1ca, 0x12af8131, 0x10dc09f2, 0x14828b45, 0x55a98a}}}, + /* 9*16^22*G: */ + {{{0x14fe39b0, 0x195e8861, 0x0d37b452, 0x02e79ae9, 0x01f358a3, 0x020e119c, 0x14a5d8e9, 0x0b14c966, 0xa345e0}}, + {{0x0baf79dc, 0x184a405d, 0x0da77d4d, 0x18d4eb56, 0x1d949ce8, 0x02ad947c, 0x1c3e47c2, 0x14dab954, 0x7c6dd5}}}, + /* 11*16^22*G: */ + {{{0x126695fb, 0x1df03dcc, 0x0eb7b84f, 0x01773fc8, 0x0ccb6afd, 0x05c3a36b, 0x0b20806b, 0x10603214, 0x3a10bf}}, + {{0x04eb7e47, 0x0ae39595, 0x1514a21d, 0x1fd4d887, 0x065c5f28, 0x1c243445, 0x1c55d880, 0x167d8344, 0xb5d585}}}, + /* 13*16^22*G: */ + {{{0x1d2c8614, 0x00d474b3, 0x16116589, 0x012c14c0, 0x12876a7b, 0x1ad61848, 0x10cf2973, 0x1f592020, 0x12fbdd}}, + {{0x18b0b174, 0x03bde0ea, 0x067d257c, 0x04b04738, 0x0cfb54d4, 0x08a14669, 0x01a0cff6, 0x1a5204f0, 0xa5ff20}}}, + /* 15*16^22*G: */ + {{{0x0d9aeeaf, 0x1fbb2181, 0x16bf2194, 0x00f29940, 0x19a3005d, 0x11d0fd7c, 0x01ddee2b, 0x0b34572f, 0x8bece8}}, + {{0x05f9eb5e, 0x0d06d1e4, 0x02603dd5, 0x04071edf, 0x1c368bb2, 0x0885877f, 0x07f3eee2, 0x1898a0ca, 0x35f933}}} + }, + { + /* 1*16^23*G: */ + {{{0x0b519178, 0x05b5c761, 0x0f47f161, 0x17333148, 0x0ee0ab63, 0x067c5af1, 0x10c33f82, 0x0976bca0, 0xf41d7f}}, + {{0x0a6a3551, 0x1d0b32ee, 0x06787e69, 0x135986d2, 0x02347ab4, 0x16f1fd54, 0x035bc411, 0x17d7b35f, 0xe6a669}}}, + /* 3*16^23*G: */ + {{{0x1e3501d9, 0x138989f0, 0x187f22c6, 0x1059c376, 0x13e611be, 0x0b48eb16, 0x19fccccb, 0x0fdac5a9, 0xd65f82}}, + {{0x0b55759e, 0x1ca23637, 0x1cc2f86a, 0x02755aba, 0x187fc61c, 0x1e528b35, 0x03f94ded, 0x1f9e1352, 0xf24b60}}}, + /* 5*16^23*G: */ + {{{0x0f2ed84e, 0x1b17f120, 0x18ba84b2, 0x129be6bf, 0x0c0ade79, 0x045abc16, 0x10e17b9b, 0x16dbd60b, 0x55a9b5}}, + {{0x146495a3, 0x1cee2a82, 0x08363c15, 0x1f69ae25, 0x1f43ae8f, 0x068c876b, 0x06aca45d, 0x12d593e3, 0xc4f221}}}, + /* 7*16^23*G: */ + {{{0x075495a2, 0x1e967c38, 0x0f002ca6, 0x1c3d6c91, 0x08bc49fd, 0x0b05312a, 0x07687dfb, 0x0db8fb6e, 0x79de57}}, + {{0x0aec6fb8, 0x01646355, 0x04e9d1a0, 0x09ea12c9, 0x079c6831, 0x16d723ee, 0x0809e179, 0x01cd630d, 0x3b2d3d}}}, + /* 9*16^23*G: */ + {{{0x02d1e7bb, 0x17495664, 0x0465933b, 0x07ff206a, 0x1e55524c, 0x19b4acab, 0x12db8992, 0x0642c480, 0x8fe04e}}, + {{0x1a62e84f, 0x13acd686, 0x011aa336, 0x02c8c8de, 0x1b34e5b9, 0x0752bd46, 0x163f975b, 0x0a045ec6, 0x3987db}}}, + /* 11*16^23*G: */ + {{{0x09f7c360, 0x00d25763, 0x00217084, 0x19aaefc9, 0x024e24ef, 0x0531b469, 0x03c542a5, 0x012afdbf, 0x3af499}}, + {{0x087d7a7b, 0x1cee1553, 0x18225734, 0x0a9bac4e, 0x13c33485, 0x03241739, 0x04754c7e, 0x061a763b, 0xd17060}}}, + /* 13*16^23*G: */ + {{{0x170373be, 0x0d291d73, 0x18f4ed4e, 0x1a0c4424, 0x07c4e213, 0x0179ff11, 0x01db0696, 0x0b8c4790, 0x31a7e0}}, + {{0x090950a8, 0x16bc3c87, 0x08950c23, 0x105732a3, 0x1db1e8ce, 0x05571a11, 0x199c086c, 0x1e7f1a3b, 0xc23d2e}}}, + /* 15*16^23*G: */ + {{{0x0b923b45, 0x0130876e, 0x0f892f6d, 0x153f7fe2, 0x18458a33, 0x09a316ec, 0x08fb4554, 0x09026ada, 0x740a66}}, + {{0x11ecaa5f, 0x0609f1c2, 0x121202ea, 0x0f32720f, 0x006cfa89, 0x03c453ab, 0x08fcbd39, 0x1184b9aa, 0x7f6d04}}} + }, + { + /* 1*16^24*G: */ + {{{0x1512218e, 0x025549cb, 0x1280506a, 0x0a4360e9, 0x0e902e9a, 0x059d0c51, 0x1e995e20, 0x0cc254ce, 0x4a5b50}}, + {{0x0c4f3840, 0x1f56d3d2, 0x189b6742, 0x1b62a833, 0x07d40626, 0x027df0b1, 0x07c71098, 0x039d5811, 0xeb1346}}}, + /* 3*16^24*G: */ + {{{0x118473fd, 0x177a0712, 0x05cce454, 0x1204d78d, 0x0a9e3bbb, 0x155ccb94, 0x0e8214a4, 0x06466631, 0x106406}}, + {{0x185cf805, 0x1d30b173, 0x08740e6b, 0x1c321a18, 0x1883ccfb, 0x014f32c2, 0x0b63239e, 0x10477174, 0x9c6832}}}, + /* 5*16^24*G: */ + {{{0x053f30f3, 0x0b3f7643, 0x088e374d, 0x047da5db, 0x03f212b4, 0x0086aa04, 0x1efd7b69, 0x11fe0be4, 0x38c8ad}}, + {{0x0be9217b, 0x03073a4f, 0x1677cde8, 0x07f53052, 0x1641f658, 0x0b2a6ede, 0x045bce42, 0x0f0edb96, 0x83c261}}}, + /* 7*16^24*G: */ + {{{0x16ef1584, 0x01f3488d, 0x04832fd5, 0x1345471f, 0x1184277a, 0x1df1fe9a, 0x0c256163, 0x179d0c17, 0x6fcb8c}}, + {{0x1d3110f2, 0x1e4fbe10, 0x0290c970, 0x05edad25, 0x0459d599, 0x0411d618, 0x15db5530, 0x07b16e43, 0xa8f1a8}}}, + /* 9*16^24*G: */ + {{{0x062255ef, 0x07a89a02, 0x1901e33a, 0x0e3a68ce, 0x1b0ee50d, 0x0e486f22, 0x0af5cd45, 0x1727eedc, 0x7811d0}}, + {{0x1be3252e, 0x0c5f7cd8, 0x150dda29, 0x1da0a275, 0x05132f51, 0x174a00dc, 0x11e95300, 0x06e176b5, 0x99bb76}}}, + /* 11*16^24*G: */ + {{{0x153d2ea7, 0x1f088789, 0x1ea19424, 0x1a104747, 0x16f2a9ee, 0x194eafc0, 0x033831ed, 0x1289516a, 0xc3583d}}, + {{0x0b667bbd, 0x18c1f4f0, 0x17c710ca, 0x124b4ca2, 0x1cccdd6c, 0x113f7c4f, 0x043d6591, 0x1812723d, 0x53967a}}}, + /* 13*16^24*G: */ + {{{0x102e3099, 0x16ef8702, 0x01005963, 0x04274cc5, 0x0d00d53f, 0x180c5dbd, 0x1407ce83, 0x05079bdc, 0xd591cf}}, + {{0x055ac5af, 0x1de13b2a, 0x045bf58c, 0x05dff116, 0x1c5825cf, 0x19869a85, 0x1b913944, 0x19f88f1c, 0x87ebe5}}}, + /* 15*16^24*G: */ + {{{0x12a940a5, 0x1932b231, 0x0539ac4a, 0x06bd6b48, 0x1e8714a4, 0x00f0d27c, 0x17130049, 0x0c25e646, 0xa0aa0d}}, + {{0x0b950422, 0x1fc41534, 0x146fe4e4, 0x1239bb4f, 0x01f3e6c6, 0x067cd00c, 0x10a066cd, 0x174edeec, 0x22340e}}} + }, + { + /* 1*16^25*G: */ + {{{0x008e2df0, 0x1146c1b8, 0x0a397dd9, 0x0764ae86, 0x00f5032c, 0x14efc5df, 0x065404b0, 0x017bc557, 0x2eb391}}, + {{0x1274eaae, 0x08866276, 0x1d97d242, 0x01a241d9, 0x0999f954, 0x1e9a46d2, 0x0ce9df4d, 0x0466e8e9, 0x3f29c0}}}, + /* 3*16^25*G: */ + {{{0x082867f0, 0x1815c25b, 0x06428e6f, 0x084dc436, 0x100f0a21, 0x08b53c04, 0x1388aaaf, 0x111cc98f, 0xf6e9db}}, + {{0x0f1861ea, 0x1ad9d788, 0x1c2d88d1, 0x08374bf2, 0x0d5b1270, 0x1dbb7460, 0x0dd20764, 0x016f5a55, 0x53ca79}}}, + /* 5*16^25*G: */ + {{{0x120d6102, 0x16e821c7, 0x114e5026, 0x1aa6f146, 0x19a5ef06, 0x0adcdb0c, 0x1275e170, 0x070ec1c8, 0xfd1ddb}}, + {{0x0e003b7c, 0x1053248d, 0x144e60f6, 0x1c322422, 0x1b700163, 0x0f8fbc41, 0x0e2bb6a8, 0x0e720a0c, 0xcb54b8}}}, + /* 7*16^25*G: */ + {{{0x02c77cb7, 0x1fb9a0ee, 0x17056dbc, 0x1b281205, 0x0698fef6, 0x139f32f7, 0x00767f92, 0x1844b332, 0x61a273}}, + {{0x1ddb25ed, 0x0a308fc0, 0x0b87dd21, 0x0b5b34e1, 0x10cc9c5c, 0x10cfaf75, 0x0f4fd3a8, 0x0669a75a, 0x5bbacd}}}, + /* 9*16^25*G: */ + {{{0x177d8976, 0x0cc6ab71, 0x03ca4b6e, 0x0e7471c6, 0x104b55e1, 0x164c114e, 0x06d932c0, 0x0cbdeec0, 0xcd2e8e}}, + {{0x0867bc22, 0x0b4b7a0e, 0x10a30144, 0x1dbc1e6a, 0x0ff68f60, 0x074796a3, 0x0c7ff0c7, 0x06c46854, 0xf58ead}}}, + /* 11*16^25*G: */ + {{{0x0c67c998, 0x04d98361, 0x13c6e198, 0x160fd547, 0x04c259a9, 0x0f545218, 0x1bed0089, 0x13870447, 0x9bd61f}}, + {{0x08199514, 0x1a057ce1, 0x1092c630, 0x1b383d20, 0x050fa927, 0x104b4b4a, 0x1d71723c, 0x01322d8d, 0x77b204}}}, + /* 13*16^25*G: */ + {{{0x0aafa568, 0x122f0bdf, 0x07889d9a, 0x1af52ee0, 0x1a016b4c, 0x13d2088b, 0x1dd44ab8, 0x09ef2e0e, 0x7afaeb}}, + {{0x01c1f2df, 0x16a9d17c, 0x14e408cf, 0x1cd28653, 0x1365a972, 0x0a09a820, 0x09f62574, 0x03267f7a, 0xc6efe6}}}, + /* 15*16^25*G: */ + {{{0x0e59ddeb, 0x1f381f28, 0x07a62a2d, 0x1cc5395a, 0x10c3b483, 0x0a60a4b5, 0x0be41876, 0x044fc482, 0xd9a002}}, + {{0x0f0af5a4, 0x19c9ffc0, 0x17c63397, 0x05517956, 0x10581856, 0x07c521b3, 0x08b10f18, 0x1f276f40, 0x975eb2}}} + }, + { + /* 1*16^26*G: */ + {{{0x1ecca7e0, 0x19cd2f51, 0x10cccfb1, 0x05931ece, 0x19428a7d, 0x119a9126, 0x08303fbd, 0x078b8f25, 0x7ef2ee}}, + {{0x152ac094, 0x015916ea, 0x0f4f480c, 0x0428a1bf, 0x009db81b, 0x1fa8eaf3, 0x004693d9, 0x04e61598, 0xafb686}}}, + /* 3*16^26*G: */ + {{{0x033f0ffa, 0x179ac1ed, 0x001e905c, 0x09958c23, 0x1c641cae, 0x12bb64ec, 0x0b35d892, 0x04e2d10f, 0x370457}}, + {{0x00fa35ac, 0x1f69fe33, 0x02fcbf17, 0x147d6f55, 0x1ff4c57b, 0x1a5dc44a, 0x1d3e106e, 0x18c34e3c, 0x5c6408}}}, + /* 5*16^26*G: */ + {{{0x12603513, 0x037cc329, 0x1280254f, 0x0c9b8d02, 0x1b697f6c, 0x15216045, 0x0dc8b47a, 0x1ca8531b, 0xc2a979}}, + {{0x1c3505b9, 0x0baad961, 0x05451265, 0x0ccdbe90, 0x1ce7e8cc, 0x0040c9ad, 0x1721509e, 0x0943cdd6, 0xef9dd7}}}, + /* 7*16^26*G: */ + {{{0x19fdee3e, 0x1237fd8e, 0x0c0b8079, 0x1f2ee2d8, 0x1afb9681, 0x18ae6d8b, 0x07845cae, 0x0d563f8b, 0xc9f418}}, + {{0x0ced22e7, 0x0f33662e, 0x1637d3d4, 0x02a638f6, 0x1214344e, 0x18a15c41, 0x0d0437dc, 0x0fe79674, 0x9420a}}}, + /* 9*16^26*G: */ + {{{0x0814f217, 0x0659f33a, 0x15b3febc, 0x147ef70e, 0x1b6a4a9d, 0x00127c1d, 0x095ac228, 0x0b700900, 0x27dcf5}}, + {{0x0cf2ed4c, 0x064a5ac4, 0x07f0efc5, 0x1d1f5632, 0x02a380a4, 0x1d584a50, 0x17b82f22, 0x09ea91ab, 0xbe18f5}}}, + /* 11*16^26*G: */ + {{{0x1b9e310c, 0x0d1527ea, 0x133f529d, 0x19cb7370, 0x19187313, 0x125619ca, 0x092e7234, 0x00764add, 0x3b8b02}}, + {{0x0d19fc9b, 0x0a0b302d, 0x0cc160e6, 0x1f009705, 0x117a6a8f, 0x171bbe20, 0x056f585d, 0x09d6066c, 0x91117c}}}, + /* 13*16^26*G: */ + {{{0x07615b36, 0x18f0f15d, 0x1cf057e3, 0x1fe3d18f, 0x0c3372a4, 0x12037c8a, 0x00e84170, 0x118b7982, 0xa5e739}}, + {{0x09136d80, 0x0cc171ca, 0x0d84f013, 0x1f8326e1, 0x1f8c547f, 0x06f7950a, 0x1339190e, 0x11b92e9c, 0x1f3ff5}}}, + /* 15*16^26*G: */ + {{{0x0d3e10db, 0x1bd82197, 0x0e1c566e, 0x065c3591, 0x161fa477, 0x021ffa75, 0x0caf7b81, 0x06ab592a, 0xaad218}}, + {{0x0fa05fb3, 0x1a9ad5eb, 0x072e89d8, 0x064ee566, 0x18b4a720, 0x0f147eaf, 0x18ece9f7, 0x1dd75f05, 0x793b1e}}} + }, + { + /* 1*16^27*G: */ + {{{0x0c49e853, 0x180f8487, 0x1d781299, 0x16a6a532, 0x1a77aa9c, 0x12aa75af, 0x0bad5e00, 0x0c842c81, 0xe5141}}, + {{0x16405cb2, 0x1b71e89b, 0x127b8d8c, 0x10728321, 0x1030df56, 0x1a529d48, 0x11a49e3a, 0x1d4cb20a, 0xcf331c}}}, + /* 3*16^27*G: */ + {{{0x1dc6afad, 0x0511f8e0, 0x1e03d1b1, 0x07b2452b, 0x0c6771bd, 0x092fb260, 0x1c977a3b, 0x0cecb959, 0x977ba0}}, + {{0x1d7faa09, 0x0b166e28, 0x16868553, 0x0b42483b, 0x1d9992f7, 0x1d506840, 0x1e8a20cf, 0x0875db71, 0x1bc3bc}}}, + /* 5*16^27*G: */ + {{{0x1d81983b, 0x0fba2ebe, 0x03aade19, 0x041fa05f, 0x1facc102, 0x1b03b8ff, 0x0af930f2, 0x0c254247, 0x3bc328}}, + {{0x11a637b6, 0x0ccf9428, 0x0ee1236f, 0x0910b51f, 0x00a7f433, 0x182e5578, 0x0880942c, 0x0dbda191, 0xa6b582}}}, + /* 7*16^27*G: */ + {{{0x17c8537b, 0x0fbb60ff, 0x07e0185d, 0x19596e88, 0x16c21ca8, 0x17f0e57c, 0x12b146eb, 0x1d1d3d80, 0x67cba4}}, + {{0x185726da, 0x075a82b9, 0x100fdb87, 0x1e087da0, 0x1cc047bd, 0x030f97d8, 0x06128787, 0x1dbd4391, 0x5b65ed}}}, + /* 9*16^27*G: */ + {{{0x0b6a1226, 0x14114672, 0x15194e4d, 0x13d39156, 0x0f3922af, 0x1eb43234, 0x064c681c, 0x14daae93, 0xc5958e}}, + {{0x066961d4, 0x00803446, 0x08422e47, 0x0edb7d74, 0x1bd82968, 0x0cb8cea2, 0x175fb2f8, 0x105ee1ef, 0xa031a8}}}, + /* 11*16^27*G: */ + {{{0x13713a52, 0x06f1fc2d, 0x0f1505f9, 0x0eb718d0, 0x05fa025c, 0x0061e3a5, 0x129f07f3, 0x1f05c84b, 0x83f07b}}, + {{0x1fb0ffb6, 0x088939fc, 0x1ed174a8, 0x0b6ec1c3, 0x1d3cfcab, 0x0a648963, 0x0ee4125e, 0x02144b71, 0xf2ec4f}}}, + /* 13*16^27*G: */ + {{{0x01ba21b0, 0x16cd0489, 0x109954e6, 0x0bb1a401, 0x00076d85, 0x00343757, 0x0b539bda, 0x09f36b48, 0x938dba}}, + {{0x0d5b4325, 0x1a6ccaf2, 0x0ada5f0d, 0x0377dc7e, 0x0b90bcbf, 0x16dee89b, 0x1959ba6a, 0x15135eea, 0x48afc0}}}, + /* 15*16^27*G: */ + {{{0x1f2b24f1, 0x09f24d31, 0x19daf3ed, 0x1d8c9ebf, 0x09779cfa, 0x0d5679da, 0x079cea07, 0x16ed9406, 0x7efa89}}, + {{0x0f8e4b83, 0x12ee2ea5, 0x15291575, 0x15be758a, 0x0ab211db, 0x09117e0b, 0x1bfde33b, 0x1d8c3e95, 0xa99ff3}}} + }, + { + /* 1*16^28*G: */ + {{{0x02f2b734, 0x034cdfcf, 0x007499fc, 0x0776b6aa, 0x0445779c, 0x13c3788b, 0x066818d2, 0x0533dd99, 0x224a02}}, + {{0x11ec7fdf, 0x007ac2a4, 0x11ebf421, 0x0e096ce7, 0x17fff07b, 0x0456c38e, 0x0ad05268, 0x1a536da4, 0xfa41a8}}}, + /* 3*16^28*G: */ + {{{0x1d254033, 0x0f497a3a, 0x091c1eb2, 0x0d050185, 0x0f240783, 0x0fd3445b, 0x1167f36b, 0x1dc2a79d, 0xe63502}}, + {{0x08f05e3f, 0x1455444b, 0x1f3cd81f, 0x02b41897, 0x1ce482c9, 0x14078384, 0x1846b5d3, 0x0f63ec43, 0xa7c583}}}, + /* 5*16^28*G: */ + {{{0x01e08f33, 0x1cb04d7f, 0x060b328a, 0x1ac63b97, 0x000e2b1f, 0x02e299f8, 0x1a1c4aad, 0x184e5d72, 0x5648de}}, + {{0x0269f1c9, 0x1776cd62, 0x045e5449, 0x0577e5c0, 0x1a2693e7, 0x1ae326bb, 0x010fd1e3, 0x0e0e11ee, 0x95fddc}}}, + /* 7*16^28*G: */ + {{{0x1ab98c43, 0x1dc1005f, 0x0fa2a938, 0x14aeeafc, 0x08f03c5b, 0x17c765f6, 0x149a5454, 0x13e6701d, 0x1941c1}}, + {{0x02abe217, 0x1d1086d1, 0x17d58e63, 0x0c5a7b71, 0x18c53d8f, 0x0d95247d, 0x0adb8adb, 0x068b2bb6, 0x8e66b8}}}, + /* 9*16^28*G: */ + {{{0x02d8c2f4, 0x13386882, 0x07006279, 0x064373ef, 0x18a0bd73, 0x1def1b26, 0x0f59fe9c, 0x063a2169, 0xfc5864}}, + {{0x1cfd274a, 0x08c6447f, 0x10ce2d00, 0x05960bd7, 0x1b4ab29e, 0x013cf35e, 0x055928cf, 0x0a59d15b, 0x99fade}}}, + /* 11*16^28*G: */ + {{{0x11ff0f18, 0x1583bf8e, 0x12690b44, 0x0179ce0a, 0x0d6c207e, 0x1d67cd79, 0x077aa6fe, 0x14d6189e, 0xee993b}}, + {{0x050782ad, 0x009c3b65, 0x03cb2c60, 0x0b901bee, 0x1bcfeb43, 0x0eac6742, 0x0cb07db1, 0x1b28af1b, 0xd87fd4}}}, + /* 13*16^28*G: */ + {{{0x0fdde3fc, 0x03d3fbaa, 0x18a33279, 0x11113c0a, 0x131fbe5a, 0x0e20d297, 0x090a3683, 0x0f55d3fc, 0xba4951}}, + {{0x0fcb7d01, 0x1f7e9197, 0x0a4b2aa8, 0x1c3903c6, 0x1359adfd, 0x121bb009, 0x16542d3b, 0x1c39528a, 0xa88fb6}}}, + /* 15*16^28*G: */ + {{{0x1d14da68, 0x0449517b, 0x14805cde, 0x0983f09b, 0x012b54a6, 0x07c139bb, 0x06e9378b, 0x02e8455e, 0xd3398f}}, + {{0x15a90e3d, 0x183b18da, 0x10514e8f, 0x0f4f245a, 0x0b020afc, 0x0968f451, 0x1583adc9, 0x1a9a3fa7, 0xa34ad4}}} + }, + { + /* 1*16^29*G: */ + {{{0x1789b84d, 0x13032d6b, 0x010738ba, 0x1abdc9a2, 0x093fe167, 0x0888dab2, 0x0d3336d7, 0x028ae6e9, 0x4a89a6}}, + {{0x018e3ea8, 0x1cdbebf8, 0x1c46667b, 0x1c500c68, 0x077e6a72, 0x154bcff3, 0x1570230b, 0x10fda901, 0x45b04e}}}, + /* 3*16^29*G: */ + {{{0x02e14c34, 0x13b591ba, 0x0c9d64db, 0x18bd7c0e, 0x177c9834, 0x19873c09, 0x1e4a8aa6, 0x09150e1d, 0xcc9f96}}, + {{0x14be556b, 0x0490abd4, 0x031f8162, 0x122fe305, 0x089db299, 0x1319edf3, 0x05bd0c45, 0x0596bf19, 0x829bff}}}, + /* 5*16^29*G: */ + {{{0x15de4837, 0x1ef3642c, 0x1979d3a1, 0x050d905b, 0x18a758d4, 0x060e856c, 0x106bd3d6, 0x143f55a0, 0x5c618d}}, + {{0x034a7dee, 0x05c2c1db, 0x089e32b0, 0x06cc92c8, 0x19671ecd, 0x071c24fe, 0x067622e0, 0x02f79e74, 0x4c7191}}}, + /* 7*16^29*G: */ + {{{0x16635396, 0x0c49a132, 0x029b0309, 0x15c2b46d, 0x1c11ef2c, 0x116ef338, 0x1a8b9617, 0x0d522b40, 0xab8a88}}, + {{0x1fc92c23, 0x11c7d351, 0x14b5ab5f, 0x0c0b07ef, 0x0d5abb93, 0x1fc1245e, 0x0c6559a3, 0x0f8fb508, 0xe773cf}}}, + /* 9*16^29*G: */ + {{{0x01df49c3, 0x0121f2c4, 0x01a68d6b, 0x11210cda, 0x1604aa9e, 0x125c5b4a, 0x0e337b19, 0x0a284830, 0x62b7c8}}, + {{0x16341c21, 0x1f1b407f, 0x1f2d5426, 0x0a76c270, 0x122db8fb, 0x0b5746a4, 0x0cd56522, 0x042a55c0, 0xa71ed6}}}, + /* 11*16^29*G: */ + {{{0x044f47d0, 0x178a3717, 0x07c00bce, 0x13ebbd29, 0x1c9791b5, 0x0f8434ef, 0x0717e5d4, 0x17f48b91, 0x4897bd}}, + {{0x0e1320bf, 0x0f280d56, 0x1c046560, 0x1b400351, 0x1093f526, 0x1654a0bd, 0x0e30d3a3, 0x01ef5f82, 0xe77dc2}}}, + /* 13*16^29*G: */ + {{{0x11581bd3, 0x10de74db, 0x0641e1b9, 0x14631bec, 0x02f2ecbf, 0x103ded39, 0x15db1147, 0x16aac90c, 0xeb1cfc}}, + {{0x1cd98a09, 0x1e746cc2, 0x0f469916, 0x19e6338b, 0x16e3cb39, 0x002f5f26, 0x14814110, 0x0eae3f29, 0xfd47ce}}}, + /* 15*16^29*G: */ + {{{0x073a3111, 0x00915edb, 0x132063e4, 0x105c82f6, 0x176a018b, 0x05617392, 0x18270355, 0x177c91c3, 0xa1fd8b}}, + {{0x18c704ef, 0x043f3285, 0x15445a6e, 0x18928b7f, 0x116c0c7e, 0x1f03de04, 0x015a6f5d, 0x11469461, 0xad215d}}} + }, + { + /* 1*16^30*G: */ + {{{0x12bd05a0, 0x01c64253, 0x07f2034d, 0x0466fa16, 0x11f90ba8, 0x1ccaf9b6, 0x0173b70b, 0x06c74631, 0xe5e892}}, + {{0x01a69f5d, 0x09b6f15f, 0x14266bb2, 0x0732b739, 0x15c3eca7, 0x1580f3cd, 0x1f484c07, 0x1c9b4370, 0x77439d}}}, + /* 3*16^30*G: */ + {{{0x01467d6b, 0x184a9408, 0x0892d453, 0x1ae252a5, 0x0f1d8357, 0x0308b216, 0x13d74406, 0x1bf286b9, 0x5d2393}}, + {{0x11bc5458, 0x1e339e35, 0x011cea01, 0x0e0f4ea2, 0x0f46d72a, 0x0c2d96ad, 0x1df5eb2f, 0x1e4c7fa1, 0xe66e63}}}, + /* 5*16^30*G: */ + {{{0x1d159f7a, 0x058f49e4, 0x10b9643c, 0x127539e4, 0x1873fecf, 0x1d95e97f, 0x04fceb73, 0x14a75571, 0x453657}}, + {{0x0a02fb78, 0x0e115b84, 0x07769766, 0x0937a9d0, 0x1c7286f9, 0x18489d00, 0x171768bb, 0x1ff10047, 0xbfb5ae}}}, + /* 7*16^30*G: */ + {{{0x146cb42a, 0x0f6f6f9e, 0x08e424cc, 0x0a50a74e, 0x173e7bc0, 0x16f5509e, 0x11193452, 0x1960f609, 0x435b54}}, + {{0x1af72dd0, 0x1f126f6e, 0x0e5269ad, 0x1898f286, 0x0585d5ed, 0x12a660f0, 0x086927d2, 0x063c8e31, 0xd726c0}}}, + /* 9*16^30*G: */ + {{{0x1d2fc263, 0x0beca0d8, 0x19755eea, 0x1f027cb6, 0x1da0e89c, 0x023e2709, 0x15f867ef, 0x033c29db, 0x1805b8}}, + {{0x10870ec7, 0x132385a7, 0x147b2bc9, 0x1835f1ca, 0x131489c8, 0x0d5435e5, 0x05c56163, 0x05012870, 0x101f64}}}, + /* 11*16^30*G: */ + {{{0x0076d1df, 0x1158db86, 0x1fe86ce6, 0x1c410284, 0x15f45f41, 0x1d45153b, 0x053d0185, 0x086cda63, 0xc73aaf}}, + {{0x05ad6605, 0x13cea8b7, 0x024dc834, 0x16af4a3b, 0x0dcfef75, 0x00df1dde, 0x05bbe738, 0x0d3d99f2, 0x9201ec}}}, + /* 13*16^30*G: */ + {{{0x05a745c7, 0x1b41fbcc, 0x1fab01f4, 0x144e7182, 0x152c1bc8, 0x0db57b0e, 0x1b49dc62, 0x11efab86, 0xe3ddee}}, + {{0x178efcc2, 0x1cf84a03, 0x1409ec68, 0x1185e2f7, 0x1d8f47c2, 0x0dc3553d, 0x0e1c6f94, 0x1a723265, 0x487199}}}, + /* 15*16^30*G: */ + {{{0x0d4170b3, 0x06399f42, 0x0e8d61fd, 0x0882adf8, 0x0a1d5401, 0x1508c360, 0x0796bda2, 0x1b8406be, 0x45c78d}}, + {{0x111faa1d, 0x177d1f6a, 0x08331cc5, 0x008cc0c8, 0x13a512cb, 0x14e8a27d, 0x1032c386, 0x08c471ab, 0x11af99}}} + }, + { + /* 1*16^31*G: */ + {{{0x1ab5c2cf, 0x0abccc97, 0x0c213788, 0x05e1843f, 0x090a4531, 0x03a0827d, 0x1d41c79d, 0x0863c443, 0xe4107e}}, + {{0x0b955c2b, 0x014201f9, 0x1ec90434, 0x1125b9fe, 0x02b323c7, 0x03343a0e, 0x1268e523, 0x1cd9ee03, 0x1e5f11}}}, + /* 3*16^31*G: */ + {{{0x09b67b7e, 0x1dadb1e0, 0x070f5216, 0x16f65722, 0x01142766, 0x0b80ef5e, 0x1df6ab3e, 0x0cbdc2f3, 0x85df9a}}, + {{0x030a36e3, 0x059c34f4, 0x0f1ba962, 0x15293a3f, 0x0297386f, 0x1eaf7f87, 0x0c22edad, 0x154735ec, 0xa6b4b7}}}, + /* 5*16^31*G: */ + {{{0x0b3510d3, 0x13e7ef30, 0x0875b904, 0x062cce09, 0x1292885d, 0x10c32e16, 0x1422a362, 0x1fcff3f9, 0x23c493}}, + {{0x1ae89a47, 0x1a317621, 0x0e76f5c4, 0x13c6bc4d, 0x1e6d8f79, 0x0ecf277f, 0x108c309d, 0x153c8682, 0xadf456}}}, + /* 7*16^31*G: */ + {{{0x0b49a71f, 0x06348c04, 0x089f0e6b, 0x0dd6d8c8, 0x035ddac3, 0x09f5d579, 0x03b77966, 0x016629ba, 0x94406d}}, + {{0x10b249a9, 0x18e0ca26, 0x18f3e511, 0x04e4c394, 0x1e897d0e, 0x1e484837, 0x05e73481, 0x0fd0c9be, 0x61aae7}}}, + /* 9*16^31*G: */ + {{{0x00e50c32, 0x0e6f19e3, 0x11a1cff9, 0x0212e5d1, 0x08d3877f, 0x1b6fadf7, 0x1050cdc4, 0x087bd10d, 0x3c2ebf}}, + {{0x0a93c124, 0x117c48be, 0x1a326627, 0x1a118735, 0x028e7be2, 0x1c9e9017, 0x06719496, 0x14d8bd07, 0xa703d9}}}, + /* 11*16^31*G: */ + {{{0x03a2355a, 0x041ecef6, 0x1812641c, 0x0a37e6b8, 0x15f12996, 0x12dc4f62, 0x1ae7ce47, 0x1ae4f281, 0x987536}}, + {{0x1bd05276, 0x063ae260, 0x0eb7337c, 0x1ed8f8d0, 0x17b48b85, 0x18e0a4f2, 0x05c90f82, 0x1ace9e22, 0x12f19a}}}, + /* 13*16^31*G: */ + {{{0x12f3ec22, 0x0782a3e2, 0x0a3e29ac, 0x10db9ee3, 0x0ddddd1f, 0x1c58bf79, 0x11a3c674, 0x08134988, 0xd515ac}}, + {{0x05a2863b, 0x10a31759, 0x06e01bb2, 0x0afbe006, 0x0bd464cd, 0x12420029, 0x0cb87a97, 0x02844893, 0x51c048}}}, + /* 15*16^31*G: */ + {{{0x1971c857, 0x0cd0f4d2, 0x19824fe9, 0x133cdb57, 0x1cf3ddec, 0x1e61c5b0, 0x02eb8914, 0x0930bcb6, 0x70b9d}}, + {{0x0ac8dfc1, 0x1ce5b1ee, 0x15186abf, 0x107b43d8, 0x13cfb33a, 0x0857231b, 0x09421ae0, 0x037fe96c, 0xed8046}}} + }, + { + /* 1*16^32*G: */ + {{{0x1789bd85, 0x1e427e4e, 0x05fab0d5, 0x0bfefb85, 0x0766efc3, 0x17eac463, 0x199fee60, 0x137ddb6b, 0x447d73}}, + {{0x12e25b32, 0x03f19e4b, 0x1eb94003, 0x09372b4f, 0x0aff73d3, 0x0eca9d25, 0x07bb84ba, 0x15706826, 0x2d4825}}}, + /* 3*16^32*G: */ + {{{0x1ea6db68, 0x1f0ccc76, 0x098cb09c, 0x15b0ac10, 0x0f4f6ddf, 0x06fcb2ef, 0x05fd62c5, 0x07c07940, 0xf8b653}}, + {{0x0e760da9, 0x0de92d85, 0x17283b5a, 0x1ae1bb38, 0x03ec66bb, 0x16ed2855, 0x1218bc11, 0x1ebd888a, 0xc30f4e}}}, + /* 5*16^32*G: */ + {{{0x1924e753, 0x16aea75f, 0x1e1c9f19, 0x02e60e59, 0x1fb755f0, 0x18c394f6, 0x11f1523b, 0x1a6ab050, 0xf35289}}, + {{0x0b13a20c, 0x122dae17, 0x0b43c12a, 0x05f8ae52, 0x01bd8c56, 0x0450da87, 0x0fee4f7c, 0x03d8bd82, 0x75c178}}}, + /* 7*16^32*G: */ + {{{0x1018017e, 0x1cae5b39, 0x17c8f5c8, 0x083fffad, 0x14e01af1, 0x0c2fdb39, 0x1c5920d0, 0x0e9b4882, 0xcfd06b}}, + {{0x0e0dff50, 0x0573df26, 0x1de5dde8, 0x0060f0d6, 0x07950003, 0x19cac3ed, 0x044e040e, 0x1536e575, 0xb647b7}}}, + /* 9*16^32*G: */ + {{{0x192db860, 0x0640c82b, 0x06891ec1, 0x11251065, 0x1dbe9810, 0x0b68478a, 0x1344544b, 0x09895abb, 0x755e7}}, + {{0x00d09849, 0x0b3006dd, 0x109dde9f, 0x06e8c99f, 0x15bc2b29, 0x196c11c0, 0x19374926, 0x14ea75b3, 0xfe16af}}}, + /* 11*16^32*G: */ + {{{0x0a26ba6b, 0x0f7b4aaf, 0x1684b0ea, 0x1945492d, 0x0bbdedd2, 0x0606bf58, 0x05a5a284, 0x09986e59, 0x88fba8}}, + {{0x044972f8, 0x020a3c0f, 0x033f73a0, 0x153c51c0, 0x0788d484, 0x0d22da8a, 0x183f499b, 0x0c93a737, 0x512aef}}}, + /* 13*16^32*G: */ + {{{0x14720cab, 0x0e245214, 0x0e6c5f8e, 0x1d4ba82e, 0x06f2c1a1, 0x1b73aaae, 0x0fae3943, 0x197d80ab, 0xe1a0ac}}, + {{0x14b185c9, 0x16a29fab, 0x000953a9, 0x1454e3a1, 0x0bcbf084, 0x1c183bf7, 0x015060db, 0x1f6fd319, 0xe07968}}}, + /* 15*16^32*G: */ + {{{0x0c26c42a, 0x14147eff, 0x0c46ed8a, 0x17f6fc8d, 0x1e6a0249, 0x1ac498f3, 0x11f9436c, 0x10dcd4e7, 0xae93b}}, + {{0x19f808ca, 0x08319a1d, 0x0ab6d924, 0x1473ddeb, 0x00b37278, 0x11f6e0f1, 0x184ea50b, 0x1ba28f0f, 0x7028f6}}} + }, + { + /* 1*16^33*G: */ + {{{0x137de736, 0x15ec5d60, 0x02338907, 0x11aac30d, 0x0c18ea2f, 0x0a15c66f, 0x1cfa24dd, 0x02929399, 0x9022e3}}, + {{0x04c42ecc, 0x077ae042, 0x1a9d95fd, 0x126cd889, 0x0ce087f4, 0x1d822913, 0x0e519b42, 0x09e52094, 0x2fae5e}}}, + /* 3*16^33*G: */ + {{{0x109b01f5, 0x0b8365c6, 0x1cf01464, 0x1e6a3064, 0x1857af73, 0x169f8d7b, 0x0517ec3c, 0x1c60edfd, 0x6e5872}}, + {{0x0bcd5fde, 0x183ac6cd, 0x06a169f0, 0x02f3a9c1, 0x0f0ebc13, 0x0ea579df, 0x05c60330, 0x05d91aee, 0x4213c0}}}, + /* 5*16^33*G: */ + {{{0x0fe45b1f, 0x09cdba3f, 0x185a30ad, 0x02abf65c, 0x1df827bd, 0x0f1a260b, 0x1d412bf4, 0x1634bb47, 0x292220}}, + {{0x134bb026, 0x1f205c5a, 0x17504117, 0x06886099, 0x18d7ff7c, 0x1caffd74, 0x16bb8df1, 0x0a657e2e, 0xe316a0}}}, + /* 7*16^33*G: */ + {{{0x03abd540, 0x14283315, 0x059b790f, 0x0080ca96, 0x13a340e8, 0x07c39084, 0x1c2b0c89, 0x0900f489, 0x3644e1}}, + {{0x155da631, 0x0e37f0e6, 0x1be85378, 0x0ef9db4c, 0x1e293001, 0x02984fd0, 0x05d8470a, 0x0a1b784f, 0xec5a91}}}, + /* 9*16^33*G: */ + {{{0x09142ccb, 0x117e90f4, 0x01fb78c6, 0x0414b58d, 0x1b4824c9, 0x1c7a4c9d, 0x00e3956c, 0x15c2ff9b, 0xedfe7d}}, + {{0x0c593d75, 0x1ca8e98d, 0x14450bae, 0x08856d3a, 0x1beb81da, 0x0e95a7aa, 0x1c858615, 0x0245c1b5, 0xc88692}}}, + /* 11*16^33*G: */ + {{{0x0a41b208, 0x1ed38e32, 0x189c003c, 0x0e8eb9bd, 0x00de8e7f, 0x00135e75, 0x1a5661c5, 0x11680b34, 0x531196}}, + {{0x048e4b69, 0x08f0fcf0, 0x0da1dfda, 0x183b8048, 0x1da113b3, 0x0c0f19d9, 0x05e29b19, 0x0567b845, 0xb78d73}}}, + /* 13*16^33*G: */ + {{{0x095daae7, 0x1e80dae2, 0x1c0cef2c, 0x0dcdd8f8, 0x0e1d5af4, 0x15fb7ab0, 0x1653de88, 0x1e45ff74, 0xf65dd1}}, + {{0x02556738, 0x0a72d4ce, 0x0b366ca3, 0x0c301f3d, 0x04cd0eb0, 0x1f31be7e, 0x1ba0839b, 0x0419c36c, 0x9d71b8}}}, + /* 15*16^33*G: */ + {{{0x1afcf1ee, 0x1f7e0af1, 0x01a6846a, 0x155008e4, 0x007c60af, 0x1a405554, 0x0922d06f, 0x0fdb995c, 0xa6670e}}, + {{0x00c67fc8, 0x1c43855f, 0x0f3ce28d, 0x07312392, 0x1ac131ef, 0x1b97a4ab, 0x0bce8e57, 0x19274a38, 0xa7fd14}}} + }, + { + /* 1*16^34*G: */ + {{{0x04cd3397, 0x008a2e0a, 0x1457ad5d, 0x09820c32, 0x16deddc3, 0x16795a8a, 0x1c8e24e1, 0x00833db4, 0x73baff}}, + {{0x1adcb8e4, 0x0f79509c, 0x0984d250, 0x1df259f0, 0x1825e779, 0x08f460c7, 0x117da803, 0x0c692ef5, 0x1e97de}}}, + /* 3*16^34*G: */ + {{{0x161fb913, 0x1587ca90, 0x14c4a5df, 0x0048c2ad, 0x0d04e3e1, 0x046f225f, 0x0860d10e, 0x01867cc1, 0x45f833}}, + {{0x0c15c3bc, 0x06a40be0, 0x1f0cdcdc, 0x1a10f3ed, 0x07760f06, 0x003b6c5d, 0x01bbe03a, 0x0db7fad2, 0xc212cf}}}, + /* 5*16^34*G: */ + {{{0x0e393654, 0x177cb48f, 0x1b75f1d6, 0x1a97c3e7, 0x15991965, 0x100e45e6, 0x16ad97d6, 0x09359af7, 0x5544dc}}, + {{0x0f53206f, 0x1b085dfc, 0x15639cc8, 0x1dfd2e07, 0x15192241, 0x02dadc49, 0x152b0130, 0x112a10ff, 0xed3e85}}}, + /* 7*16^34*G: */ + {{{0x08a15dfd, 0x1f9acca1, 0x1ea79544, 0x1b75804b, 0x0f695741, 0x176aad71, 0x1dcd2cf4, 0x120c33dc, 0x2757b7}}, + {{0x1042c341, 0x04742067, 0x09c55b7f, 0x112f1479, 0x1500d176, 0x1a909e6a, 0x04b97325, 0x1aaa9856, 0xdf46d2}}}, + /* 9*16^34*G: */ + {{{0x1f840a5b, 0x1f6fa135, 0x0b52613b, 0x195b4ba7, 0x03ccd148, 0x10e6608a, 0x0f236610, 0x0fbca1c5, 0xe87243}}, + {{0x1377e86a, 0x0272c2a8, 0x0e59192e, 0x1468d9f2, 0x08bf5ac3, 0x048fb312, 0x185964ef, 0x11224e52, 0xf984bc}}}, + /* 11*16^34*G: */ + {{{0x136afd6a, 0x0ab54dc5, 0x1e078f52, 0x03f8a142, 0x1334a926, 0x1b6af379, 0x1700e4bb, 0x15749aee, 0xc56cdc}}, + {{0x13f87c26, 0x10435f77, 0x0331c8f9, 0x090764ad, 0x14ef566f, 0x0f58bfac, 0x01c334bb, 0x1c5e70d6, 0xd16056}}}, + /* 13*16^34*G: */ + {{{0x0cfd2cd0, 0x0d6cc069, 0x070304c7, 0x08266883, 0x0abb1239, 0x142c1f24, 0x0a1a73f4, 0x0e71e7fe, 0xd3d6e6}}, + {{0x194ae2dc, 0x02bce3bb, 0x1dbe3c53, 0x1f4ad185, 0x1e59b001, 0x147fb9be, 0x0da14db6, 0x0bf9a2b3, 0xff448f}}}, + /* 15*16^34*G: */ + {{{0x1ab7a64c, 0x0ac442ae, 0x026ade82, 0x011ad474, 0x1d406565, 0x196b911b, 0x101ec0c4, 0x110adc8b, 0x88f977}}, + {{0x1575f103, 0x02f4c708, 0x1b499ce8, 0x06012442, 0x09e5836c, 0x0d792bcb, 0x11d0d14c, 0x1ba8d6ab, 0x4a745c}}} + }, + { + /* 1*16^35*G: */ + {{{0x1fa4e33c, 0x1172fd98, 0x02632cc3, 0x077d8f16, 0x0fb98268, 0x023614bb, 0x16ef25d1, 0x17234984, 0x9cf646}}, + {{0x0e0d4563, 0x0e22f030, 0x10580c86, 0x00b04fd7, 0x01f319e2, 0x0712c5c1, 0x0a247902, 0x09b83ecb, 0x37b062}}}, + /* 3*16^35*G: */ + {{{0x06bf1e67, 0x0c5b0c66, 0x172bd8fa, 0x0cce93fc, 0x04e0f4c5, 0x129c13bb, 0x126675e9, 0x1bc2a36c, 0x83cb43}}, + {{0x099acc97, 0x13f74598, 0x1445a7a8, 0x0884597b, 0x018f8287, 0x00373122, 0x1be3bec6, 0x1449731e, 0xbce28c}}}, + /* 5*16^35*G: */ + {{{0x1c0f057e, 0x1856ba46, 0x154f7608, 0x10c50e03, 0x1022484e, 0x07e0af12, 0x02300cd0, 0x1cac19d6, 0x3ff3b3}}, + {{0x0817965e, 0x0a0fbed5, 0x1c05d88b, 0x0046dd88, 0x07843a01, 0x08b82bc3, 0x1e3dbdff, 0x0de776ca, 0x7f17ad}}}, + /* 7*16^35*G: */ + {{{0x125e69f1, 0x088a5a01, 0x08af2d45, 0x0f51e5a8, 0x0af99636, 0x0ef0b9eb, 0x00ff7686, 0x05bb1ffb, 0x6e9edb}}, + {{0x002b7e9b, 0x1070bf1a, 0x07ca06dc, 0x04e8a8f3, 0x1bff61c7, 0x0b55b2f9, 0x153aacd5, 0x02d9dff2, 0xc08222}}}, + /* 9*16^35*G: */ + {{{0x0288f038, 0x19297b35, 0x17fe082f, 0x0ed129d6, 0x02d32f08, 0x00cef376, 0x112fbeaf, 0x1d009883, 0x5ee280}}, + {{0x16f1ee6e, 0x02d55c35, 0x19b1bd07, 0x0f067531, 0x1eec011d, 0x0c37f664, 0x0e4a1301, 0x1f28cefc, 0xbcd969}}}, + /* 11*16^35*G: */ + {{{0x00c708c8, 0x05f992b6, 0x1c2a1aa8, 0x08609e5e, 0x0288c2c3, 0x1b2ec8ff, 0x15cdb7f8, 0x0dc0b840, 0xe1f016}}, + {{0x1896ed38, 0x18c6b9d9, 0x0d6802b9, 0x0abe45df, 0x13016fb6, 0x1195f451, 0x0d481111, 0x07d22d87, 0xe64765}}}, + /* 13*16^35*G: */ + {{{0x076edefb, 0x10784e52, 0x039f575f, 0x117b0020, 0x1c7badd5, 0x0d5a14bc, 0x1171fc48, 0x10f57ec6, 0x280896}}, + {{0x1d1b0ae0, 0x17a2b914, 0x00e4848b, 0x06360f7c, 0x141c44dd, 0x0cf5ec82, 0x064699f8, 0x1e67a766, 0x5d071c}}}, + /* 15*16^35*G: */ + {{{0x1344897a, 0x096ccde7, 0x1309a774, 0x1da60eb4, 0x1edab7b9, 0x0f429212, 0x132dc161, 0x1bc50320, 0xeb15b0}}, + {{0x05bfe7ee, 0x0cef41e7, 0x1f42e0ab, 0x0d3165f2, 0x12f85814, 0x157c66b9, 0x01c42262, 0x02d384cc, 0x96cdd}}} + }, + { + /* 1*16^36*G: */ + {{{0x123b716d, 0x137d6e02, 0x13869ae0, 0x0712cdee, 0x0df9e0d2, 0x03c9c68c, 0x14d3a297, 0x1c717194, 0xf81f5b}}, + {{0x12632401, 0x120017a8, 0x01e0cc11, 0x0eb0a075, 0x00328660, 0x094e9c07, 0x1b7c755b, 0x065383e0, 0xdc7f49}}}, + /* 3*16^36*G: */ + {{{0x19d19d8a, 0x062b4281, 0x09548561, 0x024f12a3, 0x1490a3b4, 0x161ae0b6, 0x18e16bdb, 0x0a1f6250, 0xa60084}}, + {{0x190d2c4f, 0x0caedd9a, 0x0995cf7f, 0x1c011f4e, 0x1ca84f0f, 0x0a2f3df4, 0x11cb23db, 0x180cf46f, 0x7b4b79}}}, + /* 5*16^36*G: */ + {{{0x096cac9f, 0x14c3d9ca, 0x11379c89, 0x0719d4b8, 0x10e1c59e, 0x04bcc7f5, 0x0d531930, 0x1632cfbe, 0xde5382}}, + {{0x0fa473d3, 0x070417ed, 0x1610455f, 0x07c528e7, 0x19f2bc2e, 0x1804c8e7, 0x06158b0e, 0x0f16d392, 0x9c47b6}}}, + /* 7*16^36*G: */ + {{{0x06807a23, 0x0594b1bc, 0x097fcf9f, 0x0040a2d4, 0x14ec8400, 0x1ccea88c, 0x04214d12, 0x0100fe55, 0xa7227b}}, + {{0x14115894, 0x0238cc1e, 0x0a337247, 0x1f02af6e, 0x075abe7b, 0x023c9050, 0x07a1176a, 0x04ba8ba7, 0xf55075}}}, + /* 9*16^36*G: */ + {{{0x044d14c0, 0x13cbfe10, 0x0a3cd796, 0x1956bf43, 0x01d39005, 0x1f39d1b3, 0x1659196a, 0x11a84688, 0xcd2b2e}}, + {{0x02496315, 0x0b056791, 0x0b33b3a9, 0x16de1973, 0x0f671fdd, 0x0f76ed60, 0x02893541, 0x1bf3610e, 0xf13012}}}, + /* 11*16^36*G: */ + {{{0x199a1566, 0x17a75891, 0x08a3da59, 0x18cab5fe, 0x19dd8f25, 0x1e4dc1ef, 0x1fad33be, 0x0991e1be, 0x11b2e8}}, + {{0x1c56bb73, 0x0d259e02, 0x16025a16, 0x16ab0819, 0x005cc824, 0x0b4c3cba, 0x05d7410b, 0x13b79446, 0x610376}}}, + /* 13*16^36*G: */ + {{{0x1ba6f34a, 0x084f5946, 0x1171be1f, 0x10d41fc4, 0x11485312, 0x1051f7cc, 0x01c1d676, 0x052be574, 0xfb6d19}}, + {{0x0bcf0452, 0x1af9e411, 0x1a16c7e6, 0x19f45b92, 0x191b9ecf, 0x07ea6253, 0x1b7678e2, 0x053af7e8, 0xf29b2d}}}, + /* 15*16^36*G: */ + {{{0x0a435427, 0x06516950, 0x1936d41d, 0x0021304f, 0x06651a69, 0x13b286c8, 0x0f042abd, 0x19820782, 0xff429f}}, + {{0x19853824, 0x12822869, 0x0e8368a3, 0x17dca694, 0x06876205, 0x04e42b0c, 0x16c25350, 0x0cd149d9, 0x2addf9}}} + }, + { + /* 1*16^37*G: */ + {{{0x07354b7a, 0x16ce3f3c, 0x045725e3, 0x1d94ad87, 0x0de1d022, 0x1a31f29e, 0x01208e5f, 0x0e9aefc1, 0x856854}}, + {{0x116e04c6, 0x0aa2015e, 0x15bf6f62, 0x1123b83f, 0x1728706a, 0x089d9537, 0x1edbbaf2, 0x16a17eb0, 0x20b50e}}}, + /* 3*16^37*G: */ + {{{0x1b26165d, 0x1b663fd3, 0x1dc4dfab, 0x07442bec, 0x092dea71, 0x090497e6, 0x199527a7, 0x18ea5647, 0x617344}}, + {{0x06b2a286, 0x04dfee5c, 0x1c5c6931, 0x13582dc6, 0x020b2989, 0x0885e8c1, 0x0d6926df, 0x02f486f0, 0xe99016}}}, + /* 5*16^37*G: */ + {{{0x10442770, 0x10dfbb32, 0x006eb440, 0x11ef4dc2, 0x0fc2e901, 0x00ed4f20, 0x088b6813, 0x1dcabf6d, 0xf8ac03}}, + {{0x1b00c82d, 0x11432788, 0x0107408e, 0x0c057f2a, 0x06d17a97, 0x03a8d9a7, 0x1a3a90db, 0x0cab33a3, 0x5a358e}}}, + /* 7*16^37*G: */ + {{{0x165460de, 0x1f036c37, 0x0fddee3e, 0x07f1a155, 0x08ea60c7, 0x1e5f1866, 0x1719b1b2, 0x16a4b792, 0x8a731f}}, + {{0x1f8dc207, 0x0c2cd0ef, 0x1423b8fa, 0x0cfc78a4, 0x04dc4358, 0x0aa5ffd8, 0x0c663d4a, 0x18bc556c, 0x585855}}}, + /* 9*16^37*G: */ + {{{0x151634a5, 0x0155d6fd, 0x1b549399, 0x1214f06b, 0x1967f3cf, 0x15d0166b, 0x0892fc29, 0x0c26551e, 0xa20b60}}, + {{0x1226afad, 0x1609bcaf, 0x1517b7c9, 0x08eafc79, 0x1315bc67, 0x0ff41b9d, 0x0676b4f5, 0x13ed2fb7, 0x9c43ce}}}, + /* 11*16^37*G: */ + {{{0x13ba54a8, 0x02191225, 0x0eaa5c1c, 0x164c6ac0, 0x1cc0ab58, 0x0761a7e2, 0x1c26450e, 0x127e24ac, 0x16f1f}}, + {{0x064f2889, 0x0be30fd3, 0x08ed39bb, 0x15e4ea8d, 0x0e658d93, 0x188df24e, 0x055dbca6, 0x0822b12a, 0x8bcd3}}}, + /* 13*16^37*G: */ + {{{0x07c16d94, 0x177249e7, 0x0a117f77, 0x103a1540, 0x0c31fe25, 0x1eb3e667, 0x11e23023, 0x0ce17a06, 0xe61586}}, + {{0x114810ab, 0x1a768cd5, 0x0910eefe, 0x0b9a3c8f, 0x0f0ee4e6, 0x15c9fa5b, 0x12fa316c, 0x15d3ce24, 0x65ca03}}}, + /* 15*16^37*G: */ + {{{0x13ce728d, 0x0b6e5332, 0x1c7342f3, 0x1b20fc50, 0x05347f4c, 0x04510b64, 0x08995568, 0x01671aad, 0xdcd37f}}, + {{0x17cacbcb, 0x0be89b4c, 0x076afae3, 0x19a68da5, 0x0d6f3caa, 0x1db159e3, 0x1061cb0d, 0x1aef9b49, 0x6574db}}} + }, + { + /* 1*16^38*G: */ + {{{0x12378c16, 0x0e36e0d8, 0x05588ab7, 0x0c0eaa8c, 0x1597d9e3, 0x1296b5fc, 0x0c46cc67, 0x0b382567, 0x1136b7}}, + {{0x13488127, 0x0facde85, 0x147338de, 0x095451ce, 0x1ecb2961, 0x15e307f4, 0x1f7427c2, 0x19e8a2d1, 0x7dec0f}}}, + /* 3*16^38*G: */ + {{{0x1b299740, 0x18736c13, 0x0f6c4d25, 0x1e8cfae2, 0x0ad48f40, 0x089bc9fd, 0x0cdb312b, 0x16e39ba8, 0x53893e}}, + {{0x1fff7509, 0x0f5378d9, 0x1f7a3354, 0x0265de43, 0x1dc8dd8a, 0x0714753a, 0x0f60e107, 0x0fd290bf, 0x27728a}}}, + /* 5*16^38*G: */ + {{{0x138000fc, 0x059e4ab3, 0x1c0ef04c, 0x1c4ee0f7, 0x0e5604e1, 0x1b5d78fd, 0x089d5f8d, 0x1baea99b, 0xdd64e9}}, + {{0x1df4ac12, 0x1d50e286, 0x07923e2f, 0x0ba04572, 0x079ffbe4, 0x0f1f0ce5, 0x0b25b7e2, 0x1d618526, 0x432193}}}, + /* 7*16^38*G: */ + {{{0x13878c4a, 0x1c10a8b9, 0x1470157b, 0x07a0790a, 0x11b21d88, 0x0e307254, 0x145bcb1a, 0x150fdffa, 0xff9845}}, + {{0x1488df68, 0x1d3423eb, 0x173faf1a, 0x066e8d6c, 0x1e8ddf1f, 0x12476ffa, 0x0a3e3f62, 0x1e949c48, 0x835b9a}}}, + /* 9*16^38*G: */ + {{{0x00f51524, 0x054dc40a, 0x0be1bf23, 0x1d6d42b7, 0x093de5fa, 0x1d184229, 0x0273a1d9, 0x1d17722c, 0x954e13}}, + {{0x05e30a69, 0x0fed9eca, 0x079f2f7a, 0x1d486228, 0x0cbcfa9e, 0x1121a3cf, 0x010764f1, 0x07ff2548, 0xecc836}}}, + /* 11*16^38*G: */ + {{{0x04f77287, 0x1bcc6a9e, 0x0c3678c7, 0x12a786d2, 0x00497412, 0x0862e0ec, 0x0e4f7f35, 0x1edf483f, 0xb2217e}}, + {{0x04e7b5da, 0x1d0253b9, 0x17a4f2e5, 0x13b8e738, 0x0c843d70, 0x1e7c469c, 0x0e8ad77e, 0x1c19cf9e, 0xfb5153}}}, + /* 13*16^38*G: */ + {{{0x05c723e9, 0x17a9cb64, 0x09e05c62, 0x0e775535, 0x15c351f9, 0x175748ae, 0x16b448b1, 0x162f6f37, 0x3c3950}}, + {{0x16834763, 0x11c3bafb, 0x1241ab97, 0x1b596af8, 0x0a8b0b01, 0x1dbf50c3, 0x036ed252, 0x0c441222, 0xb99b73}}}, + /* 15*16^38*G: */ + {{{0x1c18abeb, 0x18beb711, 0x09758a8d, 0x1b9a320e, 0x0a944d59, 0x097c225d, 0x136b477b, 0x0599f2c3, 0x803c7a}}, + {{0x029bfa78, 0x121cdf87, 0x19c8735a, 0x18887854, 0x14409ed7, 0x078b4e25, 0x04a1ac6b, 0x0814f2dd, 0x83fb50}}} + }, + { + /* 1*16^39*G: */ + {{{0x143e832a, 0x03b1778c, 0x01b7dc27, 0x0a15602f, 0x1f18e07e, 0x19d412c4, 0x146a43d5, 0x1174f854, 0xd2bf2}}, + {{0x1b20d37c, 0x0131d78a, 0x15451192, 0x193b72c0, 0x0e7ed27e, 0x10854a5a, 0x02b1c21e, 0x086277a0, 0xcac3f}}}, + /* 3*16^39*G: */ + {{{0x112c9666, 0x0f5625e0, 0x1ff37cd8, 0x118d86cb, 0x1531b1cf, 0x061adbec, 0x00b3f66f, 0x0bd72cff, 0x34e5e8}}, + {{0x192a3666, 0x0d0c29e4, 0x18e949ad, 0x0fdac783, 0x046330f4, 0x12bae65e, 0x0dae0a11, 0x06264434, 0xc0ce68}}}, + /* 5*16^39*G: */ + {{{0x11209502, 0x0b295f1a, 0x16499970, 0x02e4004b, 0x1e154594, 0x09f7848c, 0x018e9b12, 0x198b3e9b, 0x727362}}, + {{0x17042b0d, 0x13be8e9e, 0x09d82ef1, 0x1a6ff376, 0x11d20a18, 0x05c61674, 0x0d627c40, 0x04537575, 0x17b0f4}}}, + /* 7*16^39*G: */ + {{{0x0447f959, 0x0af3c945, 0x1c74da11, 0x0fb57504, 0x1eee9f94, 0x0a625da4, 0x13b25ce8, 0x0c00a94d, 0xa0c3f2}}, + {{0x168b671f, 0x01ce9244, 0x149cb26c, 0x0aa804b1, 0x08208b1b, 0x060865b1, 0x113ce0be, 0x0121c965, 0x508c73}}}, + /* 9*16^39*G: */ + {{{0x1b37e1d3, 0x10fcf812, 0x193788c0, 0x0c37c279, 0x1fd04107, 0x019df20a, 0x09e9032d, 0x063ef2b9, 0x7e81b2}}, + {{0x0155681a, 0x1afe5132, 0x10b8380f, 0x1097e563, 0x07c4e4ec, 0x04b67736, 0x144c497a, 0x0361d37d, 0xf3855a}}}, + /* 11*16^39*G: */ + {{{0x16c051a0, 0x04bb8afa, 0x16cf9b71, 0x1e5248de, 0x12abddcd, 0x0c736d87, 0x1b6128db, 0x038fb004, 0x8035e2}}, + {{0x12d0dd12, 0x00f5d97d, 0x02d88d58, 0x0fb8c613, 0x1d318b3e, 0x1f341bde, 0x0fbcdd76, 0x14896f45, 0xb8810}}}, + /* 13*16^39*G: */ + {{{0x1f12c69e, 0x1006184f, 0x194658f3, 0x0b5deb12, 0x0fcecafe, 0x18008102, 0x14cc1aeb, 0x1bfac314, 0x196908}}, + {{0x1d114831, 0x0998c820, 0x1ee21ae3, 0x05c66e3f, 0x1054eb6b, 0x0ef56e90, 0x18102fb8, 0x0d65f22d, 0x3f65bf}}}, + /* 15*16^39*G: */ + {{{0x0b89c5ed, 0x04c700fe, 0x1e9e31f9, 0x0a619ef2, 0x10f3577b, 0x10e90856, 0x0abd1b9b, 0x1d712c34, 0xdb77fc}}, + {{0x0d25b46e, 0x0ff8e3f7, 0x0266be96, 0x0d8f56d1, 0x1ad411f1, 0x1cdf264c, 0x173bb3cc, 0x070e39dc, 0x7fd1dc}}} + }, + { + /* 1*16^40*G: */ + {{{0x17f82f2a, 0x174e3aef, 0x1f7d0eab, 0x186b0e95, 0x113269e4, 0x16fa1b9b, 0x185fd588, 0x0acdd8e6, 0x8a535f}}, + {{0x023094b7, 0x0fcd0561, 0x031d9a71, 0x0a670c99, 0x092bfcde, 0x140c842d, 0x0f5cdf80, 0x108d1611, 0x455c0}}}, + /* 3*16^40*G: */ + {{{0x0b348fa0, 0x18a790bd, 0x0550777e, 0x1c48b20a, 0x0b4bce0f, 0x1191b612, 0x00b70a88, 0x07bbbd71, 0x86eac9}}, + {{0x0da51cee, 0x171c04aa, 0x13fba293, 0x0db2c6a3, 0x146716c2, 0x17cf46b7, 0x1635690d, 0x0a797789, 0x948f38}}}, + /* 5*16^40*G: */ + {{{0x19222c03, 0x17a0ffe4, 0x197840de, 0x19cefd0f, 0x1f407948, 0x1ebc242c, 0x0ab8fd79, 0x175f3f67, 0x8bf09e}}, + {{0x0a72bb54, 0x0a2fba17, 0x08387528, 0x1d81c3bc, 0x1ba309c9, 0x18edf3f2, 0x09cced22, 0x15fc5c4f, 0x509cba}}}, + /* 7*16^40*G: */ + {{{0x11ae9300, 0x029d160e, 0x1120a02d, 0x188e08eb, 0x1735b5e1, 0x05d6d179, 0x1f18644c, 0x1976fce1, 0xe85e2d}}, + {{0x1546e25e, 0x1506fee8, 0x030c6edc, 0x0fc30bbf, 0x02707deb, 0x1dadc11e, 0x02ff1ee9, 0x14daa39c, 0x451aaf}}}, + /* 9*16^40*G: */ + {{{0x05260cb8, 0x092eaab0, 0x0c854bc9, 0x1e95019d, 0x1dbf6836, 0x13ed0dd3, 0x1e0a8fc0, 0x1e451925, 0x3f5fb0}}, + {{0x1852c964, 0x17da5a20, 0x17b0cc9c, 0x1d0ea3f8, 0x183f2fa3, 0x0f0a9b33, 0x061c38e3, 0x1b5b4933, 0xc55834}}}, + /* 11*16^40*G: */ + {{{0x1a1cd60f, 0x15222216, 0x0c24ba92, 0x0d315398, 0x0002b9f9, 0x083a5a6d, 0x06595ebb, 0x045631b3, 0x336856}}, + {{0x0fd57d67, 0x1fb9bb28, 0x142e2c92, 0x1eb49978, 0x1af175fe, 0x06006f53, 0x1366ea16, 0x13de248f, 0xd42f50}}}, + /* 13*16^40*G: */ + {{{0x17576342, 0x029db75d, 0x06488abc, 0x19110673, 0x179d95b2, 0x1cec4b04, 0x0203df43, 0x0b811e00, 0x4813eb}}, + {{0x17376316, 0x060aaf5c, 0x1aa413d9, 0x1b8cfaa0, 0x1524aca2, 0x0b424719, 0x0903d980, 0x1a846748, 0x043f}}}, + /* 15*16^40*G: */ + {{{0x1f69b2be, 0x1b38b8ef, 0x04447027, 0x03ee9db8, 0x06e56ba4, 0x16ddd71c, 0x05ebc4c8, 0x1f34b5d3, 0x80c3f1}}, + {{0x0102d2f5, 0x0825cbe2, 0x0dea2fe2, 0x16e966b9, 0x15a9bf14, 0x113b2d8e, 0x1a14a603, 0x0814013b, 0xa9321}}} + }, + { + /* 1*16^41*G: */ + {{{0x0476c81d, 0x041bfa7f, 0x194f57a7, 0x06061d33, 0x0a366b7a, 0x06939ed4, 0x08066bdf, 0x1bfb9683, 0xad6090}}, + {{0x0e6705dd, 0x0c55e427, 0x0c1237e0, 0x1fb86c9e, 0x1d8a8393, 0x1ae1662d, 0x047ab335, 0x1ba91e99, 0x77b5d1}}}, + /* 3*16^41*G: */ + {{{0x02725490, 0x11239be7, 0x12d66402, 0x06480c47, 0x1863c4ac, 0x15299d84, 0x05f28ab6, 0x176e35ad, 0xd90b45}}, + {{0x0feb1299, 0x07c27c25, 0x1119b32c, 0x180f7fe7, 0x0cbd80cd, 0x1ab439bc, 0x143c2762, 0x11d83766, 0x332692}}}, + /* 5*16^41*G: */ + {{{0x1e933668, 0x1b59d32b, 0x1aeafc29, 0x05b099f0, 0x106befb0, 0x1c9d7d0f, 0x1f1eb014, 0x02e62427, 0xac2592}}, + {{0x12776fb5, 0x173bfb39, 0x07879e85, 0x0c83138d, 0x0ed7c6f8, 0x009f75ec, 0x02ea143b, 0x070e1b75, 0x6c79a0}}}, + /* 7*16^41*G: */ + {{{0x128de7e3, 0x130be090, 0x1aa65b66, 0x08558757, 0x031bf868, 0x07ded3da, 0x0ea21cc8, 0x095d2f3e, 0x7bc337}}, + {{0x0e6e5e29, 0x1d059975, 0x10333c19, 0x05b67369, 0x1acbd55f, 0x1bd73725, 0x19778031, 0x048c10a9, 0xb431f0}}}, + /* 9*16^41*G: */ + {{{0x035e057d, 0x1659191c, 0x162696d2, 0x1e21b5ed, 0x1119329c, 0x0397e1a0, 0x0ac9a2f2, 0x128a75c0, 0xbf9c14}}, + {{0x0da20fea, 0x1ab25941, 0x1711e6db, 0x173c3038, 0x1a542440, 0x189f82e3, 0x0f83ce14, 0x13e4be47, 0x92acb8}}}, + /* 11*16^41*G: */ + {{{0x021d6584, 0x1566be10, 0x0cf67974, 0x00bac887, 0x1014ef27, 0x1ed1ad6f, 0x1e2a5ba5, 0x0736af09, 0x55bf08}}, + {{0x0ed744ea, 0x0ab27a14, 0x19696a03, 0x1c284055, 0x07dcf089, 0x1fb5d45e, 0x133c9eb2, 0x067c96c3, 0x3d9807}}}, + /* 13*16^41*G: */ + {{{0x01068c5e, 0x0b4efb72, 0x195cf437, 0x0bdcdc97, 0x1d4872a4, 0x10a73c0a, 0x15467cab, 0x02ca66f1, 0xfcf24e}}, + {{0x07c19d75, 0x10fa29f9, 0x052156dd, 0x0ed49650, 0x0e7aee91, 0x0f10dac9, 0x1f8d719d, 0x1ca66dff, 0x186bb0}}}, + /* 15*16^41*G: */ + {{{0x020da860, 0x1becaf83, 0x12744022, 0x08a35490, 0x11f2f843, 0x1b29a1ec, 0x1fb287f6, 0x1a05ea2c, 0x7bc280}}, + {{0x07aac76d, 0x11488208, 0x1058cbaf, 0x03345fb9, 0x0e6e0f2e, 0x15fd382f, 0x07978989, 0x0ef777a6, 0xb33f6d}}} + }, + { + /* 1*16^42*G: */ + {{{0x13c4a205, 0x097961b4, 0x042a1229, 0x15bf13ea, 0x129fcde1, 0x0ab83adc, 0x0f139199, 0x0a2c60b7, 0xb6e06c}}, + {{0x1db36d05, 0x1bfa6c32, 0x0aa7e1d4, 0x13283350, 0x0d73b63f, 0x189373cf, 0x0fd71787, 0x0f843664, 0xbdb427}}}, + /* 3*16^42*G: */ + {{{0x19e27907, 0x17c10fb7, 0x167935a4, 0x15a96711, 0x1bd68771, 0x0eaeb7ef, 0x1139ace5, 0x07f08483, 0xed9e4}}, + {{0x0e78c4fb, 0x09fa7a83, 0x0e86417c, 0x0a39fd71, 0x00e0ce91, 0x07ec7589, 0x0d1fd6f0, 0x095fed64, 0xb5af87}}}, + /* 5*16^42*G: */ + {{{0x15b38f54, 0x1682b929, 0x0bc1f38d, 0x150c1cb9, 0x0e1f92e2, 0x146da47f, 0x1df71549, 0x0200edb1, 0x57a7e4}}, + {{0x045f2809, 0x1d3c0f31, 0x01fae9d7, 0x0edc7352, 0x0dd21dfd, 0x0511de41, 0x14906532, 0x00791d95, 0xfd7f0f}}}, + /* 7*16^42*G: */ + {{{0x07a79593, 0x17f5dfcd, 0x17125e51, 0x14e493a4, 0x05cf3347, 0x0d92c665, 0x16fbd4b6, 0x1c5e7deb, 0x24799a}}, + {{0x10920d55, 0x11c46bae, 0x1ac4e635, 0x086c3f37, 0x1e300999, 0x08d4e9b1, 0x0deccb0f, 0x04c5d90f, 0x9436b5}}}, + /* 9*16^42*G: */ + {{{0x0bf5abdc, 0x010b75a8, 0x02d94198, 0x19a75f7a, 0x15d60456, 0x08e58406, 0x009bb4c4, 0x18d1098e, 0xf15017}}, + {{0x0def85b7, 0x1aef15c5, 0x0e15612d, 0x18ceb84f, 0x1e232cbd, 0x1a0f1fe2, 0x0aa3b360, 0x03858be0, 0xdea7ef}}}, + /* 11*16^42*G: */ + {{{0x1e78e9b1, 0x0f92958f, 0x10507a1a, 0x11cdb89d, 0x0dfcc897, 0x018fba89, 0x1aa9b83e, 0x01f13697, 0xe196e2}}, + {{0x0a77eed7, 0x00bab0fe, 0x1b4d7d11, 0x1a02257d, 0x0ed9a908, 0x045f3b59, 0x1698a990, 0x10bf0350, 0xa5d66d}}}, + /* 13*16^42*G: */ + {{{0x1b1cb64b, 0x17719f2b, 0x02b0f55d, 0x13ca4ac3, 0x1ed14d60, 0x1e6b8a9c, 0x0c0bce5f, 0x1bcd8360, 0x7779f5}}, + {{0x1aba3aab, 0x070c68e5, 0x0aa54cf6, 0x10528479, 0x0e3fae2a, 0x189a53d1, 0x1afab7ea, 0x07e8e987, 0x9a842}}}, + /* 15*16^42*G: */ + {{{0x11d1b492, 0x16c2c3b4, 0x0669bd4f, 0x00c31840, 0x08ce8dfa, 0x16cd5759, 0x0bc4797d, 0x097c8474, 0x8605b9}}, + {{0x18ac6598, 0x18ebbdfb, 0x07a49715, 0x06a4da90, 0x1d1a8ee2, 0x170610a1, 0x1d63cfbd, 0x050fbcea, 0xa8e561}}} + }, + { + /* 1*16^43*G: */ + {{{0x09aa52df, 0x06afa725, 0x0a989fcf, 0x08a56368, 0x1ece618c, 0x00c4ecc8, 0x0fddb6f1, 0x192fec11, 0x45a511}}, + {{0x125ec16c, 0x1a95e890, 0x0a55739e, 0x03364fa4, 0x05ad25a9, 0x19bfe5b1, 0x0db4ff8c, 0x18ee7d53, 0x73be0e}}}, + /* 3*16^43*G: */ + {{{0x0e75bc7f, 0x0b3a9f7d, 0x1dfec7d9, 0x0e2e1b6d, 0x14c7b95c, 0x07890be6, 0x0d0e3bd0, 0x09fce572, 0xb57ac}}, + {{0x0972e9a9, 0x078b96e8, 0x127f1881, 0x12d81c80, 0x094fab1f, 0x1a67d2bf, 0x0ed7ca30, 0x104ef53e, 0xceedbf}}}, + /* 5*16^43*G: */ + {{{0x033125ff, 0x1ba19e7c, 0x16a05084, 0x0aaf60b4, 0x0ae99354, 0x016ce50e, 0x05233d1a, 0x1f6dc97a, 0x1178b3}}, + {{0x18486abd, 0x007cb84e, 0x195346fa, 0x115b9a11, 0x10ed10dc, 0x131bf518, 0x056ce0b7, 0x1d53757b, 0xdfd697}}}, + /* 7*16^43*G: */ + {{{0x1662d955, 0x19b4ae67, 0x033913ce, 0x09ad0b69, 0x15693844, 0x1dbf4693, 0x041fe2a0, 0x104df29d, 0x8ac7a0}}, + {{0x046b3dae, 0x03516df3, 0x1b04e36f, 0x09038b7a, 0x19ad9e3f, 0x1291a65a, 0x0c73275f, 0x1241e664, 0xc80f40}}}, + /* 9*16^43*G: */ + {{{0x088803e5, 0x06c3cd6f, 0x0d1972df, 0x156f9c1a, 0x09e02cc1, 0x1d43802b, 0x08446adf, 0x050b3bbe, 0xc0f48b}}, + {{0x076619ee, 0x14991014, 0x00a3b6e9, 0x1c9c0e17, 0x0d7d3932, 0x12393f30, 0x08da7269, 0x16df1079, 0x3d4326}}}, + /* 11*16^43*G: */ + {{{0x1e1f515c, 0x0b6b384c, 0x194e6bea, 0x09146442, 0x1b8c0e2b, 0x047087fb, 0x19b68067, 0x01e06a2e, 0x2ce870}}, + {{0x052eed6e, 0x08c9b24c, 0x10f54b25, 0x13b9a7c5, 0x15d2ca7a, 0x17a17bf7, 0x129eeb2c, 0x09e76bd8, 0x73879f}}}, + /* 13*16^43*G: */ + {{{0x04dcf274, 0x0d51bbaf, 0x0b3a8911, 0x0400d059, 0x0ca1d807, 0x0dd87ebe, 0x04245178, 0x0c3b96f8, 0xfac442}}, + {{0x112f0472, 0x1b4c3007, 0x11652c58, 0x004f8c4e, 0x097bd732, 0x11eaea77, 0x02a1f31c, 0x18c2acd5, 0x9713fd}}}, + /* 15*16^43*G: */ + {{{0x1dc39d21, 0x1af25b55, 0x06b71a0a, 0x0e7d7a81, 0x12813683, 0x0f21d0cc, 0x18011964, 0x02cb6807, 0xf891e6}}, + {{0x05ca579f, 0x0c8470be, 0x11809535, 0x12fd89ea, 0x1c43b73a, 0x1c54716d, 0x13b462ba, 0x162577bf, 0x129f53}}} + }, + { + /* 1*16^44*G: */ + {{{0x1076f57b, 0x0678d5cc, 0x12b181ca, 0x170dc79d, 0x0a53f030, 0x11a80e79, 0x1e72e49f, 0x0ac91018, 0x20e118}}, + {{0x0da7afe6, 0x0c1df6dd, 0x186dc7a1, 0x01b93681, 0x1e9979fe, 0x01f1fdb6, 0x1bdcc6f7, 0x0a55886f, 0xff67b3}}}, + /* 3*16^44*G: */ + {{{0x0b661da4, 0x04f1d76a, 0x0a129721, 0x14a358c9, 0x01a21940, 0x1fec8183, 0x1b3a3df1, 0x0b770cc0, 0xffca2a}}, + {{0x0f9b8a5a, 0x0c241af5, 0x1a1fdbe2, 0x1f0f5bca, 0x06c8c478, 0x04d0a47a, 0x172294f3, 0x1c656e39, 0xce1cb0}}}, + /* 5*16^44*G: */ + {{{0x1c8cfd22, 0x0560f09c, 0x0daf960f, 0x160ce36c, 0x1354a4e2, 0x1a8a86d6, 0x1aad8d94, 0x1dbff822, 0xff209c}}, + {{0x13bc5535, 0x1c4d8a74, 0x1a12e508, 0x1e972592, 0x1f1c80d4, 0x00e92667, 0x1df881f5, 0x12e8d3a1, 0xad79f3}}}, + /* 7*16^44*G: */ + {{{0x0f4e983e, 0x003f2c2c, 0x05fddf3a, 0x1cdd90e0, 0x14b156d9, 0x0040699d, 0x0ea56181, 0x05a160f3, 0x33d5f0}}, + {{0x11ea37a9, 0x0b86dc3c, 0x18c3255b, 0x035524b2, 0x1a1eb7a7, 0x0b0b2add, 0x1e11cf0f, 0x0eda7388, 0x1eb5a}}}, + /* 9*16^44*G: */ + {{{0x01e52a73, 0x14ad3cfb, 0x01686f2d, 0x0622d92e, 0x15e65d0e, 0x1a6cd6dd, 0x1a42a7a5, 0x07b4c4c2, 0x8f1bd2}}, + {{0x0bea8f2e, 0x1ed0a28f, 0x06142a15, 0x0a05cc96, 0x1eade590, 0x0f2183a1, 0x02e2e1da, 0x0c7275b5, 0x4c7a8f}}}, + /* 11*16^44*G: */ + {{{0x1d2bc078, 0x0217f617, 0x1cfbd696, 0x00ec0a53, 0x080aec4a, 0x0c5bed6c, 0x1fcfb98a, 0x1fdef6c6, 0x963bc9}}, + {{0x1afb8657, 0x0ed918fa, 0x10e8f09f, 0x180851e8, 0x0a4f1c5e, 0x128494c0, 0x188a028f, 0x07f9a3ce, 0x7ed338}}}, + /* 13*16^44*G: */ + {{{0x0fb608ad, 0x038f3b75, 0x03f64e9a, 0x00aca9b1, 0x0bc49748, 0x16ee5be4, 0x011398c6, 0x15b0c3b5, 0x89fe6d}}, + {{0x064d4dad, 0x00a0373c, 0x1c2a1ed8, 0x13aa27c1, 0x1df39aea, 0x1effdb92, 0x03f21ed5, 0x10463e9d, 0x289827}}}, + /* 15*16^44*G: */ + {{{0x19ae4a29, 0x18c4c0d8, 0x08ca82c1, 0x1fb4dd44, 0x06984df2, 0x1d62708b, 0x1d654435, 0x0fe77af2, 0xc34804}}, + {{0x0eb09235, 0x04acd626, 0x126bafcd, 0x05d33e0a, 0x1ae9a842, 0x06e90706, 0x11584f6a, 0x1e40ad43, 0x584781}}} + }, + { + /* 1*16^45*G: */ + {{{0x19273a8f, 0x08a29744, 0x13f552b7, 0x1a8f1cd2, 0x1dac93fd, 0x163fefeb, 0x09ec0c63, 0x1f0e4740, 0x5c9cc4}}, + {{0x1ce80d6e, 0x0ed6534a, 0x06b2ad6b, 0x006ceb42, 0x0af964f0, 0x0c4e9b84, 0x0966a09d, 0x0f43bfda, 0x84efe0}}}, + /* 3*16^45*G: */ + {{{0x0883e382, 0x0464c2a2, 0x154dbce3, 0x009f9dea, 0x07431d06, 0x001ca900, 0x01716f89, 0x12577bfb, 0x5ac8e1}}, + {{0x1dfeaadc, 0x09b9ecde, 0x13674b94, 0x0dd9427a, 0x03976de7, 0x1ff9784b, 0x1200e723, 0x00098f51, 0xfcb7e5}}}, + /* 5*16^45*G: */ + {{{0x0f01a3e8, 0x052183bf, 0x120253af, 0x16ca865c, 0x07362c6e, 0x0ea2706b, 0x0460b545, 0x1316f224, 0x99dc06}}, + {{0x00f61114, 0x14322ff2, 0x1e3ca514, 0x0ce069af, 0x00044b7a, 0x0388b8ec, 0x0af1a5eb, 0x1ba47730, 0x67c69c}}}, + /* 7*16^45*G: */ + {{{0x0cd535ab, 0x01fbd802, 0x1d9370ce, 0x09b107d0, 0x1b9f3772, 0x01abe7e7, 0x18591009, 0x0c31c080, 0xabe2f3}}, + {{0x117b9c1a, 0x0388d9a2, 0x0b237664, 0x1cf43187, 0x1f7957fd, 0x1f959016, 0x0a4f7836, 0x0996eab6, 0x4f02d6}}}, + /* 9*16^45*G: */ + {{{0x0909970c, 0x1a5b359b, 0x19b93836, 0x11b74b33, 0x0099e451, 0x1d8fbbf3, 0x1c84df1b, 0x1af1873c, 0x227cd0}}, + {{0x1f809727, 0x02d25718, 0x0e67b10a, 0x01d87efd, 0x15defa21, 0x043a0e7f, 0x04761f5b, 0x0e390327, 0x2225e7}}}, + /* 11*16^45*G: */ + {{{0x09e65b59, 0x0cd6fe4c, 0x113fddf3, 0x02045efa, 0x1053b7a4, 0x14985466, 0x16da09fb, 0x10415db8, 0x363146}}, + {{0x09b4c2cf, 0x0050b213, 0x116dba72, 0x0792076b, 0x07fc1c14, 0x1c7c9011, 0x0a4a3a09, 0x0c42f12e, 0x1d87db}}}, + /* 13*16^45*G: */ + {{{0x0d4c2506, 0x0bd8ac5e, 0x07a7ebc0, 0x18bb8fe3, 0x11fec5b6, 0x14670c4e, 0x028f9d29, 0x16cd0d63, 0xf65ed6}}, + {{0x1913dfac, 0x0296e129, 0x15950af3, 0x11df8699, 0x0e7bd412, 0x0e17e9bb, 0x0ba14957, 0x0d065175, 0xd6d0bc}}}, + /* 15*16^45*G: */ + {{{0x1b47a80b, 0x0f27cba9, 0x0925d5e0, 0x0f8b4cc8, 0x1dba8ff9, 0x0e13b7d5, 0x0d5ca776, 0x1f423ec2, 0x66a0de}}, + {{0x1f795e8b, 0x1b8cafc7, 0x1bb74803, 0x014850a4, 0x0f474c23, 0x0f92b0d7, 0x09072b63, 0x0dbc6f59, 0x3e24aa}}} + }, + { + /* 1*16^46*G: */ + {{{0x08523eb3, 0x1eecd599, 0x1b1b12b9, 0x1474eaaa, 0x1cde3351, 0x0c22279e, 0x16ad1ab0, 0x1d7f1516, 0xbaffd4}}, + {{0x15acf387, 0x0bc18066, 0x122dbb86, 0x02399f7e, 0x0c09c245, 0x0759bcad, 0x1a0c00ea, 0x18bb792b, 0xfa93d}}}, + /* 3*16^46*G: */ + {{{0x14737641, 0x004cda77, 0x10b84eb4, 0x158182cd, 0x03fb71af, 0x1a891b2b, 0x07b5dde2, 0x04488391, 0x91b445}}, + {{0x0101bbe0, 0x1289594f, 0x01df40fc, 0x0fb8ccdc, 0x1b428c9d, 0x11ad5817, 0x05d8b04c, 0x17a6ffb5, 0x3f4463}}}, + /* 5*16^46*G: */ + {{{0x06dd01df, 0x1515d5c1, 0x03fbee71, 0x1dfeeca7, 0x09165743, 0x1363e434, 0x01ea8dfa, 0x0bbd05b6, 0x296d13}}, + {{0x0a3a0dc8, 0x0a869905, 0x199bd812, 0x15ec31cb, 0x177cadff, 0x094bbb63, 0x1790ae1c, 0x1bb25c28, 0x14054e}}}, + /* 7*16^46*G: */ + {{{0x1650f5dd, 0x13f37836, 0x046cb231, 0x1fc24843, 0x04a9654f, 0x0178f8cf, 0x08c63c4f, 0x1e7226cf, 0x4b0942}}, + {{0x1aeddc6d, 0x1752de63, 0x19ce98b0, 0x18cf05c9, 0x1c25f023, 0x0080ab09, 0x0f038157, 0x14c6cd95, 0x7432aa}}}, + /* 9*16^46*G: */ + {{{0x04e4a96f, 0x11baf478, 0x1956c51e, 0x10c5686a, 0x0e4af188, 0x0dc7e269, 0x046a0cfb, 0x14c5fd98, 0x36377}}, + {{0x11e08cdb, 0x1d7dbf02, 0x0244c9a2, 0x13184286, 0x06263840, 0x062abc2a, 0x1e0e364c, 0x03a6a9fb, 0xa1de19}}}, + /* 11*16^46*G: */ + {{{0x079b5a59, 0x00fe9f0b, 0x1a798a85, 0x03d13d64, 0x03251c62, 0x16ab84ec, 0x058af2ef, 0x1ee61ebc, 0xa1041a}}, + {{0x0c5514a3, 0x0695a011, 0x0b19b676, 0x00d21c3d, 0x02afbfb3, 0x086c39de, 0x0c650899, 0x0d551eb1, 0xad4217}}}, + /* 13*16^46*G: */ + {{{0x0ddd597b, 0x1746d836, 0x015d637a, 0x1262e199, 0x12007d88, 0x0d687cf4, 0x191c0cdc, 0x15a163ca, 0xda2167}}, + {{0x1de7fa04, 0x140b93f1, 0x13e7d189, 0x1c54a428, 0x0ebf6cdd, 0x180753cc, 0x14d87fd9, 0x16c4a8dd, 0xa21992}}}, + /* 15*16^46*G: */ + {{{0x13f7dbb0, 0x09a51115, 0x0fca7026, 0x1c47b84d, 0x0f29df4e, 0x1dc390b2, 0x19e2f218, 0x1846fed8, 0x1c3fdb}}, + {{0x12e16d32, 0x1265ee4b, 0x083e2b75, 0x0c8c7000, 0x118d41f0, 0x129ca525, 0x004fc2ba, 0x0206f253, 0x39260f}}} + }, + { + /* 1*16^47*G: */ + {{{0x1a111101, 0x1a9046c5, 0x16cf17e0, 0x1e1634ed, 0x04c96479, 0x11a692bf, 0x1d9bb48a, 0x0131f9da, 0x6ad2a}}, + {{0x1fb37ef4, 0x0d3dd4ea, 0x03a26bb0, 0x053b056e, 0x162a0de4, 0x000ddcf5, 0x18d56693, 0x038b1f0b, 0x2d5b8b}}}, + /* 3*16^47*G: */ + {{{0x102bef6f, 0x17e1c23b, 0x0c7b90dd, 0x1c9e308a, 0x0d475bba, 0x05eb35ec, 0x15c813de, 0x0aad8779, 0xf1a2ca}}, + {{0x0f6c1ca3, 0x0d968fec, 0x154ad004, 0x08fd503f, 0x00168b0b, 0x0a0bee01, 0x04fb7d15, 0x15e09106, 0xb39b59}}}, + /* 5*16^47*G: */ + {{{0x00455367, 0x030147e5, 0x1584f820, 0x09e59049, 0x11851e6c, 0x00b2c75f, 0x00c3b864, 0x093fc770, 0xe924ad}}, + {{0x08257e92, 0x1e8c67c1, 0x099d4ad7, 0x1463549f, 0x1bb6f52d, 0x029c9eb7, 0x1b55d482, 0x12f34287, 0xfcc97d}}}, + /* 7*16^47*G: */ + {{{0x02f8f1e7, 0x08f849ca, 0x147e78ba, 0x0954ba9b, 0x122f68dd, 0x09a882fd, 0x09be802e, 0x0fb8bee0, 0xf49d9c}}, + {{0x114d9972, 0x1114558c, 0x135ea0e4, 0x0002789c, 0x0f67901b, 0x09d9dcca, 0x12b9ab97, 0x08407c75, 0xf5585d}}}, + /* 9*16^47*G: */ + {{{0x1b219a79, 0x19d4b3bf, 0x0609e9de, 0x19a882fd, 0x189e65c4, 0x01aabaa8, 0x1522c38f, 0x007c8d53, 0x28b04d}}, + {{0x102dbe24, 0x05fbe6c8, 0x0012f97e, 0x0cd99f0c, 0x0206b861, 0x12b90c1f, 0x1c51673f, 0x13cb4299, 0xf658da}}}, + /* 11*16^47*G: */ + {{{0x17774af9, 0x14116ce2, 0x1182b62c, 0x0d74ca22, 0x0efb54c1, 0x01c94435, 0x1a4f5a14, 0x17cf983f, 0x3f4766}}, + {{0x1d4de990, 0x1e6bfb26, 0x0dcc9bfd, 0x1299fbf3, 0x05f511ca, 0x1c483737, 0x12e8eac5, 0x1ad4e663, 0xcc810d}}}, + /* 13*16^47*G: */ + {{{0x08e57705, 0x020cf8f2, 0x1356639e, 0x1d6ff590, 0x1361721c, 0x0d5a0eb7, 0x19e47cab, 0x00581f1d, 0xe3249e}}, + {{0x18caec0c, 0x0a58cf41, 0x1ce10882, 0x128bae2c, 0x06b5a501, 0x1c60f924, 0x141f72dd, 0x10e026d2, 0xa66665}}}, + /* 15*16^47*G: */ + {{{0x125b93ac, 0x1c5e757d, 0x01fe34b1, 0x0404a20b, 0x1b4e917e, 0x13b49efd, 0x1872f7e7, 0x017bf6f2, 0xa68958}}, + {{0x0f589aab, 0x11e8e26b, 0x113f4eba, 0x03f02fb3, 0x19ff2fcf, 0x1780af82, 0x00faa9fc, 0x12969e0f, 0x4657ca}}} + }, + { + /* 1*16^48*G: */ + {{{0x113728be, 0x07907fd9, 0x11ae529b, 0x072b2347, 0x15fc5964, 0x1fc1a218, 0x09d89cdb, 0x0ef4f092, 0xa6d396}}, + {{0x0357f5f4, 0x15d5c19e, 0x010166fc, 0x15241845, 0x1ecdf824, 0x1d5e9693, 0x00599ae2, 0x0e936171, 0x674f84}}}, + /* 3*16^48*G: */ + {{{0x0f15d864, 0x02a5ab4e, 0x13234f30, 0x0dfb8d43, 0x0cf35240, 0x1673df13, 0x0c36bf23, 0x1af8bbdb, 0x7ef66}}, + {{0x140907fe, 0x0312a13b, 0x1392a2d5, 0x0e1c7639, 0x1505e9f4, 0x062910fe, 0x1a941b50, 0x0bb713bc, 0x2db332}}}, + /* 5*16^48*G: */ + {{{0x17c7c05e, 0x14a8a1e0, 0x19505ab1, 0x07972a59, 0x08c0bb28, 0x1397baf9, 0x118053ce, 0x0bf80db8, 0x82df4e}}, + {{0x18fff26b, 0x0b09e816, 0x0b6cc02e, 0x0bb28d08, 0x1cbd1cf1, 0x0b10890b, 0x08289d48, 0x193192c8, 0xe4e188}}}, + /* 7*16^48*G: */ + {{{0x140368b0, 0x0570ea0a, 0x1ba52760, 0x09845f1f, 0x07a62132, 0x0dc69c72, 0x11a9f679, 0x13782561, 0x261efc}}, + {{0x1deb011f, 0x1d692acd, 0x06d74c04, 0x0817c3c7, 0x1ff797e3, 0x02966b27, 0x13a7f722, 0x1b7c70df, 0x8a9d2a}}}, + /* 9*16^48*G: */ + {{{0x15eb8034, 0x0819f4a8, 0x1a3292bc, 0x1666bead, 0x1692df30, 0x1f2cecf3, 0x1e4526a9, 0x1aef4584, 0x6d48df}}, + {{0x1ab0ce30, 0x039843d2, 0x0fa2587a, 0x0421d454, 0x14763080, 0x1cb24f02, 0x04bcf579, 0x08a2cbba, 0x3cb472}}}, + /* 11*16^48*G: */ + {{{0x140535c8, 0x08a1efe2, 0x036c4fad, 0x014ac619, 0x14e6f65f, 0x11fda7e2, 0x048e9244, 0x03cf7731, 0x93a5c0}}, + {{0x1d59b844, 0x04aba041, 0x16fb7ff1, 0x02c40926, 0x1a5c166a, 0x021ac70a, 0x0bd305aa, 0x12093018, 0x2d440e}}}, + /* 13*16^48*G: */ + {{{0x1e2047ba, 0x130d2b34, 0x0c3d94a8, 0x0e0932d7, 0x07031e54, 0x10700beb, 0x0aeecd76, 0x0522c24e, 0x3fb0b9}}, + {{0x1db24158, 0x1ff66a76, 0x1c0274d5, 0x0415cee2, 0x06dc86c4, 0x110e4cb3, 0x1e5329c9, 0x1cd042fb, 0x9d467a}}}, + /* 15*16^48*G: */ + {{{0x02df71c1, 0x05ededd7, 0x0edc8e80, 0x030d7d5f, 0x1e4381c3, 0x1dd4ef19, 0x0f5741d8, 0x073c11d0, 0xdab094}}, + {{0x04c3a1e3, 0x039a4209, 0x0d138eee, 0x0c661949, 0x00b3d6e9, 0x14379069, 0x13bce16b, 0x03ca89c3, 0x763cbc}}} + }, + { + /* 1*16^49*G: */ + {{{0x0514c6fd, 0x177b17fa, 0x0ac04f9b, 0x10769a1b, 0x15936fd6, 0x0dab887f, 0x1380cf53, 0x1001139e, 0xac25da}}, + {{0x05830541, 0x05a9cbb8, 0x0efcde98, 0x1307d048, 0x1338c810, 0x1498950d, 0x11ee20f6, 0x130b9689, 0xebc69d}}}, + /* 3*16^49*G: */ + {{{0x12fd0e3d, 0x0ca0e3f1, 0x09eb5820, 0x03c9b8a8, 0x05547e63, 0x04338f0b, 0x122ed35d, 0x0d893747, 0xeea7e6}}, + {{0x191d5868, 0x0208fb46, 0x0678f304, 0x175b4460, 0x17d985ac, 0x1f93df4d, 0x0984a210, 0x0b73f112, 0x83fed4}}}, + /* 5*16^49*G: */ + {{{0x0ccb63b3, 0x0cf8a793, 0x10239744, 0x1ffcd888, 0x1c67b7dd, 0x0a59aa01, 0x06f6eb46, 0x1a7d27c4, 0x9a3de2}}, + {{0x1df93fcf, 0x0e2994bb, 0x16089800, 0x003b3fde, 0x13c4c49c, 0x139b7740, 0x0b22027c, 0x120c2222, 0x2f809b}}}, + /* 7*16^49*G: */ + {{{0x163f1117, 0x07651a6e, 0x1fdb62f9, 0x12dee174, 0x1adaf348, 0x0698091f, 0x0b6c1440, 0x1e96a772, 0xa7c382}}, + {{0x1934cb9a, 0x091062f0, 0x1129a330, 0x1d6edda7, 0x1cfb5ae4, 0x1bbbb82e, 0x1e201167, 0x022cec37, 0x1b91e5}}}, + /* 9*16^49*G: */ + {{{0x06774a96, 0x05e40e2d, 0x0e38fa14, 0x063fb230, 0x0f497e82, 0x1dfe41d3, 0x084e1c50, 0x1319b0c2, 0x555aa9}}, + {{0x06db0e34, 0x0a04b96c, 0x189be9a7, 0x1aba9791, 0x0bbb89a0, 0x00f389a8, 0x11a66751, 0x0d1a40f7, 0xb05328}}}, + /* 11*16^49*G: */ + {{{0x0f7dac7d, 0x17bf779d, 0x0904848c, 0x0e572422, 0x1c369165, 0x0bc7c6bb, 0x0b5ed633, 0x0b66914f, 0x1a42f}}, + {{0x0195e46e, 0x1c4a518c, 0x13aa5ac9, 0x15e52651, 0x19216172, 0x1caa5c5f, 0x1e04d25f, 0x070aa40e, 0x957a9c}}}, + /* 13*16^49*G: */ + {{{0x002afc36, 0x015d0ea1, 0x0c1c74f8, 0x1bddaa28, 0x1d3a3134, 0x04f78da2, 0x18f4e96c, 0x06fd60b9, 0x4b47f5}}, + {{0x0f8133ff, 0x144fbb53, 0x17ef68d3, 0x1597d364, 0x1f573345, 0x037d0746, 0x1c30b72c, 0x0073390b, 0xf2fc45}}}, + /* 15*16^49*G: */ + {{{0x17d17f68, 0x15f971e3, 0x02eb61aa, 0x0b43bf97, 0x0418f791, 0x0b7a9b57, 0x033b5594, 0x1398a49d, 0x6b3dec}}, + {{0x09232402, 0x1d73d106, 0x1732da33, 0x0552d54d, 0x15d4747f, 0x00da0b66, 0x07bc1426, 0x06ffbdfb, 0xb86539}}} + }, + { + /* 1*16^50*G: */ + {{{0x0fd20bae, 0x0ea71bd1, 0x0d2a0455, 0x06ace5ab, 0x1343a260, 0x1d090bb6, 0x136409ee, 0x1db8779f, 0x285250}}, + {{0x14e0ab97, 0x0ef22ad2, 0x1bfcc8fe, 0x163459e3, 0x0c1716e9, 0x02568823, 0x1aa0fdca, 0x10de95af, 0x7866c0}}}, + /* 3*16^50*G: */ + {{{0x0636a50b, 0x0443b55d, 0x0dd465b3, 0x1dec2d57, 0x0baf65d2, 0x1d097e3c, 0x1d7160db, 0x0ca8bab4, 0xf1e3c5}}, + {{0x0b3128b6, 0x1bbc8a75, 0x0b5e9bb7, 0x1f4aeda4, 0x1f3136b7, 0x1533fb52, 0x139db1cd, 0x0f4dc3df, 0x12b884}}}, + /* 5*16^50*G: */ + {{{0x0e583340, 0x0bf8990a, 0x0d3cec94, 0x1836d6ba, 0x1228cf45, 0x06d5fd4d, 0x129db61f, 0x13903c26, 0x584d72}}, + {{0x14fb24b9, 0x17b72f4b, 0x05301c1c, 0x0ee14cc9, 0x0affa8f1, 0x1e2c9818, 0x02af34c1, 0x148ac1b0, 0x2fdd80}}}, + /* 7*16^50*G: */ + {{{0x0235809b, 0x1641f6f0, 0x1ae05ce4, 0x0e5be16b, 0x03c453c5, 0x0146e11c, 0x1df478b8, 0x001906fb, 0xbaaeae}}, + {{0x0154fd62, 0x0b0ec52e, 0x14b9f973, 0x18788543, 0x1f299835, 0x183de5a4, 0x0e02d288, 0x1067e649, 0x325788}}}, + /* 9*16^50*G: */ + {{{0x0d612268, 0x10021620, 0x17b405bd, 0x1eb3be14, 0x0b8b906c, 0x0f7d21ca, 0x0c69944e, 0x0c6c1842, 0x6c7e4}}, + {{0x060166a0, 0x05a5b009, 0x0b9c262f, 0x1b14b4f0, 0x053ca238, 0x03ae717a, 0x0335d1ff, 0x0bbee5bb, 0xcb6ad5}}}, + /* 11*16^50*G: */ + {{{0x012fbdc8, 0x0a1d1adc, 0x1038a8ef, 0x1c419545, 0x1a36db89, 0x1663db88, 0x10f96f0b, 0x1bd57acc, 0x64131}}, + {{0x09f99380, 0x09ff984d, 0x1ec08297, 0x15c4d163, 0x17598603, 0x006c9a4a, 0x00a3cace, 0x15865ace, 0x882c7f}}}, + /* 13*16^50*G: */ + {{{0x0bac9f32, 0x0f580032, 0x1a26c19d, 0x104398b2, 0x16400443, 0x00b7f0cd, 0x08de2859, 0x15984eb8, 0x366bb7}}, + {{0x1c85d47d, 0x17872e7d, 0x1c09a290, 0x19ca180f, 0x1cfc01fd, 0x01d5c6b0, 0x1c193c1e, 0x0e10f0b5, 0x7d107b}}}, + /* 15*16^50*G: */ + {{{0x06dd27eb, 0x1e5a9294, 0x0ed588c0, 0x18ab86f6, 0x032ecb98, 0x0871cddf, 0x1001ac9f, 0x04af98e7, 0xb767db}}, + {{0x0f65dac4, 0x0a90d23e, 0x1803b505, 0x0e016890, 0x1d85b64b, 0x05c0b5cc, 0x072d73ab, 0x1864c245, 0xdc1308}}} + }, + { + /* 1*16^51*G: */ + {{{0x1e07e4f1, 0x18f9d151, 0x17099f05, 0x0a28fba1, 0x0890e1fc, 0x15e03f7c, 0x19be0637, 0x00c5da06, 0xc472c1}}, + {{0x06b0daa9, 0x18e341ec, 0x0ad76295, 0x076f63c4, 0x067d885f, 0x15ad426b, 0x03a590ca, 0x00328bee, 0x41820e}}}, + /* 3*16^51*G: */ + {{{0x02f8acb1, 0x1b4a9a55, 0x0fc14fd0, 0x1acaa8c2, 0x0c31eb39, 0x1bd25c76, 0x0f5a1786, 0x0732a8bc, 0x86f88f}}, + {{0x05098a19, 0x05821688, 0x05949c45, 0x14c57dd2, 0x1b1214ee, 0x0bc7dac8, 0x035acdf2, 0x0d34d047, 0x2ccfdd}}}, + /* 5*16^51*G: */ + {{{0x1cadab66, 0x041bbdb7, 0x14d580b6, 0x0d46787b, 0x1f9a0e1c, 0x134b306c, 0x19d1f7dd, 0x03f93291, 0xa2a3be}}, + {{0x0c1aeb12, 0x1a509242, 0x002b497b, 0x1bd14a69, 0x15ee1fb2, 0x1f6ba691, 0x199c1941, 0x1cd6a497, 0xa93bd8}}}, + /* 7*16^51*G: */ + {{{0x007c25bf, 0x04e91099, 0x0b6025ee, 0x178eef7c, 0x080be82f, 0x09233be0, 0x16e4d9f8, 0x1daadcf1, 0x354e7c}}, + {{0x1a864211, 0x091d1cca, 0x082a8854, 0x0ead960b, 0x1e5585fd, 0x0c1252e7, 0x01c39baf, 0x1d377328, 0x629d62}}}, + /* 9*16^51*G: */ + {{{0x15e1dad8, 0x13ec1703, 0x112f4b92, 0x014f0b0d, 0x19f498b8, 0x07b90604, 0x016de6d7, 0x155ebbd0, 0xc751c9}}, + {{0x0e4afc8b, 0x09b59b09, 0x02a300b4, 0x112e1474, 0x1a4ffe6f, 0x00feb6e4, 0x178afad3, 0x06ff8f33, 0xcb9864}}}, + /* 11*16^51*G: */ + {{{0x099d9ba2, 0x1890d908, 0x0aa9d5f4, 0x0cafad2f, 0x19ca80cf, 0x09435a87, 0x0085e2ad, 0x08373a3a, 0xaee9cc}}, + {{0x1ed2ad58, 0x06b0241d, 0x05d2439e, 0x1cd1c4d4, 0x0b1f5312, 0x1dfd8063, 0x0dbdefa5, 0x005b6a54, 0xb72cf3}}}, + /* 13*16^51*G: */ + {{{0x094d208d, 0x1ff84124, 0x1ce46918, 0x1b51fe1f, 0x1f0cefb0, 0x1057958f, 0x1b15affd, 0x08d4f225, 0xd0d3f}}, + {{0x1d45d1b1, 0x117a9fbc, 0x0776aff5, 0x0781a34e, 0x09ff0b21, 0x06d32fc8, 0x05a6c11d, 0x1015c3ee, 0xdc371b}}}, + /* 15*16^51*G: */ + {{{0x19cfd890, 0x17411640, 0x01c45171, 0x0d86e6f8, 0x041e1cb3, 0x0c1c3f2a, 0x14d7d1f3, 0x0d68826e, 0x778d14}}, + {{0x16f057f2, 0x0d43fd3e, 0x1482b2b2, 0x148a911d, 0x0cd3796c, 0x18c6cbc3, 0x0bdca949, 0x02676023, 0x3c85ba}}} + }, + { + /* 1*16^52*G: */ + {{{0x039bb85f, 0x10784e1c, 0x14398b0c, 0x03f60d40, 0x13458010, 0x0164cd6a, 0x0cad55d6, 0x11a2ccc8, 0x55d539}}, + {{0x0fed936f, 0x1fb188c2, 0x0cf6787d, 0x1ad4fe30, 0x0a72ad90, 0x154f475d, 0x18b41671, 0x12093ff1, 0x576e22}}}, + /* 3*16^52*G: */ + {{{0x12ea285a, 0x03f15c90, 0x0e443470, 0x0383cdef, 0x0a7a39f0, 0x02c6eee0, 0x022de160, 0x011029f1, 0x1bb464}}, + {{0x107dc702, 0x006453cc, 0x0bea53b7, 0x05469732, 0x0a4fc1e5, 0x0cdc495a, 0x0496903f, 0x17f5f5c2, 0x8a3016}}}, + /* 5*16^52*G: */ + {{{0x1539785a, 0x08094806, 0x0a5fc051, 0x11a0ed70, 0x07a548c8, 0x1f133c31, 0x053b825c, 0x0e1c05f4, 0x4bc4e}}, + {{0x0a62e3bb, 0x1cc823ae, 0x0c9d70d4, 0x002ee4f4, 0x1fb4f877, 0x10e79f9e, 0x130e97d6, 0x04971969, 0x5729f2}}}, + /* 7*16^52*G: */ + {{{0x0114cfb5, 0x096a5d8f, 0x1569a8c4, 0x0c313547, 0x10876d13, 0x1cf0dbef, 0x05cd61dc, 0x1fb83f10, 0xd3e492}}, + {{0x097f7045, 0x075cd52b, 0x1d53bbef, 0x11e0dc8c, 0x1fea39c8, 0x133b5f8a, 0x122c7fb8, 0x014e7f18, 0x3604a7}}}, + /* 9*16^52*G: */ + {{{0x0c55ddff, 0x1133a5dd, 0x152de1c6, 0x1a367ec9, 0x1c1791da, 0x091887e6, 0x094e2939, 0x0ab0c508, 0xbebfc2}}, + {{0x1ea2f303, 0x0a6d8651, 0x02ea9c1b, 0x15045aca, 0x0576c3cc, 0x08e25bbb, 0x133a28e8, 0x0cb812c0, 0x850de2}}}, + /* 11*16^52*G: */ + {{{0x099ead51, 0x0d7a0f26, 0x164893a4, 0x06a87443, 0x184715da, 0x098e8b03, 0x1580beca, 0x1b8e7a41, 0x36e984}}, + {{0x0e0e5e3e, 0x17dcea13, 0x10a02bf1, 0x177d82e0, 0x12b203ba, 0x0fe4d671, 0x017d2fef, 0x08e437e9, 0x143771}}}, + /* 13*16^52*G: */ + {{{0x03093df0, 0x145bbdd9, 0x1d91935e, 0x1c4902ce, 0x193af785, 0x0a3caa6b, 0x12bbcc54, 0x00087ec0, 0x91b675}}, + {{0x12cc9f14, 0x06f461be, 0x12bd9fd7, 0x0d0bf1f3, 0x01c5d933, 0x1d6d4b71, 0x00351df1, 0x03cb0494, 0x7fedb}}}, + /* 15*16^52*G: */ + {{{0x1b7b8dc4, 0x11b3dc37, 0x06ce1228, 0x09260515, 0x02fac5b3, 0x1f0d01b0, 0x00f9f125, 0x18f43891, 0xc5d9c3}}, + {{0x1b2df0ea, 0x0b24bc68, 0x1f524dbb, 0x11ee5fa3, 0x10af3d0d, 0x01c302e2, 0x1e796023, 0x1c4b9fb6, 0x3e59b9}}} + }, + { + /* 1*16^53*G: */ + {{{0x12513926, 0x116de4e1, 0x1b217b31, 0x0d1281df, 0x03383cc2, 0x1d5925fe, 0x1c9a53fa, 0x08c79410, 0x884286}}, + {{0x0fb8a54d, 0x17214155, 0x16f05683, 0x1d6245d7, 0x01207ce0, 0x06d9b2ec, 0x1569a598, 0x1380385b, 0xf77842}}}, + /* 3*16^53*G: */ + {{{0x0520f1f1, 0x095d60cb, 0x1fdf693b, 0x03693cc8, 0x16f4ad7f, 0x1e8b4667, 0x00675697, 0x06deede1, 0x3bad9d}}, + {{0x1698df4d, 0x10cb8392, 0x0d9c961a, 0x1303449d, 0x00e80499, 0x1d3ecef6, 0x1966f367, 0x1c67c49c, 0x49f99d}}}, + /* 5*16^53*G: */ + {{{0x1185ae66, 0x18f22545, 0x09e0f7c2, 0x182db7df, 0x0118ba6e, 0x0fa9e892, 0x11b59e9f, 0x1635b618, 0xa6b3a4}}, + {{0x0bfbf21b, 0x1f5aa9fc, 0x0c92375e, 0x1e14bbeb, 0x1e8d8bcb, 0x148e6cb4, 0x188c4d86, 0x1aec7112, 0xcb6e8}}}, + /* 7*16^53*G: */ + {{{0x0c630e3a, 0x0b426727, 0x1cfc70aa, 0x00c182ff, 0x110c1f61, 0x11d3ec3f, 0x1293889f, 0x0b4d9222, 0x1f848f}}, + {{0x06ca610a, 0x176cffb4, 0x0d65c92b, 0x0724d7fc, 0x1a4264ba, 0x041e8d4e, 0x058e18a7, 0x0599a0df, 0xa02855}}}, + /* 9*16^53*G: */ + {{{0x041bcd10, 0x0ff79e6e, 0x0d138553, 0x12aaec8b, 0x02eb211b, 0x133be365, 0x0d622c68, 0x097d2938, 0xcfd1e5}}, + {{0x0a62e732, 0x000af5cd, 0x154e596b, 0x1322ba10, 0x12fafacd, 0x08dead82, 0x02ee715d, 0x0bec012b, 0xf4e31f}}}, + /* 11*16^53*G: */ + {{{0x028da5fb, 0x1d30ec91, 0x01c39c93, 0x097ebc55, 0x1291b90e, 0x0af1f3b8, 0x19544b1e, 0x01808d93, 0x4b3e73}}, + {{0x1c169e48, 0x1f8531c5, 0x08d1caef, 0x07423938, 0x0b48c65a, 0x1d03dc70, 0x03a7a032, 0x09b446cc, 0x8c4096}}}, + /* 13*16^53*G: */ + {{{0x1f894780, 0x0748d2c4, 0x06bb176e, 0x139946b1, 0x0d8f737e, 0x1c52a5a1, 0x1f9c7552, 0x1bee8871, 0xafe915}}, + {{0x1511d444, 0x1cb4aa9e, 0x102bd14b, 0x1d17ce75, 0x12e36909, 0x01a1199f, 0x1a1aa52d, 0x0811a408, 0x3caec3}}}, + /* 15*16^53*G: */ + {{{0x1182fa6e, 0x0d18fe5e, 0x00722bfd, 0x0c65bad3, 0x1c9e7c0a, 0x11d1b69a, 0x1215619a, 0x05021ff5, 0xc0548}}, + {{0x061cd145, 0x15e7f55b, 0x1db407db, 0x0cc8b096, 0x1602bf5e, 0x05c4a32e, 0x14cf46aa, 0x195a1e3e, 0x2f9cd2}}} + }, + { + /* 1*16^54*G: */ + {{{0x14441fbb, 0x105aead6, 0x1b44578f, 0x150a414b, 0x125559ea, 0x062af6dc, 0x0751fed8, 0x09c43bc4, 0xb4958c}}, + {{0x0add7118, 0x03f1e4fa, 0x118e1053, 0x13ec265a, 0x0b7e12db, 0x00fde1c6, 0x03bf9701, 0x17f32cc8, 0xfcd6e7}}}, + /* 3*16^54*G: */ + {{{0x171f07f1, 0x176b4261, 0x15c31296, 0x19db6e61, 0x0684b878, 0x105b2303, 0x11348cba, 0x0bcf6408, 0xb3a43f}}, + {{0x001dfda5, 0x1e6917a7, 0x12c3b314, 0x10ef419b, 0x15ee7ec7, 0x1139b4cb, 0x1ae1060c, 0x0b6491fa, 0x34c64f}}}, + /* 5*16^54*G: */ + {{{0x0b559d49, 0x1be4bd23, 0x1726d13f, 0x0368d21b, 0x008b148e, 0x17fec260, 0x052b0998, 0x0d348d7c, 0x452e98}}, + {{0x1a0d6ba7, 0x03e30236, 0x07b9095d, 0x050ad876, 0x1ee52598, 0x02bdb3b0, 0x0343d700, 0x11e32bf0, 0x8c5e8a}}}, + /* 7*16^54*G: */ + {{{0x13ce94da, 0x1bd33f5a, 0x00a79814, 0x049e84ad, 0x074ee8bb, 0x106e1d62, 0x0aed4737, 0x1a918dac, 0x19a7f5}}, + {{0x1025e7bb, 0x182238b1, 0x097ac0dc, 0x04a60d5c, 0x0a2e8fb6, 0x08ea2100, 0x170cbda9, 0x14f5260e, 0xa2504b}}}, + /* 9*16^54*G: */ + {{{0x147dc697, 0x0b4b6636, 0x1856e6ee, 0x1c315f6b, 0x06fa417e, 0x18595afe, 0x1370047a, 0x004149e6, 0xbdaf5b}}, + {{0x06a479e2, 0x088e5f3c, 0x0de91dee, 0x045cf10b, 0x1aa08551, 0x1af23dab, 0x0db233d5, 0x0d97bf50, 0x3cec0c}}}, + /* 11*16^54*G: */ + {{{0x0ce05e10, 0x1005c8db, 0x1841880a, 0x1ef93e87, 0x070db8ae, 0x1bff4267, 0x0576ae12, 0x1d2be73a, 0x265dba}}, + {{0x171324f4, 0x1bf80c58, 0x19319fff, 0x07e7267a, 0x15dcb066, 0x0745ab65, 0x1eb7cecf, 0x1a134606, 0x58df63}}}, + /* 13*16^54*G: */ + {{{0x13fe5938, 0x08ade2ad, 0x0db5f37b, 0x10607b6c, 0x068669ec, 0x04ea9d2b, 0x0e5a27dd, 0x07e2fccc, 0xc43e08}}, + {{0x183732f3, 0x13cbab76, 0x1101d1dd, 0x0460e2c4, 0x0402eab6, 0x0181d5e2, 0x1160d424, 0x12473f79, 0x54c602}}}, + /* 15*16^54*G: */ + {{{0x1dd52975, 0x18e387bc, 0x0d106030, 0x1e1fa60d, 0x066ba2bc, 0x086d3bd9, 0x121996b1, 0x0e0147f0, 0x7868f1}}, + {{0x1c626fac, 0x00b76a81, 0x05a4110c, 0x0df3d94d, 0x1276c68f, 0x10e36d88, 0x1b8444fe, 0x19e6242f, 0xe89097}}} + }, + { + /* 1*16^55*G: */ + {{{0x0e12e1df, 0x0127321b, 0x1d87412b, 0x0ffa16fa, 0x0027cd8a, 0x1f89d9a3, 0x0ad904d2, 0x12d11d26, 0xd0e091}}, + {{0x1fd28fbe, 0x132a26dc, 0x11ae37da, 0x19897b30, 0x1f867544, 0x105b48ed, 0x114ad3ad, 0x0b3fcfa2, 0x69c9a}}}, + /* 3*16^55*G: */ + {{{0x084aa098, 0x186c2880, 0x1b8f80ae, 0x02028152, 0x1fa8509c, 0x1ed65fe0, 0x03ace629, 0x0a942661, 0xb517a4}}, + {{0x0540efbf, 0x0025acfa, 0x0911ff58, 0x0916a8d2, 0x06fa3a4d, 0x1f17d879, 0x1e6983a8, 0x0fa183f0, 0xa3d87}}}, + /* 5*16^55*G: */ + {{{0x0744bfa1, 0x0cad6552, 0x04d90f5b, 0x0da4f9c1, 0x1e387cc2, 0x13896c79, 0x1bd9ef08, 0x07096a2c, 0xf8ec14}}, + {{0x12b65f6d, 0x14927319, 0x04001831, 0x06f58b87, 0x00f610a6, 0x07d934eb, 0x0698c8da, 0x164227f7, 0x761134}}}, + /* 7*16^55*G: */ + {{{0x1227a4bb, 0x1161df49, 0x03667cbd, 0x0d63e01f, 0x0f2e64be, 0x075690ea, 0x0b9e539d, 0x0f1b6f7f, 0x320cff}}, + {{0x10f3d2d4, 0x00e64835, 0x18be5c16, 0x0e46e813, 0x16299604, 0x0b512a7f, 0x1a4aadde, 0x1a80e550, 0xaf9fe8}}}, + /* 9*16^55*G: */ + {{{0x1c2ca683, 0x1adad2f2, 0x0569cdce, 0x19e6bc15, 0x1426a206, 0x0ee65aa1, 0x16145fb7, 0x0f8d4f5d, 0xc08de}}, + {{0x1db5f259, 0x12036dab, 0x1a9a31a4, 0x11af6fc1, 0x00e79c3c, 0x14ce6fe7, 0x1866df20, 0x10abd42d, 0xddb76d}}}, + /* 11*16^55*G: */ + {{{0x052ae5cd, 0x033d67c1, 0x1f75e187, 0x0ca5f5e9, 0x0390995b, 0x1bd22672, 0x10f4639b, 0x0d5a188f, 0xd1f8c7}}, + {{0x1e6d2dda, 0x15cbde1f, 0x027d3f1f, 0x15d02ad3, 0x1203239b, 0x0bd80fb0, 0x000ab1e6, 0x18cc241d, 0x74d45d}}}, + /* 13*16^55*G: */ + {{{0x0bdc603f, 0x1c803355, 0x17ff96ad, 0x1acb9acf, 0x020d8c96, 0x1f63133b, 0x03024f8c, 0x0d27e712, 0xa6cb83}}, + {{0x096befcc, 0x16701f06, 0x1985cd72, 0x1d82d498, 0x10b72fb1, 0x0ded2628, 0x0bf23cb6, 0x1c8c3e79, 0xd823c8}}}, + /* 15*16^55*G: */ + {{{0x02c374b0, 0x0f1d3097, 0x1c36d28a, 0x166b316a, 0x04ef0bf5, 0x04b8a921, 0x0c84dafb, 0x123d4d86, 0x8a6c9c}}, + {{0x178c08bd, 0x1fbe7c6d, 0x03d3560e, 0x0a69e868, 0x132a0461, 0x042ee480, 0x1ebde69e, 0x09ecb9bf, 0xe4bc7f}}} + }, + { + /* 1*16^56*G: */ + {{{0x0895df07, 0x1381f887, 0x01daf61a, 0x0be7f403, 0x08ffefd7, 0x03738670, 0x1fbbad6c, 0x0a84f07b, 0x68f6b8}}, + {{0x18712655, 0x063b7c53, 0x042fdfe4, 0x0527a5e6, 0x05028cf5, 0x0226fed2, 0x139bef20, 0x17525c81, 0xcbe1fe}}}, + /* 3*16^56*G: */ + {{{0x1aa89692, 0x00c7d119, 0x1308c239, 0x1611adf2, 0x0a776713, 0x1320920c, 0x1d37a65b, 0x0e302cd1, 0x3bdfca}}, + {{0x1a510308, 0x1ededa18, 0x18bbc4e4, 0x1818c68b, 0x05333c7d, 0x10a8d76d, 0x1ee12509, 0x0d0aca2c, 0xa721f}}}, + /* 5*16^56*G: */ + {{{0x037ea1d7, 0x1cd91a16, 0x06ab9341, 0x126cb1f1, 0x19e4fb23, 0x02a928c6, 0x0158d4ca, 0x12b6ea42, 0xaca8ac}}, + {{0x1ba973a2, 0x0f7d8824, 0x0e4d7a77, 0x1b7fb882, 0x12189e1e, 0x0625c943, 0x108250c1, 0x0cfbaeb9, 0x7f12c5}}}, + /* 7*16^56*G: */ + {{{0x13245b7f, 0x0d93c0e6, 0x165fefd7, 0x1acd2d20, 0x05311a37, 0x10d7cc6c, 0x103881b0, 0x009b6ccf, 0xfa9047}}, + {{0x0ddf6bef, 0x1c19ef8a, 0x13c751fb, 0x1dd9a4c4, 0x1c189cb9, 0x11f6a078, 0x1a90b5be, 0x06ce9506, 0x1a1c8c}}}, + /* 9*16^56*G: */ + {{{0x17f0953a, 0x10fbc7eb, 0x0550e6ad, 0x122907fe, 0x092a8184, 0x1c70e97d, 0x163359ca, 0x15723eaf, 0x1d431a}}, + {{0x1d68f260, 0x1e2cebb4, 0x1c1f9f05, 0x1c535210, 0x1fae0f48, 0x0641e70a, 0x087af14c, 0x1877e322, 0x8d667}}}, + /* 11*16^56*G: */ + {{{0x05227c70, 0x1188b89b, 0x08fa9c16, 0x17d56667, 0x1c60ff32, 0x19ad9718, 0x04df7692, 0x01ab47c2, 0x6deeea}}, + {{0x0369b9b7, 0x0b6d1c08, 0x0420f6eb, 0x15d83cad, 0x0cc84287, 0x05d7f7b5, 0x19000053, 0x01f8e887, 0xcbb93f}}}, + /* 13*16^56*G: */ + {{{0x0421b54d, 0x1afeaf44, 0x159293fe, 0x1657074a, 0x09dca579, 0x0e95d8fd, 0x036352b2, 0x020c6aaf, 0x28135c}}, + {{0x0ee5d868, 0x0e6784dc, 0x18c4362b, 0x09299923, 0x18c15ef0, 0x0eba083f, 0x18541bea, 0x17c70a37, 0x9ed84a}}}, + /* 15*16^56*G: */ + {{{0x08e4ac10, 0x0bf2ac8f, 0x1892a6a4, 0x0d502559, 0x1b568799, 0x062d04ff, 0x1def5b0f, 0x0ec41620, 0x339a05}}, + {{0x0d1312a0, 0x1b6d4322, 0x07319bbd, 0x13cf11e8, 0x1553e503, 0x06fbc567, 0x11fd0e15, 0x17dbad52, 0xca985f}}} + }, + { + /* 1*16^57*G: */ + {{{0x07ea1f34, 0x1a6d58bb, 0x1ce78374, 0x17a6a808, 0x153d6646, 0x0e8bc9d9, 0x0d9b0ed0, 0x1447a8e9, 0x7f9460}}, + {{0x0d9063ec, 0x05f61656, 0x18d3ff16, 0x1f02a249, 0x16661c6f, 0x195fc783, 0x061da7c7, 0x0ef1f60c, 0xd0d516}}}, + /* 3*16^57*G: */ + {{{0x1dfc0a9d, 0x02dba64d, 0x1ddea837, 0x0846b629, 0x0a2a2de4, 0x11b23570, 0x1808a983, 0x1f098653, 0xdb43e7}}, + {{0x1e9ad713, 0x058849d4, 0x14bc153c, 0x1208e6ad, 0x19fa4883, 0x1640a677, 0x1646e295, 0x1457c6d6, 0xb0168d}}}, + /* 5*16^57*G: */ + {{{0x0283808d, 0x05e58bba, 0x190ea24c, 0x1e0f9c6a, 0x078980f8, 0x0f4890fd, 0x06bae145, 0x103dc1a4, 0xe9af30}}, + {{0x19b39d33, 0x13617c71, 0x04db4665, 0x1724a22a, 0x14971976, 0x132a87f7, 0x098216bc, 0x091388e2, 0xa976c8}}}, + /* 7*16^57*G: */ + {{{0x0d8426df, 0x1497a165, 0x1be28289, 0x0272b49c, 0x083590f4, 0x049c1dfc, 0x0601c0eb, 0x0c65900d, 0x6eb733}}, + {{0x000b4267, 0x1fbacf71, 0x1f86e0cf, 0x05d2f907, 0x08e8d925, 0x05967660, 0x0a680c39, 0x1822e60f, 0x96c56e}}}, + /* 9*16^57*G: */ + {{{0x1a16453d, 0x1e8b6be0, 0x040cf6fd, 0x1d0f7cec, 0x0d0e68b7, 0x0dc8fc5a, 0x1d1785d0, 0x0bc0f3ab, 0xd3979d}}, + {{0x07b6d737, 0x1e5dc18b, 0x1a58693c, 0x1becc514, 0x0456917d, 0x092ba9b9, 0x16e69342, 0x06baf335, 0xc55f93}}}, + /* 11*16^57*G: */ + {{{0x0d326504, 0x150ccd2f, 0x0d480b33, 0x15368c4f, 0x0fc12f65, 0x1dc460d7, 0x08d0b0e0, 0x139cb718, 0x54c392}}, + {{0x1ec9e107, 0x1f808fd3, 0x0299c9a2, 0x0a7b3cc1, 0x0ab4447a, 0x1514248a, 0x1b431226, 0x04ac1d42, 0x469630}}}, + /* 13*16^57*G: */ + {{{0x1b5ecce7, 0x0227abb5, 0x03484d4c, 0x102b1618, 0x059c8732, 0x0741e0cb, 0x1b16e13a, 0x1650ccda, 0x3744e9}}, + {{0x0a247721, 0x00990a0b, 0x09be0e48, 0x116be0a5, 0x1ec3c28e, 0x14ab7594, 0x1ea83839, 0x1f1b3208, 0x9546e0}}}, + /* 15*16^57*G: */ + {{{0x05fef996, 0x064946f2, 0x1094d454, 0x0d1ec728, 0x1f2de140, 0x08520f79, 0x154cc933, 0x02fc6cad, 0x11343e}}, + {{0x1792aded, 0x00a8573a, 0x01bc6390, 0x1c9acb41, 0x13765d0c, 0x0fc98313, 0x1bbac137, 0x101bd751, 0x4a9e84}}} + }, + { + /* 1*16^58*G: */ + {{{0x0e6d8483, 0x164da6bc, 0x059fc373, 0x05af508d, 0x0e94582f, 0x1a4f8db7, 0x18f4a563, 0x0c1467aa, 0x9c39cb}}, + {{0x0b2c50fb, 0x0599ab7b, 0x0f90da25, 0x1bca5e7b, 0x16546bba, 0x0b5bde50, 0x14400f46, 0x03dc927c, 0xf097bf}}}, + /* 3*16^58*G: */ + {{{0x07a14dda, 0x0ae18fc2, 0x12d37cfd, 0x1a0852ce, 0x13083606, 0x147b9200, 0x0b5d10dd, 0x19921d5b, 0x18f37a}}, + {{0x05ac8364, 0x117e6e19, 0x014db2f8, 0x11ef8f15, 0x1cd0b77d, 0x1ff6770d, 0x109b5eef, 0x17554125, 0x2f944b}}}, + /* 5*16^58*G: */ + {{{0x0c7d59be, 0x13a74746, 0x024cbed5, 0x1430b11c, 0x0736b98e, 0x1ff723b9, 0x07693b17, 0x118503cf, 0x541335}}, + {{0x04f80590, 0x0bf50fb7, 0x002cd9cb, 0x04b62b92, 0x0515a53e, 0x07e900b2, 0x00939f12, 0x1bd2d396, 0x680fd4}}}, + /* 7*16^58*G: */ + {{{0x076051a8, 0x15c064fc, 0x115fa963, 0x0ee72c74, 0x05280bed, 0x00d1e0dc, 0x13c1773f, 0x04d23632, 0x4e2fd1}}, + {{0x09cc9005, 0x17f63a7f, 0x113f8b9a, 0x11754b25, 0x031bcebb, 0x1ad0a845, 0x0ec8dc6c, 0x1b7ebe9f, 0x122abb}}}, + /* 9*16^58*G: */ + {{{0x16a80e09, 0x13e547d2, 0x097d7f8d, 0x0be1eecc, 0x08fa0a27, 0x1ab409e0, 0x0d648013, 0x1a97dfe3, 0x2de758}}, + {{0x036a1cd3, 0x0b176faa, 0x16b5b267, 0x15b8cd2b, 0x064a07a1, 0x1958132f, 0x199f5f00, 0x062efb1d, 0xdd9acf}}}, + /* 11*16^58*G: */ + {{{0x1a3628ac, 0x1281ad97, 0x16d593b0, 0x177459be, 0x012a4568, 0x10b9e377, 0x095ca316, 0x159b83a9, 0xf597a0}}, + {{0x0cd3550f, 0x1501886b, 0x04841b9f, 0x1d9f23a8, 0x02cc0772, 0x1db944b1, 0x1155eec6, 0x11c6657a, 0xdb916}}}, + /* 13*16^58*G: */ + {{{0x0b75d6c3, 0x06b41ea5, 0x0e8a159e, 0x14a8afaa, 0x040f3c42, 0x038c10df, 0x1ee42284, 0x10dade89, 0xaa222a}}, + {{0x044cb028, 0x1932d273, 0x0a323d84, 0x03f9296b, 0x0df42607, 0x0512d771, 0x19db3912, 0x12600351, 0x563787}}}, + /* 15*16^58*G: */ + {{{0x035790c9, 0x1d42b9c9, 0x1cf140df, 0x03722ee7, 0x15e2e9e0, 0x0321c979, 0x16dd5bc3, 0x0d79b2e2, 0x8568b2}}, + {{0x06b1f5ac, 0x09faa9c1, 0x0151e7f7, 0x0bcbf1f7, 0x03ce8014, 0x0705721b, 0x0ab7a41e, 0x034b09ba, 0xd3f226}}} + }, + { + /* 1*16^59*G: */ + {{{0x1c38d4e4, 0x0ed4be2a, 0x0c5cdd1f, 0x1acbe363, 0x01e25e2f, 0x173a180c, 0x04e25d59, 0x04c22453, 0x3d9285}}, + {{0x00ccf0d3, 0x1d577806, 0x1eaf1fdb, 0x15627032, 0x069eacc0, 0x0b81ae14, 0x0ad4ffc1, 0x14ba8d1d, 0xd32ca0}}}, + /* 3*16^59*G: */ + {{{0x0dd7718a, 0x039192c0, 0x0f9393b4, 0x1036ca33, 0x0dff891c, 0x0cd12f3b, 0x1f0fdf05, 0x06e57205, 0x3b180e}}, + {{0x1afd82db, 0x1331314c, 0x0b45341c, 0x1222ace0, 0x1211e584, 0x1e4e9482, 0x02abea92, 0x02fe6d6b, 0xf2b6b1}}}, + /* 5*16^59*G: */ + {{{0x13d3653d, 0x140d288c, 0x1919f57e, 0x1d8964e9, 0x03eb2134, 0x07a04c54, 0x127d17bd, 0x00723ad3, 0xf352ff}}, + {{0x0a44f28e, 0x0f333c64, 0x1160b41e, 0x132bf2e5, 0x0d5055c1, 0x0b9efed3, 0x1af4082f, 0x0d996d8b, 0x447262}}}, + /* 7*16^59*G: */ + {{{0x0d4478da, 0x130ef8d7, 0x03805535, 0x19ed8448, 0x13ca1f15, 0x02e6dfbc, 0x108c2bed, 0x0e2069b1, 0x670ee4}}, + {{0x14f563f0, 0x147ff27f, 0x1c91dc18, 0x07702c1f, 0x150e81cc, 0x102d89c3, 0x0b289519, 0x084b7404, 0x5ca0c7}}}, + /* 9*16^59*G: */ + {{{0x03dbd581, 0x02714822, 0x15acd6eb, 0x1d671051, 0x06fa93cf, 0x1d185676, 0x0f6fbeef, 0x0a8693b3, 0x96f2e3}}, + {{0x08f59952, 0x0cd27ff7, 0x13d75153, 0x031e3aa0, 0x1baf435a, 0x0c71cb06, 0x1b3d3f97, 0x0110baa4, 0x9fcd8}}}, + /* 11*16^59*G: */ + {{{0x04e9c2fc, 0x159a1925, 0x0f5c1a87, 0x19e19e5f, 0x09a35e72, 0x01058a30, 0x06b20106, 0x0a2fd073, 0xc4946e}}, + {{0x06398ce8, 0x01a3b1bb, 0x1188c48d, 0x17e71da2, 0x18237e48, 0x07b47a3d, 0x0933a668, 0x041630b1, 0x748901}}}, + /* 13*16^59*G: */ + {{{0x04960b03, 0x05c8b9f4, 0x0023a3d7, 0x18756191, 0x14d8fb6a, 0x0462bc04, 0x0f6fe923, 0x09b537c6, 0x1653b6}}, + {{0x10f73c69, 0x00352a75, 0x0d5939fe, 0x11c1c943, 0x1a8948b3, 0x15ec1f4a, 0x15827d2c, 0x102d3cba, 0xa58370}}}, + /* 15*16^59*G: */ + {{{0x1b63640e, 0x0abbf18d, 0x11cb7cb1, 0x176fe521, 0x1cbf4979, 0x13ce5342, 0x14fd4031, 0x1afda5e2, 0x51076d}}, + {{0x02e4476c, 0x1b4b943c, 0x083bc087, 0x1d49d3ce, 0x0fda6c8b, 0x1280b970, 0x1ddabaf9, 0x0945adbb, 0xb39727}}} + }, + { + /* 1*16^60*G: */ + {{{0x044e8de3, 0x0318258d, 0x130781d8, 0x112cd45d, 0x117915c0, 0x1ee7845e, 0x02dce969, 0x16e8d102, 0xf50b99}}, + {{0x1b7f3588, 0x11f9dd36, 0x1c87a152, 0x0be31a42, 0x1cebbe97, 0x0b9d16f6, 0x1c321e26, 0x03cabe31, 0xe2b506}}}, + /* 3*16^60*G: */ + {{{0x02accf8b, 0x0ee35b5c, 0x005be9f7, 0x05332305, 0x1430481d, 0x1871289c, 0x1dc1917c, 0x0c34aa0a, 0x598d7f}}, + {{0x0f6cb808, 0x1c2339e0, 0x0d502e46, 0x11351e6a, 0x1ebcad22, 0x08b15939, 0x182551b1, 0x1ee9f1e4, 0x9f3121}}}, + /* 5*16^60*G: */ + {{{0x04aa7b3e, 0x1d9cd2a3, 0x1b273aa7, 0x09de360a, 0x0581013c, 0x1048aa0e, 0x113593f4, 0x025e93e9, 0x715a20}}, + {{0x1c7d081d, 0x0d19ca25, 0x02d1f436, 0x178b1151, 0x13b62421, 0x1447c548, 0x10287de4, 0x16354c0d, 0x5922b3}}}, + /* 7*16^60*G: */ + {{{0x020db220, 0x0dedb4bb, 0x01eb3934, 0x1996202d, 0x07876c71, 0x0744bfdc, 0x04971027, 0x0bcd5536, 0x49ec8c}}, + {{0x077338c2, 0x0dc56503, 0x0ee733a6, 0x1860e7ca, 0x15429842, 0x061a432f, 0x0a6cf6a7, 0x09fcbd4c, 0x99a97d}}}, + /* 9*16^60*G: */ + {{{0x1e771268, 0x0f5e518a, 0x02995a14, 0x1e294fb2, 0x07b7a2f4, 0x0c8702f0, 0x1120f9bc, 0x01a90a16, 0x1f8fb7}}, + {{0x0909c8dd, 0x17e98086, 0x04aceac6, 0x1a786239, 0x192a14e1, 0x16ba3930, 0x0afc4b0b, 0x1b68c374, 0xf53e7a}}}, + /* 11*16^60*G: */ + {{{0x08d9819e, 0x0f607959, 0x0b6ac695, 0x0cf25ee8, 0x0732cd60, 0x0a15d33c, 0x187f7574, 0x034c92fe, 0xb8d5c7}}, + {{0x09138ac4, 0x03c5f475, 0x15170f37, 0x093e26c3, 0x1dc79e2f, 0x121acdb1, 0x1e08edbb, 0x1e26426f, 0x1cd14e}}}, + /* 13*16^60*G: */ + {{{0x00bca3f3, 0x149edf33, 0x1801241a, 0x0686a28a, 0x0d8c4ecb, 0x15b0e440, 0x18f7758f, 0x158cb755, 0x2265b5}}, + {{0x117409ff, 0x0c14e362, 0x0e4f5689, 0x014c25a4, 0x164983c8, 0x09d1d884, 0x183d868c, 0x17eb959f, 0x97a198}}}, + /* 15*16^60*G: */ + {{{0x05bac36e, 0x19691ffa, 0x1d77340d, 0x11328b32, 0x1abcb599, 0x054fafe4, 0x049487f6, 0x1a206b09, 0xaf381c}}, + {{0x11b119a9, 0x19ec43d5, 0x1352a43f, 0x1705e3de, 0x02648589, 0x1f914a8d, 0x1ef72515, 0x0ff6fbe1, 0x681a08}}} + }, + { + /* 1*16^61*G: */ + {{{0x0037cfb4, 0x10cca02b, 0x136aa167, 0x01e48d87, 0x0b0e0740, 0x1a4406d0, 0x142c35df, 0x1047febd, 0x42531}}, + {{0x18775d23, 0x05b992f6, 0x1b177500, 0x07c9ea69, 0x01faceea, 0x12686433, 0x1df98a32, 0x199f5c01, 0xc90e8b}}}, + /* 3*16^61*G: */ + {{{0x06c34577, 0x027b39aa, 0x094abdde, 0x013d91cd, 0x0e4bde64, 0x11847460, 0x0922c7d7, 0x141c6179, 0x27557a}}, + {{0x0416193c, 0x0ff5cdc0, 0x110e02eb, 0x0594e6e9, 0x134f318a, 0x0bad24fe, 0x0ceddf23, 0x0b08c20b, 0x8399c7}}}, + /* 5*16^61*G: */ + {{{0x0401f4d3, 0x12c7edb5, 0x056cc07b, 0x185ca1d5, 0x1d7decf6, 0x1c1dfab0, 0x0d923941, 0x02fa4b0e, 0x8e6878}}, + {{0x0294d86b, 0x0140f4a2, 0x08644a24, 0x172de25d, 0x13cae900, 0x04d02836, 0x0fe98be0, 0x110dc593, 0x989cab}}}, + /* 7*16^61*G: */ + {{{0x0d2aa8e6, 0x0cb1ef13, 0x1bff8b71, 0x06b3f881, 0x1dbee205, 0x1401e533, 0x13db440d, 0x08c4a7cb, 0x98a417}}, + {{0x006cf75b, 0x0ba05f6b, 0x1fb4865a, 0x042ff556, 0x1cec9e30, 0x017ad17a, 0x0f5ac455, 0x128fc68c, 0x579ac6}}}, + /* 9*16^61*G: */ + {{{0x152c2d31, 0x0516647b, 0x187bb35f, 0x01576118, 0x1a946180, 0x17221f10, 0x03f64885, 0x084460a8, 0xe16854}}, + {{0x04a50fec, 0x05c41b41, 0x0660e507, 0x0c3257f1, 0x1c9343e7, 0x13216815, 0x0850becb, 0x0b9251ce, 0x70085}}}, + /* 11*16^61*G: */ + {{{0x13a7cdad, 0x119dd71b, 0x02f3ebd2, 0x1b07a5ca, 0x151a53ca, 0x117299c4, 0x0fa8728a, 0x09613aa2, 0xbfa631}}, + {{0x095cb953, 0x0fc44981, 0x1011e871, 0x0321f190, 0x0d1a7261, 0x02ddd8f7, 0x11e0a97e, 0x03299005, 0xb452e8}}}, + /* 13*16^61*G: */ + {{{0x0ade0fb7, 0x0de64280, 0x1946363b, 0x1b8e9bd4, 0x18e200c6, 0x0baf36ec, 0x134fb3c2, 0x05a152c3, 0xe68708}}, + {{0x1d4d6ece, 0x0bcf0798, 0x03089664, 0x03d0bdf1, 0x1a72117c, 0x072990e8, 0x1555797c, 0x090d0992, 0x2135a4}}}, + /* 15*16^61*G: */ + {{{0x0e432daf, 0x01e5af86, 0x009eb272, 0x1db41155, 0x14975f3b, 0x146bca83, 0x07a52ff0, 0x1f0c5535, 0x7ab16e}}, + {{0x071bdc48, 0x08bac455, 0x13f6c04c, 0x139c75ce, 0x1a7c5c7e, 0x0c1f146d, 0x02c68d64, 0x0152f865, 0x584e0e}}} + }, + { + /* 1*16^62*G: */ + {{{0x085fbd44, 0x03a3894c, 0x09899eb0, 0x0595473e, 0x1222c89c, 0x18e70b6a, 0x04178151, 0x1b5b356b, 0x9bbf06}}, + {{0x1a1d3e88, 0x1a23efd3, 0x00dbb4a8, 0x097bb82a, 0x147e8c0d, 0x0d798537, 0x028d9d57, 0x1509bc24, 0x1bcc7f}}}, + /* 3*16^62*G: */ + {{{0x14a7b506, 0x1bd73c95, 0x0182f822, 0x0b1775cf, 0x00ee9227, 0x1d9573a8, 0x0f4d0a73, 0x1ecb676b, 0x646ff7}}, + {{0x0b36f946, 0x0bce0929, 0x039a6572, 0x1bf89f81, 0x08fe8492, 0x1198c025, 0x02956987, 0x13a0c943, 0xbc112a}}}, + /* 5*16^62*G: */ + {{{0x0103b553, 0x0a5b8e4e, 0x0e16a005, 0x01d44f06, 0x0507ebe6, 0x0800593f, 0x0e6a7430, 0x0f54c2fa, 0x7cc054}}, + {{0x0d45a526, 0x1cce5d1e, 0x08a2df55, 0x10b41558, 0x094c4001, 0x14659cfe, 0x116af75c, 0x17a46500, 0x7b329e}}}, + /* 7*16^62*G: */ + {{{0x19e717a4, 0x0177a2a0, 0x1c06e06b, 0x1457b559, 0x0e15468d, 0x16a9b6d7, 0x0d38158f, 0x1321783b, 0x946851}}, + {{0x1526dea3, 0x1af87e8d, 0x02b36729, 0x132b21ee, 0x07dc0579, 0x08735933, 0x06a2cee3, 0x0841b8b2, 0xca5c78}}}, + /* 9*16^62*G: */ + {{{0x08362735, 0x1c161b78, 0x0acc9ef7, 0x1166fde2, 0x0d1f5dff, 0x07c75229, 0x03eac496, 0x037cfc1d, 0xe1b98f}}, + {{0x0ecb11b7, 0x0b09c64e, 0x1d0242be, 0x13dc67be, 0x12ba27e1, 0x1a4d41dd, 0x082df816, 0x15b352ed, 0xca83e9}}}, + /* 11*16^62*G: */ + {{{0x19046346, 0x1a05dec1, 0x0510020e, 0x0b1bce60, 0x1962d56a, 0x1035ecb3, 0x1fbdb422, 0x0b91fac0, 0x3536f2}}, + {{0x0c14fea6, 0x0008315e, 0x166cf8a5, 0x187a3d45, 0x1e1c39e6, 0x0b50d294, 0x0279cee8, 0x1c2fbc6a, 0x1e271f}}}, + /* 13*16^62*G: */ + {{{0x0847f6f8, 0x1ffc1f6b, 0x103bd4c3, 0x136a3f7a, 0x18c3c102, 0x08f9a5bf, 0x1379a405, 0x08d7c47a, 0xdf502}}, + {{0x1b1633a6, 0x06654535, 0x17cd126d, 0x1c2ccd13, 0x174ab4c6, 0x1627de10, 0x0728ac8c, 0x0fe53b5e, 0x210704}}}, + /* 15*16^62*G: */ + {{{0x1d4f1b81, 0x02e7576f, 0x1d2ec546, 0x134f4919, 0x09330ad6, 0x11d99b29, 0x0e63da77, 0x1a899ea6, 0xca9dd5}}, + {{0x02b7c8bd, 0x06316572, 0x0b6843e3, 0x12ec17f5, 0x07b3e66c, 0x18fda223, 0x11590091, 0x10ca37c5, 0x6e4b98}}} + }, + { + /* 1*16^63*G: */ + {{{0x044c0448, 0x1569919a, 0x00c678c7, 0x0aed0d82, 0x093e7963, 0x1e659dbc, 0x1e95250f, 0x1ea5287b, 0xb12fad}}, + {{0x1369de57, 0x12baf3b5, 0x1f137ca5, 0x0bce0665, 0x0a6a71d0, 0x07b33bd9, 0x12b3a5be, 0x12b3361e, 0x2e245}}}, + /* 3*16^63*G: */ + {{{0x04e9151f, 0x09b622a3, 0x0361063d, 0x14673390, 0x0fb3e67e, 0x0eed48ec, 0x15a10c95, 0x069e38a0, 0x48388e}}, + {{0x0d7a9434, 0x1f724f6e, 0x17a0c406, 0x028222a7, 0x02bcc99b, 0x19c98aa4, 0x1a79a894, 0x157e9c89, 0xb0ec63}}}, + /* 5*16^63*G: */ + {{{0x0e5b833d, 0x1efda52c, 0x02c90675, 0x1400e794, 0x109c9593, 0x0fc08280, 0x0f697997, 0x08f5c28a, 0xde8e64}}, + {{0x014c5cc7, 0x07e60cd5, 0x0071b27c, 0x1c062652, 0x1e265987, 0x14465eb6, 0x0f638cc8, 0x0782a122, 0x5e146}}}, + /* 7*16^63*G: */ + {{{0x01f7df48, 0x112c9f56, 0x1c4c5ecf, 0x0e8e7d70, 0x0f385b26, 0x0da7272c, 0x1a7a3955, 0x1a149d8f, 0xb18b12}}, + {{0x0846b546, 0x0d9eac87, 0x14795664, 0x07efc907, 0x0c33b3c8, 0x0135e2d6, 0x0d1173b1, 0x04546b3f, 0xff5319}}}, + /* 9*16^63*G: */ + {{{0x19c9fd24, 0x129d6e34, 0x0847837c, 0x1bb1308a, 0x0694402c, 0x05526394, 0x10cf1f07, 0x0e391b92, 0x5f51ad}}, + {{0x0d2ffd3a, 0x01dd7443, 0x092a65a7, 0x03ec488c, 0x04d03872, 0x02fde617, 0x0528334c, 0x023befce, 0x2bbbef}}}, + /* 11*16^63*G: */ + {{{0x17cec42e, 0x1e2031fe, 0x18ef6df8, 0x1b9267dd, 0x0d8556f3, 0x117721a3, 0x1aefe960, 0x1b26e603, 0xfdf340}}, + {{0x0db6908b, 0x07b1a5be, 0x178c320e, 0x1cd57ea1, 0x0d2c4456, 0x0ff39c65, 0x1b875c30, 0x0c72d198, 0xa3e59a}}}, + /* 13*16^63*G: */ + {{{0x01dc4fe8, 0x00c4a2a7, 0x01c9057f, 0x1142752c, 0x1c983f9e, 0x0948bdbe, 0x15e7b191, 0x19173d4b, 0xe3caeb}}, + {{0x1307b403, 0x15b05b90, 0x13e1a845, 0x0006cb42, 0x1f976513, 0x1ae5c580, 0x04a34880, 0x1a4f7e0b, 0x97f093}}}, + /* 15*16^63*G: */ + {{{0x1c119eff, 0x00a6b2e9, 0x07e6e119, 0x005c1815, 0x1a003399, 0x0d2e72c7, 0x16e26bd0, 0x1728550c, 0x3312d}}, + {{0x18b1b950, 0x14ad1d4f, 0x1455617f, 0x18b3b6be, 0x1e86f0ae, 0x1518bc04, 0x137c413b, 0x0c8d66e4, 0x6e3eda}}} + }, diff --git a/applications/external/flipbip/lib/crypto/options.h b/applications/external/flipbip/lib/crypto/options.h new file mode 100644 index 000000000..f0edcc60f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/options.h @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + +// use precomputed Curve Points (some scalar multiples of curve base point G) +#ifndef USE_PRECOMPUTED_CP +#define USE_PRECOMPUTED_CP 0 +#endif + +// use fast inverse method +#ifndef USE_INVERSE_FAST +#define USE_INVERSE_FAST 1 +#endif + +// support for printing bignum256 structures via printf +#ifndef USE_BN_PRINT +#define USE_BN_PRINT 0 +#endif + +// use deterministic signatures +#ifndef USE_RFC6979 +#define USE_RFC6979 1 +#endif + +// implement BIP32 caching +#ifndef USE_BIP32_CACHE +#define USE_BIP32_CACHE 0 +#define BIP32_CACHE_SIZE 10 +#define BIP32_CACHE_MAXDEPTH 8 +#endif + +// support constructing BIP32 nodes from ed25519 and curve25519 curves. +#ifndef USE_BIP32_25519_CURVES +#define USE_BIP32_25519_CURVES 0 +#endif + +// implement BIP39 caching +#ifndef USE_BIP39_CACHE +#define USE_BIP39_CACHE 0 +#define BIP39_CACHE_SIZE 4 +#endif + +// support Ethereum operations +#ifndef USE_ETHEREUM +#define USE_ETHEREUM 1 +#endif + +// support NEM operations +#ifndef USE_NEM +#define USE_NEM 0 +#endif + +// support MONERO operations +#ifndef USE_MONERO +#define USE_MONERO 0 +#endif + +// support CARDANO operations +#ifndef USE_CARDANO +#define USE_CARDANO 0 +#endif + +// support Keccak hashing +#ifndef USE_KECCAK +#define USE_KECCAK 1 +#endif + +// add way how to mark confidential data +#ifndef CONFIDENTIAL +#define CONFIDENTIAL +#endif + +#endif diff --git a/applications/external/flipbip/lib/crypto/pbkdf2.c b/applications/external/flipbip/lib/crypto/pbkdf2.c new file mode 100644 index 000000000..e3ebe515e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/pbkdf2.c @@ -0,0 +1,195 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pbkdf2.h" +#include +#include "hmac.h" +#include "memzero.h" +#include "sha2.h" + +void pbkdf2_hmac_sha256_Init( + PBKDF2_HMAC_SHA256_CTX* pctx, + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t blocknr) { + SHA256_CTX ctx = {0}; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(blocknr, blocknr); +#endif + + hmac_sha256_prepare(pass, passlen, pctx->odig, pctx->idig); + memzero(pctx->g, sizeof(pctx->g)); + pctx->g[8] = 0x80000000; + pctx->g[15] = (SHA256_BLOCK_LENGTH + SHA256_DIGEST_LENGTH) * 8; + + memcpy(ctx.state, pctx->idig, sizeof(pctx->idig)); + ctx.bitcount = SHA256_BLOCK_LENGTH * 8; + sha256_Update(&ctx, salt, saltlen); + sha256_Update(&ctx, (uint8_t*)&blocknr, sizeof(blocknr)); + sha256_Final(&ctx, (uint8_t*)pctx->g); +#if BYTE_ORDER == LITTLE_ENDIAN + for(uint32_t k = 0; k < SHA256_DIGEST_LENGTH / sizeof(uint32_t); k++) { + REVERSE32(pctx->g[k], pctx->g[k]); + } +#endif + sha256_Transform(pctx->odig, pctx->g, pctx->g); + memcpy(pctx->f, pctx->g, SHA256_DIGEST_LENGTH); + pctx->first = 1; +} + +void pbkdf2_hmac_sha256_Update(PBKDF2_HMAC_SHA256_CTX* pctx, uint32_t iterations) { + for(uint32_t i = pctx->first; i < iterations; i++) { + sha256_Transform(pctx->idig, pctx->g, pctx->g); + sha256_Transform(pctx->odig, pctx->g, pctx->g); + for(uint32_t j = 0; j < SHA256_DIGEST_LENGTH / sizeof(uint32_t); j++) { + pctx->f[j] ^= pctx->g[j]; + } + } + pctx->first = 0; +} + +void pbkdf2_hmac_sha256_Final(PBKDF2_HMAC_SHA256_CTX* pctx, uint8_t* key) { +#if BYTE_ORDER == LITTLE_ENDIAN + for(uint32_t k = 0; k < SHA256_DIGEST_LENGTH / sizeof(uint32_t); k++) { + REVERSE32(pctx->f[k], pctx->f[k]); + } +#endif + memcpy(key, pctx->f, SHA256_DIGEST_LENGTH); + memzero(pctx, sizeof(PBKDF2_HMAC_SHA256_CTX)); +} + +void pbkdf2_hmac_sha256( + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t iterations, + uint8_t* key, + int keylen) { + uint32_t last_block_size = keylen % SHA256_DIGEST_LENGTH; + uint32_t blocks_count = keylen / SHA256_DIGEST_LENGTH; + if(last_block_size) { + blocks_count++; + } else { + last_block_size = SHA256_DIGEST_LENGTH; + } + for(uint32_t blocknr = 1; blocknr <= blocks_count; blocknr++) { + PBKDF2_HMAC_SHA256_CTX pctx = {0}; + pbkdf2_hmac_sha256_Init(&pctx, pass, passlen, salt, saltlen, blocknr); + pbkdf2_hmac_sha256_Update(&pctx, iterations); + uint8_t digest[SHA256_DIGEST_LENGTH] = {0}; + pbkdf2_hmac_sha256_Final(&pctx, digest); + uint32_t key_offset = (blocknr - 1) * SHA256_DIGEST_LENGTH; + if(blocknr < blocks_count) { + memcpy(key + key_offset, digest, SHA256_DIGEST_LENGTH); + } else { + memcpy(key + key_offset, digest, last_block_size); + } + } +} + +void pbkdf2_hmac_sha512_Init( + PBKDF2_HMAC_SHA512_CTX* pctx, + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t blocknr) { + SHA512_CTX ctx = {0}; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(blocknr, blocknr); +#endif + + hmac_sha512_prepare(pass, passlen, pctx->odig, pctx->idig); + memzero(pctx->g, sizeof(pctx->g)); + pctx->g[8] = 0x8000000000000000; + pctx->g[15] = (SHA512_BLOCK_LENGTH + SHA512_DIGEST_LENGTH) * 8; + + memcpy(ctx.state, pctx->idig, sizeof(pctx->idig)); + ctx.bitcount[0] = SHA512_BLOCK_LENGTH * 8; + ctx.bitcount[1] = 0; + sha512_Update(&ctx, salt, saltlen); + sha512_Update(&ctx, (uint8_t*)&blocknr, sizeof(blocknr)); + sha512_Final(&ctx, (uint8_t*)pctx->g); +#if BYTE_ORDER == LITTLE_ENDIAN + for(uint32_t k = 0; k < SHA512_DIGEST_LENGTH / sizeof(uint64_t); k++) { + REVERSE64(pctx->g[k], pctx->g[k]); + } +#endif + sha512_Transform(pctx->odig, pctx->g, pctx->g); + memcpy(pctx->f, pctx->g, SHA512_DIGEST_LENGTH); + pctx->first = 1; +} + +void pbkdf2_hmac_sha512_Update(PBKDF2_HMAC_SHA512_CTX* pctx, uint32_t iterations) { + for(uint32_t i = pctx->first; i < iterations; i++) { + sha512_Transform(pctx->idig, pctx->g, pctx->g); + sha512_Transform(pctx->odig, pctx->g, pctx->g); + for(uint32_t j = 0; j < SHA512_DIGEST_LENGTH / sizeof(uint64_t); j++) { + pctx->f[j] ^= pctx->g[j]; + } + } + pctx->first = 0; +} + +void pbkdf2_hmac_sha512_Final(PBKDF2_HMAC_SHA512_CTX* pctx, uint8_t* key) { +#if BYTE_ORDER == LITTLE_ENDIAN + for(uint32_t k = 0; k < SHA512_DIGEST_LENGTH / sizeof(uint64_t); k++) { + REVERSE64(pctx->f[k], pctx->f[k]); + } +#endif + memcpy(key, pctx->f, SHA512_DIGEST_LENGTH); + memzero(pctx, sizeof(PBKDF2_HMAC_SHA512_CTX)); +} + +void pbkdf2_hmac_sha512( + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t iterations, + uint8_t* key, + int keylen) { + uint32_t last_block_size = keylen % SHA512_DIGEST_LENGTH; + uint32_t blocks_count = keylen / SHA512_DIGEST_LENGTH; + if(last_block_size) { + blocks_count++; + } else { + last_block_size = SHA512_DIGEST_LENGTH; + } + for(uint32_t blocknr = 1; blocknr <= blocks_count; blocknr++) { + PBKDF2_HMAC_SHA512_CTX pctx = {0}; + pbkdf2_hmac_sha512_Init(&pctx, pass, passlen, salt, saltlen, blocknr); + pbkdf2_hmac_sha512_Update(&pctx, iterations); + uint8_t digest[SHA512_DIGEST_LENGTH] = {0}; + pbkdf2_hmac_sha512_Final(&pctx, digest); + uint32_t key_offset = (blocknr - 1) * SHA512_DIGEST_LENGTH; + if(blocknr < blocks_count) { + memcpy(key + key_offset, digest, SHA512_DIGEST_LENGTH); + } else { + memcpy(key + key_offset, digest, last_block_size); + } + } +} diff --git a/applications/external/flipbip/lib/crypto/pbkdf2.h b/applications/external/flipbip/lib/crypto/pbkdf2.h new file mode 100644 index 000000000..22b580ff4 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/pbkdf2.h @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __PBKDF2_H__ +#define __PBKDF2_H__ + +#include +#include "sha2.h" + +typedef struct _PBKDF2_HMAC_SHA256_CTX { + uint32_t odig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t idig[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t f[SHA256_DIGEST_LENGTH / sizeof(uint32_t)]; + uint32_t g[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; + char first; +} PBKDF2_HMAC_SHA256_CTX; + +typedef struct _PBKDF2_HMAC_SHA512_CTX { + uint64_t odig[SHA512_DIGEST_LENGTH / sizeof(uint64_t)]; + uint64_t idig[SHA512_DIGEST_LENGTH / sizeof(uint64_t)]; + uint64_t f[SHA512_DIGEST_LENGTH / sizeof(uint64_t)]; + uint64_t g[SHA512_BLOCK_LENGTH / sizeof(uint64_t)]; + char first; +} PBKDF2_HMAC_SHA512_CTX; + +void pbkdf2_hmac_sha256_Init( + PBKDF2_HMAC_SHA256_CTX* pctx, + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t blocknr); +void pbkdf2_hmac_sha256_Update(PBKDF2_HMAC_SHA256_CTX* pctx, uint32_t iterations); +void pbkdf2_hmac_sha256_Final(PBKDF2_HMAC_SHA256_CTX* pctx, uint8_t* key); +void pbkdf2_hmac_sha256( + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t iterations, + uint8_t* key, + int keylen); + +void pbkdf2_hmac_sha512_Init( + PBKDF2_HMAC_SHA512_CTX* pctx, + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t blocknr); +void pbkdf2_hmac_sha512_Update(PBKDF2_HMAC_SHA512_CTX* pctx, uint32_t iterations); +void pbkdf2_hmac_sha512_Final(PBKDF2_HMAC_SHA512_CTX* pctx, uint8_t* key); +void pbkdf2_hmac_sha512( + const uint8_t* pass, + int passlen, + const uint8_t* salt, + int saltlen, + uint32_t iterations, + uint8_t* key, + int keylen); + +#endif diff --git a/applications/external/flipbip/lib/crypto/rand.c b/applications/external/flipbip/lib/crypto/rand.c new file mode 100644 index 000000000..b35214285 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rand.c @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +// NOTE: +// random32() and random_buffer() have been replaced in this implementation +// with Flipper Zero specific code. The original code is commented out below. + +#include "rand.h" + +// Flipper Zero RNG code: +#include + +#ifndef RAND_PLATFORM_INDEPENDENT + +// Original code: +// #pragma message("NOT SUITABLE FOR PRODUCTION USE! Replace random32() function with your own secure code.") + +// The following code is not supposed to be used in a production environment. +// It's included only to make the library testable. +// The message above tries to prevent any accidental use outside of the test +// environment. +// +// You are supposed to replace the random8() and random32() function with your +// own secure code. There is also a possibility to replace the random_buffer() +// function as it is defined as a weak symbol. + +static uint32_t seed = 0; + +void random_reseed(const uint32_t value) { + seed = value; +} + +// Original code: +// uint32_t random32(void) { +// // Linear congruential generator from Numerical Recipes +// // https://en.wikipedia.org/wiki/Linear_congruential_generator +// seed = 1664525 * seed + 1013904223; +// return seed; +// } + +// Flipper Zero RNG code: +uint32_t random32(void) { + return furi_hal_random_get(); +} + +// Flipper Zero RNG code: +void random_buffer(uint8_t* buf, size_t len) { + furi_hal_random_fill_buf(buf, len); +} + +#endif /* RAND_PLATFORM_INDEPENDENT */ + +// +// The following code is platform independent +// + +// Original code: +// void __attribute__((weak)) random_buffer(uint8_t *buf, size_t len) { +// uint32_t r = 0; +// for (size_t i = 0; i < len; i++) { +// if (i % 4 == 0) { +// r = random32(); +// } +// buf[i] = (r >> ((i % 4) * 8)) & 0xFF; +// } +// } + +uint32_t random_uniform(uint32_t n) { + uint32_t x = 0, max = 0xFFFFFFFF - (0xFFFFFFFF % n); + while((x = random32()) >= max) + ; + return x / (max / n); +} + +void random_permute(char* str, size_t len) { + for(int i = len - 1; i >= 1; i--) { + int j = random_uniform(i + 1); + char t = str[j]; + str[j] = str[i]; + str[i] = t; + } +} diff --git a/applications/external/flipbip/lib/crypto/rand.h b/applications/external/flipbip/lib/crypto/rand.h new file mode 100644 index 000000000..fa854890f --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rand.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __RAND_H__ +#define __RAND_H__ + +#include +#include + +void random_reseed(const uint32_t value); +uint32_t random32(void); +void random_buffer(uint8_t* buf, size_t len); + +uint32_t random_uniform(uint32_t n); +void random_permute(char* buf, size_t len); + +#endif diff --git a/applications/external/flipbip/lib/crypto/rc4.c b/applications/external/flipbip/lib/crypto/rc4.c new file mode 100644 index 000000000..b42ad24ed --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rc4.c @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E1PRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "rc4.h" + +static inline void rc4_swap(RC4_CTX* ctx, uint8_t i, uint8_t j) { + uint8_t temp = ctx->S[i]; + ctx->S[i] = ctx->S[j]; + ctx->S[j] = temp; +} + +void rc4_init(RC4_CTX* ctx, const uint8_t* key, size_t length) { + ctx->i = 0; + ctx->j = 0; + + for(size_t i = 0; i < 256; i++) { + ctx->S[i] = i; + } + + uint8_t j = 0; + for(size_t i = 0; i < 256; i++) { + j += ctx->S[i] + key[i % length]; + rc4_swap(ctx, i, j); + } +} + +void rc4_encrypt(RC4_CTX* ctx, uint8_t* buffer, size_t length) { + for(size_t idx = 0; idx < length; idx++) { + ctx->i++; + ctx->j += ctx->S[ctx->i]; + + rc4_swap(ctx, ctx->i, ctx->j); + + uint8_t K = ctx->S[(ctx->S[ctx->i] + ctx->S[ctx->j]) % 256]; + buffer[idx] ^= K; + } +} diff --git a/applications/external/flipbip/lib/crypto/rc4.h b/applications/external/flipbip/lib/crypto/rc4.h new file mode 100644 index 000000000..860d33130 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rc4.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __RC4_H__ +#define __RC4_H__ + +#include +#include + +typedef struct { + uint8_t S[256]; + uint8_t i, j; +} RC4_CTX; + +void rc4_init(RC4_CTX* ctx, const uint8_t* key, size_t length); +void rc4_encrypt(RC4_CTX* ctx, uint8_t* buffer, size_t length); + +#endif diff --git a/applications/external/flipbip/lib/crypto/rfc6979.c b/applications/external/flipbip/lib/crypto/rfc6979.c new file mode 100644 index 000000000..6bede6f5c --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rfc6979.c @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include + +#include "hmac_drbg.h" +#include "memzero.h" +#include "rfc6979.h" + +void init_rfc6979( + const uint8_t* priv_key, + const uint8_t* hash, + const ecdsa_curve* curve, + rfc6979_state* state) { + if(curve) { + bignum256 hash_bn = {0}; + bn_read_be(hash, &hash_bn); + + // Make sure hash is partly reduced modulo order + assert(bn_bitcount(&curve->order) >= 256); + bn_mod(&hash_bn, &curve->order); + + uint8_t hash_reduced[32] = {0}; + bn_write_be(&hash_bn, hash_reduced); + memzero(&hash_bn, sizeof(hash_bn)); + hmac_drbg_init(state, priv_key, 32, hash_reduced, 32); + memzero(hash_reduced, sizeof(hash_reduced)); + } else { + hmac_drbg_init(state, priv_key, 32, hash, 32); + } +} + +// generate next number from deterministic random number generator +void generate_rfc6979(uint8_t rnd[32], rfc6979_state* state) { + hmac_drbg_generate(state, rnd, 32); +} + +// generate K in a deterministic way, according to RFC6979 +// http://tools.ietf.org/html/rfc6979 +void generate_k_rfc6979(bignum256* k, rfc6979_state* state) { + uint8_t buf[32] = {0}; + generate_rfc6979(buf, state); + bn_read_be(buf, k); + memzero(buf, sizeof(buf)); +} diff --git a/applications/external/flipbip/lib/crypto/rfc6979.h b/applications/external/flipbip/lib/crypto/rfc6979.h new file mode 100644 index 000000000..02367c38e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/rfc6979.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015-2017 Jochen Hoenicke + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __RFC6979_H__ +#define __RFC6979_H__ + +#include +#include "bignum.h" +#include "ecdsa.h" +#include "hmac_drbg.h" + +// rfc6979 pseudo random number generator state +typedef HMAC_DRBG_CTX rfc6979_state; + +void init_rfc6979( + const uint8_t* priv_key, + const uint8_t* hash, + const ecdsa_curve* curve, + rfc6979_state* rng); +void generate_rfc6979(uint8_t rnd[32], rfc6979_state* rng); +void generate_k_rfc6979(bignum256* k, rfc6979_state* rng); + +#endif diff --git a/applications/external/flipbip/lib/crypto/ripemd160.c b/applications/external/flipbip/lib/crypto/ripemd160.c new file mode 100644 index 000000000..7d7f3898b --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ripemd160.c @@ -0,0 +1,330 @@ +/* + * RIPE MD-160 implementation + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +/* + * The RIPEMD-160 algorithm was designed by RIPE in 1996 + * http://homes.esat.kuleuven.be/~bosselae/ripemd160.html + * http://ehash.iaik.tugraz.at/wiki/RIPEMD-160 + */ + +#include + +#include "ripemd160.h" +#include "memzero.h" + +/* + * 32-bit integer manipulation macros (little endian) + */ +#ifndef GET_UINT32_LE +#define GET_UINT32_LE(n, b, i) \ + { \ + (n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | \ + ((uint32_t)(b)[(i) + 2] << 16) | ((uint32_t)(b)[(i) + 3] << 24); \ + } +#endif + +#ifndef PUT_UINT32_LE +#define PUT_UINT32_LE(n, b, i) \ + { \ + (b)[(i)] = (uint8_t)(((n)) & 0xFF); \ + (b)[(i) + 1] = (uint8_t)(((n) >> 8) & 0xFF); \ + (b)[(i) + 2] = (uint8_t)(((n) >> 16) & 0xFF); \ + (b)[(i) + 3] = (uint8_t)(((n) >> 24) & 0xFF); \ + } +#endif + +/* + * RIPEMD-160 context setup + */ +void ripemd160_Init(RIPEMD160_CTX* ctx) { + memzero(ctx, sizeof(RIPEMD160_CTX)); + ctx->total[0] = 0; + ctx->total[1] = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +#if !defined(MBEDTLS_RIPEMD160_PROCESS_ALT) +/* + * Process one block + */ +void ripemd160_process(RIPEMD160_CTX* ctx, const uint8_t data[RIPEMD160_BLOCK_LENGTH]) { + uint32_t A = 0, B = 0, C = 0, D = 0, E = 0, Ap = 0, Bp = 0, Cp = 0, Dp = 0, Ep = 0, + X[16] = {0}; + + GET_UINT32_LE(X[0], data, 0); + GET_UINT32_LE(X[1], data, 4); + GET_UINT32_LE(X[2], data, 8); + GET_UINT32_LE(X[3], data, 12); + GET_UINT32_LE(X[4], data, 16); + GET_UINT32_LE(X[5], data, 20); + GET_UINT32_LE(X[6], data, 24); + GET_UINT32_LE(X[7], data, 28); + GET_UINT32_LE(X[8], data, 32); + GET_UINT32_LE(X[9], data, 36); + GET_UINT32_LE(X[10], data, 40); + GET_UINT32_LE(X[11], data, 44); + GET_UINT32_LE(X[12], data, 48); + GET_UINT32_LE(X[13], data, 52); + GET_UINT32_LE(X[14], data, 56); + GET_UINT32_LE(X[15], data, 60); + + A = Ap = ctx->state[0]; + B = Bp = ctx->state[1]; + C = Cp = ctx->state[2]; + D = Dp = ctx->state[3]; + E = Ep = ctx->state[4]; + +#define F1(x, y, z) (x ^ y ^ z) +#define F2(x, y, z) ((x & y) | (~x & z)) +#define F3(x, y, z) ((x | ~y) ^ z) +#define F4(x, y, z) ((x & z) | (y & ~z)) +#define F5(x, y, z) (x ^ (y | ~z)) + +#define S(x, n) ((x << n) | (x >> (32 - n))) + +#define P(a, b, c, d, e, r, s, f, k) \ + a += f(b, c, d) + X[r] + k; \ + a = S(a, s) + e; \ + c = S(c, 10); + +#define P2(a, b, c, d, e, r, s, rp, sp) \ + P(a, b, c, d, e, r, s, F, K); \ + P(a##p, b##p, c##p, d##p, e##p, rp, sp, Fp, Kp); + +#define F F1 +#define K 0x00000000 +#define Fp F5 +#define Kp 0x50A28BE6 + P2(A, B, C, D, E, 0, 11, 5, 8); + P2(E, A, B, C, D, 1, 14, 14, 9); + P2(D, E, A, B, C, 2, 15, 7, 9); + P2(C, D, E, A, B, 3, 12, 0, 11); + P2(B, C, D, E, A, 4, 5, 9, 13); + P2(A, B, C, D, E, 5, 8, 2, 15); + P2(E, A, B, C, D, 6, 7, 11, 15); + P2(D, E, A, B, C, 7, 9, 4, 5); + P2(C, D, E, A, B, 8, 11, 13, 7); + P2(B, C, D, E, A, 9, 13, 6, 7); + P2(A, B, C, D, E, 10, 14, 15, 8); + P2(E, A, B, C, D, 11, 15, 8, 11); + P2(D, E, A, B, C, 12, 6, 1, 14); + P2(C, D, E, A, B, 13, 7, 10, 14); + P2(B, C, D, E, A, 14, 9, 3, 12); + P2(A, B, C, D, E, 15, 8, 12, 6); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F2 +#define K 0x5A827999 +#define Fp F4 +#define Kp 0x5C4DD124 + P2(E, A, B, C, D, 7, 7, 6, 9); + P2(D, E, A, B, C, 4, 6, 11, 13); + P2(C, D, E, A, B, 13, 8, 3, 15); + P2(B, C, D, E, A, 1, 13, 7, 7); + P2(A, B, C, D, E, 10, 11, 0, 12); + P2(E, A, B, C, D, 6, 9, 13, 8); + P2(D, E, A, B, C, 15, 7, 5, 9); + P2(C, D, E, A, B, 3, 15, 10, 11); + P2(B, C, D, E, A, 12, 7, 14, 7); + P2(A, B, C, D, E, 0, 12, 15, 7); + P2(E, A, B, C, D, 9, 15, 8, 12); + P2(D, E, A, B, C, 5, 9, 12, 7); + P2(C, D, E, A, B, 2, 11, 4, 6); + P2(B, C, D, E, A, 14, 7, 9, 15); + P2(A, B, C, D, E, 11, 13, 1, 13); + P2(E, A, B, C, D, 8, 12, 2, 11); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F3 +#define K 0x6ED9EBA1 +#define Fp F3 +#define Kp 0x6D703EF3 + P2(D, E, A, B, C, 3, 11, 15, 9); + P2(C, D, E, A, B, 10, 13, 5, 7); + P2(B, C, D, E, A, 14, 6, 1, 15); + P2(A, B, C, D, E, 4, 7, 3, 11); + P2(E, A, B, C, D, 9, 14, 7, 8); + P2(D, E, A, B, C, 15, 9, 14, 6); + P2(C, D, E, A, B, 8, 13, 6, 6); + P2(B, C, D, E, A, 1, 15, 9, 14); + P2(A, B, C, D, E, 2, 14, 11, 12); + P2(E, A, B, C, D, 7, 8, 8, 13); + P2(D, E, A, B, C, 0, 13, 12, 5); + P2(C, D, E, A, B, 6, 6, 2, 14); + P2(B, C, D, E, A, 13, 5, 10, 13); + P2(A, B, C, D, E, 11, 12, 0, 13); + P2(E, A, B, C, D, 5, 7, 4, 7); + P2(D, E, A, B, C, 12, 5, 13, 5); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F4 +#define K 0x8F1BBCDC +#define Fp F2 +#define Kp 0x7A6D76E9 + P2(C, D, E, A, B, 1, 11, 8, 15); + P2(B, C, D, E, A, 9, 12, 6, 5); + P2(A, B, C, D, E, 11, 14, 4, 8); + P2(E, A, B, C, D, 10, 15, 1, 11); + P2(D, E, A, B, C, 0, 14, 3, 14); + P2(C, D, E, A, B, 8, 15, 11, 14); + P2(B, C, D, E, A, 12, 9, 15, 6); + P2(A, B, C, D, E, 4, 8, 0, 14); + P2(E, A, B, C, D, 13, 9, 5, 6); + P2(D, E, A, B, C, 3, 14, 12, 9); + P2(C, D, E, A, B, 7, 5, 2, 12); + P2(B, C, D, E, A, 15, 6, 13, 9); + P2(A, B, C, D, E, 14, 8, 9, 12); + P2(E, A, B, C, D, 5, 6, 7, 5); + P2(D, E, A, B, C, 6, 5, 10, 15); + P2(C, D, E, A, B, 2, 12, 14, 8); +#undef F +#undef K +#undef Fp +#undef Kp + +#define F F5 +#define K 0xA953FD4E +#define Fp F1 +#define Kp 0x00000000 + P2(B, C, D, E, A, 4, 9, 12, 8); + P2(A, B, C, D, E, 0, 15, 15, 5); + P2(E, A, B, C, D, 5, 5, 10, 12); + P2(D, E, A, B, C, 9, 11, 4, 9); + P2(C, D, E, A, B, 7, 6, 1, 12); + P2(B, C, D, E, A, 12, 8, 5, 5); + P2(A, B, C, D, E, 2, 13, 8, 14); + P2(E, A, B, C, D, 10, 12, 7, 6); + P2(D, E, A, B, C, 14, 5, 6, 8); + P2(C, D, E, A, B, 1, 12, 2, 13); + P2(B, C, D, E, A, 3, 13, 13, 6); + P2(A, B, C, D, E, 8, 14, 14, 5); + P2(E, A, B, C, D, 11, 11, 0, 15); + P2(D, E, A, B, C, 6, 8, 3, 13); + P2(C, D, E, A, B, 15, 5, 9, 11); + P2(B, C, D, E, A, 13, 6, 11, 11); +#undef F +#undef K +#undef Fp +#undef Kp + + C = ctx->state[1] + C + Dp; + ctx->state[1] = ctx->state[2] + D + Ep; + ctx->state[2] = ctx->state[3] + E + Ap; + ctx->state[3] = ctx->state[4] + A + Bp; + ctx->state[4] = ctx->state[0] + B + Cp; + ctx->state[0] = C; +} +#endif /* !MBEDTLS_RIPEMD160_PROCESS_ALT */ + +/* + * RIPEMD-160 process buffer + */ +void ripemd160_Update(RIPEMD160_CTX* ctx, const uint8_t* input, uint32_t ilen) { + uint32_t fill = 0; + uint32_t left = 0; + + if(ilen == 0) return; + + left = ctx->total[0] & 0x3F; + fill = RIPEMD160_BLOCK_LENGTH - left; + + ctx->total[0] += (uint32_t)ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if(ctx->total[0] < (uint32_t)ilen) ctx->total[1]++; + + if(left && ilen >= fill) { + memcpy((void*)(ctx->buffer + left), input, fill); + ripemd160_process(ctx, ctx->buffer); + input += fill; + ilen -= fill; + left = 0; + } + + while(ilen >= RIPEMD160_BLOCK_LENGTH) { + ripemd160_process(ctx, input); + input += RIPEMD160_BLOCK_LENGTH; + ilen -= RIPEMD160_BLOCK_LENGTH; + } + + if(ilen > 0) { + memcpy((void*)(ctx->buffer + left), input, ilen); + } +} + +static const uint8_t ripemd160_padding[RIPEMD160_BLOCK_LENGTH] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +/* + * RIPEMD-160 final digest + */ +void ripemd160_Final(RIPEMD160_CTX* ctx, uint8_t output[RIPEMD160_DIGEST_LENGTH]) { + uint32_t last = 0; + uint32_t padn = 0; + uint32_t high = 0; + uint32_t low = 0; + uint8_t msglen[8] = {0}; + + high = (ctx->total[0] >> 29) | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + PUT_UINT32_LE(low, msglen, 0); + PUT_UINT32_LE(high, msglen, 4); + + last = ctx->total[0] & 0x3F; + padn = (last < 56) ? (56 - last) : (120 - last); + + ripemd160_Update(ctx, ripemd160_padding, padn); + ripemd160_Update(ctx, msglen, 8); + + PUT_UINT32_LE(ctx->state[0], output, 0); + PUT_UINT32_LE(ctx->state[1], output, 4); + PUT_UINT32_LE(ctx->state[2], output, 8); + PUT_UINT32_LE(ctx->state[3], output, 12); + PUT_UINT32_LE(ctx->state[4], output, 16); + + memzero(ctx, sizeof(RIPEMD160_CTX)); +} + +/* + * output = RIPEMD-160( input buffer ) + */ +void ripemd160(const uint8_t* msg, uint32_t msg_len, uint8_t hash[RIPEMD160_DIGEST_LENGTH]) { + RIPEMD160_CTX ctx = {0}; + ripemd160_Init(&ctx); + ripemd160_Update(&ctx, msg, msg_len); + ripemd160_Final(&ctx, hash); +} diff --git a/applications/external/flipbip/lib/crypto/ripemd160.h b/applications/external/flipbip/lib/crypto/ripemd160.h new file mode 100644 index 000000000..271e9ec5b --- /dev/null +++ b/applications/external/flipbip/lib/crypto/ripemd160.h @@ -0,0 +1,20 @@ +#ifndef __RIPEMD160_H__ +#define __RIPEMD160_H__ + +#include + +#define RIPEMD160_BLOCK_LENGTH 64 +#define RIPEMD160_DIGEST_LENGTH 20 + +typedef struct _RIPEMD160_CTX { + uint32_t total[2]; /*!< number of bytes processed */ + uint32_t state[5]; /*!< intermediate digest state */ + uint8_t buffer[RIPEMD160_BLOCK_LENGTH]; /*!< data block being processed */ +} RIPEMD160_CTX; + +void ripemd160_Init(RIPEMD160_CTX* ctx); +void ripemd160_Update(RIPEMD160_CTX* ctx, const uint8_t* input, uint32_t ilen); +void ripemd160_Final(RIPEMD160_CTX* ctx, uint8_t output[RIPEMD160_DIGEST_LENGTH]); +void ripemd160(const uint8_t* msg, uint32_t msg_len, uint8_t hash[RIPEMD160_DIGEST_LENGTH]); + +#endif diff --git a/applications/external/flipbip/lib/crypto/script.c b/applications/external/flipbip/lib/crypto/script.c new file mode 100644 index 000000000..11173e973 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/script.c @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2016 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "script.h" +#include +#include "base58.h" + +int script_output_to_address(const uint8_t* script, int scriptlen, char* addr, int addrsize) { + uint8_t raw[35] = {0}; + + // P2PKH + if(scriptlen == 25 && script[0] == 0x76 && script[1] == 0xA9 && script[2] == 0x14 && + script[23] == 0x88 && script[24] == 0xAC) { + raw[0] = 0x00; + memcpy(raw + 1, script + 3, 20); + return base58_encode_check(raw, 1 + 20, HASHER_SHA2D, addr, addrsize); + } + + // P2SH + if(scriptlen == 23 && script[0] == 0xA9 && script[1] == 0x14 && script[22] == 0x87) { + raw[0] = 0x05; + memcpy(raw + 1, script + 2, 20); + return base58_encode_check(raw, 1 + 20, HASHER_SHA2D, addr, addrsize); + } + + // P2WPKH + if(scriptlen == 22 && script[0] == 0x00 && script[1] == 0x14) { + raw[0] = 0x06; + raw[1] = 0x00; + raw[2] = 0x00; + memcpy(raw + 3, script + 2, 20); + return base58_encode_check(raw, 3 + 20, HASHER_SHA2D, addr, addrsize); + } + + // P2WSH + if(scriptlen == 34 && script[0] == 0x00 && script[1] == 0x20) { + raw[0] = 0x0A; + raw[1] = 0x00; + raw[2] = 0x00; + memcpy(raw + 3, script + 2, 32); + return base58_encode_check(raw, 3 + 32, HASHER_SHA2D, addr, addrsize); + } + + return 0; +} diff --git a/applications/external/flipbip/lib/crypto/script.h b/applications/external/flipbip/lib/crypto/script.h new file mode 100644 index 000000000..585b059fa --- /dev/null +++ b/applications/external/flipbip/lib/crypto/script.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2016 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SCRIPT_H__ +#define __SCRIPT_H__ + +#include + +int script_output_to_address(const uint8_t* script, int scriptlen, char* addr, int addrsize); + +#endif diff --git a/applications/external/flipbip/lib/crypto/secp256k1.c b/applications/external/flipbip/lib/crypto/secp256k1.c new file mode 100644 index 000000000..d954dd733 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/secp256k1.c @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "secp256k1.h" + +const ecdsa_curve secp256k1 = { + /* .prime */ {/*.val =*/{ + 0x1ffffc2f, + 0x1ffffff7, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0xffffff}}, + + /* G */ + {/*.x =*/{/*.val =*/{ + 0x16f81798, + 0x0f940ad8, + 0x138a3656, + 0x17f9b65b, + 0x10b07029, + 0x114ae743, + 0x0eb15681, + 0x0fdf3b97, + 0x79be66}}, + /*.y =*/{/*.val =*/{ + 0x1b10d4b8, + 0x023e847f, + 0x01550667, + 0x0f68914d, + 0x108a8fd1, + 0x1dfe0708, + 0x11957693, + 0x0ee4d478, + 0x483ada}}}, + + /* order */ + {/*.val =*/{ + 0x10364141, + 0x1e92f466, + 0x12280eef, + 0x1db9cd5e, + 0x1fffebaa, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0xffffff}}, + + /* order_half */ + {/*.val =*/{ + 0x081b20a0, + 0x1f497a33, + 0x09140777, + 0x0edce6af, + 0x1ffff5d5, + 0x1fffffff, + 0x1fffffff, + 0x1fffffff, + 0x7fffff}}, + + /* a */ 0, + + /* b */ {/*.val =*/{7}} + +#if USE_PRECOMPUTED_CP + , + /* cp */ + { +#include "secp256k1.table" + } +#endif +}; + +const curve_info secp256k1_info = { + .bip32_name = "Bitcoin seed", + .params = &secp256k1, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +const curve_info secp256k1_decred_info = { + .bip32_name = "Bitcoin seed", + .params = &secp256k1, + .hasher_base58 = HASHER_BLAKED, + .hasher_sign = HASHER_BLAKE, + .hasher_pubkey = HASHER_BLAKE_RIPEMD, + .hasher_script = HASHER_BLAKE, +}; + +const curve_info secp256k1_groestl_info = { + .bip32_name = "Bitcoin seed", + .params = &secp256k1, + .hasher_base58 = HASHER_GROESTLD_TRUNC, + .hasher_sign = HASHER_SHA2, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +const curve_info secp256k1_smart_info = { + .bip32_name = "Bitcoin seed", + .params = &secp256k1, +#if USE_KECCAK + .hasher_base58 = HASHER_SHA3K, +#else + .hasher_base58 = HASHER_SHA3, +#endif + .hasher_sign = HASHER_SHA2, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; diff --git a/applications/external/flipbip/lib/crypto/secp256k1.h b/applications/external/flipbip/lib/crypto/secp256k1.h new file mode 100644 index 000000000..3b45e48fe --- /dev/null +++ b/applications/external/flipbip/lib/crypto/secp256k1.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SECP256K1_H__ +#define __SECP256K1_H__ + +#include + +#include "bip32.h" +#include "ecdsa.h" + +extern const ecdsa_curve secp256k1; +extern const curve_info secp256k1_info; +extern const curve_info secp256k1_decred_info; +extern const curve_info secp256k1_groestl_info; +extern const curve_info secp256k1_smart_info; + +#endif diff --git a/applications/external/flipbip/lib/crypto/secp256k1.table b/applications/external/flipbip/lib/crypto/secp256k1.table new file mode 100644 index 000000000..0fa87a8b1 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/secp256k1.table @@ -0,0 +1,1664 @@ + { + /* 1*16^0*G: */ + {{{0x16f81798, 0x0f940ad8, 0x138a3656, 0x17f9b65b, 0x10b07029, 0x114ae743, 0x0eb15681, 0x0fdf3b97, 0x79be66}}, + {{0x1b10d4b8, 0x023e847f, 0x01550667, 0x0f68914d, 0x108a8fd1, 0x1dfe0708, 0x11957693, 0x0ee4d478, 0x483ada}}}, + /* 3*16^0*G: */ + {{{0x1ce036f9, 0x100f889d, 0x1be66c21, 0x03908b06, 0x15229b53, 0x07c2fc4e, 0x0c4124d1, 0x00324b18, 0xf9308a}}, + {{0x04b8e672, 0x05cfebac, 0x1088c6db, 0x01533269, 0x1f356650, 0x1bf3151b, 0x00503f8c, 0x01ec65bd, 0x388f7b}}}, + /* 5*16^0*G: */ + {{{0x1240efe4, 0x1d46ab4d, 0x1866adf2, 0x17097bb8, 0x05128e88, 0x1392852e, 0x024d56d2, 0x09a340e4, 0x2f8bde}}, + {{0x06ac62d6, 0x0543e9d5, 0x035a1037, 0x104e3756, 0x1c426f78, 0x14eed364, 0x0f5b536e, 0x04c6dcbc, 0xd8ac22}}}, + /* 7*16^0*G: */ + {{{0x0ac4f9bc, 0x095eef6e, 0x0c38e73a, 0x0336fc06, 0x07a0e3d4, 0x19b2f975, 0x13aa8e63, 0x0c8dcbb6, 0x5cbdf0}}, + {{0x087264da, 0x08413140, 0x1f79ed69, 0x07a17027, 0x054dba81, 0x06b6c30d, 0x05828c5e, 0x081744ab, 0x6aebca}}}, + /* 9*16^0*G: */ + {{{0x1c27ccbe, 0x1af8886f, 0x15f9c530, 0x0f2d2e98, 0x19abde09, 0x0bc54faa, 0x194c26b4, 0x1c5e18fe, 0xacd484}}, + {{0x064f9c37, 0x0e613156, 0x17e383c1, 0x1111486e, 0x161e9add, 0x04b8bb1d, 0x07f590e0, 0x043614fb, 0xcc3389}}}, + /* 11*16^0*G: */ + {{{0x1da008cb, 0x1f60bc4a, 0x105e246e, 0x133017cb, 0x05aac564, 0x1235b863, 0x04797bd0, 0x1f0b1528, 0x774ae7}}, + {{0x0953c61b, 0x00eba64e, 0x1e75aa0c, 0x1b63c5bf, 0x1b365372, 0x0eab6bdb, 0x1864090f, 0x065d6d6b, 0xd984a0}}}, + /* 13*16^0*G: */ + {{{0x19405aa8, 0x176efc78, 0x03963377, 0x0bf78cc2, 0x08651b07, 0x0902e1ba, 0x022f1f47, 0x185b2ea5, 0xf28773}}, + {{0x1b03ed81, 0x0dae5a96, 0x07ea47ca, 0x140db4a4, 0x1af473a1, 0x0975b2e6, 0x0a25d608, 0x05d1b101, 0xab090}}}, + /* 15*16^0*G: */ + {{{0x027e080e, 0x056de7c7, 0x017de791, 0x0b28de79, 0x1f41131e, 0x0d7184af, 0x0a596919, 0x09efa87d, 0xd7924d}}, + {{0x16a26b58, 0x0826e4ff, 0x05b4e971, 0x015e57b1, 0x06defea4, 0x17611466, 0x0a9a0e10, 0x0e550d8e, 0x581e28}}} + }, + { + /* 1*16^1*G: */ + {{{0x0a6dec0a, 0x027744f1, 0x1e96ba71, 0x0626d370, 0x03e97b2a, 0x155e10e1, 0x1b14c046, 0x1276b3d3, 0xe60fce}}, + {{0x09616821, 0x0f996673, 0x148fc2f8, 0x0d123c89, 0x13710129, 0x0f9a7abc, 0x164a76e6, 0x0e733cb2, 0xf7e350}}}, + /* 3*16^1*G: */ + {{{0x1118e5c3, 0x1ec38550, 0x0afaf066, 0x0f364e8a, 0x05b4bfc5, 0x12b77a73, 0x01f6d105, 0x0bb2c8a6, 0x6eca33}}, + {{0x05a08668, 0x0c517bc0, 0x1e3b0d12, 0x12d47477, 0x075a03a4, 0x0bc83a5c, 0x1c4164bd, 0x16af4f40, 0xd50123}}}, + /* 5*16^1*G: */ + {{{0x0f87f62e, 0x16698f0a, 0x1c5849c3, 0x0dcc70c6, 0x059f010e, 0x1a2769a3, 0x03b035f1, 0x17de37f2, 0xe9623b}}, + {{0x044ee737, 0x1809f57d, 0x1a211394, 0x008793ba, 0x0a929fe6, 0x0a9d476d, 0x07a783fa, 0x07697853, 0x38a974}}}, + /* 7*16^1*G: */ + {{{0x0a8d733c, 0x18556fc1, 0x1f2a3e7a, 0x04e97ec5, 0x0d682ffc, 0x11b79040, 0x16e82212, 0x0e7ca2c3, 0xbc82dd}}, + {{0x147797f0, 0x13c30827, 0x0e25cc07, 0x074175ce, 0x102dfae9, 0x1a5fb8cf, 0x12b152a6, 0x07408963, 0xe5f28c}}}, + /* 9*16^1*G: */ + {{{0x1fbc7671, 0x1f7f118a, 0x01638cb5, 0x1e3790a5, 0x0f490743, 0x08e70bcc, 0x0847480a, 0x0918ecae, 0x8e3d12}}, + {{0x18717dec, 0x178ee320, 0x0f85129f, 0x0a57554c, 0x1e90eb93, 0x0070c9c9, 0x0e07d912, 0x1c21d9f9, 0x99a48}}}, + /* 11*16^1*G: */ + {{{0x1eb31db2, 0x1943a195, 0x1d41a83c, 0x15d04f11, 0x0a2b68fc, 0x0c9f6844, 0x126225a8, 0x15444694, 0x78a891}}, + {{0x19fa4343, 0x034eb11d, 0x002e0b4c, 0x0f379bb0, 0x02df6543, 0x192a9398, 0x172ff3d7, 0x0b7d6a06, 0x6912a3}}}, + /* 13*16^1*G: */ + {{{0x0db0e595, 0x09a47bbc, 0x0820aed9, 0x0c7973f7, 0x076eba71, 0x1bb2c0b0, 0x0c5f5f38, 0x030abb63, 0x7d8678}}, + {{0x1c733de8, 0x0ca8f1d5, 0x09754ca6, 0x0f089c1c, 0x18838293, 0x1715f6a7, 0x01dcb958, 0x1bfd90df, 0xe2b99a}}}, + /* 15*16^1*G: */ + {{{0x16060dfc, 0x047f7c28, 0x179a8a80, 0x08bf0840, 0x0b086765, 0x05cee20d, 0x0b212125, 0x01e00b05, 0xddc531}}, + {{0x07820ca8, 0x1afc55b7, 0x1411cc3e, 0x175f8d57, 0x10e9041d, 0x15b6e647, 0x1a480646, 0x075e41b2, 0xba0d2f}}} + }, + { + /* 1*16^2*G: */ + {{{0x15f51508, 0x123711fe, 0x0b072841, 0x073957ab, 0x1e238d8c, 0x171f0b96, 0x0767a8a9, 0x064258c1, 0x828226}}, + {{0x16e26caf, 0x18db757f, 0x1ec5efb4, 0x0c27585e, 0x00ace62d, 0x0b74185b, 0x1f917a09, 0x0130aafb, 0x11f8a8}}}, + /* 3*16^2*G: */ + {{{0x057e8dfa, 0x07e065cf, 0x11f8613f, 0x01232347, 0x18ca0098, 0x187c5654, 0x11303668, 0x05fe0f33, 0x8262cf}}, + {{0x1bac376a, 0x0e7fc6c7, 0x05311e0d, 0x0dda6656, 0x14f3457b, 0x111762d9, 0x19399bfb, 0x1c412213, 0x83fd95}}}, + /* 5*16^2*G: */ + {{{0x026bdb6f, 0x02972458, 0x1cd2e524, 0x0837a8f6, 0x19c877ca, 0x02d92674, 0x17545a04, 0x1163b41b, 0x19825c}}, + {{0x049cfc9b, 0x0efb8426, 0x1db4e9ad, 0x13dd9919, 0x19f6cebe, 0x10e64a7a, 0x1e3cc809, 0x01e1a990, 0x629431}}}, + /* 7*16^2*G: */ + {{{0x1d82824c, 0x07684a91, 0x054d3994, 0x0b1c68bc, 0x0999edfa, 0x1ab76361, 0x04510f17, 0x0d822c03, 0x6f12d8}}, + {{0x06eb34d0, 0x0bce1a40, 0x152f16e1, 0x19248210, 0x03769391, 0x0a79feb1, 0x1e821d66, 0x1e895677, 0x5c4ff7}}}, + /* 9*16^2*G: */ + {{{0x1b453629, 0x1b6ee016, 0x167980c1, 0x1fb9b81e, 0x0bef645c, 0x138b511d, 0x09745098, 0x0df34155, 0x203a8c}}, + {{0x1ff89f84, 0x0b8e3c29, 0x0a17b516, 0x1bd64b8a, 0x10612686, 0x1b68afa0, 0x06e4db31, 0x0a7bcbbb, 0x3b0f0b}}}, + /* 11*16^2*G: */ + {{{0x046c7ecb, 0x018986ef, 0x0ed33a5e, 0x15da7fcb, 0x0e1ec9d3, 0x1b99a433, 0x0607207b, 0x1d67a068, 0x6e2aca}}, + {{0x0ebc8720, 0x024900f7, 0x19d44b21, 0x0e0d7236, 0x1643afac, 0x026d787d, 0x18527603, 0x0cf2fdfd, 0x9e61a4}}}, + /* 13*16^2*G: */ + {{{0x10a4147e, 0x1b80c79f, 0x1d7c807a, 0x0fbb17ee, 0x10a58274, 0x18bf4524, 0x15aebd85, 0x125d3d22, 0xd5a704}}, + {{0x13fb65ff, 0x1259e5c1, 0x19fd5fe3, 0x09b308f2, 0x00532f4c, 0x04c83b2f, 0x071bf124, 0x1ebb7571, 0x9db526}}}, + /* 15*16^2*G: */ + {{{0x18edcec6, 0x16eda35e, 0x18d3d153, 0x0c3985a6, 0x0dac6a10, 0x17e31816, 0x0ea0148f, 0x13557c31, 0x38c511}}, + {{0x1933db08, 0x0b705fd8, 0x154c2991, 0x02d90456, 0x1282f28a, 0x196d13af, 0x0ca99a32, 0x0450bb2e, 0xe649dd}}} + }, + { + /* 1*16^3*G: */ + {{{0x11e5b739, 0x1fe72daa, 0x0888bb5c, 0x127067fa, 0x0846de0b, 0x0e63637e, 0x1969cbe6, 0x13ee5170, 0x175e15}}, + {{0x09fed695, 0x17d37ff7, 0x090d171b, 0x0b2ab5ba, 0x11f5eacb, 0x0bd28ffb, 0x07ae93be, 0x01b3c78f, 0xd3506e}}}, + /* 3*16^3*G: */ + {{{0x05041216, 0x0dbfc78e, 0x0ae0da99, 0x066bed08, 0x1ed523f7, 0x0cf7ee17, 0x13d04a2d, 0x0f643ef5, 0xda7531}}, + {{0x0e708572, 0x176994c3, 0x1eb3b6b6, 0x1580f5ce, 0x17fc6e9a, 0x110d9a16, 0x17c37c67, 0x08d7ee5a, 0x73f8a0}}}, + /* 5*16^3*G: */ + {{{0x0465a930, 0x00a1f38f, 0x04d4bf6c, 0x0fe382d6, 0x0eb1e258, 0x02c62541, 0x075c15cf, 0x1691d2e9, 0x1c71c5}}, + {{0x034638b5, 0x0c39fb66, 0x05d351c7, 0x08bc7f6e, 0x1b68c793, 0x18f94125, 0x08309c4f, 0x069d1ebf, 0x4a91c3}}}, + /* 7*16^3*G: */ + {{{0x1adb6ee7, 0x18c6d72d, 0x11281df8, 0x01ba864e, 0x14c9c785, 0x01bd484d, 0x159a4dba, 0x1f83e634, 0xd84e4a}}, + {{0x142ebed2, 0x16aab736, 0x08f99260, 0x11592e95, 0x1de4dfdd, 0x06ac7ab2, 0x07384a8e, 0x134f8f6f, 0xe52580}}}, + /* 9*16^3*G: */ + {{{0x049e6d10, 0x0a74f67d, 0x0b26748e, 0x15bfe75d, 0x16ed3f60, 0x15c942ff, 0x053506c8, 0x097bccd0, 0xf3d444}}, + {{0x1347da3f, 0x101c6602, 0x075b18c2, 0x15b4a19d, 0x04b5bfc1, 0x0ad60cc5, 0x18f52eae, 0x1bf4de02, 0xa4324}}}, + /* 11*16^3*G: */ + {{{0x09d33a07, 0x0659a037, 0x0e6f2ad2, 0x05dc1154, 0x1f4044e7, 0x0a9066de, 0x1627e421, 0x0593b383, 0xae3065}}, + {{0x00a0b2a6, 0x0438607b, 0x15fa571d, 0x186febbf, 0x0d5cf1c9, 0x13e99edc, 0x195fbf33, 0x1871ac7f, 0x6cb9d9}}}, + /* 13*16^3*G: */ + {{{0x0c28caca, 0x0cb2a5c8, 0x00a0769d, 0x138f7799, 0x08c9a186, 0x1f3ac19c, 0x07205785, 0x054b7abc, 0xd8dc1b}}, + {{0x03b3ec7a, 0x0db3b751, 0x004a3db3, 0x02ba59d9, 0x07d947d3, 0x06d21012, 0x1f5631b6, 0x1b24f9d8, 0x8cec0a}}}, + /* 15*16^3*G: */ + {{{0x1bc4416f, 0x090fdb31, 0x02c100a2, 0x1dfa47e6, 0x0f31da7a, 0x12d46819, 0x1b335650, 0x1258bf09, 0x2749e2}}, + {{0x1c6bbd8e, 0x14c5ef17, 0x1a58415f, 0x1d3f6cbd, 0x0db3ef59, 0x0a4c87e5, 0x0f1100f6, 0x09c6ece5, 0x50cc2d}}} + }, + { + /* 1*16^4*G: */ + {{{0x03ff4640, 0x135d6c7c, 0x154bff94, 0x0838fcaa, 0x02ee0534, 0x1602db13, 0x1272673a, 0x1a88f601, 0x363d90}}, + {{0x1bee9de9, 0x1001e3f9, 0x0667b2d8, 0x13512010, 0x1363145b, 0x0229cbf9, 0x088654ed, 0x15bf8e64, 0x4e273}}}, + /* 3*16^4*G: */ + {{{0x16e55dc8, 0x1c4890b7, 0x12810e52, 0x12b56dd5, 0x094426ff, 0x12206028, 0x1ecaea12, 0x08f218bf, 0x443140}}, + {{0x1be323b3, 0x0eca2576, 0x0a8b940c, 0x14536f3d, 0x0fed7a66, 0x01bfab21, 0x1be3fa66, 0x085cca6c, 0x96b0c1}}}, + /* 5*16^4*G: */ + {{{0x101b23a8, 0x1f4a42eb, 0x01fb82b7, 0x16fa8e15, 0x1089dab7, 0x01eadc90, 0x01f04989, 0x11b0cd95, 0x9e22fe}}, + {{0x0884edae, 0x1d209e28, 0x14473b3d, 0x0f9293f6, 0x01533c0f, 0x1f8104ce, 0x14405dfc, 0x1d394245, 0xfd2ff0}}}, + /* 7*16^4*G: */ + {{{0x071a70e4, 0x0ba045f8, 0x173d1d77, 0x1dca3ebe, 0x1306dcd5, 0x14f32382, 0x0a34bb75, 0x1aa079c5, 0x508df6}}, + {{0x09950984, 0x1972dfb9, 0x02ab7fb7, 0x006451dd, 0x049c54ec, 0x0255399f, 0x10b5ddcc, 0x13726778, 0x154c43}}}, + /* 9*16^4*G: */ + {{{0x0e1abe11, 0x157ed3b6, 0x12c883db, 0x124384b3, 0x125b2dab, 0x1ac0c980, 0x1d8cce37, 0x108aa212, 0xe3dbff}}, + {{0x1fa8de63, 0x1a4d6aa4, 0x1ad71052, 0x12fb2078, 0x08ef3d3c, 0x1d3aedc5, 0x108590e3, 0x01334682, 0x6f2f9}}}, + /* 11*16^4*G: */ + {{{0x03593449, 0x078d91b0, 0x0a91bff7, 0x16f825c8, 0x1014af61, 0x0e09d03e, 0x10361e36, 0x0c98fbd2, 0x19ace0}}, + {{0x0df83631, 0x120a5c9d, 0x0101a28e, 0x02173e81, 0x02c46ac7, 0x0b9ceca0, 0x0cceffaf, 0x006a4d14, 0xe37992}}}, + /* 13*16^4*G: */ + {{{0x0cba6b63, 0x11f4c496, 0x02ceed7b, 0x1fcce9fa, 0x08e310a0, 0x19914754, 0x16a84230, 0x1d841f0f, 0xd8740c}}, + {{0x0934c5f3, 0x1751b603, 0x005a52af, 0x009eb987, 0x0c37d401, 0x1774c81d, 0x0afcde29, 0x0678d726, 0x6472c1}}}, + /* 15*16^4*G: */ + {{{0x1b3ec038, 0x0ca7f960, 0x031ac9d8, 0x1aa2d5cc, 0x10a50d9f, 0x1f3b1794, 0x020d9220, 0x07236a0c, 0x58ac33}}, + {{0x10246279, 0x17551f88, 0x13eef285, 0x12087816, 0x1fe97021, 0x0924f4c8, 0x0b65e1de, 0x00daab92, 0x9163d7}}} + }, + { + /* 1*16^5*G: */ + {{{0x1ffdf80c, 0x0fbcd2ae, 0x16f346da, 0x094f0342, 0x1638843e, 0x025adba2, 0x0afa3189, 0x02cbbe78, 0x8b4b5f}}, + {{0x1fd4fd36, 0x1f7f8632, 0x18bb95ac, 0x066ca8c2, 0x0da04f9e, 0x0bc09d58, 0x02d2cfef, 0x0ded1a61, 0x4aad0a}}}, + /* 3*16^5*G: */ + {{{0x155812dd, 0x05152c17, 0x0b4c38a8, 0x08ce46aa, 0x0f78e3d4, 0x1f6b602c, 0x14bc2daa, 0x0f525fe6, 0x7029bd}}, + {{0x1a2d2927, 0x10e63358, 0x0cb1cf1c, 0x15d08487, 0x083ac47d, 0x0a257183, 0x0f49f759, 0x1b5fbd16, 0xb0eefa}}}, + /* 5*16^5*G: */ + {{{0x1d486ed1, 0x0e72b41d, 0x1596da92, 0x0b7d7492, 0x17560574, 0x0084ec67, 0x12640275, 0x195d5ccb, 0x9ccfed}}, + {{0x15e95d8d, 0x1b6acf6b, 0x164aa893, 0x02ceb2d2, 0x13411242, 0x12409005, 0x0b3ed848, 0x0e27ad46, 0x7c2f4d}}}, + /* 7*16^5*G: */ + {{{0x1bd0eaca, 0x10358d3a, 0x0b52ade8, 0x0e8aed74, 0x0df19d0c, 0x1ef19e52, 0x050cd6a3, 0x10ec6828, 0xcd9a4b}}, + {{0x0bff4acc, 0x137d7dad, 0x1bd8d3db, 0x0f671dda, 0x0a08b012, 0x0457499f, 0x08fa0552, 0x0f343d1e, 0xf04558}}}, + /* 9*16^5*G: */ + {{{0x07bc57c6, 0x104a9d1e, 0x0c9db2fc, 0x07af447d, 0x03094490, 0x169749eb, 0x0faa213c, 0x05f11db3, 0xad0988}}, + {{0x0e4a0ab8, 0x1196076d, 0x0438b4f2, 0x023a6e6b, 0x0be5f7b3, 0x036394ed, 0x14ae8a06, 0x11885f74, 0x7243c0}}}, + /* 11*16^5*G: */ + {{{0x0ba56302, 0x14186395, 0x03c618ba, 0x0d526f5e, 0x0d2e0f50, 0x116954fd, 0x107c7ab6, 0x015d6794, 0xd9d129}}, + {{0x08291c29, 0x156ed7d4, 0x09d3caba, 0x135d9b3f, 0x186e4173, 0x0ae33931, 0x1bb40a5c, 0x027d85a7, 0x7eb531}}}, + /* 13*16^5*G: */ + {{{0x14d1243a, 0x1669b827, 0x15297f6e, 0x024c047e, 0x1558402b, 0x10d6031f, 0x13be4734, 0x1bca73ad, 0xbc5079}}, + {{0x055db68a, 0x0e4a8b44, 0x1d1c5a7d, 0x1dc9eb63, 0x1c8d75f7, 0x195ca6ff, 0x12ee3bb1, 0x07674e0b, 0x65062a}}}, + /* 15*16^5*G: */ + {{{0x174a3f9f, 0x06922735, 0x02605a42, 0x0f4ccbb8, 0x0625ac28, 0x15573ed9, 0x1fa244f7, 0x0fca0bf8, 0x4d31a7}}, + {{0x101e0ba7, 0x1e581209, 0x18029d53, 0x0df6a36d, 0x02753cbf, 0x079c63c0, 0x1d6c1ac6, 0x192c130a, 0x22241e}}} + }, + { + /* 1*16^6*G: */ + {{{0x1232fcda, 0x1b08ac92, 0x1039def2, 0x01b7ff4d, 0x148c7b70, 0x18e005ea, 0x05b5afdd, 0x14dcbb73, 0x723cba}}, + {{0x1eb39f5f, 0x0ee034ec, 0x1e525200, 0x0140ca6e, 0x04d6e266, 0x09ba4441, 0x1262a484, 0x16ab2b98, 0x96e867}}}, + /* 3*16^6*G: */ + {{{0x00633cb1, 0x0b3f04f4, 0x140844c9, 0x144496d3, 0x01fcb575, 0x1399090c, 0x0b500318, 0x1e62f559, 0x6dde9c}}, + {{0x07ce6b34, 0x1eea4d53, 0x0167bcd5, 0x04ffb59f, 0x066a880b, 0x17c350dd, 0x10757267, 0x1cf4e0fc, 0x9188fb}}}, + /* 5*16^6*G: */ + {{{0x0933f3c5, 0x0cd28c69, 0x1c494890, 0x141ee22b, 0x1b850085, 0x1dbfc723, 0x17a04f12, 0x059ab6b9, 0x486fa7}}, + {{0x0afb0f53, 0x16a538d6, 0x03c8ede6, 0x136f079e, 0x0f19f62d, 0x045d7664, 0x150f9231, 0x033ead7b, 0x62e123}}}, + /* 7*16^6*G: */ + {{{0x1e99f728, 0x1eaca112, 0x1c48813a, 0x06ebf7cd, 0x05303677, 0x1f93dbb5, 0x1d3ed993, 0x0e951295, 0x247969}}, + {{0x0baaebff, 0x1d0028b7, 0x1d68b60d, 0x17e7812a, 0x1664a5ad, 0x143f3ec6, 0x0007b14b, 0x088d11e6, 0xe3d78d}}}, + /* 9*16^6*G: */ + {{{0x0fb0079a, 0x0f0636a1, 0x04981272, 0x1f3dee47, 0x18324916, 0x0cf73b59, 0x1fc18c69, 0x1b547aab, 0x2f39cb}}, + {{0x0c5690ba, 0x1114b981, 0x0a808c3f, 0x167f7910, 0x1a58b9bf, 0x1b6813c6, 0x060d36a4, 0x1bc270c7, 0xabeadb}}}, + /* 11*16^6*G: */ + {{{0x04f7ab73, 0x09980597, 0x1110e9de, 0x07a9d53a, 0x0aed262e, 0x0f43629a, 0x06e95a8e, 0x0d864fac, 0xe5a31d}}, + {{0x10561f42, 0x089d1fe3, 0x032e884e, 0x18889350, 0x0ce5dbf8, 0x054bbd27, 0x15e83046, 0x07b1a3d3, 0x37788c}}}, + /* 13*16^6*G: */ + {{{0x014dcd86, 0x07c94d1e, 0x0fdc6d62, 0x0ba8412d, 0x1dcf11fc, 0x0ecc1028, 0x111f7d43, 0x0941a2a6, 0xcc389d}}, + {{0x08f0a873, 0x075b6ece, 0x1f9e1d1a, 0x0afc31fc, 0x110ca05c, 0x05eddf54, 0x0fb66d5a, 0x1acc1ed7, 0x93ae4f}}}, + /* 15*16^6*G: */ + {{{0x18819311, 0x07ce375b, 0x01dc51c9, 0x1c3dc421, 0x1ed1f0b3, 0x10bf067a, 0x0408dd42, 0x1913ae3d, 0x7f9291}}, + {{0x0c2eb125, 0x14fcdabd, 0x01a85d2a, 0x15548139, 0x015b6120, 0x0f462292, 0x1bc3d743, 0x020c7d87, 0x9da00d}}} + }, + { + /* 1*16^7*G: */ + {{{0x0e7dd7fa, 0x1299f650, 0x0a4660e6, 0x0f2c246f, 0x0d3b5094, 0x17640961, 0x1e62e97f, 0x1a9277d7, 0xeebfa4}}, + {{0x01de8999, 0x0fea7ed7, 0x047dc4b7, 0x099b874e, 0x0089d9ae, 0x1f6d78bc, 0x03c9a7b9, 0x1472e1de, 0x5d9a8c}}}, + /* 3*16^7*G: */ + {{{0x1b7ceceb, 0x1fb3c7fd, 0x05febc3c, 0x0b3f2711, 0x0681473a, 0x1c0937b7, 0x1140dbfe, 0x04084eda, 0x437a86}}, + {{0x16c181e1, 0x1b1de61a, 0x03e5e09c, 0x041f9fb9, 0x097ff872, 0x1f5b4ce9, 0x0cbda6e3, 0x1427dd58, 0xb916b}}}, + /* 5*16^7*G: */ + {{{0x097f96f2, 0x0c6b94f0, 0x121cd735, 0x046a53a5, 0x0c273358, 0x1f1dcd1e, 0x06f20f2d, 0x027c5491, 0xa9ef9f}}, + {{0x16c04be4, 0x01eaad82, 0x06d1c0b0, 0x1f135eb5, 0x1613db74, 0x170b075d, 0x15f3655b, 0x1cb28ab3, 0xe814cc}}}, + /* 7*16^7*G: */ + {{{0x150cf77e, 0x0055ad09, 0x0a2ac36f, 0x17eae8a9, 0x0064207d, 0x13eb4fd5, 0x16f954e0, 0x083dc3a6, 0x66d805}}, + {{0x00eaa3a6, 0x12fcbd7d, 0x0c6ddb4a, 0x0968756f, 0x013f6944, 0x0a1029ab, 0x0d0b0fc7, 0x1ce65fff, 0x51cfdf}}}, + /* 9*16^7*G: */ + {{{0x07213a5a, 0x1aa43144, 0x17e98ae4, 0x064094f0, 0x0c3c80b7, 0x0450e326, 0x1e8afcd4, 0x1c26ca07, 0x62ac05}}, + {{0x146a9e45, 0x08690e07, 0x1e653bf0, 0x12120302, 0x12579e55, 0x13cf03d4, 0x1b934e57, 0x1e741a34, 0x236fbd}}}, + /* 11*16^7*G: */ + {{{0x07bad12b, 0x187ea4c3, 0x1d9b87b0, 0x0bd401d5, 0x12db385d, 0x09d7ae37, 0x1d6d6fd8, 0x092e4891, 0xca13c4}}, + {{0x1723b0f2, 0x0b4cfa34, 0x095d59a2, 0x0156e223, 0x1a3d6637, 0x1d327556, 0x1f22b057, 0x106c3850, 0x83aa09}}}, + /* 13*16^7*G: */ + {{{0x1c80414e, 0x0d772fca, 0x19b3dff7, 0x1f150c5a, 0x09e4914e, 0x0e27a230, 0x0ba16b04, 0x0034e0a5, 0x1cecb1}}, + {{0x12169a3b, 0x0574a496, 0x1c8c7437, 0x0aafca88, 0x1ad16907, 0x11941af6, 0x13ed396a, 0x0cd2c12f, 0xf34360}}}, + /* 15*16^7*G: */ + {{{0x0ed810a9, 0x07ade462, 0x1c005571, 0x113a7e78, 0x123924f8, 0x0d7af8ef, 0x1e732543, 0x0ea09af1, 0x2a6990}}, + {{0x1a9b4728, 0x1c359c47, 0x105171f3, 0x1a8fd2fd, 0x104fc667, 0x0c34aa7d, 0x1f6b0fb1, 0x136d87bf, 0x54f903}}} + }, + { + /* 1*16^8*G: */ + {{{0x19a48db0, 0x1ebc1ad9, 0x0f00effb, 0x042b4536, 0x1de459f1, 0x08504dbd, 0x059c9e47, 0x1b4d2dce, 0x100f44}}, + {{0x0bc65a09, 0x1deae6b1, 0x14656b03, 0x1e9431fe, 0x10666b7f, 0x19980604, 0x0ddcbb23, 0x06325401, 0xcdd9e1}}}, + /* 3*16^8*G: */ + {{{0x15bc15b4, 0x05cd09a4, 0x168bb9a7, 0x0a051c8c, 0x1ca8d927, 0x0774e76b, 0x1727b616, 0x05ca3dd5, 0x10e90e}}, + {{0x18aa258d, 0x075f304a, 0x0edaa20d, 0x0b12c605, 0x11f754ca, 0x14630b56, 0x0109355e, 0x00701abc, 0xc68a37}}}, + /* 5*16^8*G: */ + {{{0x1fe75269, 0x0e9fe181, 0x0f4cc60b, 0x0f47980a, 0x17dcda37, 0x1c85b8a5, 0x18e115d6, 0x085b4a82, 0xf7422f}}, + {{0x17e49bd5, 0x04c07438, 0x08e63806, 0x07446fe9, 0x035977fb, 0x13ee5cfb, 0x04ff4633, 0x03466261, 0x406c2f}}}, + /* 7*16^8*G: */ + {{{0x15a7175f, 0x09db34b7, 0x073d0a99, 0x11cee3a6, 0x1debbedb, 0x0d2ac16a, 0x13fdca1e, 0x0082fa87, 0x2d8cad}}, + {{0x1b9d592a, 0x19bddc8d, 0x0d797833, 0x08d7fb39, 0x09d377a8, 0x197d3096, 0x0529eec8, 0x10663195, 0xc73f3b}}}, + /* 9*16^8*G: */ + {{{0x14b51045, 0x1a64de1c, 0x07096cf8, 0x0d912de6, 0x0cf73bbc, 0x1ea758f4, 0x1a962b9c, 0x03b7314d, 0x1ecbfd}}, + {{0x02c70026, 0x1d338808, 0x0d908b54, 0x000c8d68, 0x09b38b19, 0x05d8c24d, 0x0e9911f4, 0x06117338, 0x1cf6e2}}}, + /* 11*16^8*G: */ + {{{0x09358533, 0x1d66bb37, 0x1ed2e77d, 0x1267f3a9, 0x12a8c10a, 0x0ad148e9, 0x14a20fa5, 0x18bfcaee, 0x9a0894}}, + {{0x0360ba08, 0x19f0e2ee, 0x00376b7e, 0x0dcb7b77, 0x1c32165a, 0x1eafcaa7, 0x1f0c7e45, 0x18840371, 0xa79883}}}, + /* 13*16^8*G: */ + {{{0x198ef7f6, 0x0a202eb0, 0x01e3e7da, 0x07e7eef4, 0x0aea6592, 0x042939dc, 0x0b8d6f67, 0x093b69fa, 0x664dd8}}, + {{0x1d1eac94, 0x1a4b7f9a, 0x039bb3b9, 0x1a756637, 0x058cffc3, 0x0672ee82, 0x04ca8512, 0x02e2fe4f, 0xad5120}}}, + /* 15*16^8*G: */ + {{{0x03c934b3, 0x060535be, 0x02b8b138, 0x03eb00b6, 0x1a7022b3, 0x0cb34c08, 0x018e08c7, 0x126efa17, 0x82113a}}, + {{0x042c6a0f, 0x1f2f3156, 0x111a00dd, 0x1ef84d34, 0x0c628aa1, 0x16795ad0, 0x19982119, 0x1b5935c6, 0x8da1b8}}} + }, + { + /* 1*16^9*G: */ + {{{0x0534fd2d, 0x04566f37, 0x1cece14b, 0x1f1a88c9, 0x0c017a77, 0x113d2502, 0x146c7724, 0x1c4c58fd, 0xe1031b}}, + {{0x1456a00d, 0x0278c794, 0x073b5e69, 0x05ba833c, 0x1535af29, 0x120bb2cb, 0x0179aeda, 0x12512808, 0x9d7061}}}, + /* 3*16^9*G: */ + {{{0x0f028d83, 0x1cb11d77, 0x1d0e5855, 0x0b24db74, 0x069db619, 0x1f2d0aef, 0x17b1a96a, 0x189c78f0, 0xa7ebf7}}, + {{0x19d0bed1, 0x1201c95c, 0x014e4665, 0x07124e96, 0x0804b47a, 0x039bb822, 0x0b573f67, 0x05f7fc6c, 0x620515}}}, + /* 5*16^9*G: */ + {{{0x07dd5cfa, 0x17072011, 0x02752d6e, 0x138a26fe, 0x146336a8, 0x1529a131, 0x1d307371, 0x11b96049, 0x5b5ca0}}, + {{0x1e48e98c, 0x132537cc, 0x0c9a74f9, 0x00cf995e, 0x09094bfd, 0x18675443, 0x0097a647, 0x1ee1542b, 0x3eccb6}}}, + /* 7*16^9*G: */ + {{{0x03531f82, 0x1ca94719, 0x030b27ca, 0x0264d762, 0x02c29ff5, 0x1f3a44e1, 0x0ed733bc, 0x15982229, 0x46f26}}, + {{0x0bceda07, 0x082fe458, 0x0d571fe9, 0x0b28b9b5, 0x12579d02, 0x185617e1, 0x0bab388d, 0x062c6b70, 0x6b804b}}}, + /* 9*16^9*G: */ + {{{0x10432711, 0x058aa1b8, 0x045ae418, 0x08165fbb, 0x1645acf1, 0x1787844c, 0x1ed4d7e5, 0x1b263c1d, 0xc11926}}, + {{0x0fe2610c, 0x04930f17, 0x1cd01579, 0x17245941, 0x0c6cf83a, 0x05f8c366, 0x1a246125, 0x198fa4b6, 0x8be1f8}}}, + /* 11*16^9*G: */ + {{{0x01257963, 0x0bcc3a7d, 0x12aa1870, 0x031aa586, 0x179c45b0, 0x1aa5a9c4, 0x1cb9b36e, 0x1d3d6d11, 0x690846}}, + {{0x066f9835, 0x12bb2cca, 0x124ac94e, 0x18795043, 0x1bb7f6cb, 0x18127c97, 0x0f16d005, 0x196fe7fd, 0xe2485f}}}, + /* 13*16^9*G: */ + {{{0x1ee23ace, 0x0889ca5e, 0x1374bf4b, 0x10f8cb6a, 0x12915dec, 0x0a96fa51, 0x02eaee9d, 0x1f719244, 0xfb3df7}}, + {{0x1722e8de, 0x04ef7a64, 0x149fd7b8, 0x04e285fd, 0x0b58a9e7, 0x08aa06dd, 0x11594a89, 0x178238e7, 0x510e29}}}, + /* 15*16^9*G: */ + {{{0x10272351, 0x103e7f7a, 0x07fc4261, 0x06967bf3, 0x1cd41707, 0x11f59d58, 0x062cfae2, 0x1849eb8a, 0x6dd85e}}, + {{0x1fc7664b, 0x183fd15b, 0x18205f26, 0x18dfef87, 0x041b7877, 0x03fcd7ae, 0x09f82750, 0x1e8243e8, 0x16ea67}}} + }, + { + /* 1*16^10*G: */ + {{{0x1094696d, 0x0af3446c, 0x075abd4b, 0x1164cd48, 0x1d7ec5cf, 0x01cf8a1d, 0x0d4c2b0a, 0x15c8daab, 0xfeea6c}}, + {{0x18090088, 0x0aaef5f8, 0x10510b4c, 0x1912af98, 0x0cd5c981, 0x07095f9f, 0x06eac1b9, 0x0d92fb9c, 0xe57c6b}}}, + /* 3*16^10*G: */ + {{{0x08dfd587, 0x1c9b0dda, 0x0c099581, 0x09747193, 0x1a12d5ec, 0x1d55167a, 0x022cd219, 0x03759e8a, 0x5084b4}}, + {{0x11470e89, 0x13cf4bfc, 0x047d581b, 0x0deac0d1, 0x127475db, 0x13642a94, 0x14c5866a, 0x0343b301, 0x34a963}}}, + /* 5*16^10*G: */ + {{{0x1ab34cc6, 0x0411930b, 0x1cc284b4, 0x1852ecf9, 0x17128c80, 0x1f8fe8c6, 0x17a94fec, 0x07c0c85a, 0x4f14c0}}, + {{0x187e681f, 0x0f61297c, 0x00774089, 0x1c799d1d, 0x02540b9d, 0x1387a1d3, 0x0253194e, 0x1519549d, 0x7b53d0}}}, + /* 7*16^10*G: */ + {{{0x1241d90d, 0x013b8808, 0x09113e0d, 0x19e283b6, 0x1d363e81, 0x1b04af6e, 0x1b475050, 0x0fc938f3, 0xa74db8}}, + {{0x1f7adad4, 0x1928c5c1, 0x0828c4fc, 0x1ca12689, 0x171c8a9e, 0x08452c40, 0x1bcc9ff7, 0x19b5e47d, 0xf78691}}}, + /* 9*16^10*G: */ + {{{0x11c1ae1f, 0x0f665111, 0x13502ca9, 0x1cb18d15, 0x15658456, 0x06a275d1, 0x0b3e6b37, 0x0ae89754, 0x6901fa}}, + {{0x122838b0, 0x1c19832e, 0x11dea4c3, 0x1e774bcb, 0x0900dd79, 0x09c0614e, 0x0849186d, 0x11044e78, 0x35de5c}}}, + /* 11*16^10*G: */ + {{{0x127a4bdb, 0x1818840d, 0x12532b12, 0x14da086a, 0x1a35d046, 0x1b21f8dd, 0x049e8912, 0x05a3a870, 0x8d3cd8}}, + {{0x069a8a2c, 0x1e9a63e7, 0x02b4a5b0, 0x0700fa6e, 0x0236ed4e, 0x1fce803b, 0x07d1c33e, 0x0c301dc8, 0x9bd425}}}, + /* 13*16^10*G: */ + {{{0x14ec1d2d, 0x00669e35, 0x1f63d151, 0x133eb8dc, 0x1691fd90, 0x0b394b3d, 0x1ce6bfa7, 0x0c7ac0c8, 0xeaf983}}, + {{0x0c838452, 0x07f3ab02, 0x1a12d4ff, 0x0a5aa8af, 0x199aaa9f, 0x14507358, 0x1489dd48, 0x074ffcf1, 0xe51818}}}, + /* 15*16^10*G: */ + {{{0x045ae767, 0x059adfb8, 0x025dc72f, 0x0753973d, 0x0e5d8c27, 0x07029603, 0x18d19bd0, 0x02c75db6, 0xfb95bd}}, + {{0x1bbf0e11, 0x029f5057, 0x167c4d06, 0x0de8e314, 0x0275bb81, 0x06ce9b41, 0x16f14801, 0x1b02351b, 0x664c14}}} + }, + { + /* 1*16^11*G: */ + {{{0x01ec6cb1, 0x1fd4bc5e, 0x0160f78c, 0x1acafb01, 0x1ca3cfee, 0x1f25f37f, 0x1372cd9e, 0x03b22093, 0xda67a9}}, + {{0x1a68be1d, 0x14f54713, 0x1dd0285f, 0x0f5b8a11, 0x180e5dec, 0x11fbf64b, 0x0af107d1, 0x06a902c8, 0x9bacaa}}}, + /* 3*16^11*G: */ + {{{0x15bc8a44, 0x17ee8328, 0x18546867, 0x0202ef97, 0x05fc7684, 0x12d25d2d, 0x168f4e15, 0x0b079f9d, 0x4d0180}}, + {{0x1adbc09e, 0x18fca648, 0x00b68d8b, 0x08408d0b, 0x03813969, 0x1d4003eb, 0x174d9fa6, 0x1831969e, 0x3a33c6}}}, + /* 5*16^11*G: */ + {{{0x05daeb00, 0x00d5a943, 0x1917ddaf, 0x07d6499c, 0x0e9d1592, 0x1b5139db, 0x055c20b2, 0x00fbeb9f, 0x2f6615}}, + {{0x033992c0, 0x113b3c4c, 0x174c2304, 0x1bdc4e32, 0x0add06ec, 0x09bd4100, 0x0cfdbbe5, 0x026dea56, 0xfd5c12}}}, + /* 7*16^11*G: */ + {{{0x116aa6d9, 0x01548504, 0x1c0b73c6, 0x05916c8e, 0x15a38366, 0x0ba6d0c1, 0x1f48953c, 0x0fa0bfc8, 0xf59411}}, + {{0x1f2e50cf, 0x1e834b75, 0x1ad6e1b1, 0x04ef107c, 0x0bceb7a9, 0x0ded584a, 0x0c4b3d97, 0x14add2e3, 0xcaa761}}}, + /* 9*16^11*G: */ + {{{0x1ac6f4c0, 0x15701cab, 0x12f71332, 0x1d1bd198, 0x0711dda4, 0x01c5f34b, 0x137b1387, 0x0fe3e9b3, 0xf2d4d}}, + {{0x12339b58, 0x1f3ddd6b, 0x090995b9, 0x14214f20, 0x1e71e8f6, 0x13fef9c0, 0x19345fec, 0x10afd27b, 0x3ec89f}}}, + /* 11*16^11*G: */ + {{{0x1f428cb2, 0x02a829fe, 0x088ad576, 0x1045facd, 0x0d0f1233, 0x10c0acac, 0x0ef47ade, 0x185c5429, 0x1d5dce}}, + {{0x095b189b, 0x12f68804, 0x18112947, 0x1d225f0e, 0x10b88bd3, 0x03c12437, 0x0314341e, 0x10762859, 0x6e5c40}}}, + /* 13*16^11*G: */ + {{{0x08381273, 0x1875447d, 0x04f6b97e, 0x06ed2c0d, 0x126ced84, 0x14d96620, 0x1d0e7f7a, 0x1fa6012c, 0x89d9a2}}, + {{0x1309773d, 0x01f59f76, 0x049cc928, 0x03ad015a, 0x162f58f6, 0x17903b67, 0x1d40ddea, 0x168a3acd, 0xdfca25}}}, + /* 15*16^11*G: */ + {{{0x15c71d91, 0x06195965, 0x0253f487, 0x08bc5c55, 0x0fece239, 0x0e0988ad, 0x0b8714a8, 0x10f074a1, 0x83191b}}, + {{0x02148a61, 0x05cf6047, 0x01117b9c, 0x023ea2ea, 0x0ae8a17a, 0x0f09e8be, 0x0dc2781b, 0x02ae7003, 0xe0ac7f}}} + }, + { + /* 1*16^12*G: */ + {{{0x1a37b7c0, 0x1aa2e660, 0x0441a7d5, 0x11a1ef76, 0x02151ec0, 0x0049af79, 0x13769b80, 0x15416669, 0x53904f}}, + {{0x022771c8, 0x0e584b58, 0x110d1a67, 0x133303c2, 0x13c1c139, 0x16656106, 0x01b62327, 0x1a179002, 0x5bc087}}}, + /* 3*16^12*G: */ + {{{0x08a2050e, 0x0d6217f2, 0x17e299dc, 0x1deaaec2, 0x19b89742, 0x14e63723, 0x0c625add, 0x1fa4978e, 0x673724}}, + {{0x061d3d70, 0x0864d248, 0x0d2730ae, 0x1759fa86, 0x06b6dbe6, 0x01604d44, 0x088080d2, 0x0af12d49, 0xe4cf82}}}, + /* 5*16^12*G: */ + {{{0x02de63bf, 0x1fb7241c, 0x098719b2, 0x15ea650e, 0x166a8e03, 0x05318fb0, 0x10c27966, 0x148e5be9, 0x4366ef}}, + {{0x017924cd, 0x16352047, 0x0a9b5ac0, 0x1618a4b5, 0x068eaf33, 0x09bf0981, 0x1f38bb89, 0x0137dc5a, 0x2e7dd9}}}, + /* 7*16^12*G: */ + {{{0x06f96190, 0x08293ff8, 0x125497bc, 0x005bd20f, 0x0a75fd1f, 0x0ab4b33d, 0x0c7e5ef9, 0x0c4f3235, 0x7bd753}}, + {{0x0bda00f6, 0x1e8b9025, 0x03a9a568, 0x1f98b83c, 0x027d6ce0, 0x123a4a1c, 0x0c2757b5, 0x167b774c, 0x8336f2}}}, + /* 9*16^12*G: */ + {{{0x16ad41ed, 0x1d8a98aa, 0x1c0d490c, 0x11586be9, 0x0dc92030, 0x00cf448c, 0x1706be8d, 0x0f7bb55a, 0x4f7e92}}, + {{0x1e642d57, 0x1a1abc33, 0x0bddd6f7, 0x0628e29d, 0x0f6a62e7, 0x006fa9d4, 0x0c4154a6, 0x0a2ad511, 0xdfe774}}}, + /* 11*16^12*G: */ + {{{0x07748690, 0x1d302893, 0x18c2c073, 0x0f209bb0, 0x13d007d5, 0x0958f6e9, 0x133252d1, 0x10cfa523, 0x2355cb}}, + {{0x0c89582b, 0x1e23a98a, 0x0724e451, 0x10d0b19a, 0x07c58582, 0x02f60ad5, 0x07e3d56e, 0x114b4e3c, 0x21c2f1}}}, + /* 13*16^12*G: */ + {{{0x0ade7f16, 0x0d7510b0, 0x0f80a31b, 0x1975d279, 0x15d24ae9, 0x0955b613, 0x15b004d6, 0x0b7367b4, 0xb6682}}, + {{0x08c56217, 0x0a221342, 0x19f34af6, 0x08781be7, 0x1a97fb72, 0x1b5d45ab, 0x0cffbce9, 0x17031c13, 0xa1fba0}}}, + /* 15*16^12*G: */ + {{{0x19060d5b, 0x08358114, 0x0e8e9f0a, 0x0c276238, 0x151cb904, 0x0d97ecfc, 0x08b2d842, 0x079e17a0, 0xf60204}}, + {{0x1af88f13, 0x17b7a858, 0x17468fdd, 0x14ada56f, 0x17bea37a, 0x07b25627, 0x1c66206a, 0x00d83e19, 0xf036b7}}} + }, + { + /* 1*16^13*G: */ + {{{0x1ad86047, 0x1fcacfa1, 0x06e2f2bb, 0x0a740875, 0x0906779b, 0x053bb265, 0x0e9dc673, 0x017a6b30, 0x8e7bcd}}, + {{0x0460372a, 0x108023f4, 0x1f5a2cfa, 0x111c5c8f, 0x1514579e, 0x08210654, 0x12ce500c, 0x016547b4, 0x10b777}}}, + /* 3*16^13*G: */ + {{{0x041ead4b, 0x1f443cd0, 0x06c0f07f, 0x0bdbf6d2, 0x076be3a7, 0x19a77d7f, 0x0dfb1c51, 0x019191e6, 0xbfc90c}}, + {{0x06fedaed, 0x02963784, 0x182b8f9d, 0x0d1dfe65, 0x02d36fb4, 0x18c6ea82, 0x1b4936e9, 0x163c139b, 0x7a9481}}}, + /* 5*16^13*G: */ + {{{0x15bb3b3e, 0x173de83b, 0x07dcf2c9, 0x0a6c2fdf, 0x13f5b507, 0x0c9f4616, 0x0a937296, 0x0397c7f5, 0x732df1}}, + {{0x17366693, 0x02bbf0f6, 0x11610db3, 0x1b5adac9, 0x13916e69, 0x12ac2012, 0x05df2df8, 0x07dbd1f3, 0x7f4190}}}, + /* 7*16^13*G: */ + {{{0x088dc3b9, 0x0fad9e0c, 0x1dd32671, 0x0cd249d2, 0x1ef6019a, 0x0420664b, 0x1ed0a36a, 0x172c0728, 0x4ce094}}, + {{0x05c0de52, 0x00a7bdb7, 0x1a81940b, 0x00b64193, 0x0aca2162, 0x06c0653b, 0x0cfb55ed, 0x1757e353, 0x5390f}}}, + /* 9*16^13*G: */ + {{{0x0aaafe5a, 0x01baf058, 0x026bb753, 0x08e1674a, 0x03fd0e25, 0x1354ad81, 0x1f7a5b62, 0x16edf8cc, 0x9a968e}}, + {{0x0ed975c0, 0x135fea12, 0x19c33033, 0x0b1014cb, 0x01d0ee7b, 0x13681ec3, 0x08a553f1, 0x00f4da77, 0xabf6fb}}}, + /* 11*16^13*G: */ + {{{0x134c6397, 0x1a8e5fcf, 0x1d33424d, 0x1a7b6a00, 0x0b22107d, 0x17c0129b, 0x06553c2b, 0x1da02e2c, 0xd3c6fb}}, + {{0x08cb3f9c, 0x0fe4682b, 0x1783802c, 0x18ee1120, 0x0b6468a0, 0x1ca6c975, 0x0bc65160, 0x18abcbc5, 0x4a0dd2}}}, + /* 13*16^13*G: */ + {{{0x0ac3137e, 0x0dc12c98, 0x0bd91186, 0x04b6c54b, 0x13953fe9, 0x0353b555, 0x0ee380bc, 0x13025248, 0x4cbde3}}, + {{0x0ec02fe6, 0x00478ae7, 0x03ab5830, 0x074f5cb1, 0x0c370d19, 0x1509a5ca, 0x05654024, 0x0c11e26c, 0x6ce554}}}, + /* 15*16^13*G: */ + {{{0x00fbf84b, 0x1d118367, 0x0fc2e291, 0x091f9778, 0x0cebb03c, 0x0e39860a, 0x091ee2f6, 0x0790562c, 0x4b9d33}}, + {{0x036c3c48, 0x1cf725a5, 0x1d6d7988, 0x0daa87c5, 0x1b7eb676, 0x1ecc133c, 0x054954c4, 0x1f7c4998, 0xfd7fc7}}} + }, + { + /* 1*16^14*G: */ + {{{0x19c43862, 0x1420f0ac, 0x05f99a42, 0x0fe9e307, 0x01bde71a, 0x00c344dc, 0x1c879b42, 0x069839bf, 0x385eed}}, + {{0x142e5453, 0x022c7f2a, 0x01b72330, 0x009dd841, 0x1f4576b3, 0x0f0cf4f5, 0x0fd59c07, 0x187d1d44, 0x283beb}}}, + /* 3*16^14*G: */ + {{{0x16e2d9b3, 0x05e98355, 0x0e358d45, 0x17250636, 0x1845641d, 0x15309ce7, 0x179d784b, 0x1e72f8e0, 0x19a314}}, + {{0x0baaaf33, 0x0a97712e, 0x012f95b5, 0x043a3a48, 0x128b3a50, 0x12fc43fa, 0x03748d25, 0x1ebb58e5, 0x6cacd8}}}, + /* 5*16^14*G: */ + {{{0x12f00480, 0x0c2c3f58, 0x00373b9f, 0x0b100942, 0x07219203, 0x0c31b27b, 0x0a8d5772, 0x0972b51b, 0x5840ed}}, + {{0x1e22cf9e, 0x0c96af15, 0x0b8e1c85, 0x0a44a8a5, 0x1d8daba7, 0x06f550ae, 0x05041e5a, 0x0d64417e, 0x670cda}}}, + /* 7*16^14*G: */ + {{{0x03f54c42, 0x0426f741, 0x068f7239, 0x1834784d, 0x0532545d, 0x060fa767, 0x03ec7716, 0x14a68d23, 0x9f5701}}, + {{0x0feb6a21, 0x1070e249, 0x067949e1, 0x1cf09564, 0x1bfdd89e, 0x0db5ab94, 0x195efee5, 0x171003b3, 0xce7b8f}}}, + /* 9*16^14*G: */ + {{{0x1522461a, 0x14a09994, 0x11c60c9f, 0x06144cb7, 0x0f9a0bcb, 0x0380833f, 0x13f006ee, 0x0d246b51, 0x27f611}}, + {{0x07301a2d, 0x152657ce, 0x018e511a, 0x103641b5, 0x08ee8e49, 0x02b093a1, 0x0b4ba923, 0x1532014d, 0xe512f1}}}, + /* 11*16^14*G: */ + {{{0x114f36b9, 0x07164d96, 0x0e7ce433, 0x119576c7, 0x0a981259, 0x197a1afa, 0x0504986f, 0x10ad9ddf, 0x640779}}, + {{0x04b4b50a, 0x0a58f74f, 0x073baca2, 0x1410e093, 0x07b6c8cd, 0x0bafec2d, 0x1bebbe43, 0x11e89cad, 0xda6192}}}, + /* 13*16^14*G: */ + {{{0x14d6b76f, 0x17b1efcc, 0x120576f3, 0x0b53f769, 0x1feae7fe, 0x1845e04e, 0x1a7d14bd, 0x1c6390ac, 0xa23750}}, + {{0x0dcca8da, 0x0ec96664, 0x0e123450, 0x00f823fb, 0x11113e77, 0x1d1f26a0, 0x19b6b0cb, 0x0294dcfb, 0xf7339b}}}, + /* 15*16^14*G: */ + {{{0x1ceee475, 0x1774e327, 0x1b5e0ac4, 0x15905081, 0x0ff41ab6, 0x0010f9e5, 0x1e357b4f, 0x00f47e46, 0xbbf1ac}}, + {{0x07fe5067, 0x03115004, 0x02d075a1, 0x173b1287, 0x15fe324e, 0x1e641b58, 0x03984220, 0x1bdd5a8c, 0xb4bfb8}}} + }, + { + /* 1*16^15*G: */ + {{{0x03fac3a7, 0x10376c36, 0x11fef271, 0x1bf094b2, 0x1fa180fd, 0x19d2209e, 0x06458df1, 0x17007d9e, 0x6f9d9}}, + {{0x1a842160, 0x03448301, 0x0a0400b6, 0x09ba5eb8, 0x1c4d47ea, 0x11518722, 0x06e9a6e3, 0x11cc060b, 0x7c80c6}}}, + /* 3*16^15*G: */ + {{{0x121ce204, 0x076baf46, 0x19d8f549, 0x0e4b1c84, 0x0f72fb2a, 0x06c2ce53, 0x193ee0dd, 0x1a2c5678, 0x43ca41}}, + {{0x134a8f6b, 0x09282274, 0x09291a39, 0x0d8f667d, 0x1a31f9ab, 0x07c90c6d, 0x0fe87194, 0x105c6e04, 0xdcea5a}}}, + /* 5*16^15*G: */ + {{{0x0be6efda, 0x07e74967, 0x1ca01659, 0x1a9fe7f0, 0x0506d922, 0x1b91bc2d, 0x0fd6d99b, 0x1de45125, 0x9c3e06}}, + {{0x07aefc7d, 0x18a07995, 0x0dbf7df7, 0x1790d0f6, 0x06fd5d43, 0x196a2671, 0x08f62bc2, 0x1cbcec52, 0xa7b709}}}, + /* 7*16^15*G: */ + {{{0x06c88be2, 0x0893d5ae, 0x1bb9789e, 0x18f041a0, 0x0775bea2, 0x13acec18, 0x1c0ceedc, 0x14627c41, 0x5d6f8a}}, + {{0x0d7ad75d, 0x0972a9f8, 0x0fe4b0a2, 0x16df1d4d, 0x0bc20eda, 0x1799d584, 0x13a31c6a, 0x11b1aada, 0xadc4b1}}}, + /* 9*16^15*G: */ + {{{0x12d1b844, 0x1449a8dc, 0x1cf9213c, 0x18070582, 0x08bc5c69, 0x0ae1e09c, 0x157f21ac, 0x186094c1, 0xf57d35}}, + {{0x01266837, 0x125d5deb, 0x04571a91, 0x0d2e4061, 0x0634c700, 0x09fad4f2, 0x1365e413, 0x13d531de, 0x707f3d}}}, + /* 11*16^15*G: */ + {{{0x1cc7cb09, 0x1a6803f9, 0x146d0d48, 0x0fd6d143, 0x071463bc, 0x10ff71ec, 0x1297d65b, 0x0f474cb2, 0x13e760}}, + {{0x08079160, 0x1f3ad450, 0x0d5d9046, 0x15c576cd, 0x0299d65e, 0x1eec2d9a, 0x02c78c97, 0x11bd1f77, 0x284dc8}}}, + /* 13*16^15*G: */ + {{{0x18cdee05, 0x03067092, 0x0bb0ee40, 0x0c3f642e, 0x0901da87, 0x12858d83, 0x0b989000, 0x044ad030, 0x29bea3}}, + {{0x062651c8, 0x12501acd, 0x11a638e7, 0x18636d91, 0x05ec7f9f, 0x0d9fdc38, 0x083aa402, 0x144d21d3, 0x7c40d9}}}, + /* 15*16^15*G: */ + {{{0x12f18ada, 0x0db63ab0, 0x16f6f304, 0x017b2777, 0x14a59d46, 0x17d7f99e, 0x039f670d, 0x0da47051, 0xf52178}}, + {{0x03953516, 0x0eb457e9, 0x16fc2607, 0x1de946d8, 0x1d1d6aa5, 0x10815e68, 0x0d5fb309, 0x17ec071b, 0xe0686f}}} + }, + { + /* 1*16^16*G: */ + {{{0x02d0e6bd, 0x1dbf073a, 0x03d794c4, 0x09a2c7b6, 0x16ecbf77, 0x0a3e0826, 0x18960a88, 0x00248789, 0x3322d4}}, + {{0x0c28b2a0, 0x079d174b, 0x01cebd89, 0x0bec7d45, 0x0f9b7280, 0x0cde26ed, 0x1bd6fec0, 0x12fd2cc9, 0x56e707}}}, + /* 3*16^16*G: */ + {{{0x059ab499, 0x1ece9f90, 0x1cf0cc2a, 0x065338dc, 0x101bc0b1, 0x0b59e33f, 0x16e97486, 0x1e602b80, 0x78baaf}}, + {{0x1ee097fd, 0x00e918c7, 0x0494665a, 0x065ddd1a, 0x0082e916, 0x027076c1, 0x02bebf2a, 0x1b7b60d8, 0xad4bdc}}}, + /* 5*16^16*G: */ + {{{0x1d06ace6, 0x049f0b67, 0x0e883291, 0x01366df0, 0x1ab1a237, 0x024c2494, 0x0f53082e, 0x0234295c, 0x6f70f2}}, + {{0x1602d5de, 0x045f69a5, 0x16b17b81, 0x052acd7c, 0x19f50753, 0x0c79a3dc, 0x0dcdbe57, 0x0612804f, 0x791e8a}}}, + /* 7*16^16*G: */ + {{{0x00ee1b40, 0x04771f73, 0x1a5891f7, 0x1a90b6e3, 0x1ccd48ce, 0x04f8c881, 0x1057e025, 0x1653ad54, 0xe1599d}}, + {{0x178f93a6, 0x0eb132f6, 0x0ca66778, 0x0c74e978, 0x1c7cfa63, 0x04a55517, 0x1283bebe, 0x0465503a, 0x793362}}}, + /* 9*16^16*G: */ + {{{0x09c00c3e, 0x00efd142, 0x048238be, 0x1d1e0792, 0x11859f00, 0x11699ea2, 0x05590d95, 0x12e0880d, 0xbb0b04}}, + {{0x11955a35, 0x0cd4c168, 0x1772429e, 0x0e089d20, 0x1b052fe6, 0x17d0bd58, 0x1d8d9574, 0x0b0a75f3, 0x4067e4}}}, + /* 11*16^16*G: */ + {{{0x05dd32e6, 0x073adce0, 0x0f97b9f8, 0x076aa36a, 0x05fbfc66, 0x0edf03ad, 0x027a6d92, 0x0aa832af, 0xdc5a41}}, + {{0x154a99b9, 0x073edbd3, 0x1926f3e0, 0x05dd20ed, 0x159442ff, 0x12f100df, 0x1e9db73a, 0x14c7f3ec, 0x4af3a8}}}, + /* 13*16^16*G: */ + {{{0x0544e7cb, 0x16a2dd62, 0x0a580d67, 0x0c844b6a, 0x14e99a10, 0x0a52b880, 0x0e76f8cd, 0x0e0730e7, 0x156e19}}, + {{0x0d250a37, 0x0a9c9605, 0x0d0e735b, 0x0dab1abd, 0x14be8949, 0x0b9531c1, 0x01f6a4e5, 0x13f1e632, 0x6bc08d}}}, + /* 15*16^16*G: */ + {{{0x059853ca, 0x1fde14e6, 0x066fd536, 0x12c4d73e, 0x1161348e, 0x1b511473, 0x0e09af29, 0x19d96d08, 0x4269bc}}, + {{0x15b8d367, 0x14ac77a9, 0x196a28f6, 0x12814bf3, 0x1a409fd3, 0x0654e218, 0x1adc8f21, 0x03505802, 0xed2b1c}}} + }, + { + /* 1*16^17*G: */ + {{{0x0134ab83, 0x10eba694, 0x190ce5dc, 0x167f35ee, 0x05868741, 0x1b86c4b3, 0x1f68af45, 0x0fa5bc16, 0x85672c}}, + {{0x190313a6, 0x07184a7b, 0x0a63d132, 0x1e2ff98a, 0x0c2e5e77, 0x024dfd31, 0x0bad8dd0, 0x136b6876, 0x7c481b}}}, + /* 3*16^17*G: */ + {{{0x03ba9000, 0x0f8391d4, 0x097a2dbf, 0x0e5f38d0, 0x0143dc48, 0x1b03ac20, 0x0305a121, 0x1f3ffe3b, 0xac3874}}, + {{0x0f10cf0a, 0x0d1e3ecb, 0x19ba7e93, 0x1c6a1afe, 0x17f93085, 0x0ef44a08, 0x01a6e18b, 0x04611438, 0xaa65e9}}}, + /* 5*16^17*G: */ + {{{0x0d06dbd4, 0x13037b37, 0x08834206, 0x1c7f0af1, 0x1e729ec0, 0x003af4d1, 0x004e6b45, 0x1cf54d0e, 0x570d5c}}, + {{0x1d1ed495, 0x132df675, 0x1182fb56, 0x0746db8c, 0x01bbbb68, 0x173388e8, 0x0bd816d9, 0x092841c0, 0xa6ae53}}}, + /* 7*16^17*G: */ + {{{0x092d230e, 0x1a60fc90, 0x04ce4a10, 0x1c6541a5, 0x06ef5dae, 0x114f701b, 0x0edbe1f0, 0x0e0504d1, 0x75b5f8}}, + {{0x151570b8, 0x1be5efde, 0x047e3ec0, 0x0f49600a, 0x1fa8e026, 0x03a2aa6e, 0x148d8f5e, 0x043c74f0, 0x527cce}}}, + /* 9*16^17*G: */ + {{{0x034fcc0e, 0x1db5fb56, 0x18e21311, 0x1e214857, 0x059c8927, 0x1c0713e9, 0x1b0ac296, 0x1f5c3dbb, 0x44fc8e}}, + {{0x119c420a, 0x07f818f3, 0x14122767, 0x1d294979, 0x1d3d7720, 0x13f3c419, 0x0d9f9249, 0x12975362, 0xd2c7de}}}, + /* 11*16^17*G: */ + {{{0x0bb69991, 0x0d240fe4, 0x0c1c5d75, 0x167f4586, 0x0f535fff, 0x0b310701, 0x0f1a19a8, 0x08f759cf, 0xdea2ba}}, + {{0x116fe2df, 0x033a154f, 0x07b66f1c, 0x1b9b66c4, 0x1b9e7229, 0x1ff6427b, 0x0392b172, 0x1adb2185, 0xae28bf}}}, + /* 13*16^17*G: */ + {{{0x114fc9cc, 0x01206812, 0x09185963, 0x00157853, 0x0a83626d, 0x07b6625c, 0x035bbf07, 0x1314dc2d, 0x3968fc}}, + {{0x1fad37dd, 0x0c9ca44a, 0x023ccd00, 0x08caedff, 0x132c91a1, 0x0c168d56, 0x04a048de, 0x1a1b696b, 0x789cbb}}}, + /* 15*16^17*G: */ + {{{0x0e0c85f1, 0x17610c8d, 0x0e8e8942, 0x0a5ca6c0, 0x145d5a6a, 0x0ef84abc, 0x08a49ac9, 0x00d31c0d, 0x689691}}, + {{0x0dc1de21, 0x03a41199, 0x1540e48b, 0x01833b41, 0x1d72d1f4, 0x02e0ec22, 0x1e495bcc, 0x16967192, 0xaefd3f}}} + }, + { + /* 1*16^18*G: */ + {{{0x00c82a0a, 0x1ecacd7b, 0x19a20cbf, 0x044d8c1e, 0x013b10f9, 0x04f8c8ca, 0x0291ac1b, 0x10136331, 0x948bf}}, + {{0x18c8e589, 0x065bfc46, 0x1f34afb5, 0x1bfe1192, 0x1418c6d4, 0x1a62e8e1, 0x191b71ad, 0x10adb96c, 0x53a562}}}, + /* 3*16^18*G: */ + {{{0x18c8ac7f, 0x1417f2fd, 0x18aa949c, 0x0485dccb, 0x0f849641, 0x1cb6902b, 0x0ef2d70c, 0x1f7c7045, 0x9945b2}}, + {{0x09aea3b0, 0x16ca1d0b, 0x16b37ea5, 0x1ef447dd, 0x0eff5282, 0x1a27fd94, 0x00b581f6, 0x104961e5, 0x3eefed}}}, + /* 5*16^18*G: */ + {{{0x169e353a, 0x08ebcf1c, 0x0ef87dbb, 0x008810a5, 0x1d5fe10a, 0x01113883, 0x03988d7e, 0x0d640b0e, 0x2a314c}}, + {{0x05746067, 0x12c9370f, 0x09962ff0, 0x14a955b6, 0x01ba0138, 0x1f23b5d5, 0x1eb06918, 0x017e6b44, 0x15a4ac}}}, + /* 7*16^18*G: */ + {{{0x09b84966, 0x0f17a4f7, 0x1fcffe62, 0x1e820dba, 0x16c911b4, 0x17d79d35, 0x10b5262d, 0x0016e07f, 0x5959a5}}, + {{0x07473a6a, 0x0533190c, 0x1f890990, 0x01b98119, 0x02a70910, 0x094106e4, 0x025fe50c, 0x0e83eb95, 0x370e6}}}, + /* 9*16^18*G: */ + {{{0x0bc6b173, 0x1864dc91, 0x1b4fface, 0x10c478fd, 0x15f9d7dd, 0x0623182d, 0x1fa38458, 0x0726e445, 0x9eeb31}}, + {{0x1723a71d, 0x09cb106c, 0x19c312e5, 0x0e357da1, 0x01ae30ea, 0x15fedddd, 0x163654aa, 0x1c0221da, 0xe121f1}}}, + /* 11*16^18*G: */ + {{{0x1ec805f3, 0x1b9221fb, 0x16656455, 0x081eea13, 0x097887de, 0x191c4037, 0x03d45bae, 0x1c98fee3, 0x39cc4f}}, + {{0x1a3c48d3, 0x0c8d0609, 0x1244b934, 0x12ae6e7f, 0x0de7dbbb, 0x1349e7a1, 0x03cc5479, 0x058b48df, 0xecb147}}}, + /* 13*16^18*G: */ + {{{0x0e22580e, 0x1eb17dae, 0x15be21e4, 0x06512da5, 0x1d4d2c71, 0x1fef326d, 0x11a5a24b, 0x0e8cdd9b, 0xf94c80}}, + {{0x1127db82, 0x0f291fb3, 0x0dc753a0, 0x0fd88a64, 0x081f6988, 0x1ffb55ad, 0x096a4652, 0x1b8cf0a4, 0x5e9c7f}}}, + /* 15*16^18*G: */ + {{{0x10c4f21f, 0x1f205d68, 0x00f69e9a, 0x0952b29a, 0x199d2502, 0x10346d8c, 0x058bf300, 0x0d8d5aba, 0x8cccb8}}, + {{0x129dfea0, 0x1f893753, 0x192f8a3d, 0x05b95904, 0x158a54b9, 0x17aa4e43, 0x110da66f, 0x0b0c4ea3, 0x57f896}}} + }, + { + /* 1*16^19*G: */ + {{{0x138fd8e8, 0x0766c0cf, 0x1a5d4ab3, 0x01c89bf8, 0x073a8f1b, 0x1e707814, 0x070d3c19, 0x0fe8c300, 0x6260ce}}, + {{0x12b4ae17, 0x0d4274ad, 0x14706630, 0x05244700, 0x01ef7ecd, 0x0824bbb5, 0x15c69fc2, 0x056df4b6, 0xbc2da8}}}, + /* 3*16^19*G: */ + {{{0x01136602, 0x185d232a, 0x078e96ee, 0x08de901b, 0x13b3d38c, 0x049bf919, 0x1f0cf416, 0x0500905b, 0x87d127}}, + {{0x18af6aac, 0x1b41e20e, 0x14efdf13, 0x1108e8df, 0x1745387a, 0x13b67fb3, 0x0f7a49a8, 0x10e14b40, 0x71ce24}}}, + /* 5*16^19*G: */ + {{{0x08c5a916, 0x1902eb9a, 0x15843c96, 0x18881aa6, 0x14aa13f5, 0x0631ed5a, 0x05d1ac2b, 0x07fc4c3d, 0xfd5d7d}}, + {{0x1adb8bda, 0x0a59bd83, 0x13dbeaac, 0x0e70297b, 0x1b52fe5d, 0x1e533ce3, 0x0c1f4ad0, 0x1a1dd6ab, 0xdd83e}}}, + /* 7*16^19*G: */ + {{{0x15f7529c, 0x0ecc84b1, 0x0f547751, 0x0cb7316b, 0x04381387, 0x09e1969a, 0x1848ae91, 0x02130384, 0xde0dd4}}, + {{0x04cd88fe, 0x1e106017, 0x06ddd018, 0x12498d11, 0x03570178, 0x04c116bd, 0x117e6c84, 0x13a21442, 0xd70a6e}}}, + /* 9*16^19*G: */ + {{{0x18f76d11, 0x0417a469, 0x014555cf, 0x02001f7f, 0x092a2151, 0x1f7e1ce1, 0x1139a716, 0x115b499e, 0xb26c20}}, + {{0x03b7356b, 0x00e0058f, 0x009892f7, 0x0060cb3b, 0x16433050, 0x143ec866, 0x0cf3c313, 0x1041293a, 0x1f1cf8}}}, + /* 11*16^19*G: */ + {{{0x069e22db, 0x0a71d833, 0x07224cfe, 0x127feb3f, 0x0294eac9, 0x1e9480c6, 0x148e9f8e, 0x171f8ffe, 0xfceb14}}, + {{0x1c2260a1, 0x1ab96d92, 0x13af932f, 0x0a1cf274, 0x10924ad5, 0x12d3a92f, 0x0347f362, 0x07481ad7, 0x64aa6b}}}, + /* 13*16^19*G: */ + {{{0x038a2755, 0x18420bcd, 0x1271541b, 0x0434e2ca, 0x07faa537, 0x1abba5a8, 0x12b15705, 0x0c4799ab, 0x4e909a}}, + {{0x03cca3de, 0x1bd24fb1, 0x0a60032b, 0x1aa729a4, 0x159dbb6c, 0x036ea83a, 0x16bb3bc9, 0x10f199c6, 0xae56da}}}, + /* 15*16^19*G: */ + {{{0x183ba64d, 0x012c4acc, 0x116568ea, 0x0c1eef11, 0x040e2bde, 0x10a2f5c7, 0x100efec2, 0x0b845c09, 0xc36745}}, + {{0x0a2181fd, 0x0a6647cb, 0x1de3c518, 0x04d55942, 0x164c7426, 0x0737426c, 0x00cc2038, 0x1a0d396c, 0x3a520a}}} + }, + { + /* 1*16^20*G: */ + {{{0x0037fa2d, 0x0a9e6469, 0x0ff710ca, 0x1d91eaeb, 0x14103043, 0x0420a5df, 0x0350f60d, 0x1c15f83b, 0xe5037d}}, + {{0x1d755bda, 0x072ee420, 0x1207c438, 0x1eb607d8, 0x10bddbd5, 0x0684fdcc, 0x0ed7e7e6, 0x0975529a, 0x457153}}}, + /* 3*16^20*G: */ + {{{0x177e7775, 0x04545370, 0x1b657d8e, 0x02ab2711, 0x091aeb5e, 0x01dd67a9, 0x0f3b9615, 0x075ff2c6, 0x9d896a}}, + {{0x1a056691, 0x1e7b69d5, 0x06494efb, 0x139afdc5, 0x0927de89, 0x1276b928, 0x1c2e53a5, 0x1c87e937, 0xdd91a9}}}, + /* 5*16^20*G: */ + {{{0x1c2a3293, 0x1ef026f1, 0x00d1db17, 0x1170ddd2, 0x0f4cd568, 0x052b9941, 0x1e4b43ac, 0x1dce22c6, 0x8327b8}}, + {{0x0e0df9bd, 0x1e42a70c, 0x0c9a905a, 0x1fb569dc, 0x1708496a, 0x1f53313c, 0x063862ec, 0x04cddc15, 0x4997e}}}, + /* 7*16^20*G: */ + {{{0x0562c042, 0x010d9362, 0x037ec689, 0x1a464697, 0x08ed6092, 0x130ec7cd, 0x05a25f59, 0x15454db6, 0x5ae42a}}, + {{0x0f79269c, 0x082e66fc, 0x1f3636fe, 0x01b72a20, 0x09d4a94e, 0x0eee301c, 0x147aad70, 0x0f80bfe0, 0x99d93a}}}, + /* 9*16^20*G: */ + {{{0x1e85af61, 0x1a440942, 0x12b9d9ac, 0x1dae45ba, 0x01b0f4e8, 0x1b47fb61, 0x03ad66ba, 0x1c84d439, 0x92c23a}}, + {{0x036a2b09, 0x1391b34e, 0x0a1bfb53, 0x075b056c, 0x0d5792d2, 0x0beae39c, 0x0ed027c8, 0x11e02aa3, 0x414cf8}}}, + /* 11*16^20*G: */ + {{{0x07b5eba8, 0x11578d96, 0x063a8db3, 0x17db8ff2, 0x0df422da, 0x1a0bb57c, 0x1c422343, 0x118ed5fb, 0xfee560}}, + {{0x0d0b9b5c, 0x1a8ae9b4, 0x04151e4f, 0x01fe857f, 0x1c14ee38, 0x017cc943, 0x02bec450, 0x12269fcb, 0x380759}}}, + /* 13*16^20*G: */ + {{{0x1c63caf4, 0x0f1dd259, 0x1d4f54a0, 0x1fe75651, 0x06afca28, 0x09da6315, 0x1f988284, 0x1d725ccc, 0x42e544}}, + {{0x169c29c8, 0x03d7604c, 0x1bf17c46, 0x0a1cf6d7, 0x15e7873a, 0x11060ba0, 0x19c7dc7c, 0x1c1f2398, 0x9ff854}}}, + /* 15*16^20*G: */ + {{{0x1e0f09a1, 0x0515ecc2, 0x100ca0e0, 0x0213e372, 0x00efef0a, 0x17695238, 0x138e0e65, 0x16ccaa65, 0x7aed83}}, + {{0x05857d73, 0x02ec66f4, 0x0fd29501, 0x165e601e, 0x12d8ed88, 0x1e855881, 0x1df1f76b, 0x0bf3463d, 0xf5b854}}} + }, + { + /* 1*16^21*G: */ + {{{0x04fce725, 0x0c335057, 0x09b16dc9, 0x11b7a38d, 0x171b4e7e, 0x082f478b, 0x1eb7d7aa, 0x161e9440, 0xe06372}}, + {{0x0eee31dd, 0x1381a7ca, 0x04121c2c, 0x1094ef0e, 0x0488cd74, 0x1dd956ad, 0x13f84a89, 0x0e979c31, 0x7a9089}}}, + /* 3*16^21*G: */ + {{{0x1a328d6a, 0x1d540c46, 0x0b7062f6, 0x1aee752b, 0x08fa7b24, 0x04c8c2d8, 0x18028d1a, 0x0b74c469, 0xc663c0}}, + {{0x1ec9b8c0, 0x1d85db55, 0x0afe7308, 0x0aa3d4a2, 0x11317dd8, 0x1ee793ab, 0x10e34e6b, 0x11abee43, 0x3331e9}}}, + /* 5*16^21*G: */ + {{{0x1996de2f, 0x048e4241, 0x0c94452f, 0x1dbe5dc1, 0x1e4e977c, 0x186f7507, 0x091673ac, 0x105bf70d, 0xd3fc26}}, + {{0x14526f8c, 0x0249120e, 0x1eafc5a3, 0x136931be, 0x181da4e5, 0x03aa6a7b, 0x0863da2d, 0x13348be1, 0xc4f0df}}}, + /* 7*16^21*G: */ + {{{0x03e697ea, 0x03624688, 0x17e0fa17, 0x0cf1f730, 0x1abd19ce, 0x0ff64d1f, 0x008df728, 0x087fd658, 0xc17a4b}}, + {{0x1edc6c87, 0x171dc3ee, 0x07c0aac3, 0x03436aff, 0x01fae96e, 0x1c7b8cb0, 0x05532b85, 0x05aab56b, 0x39355c}}}, + /* 9*16^21*G: */ + {{{0x1163da1b, 0x16961811, 0x04e8c460, 0x1dbdcc1f, 0x11fde9a0, 0x1a4ebfe0, 0x02d1a324, 0x0f944cf2, 0x8f618b}}, + {{0x03bdd76e, 0x1f989088, 0x126db9f1, 0x018cd464, 0x05a42645, 0x0d3a6bd6, 0x0dbad7ef, 0x04be117d, 0x78233f}}}, + /* 11*16^21*G: */ + {{{0x0ec8ae90, 0x142c87f0, 0x0ef177bb, 0x04d725d1, 0x1f1b8cfb, 0x0dc6d641, 0x19bae1b5, 0x1e2b6f43, 0x9798c0}}, + {{0x052844d3, 0x14d61757, 0x0c62389e, 0x092cb7a0, 0x073bee2e, 0x04a4a7ce, 0x1b4f74bb, 0x154eb485, 0xba40e2}}}, + /* 13*16^21*G: */ + {{{0x11d66b7f, 0x0d0cbc78, 0x01f72041, 0x0d24a0a3, 0x084757aa, 0x0dc85c49, 0x159d1f3c, 0x1c7f6b45, 0xfdfa6e}}, + {{0x18e5178b, 0x1547c033, 0x15e37a76, 0x0df3ba27, 0x018c4d84, 0x00e4d1ed, 0x036e4f03, 0x03c44933, 0x4d9cf3}}}, + /* 15*16^21*G: */ + {{{0x1265bcf0, 0x003abc24, 0x071f4c2e, 0x1c56f082, 0x1220e69c, 0x14d230e7, 0x190eb77a, 0x071bc453, 0xfd58ce}}, + {{0x0b996292, 0x19f3d4e7, 0x1c73477c, 0x0c37fc51, 0x1e4fb872, 0x155cd242, 0x056f54e0, 0x1ca6ec64, 0xffe0a5}}} + }, + { + /* 1*16^22*G: */ + {{{0x10559754, 0x056b4846, 0x08fd6150, 0x0217bbc5, 0x0e02204b, 0x1dfcee06, 0x114d6342, 0x0e2b9aba, 0x213c7a}}, + {{0x14b458f2, 0x1f9613a9, 0x1a9fbb77, 0x10a1ebe6, 0x1a190bb4, 0x1683122d, 0x0941c04e, 0x016b5c8c, 0x4b6dad}}}, + /* 3*16^22*G: */ + {{{0x1e05dccc, 0x196c008c, 0x066a4f94, 0x1f47da98, 0x1d172ae3, 0x104b5ca9, 0x00c2551b, 0x1c2ea7b4, 0xb8cef6}}, + {{0x0c6d5750, 0x00a5067e, 0x1ada04cc, 0x0aff86d4, 0x0bd99df7, 0x053a7269, 0x0efda935, 0x0c14d993, 0x302b8a}}}, + /* 5*16^22*G: */ + {{{0x173bb31a, 0x0deff709, 0x16e5ed21, 0x1ef6d6bf, 0x15f49701, 0x05ef175d, 0x0e1780a8, 0x1cef368e, 0x3fb33}}, + {{0x1d215c9e, 0x1a65f34b, 0x1d903538, 0x14f8ed88, 0x1572bc65, 0x0b0d55dd, 0x18a07830, 0x0a4a91df, 0xf36ad9}}}, + /* 7*16^22*G: */ + {{{0x1d9a4ab4, 0x0da8af5e, 0x16edf029, 0x186d830a, 0x17a36717, 0x17bda687, 0x184587c5, 0x1a213d87, 0x4b177c}}, + {{0x035ab6f7, 0x156eff23, 0x1d07d562, 0x0fc4abe2, 0x06b486e3, 0x1b3949b1, 0x0997f6a3, 0x1d34bc5f, 0x3ec966}}}, + /* 9*16^22*G: */ + {{{0x101c23a4, 0x1a37d94d, 0x09273d5b, 0x1482b08f, 0x1cd75c22, 0x1c14dcdc, 0x081b0a80, 0x0a40d44f, 0x5e8703}}, + {{0x10d986f9, 0x10bccb58, 0x1333f684, 0x0e3b0e94, 0x06e2da21, 0x0ae8d716, 0x08879c5d, 0x09df7392, 0x5b9664}}}, + /* 11*16^22*G: */ + {{{0x007b0c66, 0x19f9ae90, 0x1b21bec7, 0x0ffdc8ca, 0x0eb7434a, 0x1227b056, 0x002911c8, 0x003261ad, 0xe545c3}}, + {{0x11f2d470, 0x03fabe93, 0x1688e776, 0x0b051c36, 0x1bcbf97e, 0x17ed1cac, 0x1579f971, 0x01d18c52, 0xe06a34}}}, + /* 13*16^22*G: */ + {{{0x03648bba, 0x12c0c85c, 0x10f1d112, 0x01e160d3, 0x1b39882a, 0x17112f80, 0x160284cf, 0x02af4a9e, 0xb2a442}}, + {{0x00fb6452, 0x037d5a50, 0x0705eec9, 0x10aa2a39, 0x17e31c0d, 0x1e4bc7de, 0x19867a7e, 0x1856d26c, 0xfe4f5f}}}, + /* 15*16^22*G: */ + {{{0x0bab27d0, 0x1b6e9177, 0x11440ff3, 0x1683f458, 0x17007f70, 0x180c8c6c, 0x01946ea5, 0x01e7a8a7, 0x1b908e}}, + {{0x00d3d110, 0x0f433af2, 0x1f946d5c, 0x179526b0, 0x04b9ab5b, 0x1e48c0be, 0x0d79bcd5, 0x0bdd88cd, 0x9b6d62}}} + }, + { + /* 1*16^23*G: */ + {{{0x08fbd53c, 0x0661d1d8, 0x118b377c, 0x0718e15b, 0x19a87e28, 0x09a952a0, 0x0d3a36ee, 0x054f5e96, 0x4e7c27}}, + {{0x17dcaae6, 0x059ca0c0, 0x1df74cf8, 0x172c297f, 0x1681b530, 0x084fb6f7, 0x0c6385bf, 0x0ecd93a1, 0x17749c}}}, + /* 3*16^23*G: */ + {{{0x0521b3ff, 0x114c2327, 0x0a9d433b, 0x180e2fd3, 0x024a5233, 0x0695f4d7, 0x1571d791, 0x06021928, 0x2484e}}, + {{0x0269da7e, 0x00d725c8, 0x0eb22ef3, 0x000dbd24, 0x10eebad7, 0x159e1596, 0x0c341fb0, 0x1415447c, 0x9619d0}}}, + /* 5*16^23*G: */ + {{{0x004ba7b9, 0x0b5bcfd4, 0x1d05d47e, 0x0d48e060, 0x0954e20d, 0x1b86b396, 0x195fbde6, 0x04d9f6d9, 0x16c1c5}}, + {{0x1c5bd741, 0x1adf1d28, 0x126a7311, 0x0ab8037b, 0x094deec7, 0x13a2ce45, 0x10e41898, 0x0f868062, 0xdb157f}}}, + /* 7*16^23*G: */ + {{{0x036683fa, 0x01b243d5, 0x1b02097a, 0x0436a701, 0x07b66958, 0x0e73ba29, 0x06be1ea2, 0x1aea7f26, 0x3973c}}, + {{0x1f4a577f, 0x1afd95bb, 0x1a6077b7, 0x1109c31f, 0x1a26cd77, 0x095b195a, 0x0e8d90f8, 0x05986194, 0x38cf5a}}}, + /* 9*16^23*G: */ + {{{0x00bf6f06, 0x0059ccce, 0x010ed5c6, 0x1826644a, 0x05765713, 0x027a5810, 0x054470b0, 0x174e5d9e, 0xba6a9b}}, + {{0x181db551, 0x195f7b83, 0x1a5eabf8, 0x1a29ef58, 0x0bd8e9e5, 0x05f972ac, 0x06c0c808, 0x07166942, 0x13771e}}}, + /* 11*16^23*G: */ + {{{0x11231a43, 0x08f7cf83, 0x0b50ee7f, 0x0a29accb, 0x0442f44d, 0x0ca8326f, 0x174b62bb, 0x1984f989, 0x35c5bc}}, + {{0x1620c8f6, 0x0db97228, 0x0f3c2b9f, 0x0f49980c, 0x0589d4cf, 0x0c105b7d, 0x1b39cd39, 0x0e4772e8, 0xe3675}}}, + /* 13*16^23*G: */ + {{{0x1dd40609, 0x1a05e2b7, 0x00735daf, 0x0321301b, 0x0356ac74, 0x1897e2c4, 0x1af2848b, 0x048b8ab0, 0xfd479c}}, + {{0x0a64ca53, 0x0f1789b3, 0x07291ce7, 0x075dae4c, 0x041fd911, 0x0bd21e4c, 0x1fbfcb2b, 0x16a4d295, 0x3069cf}}}, + /* 15*16^23*G: */ + {{{0x0b799a7f, 0x0db817a9, 0x0e3a1093, 0x116d9aa7, 0x07d544f1, 0x075cd796, 0x0192f7b6, 0x0547599b, 0x6c4000}}, + {{0x13c81e32, 0x1ae64166, 0x0120fda2, 0x157a9904, 0x1dcbdc07, 0x01b5070e, 0x16f9a42e, 0x02a616c6, 0x95c2dc}}} + }, + { + /* 1*16^24*G: */ + {{{0x00fb27b6, 0x1213f142, 0x10c15d8c, 0x1c7b657c, 0x06aa5c76, 0x1c56b0b4, 0x0c6c43c8, 0x07b7cef1, 0xfea74e}}, + {{0x123cb96f, 0x00e9edbf, 0x0fdedddc, 0x16b2d72e, 0x0af93126, 0x1a6f665b, 0x0ca5f3d9, 0x1b736162, 0x6e0568}}}, + /* 3*16^24*G: */ + {{{0x1e889756, 0x0ec0d74d, 0x0012ec97, 0x16c932f6, 0x099f3f27, 0x0cbd938c, 0x1aa089b3, 0x1866423f, 0x762e8b}}, + {{0x1ca6b774, 0x0f12cf03, 0x013e9789, 0x05b66291, 0x0e347197, 0x0278a4c1, 0x05f0f1f3, 0x04c15e7d, 0xc02894}}}, + /* 5*16^24*G: */ + {{{0x0975d2ea, 0x17baf4b8, 0x053a3a89, 0x0559f420, 0x0f4a91e5, 0x1edd9184, 0x14d23866, 0x08fbec12, 0xdf077d}}, + {{0x11936f95, 0x11e16cf1, 0x0f749dea, 0x1d8b709f, 0x0527c8a1, 0x012e4c51, 0x1d109321, 0x11001def, 0xf8617a}}}, + /* 7*16^24*G: */ + {{{0x1c4c92d7, 0x1c248fdd, 0x10e46d16, 0x169addca, 0x1142935d, 0x0f5419a5, 0x080cb85f, 0x0eb17a7b, 0x9f3e7d}}, + {{0x114906dd, 0x05ddfe7d, 0x0538461b, 0x144607ad, 0x11502452, 0x1590e5d5, 0x19ad6218, 0x03d4efa8, 0xecd284}}}, + /* 9*16^24*G: */ + {{{0x12a8c483, 0x1706b995, 0x0102b0d6, 0x1619118a, 0x15281174, 0x01e9177c, 0x1e7b70e3, 0x0baf6b99, 0xa0cc79}}, + {{0x12cc6ba9, 0x04b3a2ac, 0x1a4d8154, 0x091e37be, 0x1df786b3, 0x07e4b918, 0x1cfb88dd, 0x045f1670, 0xabc301}}}, + /* 11*16^24*G: */ + {{{0x05dd3aee, 0x1878db5e, 0x05b4bc85, 0x0a75151f, 0x176ca131, 0x154d6354, 0x1f338388, 0x14a2aa78, 0x6d1c50}}, + {{0x1df597f7, 0x171aa727, 0x1b54eb7f, 0x1c621551, 0x1d474851, 0x19001143, 0x1f725dc9, 0x11c0d57b, 0xafff14}}}, + /* 13*16^24*G: */ + {{{0x04a6d0bb, 0x1c654dbf, 0x086bf719, 0x1a6245eb, 0x0418f659, 0x136c5453, 0x07cfcc46, 0x0c3172ff, 0x5e5f1d}}, + {{0x1033eaf9, 0x141c23c8, 0x1bd94e85, 0x0abe5ca0, 0x121da725, 0x15e68273, 0x1bdcd63d, 0x0560d4fc, 0xd7b150}}}, + /* 15*16^24*G: */ + {{{0x0f005e3f, 0x0d4daf22, 0x10e6f4b7, 0x0d1c637d, 0x1a1495af, 0x05cd6700, 0x09ffff4f, 0x0d6782c8, 0xf8138a}}, + {{0x0f357eb7, 0x16bf0101, 0x12f884d0, 0x18837aaa, 0x1cb51f4e, 0x0af2bd52, 0x0f67e740, 0x077df69d, 0xca758f}}} + }, + { + /* 1*16^25*G: */ + {{{0x17bdde39, 0x16015220, 0x1810ca54, 0x09c2f36e, 0x168d3154, 0x0b86accc, 0x1c384289, 0x027ecef9, 0x76e641}}, + {{0x1901ac01, 0x058ba968, 0x1b480cad, 0x1467a56a, 0x1f0d35e2, 0x136b8340, 0x173d5dc1, 0x11bdc9d2, 0xc90ddf}}}, + /* 3*16^25*G: */ + {{{0x0078ee8d, 0x182848e6, 0x1a46510b, 0x1e419ca0, 0x14ff64eb, 0x1931d54d, 0x06f897fd, 0x15b0b3b5, 0xd08e57}}, + {{0x0da63e86, 0x0cbfa6e1, 0x08bb677a, 0x1def9f28, 0x06df4123, 0x19773abf, 0x035cb585, 0x13095691, 0x852e97}}}, + /* 5*16^25*G: */ + {{{0x029129ec, 0x0c8a3382, 0x12095205, 0x1c759e3c, 0x11d080ca, 0x1f407669, 0x149d7d62, 0x10bc9a89, 0x7da6c0}}, + {{0x0cd9ff0e, 0x1a857715, 0x12961aba, 0x11810ca9, 0x027bf044, 0x0103a48b, 0x015d4474, 0x0d773e83, 0xf49814}}}, + /* 7*16^25*G: */ + {{{0x11654f22, 0x1c1ea4aa, 0x06abba53, 0x0fe72846, 0x1d94fb2f, 0x0800df34, 0x19b886fa, 0x19feb837, 0x90d090}}, + {{0x001a43e1, 0x1aef02bb, 0x08fe1d03, 0x0c6aca7b, 0x170336dd, 0x010f035f, 0x186a54fc, 0x03a5759e, 0xcd569a}}}, + /* 9*16^25*G: */ + {{{0x076b19fa, 0x1b77b28e, 0x020675c6, 0x0dc0da0d, 0x1292ed9d, 0x16188410, 0x07b31cc8, 0x0b0f9e3a, 0xda4798}}, + {{0x126f5af7, 0x15137759, 0x14ff081a, 0x17a27d2a, 0x0569ea67, 0x1483bf0b, 0x1c0745cd, 0x0f137995, 0xebb1d7}}}, + /* 11*16^25*G: */ + {{{0x19135dbd, 0x0c97db2d, 0x1618c7b3, 0x010f5e73, 0x1897cf0c, 0x157ac174, 0x19ab605e, 0x00951bbd, 0xe3e475}}, + {{0x0748045d, 0x083579f2, 0x12576a5a, 0x0405febd, 0x03ffea5a, 0x040ca95c, 0x1b102e63, 0x1f013978, 0x930a5b}}}, + /* 13*16^25*G: */ + {{{0x0dee455f, 0x1f85cf2e, 0x13901d72, 0x0fffcdd1, 0x1db4aff6, 0x099c7c05, 0x06c291d1, 0x0dfd0e15, 0x7e8c65}}, + {{0x171b9cba, 0x19ef4cc0, 0x1d1989c5, 0x05a2ce8d, 0x1a53b4aa, 0x1b07a401, 0x103ca8fd, 0x0659460e, 0xbdddc6}}}, + /* 15*16^25*G: */ + {{{0x0698b59e, 0x1bcb5cdb, 0x0d11e90d, 0x06b24b12, 0x1c7260a3, 0x01ad59f1, 0x1ac56fac, 0x1f12352b, 0x3df841}}, + {{0x0b92baf5, 0x07c733cb, 0x12527e2f, 0x190cf642, 0x0f3867bf, 0x1d74788e, 0x0307680a, 0x1bf31612, 0xb38fe6}}} + }, + { + /* 1*16^26*G: */ + {{{0x0bcbb891, 0x158a8121, 0x09b2fb8e, 0x198c87be, 0x18f9a8f7, 0x0dd53a1f, 0x0f87a0a0, 0x0d607655, 0xc738c5}}, + {{0x099a84c3, 0x1f39aecb, 0x0033fa45, 0x029ddef1, 0x1bbbb823, 0x0797565f, 0x094dfdc6, 0x0f12a35a, 0x893fb5}}}, + /* 3*16^26*G: */ + {{{0x0761d58d, 0x05d5799c, 0x15838bcd, 0x1937c811, 0x0df7aca4, 0x0051ab90, 0x05184289, 0x04f047ec, 0xb8c461}}, + {{0x1d1051a4, 0x1c7505d4, 0x041f16d8, 0x0a08f0bc, 0x1c5053f7, 0x0c7b4bf9, 0x0df45291, 0x0d8a2e1c, 0x8f9ed9}}}, + /* 5*16^26*G: */ + {{{0x050b0040, 0x0d859820, 0x04d2b70b, 0x08fb73e0, 0x03671f3f, 0x04c9ff4d, 0x07de8d43, 0x13ee204e, 0x8d56e}}, + {{0x1b3fd0a1, 0x0c7133d2, 0x05e0afad, 0x0f88e41c, 0x1b285f6d, 0x0a8546bc, 0x0a887ff4, 0x15d7a153, 0xa12185}}}, + /* 7*16^26*G: */ + {{{0x13573b7f, 0x0b2b1ef8, 0x02d89c3d, 0x1438bda6, 0x05a37889, 0x07cbbdf3, 0x198d0788, 0x065a85f9, 0xdc13f2}}, + {{0x0c1f2ba6, 0x15669142, 0x1012c710, 0x0aa8e02e, 0x10a76704, 0x086d4254, 0x1030f1d0, 0x100853c6, 0xc909ba}}}, + /* 9*16^26*G: */ + {{{0x07ae9ceb, 0x017ac85c, 0x1bcb452c, 0x1843d4e1, 0x119a8226, 0x0ab2ed90, 0x1c1cebc6, 0x1cc03bef, 0x25c02d}}, + {{0x0bc6e275, 0x1848689a, 0x1961c991, 0x0c83be14, 0x111d537c, 0x0706e7d6, 0x00f03221, 0x1a590247, 0x8a9fea}}}, + /* 11*16^26*G: */ + {{{0x0c5a3c34, 0x1c04c7b7, 0x16527f87, 0x1d058052, 0x03e58aec, 0x0fa653d7, 0x1273a2ae, 0x03659f1c, 0xfedd9d}}, + {{0x013d7714, 0x15aa5fa7, 0x0fe0a5d8, 0x1d6a8d33, 0x14b00ada, 0x02989647, 0x0382c2fa, 0x18630a77, 0xa52e24}}}, + /* 13*16^26*G: */ + {{{0x0ab6a396, 0x1a72a68d, 0x04d81da6, 0x04372342, 0x088b3730, 0x16bfaf42, 0x1230b7b8, 0x10d78dd4, 0x3e0e32}}, + {{0x1980e27e, 0x1598f246, 0x11a3da8b, 0x002083e6, 0x13fc66ab, 0x1f0ef5a2, 0x1e593cc7, 0x0e5f4766, 0xca4481}}}, + /* 15*16^26*G: */ + {{{0x141023ec, 0x179ac311, 0x1b3d5c2a, 0x0bb1eedf, 0x0b9af564, 0x101004c1, 0x14a1260b, 0x06101865, 0x344ab9}}, + {{0x1e1eeb87, 0x07c4c148, 0x0e6575c1, 0x1d2ed5f8, 0x14c5ffc4, 0x1968f528, 0x18a9cfe3, 0x00856488, 0x6e1c2b}}} + }, + { + /* 1*16^27*G: */ + {{{0x08f6c14b, 0x1974fb2c, 0x0494050d, 0x0e5cbe75, 0x12877d1d, 0x03b1be4b, 0x0e078993, 0x0ca916cb, 0xd89562}}, + {{0x1d7d991f, 0x09b1f6ba, 0x0c19f85e, 0x051ac657, 0x140eb034, 0x03040c61, 0x1ab9ca3b, 0x071e578f, 0xfebfaa}}}, + /* 3*16^27*G: */ + {{{0x0127b756, 0x05d43ffb, 0x0825c120, 0x0517c957, 0x0b416034, 0x116d2830, 0x0499cb4d, 0x05ee2dbe, 0x6d8c78}}, + {{0x1f172571, 0x0a8fba55, 0x1f373299, 0x154db45a, 0x14daf4e3, 0x14169b69, 0x04445166, 0x0112dfb7, 0x99aedf}}}, + /* 5*16^27*G: */ + {{{0x158cf17a, 0x0f70d39b, 0x0208d493, 0x10bb974b, 0x097f8f1f, 0x0d778da0, 0x0b2a3416, 0x1bb2b7ef, 0xebcabe}}, + {{0x1caa0ccd, 0x0366e2fa, 0x0b3a5711, 0x15a425a1, 0x12e6b10f, 0x050db3e1, 0x072c0b00, 0x01f1e457, 0x47d3ce}}}, + /* 7*16^27*G: */ + {{{0x0c855c5b, 0x077728ad, 0x1f22beef, 0x0ac43402, 0x1fc28118, 0x0d1b4f0b, 0x189114cc, 0x05c97a99, 0xe8df4d}}, + {{0x0e465650, 0x0eaf3961, 0x07935f56, 0x076abe3c, 0x132c5966, 0x0da7acf7, 0x0c991113, 0x0e188ff3, 0x6c57fd}}}, + /* 9*16^27*G: */ + {{{0x12e7e454, 0x047aded2, 0x03985434, 0x05dfde1e, 0x01662fe3, 0x03011d4c, 0x00ca4492, 0x1ae31d95, 0x4068d3}}, + {{0x18ef191e, 0x1cd66f2e, 0x10dccc9d, 0x1a43da27, 0x138d1988, 0x0a2cbece, 0x1eaae7b0, 0x16e4a948, 0x8cd853}}}, + /* 11*16^27*G: */ + {{{0x06c5d939, 0x02bd6fc2, 0x0a4cf782, 0x0b450ef7, 0x0027ea47, 0x19973065, 0x1782d56f, 0x19b63b04, 0x12550e}}, + {{0x19e757c9, 0x153a7e2a, 0x16350c64, 0x16e83fd9, 0x04a72838, 0x121e0bb9, 0x1e9d5123, 0x069f0e5a, 0x7b8f83}}}, + /* 13*16^27*G: */ + {{{0x16c8a56f, 0x06855632, 0x1cdd084e, 0x1278a869, 0x0d08f850, 0x1bda9d7d, 0x17531a6e, 0x035876b0, 0x944d67}}, + {{0x1a7be289, 0x0fa6e32e, 0x01945fae, 0x0982e9ba, 0x0c61967d, 0x1c9b099d, 0x1ffd3050, 0x12ef6a03, 0xa71065}}}, + /* 15*16^27*G: */ + {{{0x0e08f15a, 0x175c50c5, 0x04a402eb, 0x13cadb90, 0x1c305fd6, 0x01b2ad69, 0x0833f9ac, 0x1239a133, 0x86a54e}}, + {{0x01388308, 0x09268f3e, 0x0d49534d, 0x053a24b6, 0x16867771, 0x146836ba, 0x1180e9ca, 0x0906f4f0, 0xcfee61}}} + }, + { + /* 1*16^28*G: */ + {{{0x0f676e03, 0x08a852b2, 0x1a13b752, 0x1f8e6d27, 0x08761cef, 0x1219ab8f, 0x1463ac3d, 0x006552ae, 0xb8da94}}, + {{0x0efdf6e7, 0x0447273a, 0x1fced445, 0x18b09b2b, 0x008b092c, 0x0e64bb14, 0x07935f26, 0x148900b4, 0x2804df}}}, + /* 3*16^28*G: */ + {{{0x06e1346b, 0x10cc24ee, 0x16bc717a, 0x1cf62070, 0x152c05ab, 0x1b0f8a68, 0x042f9531, 0x1fe1305a, 0x69068}}, + {{0x17226c13, 0x1dac52a6, 0x11809b9e, 0x0d127329, 0x0442aa4f, 0x0d95e843, 0x189b6a17, 0x1c1217fb, 0xb863e3}}}, + /* 5*16^28*G: */ + {{{0x1ca1f6a1, 0x07348fe6, 0x033fc68c, 0x197a2869, 0x06ce1068, 0x0e2e58f4, 0x1d854a1b, 0x127964b2, 0x898c34}}, + {{0x164f647c, 0x056e1078, 0x0af5e729, 0x0f9f2f3e, 0x06e93b2a, 0x0a122956, 0x15527611, 0x10d56ad4, 0x75f759}}}, + /* 7*16^28*G: */ + {{{0x1d1e3998, 0x134f01e1, 0x0810ca2a, 0x0d91722f, 0x0274e5e5, 0x0ceb8115, 0x0fc0694a, 0x1fda5231, 0xb213e2}}, + {{0x125fb81e, 0x0165ee31, 0x17b45d7b, 0x082cb7d7, 0x03bc3d53, 0x0f5fc1d2, 0x104b0f58, 0x1841e5a7, 0x229f8e}}}, + /* 9*16^28*G: */ + {{{0x025be234, 0x05f0ff65, 0x17cd41c0, 0x07c7ce1b, 0x06e060e8, 0x05d348b0, 0x04f97474, 0x1b02d8ff, 0x4b3b3a}}, + {{0x120e8362, 0x11dbf01c, 0x0846e101, 0x0ffb259e, 0x0c04c41e, 0x14b6fec6, 0x1271e1d7, 0x0770bb57, 0x5eec02}}}, + /* 11*16^28*G: */ + {{{0x0289f55e, 0x0b31a53f, 0x11d9c1ee, 0x0d61e1c5, 0x165be297, 0x08d813c4, 0x1e580809, 0x16dbb609, 0x9f7b88}}, + {{0x1e1e4bde, 0x1e69db2f, 0x0428ac6c, 0x0a6dc1d6, 0x1c71fc0e, 0x035a14c7, 0x03def8c5, 0x1098e082, 0x32f9f7}}}, + /* 13*16^28*G: */ + {{{0x1dfe1d2b, 0x12a4e460, 0x1a1e3945, 0x07ea13ca, 0x173a4c49, 0x056fe9fd, 0x038f9db3, 0x1d396e89, 0xd58a43}}, + {{0x0cd50922, 0x0793cadc, 0x0f5befff, 0x137442b9, 0x0276b54d, 0x14899414, 0x0f3c429c, 0x0d740b10, 0xfc1786}}}, + /* 15*16^28*G: */ + {{{0x049c795e, 0x120a8df1, 0x13a01784, 0x080d5533, 0x1ea4eed4, 0x0b4e1e13, 0x0c4335b6, 0x072e2230, 0x21d271}}, + {{0x19208ece, 0x0009761c, 0x17edd86c, 0x03289495, 0x1b4c3d67, 0x0dc2a915, 0x13a85dcd, 0x16960eb5, 0x94c5f9}}} + }, + { + /* 1*16^29*G: */ + {{{0x03c0df5d, 0x0d08bbc7, 0x15a9e4bc, 0x13dff6a2, 0x17fab201, 0x0d5ca3ae, 0x0ce9f62b, 0x028883f6, 0xe80fea}}, + {{0x0ac9ec78, 0x05a148db, 0x0c8baa7f, 0x0abd015e, 0x094472d1, 0x1b4651e5, 0x01dc7a25, 0x0fec71c0, 0xeed1de}}}, + /* 3*16^29*G: */ + {{{0x17592d55, 0x001acf66, 0x18d4064b, 0x0b728e81, 0x0e3b106d, 0x17b4b19e, 0x149822bf, 0x1b789420, 0x5d2ec6}}, + {{0x0f5183a7, 0x1372e875, 0x04545d09, 0x14f79b5a, 0x0d6950e2, 0x087d1346, 0x17ad63dc, 0x1f138dc8, 0xa92cd}}}, + /* 5*16^29*G: */ + {{{0x1e8f9f5c, 0x08fa5a4f, 0x02029466, 0x03e3c2b3, 0x1404d736, 0x171a10af, 0x1d0bf8b2, 0x1876237e, 0xac371d}}, + {{0x125a503c, 0x1d41ff99, 0x1d478745, 0x0a68b1dc, 0x0e735229, 0x00f3992a, 0x11dffc84, 0x1830e134, 0xc51616}}}, + /* 7*16^29*G: */ + {{{0x19e33446, 0x050e46a8, 0x0bce177d, 0x127788a5, 0x0a17a408, 0x005e8111, 0x10324d23, 0x07429e30, 0x894200}}, + {{0x06387689, 0x069c5007, 0x19d3e610, 0x1aee6cf3, 0x1e4e06bf, 0x16b6877e, 0x1de9362c, 0x12b2b4a0, 0xa9fd03}}}, + /* 9*16^29*G: */ + {{{0x1913cb26, 0x0b9464ad, 0x0ef5b40f, 0x16833802, 0x05c9899e, 0x1227faa8, 0x0aa28b36, 0x0d661468, 0x277026}}, + {{0x1348a7a2, 0x1f38b99f, 0x0056faef, 0x01923799, 0x0b324e94, 0x092683f9, 0x0c69554b, 0x0bcf361b, 0xf649bc}}}, + /* 11*16^29*G: */ + {{{0x195e8247, 0x0555010a, 0x01b346bc, 0x1fb88aad, 0x0ba9097b, 0x13700e7c, 0x1485e397, 0x1a70797d, 0x75e4d0}}, + {{0x19982d22, 0x111fecea, 0x06b624f2, 0x156b6dd5, 0x126d47dd, 0x0b8763db, 0x0641d07e, 0x142ea821, 0x1fce42}}}, + /* 13*16^29*G: */ + {{{0x06333323, 0x03cfa26d, 0x1d2afd1d, 0x177838d1, 0x0da849cb, 0x06b02cc2, 0x0fc0fc08, 0x07066c37, 0x3ed1b6}}, + {{0x15d61ba3, 0x189fe245, 0x1e3dca52, 0x0e514216, 0x1929ea9b, 0x04c4b447, 0x1b9d765f, 0x14916b69, 0xd84f2d}}}, + /* 15*16^29*G: */ + {{{0x133980bf, 0x1282bea5, 0x17402ebc, 0x06e05ca1, 0x0dd4368a, 0x1ebb91a4, 0x0606e11b, 0x1e0d4eb0, 0x70fdd2}}, + {{0x00b75785, 0x17754675, 0x15d29584, 0x006b070b, 0x0596b0a1, 0x008688f7, 0x1a5a55e9, 0x181a1ab0, 0x5edfca}}} + }, + { + /* 1*16^30*G: */ + {{{0x04e16070, 0x0e03dde6, 0x1f5a4577, 0x0304063d, 0x07543f2a, 0x04728eab, 0x010c4ee9, 0x0f7bf9ae, 0xa30169}}, + {{0x1e177ea1, 0x0068d020, 0x084684c3, 0x0bb7ef81, 0x00f9b173, 0x04fd12ea, 0x13d42060, 0x039f6cfc, 0x7370f9}}}, + /* 3*16^30*G: */ + {{{0x138011fc, 0x18093800, 0x1ca15899, 0x12d4cf5a, 0x00a4d835, 0x09984110, 0x0c4455ac, 0x146102bd, 0x6e8313}}, + {{0x1f15ab7d, 0x165b4fd1, 0x1147e69a, 0x1f22b5d3, 0x0c30426a, 0x16d900ed, 0x08130684, 0x117b849e, 0xc14781}}}, + /* 5*16^30*G: */ + {{{0x100e6ba7, 0x1d3a4dc6, 0x045bdfd4, 0x0dd8b689, 0x1e1b43d3, 0x101c526c, 0x147caf47, 0x0132f090, 0xf952a9}}, + {{0x0175e4c1, 0x0dd77728, 0x18a8ae63, 0x0e2cf698, 0x1a0f6555, 0x1b51713f, 0x1afe184d, 0x0b611579, 0xd8a93a}}}, + /* 7*16^30*G: */ + {{{0x03aa0e93, 0x08032d14, 0x1ec7d89a, 0x1c72875d, 0x0893a8f2, 0x18d0cecf, 0x1b9d4100, 0x0bc63a7f, 0x94016d}}, + {{0x07addac2, 0x07769344, 0x15ec1e8e, 0x086e7754, 0x06fd7f48, 0x0e9aa777, 0x165900d5, 0x1dcb88a9, 0x675032}}}, + /* 9*16^30*G: */ + {{{0x0266b17b, 0x07a43170, 0x18aeccac, 0x0ad14404, 0x109c2023, 0x1c42354f, 0x0a246ee5, 0x0e9ab3f6, 0xef22d1}}, + {{0x19dac83e, 0x1537021b, 0x10d06dcc, 0x0e4edee3, 0x0a1073ee, 0x0661d71a, 0x11d5a3e7, 0x192f5649, 0xbc5784}}}, + /* 11*16^30*G: */ + {{{0x12d382a0, 0x18980ad4, 0x1b366b88, 0x1b9779c5, 0x1f927f28, 0x063c0596, 0x04b4e72b, 0x19c99d71, 0xb5f7ef}}, + {{0x05b4b532, 0x117855dd, 0x0b3e316e, 0x1612da53, 0x1ddd371f, 0x0be37065, 0x08d4f025, 0x0b6a387e, 0x684354}}}, + /* 13*16^30*G: */ + {{{0x012cffa5, 0x13492322, 0x0331711f, 0x1a8410cd, 0x0624389e, 0x0a6c7dea, 0x01d9021d, 0x1a565ce2, 0x1cddc3}}, + {{0x1521954e, 0x0f36c4e6, 0x0dad4a2b, 0x193084d6, 0x0b08ac41, 0x0935fca1, 0x0298ff6c, 0x01965e3f, 0x1e476a}}}, + /* 15*16^30*G: */ + {{{0x14a9f22f, 0x1aff21c9, 0x1ea38ab4, 0x10338a42, 0x035b0cc0, 0x05c5ca44, 0x04e7c87e, 0x0b3e4b9d, 0x2accb3}}, + {{0x175c4927, 0x1baee59d, 0x0e9542de, 0x17af7d8b, 0x0edf1154, 0x1d1bf6f8, 0x0b946484, 0x1d2b115a, 0xd518a4}}} + }, + { + /* 1*16^31*G: */ + {{{0x1fb04ed4, 0x1bd631f1, 0x0c1fffea, 0x18661622, 0x18de208c, 0x0e828933, 0x04d918fe, 0x16713ad7, 0x90ad85}}, + {{0x0b6ef150, 0x08ea6a46, 0x00a25366, 0x1df57c2b, 0x022b839a, 0x05eca139, 0x0986bff7, 0x06c41470, 0xe507a}}}, + /* 3*16^31*G: */ + {{{0x10b7b678, 0x13aed99d, 0x1d8e0598, 0x18862379, 0x16c76f13, 0x15e52135, 0x0c6e8661, 0x0e669c84, 0x186e49}}, + {{0x18d91fc1, 0x1d03b797, 0x054d7729, 0x0ee44a89, 0x1e67e110, 0x0412e05b, 0x1612a9ff, 0x1c9300f7, 0xc0d460}}}, + /* 5*16^31*G: */ + {{{0x13421fb8, 0x18372e5d, 0x16957433, 0x12e3e5de, 0x12412984, 0x159a61db, 0x1d8b9f81, 0x1069edb7, 0x61c8d}}, + {{0x0e3ccd80, 0x0af7b342, 0x1bf374a6, 0x0e269674, 0x1eb5c806, 0x092c8702, 0x12deea4e, 0x1b320076, 0x6dfc6a}}}, + /* 7*16^31*G: */ + {{{0x0f6b35a4, 0x0925f0c5, 0x09fed21c, 0x1e6f4d56, 0x068ad889, 0x1920399d, 0x144edcd8, 0x074411dc, 0xf6a6b6}}, + {{0x01f422a6, 0x175b7f64, 0x1b8618b2, 0x0aeadceb, 0x0186f19d, 0x1b827ab0, 0x0e2c72b4, 0x150005a2, 0x3df7c8}}}, + /* 9*16^31*G: */ + {{{0x06954c11, 0x0b411f45, 0x1834062e, 0x1148782a, 0x178ff7fa, 0x0d878a83, 0x0dd88834, 0x051850d8, 0x87a2fc}}, + {{0x072a8b45, 0x0b719971, 0x1e0492dd, 0x11267e54, 0x07532cc4, 0x0a46d069, 0x13be5ec6, 0x1168b55d, 0x33ad51}}}, + /* 11*16^31*G: */ + {{{0x02706ab6, 0x123a3957, 0x194f036b, 0x16683ba5, 0x04cfe3c0, 0x177e5e1c, 0x069a1155, 0x008dcf10, 0xe1472e}}, + {{0x1d58de05, 0x174350b4, 0x0f349d4d, 0x113aaa8a, 0x021f8aa5, 0x08cbc643, 0x1f1a0fda, 0x1548f8b1, 0x82cd92}}}, + /* 13*16^31*G: */ + {{{0x07a84fb6, 0x1fd72a10, 0x0854087a, 0x06d0ea1f, 0x0b9ebc42, 0x06b00f24, 0x1bd77a2d, 0x19009f15, 0x1caf92}}, + {{0x07149109, 0x158c0c81, 0x0b399d85, 0x1982d2d4, 0x01622ec1, 0x127c7f88, 0x14e92069, 0x0b592edc, 0xbc24b8}}}, + /* 15*16^31*G: */ + {{{0x0a955911, 0x1b467f0a, 0x17b54b6d, 0x1c2d44c1, 0x18397107, 0x17f4d9eb, 0x14349627, 0x0d35e12c, 0x705bfd}}, + {{0x1fd200e4, 0x1dbe349f, 0x10b9cb62, 0x1e76a454, 0x051fa297, 0x1ec0faa0, 0x06429f98, 0x02616e7f, 0xe14aa4}}} + }, + { + /* 1*16^32*G: */ + {{{0x1ec4c0da, 0x1bda2264, 0x0fa8cd46, 0x18acf0e4, 0x1162ee88, 0x00d6cc0f, 0x1cce48e7, 0x1a5ec76b, 0x8f68b9}}, + {{0x101fff82, 0x11e5fbca, 0x1442ff7c, 0x1459fd2a, 0x0215dbbe, 0x08615b5f, 0x061b7876, 0x05b740c7, 0x662a9f}}}, + /* 3*16^32*G: */ + {{{0x123809fa, 0x0715c76e, 0x16552f86, 0x08b966a3, 0x11f08fd8, 0x19b1f922, 0x1c8a2ea4, 0x17c5ca13, 0x38381d}}, + {{0x131fed52, 0x0b83a8c1, 0x163c936f, 0x03f99665, 0x0b1cc368, 0x02d2a907, 0x1f72c250, 0x0141f722, 0xe4a32d}}}, + /* 5*16^32*G: */ + {{{0x17c2a310, 0x15213244, 0x04898c0f, 0x0d5d4a80, 0x099a1f18, 0x0dc15523, 0x0b9bda48, 0x049c86e5, 0x492627}}, + {{0x1e27ded0, 0x020db40a, 0x17fe3383, 0x0c6c254e, 0x0303b6d1, 0x1d2b4b8a, 0x0fe568b3, 0x0e7794f5, 0x1337e7}}}, + /* 7*16^32*G: */ + {{{0x0ebd2d31, 0x1c2583ce, 0x01b6e344, 0x1834adfe, 0x1e2f84dc, 0x09d9f23b, 0x12435789, 0x11834481, 0xe30656}}, + {{0x12546e44, 0x095a041c, 0x0dce099a, 0x1900857c, 0x10db6ffb, 0x15883fbe, 0x0982223c, 0x1c6f1268, 0xeac6f}}}, + /* 9*16^32*G: */ + {{{0x163136b0, 0x09861cf1, 0x0d077671, 0x17f1b355, 0x1d63374e, 0x073b11fd, 0x1bf09c6c, 0x01c48519, 0x3b9e10}}, + {{0x0cdbbc8a, 0x09f60b7b, 0x14c7e065, 0x1c514675, 0x15b26a2a, 0x19f5c7a3, 0x0dc77c54, 0x02a5a2d7, 0xfafb98}}}, + /* 11*16^32*G: */ + {{{0x0f485d3f, 0x10478239, 0x01efbba5, 0x140ed102, 0x0def717c, 0x05407aef, 0x06a4addb, 0x092e2559, 0xbb0aad}}, + {{0x1ca2f975, 0x1c9c9281, 0x19c2fff9, 0x14b5f462, 0x1da34895, 0x100fb94b, 0x11e63b34, 0x0a78b06a, 0xea699c}}}, + /* 13*16^32*G: */ + {{{0x16718dc9, 0x177699d1, 0x0448f792, 0x0b169b60, 0x00113e1e, 0x158cbd7f, 0x130353a3, 0x191c9ddf, 0x79090a}}, + {{0x1cfae7c5, 0x11991588, 0x0a4022e5, 0x0d5f6e17, 0x0aa56dd3, 0x0b65e6cd, 0x0e3c4f60, 0x0572320b, 0xeaab72}}}, + /* 15*16^32*G: */ + {{{0x1f60c7d1, 0x134b4a63, 0x1dd6b4a8, 0x0e3bcf9a, 0x1ba668dd, 0x0dde72a4, 0x0d54700f, 0x15bd3f2f, 0xe77c81}}, + {{0x02d72449, 0x162c0f94, 0x0a61b4d3, 0x08e1ee38, 0x01543631, 0x1d991f54, 0x0c8717f0, 0x0f1ddf02, 0x3acf14}}} + }, + { + /* 1*16^33*G: */ + {{{0x13231e11, 0x1437ea82, 0x1a078f99, 0x11d0ca06, 0x036091f4, 0x0ffc8cc6, 0x17597fe6, 0x002ed5f0, 0xe4f3fb}}, + {{0x0feb73bc, 0x1161c2bb, 0x14747260, 0x0fce9d92, 0x0b7286cc, 0x13687501, 0x1c705986, 0x075a1de9, 0x1e6363}}}, + /* 3*16^33*G: */ + {{{0x0bf05bd6, 0x1ccf8273, 0x0aa65194, 0x0adc0642, 0x10deca2f, 0x1a8ff5a3, 0x1fa420cb, 0x0837dc89, 0x900c32}}, + {{0x100d358b, 0x129569df, 0x13bec577, 0x10a6b078, 0x12439d69, 0x19022b85, 0x03d7e571, 0x1d1d163e, 0x6c31f9}}}, + /* 5*16^33*G: */ + {{{0x1f105c50, 0x13e14664, 0x04e1495e, 0x01bddef6, 0x033cd82e, 0x061a01e1, 0x02ab58a3, 0x0c5560b2, 0x5a8d03}}, + {{0x18a4cde9, 0x04d900c1, 0x1404f0d7, 0x0bed14cd, 0x1ff74a60, 0x15b920a1, 0x14da4da9, 0x16227a9c, 0xc059ea}}}, + /* 7*16^33*G: */ + {{{0x12d64feb, 0x03f0c5cd, 0x048a4b19, 0x05f14b25, 0x1a4e8377, 0x1d2bbb65, 0x182923bc, 0x0062465e, 0xd93f4d}}, + {{0x14698359, 0x187deac9, 0x124368de, 0x008617dd, 0x08c4d93e, 0x188e2a6e, 0x1cce88dc, 0x0ba8b964, 0x792555}}}, + /* 9*16^33*G: */ + {{{0x039fbc84, 0x1110266a, 0x15e8059c, 0x00c522c0, 0x0c65b7e7, 0x115e3315, 0x01106c53, 0x18dc6de5, 0x2f0769}}, + {{0x1c201bec, 0x1dc816f0, 0x137575cf, 0x0f36d498, 0x02149cca, 0x1803cc87, 0x1777e977, 0x0e49ae77, 0xb434f3}}}, + /* 11*16^33*G: */ + {{{0x06a758ea, 0x09bf5664, 0x1fc67135, 0x11063124, 0x16e39911, 0x04ad0aa0, 0x0c26561a, 0x100ab3c0, 0xfe7e67}}, + {{0x1b7ab649, 0x07916cae, 0x0c483479, 0x002e0e88, 0x1251f3b8, 0x070b4c24, 0x12e62302, 0x0cf4503b, 0x38aa69}}}, + /* 13*16^33*G: */ + {{{0x0cfffefa, 0x138ab134, 0x1946beb7, 0x10089ee0, 0x1af85101, 0x17c8a861, 0x049f5b7d, 0x194ea706, 0x91baf5}}, + {{0x1f7f6faf, 0x1e9b79a6, 0x1f4c0f71, 0x1de621cd, 0x13d92f4b, 0x14f893ee, 0x13af9765, 0x023268f7, 0x4e5cf}}}, + /* 15*16^33*G: */ + {{{0x0d33c546, 0x1e048bf4, 0x17b6bccb, 0x0ebf6650, 0x1ddd7825, 0x09b9f07a, 0x029f2cb1, 0x043967ef, 0x445841}}, + {{0x000b4fd4, 0x05368c38, 0x0b29cd98, 0x1e1479f6, 0x0da20852, 0x0f571c8d, 0x14e9dc89, 0x0efe7f0e, 0x308d93}}} + }, + { + /* 1*16^34*G: */ + {{{0x00eae29e, 0x17db7571, 0x138741c5, 0x069e5e1a, 0x04266c70, 0x0a9bd22d, 0x0cc7ae58, 0x13631d7e, 0x8c00fa}}, + {{0x0702414b, 0x1e952633, 0x18db1539, 0x15b5f503, 0x0c974c2f, 0x1a1d1b9b, 0x0686a770, 0x0cffd4a4, 0xefa472}}}, + /* 3*16^34*G: */ + {{{0x0bfd913d, 0x1fcab01f, 0x15327ab0, 0x0d01cddc, 0x08d2050a, 0x1d042615, 0x17e1d341, 0x14fd20fb, 0x36362a}}, + {{0x052e243d, 0x027cd756, 0x0cbeabf1, 0x017621ad, 0x02a82d83, 0x1221b86d, 0x1f54d058, 0x0ced9715, 0x48f278}}}, + /* 5*16^34*G: */ + {{{0x0d132896, 0x055d5fcd, 0x0f1b259f, 0x03c7756f, 0x1200dfcb, 0x16cb16ec, 0x180bca56, 0x0dbe6543, 0x448797}}, + {{0x0f685248, 0x0600d895, 0x1daa9e92, 0x081ab4c4, 0x01a3306b, 0x1483ba2b, 0x1f87bf26, 0x0c1a22b5, 0x27bd58}}}, + /* 7*16^34*G: */ + {{{0x1fa2670c, 0x040ab7b5, 0x189cf9b8, 0x01f05740, 0x10324740, 0x0e55419a, 0x0de22daa, 0x18517970, 0x4a4d3a}}, + {{0x16c1764d, 0x045cffee, 0x0a6fbb60, 0x00b2997e, 0x02b9d493, 0x188eef78, 0x093c5f9d, 0x0380308b, 0x70abb9}}}, + /* 9*16^34*G: */ + {{{0x0cb24aa7, 0x0cb57f12, 0x0d1714c3, 0x02118c72, 0x0fe18903, 0x17ec4ffc, 0x000b0eb9, 0x03215d23, 0x5f7b2d}}, + {{0x0a693d7d, 0x0bc03a1a, 0x1207431b, 0x1bd6e127, 0x07c47ce8, 0x1c051e73, 0x0accc28f, 0x00189fbe, 0x77036}}}, + /* 11*16^34*G: */ + {{{0x0f7fb8bd, 0x0c8de2f9, 0x19024290, 0x08c942bf, 0x086d87bc, 0x12736ca4, 0x0340abb0, 0x0a2673b2, 0x513974}}, + {{0x03908c0f, 0x04a5616c, 0x1e6356a0, 0x1865a7c7, 0x15c47faf, 0x1f7ed740, 0x0fcd2e23, 0x07c8ec87, 0xfcd714}}}, + /* 13*16^34*G: */ + {{{0x0e36ca73, 0x15ba48af, 0x0efcfb78, 0x10c9a6d7, 0x14887eac, 0x08895f80, 0x1ee2a90a, 0x1ac57f7b, 0xcf8316}}, + {{0x0ec25534, 0x1a490ca1, 0x035c43d6, 0x0c2b31b1, 0x0ca681c9, 0x0284486e, 0x15cf8e11, 0x11bd6bb3, 0x9feb5}}}, + /* 15*16^34*G: */ + {{{0x1752f97d, 0x0449beb5, 0x0d1d984f, 0x1df78ebe, 0x1a6165a2, 0x09433467, 0x127794e7, 0x13498976, 0x8610de}}, + {{0x1f1b1af2, 0x02bee6a0, 0x1550f820, 0x169b3ea8, 0x1f99e57a, 0x0ccd9299, 0x0ef24df3, 0x14056c61, 0xd31997}}} + }, + { + /* 1*16^35*G: */ + {{{0x00cb3e41, 0x0bfeefe3, 0x02e4b026, 0x1a109e61, 0x18ed3143, 0x076054f4, 0x0a7cf843, 0x1cd3ba90, 0xe7a26c}}, + {{0x0f2cfd51, 0x1454a10e, 0x03a0f883, 0x0d658084, 0x1bb18d0a, 0x00350d57, 0x012d1c6c, 0x0601f4f3, 0x2a758e}}}, + /* 3*16^35*G: */ + {{{0x1aee42db, 0x07cfe95f, 0x0c1c529c, 0x1778b68e, 0x0bfc1d9b, 0x176dc8f6, 0x0543f1ed, 0x1cfb36b2, 0xcc3427}}, + {{0x15d87bdb, 0x1114e008, 0x1c908b71, 0x0b975b1c, 0x1520010e, 0x1fe9fd90, 0x1a862178, 0x0834a438, 0xea2498}}}, + /* 5*16^35*G: */ + {{{0x1ed4a086, 0x1a1b3633, 0x071043bf, 0x0eb82b1d, 0x15cf4b1d, 0x02c2fde5, 0x1177b20f, 0x1759b308, 0x948f05}}, + {{0x1e2bca4b, 0x150c007c, 0x0fe8b468, 0x06514e38, 0x139411c2, 0x08533008, 0x08ce0bd1, 0x13ff6b45, 0x864ca8}}}, + /* 7*16^35*G: */ + {{{0x17542c21, 0x065f5365, 0x09930570, 0x13e9a51d, 0x16d43ae1, 0x1e22a28e, 0x0b24195b, 0x0c525233, 0x258419}}, + {{0x072bfabf, 0x1f5f18cb, 0x10ab5ece, 0x07430dc9, 0x113d5f3e, 0x0d52663a, 0x11200797, 0x03e39b64, 0xfcb35b}}}, + /* 9*16^35*G: */ + {{{0x0c1ecbf8, 0x1e230fa0, 0x1bb5f290, 0x13e1bd35, 0x0421f648, 0x1aa660f4, 0x14948aa5, 0x18826e78, 0x7e12cd}}, + {{0x10bed615, 0x0a2dc66d, 0x18767d67, 0x13ec3b1f, 0x11259c96, 0x0a6d5f26, 0x00dc50fe, 0x111111b9, 0x71284f}}}, + /* 11*16^35*G: */ + {{{0x14557d86, 0x1f3328e0, 0x199ffd05, 0x1dd88f1c, 0x1a6cf1cf, 0x08e53d02, 0x0a99dcae, 0x1fe546e8, 0x4b8ec2}}, + {{0x15167eb9, 0x0ecd8c8d, 0x10fda4af, 0x0be5de1f, 0x1ac5f28d, 0x0396f293, 0x1eac5290, 0x1fe0982a, 0xfde6c3}}}, + /* 13*16^35*G: */ + {{{0x0780763c, 0x15c169da, 0x195a4754, 0x14dabd24, 0x0c07e5f8, 0x1b6e34bd, 0x09094c90, 0x00e672c7, 0xfcd5c1}}, + {{0x18e851cb, 0x0a73a101, 0x1918e92d, 0x13645ce2, 0x0e38cb11, 0x06d9afb9, 0x1118edc8, 0x1c5caa45, 0x18ddab}}}, + /* 15*16^35*G: */ + {{{0x1d8ef686, 0x071df182, 0x09cb99af, 0x1c91e804, 0x06e53f68, 0x12ed7c13, 0x0f9488e2, 0x1dcb0879, 0x900f2c}}, + {{0x0121a8cf, 0x19d24b3f, 0x0455b541, 0x19bfe879, 0x1d110596, 0x0a8d89a4, 0x096b5871, 0x0abd8c08, 0x732ac1}}} + }, + { + /* 1*16^36*G: */ + {{{0x1e6b80ef, 0x0794f59e, 0x1e5093cf, 0x17972cfa, 0x0bdc571c, 0x006111de, 0x1b2348d5, 0x01dc6cc5, 0xb6459e}}, + {{0x1a71ba45, 0x185f85b0, 0x18d6cbfc, 0x075cda91, 0x01db3c4b, 0x0f8b72b3, 0x01b7876b, 0x0da0de7c, 0x67c87}}}, + /* 3*16^36*G: */ + {{{0x119888e9, 0x1ce793c9, 0x1122a2d0, 0x0574d7e4, 0x081673d1, 0x069814b3, 0x1b8b7798, 0x0ee75874, 0x1f90ea}}, + {{0x0f113b79, 0x17efe4bf, 0x0548b995, 0x0ea3fdcb, 0x196a8213, 0x09e938f5, 0x043a5605, 0x0f82bb54, 0x89be36}}}, + /* 5*16^36*G: */ + {{{0x1562222c, 0x02f7db79, 0x19bcb182, 0x0688f323, 0x152bade0, 0x15d699a5, 0x02b5b9c0, 0x09bdbffc, 0x13a4e5}}, + {{0x08200145, 0x058b3465, 0x12413023, 0x138aef5b, 0x09a52d4f, 0x017c0eb0, 0x004ecb2b, 0x09cb02dd, 0xc9d67d}}}, + /* 7*16^36*G: */ + {{{0x143b46bb, 0x1bf26e07, 0x12494950, 0x1a74c7f5, 0x15dbd12e, 0x1e02ec22, 0x0b747501, 0x17e46795, 0x61991e}}, + {{0x0c20a848, 0x047ac80e, 0x0bb363bd, 0x10e5394a, 0x1adf11ca, 0x1c38b37d, 0x124a54bc, 0x011e7fbc, 0x1c5e3}}}, + /* 9*16^36*G: */ + {{{0x121add3b, 0x12df1eee, 0x1c9f63df, 0x15289e8a, 0x026118b9, 0x0e6d868b, 0x0e1d240e, 0x1496f0fa, 0xea27ae}}, + {{0x168ce7dd, 0x1af148ed, 0x1386f9c6, 0x0425ad1c, 0x02f6278b, 0x0759192e, 0x1f795c8f, 0x1cdc8542, 0xc70ff1}}}, + /* 11*16^36*G: */ + {{{0x011ff757, 0x1457c5db, 0x13089b9b, 0x19d2e838, 0x0b6da9b4, 0x087c5b71, 0x1552ea40, 0x06ad6fff, 0x594651}}, + {{0x094d031a, 0x05337654, 0x0fff8eca, 0x1778f6ff, 0x006f9961, 0x1c680ae2, 0x1d401080, 0x019cbbe4, 0x361136}}}, + /* 13*16^36*G: */ + {{{0x036160b5, 0x1b12c51b, 0x19faf019, 0x0a7e48e1, 0x11ec0ccc, 0x17bcb804, 0x0a43a10e, 0x0722bee6, 0x16b26e}}, + {{0x18a1dc0e, 0x0312fdfa, 0x0e8fbe05, 0x0c7558e1, 0x054d4e13, 0x01a9231b, 0x1e2ed8d9, 0x0b4c605d, 0x60f56}}}, + /* 15*16^36*G: */ + {{{0x17b32db8, 0x0b269449, 0x11de47ce, 0x1dae93ec, 0x0ea85c91, 0x0a1e4216, 0x1e4c6fa8, 0x12b88ab3, 0x24b52}}, + {{0x0a64f760, 0x0a2a7d55, 0x16e06a56, 0x16d02240, 0x05be862a, 0x1410e62f, 0x0271edb8, 0x11eb7fe6, 0x609fef}}} + }, + { + /* 1*16^37*G: */ + {{{0x096943e8, 0x16d07ada, 0x1cf16977, 0x1e3f8cfc, 0x106231d6, 0x1a5508c7, 0x0101e4c8, 0x19050177, 0xd68a80}}, + {{0x0b133120, 0x0a642133, 0x114a568a, 0x1cf71ef0, 0x0e28b5b0, 0x0fc8bbd8, 0x1b40312c, 0x1ffe96b0, 0xdb8ba9}}}, + /* 3*16^37*G: */ + {{{0x0ca1c4f9, 0x0da1550c, 0x0df8a08d, 0x1dfc6995, 0x116f44f4, 0x1c1ed30f, 0x0a313102, 0x11e457ae, 0x7815f7}}, + {{0x1778bc15, 0x158f51b5, 0x1df47866, 0x085bcc2a, 0x0c35d5cb, 0x1e798a2c, 0x1da9f764, 0x1d19a735, 0xc1c601}}}, + /* 5*16^37*G: */ + {{{0x0e8c8530, 0x09370e1f, 0x03d3639b, 0x1bed03df, 0x06c6d512, 0x1e3200b5, 0x005db8dd, 0x19b41d88, 0xc39273}}, + {{0x198446c7, 0x0018787b, 0x1bb5c571, 0x1bf97b45, 0x1199850e, 0x09ca20e1, 0x123a7407, 0x084ae867, 0x8c41be}}}, + /* 7*16^37*G: */ + {{{0x037a26c1, 0x0f9205d9, 0x16fda94c, 0x18dcb181, 0x03b25166, 0x1218e0e8, 0x06c09d48, 0x08feb082, 0xda3174}}, + {{0x0cf74d6f, 0x08c1b767, 0x0a05497d, 0x106d8baf, 0x1d8b7d36, 0x00b3e12c, 0x11a748e1, 0x170febb1, 0x753b97}}}, + /* 9*16^37*G: */ + {{{0x1739dc49, 0x15b14549, 0x1d5580f4, 0x0725b4cd, 0x1231a239, 0x162845ff, 0x19b04192, 0x196055e1, 0x6a4be6}}, + {{0x12edd5cf, 0x13515cef, 0x1292934f, 0x1c569962, 0x08058ab7, 0x1371b07d, 0x1d65f705, 0x15455120, 0xf15d8f}}}, + /* 11*16^37*G: */ + {{{0x06987fac, 0x05f45062, 0x0a1bd9e6, 0x121f0c81, 0x18a3c8bc, 0x01301f64, 0x1c4d13a6, 0x13e275cf, 0x1f7c6}}, + {{0x19174c68, 0x1c8f39c0, 0x1daf098f, 0x1ebbc433, 0x0b6f9cb7, 0x01b7194d, 0x08b796c4, 0x07e6dfb4, 0x9d4ecc}}}, + /* 13*16^37*G: */ + {{{0x16daba4d, 0x099d0deb, 0x0c658987, 0x0bf61c40, 0x02bcb9c6, 0x1176ec7d, 0x0e072dc1, 0x002ec3fa, 0x557e94}}, + {{0x17a52316, 0x09ba50dd, 0x10d64294, 0x1371ebf4, 0x0bc86c5e, 0x0e7d0ae6, 0x1f811b4d, 0x074c03f4, 0x7a7e8f}}}, + /* 15*16^37*G: */ + {{{0x09f5341a, 0x1c7a97f9, 0x058dae00, 0x0f4658cd, 0x101419cc, 0x0a0753d8, 0x16a4eca7, 0x10433310, 0x3adada}}, + {{0x0586c6cc, 0x08ac049b, 0x17a3ef2d, 0x0da5cb68, 0x11017e9d, 0x1adf9b55, 0x1a7d54b8, 0x04513326, 0xbfea1e}}} + }, + { + /* 1*16^38*G: */ + {{{0x028d3d5d, 0x04acc07e, 0x11273a90, 0x055d72e6, 0x030b0961, 0x0138483d, 0x01094b70, 0x0fbecb90, 0x324aed}}, + {{0x16ab7c84, 0x1391257c, 0x0cca10e5, 0x027618fc, 0x01f4f192, 0x0061ad76, 0x1cbfc4c3, 0x0aee96c3, 0x648a36}}}, + /* 3*16^38*G: */ + {{{0x0fd53ed3, 0x0e48bac1, 0x095b33bd, 0x1ee9f73b, 0x17c49163, 0x105c98ef, 0x0ab56e3d, 0x1ab32cee, 0x20840b}}, + {{0x1a7a7132, 0x18a1ff28, 0x19c22661, 0x0f88e729, 0x0fac2548, 0x0a3b535d, 0x090d21ef, 0x12f9d830, 0xf29934}}}, + /* 5*16^38*G: */ + {{{0x08a35b35, 0x1e965dac, 0x028487b6, 0x0bb114b8, 0x0ebfd1ab, 0x0814f2c4, 0x06eef44f, 0x1ec1d667, 0xe6b6bf}}, + {{0x1c1007bd, 0x0b949edc, 0x1a6671f1, 0x16d93a77, 0x161ddfe3, 0x01f1c1ac, 0x0bcc99bd, 0x17a6601a, 0x1a5ff2}}}, + /* 7*16^38*G: */ + {{{0x00360dd3, 0x0a77c696, 0x14388243, 0x11506db0, 0x0e3bb47a, 0x1c043706, 0x06ca22c2, 0x0e8b7c93, 0xe05317}}, + {{0x1c24f87b, 0x15766c89, 0x040f70ac, 0x130fbd30, 0x0a01461b, 0x0ac15adb, 0x1ce73602, 0x0e34bb25, 0xdc1c3b}}}, + /* 9*16^38*G: */ + {{{0x1098dfea, 0x00a33316, 0x099e1e5f, 0x1967925d, 0x05e57fad, 0x12e9541d, 0x11678063, 0x074ef10d, 0xa8153b}}, + {{0x0e6d892f, 0x124d4efb, 0x16bd0562, 0x0bc1ee85, 0x13e03b1b, 0x0dce2bc2, 0x03f14f63, 0x0c8c3a0c, 0x2a4739}}}, + /* 11*16^38*G: */ + {{{0x0bcecfa5, 0x13f3bd24, 0x05aec082, 0x1ac5b436, 0x0da3b2a0, 0x14ae31c7, 0x176b7ad1, 0x1661fd95, 0x4f05c3}}, + {{0x15d37b53, 0x16681254, 0x06d2334b, 0x1dc863e0, 0x134b9447, 0x191b0aca, 0x09beb758, 0x1d4c07a8, 0x53a499}}}, + /* 13*16^38*G: */ + {{{0x084b96aa, 0x10f3b2c8, 0x0aaf3391, 0x130c6aa4, 0x1980ed02, 0x02f0d51d, 0x046f8990, 0x1733ecf5, 0xd9309a}}, + {{0x02b28a86, 0x136279be, 0x13fa5e3c, 0x0d93f75f, 0x0cb5fd3b, 0x0783313a, 0x155f5f84, 0x055369d8, 0x6ef99b}}}, + /* 15*16^38*G: */ + {{{0x1f8fcf0e, 0x1bdc682c, 0x1129beb3, 0x16dffbcf, 0x03411d65, 0x1a236f55, 0x14d6ea70, 0x14270ac5, 0x7d587c}}, + {{0x18bc9459, 0x00e0d04e, 0x08de0294, 0x072015a6, 0x16ad0c46, 0x0005b67e, 0x11849c8d, 0x00710609, 0xa7295c}}} + }, + { + /* 1*16^39*G: */ + {{{0x1d054c96, 0x145e9b9f, 0x1472a223, 0x08287751, 0x0e5dceec, 0x0fedf2ff, 0x187db547, 0x092339bc, 0x4df9c1}}, + {{0x0ad10d5d, 0x175d6036, 0x02124064, 0x0a0d9b85, 0x185d4b5d, 0x1a611d0e, 0x1ca01425, 0x0a2125b0, 0x35ec}}}, + /* 3*16^39*G: */ + {{{0x1def001d, 0x07912ed2, 0x06e89fbd, 0x1377ad31, 0x1b64b21f, 0x1e8e04f1, 0x12bc8382, 0x05b64fc5, 0xa549a3}}, + {{0x10624783, 0x0aed8d3f, 0x125c16b7, 0x083c54c5, 0x19456eb1, 0x01876c52, 0x1b2f7d33, 0x0f20db2c, 0x799b7a}}}, + /* 5*16^39*G: */ + {{{0x052ed4cb, 0x0d778f2e, 0x027b1681, 0x09bdf678, 0x12e6ec95, 0x05ecc1a9, 0x13448fc2, 0x061b40fd, 0x7e798f}}, + {{0x14bb9462, 0x0b8b303c, 0x07cde849, 0x06ae37ff, 0x0f057b17, 0x0aa4ef79, 0x120c106a, 0x189449b5, 0xd23dcc}}}, + /* 7*16^39*G: */ + {{{0x13630834, 0x0f9d07de, 0x16b019ff, 0x07e250c7, 0x08108846, 0x0f4b5d46, 0x1e0eb56e, 0x00062a28, 0x224fa2}}, + {{0x047a2272, 0x09e239be, 0x0943dd73, 0x05249d81, 0x109f53d6, 0x187d1c8e, 0x0970f12d, 0x065767db, 0xbbe54e}}}, + /* 9*16^39*G: */ + {{{0x183c19d7, 0x13b24e8a, 0x0b3d5eed, 0x16bc8b58, 0x08ef2bbb, 0x12e67211, 0x07904a68, 0x198c0147, 0xc2d4a0}}, + {{0x0507928d, 0x17945c16, 0x0d1725dc, 0x0095062e, 0x1260d268, 0x1dafbfa0, 0x0a535060, 0x1f38100c, 0x65ada0}}}, + /* 11*16^39*G: */ + {{{0x1c940c9a, 0x064056a5, 0x1d08cc21, 0x1e79c275, 0x1dc2113c, 0x02f13a26, 0x0c643956, 0x0fd860be, 0x2ec22a}}, + {{0x051e7a4d, 0x08ca7ecc, 0x08d6f6b3, 0x00a307b3, 0x07feb124, 0x127a814e, 0x05a130b8, 0x0d1bc66f, 0x8b1da4}}}, + /* 13*16^39*G: */ + {{{0x1d683eeb, 0x138772fe, 0x034c4cea, 0x0dd67141, 0x0b8f33e3, 0x1b292842, 0x13b2ac6b, 0x0e71f351, 0xafc669}}, + {{0x10cd4509, 0x0f14e559, 0x1b77f724, 0x1756aa4b, 0x19c16570, 0x0e3fe511, 0x1d4af0d6, 0x12edba44, 0x2c21}}}, + /* 15*16^39*G: */ + {{{0x176f4293, 0x1100fc3d, 0x0f144e7a, 0x12f16aca, 0x1282e10e, 0x04679b85, 0x0c24486f, 0x0b53e686, 0xd2557b}}, + {{0x1282740a, 0x0d8c3d12, 0x101697c7, 0x16d071bc, 0x0d21fe34, 0x178375a5, 0x1fc049a0, 0x086abc84, 0xa787b3}}} + }, + { + /* 1*16^40*G: */ + {{{0x0c1f98cd, 0x1fe4ce45, 0x1fc0c232, 0x09120a9a, 0x06021523, 0x054e0e63, 0x01c3ebb6, 0x150948e9, 0x9c3919}}, + {{0x14fc599d, 0x13f2f01e, 0x193239af, 0x064deed8, 0x0e641905, 0x0225f930, 0x155d613c, 0x01e949bb, 0xddb84f}}}, + /* 3*16^40*G: */ + {{{0x0fb64db3, 0x1dcc6a9c, 0x1754e105, 0x1bc99473, 0x1b8d6a7e, 0x1c1fdf29, 0x12dd02ee, 0x124537b9, 0xc11423}}, + {{0x1c0259be, 0x118674ff, 0x1159f478, 0x0b01209a, 0x18bd1a87, 0x06f27f4b, 0x1f0a973b, 0x1b8b690d, 0x1237f6}}}, + /* 5*16^40*G: */ + {{{0x03081e46, 0x16f6c1a0, 0x11567a87, 0x044318aa, 0x034713a5, 0x0e160c93, 0x089020b6, 0x1f0634ee, 0x6c5b4b}}, + {{0x0bfbcd70, 0x08fce5c0, 0x108a98bb, 0x019f04d5, 0x1e47841d, 0x1c31e715, 0x10bec8d1, 0x0e2924da, 0xcb0513}}}, + /* 7*16^40*G: */ + {{{0x064dcd4b, 0x0572d762, 0x04704937, 0x018fab32, 0x10a450c3, 0x0332e558, 0x1792d59c, 0x0acce195, 0xe1e9a8}}, + {{0x1b041f2c, 0x085b12f5, 0x085aca4b, 0x09a33559, 0x177927f4, 0x01accd92, 0x14c6deb1, 0x12a88ab8, 0x562b0a}}}, + /* 9*16^40*G: */ + {{{0x0badd73c, 0x02c3b7f1, 0x0992df40, 0x139bb205, 0x014208fd, 0x1a72176e, 0x0265de29, 0x0af5a236, 0x51b21a}}, + {{0x0b36d8d1, 0x1bea570f, 0x11cd2e9b, 0x00261e51, 0x01cfa6c2, 0x03f80e96, 0x0f975528, 0x020003fa, 0x7930}}}, + /* 11*16^40*G: */ + {{{0x1b09f34b, 0x0bae85b9, 0x1319b39b, 0x10e7cc11, 0x19d61e58, 0x114b79f9, 0x1e6186ad, 0x14c76396, 0x9701f3}}, + {{0x00df5793, 0x06e42866, 0x1731e52b, 0x097872ff, 0x08337710, 0x18da98ab, 0x1b4575c0, 0x177195e1, 0x3dd44b}}}, + /* 13*16^40*G: */ + {{{0x1f1e2f46, 0x0e73111d, 0x09de0c05, 0x01ee3d0e, 0x03c57527, 0x0970206b, 0x1b311156, 0x03a593cc, 0xa036b4}}, + {{0x1effb349, 0x198f134a, 0x1c2c7d3d, 0x01c5059f, 0x0b08d068, 0x1b5523cf, 0x0cf5f7c7, 0x14007d2d, 0xc3bf91}}}, + /* 15*16^40*G: */ + {{{0x0c4cea08, 0x06c5c81c, 0x03a8876f, 0x16b1741c, 0x04652654, 0x108a9a00, 0x1141bd29, 0x1b7549d1, 0x6a85fa}}, + {{0x1862f4f3, 0x0cef672c, 0x15c86da8, 0x0e349687, 0x06230b42, 0x19e0a47f, 0x16754c64, 0x00975c8c, 0xb646}}} + }, + { + /* 1*16^41*G: */ + {{{0x00a959e5, 0x1109c109, 0x04753316, 0x02927517, 0x006bb91e, 0x0f940ec7, 0x1f7e3781, 0x0163ba25, 0x605717}}, + {{0x0385a2a8, 0x04cdf499, 0x1893197a, 0x02a5787d, 0x1f262465, 0x116d7b8e, 0x001eb766, 0x164d4d49, 0x9a1af0}}}, + /* 3*16^41*G: */ + {{{0x171c032b, 0x0a6d0b14, 0x0bf72603, 0x16cd142f, 0x166c5ff6, 0x0dafefe3, 0x0980f744, 0x1f9adc00, 0x71eba8}}, + {{0x1668359f, 0x1d5ad470, 0x12d1d579, 0x0635a2ee, 0x0bb7f719, 0x028b7aa6, 0x0e77bd98, 0x0c496c3a, 0xd2ff12}}}, + /* 5*16^41*G: */ + {{{0x0a03a61c, 0x03723a29, 0x01c15d34, 0x10d1e8d2, 0x09dd0507, 0x1a215d55, 0x148cb285, 0x00b66493, 0x855ec3}}, + {{0x065dfc07, 0x0fe37556, 0x1f912597, 0x05ee9d42, 0x0fb4ed33, 0x05ffcda1, 0x105fd50f, 0x05d8be03, 0xdd85d}}}, + /* 7*16^41*G: */ + {{{0x1f32d706, 0x00605240, 0x1a819e2c, 0x119948e8, 0x1bfa2061, 0x094d184a, 0x0fc7c543, 0x0d57567f, 0x3ce448}}, + {{0x1c7fd9e4, 0x05b9b1bf, 0x187a27d0, 0x02ac879a, 0x14906edd, 0x08235884, 0x014a23bf, 0x11b55c6f, 0xe77540}}}, + /* 9*16^41*G: */ + {{{0x191cd3fb, 0x0da065db, 0x0a6f9a1b, 0x1467fb2e, 0x044eb4a2, 0x0190c7c4, 0x1febc0b8, 0x0287e9c6, 0x11ccc5}}, + {{0x15160d86, 0x09b8b5d2, 0x174d1caa, 0x163dfa59, 0x0c239fa0, 0x112249c6, 0x077ad4a3, 0x05520562, 0x4aa56b}}}, + /* 11*16^41*G: */ + {{{0x018f7552, 0x03dc88cb, 0x0153eb0e, 0x02271730, 0x182ddbd4, 0x1bba7c11, 0x11bd0ee5, 0x02fca293, 0x250bb}}, + {{0x1510b14d, 0x18424b11, 0x0f5bc78f, 0x00de7866, 0x1d817da0, 0x1efbaff4, 0x0208d0b5, 0x1f9377d0, 0x731930}}}, + /* 13*16^41*G: */ + {{{0x1f725d12, 0x0e89f49c, 0x0d7d1d41, 0x0c8577b9, 0x02fbfd94, 0x1ce70501, 0x1f4ead28, 0x111668cf, 0x1a749c}}, + {{0x03ac56e4, 0x09b28a69, 0x0436a9c0, 0x0410d313, 0x13d8f607, 0x1f3ae157, 0x18b3d162, 0x12ae7d81, 0x7e91d1}}}, + /* 15*16^41*G: */ + {{{0x09fae458, 0x10824729, 0x1bb25ff5, 0x14b884ec, 0x17b328b0, 0x0ab52efd, 0x06304274, 0x0b7c1f04, 0xc75068}}, + {{0x1757b598, 0x00b420ca, 0x165468ac, 0x1b94a066, 0x0c7b40a5, 0x1a0a6339, 0x1817ed4b, 0x1f19f243, 0xead795}}} + }, + { + /* 1*16^42*G: */ + {{{0x0cb94266, 0x0d34b9f7, 0x1537c4ac, 0x1de1f74f, 0x1a31880c, 0x1cd228c6, 0x10450850, 0x11c47410, 0xa576df}}, + {{0x01b28ec8, 0x145f08d7, 0x05367cfb, 0x1c214fea, 0x0d82c432, 0x0bd7f2c6, 0x02cb24ae, 0x041cecc8, 0x40a6bf}}}, + /* 3*16^42*G: */ + {{{0x0d9ed6c1, 0x14575ac6, 0x1564f5ad, 0x1ce8b787, 0x0dd0ec24, 0x00c3b82f, 0x14fa02ff, 0x0db96e9e, 0x32833}}, + {{0x18fafeee, 0x16375f37, 0x12d252b7, 0x17e9be4b, 0x17c8c265, 0x0ca1d106, 0x1ca311b5, 0x07025fb3, 0x71a898}}}, + /* 5*16^42*G: */ + {{{0x1235983a, 0x0cd4d469, 0x0ef3aca4, 0x14206e02, 0x01531e38, 0x0936b87f, 0x1153718e, 0x15d17223, 0xce4f4e}}, + {{0x0d3cdecf, 0x07eb58c8, 0x0fdd02bb, 0x18ca451d, 0x07543526, 0x10124f38, 0x0eecfab7, 0x0e78721f, 0xf3c9f9}}}, + /* 7*16^42*G: */ + {{{0x15b0e6c9, 0x16d55b32, 0x1b932269, 0x1ff39ef0, 0x0bcbddb5, 0x07d9b6fc, 0x0889e38a, 0x14a9730c, 0x4dbebf}}, + {{0x1eb2cc25, 0x0a53c2aa, 0x1413beba, 0x06236578, 0x029f3589, 0x11373711, 0x0bb7d169, 0x16079227, 0x10fee7}}}, + /* 9*16^42*G: */ + {{{0x05857295, 0x0700d08d, 0x10cfc059, 0x11c8fe06, 0x0a12069c, 0x08c7e50e, 0x10862cb8, 0x017fde8b, 0xa42a24}}, + {{0x0a7eb9c1, 0x159bbff6, 0x1464e555, 0x038459a2, 0x1a4c427a, 0x1915926e, 0x15159e9a, 0x1e4c200b, 0x3aa0b3}}}, + /* 11*16^42*G: */ + {{{0x0fcdc098, 0x1107faab, 0x191a00c8, 0x15c01ed5, 0x099c1550, 0x0fc36062, 0x0899e9fc, 0x05f2df64, 0x34e12b}}, + {{0x0a7474e2, 0x0658d6f3, 0x0620fd99, 0x1ea261e3, 0x172db04d, 0x05e420bc, 0x0c8b65d3, 0x1bbaf6ba, 0xa64ac2}}}, + /* 13*16^42*G: */ + {{{0x0f173b92, 0x06b75af4, 0x07edd847, 0x1ce5e82d, 0x165683b7, 0x0d10c7a6, 0x07ca6f8c, 0x081b3772, 0x10f4d2}}, + {{0x033146c2, 0x0810036b, 0x01ab6df2, 0x16ed3a29, 0x108ba90b, 0x12d2d19c, 0x0eb4846c, 0x12a122ea, 0x850e2d}}}, + /* 15*16^42*G: */ + {{{0x08d84958, 0x137e8ecd, 0x0b3172bb, 0x03bd62d9, 0x0cc866a1, 0x0dcbb6a0, 0x1f9d27c6, 0x016d36ce, 0xe846e8}}, + {{0x1882cf9f, 0x062323db, 0x18306990, 0x03466ce3, 0x0b76fad5, 0x0c8823cc, 0x1895076f, 0x1f91298f, 0xa29cb8}}} + }, + { + /* 1*16^43*G: */ + {{{0x1e58ad71, 0x1bb1c44d, 0x068e8823, 0x01a3eb9f, 0x08c38bb3, 0x1f4b14ef, 0x0f8c2817, 0x11851bd8, 0x7778a7}}, + {{0x1d9f43ac, 0x1a89fe0f, 0x092b158e, 0x070823fe, 0x1580087b, 0x0709797f, 0x08bfdc26, 0x1356b4b6, 0x34626d}}}, + /* 3*16^43*G: */ + {{{0x1319c869, 0x1be37571, 0x07ac9c0b, 0x13f2baec, 0x0acdc18a, 0x1c8117e6, 0x1f234060, 0x0bb302e7, 0x301804}}, + {{0x12b856f0, 0x0063b64e, 0x0f669eff, 0x15099494, 0x02e3bca2, 0x121906b1, 0x1edbe198, 0x0f04a076, 0xac5fc5}}}, + /* 5*16^43*G: */ + {{{0x05ed29b5, 0x0334c37d, 0x0ab746d3, 0x0616c0e2, 0x1c885f58, 0x13edcd31, 0x1bcd0ead, 0x16c3dcaf, 0x322881}}, + {{0x1cd15ad2, 0x1a789373, 0x1b9d813b, 0x075d5729, 0x131e1ca8, 0x18cefa0a, 0x113ac442, 0x1082f406, 0x167702}}}, + /* 7*16^43*G: */ + {{{0x06c96100, 0x1fd4203c, 0x1a72398e, 0x0602354d, 0x1dbcca16, 0x0ecd96f9, 0x0e7fed0f, 0x07581f63, 0x3f3847}}, + {{0x12624707, 0x0f1560df, 0x1d7c6f3c, 0x0e38f816, 0x19ce5665, 0x02231783, 0x0e5494d3, 0x0abeba80, 0x70c69c}}}, + /* 9*16^43*G: */ + {{{0x1d22d2ac, 0x1a637637, 0x12ab3808, 0x1bfc24db, 0x1df2f10d, 0x00704bc2, 0x1db72d0f, 0x18bfa4f4, 0x288113}}, + {{0x0f42a268, 0x00f5aafc, 0x12323f42, 0x07a8942a, 0x16137ddc, 0x1b93064b, 0x1723c81d, 0x002b1f78, 0xa1a7eb}}}, + /* 11*16^43*G: */ + {{{0x045f8ea8, 0x05d406e9, 0x134a4035, 0x0491c72c, 0x19fe5d17, 0x06caeb88, 0x08a954fd, 0x001908c7, 0xf963a2}}, + {{0x19bc99eb, 0x1e2afd82, 0x02d82092, 0x11e46b3b, 0x027208bb, 0x11180ffa, 0x0f028edc, 0x04d18ff0, 0x9c8594}}}, + /* 13*16^43*G: */ + {{{0x0606a315, 0x10d44189, 0x1a58eb67, 0x04c0e5e4, 0x0097e407, 0x05952c87, 0x069fe636, 0x099fee1b, 0xa5d922}}, + {{0x1e3b68d1, 0x1ab099ac, 0x0469f274, 0x1a1a68fa, 0x00de9ed4, 0x0355ebcc, 0x096cd0cc, 0x0007641b, 0x87328b}}}, + /* 15*16^43*G: */ + {{{0x06231493, 0x06dbdaa0, 0x131351a7, 0x02350619, 0x1e6a4964, 0x120e8072, 0x0d813ad3, 0x05c36e78, 0xf1fe98}}, + {{0x158848c1, 0x0b54cd33, 0x17fc3406, 0x07f668dc, 0x199d3f17, 0x1e102fbe, 0x177085b4, 0x1d5db349, 0x2e2019}}} + }, + { + /* 1*16^44*G: */ + {{{0x06d903ac, 0x04f6d4e0, 0x0b5f972c, 0x12c4e9cb, 0x0fd2ed5f, 0x0fe9873d, 0x01118dca, 0x0bdcc6f5, 0x92895}}, + {{0x1bcd091f, 0x08c0749a, 0x0a360ff1, 0x1a4ddf51, 0x095eeac3, 0x0509849d, 0x0aa09ede, 0x0007a7e8, 0xc25621}}}, + /* 3*16^44*G: */ + {{{0x1874b839, 0x088943ab, 0x0f4ad060, 0x022b672a, 0x0b6aebe4, 0x186fd918, 0x16a014f7, 0x03f81c3c, 0x3e03b8}}, + {{0x1c0594ba, 0x060e72b3, 0x0ad6e368, 0x0b8be1fb, 0x18f667de, 0x1303ab8c, 0x1d0b113d, 0x0c7bfe0f, 0xd13ae1}}}, + /* 5*16^44*G: */ + {{{0x0357a513, 0x11fbc734, 0x0cc08fce, 0x037a268b, 0x122c5f15, 0x1141d514, 0x04b358be, 0x16f45e89, 0xe662c0}}, + {{0x0017d07e, 0x095100e5, 0x14e36246, 0x06b9ac4a, 0x1a419d80, 0x11045090, 0x148c176b, 0x079cc248, 0xab0b19}}}, + /* 7*16^44*G: */ + {{{0x1f37d242, 0x0cafbf7e, 0x07052c12, 0x1fd94c0f, 0x1587dc29, 0x1163e5f1, 0x1b2e10e1, 0x1639299e, 0x40bf80}}, + {{0x06405088, 0x08ec13cd, 0x0f4d560f, 0x043d7485, 0x0fe12743, 0x1f4d8d93, 0x0bc13d4f, 0x06bb0ad5, 0xb579dd}}}, + /* 9*16^44*G: */ + {{{0x195a3558, 0x17959b22, 0x0d29fcae, 0x0e3f0bc4, 0x159f6ac0, 0x0bc09c6d, 0x09c201be, 0x12ec03b9, 0x3d14fe}}, + {{0x0443df4c, 0x156d9d63, 0x1075c9f1, 0x0145c28f, 0x16e1482e, 0x1498edfa, 0x07be3ca6, 0x1add08d0, 0x16c6bd}}}, + /* 11*16^44*G: */ + {{{0x02b1fc24, 0x0f4fad6c, 0x0fdd5c3b, 0x11b038fc, 0x04865252, 0x16269649, 0x14947306, 0x081d05cc, 0xdd6fa5}}, + {{0x0e9b74ca, 0x1218e230, 0x1cc88c12, 0x01bcd7da, 0x17e77ec1, 0x18f5f8b2, 0x01bf8d9b, 0x0fd63a63, 0x67e62b}}}, + /* 13*16^44*G: */ + {{{0x1dd08e02, 0x0f548ac9, 0x0b7c0a20, 0x0a8f6ffb, 0x11e80108, 0x0a4cd51e, 0x15e03e1a, 0x1505bcab, 0x13fa2d}}, + {{0x1cb03410, 0x12aa0ee1, 0x090ae5f6, 0x095f7633, 0x032c7e64, 0x0b1035da, 0x09c8c4cd, 0x1608aabb, 0x136338}}}, + /* 15*16^44*G: */ + {{{0x144ee41a, 0x0119d5cc, 0x1f5a69ab, 0x16adba76, 0x08282879, 0x085b3963, 0x0910fdf0, 0x0a3a78e1, 0xd06c48}}, + {{0x0b295e6f, 0x18fc274c, 0x18bb894b, 0x170868c2, 0x030919b7, 0x166a7a7b, 0x02b6eec2, 0x0980b09a, 0x5815fd}}} + }, + { + /* 1*16^45*G: */ + {{{0x03d82751, 0x1d573a8b, 0x0b4d5149, 0x0b69520f, 0x1b285564, 0x1279d071, 0x0424e641, 0x1e7d8db6, 0x85d0fe}}, + {{0x0eb1f962, 0x1611bd12, 0x1dccc560, 0x0ea3d2d0, 0x0f5663e8, 0x04b72c16, 0x102f8a75, 0x10827471, 0x1f0364}}}, + /* 3*16^45*G: */ + {{{0x0cde4cf3, 0x0caa830f, 0x02819aae, 0x01ca6a8f, 0x19ae7934, 0x169368ae, 0x0b0ef9f0, 0x09582284, 0x384dab}}, + {{0x052d0566, 0x1e3cb591, 0x146e9ced, 0x0614672e, 0x0c6f01f4, 0x16b6d15a, 0x090efed3, 0x179a3739, 0xd6e3c5}}}, + /* 5*16^45*G: */ + {{{0x1e5238c2, 0x0579f490, 0x03b2e2e6, 0x0abeb870, 0x0ed48403, 0x1085a741, 0x16a906c5, 0x01d6fa82, 0x14f0ec}}, + {{0x12f07922, 0x14351a3c, 0x0124e75b, 0x1801b006, 0x0747fd25, 0x039f1c21, 0x1602487f, 0x07ba906b, 0xab12d5}}}, + /* 7*16^45*G: */ + {{{0x1543e94d, 0x1b1a977e, 0x1e638623, 0x06054ead, 0x00ddadd1, 0x1a33c52d, 0x01fb1070, 0x176f0585, 0xeb42f3}}, + {{0x05924d89, 0x02acef22, 0x035b5090, 0x108d1bcc, 0x1fb774cd, 0x0eab97e6, 0x04b72683, 0x00e9e4bb, 0x234a6d}}}, + /* 9*16^45*G: */ + {{{0x1e19aaed, 0x19272dab, 0x199cc9c0, 0x1759bd18, 0x0a920459, 0x0017b703, 0x0366a7bb, 0x194a2d04, 0x1cf138}}, + {{0x092f400e, 0x09b752eb, 0x11dffef0, 0x1ddf1fdf, 0x1de17479, 0x195335b6, 0x0e197d0d, 0x1e62e38c, 0xd6ffda}}}, + /* 11*16^45*G: */ + {{{0x16a8aa39, 0x1b6074fd, 0x1e3eb157, 0x0cc6f694, 0x190d937a, 0x104b424c, 0x104b21d6, 0x17cbe81a, 0xb58686}}, + {{0x0b493c1f, 0x1e3c9ae9, 0x16cd1ee3, 0x1b5f31cd, 0x0a91dabb, 0x1c6a2a60, 0x10b05251, 0x086498f1, 0x5632d5}}}, + /* 13*16^45*G: */ + {{{0x103b4cc5, 0x148f5f1d, 0x071df0bb, 0x106374b4, 0x1a802572, 0x1e27f3f9, 0x10ad9ed6, 0x160d7179, 0x5fc19d}}, + {{0x05b57c28, 0x1d9cfdc3, 0x021fb128, 0x0dea0798, 0x05ef4927, 0x09c7cd1d, 0x1f19bb88, 0x181d9318, 0xec8e84}}}, + /* 15*16^45*G: */ + {{{0x0cb38cb5, 0x1a5c2bea, 0x0e22522e, 0x16ffbe9a, 0x0ea1be10, 0x05207e9f, 0x0a277aea, 0x01a85dbc, 0xb88fb7}}, + {{0x1965f3d7, 0x1dfd3ab2, 0x0be31c65, 0x1e7c244f, 0x1a8e24d4, 0x1dcca59a, 0x0a0180d2, 0x15a8dd46, 0xd6c736}}} + }, + { + /* 1*16^46*G: */ + {{{0x0526087e, 0x1aa02412, 0x16880c23, 0x16db1105, 0x0b85dfdf, 0x1b020bcc, 0x1a5f0726, 0x19d2fdd9, 0xff2b0d}}, + {{0x10c29907, 0x04a8f00f, 0x038b3acb, 0x0fdadf72, 0x07936c7b, 0x026e2a68, 0x08622bd3, 0x1fdea497, 0x493d13}}}, + /* 3*16^46*G: */ + {{{0x19d681f9, 0x0c82a7f3, 0x03fae7f1, 0x1c1ddf59, 0x094b066c, 0x1f92f016, 0x0c2222df, 0x1e4eebe4, 0xc745fd}}, + {{0x1bbb1247, 0x018b9a1b, 0x1f5171d8, 0x17a66b8c, 0x018678cd, 0x1ca63874, 0x179e29c4, 0x1e5ed73c, 0x590222}}}, + /* 5*16^46*G: */ + {{{0x15cd0ea3, 0x10267769, 0x12b12057, 0x08f1d041, 0x0e7f2b34, 0x0f2f5b39, 0x142c9e96, 0x1e752ea0, 0xabb279}}, + {{0x1c307bce, 0x1849899b, 0x00bced91, 0x0ed20b3c, 0x18ed47c9, 0x1f060183, 0x1c367ed2, 0x0777e2f2, 0x5dee10}}}, + /* 7*16^46*G: */ + {{{0x1bc9ee3e, 0x017179f8, 0x19ce0b17, 0x1f4352c7, 0x1ed11ea9, 0x1553a133, 0x00a09feb, 0x016b3f8d, 0x3f8115}}, + {{0x199aae06, 0x0756d862, 0x16a0580f, 0x0765b9f9, 0x15662762, 0x1f59e23c, 0x00b519c6, 0x0d1fb7f5, 0x19c88a}}}, + /* 9*16^46*G: */ + {{{0x18e4a007, 0x0b8df7d7, 0x0ecd62d8, 0x19dd9e11, 0x0c7ec15e, 0x1d19d52b, 0x179a5652, 0x05ba0105, 0x5cf813}}, + {{0x1068b883, 0x131f8484, 0x071ffa33, 0x08df2d8f, 0x03df6c89, 0x00ac4246, 0x0837d2b5, 0x0b81ac3f, 0xb45aee}}}, + /* 11*16^46*G: */ + {{{0x06c2d4a7, 0x0ab7f3d9, 0x13ffec42, 0x06df2677, 0x04ed21bc, 0x19cb9e20, 0x125194f8, 0x09a1a974, 0xb6d5fe}}, + {{0x1ae86371, 0x0c6e73f1, 0x178f3204, 0x16fc9cde, 0x1fd8e745, 0x1c904eff, 0x1b0537f3, 0x1427577a, 0x47f373}}}, + /* 13*16^46*G: */ + {{{0x1c66dd33, 0x0499b117, 0x171db714, 0x1f791fe3, 0x1b022ea8, 0x0d8a8014, 0x021c1aec, 0x180cd9eb, 0x61c8bb}}, + {{0x16f10bfa, 0x1ddd4f9d, 0x00832328, 0x020dd585, 0x1d3fb6a5, 0x0cca5cc2, 0x1c0d119a, 0x0473ca9e, 0x93599e}}}, + /* 15*16^46*G: */ + {{{0x1d6b2ff8, 0x002dbe66, 0x01b23ea6, 0x066d82e5, 0x1bdf1876, 0x1a9b9f61, 0x01461f27, 0x14ae84cf, 0x94e32b}}, + {{0x0ce1af3e, 0x0ea42aa9, 0x1aff84eb, 0x15e084a4, 0x19e8cb33, 0x12443316, 0x13864bc7, 0x11687b40, 0xd1b44}}} + }, + { + /* 1*16^47*G: */ + {{{0x1856e241, 0x0072f167, 0x15b74a1e, 0x03dc2919, 0x1212b57f, 0x1973180d, 0x03aa7b4a, 0x1c963d10, 0x827fbb}}, + {{0x0ec293ec, 0x102db45d, 0x040c59b5, 0x0f4c630d, 0x112687ff, 0x19633e8e, 0x0c2dc6fb, 0x12478e4f, 0xc60f9c}}}, + /* 3*16^47*G: */ + {{{0x1bb80fa7, 0x1a242e59, 0x0104e218, 0x0fb4d76e, 0x0819f3aa, 0x1035e990, 0x0bef0346, 0x03ec6118, 0x857e3}}, + {{0x09366b2d, 0x0cc108f8, 0x15c05aaf, 0x1c6e0879, 0x17147172, 0x064e8ee5, 0x1c824b5f, 0x08475c02, 0xf64393}}}, + /* 5*16^47*G: */ + {{{0x09c70e63, 0x0e8161d0, 0x14f525bd, 0x1716f1ce, 0x0672e9cb, 0x032abb25, 0x0010d517, 0x1d4ad7ac, 0x28aacc}}, + {{0x1057da4e, 0x0f81c417, 0x13687a2e, 0x18b39c88, 0x0ebb7f5f, 0x0b33e3b4, 0x18559ea2, 0x05df0341, 0x2b6932}}}, + /* 7*16^47*G: */ + {{{0x13e674b5, 0x00ee297b, 0x0182ab18, 0x11ed39ce, 0x18a4f92d, 0x1964de75, 0x19851776, 0x04b40ab4, 0xa2f3b6}}, + {{0x0e937941, 0x049c6470, 0x0cfe94ec, 0x05f462f8, 0x07c4b922, 0x05487995, 0x02ba0011, 0x0b2c298d, 0x620ea1}}}, + /* 9*16^47*G: */ + {{{0x191eb056, 0x1b00b18e, 0x13f4e1b1, 0x05dfb71d, 0x115f5a00, 0x1ae351fa, 0x048c7662, 0x193d55cb, 0x3c4f83}}, + {{0x005cecab, 0x012c49ed, 0x13dae1dd, 0x056a8903, 0x07880198, 0x12b9e1d9, 0x0da8ceb5, 0x00ea2951, 0x944790}}}, + /* 11*16^47*G: */ + {{{0x1d86dfa9, 0x0830fedd, 0x0e64e9c6, 0x11694813, 0x03baadc3, 0x0f01f408, 0x1f538a70, 0x0511532c, 0xaff8e1}}, + {{0x01d12681, 0x1881e1b6, 0x067e71c1, 0x02db5288, 0x153f4f91, 0x15d50fe7, 0x10ff4f4f, 0x166426ef, 0x8d8b4b}}}, + /* 13*16^47*G: */ + {{{0x189ba9c1, 0x07939b5c, 0x074ce38a, 0x1ef94b41, 0x0e579e40, 0x01315767, 0x02cfa116, 0x08a51b80, 0xd3fb78}}, + {{0x0b51b267, 0x10dc46ff, 0x046b7801, 0x19dbab80, 0x10fe6341, 0x102bac5b, 0x139f29f2, 0x069df4d6, 0xf894d4}}}, + /* 15*16^47*G: */ + {{{0x0f2bb909, 0x0d4b60e8, 0x16636667, 0x0204f8a6, 0x07d7f639, 0x14c41c8c, 0x0a23fd1c, 0x01c15935, 0x4ec930}}, + {{0x04cf4071, 0x0451c1fd, 0x0b0e09ee, 0x1c2d041b, 0x049bad52, 0x0e228c26, 0x13717203, 0x00d7c360, 0x782ba1}}} + }, + { + /* 1*16^48*G: */ + {{{0x0120e2b3, 0x19dac7d1, 0x11fe6a9f, 0x11fb9cfe, 0x0e5217a5, 0x0571a673, 0x16eb9ef9, 0x1e43ea37, 0xeaa649}}, + {{0x1a5ad93d, 0x03d2982d, 0x0fdf9675, 0x0d72cbe2, 0x1aa5a01a, 0x007c4c3c, 0x00eb1a6a, 0x1dab7776, 0xbe3279}}}, + /* 3*16^48*G: */ + {{{0x1f2e070d, 0x0c1fe9d1, 0x0a9aa63d, 0x156e398a, 0x047e229a, 0x18e1dc28, 0x0affd21c, 0x1d2085e9, 0x4b72a5}}, + {{0x096dd780, 0x025d4177, 0x05230f79, 0x08cbbba5, 0x13c10b0b, 0x1dd9b687, 0x073d809d, 0x09c3ad5c, 0x599e1d}}}, + /* 5*16^48*G: */ + {{{0x0a02591c, 0x0e73fec2, 0x1449687a, 0x0a932cb0, 0x1fd613ef, 0x1fdf5af0, 0x038a169a, 0x1f8ca739, 0xa9fc93}}, + {{0x09bec2dc, 0x0856ef7b, 0x13dc94de, 0x111882bf, 0x165e5ca8, 0x00bd0d48, 0x1c5cfa13, 0x073b8a70, 0x9c2ce7}}}, + /* 7*16^48*G: */ + {{{0x0d968b59, 0x08037071, 0x12ef0b84, 0x05175c27, 0x1027709a, 0x1d60904d, 0x1c29a9f5, 0x0f834df3, 0xc94001}}, + {{0x0de572fb, 0x17ebb204, 0x0432723f, 0x08596c87, 0x1742ce28, 0x10dfd2da, 0x18804ee2, 0x0a019370, 0x39d922}}}, + /* 9*16^48*G: */ + {{{0x126b3332, 0x143999ab, 0x1b9779a8, 0x0711a0e7, 0x1f8e0310, 0x09d2fb85, 0x0093b19e, 0x13afdda0, 0x1f84bb}}, + {{0x14e8d52e, 0x0a214518, 0x1b70e895, 0x199c5a86, 0x1edf0c2b, 0x013fbadc, 0x1b30951f, 0x00e57953, 0xee726d}}}, + /* 11*16^48*G: */ + {{{0x0defa98e, 0x06d52a56, 0x0b09e657, 0x1088d023, 0x1e9c7724, 0x0abd9cc8, 0x1341b2a0, 0x112128bf, 0xf13e0}}, + {{0x1e286767, 0x0453bb4d, 0x13ab3370, 0x1ce0bc2d, 0x162db287, 0x1c5853d9, 0x1140d78f, 0x1e2ec9cf, 0xadd521}}}, + /* 13*16^48*G: */ + {{{0x09f59b6b, 0x0f0e01df, 0x02238be9, 0x0718c783, 0x026d3e9b, 0x050e96ac, 0x11f6cdca, 0x14aa3bbd, 0xdde191}}, + {{0x06cb1410, 0x156cb149, 0x0553fb3d, 0x0e7177ce, 0x0e14e8b5, 0x0beb0e29, 0x172f829d, 0x0f00504e, 0x5b2bfb}}}, + /* 15*16^48*G: */ + {{{0x09c6b699, 0x1462afee, 0x191a1c6d, 0x1eae6ad7, 0x01682a86, 0x0bdfcbda, 0x1de9685b, 0x05ddb06d, 0x5fab01}}, + {{0x01c6c3aa, 0x0b990a96, 0x020d466c, 0x1622ffd5, 0x02f7b90a, 0x1a08986b, 0x0513a7ae, 0x0e14787a, 0x2d9bfa}}} + }, + { + /* 1*16^49*G: */ + {{{0x1a34d24f, 0x111b196e, 0x084dd007, 0x0db1e193, 0x02ee541b, 0x0fb6f67a, 0x1a764e47, 0x0878b9e2, 0xe4a42d}}, + {{0x1eba9414, 0x13fb898e, 0x16393c4e, 0x0dddbf51, 0x0d34ce88, 0x0ce67dc5, 0x1cd49bf2, 0x1ce2da38, 0x4d9f92}}}, + /* 3*16^49*G: */ + {{{0x1bea0c68, 0x04208579, 0x1ece4ad7, 0x060246ce, 0x16faf094, 0x1e47469c, 0x0e892526, 0x069c2ad4, 0x3e4196}}, + {{0x1a45edb6, 0x05db7fb8, 0x0f3686af, 0x02328c60, 0x093062fa, 0x05ff1b83, 0x07dfcdcf, 0x13b24964, 0x123c5}}}, + /* 5*16^49*G: */ + {{{0x139824d7, 0x1bae91e4, 0x072625eb, 0x0f6c986a, 0x10b576eb, 0x11f317bf, 0x1423bb52, 0x1ea8abae, 0x8d9438}}, + {{0x1366489f, 0x10027a44, 0x1ac18f62, 0x13c57064, 0x0ef6f8fb, 0x05e98d5b, 0x10a8b298, 0x0e69fdcd, 0x3261e0}}}, + /* 7*16^49*G: */ + {{{0x18d713de, 0x124038d4, 0x0a398823, 0x0185f6e8, 0x14543936, 0x089517f2, 0x1108352a, 0x18ab1dca, 0xb72524}}, + {{0x1b8350e9, 0x17ff292c, 0x1297f2dd, 0x05a4dfc8, 0x09415048, 0x08c174eb, 0x1914410b, 0x13514507, 0x4c51b3}}}, + /* 9*16^49*G: */ + {{{0x1e3b2cb4, 0x0636149f, 0x1c84100b, 0x13e6b7e6, 0x1149e304, 0x1b71c090, 0x09466b71, 0x0b442da2, 0x3de45f}}, + {{0x107eb02f, 0x10f19d61, 0x01c1133d, 0x1c51ccb5, 0x09106823, 0x055254be, 0x17714382, 0x13080bd5, 0xba2a85}}}, + /* 11*16^49*G: */ + {{{0x0ce4e5bf, 0x11a3b37b, 0x04016c5c, 0x0f950d41, 0x106ae9b6, 0x1e71ba44, 0x1a1f078f, 0x18d12b37, 0x8511f1}}, + {{0x01789c08, 0x1e494a26, 0x14d9498b, 0x10f11378, 0x000232da, 0x0fbf6355, 0x121d3077, 0x19f2379a, 0xecdff5}}}, + /* 13*16^49*G: */ + {{{0x1d3258ab, 0x0dab3451, 0x0f05370c, 0x04850315, 0x0ab5957d, 0x0e39770a, 0x0088b3e8, 0x05d039ec, 0x8c5a05}}, + {{0x022d0f8f, 0x0bf04298, 0x16512b79, 0x15d1f381, 0x008c246d, 0x0063c826, 0x16841e6a, 0x09768877, 0x6811db}}}, + /* 15*16^49*G: */ + {{{0x1f91bcee, 0x0c615055, 0x03036105, 0x1e3b1e3c, 0x1f137f5c, 0x1e762ab5, 0x1582f718, 0x02dbd7a6, 0xcef7f8}}, + {{0x01966b33, 0x1da6d4fc, 0x1cbdab1a, 0x1c960542, 0x1245fa63, 0x199ce00e, 0x1c04918e, 0x106c6e90, 0x67e74c}}} + }, + { + /* 1*16^50*G: */ + {{{0x0300bf19, 0x18b9dcea, 0x03fa9251, 0x0a6aed51, 0x12b6b92b, 0x07d6d59a, 0x17655058, 0x1de6c197, 0x1ec80f}}, + {{0x0107cefd, 0x18e6e0e6, 0x05681ed9, 0x0dcefec5, 0x1bf5e014, 0x04ac53d5, 0x1034bce9, 0x06ead6a6, 0xaeefe9}}}, + /* 3*16^50*G: */ + {{{0x12fea1f9, 0x18be5de2, 0x0114ae52, 0x1e16a118, 0x06531c4f, 0x0ed5b388, 0x0ba0ef3f, 0x014aba3e, 0xa6dc88}}, + {{0x1bc345e9, 0x18e0a723, 0x1a3df98e, 0x1713b6fc, 0x0bc50057, 0x01d08b56, 0x1f0c0e1a, 0x0a8fb86c, 0x7ef1a8}}}, + /* 5*16^50*G: */ + {{{0x06d6c9b3, 0x06a061f8, 0x01958df2, 0x1899d0e9, 0x081ba8c6, 0x114e3c52, 0x1664af70, 0x07fd4848, 0xfe6ba9}}, + {{0x0948bdfb, 0x0163c47d, 0x10ba6c03, 0x01e37e0b, 0x13b56d98, 0x00d9a2a0, 0x01cadaed, 0x1ae80a73, 0x7ee918}}}, + /* 7*16^50*G: */ + {{{0x0cf95151, 0x11788398, 0x0b12d910, 0x0900dc88, 0x0ded1b96, 0x04616e04, 0x02fec083, 0x1e28df93, 0x15d5e2}}, + {{0x0ff8ecf2, 0x01503e61, 0x16303e52, 0x009f72fb, 0x023f9bb2, 0x1084bc48, 0x13b1fe43, 0x06322bfa, 0xa5b72e}}}, + /* 9*16^50*G: */ + {{{0x096a5658, 0x0bc085c9, 0x1bd9590f, 0x0964a483, 0x029be381, 0x100493d7, 0x11eb631f, 0x0e4ad108, 0x84c0e8}}, + {{0x181b80d1, 0x0cb394de, 0x13c7f48b, 0x0c35303e, 0x1725ed3a, 0x118c8329, 0x0b12821f, 0x10182c04, 0x265983}}}, + /* 11*16^50*G: */ + {{{0x14dc6a0f, 0x1addae44, 0x1f855d4d, 0x06832285, 0x077c2744, 0x1273d160, 0x0c755949, 0x18e3526e, 0xfed6b1}}, + {{0x176fc7e0, 0x05c6b96c, 0x0ff10273, 0x09ab2614, 0x1ae23137, 0x0c0d7269, 0x1c2a11e4, 0x1cd61fff, 0x8de2ab}}}, + /* 13*16^50*G: */ + {{{0x18e29355, 0x19c42f88, 0x1c8361b6, 0x191d2672, 0x1b9d82f1, 0x1c302011, 0x0f1c3f3b, 0x1b325a79, 0x2a6a4d}}, + {{0x15cc2872, 0x029f007d, 0x1131db00, 0x00b474c9, 0x0f90dfe9, 0x0e40f134, 0x1831d83f, 0x174f894f, 0x8677df}}}, + /* 15*16^50*G: */ + {{{0x0148dabf, 0x1cfd447f, 0x075e3ac7, 0x1ba57269, 0x0e735c1a, 0x07611afd, 0x151b65d9, 0x004d924e, 0xe42d93}}, + {{0x011e1361, 0x1b963ab4, 0x19dfae75, 0x04eae033, 0x18530327, 0x0675fef9, 0x12c362ce, 0x058dc5b0, 0x641386}}} + }, + { + /* 1*16^51*G: */ + {{{0x166642be, 0x0edac941, 0x162ea227, 0x0920e2fa, 0x1fa8bce3, 0x057a3406, 0x10be46c0, 0x11808ce1, 0x146a77}}, + {{0x1d83efd0, 0x0594ba41, 0x1f97b474, 0x152e3a5e, 0x0b2870aa, 0x0c13fcea, 0x0a2b759a, 0x1d866a80, 0xb318e0}}}, + /* 3*16^51*G: */ + {{{0x07315443, 0x0c9c39c1, 0x1a19ca67, 0x1377aa95, 0x142a13d7, 0x04cc1050, 0x0d7fd0b2, 0x0080cc12, 0xfc696c}}, + {{0x17d28960, 0x0486b05a, 0x06f46c5d, 0x1fe90c21, 0x077b5487, 0x10e6eb4b, 0x024aefc3, 0x1d7f076b, 0xe0ce27}}}, + /* 5*16^51*G: */ + {{{0x16fdb4eb, 0x0dd97ae0, 0x0b9a9e74, 0x07baf38c, 0x0b0928fa, 0x151ab15f, 0x0ab46b95, 0x043fe9fe, 0x974af2}}, + {{0x09f6f484, 0x004e1efd, 0x1ff08d21, 0x18ae5477, 0x090ed111, 0x121e8160, 0x0f299347, 0x0faa6a00, 0x555238}}}, + /* 7*16^51*G: */ + {{{0x1d5aeee3, 0x1c8256da, 0x163204dc, 0x109786cc, 0x070c5e82, 0x0d9d3349, 0x062c2448, 0x13693bc7, 0x5baab5}}, + {{0x10f69717, 0x1694d7db, 0x14c7bb60, 0x1bb93b57, 0x1daf7215, 0x004330ff, 0x1a15b968, 0x0c2f81ef, 0x8a577f}}}, + /* 9*16^51*G: */ + {{{0x15726890, 0x08d8f227, 0x00df4561, 0x004bfd59, 0x10a0ee59, 0x17e75fc8, 0x10f4040c, 0x14fd6938, 0xfb685f}}, + {{0x1835783a, 0x0375479d, 0x039e6c98, 0x0d9625d2, 0x0ba094fa, 0x10b6cc32, 0x18ba1a72, 0x045931cb, 0xd750df}}}, + /* 11*16^51*G: */ + {{{0x08bca48a, 0x0325f89d, 0x0f7d8bd5, 0x09abe9d0, 0x1f71d78b, 0x1dc8e143, 0x05682ac9, 0x1ecb3c53, 0x5de58f}}, + {{0x12fd41cd, 0x03ca7406, 0x03e2aa56, 0x0b7b389c, 0x13c843fe, 0x111e1296, 0x0d54c269, 0x07b006b3, 0x685a3b}}}, + /* 13*16^51*G: */ + {{{0x05ef63b6, 0x0f1c1a27, 0x06c60baf, 0x09a02ce6, 0x1c1c85ab, 0x1fed1da7, 0x02febc6d, 0x19bd5ac3, 0x6f1825}}, + {{0x05c655f3, 0x1642367a, 0x1fe51504, 0x12b4c804, 0x134553c8, 0x1026bc19, 0x046e63d0, 0x0fbab232, 0xff097e}}}, + /* 15*16^51*G: */ + {{{0x14a63f3b, 0x1a823f6f, 0x1e3e5c9e, 0x00760332, 0x0c765832, 0x08afaf3b, 0x0ddad61c, 0x12beec54, 0xc5ecb8}}, + {{0x05005024, 0x09f9ba34, 0x0c2fb96d, 0x16cbdcbc, 0x033ec8be, 0x002c7fc9, 0x07cdd3a9, 0x03032f10, 0x222525}}} + }, + { + /* 1*16^52*G: */ + {{{0x1180eef9, 0x0bb543c9, 0x0a2e5ddb, 0x00244134, 0x07b128d0, 0x075d8d50, 0x17c1f8eb, 0x1ec3a45c, 0xfa50c0}}, + {{0x1f4f2811, 0x066c6be9, 0x1e884ece, 0x1065274a, 0x1a68a5e6, 0x09439140, 0x0ea6dcb3, 0x124472fd, 0x6b84c6}}}, + /* 3*16^52*G: */ + {{{0x11da5e12, 0x0f70719c, 0x12b2ca5c, 0x0c14802b, 0x0a2b6a9c, 0x14fc9d0e, 0x0c6f368c, 0x07886f3c, 0xf7502e}}, + {{0x0385f4eb, 0x125ce2f4, 0x0973af1e, 0x0da65dee, 0x072047b8, 0x04a2e1eb, 0x0bf5665c, 0x1dbacf9f, 0x3c57f5}}}, + /* 5*16^52*G: */ + {{{0x10b7d105, 0x03a49988, 0x195f27c8, 0x14b89729, 0x055b3f4c, 0x1b31271a, 0x018a8e93, 0x1f3075cb, 0x12fe78}}, + {{0x1f794a60, 0x0c5637dc, 0x1ba42e11, 0x19c4cbad, 0x1cb771de, 0x0d50ccd3, 0x13dde1ad, 0x14671ad7, 0x2062f1}}}, + /* 7*16^52*G: */ + {{{0x1e0c5d05, 0x110ec19c, 0x15cb6bfd, 0x1cd8a154, 0x04b1a480, 0x1d881404, 0x1cf56312, 0x0268fbe8, 0x76aac3}}, + {{0x11ece63e, 0x0b30cdba, 0x179bb8d5, 0x044b9e02, 0x0625f4b1, 0x19641901, 0x03beafbc, 0x1de1ab8e, 0xef5576}}}, + /* 9*16^52*G: */ + {{{0x1c53c086, 0x1137a42f, 0x0686bb27, 0x0ab86939, 0x08104c6b, 0x0618a2f4, 0x13321f98, 0x0b77cb8b, 0xa663fe}}, + {{0x05016201, 0x14e28195, 0x0653f039, 0x1d994ae7, 0x03dc3991, 0x081644c1, 0x1efd746c, 0x0fed6423, 0xb54199}}}, + /* 11*16^52*G: */ + {{{0x0b758574, 0x16c00f6a, 0x13e71ba6, 0x04cd1286, 0x0bdf3d83, 0x10813d71, 0x16096df2, 0x0f4040d9, 0xde9552}}, + {{0x1b67232a, 0x1bef8fe7, 0x168b7ad2, 0x0d420a2a, 0x09ed7bae, 0x0e423f8b, 0x05393887, 0x0ad5927a, 0x4cd3e0}}}, + /* 13*16^52*G: */ + {{{0x1d85474f, 0x1bcdc55f, 0x18d19a35, 0x18945712, 0x05aea894, 0x065f223c, 0x0b76ffb7, 0x1c0cda65, 0x8da6bc}}, + {{0x1d7b4ef7, 0x1efce3e9, 0x16f75e97, 0x198b260b, 0x1fff10b1, 0x0fae838e, 0x13fe13f7, 0x0d5e63da, 0x13fc6c}}}, + /* 15*16^52*G: */ + {{{0x1dd042ea, 0x1aea12b3, 0x03abf41a, 0x144d4c8b, 0x093beebf, 0x1324f19e, 0x08e6c6f5, 0x18f9f677, 0x7329ac}}, + {{0x0f5c94a1, 0x0d467f61, 0x1ead3c2b, 0x112ee63b, 0x168ee184, 0x073ca7d5, 0x1c5224a1, 0x06a836ed, 0x927249}}} + }, + { + /* 1*16^53*G: */ + {{{0x1f067ec2, 0x129e995a, 0x0ee94883, 0x11156bab, 0x0e8421a2, 0x1fb5bec4, 0x0846c696, 0x1a194e43, 0xda1d61}}, + {{0x1ad836f1, 0x0afdd078, 0x1e6d2299, 0x0e7133a4, 0x11e2966a, 0x1b30b0e4, 0x01b1e701, 0x0b4f9326, 0x8157f5}}}, + /* 3*16^53*G: */ + {{{0x1a95a8db, 0x0ec3b997, 0x074dbd85, 0x1d81888f, 0x11723b83, 0x13234c8d, 0x141067a5, 0x148c607b, 0xe3e90d}}, + {{0x1b0d1cf9, 0x00b67bf8, 0x06134f44, 0x03df2f99, 0x0e76afbb, 0x15486381, 0x1e2ec03e, 0x1800ad82, 0xfbe53b}}}, + /* 5*16^53*G: */ + {{{0x112ee214, 0x1d57eb20, 0x04c55005, 0x149d2f2b, 0x0c01c782, 0x086feae0, 0x1dd6a2d9, 0x18e65c7a, 0x9f4ffe}}, + {{0x1085f37a, 0x17a21112, 0x12200a0f, 0x136d2617, 0x1c69d971, 0x13417eba, 0x1cb983a5, 0x1c2631c5, 0x639ce2}}}, + /* 7*16^53*G: */ + {{{0x1f61a0a5, 0x05b40a28, 0x10538fbe, 0x04a7c367, 0x00b2c4b1, 0x10520fa2, 0x0b06c5c6, 0x05a82269, 0x431f62}}, + {{0x18cef899, 0x15bdbff3, 0x1595dc91, 0x1554086a, 0x1aa7241b, 0x1328cb91, 0x0e3db5b7, 0x0ffcf548, 0xa29832}}}, + /* 9*16^53*G: */ + {{{0x07748503, 0x16133eab, 0x04ca39f2, 0x1c1c8ee6, 0x012ac0e6, 0x1779cfc5, 0x1ee1b2d8, 0x1bbd9cf1, 0x993dba}}, + {{0x1eb0cee2, 0x1d39b09b, 0x02b6a926, 0x10f7ed37, 0x1f51a17f, 0x1c23c3d0, 0x1bade17d, 0x1dd0ad3d, 0xa521a9}}}, + /* 11*16^53*G: */ + {{{0x06d23d80, 0x0f5113ad, 0x11d351e3, 0x17a23e4c, 0x162c0e10, 0x018e5c84, 0x1a968ce5, 0x1740d5ab, 0x75f17a}}, + {{0x080dd57e, 0x03542d81, 0x13a96426, 0x1ff7db76, 0x16292372, 0x1e85f8cd, 0x0a031ff1, 0x1fc2ac73, 0xa07a62}}}, + /* 13*16^53*G: */ + {{{0x01ad3413, 0x115cdb6b, 0x09f5a12b, 0x13800806, 0x07b7a8db, 0x0fa42e5c, 0x12829ba5, 0x0bc23b3e, 0x667855}}, + {{0x1ebca672, 0x12408103, 0x17199804, 0x1c5a2a75, 0x1df9ea6c, 0x136c93e5, 0x191a4949, 0x07bc4f1e, 0x510dda}}}, + /* 15*16^53*G: */ + {{{0x06cc8563, 0x00057ad8, 0x18407aab, 0x09beb7ff, 0x03688922, 0x015ec0cb, 0x1d22b6b2, 0x06c59b4e, 0xebdc4a}}, + {{0x0394ccfa, 0x0d8bde75, 0x0813e492, 0x080f2492, 0x14f07bec, 0x11af366e, 0x0d7e6c7b, 0x089d0ada, 0x659a31}}} + }, + { + /* 1*16^54*G: */ + {{{0x0d064e13, 0x139d8308, 0x1bc7818a, 0x023bc088, 0x14166153, 0x1fcc747e, 0x1a41c857, 0x1fe192e0, 0xa8e282}}, + {{0x11f4cc0c, 0x17be3988, 0x175af5b3, 0x0f347ca1, 0x115888b6, 0x1f9e2d92, 0x1026afed, 0x0b71b703, 0x7f9735}}}, + /* 3*16^54*G: */ + {{{0x1a3979b5, 0x150ccd85, 0x1fa0a788, 0x111f1bcc, 0x00e50ba2, 0x1f858f72, 0x098c9fcd, 0x18b9b5bc, 0xae2207}}, + {{0x0450fa6f, 0x079e6b34, 0x153e225a, 0x10f6fa6f, 0x17060fca, 0x092291d6, 0x1dc6b532, 0x0a2180f3, 0xea91fe}}}, + /* 5*16^54*G: */ + {{{0x0efca824, 0x0080c880, 0x08593eb9, 0x1c189fd4, 0x05461e0b, 0x0a08032c, 0x139673b1, 0x0195ae55, 0xcb8ded}}, + {{0x0f227361, 0x0a05e82c, 0x04c5d0bc, 0x1a3fbf8f, 0x0cbc496a, 0x16243d16, 0x03216cc5, 0x11ee81b1, 0x33a500}}}, + /* 7*16^54*G: */ + {{{0x1bcbd327, 0x008da6d1, 0x192abba5, 0x1c10a443, 0x16ed6b04, 0x04806bf3, 0x0d9c23a5, 0x05315e30, 0xb0c53b}}, + {{0x0d7be436, 0x10b5e251, 0x18da83c5, 0x144418e8, 0x0b2afd82, 0x15300db3, 0x1a858e3d, 0x0803f7af, 0xee2a97}}}, + /* 9*16^54*G: */ + {{{0x17b836a1, 0x1377db1c, 0x19e7bdf7, 0x0c80bfee, 0x1f526c80, 0x0317673b, 0x04835362, 0x07e653b7, 0x6f6ba7}}, + {{0x06832b84, 0x0def97d8, 0x187fe4d3, 0x1218a511, 0x0e9c0d89, 0x1c5202df, 0x0638f6c2, 0x02ffebf8, 0xdc778a}}}, + /* 11*16^54*G: */ + {{{0x1eb39ede, 0x1771a104, 0x0184b50a, 0x110065ae, 0x04360310, 0x1a33f081, 0x0bd2ef3f, 0x0fb8e845, 0x7d471a}}, + {{0x0bf6607e, 0x04de6ca5, 0x08aa3f43, 0x0b9efa38, 0x086fa779, 0x1118f7fe, 0x15941ee0, 0x033e74d0, 0x4a7b}}}, + /* 13*16^54*G: */ + {{{0x165eb1c1, 0x0b6a4f12, 0x0b9716a9, 0x1aeacf9b, 0x0f0967fc, 0x1e618cc1, 0x1eb1c1c3, 0x0c7f36e7, 0xf00251}}, + {{0x0dde2ae0, 0x0d3eb823, 0x1344795f, 0x0dfeb9ad, 0x0afed857, 0x1a52ed13, 0x037d319a, 0x1d1107a4, 0x54ea9}}}, + /* 15*16^54*G: */ + {{{0x1ac32c64, 0x079a849e, 0x0b92fe13, 0x0dfc07d9, 0x1715e0e3, 0x074a87a3, 0x0ea83dc1, 0x00003da4, 0xac1214}}, + {{0x1eb1a867, 0x00956dd6, 0x14fc2262, 0x14ba74b4, 0x099a3ed5, 0x1b4ab982, 0x0ebc61c2, 0x19758671, 0xce8ebc}}} + }, + { + /* 1*16^55*G: */ + {{{0x0319497c, 0x179c16f4, 0x09423008, 0x1363f4a2, 0x1cab15d5, 0x12b73489, 0x161cb4e7, 0x17393450, 0x174a53}}, + {{0x079afa73, 0x1ed09d60, 0x0e6150e0, 0x16743b19, 0x1f9e6646, 0x0aaf9623, 0x10595ed0, 0x06f57f93, 0xccc9dc}}}, + /* 3*16^55*G: */ + {{{0x154b8367, 0x0a4039eb, 0x1541affa, 0x0b6efacf, 0x16a5db77, 0x12ea2c21, 0x09b9032a, 0x095c88ca, 0x5e5a09}}, + {{0x11ce85ca, 0x0994d4ec, 0x197fe911, 0x1553de7b, 0x04b7a796, 0x00f8ab95, 0x18170b24, 0x19348f2b, 0xae8af8}}}, + /* 5*16^55*G: */ + {{{0x17b10d9d, 0x0cc2bc9c, 0x07e3f2bc, 0x0a5e313a, 0x0b82de6a, 0x05c19886, 0x1a1677b3, 0x15b72e05, 0xd4e0}}, + {{0x1140dced, 0x01080243, 0x18b648fa, 0x113192f1, 0x087f70b5, 0x1c232191, 0x10251f4b, 0x130306ec, 0x87b801}}}, + /* 7*16^55*G: */ + {{{0x1c9caee8, 0x0408d304, 0x089c2ec8, 0x1c408b63, 0x0a667632, 0x10cd7762, 0x1303dbde, 0x026d1dee, 0x36652}}, + {{0x1772b711, 0x14f6351d, 0x056e9fc2, 0x17531265, 0x0944501c, 0x1e340dd3, 0x1b666527, 0x0565527b, 0x1f18c3}}}, + /* 9*16^55*G: */ + {{{0x1446c85c, 0x1ffcba8c, 0x007018d4, 0x0fbc11cc, 0x0c6eade3, 0x1b6229fb, 0x1c7ea819, 0x00adfb71, 0xe5891}}, + {{0x0148972e, 0x1b63bf39, 0x1376b757, 0x00469d01, 0x01898f49, 0x00c5d7a4, 0x0f683b1d, 0x0be23f4f, 0xe39a48}}}, + /* 11*16^55*G: */ + {{{0x022e1259, 0x18c56f9e, 0x004d8abf, 0x0e73480d, 0x17771931, 0x1afd5003, 0x18fcadb3, 0x0da4de28, 0xa74012}}, + {{0x134a5f43, 0x1bb8b921, 0x075b6b57, 0x0cbcea76, 0x07ee5178, 0x18ba533e, 0x07fc6c17, 0x175e329e, 0x5a9ff}}}, + /* 13*16^55*G: */ + {{{0x0b08f1fe, 0x0450bd1e, 0x0821eff6, 0x1cfdce17, 0x0d177d7c, 0x02bb2ec1, 0x13929950, 0x042db28e, 0x87e4b8}}, + {{0x0ed5e2ec, 0x0fba564d, 0x1e1b675d, 0x1e47b7ac, 0x0c2cc6e2, 0x1fc7517c, 0x033b35f5, 0x180ecc69, 0xf74e3a}}}, + /* 15*16^55*G: */ + {{{0x01c4d15c, 0x14380735, 0x1039d2e6, 0x1d4f7e0f, 0x14a44105, 0x11606092, 0x0696a4b0, 0x08c7fd4f, 0x35ea1b}}, + {{0x1f3fe1ea, 0x049cdd7b, 0x194257b7, 0x06754060, 0x13b185d6, 0x1d2feba6, 0x0b35e223, 0x0ca373da, 0xad2191}}} + }, + { + /* 1*16^56*G: */ + {{{0x1475b7ba, 0x027eff84, 0x0462cf62, 0x13ce61c9, 0x18cdbe03, 0x0bf6fa80, 0x0170f4f9, 0x1303286f, 0x959396}}, + {{0x1524f2fd, 0x0dc55fc3, 0x1c24e17a, 0x0a7ec990, 0x0d6849c6, 0x1c3525ce, 0x07762e80, 0x05111866, 0x2e7e55}}}, + /* 3*16^56*G: */ + {{{0x0fd69985, 0x04e2eec8, 0x17dcaba8, 0x013996db, 0x0cf149f3, 0x1486fde6, 0x1df9e23d, 0x0eb9d6e5, 0xae976}}, + {{0x1409a003, 0x0e475a08, 0x1b86bfe2, 0x133a82f5, 0x054c5d0b, 0x0ff7028d, 0x1453a6e3, 0x0e7edc91, 0x912199}}}, + /* 5*16^56*G: */ + {{{0x19262b90, 0x0e0c9efe, 0x0f30a6a7, 0x078983fc, 0x05d1fb72, 0x0f8bbc01, 0x04bb26d9, 0x054b582c, 0x2b1586}}, + {{0x083d7557, 0x08ccb732, 0x05226926, 0x0692e1f3, 0x149066f5, 0x06a96d43, 0x0fea9e8c, 0x07b54146, 0x2eb005}}}, + /* 7*16^56*G: */ + {{{0x08e7be40, 0x1fcb8a65, 0x0103b964, 0x05912922, 0x0769af2d, 0x0e0b0b72, 0x199dfba5, 0x1da352dd, 0x6af9ea}}, + {{0x0e387e1c, 0x120b7013, 0x1d655a7e, 0x07eccd41, 0x1dc8145e, 0x152141a3, 0x19259c27, 0x022d200c, 0xb3812a}}}, + /* 9*16^56*G: */ + {{{0x1482801e, 0x135b7d07, 0x0f505574, 0x129f178b, 0x0d6f9407, 0x15a1265c, 0x113bacea, 0x1dc08882, 0x596668}}, + {{0x04870c37, 0x03b8a478, 0x14d4d6b5, 0x0d8396c7, 0x1304e8db, 0x1cb043b8, 0x1d7c7b23, 0x150b775d, 0x949aa0}}}, + /* 11*16^56*G: */ + {{{0x032c19fd, 0x064d973e, 0x000a30f9, 0x1571d20b, 0x10b5b4ac, 0x068bd5ab, 0x01d8bf7d, 0x11036a0a, 0xbe84d1}}, + {{0x12f1281f, 0x1b4a529b, 0x14370dd9, 0x0b4feabc, 0x03994795, 0x12fa4184, 0x02513479, 0x19665b8a, 0xeff960}}}, + /* 13*16^56*G: */ + {{{0x16c69482, 0x08dbafea, 0x0be859ef, 0x156a8026, 0x0bc88cbe, 0x193a6579, 0x1b9507d5, 0x062981af, 0x9867a0}}, + {{0x0f792cd7, 0x178308a3, 0x158a2a45, 0x048b9ea2, 0x099639e6, 0x16aad8dd, 0x0d3e71e4, 0x0b476210, 0xd02e61}}}, + /* 15*16^56*G: */ + {{{0x1d557aa1, 0x0511cec8, 0x007f0a5e, 0x1b25fd9a, 0x1d6abdf1, 0x1975004c, 0x0569649f, 0x08a81b10, 0xa866f2}}, + {{0x01430634, 0x0c0ddda6, 0x184692de, 0x16d38cf8, 0x0e13961e, 0x0c7d2ed8, 0x0d135e4f, 0x1ed50045, 0xb58739}}} + }, + { + /* 1*16^57*G: */ + {{{0x1d82b151, 0x1a89a064, 0x07ee8b6e, 0x01487aac, 0x09a8fcca, 0x108a9d88, 0x195b5916, 0x0a15c803, 0xd2a63a}}, + {{0x1cf89405, 0x00a10ba6, 0x013294b5, 0x1eea15e9, 0x08220a70, 0x172c594a, 0x12dd596b, 0x1f6c887f, 0xe82d86}}}, + /* 3*16^57*G: */ + {{{0x0e4b3ba0, 0x02cfb1af, 0x02fc7c5e, 0x157debe3, 0x1245f5c2, 0x0b8798df, 0x0dcefbf8, 0x00a443ff, 0x410811}}, + {{0x17525595, 0x034b0ee0, 0x08191552, 0x0c930acb, 0x18498133, 0x12d70eb5, 0x19a3cb29, 0x0d2edfea, 0xdc37f3}}}, + /* 5*16^57*G: */ + {{{0x13d98ded, 0x114e4dc4, 0x02808611, 0x1e450677, 0x1d65edbd, 0x114c8298, 0x0323233c, 0x02142d98, 0x63a2a2}}, + {{0x00d1cfc2, 0x0c8cbea7, 0x11e1ce94, 0x17ed4013, 0x194461fa, 0x01992a76, 0x1dbf4194, 0x1c5cffd8, 0x882b42}}}, + /* 7*16^57*G: */ + {{{0x18045445, 0x1430d285, 0x07a35fba, 0x1ee6e320, 0x1bef080f, 0x17172eab, 0x1f28f7c4, 0x0ba893ac, 0xc1581}}, + {{0x0054a206, 0x0a7c3eaa, 0x1633a8c8, 0x00a9c86c, 0x1cd28ba3, 0x1e1db331, 0x045742a4, 0x01475d28, 0x2f30d6}}}, + /* 9*16^57*G: */ + {{{0x03857faf, 0x14891ce6, 0x05865c07, 0x1f95bc3d, 0x0ef7f882, 0x1d47a414, 0x0a70355e, 0x0d7135d1, 0xc757eb}}, + {{0x00584ca4, 0x0eced865, 0x06253040, 0x1a621a20, 0x0c61b627, 0x14f9045f, 0x1cd895cd, 0x19f9a47f, 0xf03a59}}}, + /* 11*16^57*G: */ + {{{0x1459225d, 0x1268c27a, 0x02163443, 0x15eefc5b, 0x002a244f, 0x0a04c8ce, 0x0343e057, 0x15d5b5f4, 0xfa8063}}, + {{0x1ece1507, 0x115bf1db, 0x01f1670e, 0x16d86da0, 0x0425c0a2, 0x11104126, 0x01a45837, 0x120af818, 0xba71f}}}, + /* 13*16^57*G: */ + {{{0x16f0d044, 0x13cff4a0, 0x079869e5, 0x153fa921, 0x0e0a5aab, 0x14e7c317, 0x1ea278bd, 0x18b3a04a, 0x658ca3}}, + {{0x17cb872d, 0x0e8f9977, 0x0633a26b, 0x02cec788, 0x1d37655a, 0x0eb1389d, 0x15183c59, 0x06ef5d44, 0xae5cc1}}}, + /* 15*16^57*G: */ + {{{0x1696756d, 0x1ad64ab9, 0x158505ea, 0x15e2c0ac, 0x1305c676, 0x0cc1ae9f, 0x0aeb3930, 0x0955f23e, 0x31c94b}}, + {{0x1e08ae78, 0x1e22f46e, 0x0b441c2a, 0x1dbf95cd, 0x0160683a, 0x05acd57a, 0x0ea6fd2e, 0x096aadd0, 0xf80f88}}} + }, + { + /* 1*16^58*G: */ + {{{0x1617e073, 0x01b7cda2, 0x0e4c5ecd, 0x197b7a70, 0x1dc866ba, 0x1c4b6be7, 0x1ae243b9, 0x0466a8e3, 0x64587e}}, + {{0x1faf6589, 0x014cf2f4, 0x0ebaacd6, 0x12147226, 0x099a185b, 0x0eb223e1, 0x0b8aba5b, 0x1ab7ed20, 0xd99fcd}}}, + /* 3*16^58*G: */ + {{{0x0e103dd6, 0x0fb8a390, 0x0012166b, 0x0c0980f8, 0x0a17ac34, 0x09e7e2c9, 0x0fe0da88, 0x1aab4840, 0xbc477b}}, + {{0x16f7c343, 0x1c8416c6, 0x0ed12b18, 0x126ae58c, 0x0a6395d2, 0x02a9636f, 0x1549b2eb, 0x0485351b, 0xe31e1e}}}, + /* 5*16^58*G: */ + {{{0x144eab31, 0x086e1d86, 0x198d241f, 0x0b5e2809, 0x1f8e80ac, 0x1a11933d, 0x00e00c0e, 0x1fcb4d77, 0x589db4}}, + {{0x11361f6a, 0x00d75f3a, 0x123e36e5, 0x021caa42, 0x190f31f6, 0x0310125e, 0x0a93d81c, 0x0b821154, 0x625544}}}, + /* 7*16^58*G: */ + {{{0x1a0c2c41, 0x1fcb0b94, 0x00cdb19e, 0x063838ac, 0x0db7c428, 0x0c3056b7, 0x1e8baa28, 0x06fa2dc5, 0x1339b3}}, + {{0x09f1bc2b, 0x02f82a5d, 0x1a48e906, 0x044ff0fb, 0x1a3406b1, 0x1207e889, 0x196e9e8c, 0x0c6c58f5, 0x9f9b29}}}, + /* 9*16^58*G: */ + {{{0x18fc47af, 0x18258fc2, 0x10337b0d, 0x1bfd9065, 0x1b568a8d, 0x194da800, 0x1c5f3140, 0x14226c79, 0x7ff3bb}}, + {{0x19ba43a7, 0x1c30b267, 0x02eb0a5f, 0x0a7635a3, 0x1446b17a, 0x048dba39, 0x18a682f2, 0x15d00314, 0x1f6ba7}}}, + /* 11*16^58*G: */ + {{{0x15213775, 0x0c26b004, 0x150aa640, 0x08527647, 0x07f6100b, 0x149caff6, 0x02ed8507, 0x08c79d6c, 0x8ec670}}, + {{0x1841ffff, 0x100879f3, 0x13d47a43, 0x15314179, 0x1ee0e71d, 0x01a0ae76, 0x0f8c1b99, 0x0df41b4b, 0x8f58a6}}}, + /* 13*16^58*G: */ + {{{0x1452abbb, 0x059ffc69, 0x0e570b8f, 0x154e5fc5, 0x1d495ae6, 0x161ff6ca, 0x06ee276e, 0x16883ce0, 0x83de61}}, + {{0x054eb66e, 0x1028bb58, 0x1390a462, 0x18be6d77, 0x01563d79, 0x1b57c627, 0x027e9afe, 0x0694698c, 0x32f0e3}}}, + /* 15*16^58*G: */ + {{{0x159276f1, 0x0b446137, 0x085ec57a, 0x1c4b6525, 0x0d86833a, 0x1ae007be, 0x076c6a2e, 0x1131ea18, 0x3d7663}}, + {{0x059cbcb3, 0x0f2532dc, 0x0c65e180, 0x03304033, 0x0333ef32, 0x171a6d6c, 0x176d825d, 0x0e6f430f, 0xd37669}}} + }, + { + /* 1*16^59*G: */ + {{{0x1d45e458, 0x0c6b6436, 0x1439ff4d, 0x154d9d44, 0x1de042f0, 0x0369f2a4, 0x0216ce95, 0x1c1c9c9b, 0x8481bd}}, + {{0x1779057e, 0x0b258dac, 0x098b955b, 0x14f38856, 0x0b2ca900, 0x0df9ce76, 0x13761289, 0x11974a80, 0x38ee7b}}}, + /* 3*16^59*G: */ + {{{0x152da17d, 0x10507d20, 0x14191ac5, 0x1827b611, 0x00bc811d, 0x13582ff0, 0x117c2253, 0x03c1ea31, 0x3beaed}}, + {{0x0cc768d2, 0x13824c2f, 0x1fb105b3, 0x1bcef71b, 0x0e1b554c, 0x145f5f40, 0x0b37fbd2, 0x1eab5fef, 0xc3b0d7}}}, + /* 5*16^59*G: */ + {{{0x1a4edcc5, 0x1a7d0bed, 0x00c46dc8, 0x1c644284, 0x15997e74, 0x0fe01c93, 0x15861d4b, 0x14197b93, 0x6e73db}}, + {{0x159da0e4, 0x198fbe6e, 0x019e40de, 0x14efb4e0, 0x08693278, 0x0a844441, 0x1122fa91, 0x1f893dd9, 0xee0ac1}}}, + /* 7*16^59*G: */ + {{{0x0a80b979, 0x04e26212, 0x06aecc50, 0x01ebf465, 0x1c310049, 0x0cbf5523, 0x1649d89f, 0x1126fcb6, 0x7706dd}}, + {{0x0126cfde, 0x067a4082, 0x1fbf8c85, 0x141469fa, 0x09c7117f, 0x0ebcc8f5, 0x1c51dde3, 0x104fab76, 0x8a02a9}}}, + /* 9*16^59*G: */ + {{{0x126fe285, 0x06dce25b, 0x0fbc49f7, 0x17585282, 0x1e06aa45, 0x1e3d20fc, 0x03e034bc, 0x18b25378, 0x16d422}}, + {{0x0155b441, 0x1f07ff36, 0x0d93508c, 0x1e18226e, 0x131b1e93, 0x1f34d31a, 0x1906a2ad, 0x1f4a3c44, 0xdf888}}}, + /* 11*16^59*G: */ + {{{0x1acfa513, 0x11885e55, 0x1838ebab, 0x080f3f34, 0x16a9c4e2, 0x1a23a87a, 0x158ea968, 0x08fdd8ed, 0x1fcc0e}}, + {{0x107afc9c, 0x083add20, 0x060e461d, 0x1bdba2c9, 0x1f9b44be, 0x1c3aa19c, 0x11e2d238, 0x14083c6a, 0x165dc1}}}, + /* 13*16^59*G: */ + {{{0x1edc69b2, 0x0d1383e7, 0x1addc5c8, 0x14c11364, 0x0a386a50, 0x01821ae5, 0x0285cc19, 0x0e75ad97, 0xc12b90}}, + {{0x00dd41dd, 0x19574d81, 0x09eca800, 0x1c3b7c7a, 0x14976b8e, 0x16e44fc4, 0x17c765dc, 0x07fca699, 0x3173c4}}}, + /* 15*16^59*G: */ + {{{0x1961fe4d, 0x1b7ac2ef, 0x041c65ea, 0x0910df16, 0x0a73e8a1, 0x14989896, 0x0a3e8fcf, 0x10bb864f, 0xd059bf}}, + {{0x0ae823c2, 0x0c9ad833, 0x16ad932e, 0x111743e9, 0x155b4fd3, 0x1b2ee424, 0x0978ff81, 0x0c18116a, 0x45107a}}} + }, + { + /* 1*16^60*G: */ + {{{0x0caf666b, 0x06b181fb, 0x0c738c2f, 0x19fda789, 0x1f4637ff, 0x0bcd740b, 0x0aa98ada, 0x0af4f020, 0x13464a}}, + {{0x1f6ecc27, 0x1a4ad483, 0x0250b84f, 0x0601503a, 0x0b0ca48f, 0x019a29e6, 0x1603bdf9, 0x12008c28, 0x69be15}}}, + /* 3*16^60*G: */ + {{{0x0eca5f51, 0x10b5904c, 0x1f26bafc, 0x142e3729, 0x1b5cfdde, 0x0b595f82, 0x1a58b1bb, 0x029bb3dc, 0xdde9d5}}, + {{0x10c638f7, 0x16b4d39e, 0x0255c7e6, 0x1dd7d1bd, 0x18f0950f, 0x19a5853f, 0x04476247, 0x02679c50, 0xb84e69}}}, + /* 5*16^60*G: */ + {{{0x199c88e4, 0x1c83582c, 0x0b51bb0b, 0x1aa27c41, 0x04b179ae, 0x00375890, 0x0dcdba7d, 0x02046d32, 0xfd1a62}}, + {{0x195bc8df, 0x0e4648b2, 0x13003ca6, 0x16e3a92b, 0x17782dc6, 0x0034aa4b, 0x082fec4f, 0x0a973918, 0x1ac97b}}}, + /* 7*16^60*G: */ + {{{0x1f8018ce, 0x0a8adada, 0x024b5a2f, 0x1b4ae71b, 0x06dc7cf2, 0x1e727964, 0x1ae7c4ff, 0x063b1852, 0x4ee485}}, + {{0x1e48381f, 0x01ad30d8, 0x1a01804f, 0x0e92e369, 0x1c5e6710, 0x02046863, 0x02d7f1ed, 0x1a90217f, 0xb68f9e}}}, + /* 9*16^60*G: */ + {{{0x11473678, 0x1429748f, 0x10e4bdc0, 0x00af2a12, 0x0c070cba, 0x18a62adc, 0x036ffd78, 0x13869880, 0xfd76cc}}, + {{0x0d144f4f, 0x10b27754, 0x007210df, 0x051ddc28, 0x12cd6606, 0x10539e81, 0x0f6b83fb, 0x086f0e28, 0xf20465}}}, + /* 11*16^60*G: */ + {{{0x0d7a2193, 0x1805b8a4, 0x05d51bb7, 0x123c89e3, 0x0ea212c6, 0x07413a5a, 0x1008679b, 0x14662476, 0x85a2ab}}, + {{0x10cdcf3a, 0x18641ea5, 0x0ec4909f, 0x0db5bf38, 0x0029fe1c, 0x104168e6, 0x145b60b1, 0x0afd6560, 0x9c1298}}}, + /* 13*16^60*G: */ + {{{0x177568b0, 0x0d8182d9, 0x180ec14d, 0x1d1ba033, 0x1650f35a, 0x16b62bc1, 0x19d1102d, 0x1f8e79ff, 0xd25ddb}}, + {{0x1f39929c, 0x0509c936, 0x0f0fc018, 0x04e7103c, 0x1d92b832, 0x17ba66f3, 0x024e2fab, 0x0eb27f09, 0x7a3aff}}}, + /* 15*16^60*G: */ + {{{0x071d7c13, 0x0ff288c5, 0x03fe8e15, 0x156beff4, 0x0c805641, 0x1f4d4bd5, 0x09c957c1, 0x06287d29, 0x458135}}, + {{0x1aff63cf, 0x11c5e2a9, 0x0f65ae65, 0x000caa85, 0x169c9702, 0x0878bbee, 0x0b62d5fd, 0x1d8292f3, 0x9f5858}}} + }, + { + /* 1*16^61*G: */ + {{{0x0d83f366, 0x16d1d069, 0x1ca16232, 0x1399dbc5, 0x1c97a0cd, 0x0185e60e, 0x18ba6bbd, 0x1eb6e27f, 0xbc4a9d}}, + {{0x181f33c1, 0x1ac6b332, 0x151ec5b5, 0x1153f7f4, 0x198caa6e, 0x1bd6fa5b, 0x1018e0e4, 0x194dcf0b, 0xd3a81}}}, + /* 3*16^61*G: */ + {{{0x1712be3c, 0x03517197, 0x051a99ac, 0x0be31db4, 0x01534729, 0x12edd580, 0x0de34f1c, 0x13b26636, 0x39d734}}, + {{0x1c6ff65c, 0x0180cfa0, 0x09059133, 0x1def4bd9, 0x012e8ef5, 0x1aaa6334, 0x0fdfec49, 0x09eadde7, 0x8f929b}}}, + /* 5*16^61*G: */ + {{{0x1f77f22b, 0x15727de6, 0x1e0b80d6, 0x12e47329, 0x1af26c69, 0x06b614ca, 0x17427947, 0x02fefb83, 0xf0cba6}}, + {{0x1909a03c, 0x1e47163a, 0x08255987, 0x0318fd21, 0x0d04004f, 0x1361b28b, 0x1e627bcc, 0x08627f3b, 0x1a25ab}}}, + /* 7*16^61*G: */ + {{{0x06509c12, 0x0566e6a6, 0x147d1d96, 0x17fc46b7, 0x068ed8d6, 0x18746c39, 0x134c8745, 0x173b642a, 0x381d7a}}, + {{0x0eb46102, 0x03215471, 0x19babdd5, 0x1050b0d9, 0x181e7205, 0x122b9d32, 0x16a7ad74, 0x0b6ffb47, 0xa47aab}}}, + /* 9*16^61*G: */ + {{{0x184fe955, 0x0d9da6b1, 0x18ef3923, 0x12ad4b40, 0x08a53f69, 0x1b9f34a6, 0x1f991e4e, 0x0a8cf570, 0xa703f0}}, + {{0x161344bd, 0x135977de, 0x05c9dfe8, 0x1c2c538b, 0x1199b539, 0x1c96b58c, 0x1eb703a9, 0x06b45608, 0xd500f9}}}, + /* 11*16^61*G: */ + {{{0x06eace58, 0x04cb2b60, 0x1cc8474d, 0x1c8ac745, 0x1a03f1b7, 0x1686b829, 0x035d1b1a, 0x18b55aaa, 0x73c6b3}}, + {{0x0af8654e, 0x0b8548b9, 0x0e34a45c, 0x150f1b77, 0x191c6aa6, 0x04c89b36, 0x1dd06ea4, 0x148e6749, 0x3a2fb4}}}, + /* 13*16^61*G: */ + {{{0x00181d5e, 0x059c45f8, 0x16abc815, 0x03675372, 0x0ddb8de7, 0x069d0e07, 0x1ff68740, 0x1cea0da8, 0xc627f3}}, + {{0x169f886d, 0x13c80531, 0x12f8b0e4, 0x0d600a93, 0x1f7d68ef, 0x0b009c50, 0x098ecb5a, 0x1ae3c885, 0xd78f9d}}}, + /* 15*16^61*G: */ + {{{0x17828b16, 0x07146cfd, 0x09211fcd, 0x0f2b1e51, 0x0de53a04, 0x1a053783, 0x08cfe897, 0x17c1dbfa, 0xbb88fa}}, + {{0x0aea5df7, 0x08a39d10, 0x087a5a71, 0x1502e9c7, 0x19163ec4, 0x02cb008a, 0x0374d177, 0x16609ebd, 0xb73676}}} + }, + { + /* 1*16^62*G: */ + {{{0x05324caa, 0x0a55987f, 0x051ca8e5, 0x16cbc615, 0x0a32e694, 0x063a4a29, 0x0f0348f6, 0x0f7f0531, 0x8c28a9}}, + {{0x0bef9482, 0x138ee39e, 0x072e5167, 0x0f09e08a, 0x0c0eb7ae, 0x16f98fbe, 0x064cde3f, 0x0c74660a, 0x40a304}}}, + /* 3*16^62*G: */ + {{{0x0754dd40, 0x11f438aa, 0x0d19b3e1, 0x044c63f8, 0x0f6e9a24, 0x020fe6b9, 0x1f3d16d2, 0x0e06581b, 0x972924}}, + {{0x0aa36143, 0x025a4979, 0x0b2bd24e, 0x15d0a4ab, 0x0df3690d, 0x03aee5ea, 0x08773457, 0x0884cbfd, 0x91d1a2}}}, + /* 5*16^62*G: */ + {{{0x0c2ca7ff, 0x016c175c, 0x17c0868f, 0x06c8bb2b, 0x127af180, 0x08d6ad17, 0x05b8141e, 0x12eb014f, 0x89637f}}, + {{0x10493e68, 0x16a0af0b, 0x10baadef, 0x178d471c, 0x17489f87, 0x0e78aa1a, 0x109355ee, 0x04919110, 0x2d1fe1}}}, + /* 7*16^62*G: */ + {{{0x0ca8dd7f, 0x0c36b1d0, 0x084e0698, 0x0e5006ad, 0x157421bc, 0x0ed01ea9, 0x1824bf72, 0x1ce37c4b, 0x308138}}, + {{0x0a92c7f2, 0x00af923c, 0x12b64579, 0x0cac8c86, 0x08e18c81, 0x1622e8a0, 0x124978e7, 0x1a51051f, 0x28d1e2}}}, + /* 9*16^62*G: */ + {{{0x066a3fb1, 0x06e62b44, 0x04b881b0, 0x00125033, 0x0862f6ec, 0x1a8642db, 0x0d974795, 0x1d054dbd, 0x575fc4}}, + {{0x102655ad, 0x0dc74854, 0x08ebcbc2, 0x076ae78d, 0x087daed3, 0x0de14bc7, 0x128b59c7, 0x120854df, 0x6f6edb}}}, + /* 11*16^62*G: */ + {{{0x190117df, 0x0db54523, 0x08040a48, 0x0a77779c, 0x02a8fda2, 0x137c0f75, 0x0de889fc, 0x06d6c9d5, 0xa5ec90}}, + {{0x186462fe, 0x0094099f, 0x0528d8f6, 0x08c3e0ac, 0x1a0f71c8, 0x1cc1d68f, 0x01003165, 0x0c4bd828, 0xb79dc6}}}, + /* 13*16^62*G: */ + {{{0x172ad712, 0x187cbae0, 0x0411ca42, 0x131961bc, 0x149b95a3, 0x1cbb0a31, 0x0c252779, 0x1d226621, 0xa153df}}, + {{0x0d48fdd2, 0x052fb29c, 0x1c2cca72, 0x0a7e50b5, 0x1b89abd0, 0x0c6af8f8, 0x0cbf120c, 0x0827f60b, 0xfd94d8}}}, + /* 15*16^62*G: */ + {{{0x11cf5b3a, 0x1861b808, 0x0d69e040, 0x0675077b, 0x1c824c44, 0x1a684476, 0x18564d70, 0x18d5ef28, 0x9a541a}}, + {{0x16a44ae4, 0x1faabaf9, 0x07a94b58, 0x11fdc4a4, 0x1475f554, 0x0d79b447, 0x0adf2bf8, 0x1839620d, 0xb66148}}} + }, + { + /* 1*16^63*G: */ + {{{0x1faccae0, 0x0625d088, 0x12eccdd2, 0x166a18b4, 0x11fd23c8, 0x0a672783, 0x1ea30776, 0x0cc272a4, 0x8ea96}}, + {{0x0e62b945, 0x0d79a518, 0x1c3e3a55, 0x0f077d39, 0x1c5d735b, 0x1e067dca, 0x1e0b8939, 0x17791dc4, 0x620efa}}}, + /* 3*16^63*G: */ + {{{0x06a06f5e, 0x0d205acb, 0x0820dc08, 0x0324a2dd, 0x1b716a34, 0x06a10931, 0x14eb0dec, 0x1f7d4284, 0x383b24}}, + {{0x13c6e772, 0x04fa3c36, 0x030ac102, 0x0d5ce977, 0x05a19e8f, 0x0b36aa75, 0x0881133d, 0x0d589db7, 0x54cf70}}}, + /* 5*16^63*G: */ + {{{0x0638a136, 0x1cbae0ea, 0x00e06571, 0x1a39c66d, 0x1790c2b0, 0x0ce35b06, 0x15b5e279, 0x1a07c05d, 0xe68432}}, + {{0x0c6c2584, 0x17e8c084, 0x0d5ccdaa, 0x13c7d7b6, 0x1e6472e0, 0x13981d00, 0x0998934a, 0x02731c6b, 0xca5be4}}}, + /* 7*16^63*G: */ + {{{0x16e8c10c, 0x0743e220, 0x06f5a01f, 0x0530be72, 0x06e7fb47, 0x1ef67398, 0x10a83bbe, 0x0b3c5fcb, 0x395dd5}}, + {{0x05fe638e, 0x01eb6c98, 0x19a48b2f, 0x013809b8, 0x04e274c9, 0x1f43d7fd, 0x0b174104, 0x1a968b25, 0xfd62dc}}}, + /* 9*16^63*G: */ + {{{0x0d6c14ef, 0x0b326d90, 0x1e4f23d4, 0x11f8ea0d, 0x06480085, 0x16adf771, 0x172e7dbb, 0x1b86aa4b, 0x7a514a}}, + {{0x0b3fbd13, 0x169f0c38, 0x09b893de, 0x1d5dee55, 0x15c3729b, 0x185ca647, 0x1363a25f, 0x1fda2a5c, 0x56edd1}}}, + /* 11*16^63*G: */ + {{{0x0f6d65eb, 0x14a31e82, 0x10085492, 0x1220eea8, 0x08f235a9, 0x179dfa48, 0x04363aa3, 0x0b0864bb, 0x1ee1fd}}, + {{0x1d22941c, 0x101b5c92, 0x0d60ac47, 0x05e9d30c, 0x0fcfaca3, 0x1cfe27a1, 0x1cf073ea, 0x1237bad8, 0xbb6928}}}, + /* 13*16^63*G: */ + {{{0x0e36cb44, 0x18ca8778, 0x12880647, 0x1d16d91b, 0x1357d617, 0x07e86c41, 0x0aa2f016, 0x069a71f5, 0x155156}}, + {{0x1f495a68, 0x14d7a328, 0x0830794c, 0x06e2ebe0, 0x1205da8c, 0x11f85a31, 0x08578dc3, 0x18eaaaea, 0xab4fff}}}, + /* 15*16^63*G: */ + {{{0x1427bacc, 0x0fca3083, 0x0b58b854, 0x0662c9ba, 0x1c4aa9e7, 0x07584ac6, 0x0804d8d6, 0x0c88d7ea, 0x3bc6bc}}, + {{0x0ad6fda6, 0x0619feaf, 0x02fad1c5, 0x16eb4a45, 0x0c02bd71, 0x1771136b, 0x0c1736d8, 0x180e2ed8, 0x8e305c}}} + }, diff --git a/applications/external/flipbip/lib/crypto/segwit_addr.c b/applications/external/flipbip/lib/crypto/segwit_addr.c new file mode 100644 index 000000000..1dbc15118 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/segwit_addr.c @@ -0,0 +1,225 @@ +/* Copyright (c) 2017, 2021 Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include + +#include "segwit_addr.h" + +static uint32_t bech32_polymod_step(uint32_t pre) { + uint8_t b = pre >> 25; + return ((pre & 0x1FFFFFF) << 5) ^ (-((b >> 0) & 1) & 0x3b6a57b2UL) ^ + (-((b >> 1) & 1) & 0x26508e6dUL) ^ (-((b >> 2) & 1) & 0x1ea119faUL) ^ + (-((b >> 3) & 1) & 0x3d4233ddUL) ^ (-((b >> 4) & 1) & 0x2a1462b3UL); +} + +static uint32_t bech32_final_constant(bech32_encoding enc) { + if(enc == BECH32_ENCODING_BECH32) return 1; + if(enc == BECH32_ENCODING_BECH32M) return 0x2bc830a3; + return 0; +} + +static const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +static const int8_t charset_rev[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, + -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, + 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, + 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}; + +int bech32_encode( + char* output, + const char* hrp, + const uint8_t* data, + size_t data_len, + bech32_encoding enc) { + uint32_t chk = 1; + size_t i = 0; + while(hrp[i] != 0) { + int ch = hrp[i]; + if(ch < 33 || ch > 126) { + return 0; + } + + if(ch >= 'A' && ch <= 'Z') return 0; + chk = bech32_polymod_step(chk) ^ (ch >> 5); + ++i; + } + if(i + 7 + data_len > 90) return 0; + chk = bech32_polymod_step(chk); + while(*hrp != 0) { + chk = bech32_polymod_step(chk) ^ (*hrp & 0x1f); + *(output++) = *(hrp++); + } + *(output++) = '1'; + for(i = 0; i < data_len; ++i) { + if(*data >> 5) return 0; + chk = bech32_polymod_step(chk) ^ (*data); + *(output++) = charset[*(data++)]; + } + for(i = 0; i < 6; ++i) { + chk = bech32_polymod_step(chk); + } + chk ^= bech32_final_constant(enc); + for(i = 0; i < 6; ++i) { + *(output++) = charset[(chk >> ((5 - i) * 5)) & 0x1f]; + } + *output = 0; + return 1; +} + +bech32_encoding bech32_decode(char* hrp, uint8_t* data, size_t* data_len, const char* input) { + uint32_t chk = 1; + size_t i = 0; + size_t input_len = strlen(input); + size_t hrp_len = 0; + int have_lower = 0, have_upper = 0; + if(input_len < 8) { + return BECH32_ENCODING_NONE; + } + *data_len = 0; + while(*data_len < input_len && input[(input_len - 1) - *data_len] != '1') { + ++(*data_len); + } + hrp_len = input_len - (1 + *data_len); + if(1 + *data_len >= input_len || *data_len < 6 || hrp_len > BECH32_MAX_HRP_LEN) { + return BECH32_ENCODING_NONE; + } + *(data_len) -= 6; + for(i = 0; i < hrp_len; ++i) { + int ch = input[i]; + if(ch < 33 || ch > 126) { + return BECH32_ENCODING_NONE; + } + if(ch >= 'a' && ch <= 'z') { + have_lower = 1; + } else if(ch >= 'A' && ch <= 'Z') { + have_upper = 1; + ch = (ch - 'A') + 'a'; + } + hrp[i] = ch; + chk = bech32_polymod_step(chk) ^ (ch >> 5); + } + hrp[i] = 0; + chk = bech32_polymod_step(chk); + for(i = 0; i < hrp_len; ++i) { + chk = bech32_polymod_step(chk) ^ (input[i] & 0x1f); + } + ++i; + while(i < input_len) { + int v = (input[i] & 0x80) ? -1 : charset_rev[(int)input[i]]; + if(input[i] >= 'a' && input[i] <= 'z') have_lower = 1; + if(input[i] >= 'A' && input[i] <= 'Z') have_upper = 1; + if(v == -1) { + return BECH32_ENCODING_NONE; + } + chk = bech32_polymod_step(chk) ^ v; + if(i + 6 < input_len) { + data[i - (1 + hrp_len)] = v; + } + ++i; + } + if(have_lower && have_upper) { + return BECH32_ENCODING_NONE; + } + if(chk == bech32_final_constant(BECH32_ENCODING_BECH32)) { + return BECH32_ENCODING_BECH32; + } else if(chk == bech32_final_constant(BECH32_ENCODING_BECH32M)) { + return BECH32_ENCODING_BECH32M; + } else { + return BECH32_ENCODING_NONE; + } +} + +static int convert_bits( + uint8_t* out, + size_t* outlen, + int outbits, + const uint8_t* in, + size_t inlen, + int inbits, + int pad) { + uint32_t val = 0; + int bits = 0; + uint32_t maxv = (((uint32_t)1) << outbits) - 1; + while(inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while(bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; + } + } + if(pad) { + if(bits) { + out[(*outlen)++] = (val << (outbits - bits)) & maxv; + } + } else if(((val << (outbits - bits)) & maxv) || bits >= inbits) { + return 0; + } + return 1; +} + +int segwit_addr_encode( + char* output, + const char* hrp, + int witver, + const uint8_t* witprog, + size_t witprog_len) { + uint8_t data[65] = {0}; + size_t datalen = 0; + bech32_encoding enc = BECH32_ENCODING_BECH32; + if(witver > 16) return 0; + if(witver == 0 && witprog_len != 20 && witprog_len != 32) return 0; + if(witprog_len < 2 || witprog_len > 40) return 0; + if(witver > 0) enc = BECH32_ENCODING_BECH32M; + data[0] = witver; + convert_bits(data + 1, &datalen, 5, witprog, witprog_len, 8, 1); + ++datalen; + return bech32_encode(output, hrp, data, datalen, enc); +} + +int segwit_addr_decode( + int* witver, + uint8_t* witdata, + size_t* witdata_len, + const char* hrp, + const char* addr) { + uint8_t data[84] = {0}; + char hrp_actual[84] = {0}; + size_t data_len = 0; + if(strlen(addr) > 90) return 0; + bech32_encoding enc = bech32_decode(hrp_actual, data, &data_len, addr); + if(enc == BECH32_ENCODING_NONE) return 0; + if(data_len == 0 || data_len > 65) return 0; + if(strncmp(hrp, hrp_actual, 84) != 0) return 0; + if(data[0] > 16) return 0; + if(data[0] == 0 && enc != BECH32_ENCODING_BECH32) return 0; + if(data[0] > 0 && enc != BECH32_ENCODING_BECH32M) return 0; + *witdata_len = 0; + if(!convert_bits(witdata, witdata_len, 8, data + 1, data_len - 1, 5, 0)) return 0; + if(*witdata_len < 2 || *witdata_len > 40) return 0; + if(data[0] == 0 && *witdata_len != 20 && *witdata_len != 32) return 0; + *witver = data[0]; + return 1; +} diff --git a/applications/external/flipbip/lib/crypto/segwit_addr.h b/applications/external/flipbip/lib/crypto/segwit_addr.h new file mode 100644 index 000000000..2251e5c47 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/segwit_addr.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2017, 2021 Pieter Wuille + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef _SEGWIT_ADDR_H_ +#define _SEGWIT_ADDR_H_ 1 + +#include + +// The maximum length of the Bech32 human-readable part according to BIP-173. +#define BECH32_MAX_HRP_LEN 83 + +/** Encode a SegWit address + * + * Out: output: Pointer to a buffer of size 73 + strlen(hrp) that will be + * updated to contain the null-terminated address. + * In: hrp: Pointer to the null-terminated human readable part to use + * (chain/network specific). + * ver: Version of the witness program (between 0 and 16 inclusive). + * prog: Data bytes for the witness program (between 2 and 40 bytes). + * prog_len: Number of data bytes in prog. + * Returns 1 if successful. + */ +int segwit_addr_encode(char* output, const char* hrp, int ver, const uint8_t* prog, size_t prog_len); + +/** Decode a SegWit address + * + * Out: ver: Pointer to an int that will be updated to contain the witness + * program version (between 0 and 16 inclusive). + * prog: Pointer to a buffer of size 40 that will be updated to + * contain the witness program bytes. + * prog_len: Pointer to a size_t that will be updated to contain the length + * of bytes in prog. + * hrp: Pointer to the null-terminated human readable part that is + * expected (chain/network specific). + * addr: Pointer to the null-terminated address. + * Returns 1 if successful. + */ +int segwit_addr_decode(int* ver, uint8_t* prog, size_t* prog_len, const char* hrp, const char* addr); + +/** Supported encodings. */ +typedef enum { + BECH32_ENCODING_NONE, + BECH32_ENCODING_BECH32, + BECH32_ENCODING_BECH32M +} bech32_encoding; + +/** Encode a Bech32 or Bech32m string + * + * Out: output: Pointer to a buffer of size strlen(hrp) + data_len + 8 that + * will be updated to contain the null-terminated Bech32 string. + * In: hrp : Pointer to the null-terminated human readable part. + * data : Pointer to an array of 5-bit values. + * data_len: Length of the data array. + * enc: Which encoding to use (BECH32_ENCODING_BECH32{,M}). + * Returns 1 if successful. + */ +int bech32_encode( + char* output, + const char* hrp, + const uint8_t* data, + size_t data_len, + bech32_encoding enc); + +/** Decode a Bech32 or Bech32m string + * + * Out: hrp: Pointer to a buffer of size BECH32_MAX_HRP_LEN + 1. Will be + * updated to contain the null-terminated human readable part. + * data: Pointer to a buffer of size strlen(input) - 8 that will + * hold the encoded 5-bit data values. + * data_len: Pointer to a size_t that will be updated to be the number + * of entries in data. + * In: input: Pointer to a null-terminated Bech32 string. + * Returns BECH32_ENCODING_BECH32{,M} to indicate decoding was successful + * with the specified encoding standard. BECH32_ENCODING_NONE is returned if + * decoding failed. + */ +bech32_encoding bech32_decode(char* hrp, uint8_t* data, size_t* data_len, const char* input); + +#endif diff --git a/applications/external/flipbip/lib/crypto/setup.py b/applications/external/flipbip/lib/crypto/setup.py new file mode 100644 index 000000000..aee3dd4a9 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/setup.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +from distutils.core import setup +from distutils.extension import Extension + +from Cython.Build import cythonize +from Cython.Distutils import build_ext + +srcs = [ + "nist256p1", + "base58", + "bignum", + "bip32", + "ecdsa", + "curve25519", + "hmac", + "rand", + "ripemd160", + "secp256k1", + "sha2", +] + +extensions = [ + Extension( + "TrezorCrypto", + sources=["TrezorCrypto.pyx", "c.pxd"] + [x + ".c" for x in srcs], + extra_compile_args=[], + ) +] + +setup( + name="TrezorCrypto", + version="0.0.0", + description="Cython wrapper around trezor-crypto library", + author="Pavol Rusnak", + author_email="stick@satoshilabs.com", + url="https://github.com/trezor/trezor-crypto", + cmdclass={"build_ext": build_ext}, + ext_modules=cythonize(extensions), +) diff --git a/applications/external/flipbip/lib/crypto/sha2.c b/applications/external/flipbip/lib/crypto/sha2.c new file mode 100644 index 000000000..85e589c4d --- /dev/null +++ b/applications/external/flipbip/lib/crypto/sha2.c @@ -0,0 +1,1252 @@ +/** + * Copyright (c) 2000-2001 Aaron D. Gifford + * Copyright (c) 2013-2014 Pavol Rusnak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "sha2.h" +#include "memzero.h" +#include "byte_order.h" + +/* + * ASSERT NOTE: + * Some sanity checking code is included using assert(). On my FreeBSD + * system, this additional code can be removed by compiling with NDEBUG + * defined. Check your own systems manpage on assert() to see how to + * compile WITHOUT the sanity checking code on your system. + * + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * #define SHA2_UNROLL_TRANSFORM + * + */ + +/*** SHA-256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivilent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * #define LITTLE_ENDIAN 1234 + * #define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * #define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * #define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including (which in turn includes + * where the appropriate definitions are actually + * made). + */ + +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif + +typedef uint8_t sha2_byte; /* Exactly 1 byte */ +typedef uint32_t sha2_word32; /* Exactly 4 bytes */ +typedef uint64_t sha2_word64; /* Exactly 8 bytes */ + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define SHA1_SHORT_BLOCK_LENGTH (SHA1_BLOCK_LENGTH - 8) +#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) +#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w, n) \ + { \ + (w)[0] += (sha2_word64)(n); \ + if((w)[0] < (n)) { \ + (w)[1]++; \ + } \ + } + +#define MEMCPY_BCOPY(d, s, l) memcpy((d), (s), (l)) + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: In the original SHA-256/384/512 document, the shift-right + * function was named R and the rotate-right function was called S. + * (See: http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf on the + * web.) + * + * The newer NIST FIPS 180-2 document uses a much clearer naming + * scheme, SHR for shift-right, ROTR for rotate-right, and ROTL for + * rotate-left. (See: + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf + * on the web.) + * + * WARNING: These macros must be used cautiously, since they reference + * supplied parameters sometimes more than once, and thus could have + * unexpected side-effects if used without taking this into account. + */ + +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define SHR(b, x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define ROTR32(b, x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define ROTR64(b, x) (((x) >> (b)) | ((x) << (64 - (b)))) +/* 32-bit Rotate-left (used in SHA-1): */ +#define ROTL32(b, x) (((x) << (b)) | ((x) >> (32 - (b)))) + +/* Two of six logical functions used in SHA-1, SHA-256, SHA-384, and SHA-512: */ +#define Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Function used in SHA-1: */ +#define Parity(x, y, z) ((x) ^ (y) ^ (z)) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (ROTR32(2, (x)) ^ ROTR32(13, (x)) ^ ROTR32(22, (x))) +#define Sigma1_256(x) (ROTR32(6, (x)) ^ ROTR32(11, (x)) ^ ROTR32(25, (x))) +#define sigma0_256(x) (ROTR32(7, (x)) ^ ROTR32(18, (x)) ^ SHR(3, (x))) +#define sigma1_256(x) (ROTR32(17, (x)) ^ ROTR32(19, (x)) ^ SHR(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (ROTR64(28, (x)) ^ ROTR64(34, (x)) ^ ROTR64(39, (x))) +#define Sigma1_512(x) (ROTR64(14, (x)) ^ ROTR64(18, (x)) ^ ROTR64(41, (x))) +#define sigma0_512(x) (ROTR64(1, (x)) ^ ROTR64(8, (x)) ^ SHR(7, (x))) +#define sigma1_512(x) (ROTR64(19, (x)) ^ ROTR64(61, (x)) ^ SHR(6, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +static void sha512_Last(SHA512_CTX*); + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ + +/* Hash constant words K for SHA-1: */ +#define K1_0_TO_19 0x5a827999UL +#define K1_20_TO_39 0x6ed9eba1UL +#define K1_40_TO_59 0x8f1bbcdcUL +#define K1_60_TO_79 0xca62c1d6UL + +/* Initial hash value H for SHA-1: */ +const sha2_word32 sha1_initial_hash_value[SHA1_DIGEST_LENGTH / sizeof(sha2_word32)] = + {0x67452301UL, 0xefcdab89UL, 0x98badcfeUL, 0x10325476UL, 0xc3d2e1f0UL}; + +/* Hash constant words K for SHA-256: */ +static const sha2_word32 K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, + 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, + 0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 0xa2bfe8a1UL, 0xa81a664bUL, + 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, + 0x5b9cca4fUL, 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL}; + +/* Initial hash value H for SHA-256: */ +const sha2_word32 sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL}; + +/* Hash constant words K for SHA-384 and SHA-512: */ +static const sha2_word64 K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + +/* Initial hash value H for SHA-512 */ +const sha2_word64 sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL}; + +/* + * Constant used by SHA256/384/512_End() functions for converting the + * digest to a readable hexadecimal character string: + */ +static const char* sha2_hex_digits = "0123456789abcdef"; + +/*** SHA-1: ***********************************************************/ +void sha1_Init(SHA1_CTX* context) { + MEMCPY_BCOPY(context->state, sha1_initial_hash_value, SHA1_DIGEST_LENGTH); + memzero(context->buffer, SHA1_BLOCK_LENGTH); + context->bitcount = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-1 round macros: */ + +#define ROUND1_0_TO_15(a, b, c, d, e) \ + (e) = ROTL32(5, (a)) + Ch((b), (c), (d)) + (e) + K1_0_TO_19 + (W1[j] = *data++); \ + (b) = ROTL32(30, (b)); \ + j++; + +#define ROUND1_16_TO_19(a, b, c, d, e) \ + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; \ + (e) = ROTL32(5, a) + Ch(b, c, d) + e + K1_0_TO_19 + (W1[j & 0x0f] = ROTL32(1, T1)); \ + (b) = ROTL32(30, b); \ + j++; + +#define ROUND1_20_TO_39(a, b, c, d, e) \ + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; \ + (e) = ROTL32(5, a) + Parity(b, c, d) + e + K1_20_TO_39 + (W1[j & 0x0f] = ROTL32(1, T1)); \ + (b) = ROTL32(30, b); \ + j++; + +#define ROUND1_40_TO_59(a, b, c, d, e) \ + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; \ + (e) = ROTL32(5, a) + Maj(b, c, d) + e + K1_40_TO_59 + (W1[j & 0x0f] = ROTL32(1, T1)); \ + (b) = ROTL32(30, b); \ + j++; + +#define ROUND1_60_TO_79(a, b, c, d, e) \ + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; \ + (e) = ROTL32(5, a) + Parity(b, c, d) + e + K1_60_TO_79 + (W1[j & 0x0f] = ROTL32(1, T1)); \ + (b) = ROTL32(30, b); \ + j++; + +void sha1_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0; + sha2_word32 T1 = 0; + sha2_word32 W1[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + + j = 0; + + /* Rounds 0 to 15 unrolled: */ + ROUND1_0_TO_15(a, b, c, d, e); + ROUND1_0_TO_15(e, a, b, c, d); + ROUND1_0_TO_15(d, e, a, b, c); + ROUND1_0_TO_15(c, d, e, a, b); + ROUND1_0_TO_15(b, c, d, e, a); + ROUND1_0_TO_15(a, b, c, d, e); + ROUND1_0_TO_15(e, a, b, c, d); + ROUND1_0_TO_15(d, e, a, b, c); + ROUND1_0_TO_15(c, d, e, a, b); + ROUND1_0_TO_15(b, c, d, e, a); + ROUND1_0_TO_15(a, b, c, d, e); + ROUND1_0_TO_15(e, a, b, c, d); + ROUND1_0_TO_15(d, e, a, b, c); + ROUND1_0_TO_15(c, d, e, a, b); + ROUND1_0_TO_15(b, c, d, e, a); + ROUND1_0_TO_15(a, b, c, d, e); + + /* Rounds 16 to 19 unrolled: */ + ROUND1_16_TO_19(e, a, b, c, d); + ROUND1_16_TO_19(d, e, a, b, c); + ROUND1_16_TO_19(c, d, e, a, b); + ROUND1_16_TO_19(b, c, d, e, a); + + /* Rounds 20 to 39 unrolled: */ + ROUND1_20_TO_39(a, b, c, d, e); + ROUND1_20_TO_39(e, a, b, c, d); + ROUND1_20_TO_39(d, e, a, b, c); + ROUND1_20_TO_39(c, d, e, a, b); + ROUND1_20_TO_39(b, c, d, e, a); + ROUND1_20_TO_39(a, b, c, d, e); + ROUND1_20_TO_39(e, a, b, c, d); + ROUND1_20_TO_39(d, e, a, b, c); + ROUND1_20_TO_39(c, d, e, a, b); + ROUND1_20_TO_39(b, c, d, e, a); + ROUND1_20_TO_39(a, b, c, d, e); + ROUND1_20_TO_39(e, a, b, c, d); + ROUND1_20_TO_39(d, e, a, b, c); + ROUND1_20_TO_39(c, d, e, a, b); + ROUND1_20_TO_39(b, c, d, e, a); + ROUND1_20_TO_39(a, b, c, d, e); + ROUND1_20_TO_39(e, a, b, c, d); + ROUND1_20_TO_39(d, e, a, b, c); + ROUND1_20_TO_39(c, d, e, a, b); + ROUND1_20_TO_39(b, c, d, e, a); + + /* Rounds 40 to 59 unrolled: */ + ROUND1_40_TO_59(a, b, c, d, e); + ROUND1_40_TO_59(e, a, b, c, d); + ROUND1_40_TO_59(d, e, a, b, c); + ROUND1_40_TO_59(c, d, e, a, b); + ROUND1_40_TO_59(b, c, d, e, a); + ROUND1_40_TO_59(a, b, c, d, e); + ROUND1_40_TO_59(e, a, b, c, d); + ROUND1_40_TO_59(d, e, a, b, c); + ROUND1_40_TO_59(c, d, e, a, b); + ROUND1_40_TO_59(b, c, d, e, a); + ROUND1_40_TO_59(a, b, c, d, e); + ROUND1_40_TO_59(e, a, b, c, d); + ROUND1_40_TO_59(d, e, a, b, c); + ROUND1_40_TO_59(c, d, e, a, b); + ROUND1_40_TO_59(b, c, d, e, a); + ROUND1_40_TO_59(a, b, c, d, e); + ROUND1_40_TO_59(e, a, b, c, d); + ROUND1_40_TO_59(d, e, a, b, c); + ROUND1_40_TO_59(c, d, e, a, b); + ROUND1_40_TO_59(b, c, d, e, a); + + /* Rounds 60 to 79 unrolled: */ + ROUND1_60_TO_79(a, b, c, d, e); + ROUND1_60_TO_79(e, a, b, c, d); + ROUND1_60_TO_79(d, e, a, b, c); + ROUND1_60_TO_79(c, d, e, a, b); + ROUND1_60_TO_79(b, c, d, e, a); + ROUND1_60_TO_79(a, b, c, d, e); + ROUND1_60_TO_79(e, a, b, c, d); + ROUND1_60_TO_79(d, e, a, b, c); + ROUND1_60_TO_79(c, d, e, a, b); + ROUND1_60_TO_79(b, c, d, e, a); + ROUND1_60_TO_79(a, b, c, d, e); + ROUND1_60_TO_79(e, a, b, c, d); + ROUND1_60_TO_79(d, e, a, b, c); + ROUND1_60_TO_79(c, d, e, a, b); + ROUND1_60_TO_79(b, c, d, e, a); + ROUND1_60_TO_79(a, b, c, d, e); + ROUND1_60_TO_79(e, a, b, c, d); + ROUND1_60_TO_79(d, e, a, b, c); + ROUND1_60_TO_79(c, d, e, a, b); + ROUND1_60_TO_79(b, c, d, e, a); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + + /* Clean up */ + a = b = c = d = e = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void sha1_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0; + sha2_word32 T1 = 0; + sha2_word32 W1[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + j = 0; + do { + T1 = ROTL32(5, a) + Ch(b, c, d) + e + K1_0_TO_19 + (W1[j] = *data++); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 16); + + do { + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; + T1 = ROTL32(5, a) + Ch(b, c, d) + e + K1_0_TO_19 + (W1[j & 0x0f] = ROTL32(1, T1)); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 20); + + do { + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; + T1 = ROTL32(5, a) + Parity(b, c, d) + e + K1_20_TO_39 + (W1[j & 0x0f] = ROTL32(1, T1)); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 40); + + do { + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; + T1 = ROTL32(5, a) + Maj(b, c, d) + e + K1_40_TO_59 + (W1[j & 0x0f] = ROTL32(1, T1)); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 60); + + do { + T1 = W1[(j + 13) & 0x0f] ^ W1[(j + 8) & 0x0f] ^ W1[(j + 2) & 0x0f] ^ W1[j & 0x0f]; + T1 = ROTL32(5, a) + Parity(b, c, d) + e + K1_60_TO_79 + (W1[j & 0x0f] = ROTL32(1, T1)); + e = d; + d = c; + c = ROTL32(30, b); + b = a; + a = T1; + j++; + } while(j < 80); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + + /* Clean up */ + a = b = c = d = e = T1 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void sha1_Update(SHA1_CTX* context, const sha2_byte* data, size_t len) { + unsigned int freespace = 0, usedspace = 0; + + if(len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + usedspace = (context->bitcount >> 3) % SHA1_BLOCK_LENGTH; + if(usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA1_BLOCK_LENGTH - usedspace; + + if(len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + sha1_Transform(context->state, context->buffer, context->state); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while(len >= SHA1_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + MEMCPY_BCOPY(context->buffer, data, SHA1_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + sha1_Transform(context->state, context->buffer, context->state); + context->bitcount += SHA1_BLOCK_LENGTH << 3; + len -= SHA1_BLOCK_LENGTH; + data += SHA1_BLOCK_LENGTH; + } + if(len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void sha1_Final(SHA1_CTX* context, sha2_byte digest[SHA1_DIGEST_LENGTH]) { + unsigned int usedspace = 0; + + /* If no digest buffer is passed, we don't bother doing this: */ + if(digest != (sha2_byte*)0) { + usedspace = (context->bitcount >> 3) % SHA1_BLOCK_LENGTH; + /* Begin padding with a 1 bit: */ + ((uint8_t*)context->buffer)[usedspace++] = 0x80; + + if(usedspace > SHA1_SHORT_BLOCK_LENGTH) { + memzero(((uint8_t*)context->buffer) + usedspace, SHA1_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + /* Do second-to-last transform: */ + sha1_Transform(context->state, context->buffer, context->state); + + /* And prepare the last transform: */ + usedspace = 0; + } + /* Set-up for the last transform: */ + memzero(((uint8_t*)context->buffer) + usedspace, SHA1_SHORT_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 14; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + /* Set the bit count: */ + context->buffer[14] = context->bitcount >> 32; + context->buffer[15] = context->bitcount & 0xffffffff; + + /* Final transform: */ + sha1_Transform(context->state, context->buffer, context->state); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + for(int j = 0; j < 5; j++) { + REVERSE32(context->state[j], context->state[j]); + } +#endif + MEMCPY_BCOPY(digest, context->state, SHA1_DIGEST_LENGTH); + } + + /* Clean up state data: */ + memzero(context, sizeof(SHA1_CTX)); + usedspace = 0; +} + +char* sha1_End(SHA1_CTX* context, char buffer[SHA1_DIGEST_STRING_LENGTH]) { + sha2_byte digest[SHA1_DIGEST_LENGTH] = {0}, *d = digest; + int i = 0; + + if(buffer != (char*)0) { + sha1_Final(context, digest); + + for(i = 0; i < SHA1_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + memzero(context, sizeof(SHA1_CTX)); + } + memzero(digest, SHA1_DIGEST_LENGTH); + return buffer; +} + +void sha1_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA1_DIGEST_LENGTH]) { + SHA1_CTX context = {0}; + sha1_Init(&context); + sha1_Update(&context, data, len); + sha1_Final(&context, digest); +} + +char* sha1_Data(const sha2_byte* data, size_t len, char digest[SHA1_DIGEST_STRING_LENGTH]) { + SHA1_CTX context = {0}; + + sha1_Init(&context); + sha1_Update(&context, data, len); + return sha1_End(&context, digest); +} + +/*** SHA-256: *********************************************************/ +void sha256_Init(SHA256_CTX* context) { + if(context == (SHA256_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); + memzero(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = 0; +} + +void sha256_Init_ex(SHA256_CTX* context, const uint32_t state[8], uint64_t bitcount) { + if(context == (SHA256_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, state, SHA256_DIGEST_LENGTH); + memzero(context->buffer, SHA256_BLOCK_LENGTH); + context->bitcount = bitcount; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#define ROUND256_0_TO_15(a, b, c, d, e, f, g, h) \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + (W256[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +#define ROUND256(a, b, c, d, e, f, g, h) \ + s0 = W256[(j + 1) & 0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j + 14) & 0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j & 0x0f] += s1 + W256[(j + 9) & 0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +void sha256_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, s0 = 0, s1 = 0; + sha2_word32 T1 = 0; + sha2_word32 W256[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a, b, c, d, e, f, g, h); + ROUND256_0_TO_15(h, a, b, c, d, e, f, g); + ROUND256_0_TO_15(g, h, a, b, c, d, e, f); + ROUND256_0_TO_15(f, g, h, a, b, c, d, e); + ROUND256_0_TO_15(e, f, g, h, a, b, c, d); + ROUND256_0_TO_15(d, e, f, g, h, a, b, c); + ROUND256_0_TO_15(c, d, e, f, g, h, a, b); + ROUND256_0_TO_15(b, c, d, e, f, g, h, a); + } while(j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a, b, c, d, e, f, g, h); + ROUND256(h, a, b, c, d, e, f, g); + ROUND256(g, h, a, b, c, d, e, f); + ROUND256(f, g, h, a, b, c, d, e); + ROUND256(e, f, g, h, a, b, c, d); + ROUND256(d, e, f, g, h, a, b, c); + ROUND256(c, d, e, f, g, h, a, b); + ROUND256(b, c, d, e, f, g, h, a); + } while(j < 64); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void sha256_Transform(const sha2_word32* state_in, const sha2_word32* data, sha2_word32* state_out) { + sha2_word32 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, s0 = 0, s1 = 0; + sha2_word32 T1 = 0, T2 = 0, W256[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Apply the SHA-256 compression function to update a..h with copy */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while(j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j + 1) & 0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j + 14) & 0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j & 0x0f] += s1 + W256[(j + 9) & 0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while(j < 64); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void sha256_Update(SHA256_CTX* context, const sha2_byte* data, size_t len) { + unsigned int freespace = 0, usedspace = 0; + + if(len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + if(usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA256_BLOCK_LENGTH - usedspace; + + if(len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + sha256_Transform(context->state, context->buffer, context->state); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while(len >= SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + MEMCPY_BCOPY(context->buffer, data, SHA256_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + sha256_Transform(context->state, context->buffer, context->state); + context->bitcount += SHA256_BLOCK_LENGTH << 3; + len -= SHA256_BLOCK_LENGTH; + data += SHA256_BLOCK_LENGTH; + } + if(len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; +} + +void sha256_Final(SHA256_CTX* context, sha2_byte digest[SHA256_DIGEST_LENGTH]) { + unsigned int usedspace = 0; + + /* If no digest buffer is passed, we don't bother doing this: */ + if(digest != (sha2_byte*)0) { + usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; + /* Begin padding with a 1 bit: */ + ((uint8_t*)context->buffer)[usedspace++] = 0x80; + + if(usedspace > SHA256_SHORT_BLOCK_LENGTH) { + memzero(((uint8_t*)context->buffer) + usedspace, SHA256_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + /* Do second-to-last transform: */ + sha256_Transform(context->state, context->buffer, context->state); + + /* And prepare the last transform: */ + usedspace = 0; + } + /* Set-up for the last transform: */ + memzero(((uint8_t*)context->buffer) + usedspace, SHA256_SHORT_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 14; j++) { + REVERSE32(context->buffer[j], context->buffer[j]); + } +#endif + /* Set the bit count: */ + context->buffer[14] = context->bitcount >> 32; + context->buffer[15] = context->bitcount & 0xffffffff; + + /* Final transform: */ + sha256_Transform(context->state, context->buffer, context->state); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + for(int j = 0; j < 8; j++) { + REVERSE32(context->state[j], context->state[j]); + } +#endif + MEMCPY_BCOPY(digest, context->state, SHA256_DIGEST_LENGTH); + } + + /* Clean up state data: */ + memzero(context, sizeof(SHA256_CTX)); + usedspace = 0; +} + +char* sha256_End(SHA256_CTX* context, char buffer[SHA256_DIGEST_STRING_LENGTH]) { + sha2_byte digest[SHA256_DIGEST_LENGTH] = {0}, *d = digest; + int i = 0; + + if(buffer != (char*)0) { + sha256_Final(context, digest); + + for(i = 0; i < SHA256_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + memzero(context, sizeof(SHA256_CTX)); + } + memzero(digest, SHA256_DIGEST_LENGTH); + return buffer; +} + +void sha256_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA256_DIGEST_LENGTH]) { + SHA256_CTX context = {0}; + sha256_Init(&context); + sha256_Update(&context, data, len); + sha256_Final(&context, digest); +} + +char* sha256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) { + SHA256_CTX context = {0}; + + sha256_Init(&context); + sha256_Update(&context, data, len); + return sha256_End(&context, digest); +} + +/*** SHA-512: *********************************************************/ +void sha512_Init(SHA512_CTX* context) { + if(context == (SHA512_CTX*)0) { + return; + } + MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH); + memzero(context->buffer, SHA512_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +#ifdef SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ +#define ROUND512_0_TO_15(a, b, c, d, e, f, g, h) \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + (W512[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +#define ROUND512(a, b, c, d, e, f, g, h) \ + s0 = W512[(j + 1) & 0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j + 14) & 0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +void sha512_Transform(const sha2_word64* state_in, const sha2_word64* data, sha2_word64* state_out) { + sha2_word64 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, s0 = 0, s1 = 0; + sha2_word64 T1 = 0, W512[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + ROUND512_0_TO_15(a, b, c, d, e, f, g, h); + ROUND512_0_TO_15(h, a, b, c, d, e, f, g); + ROUND512_0_TO_15(g, h, a, b, c, d, e, f); + ROUND512_0_TO_15(f, g, h, a, b, c, d, e); + ROUND512_0_TO_15(e, f, g, h, a, b, c, d); + ROUND512_0_TO_15(d, e, f, g, h, a, b, c); + ROUND512_0_TO_15(c, d, e, f, g, h, a, b); + ROUND512_0_TO_15(b, c, d, e, f, g, h, a); + } while(j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a, b, c, d, e, f, g, h); + ROUND512(h, a, b, c, d, e, f, g); + ROUND512(g, h, a, b, c, d, e, f); + ROUND512(f, g, h, a, b, c, d, e); + ROUND512(e, f, g, h, a, b, c, d); + ROUND512(d, e, f, g, h, a, b, c); + ROUND512(c, d, e, f, g, h, a, b); + ROUND512(b, c, d, e, f, g, h, a); + } while(j < 80); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; +} + +#else /* SHA2_UNROLL_TRANSFORM */ + +void sha512_Transform(const sha2_word64* state_in, const sha2_word64* data, sha2_word64* state_out) { + sha2_word64 a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, s0 = 0, s1 = 0; + sha2_word64 T1 = 0, T2 = 0, W512[16] = {0}; + int j = 0; + + /* Initialize registers with the prev. intermediate value */ + a = state_in[0]; + b = state_in[1]; + c = state_in[2]; + d = state_in[3]; + e = state_in[4]; + f = state_in[5]; + g = state_in[6]; + h = state_in[7]; + + j = 0; + do { + /* Apply the SHA-512 compression function to update a..h with copy */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while(j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j + 1) & 0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j + 14) & 0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while(j < 80); + + /* Compute the current intermediate hash value */ + state_out[0] = state_in[0] + a; + state_out[1] = state_in[1] + b; + state_out[2] = state_in[2] + c; + state_out[3] = state_in[3] + d; + state_out[4] = state_in[4] + e; + state_out[5] = state_in[5] + f; + state_out[6] = state_in[6] + g; + state_out[7] = state_in[7] + h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; +} + +#endif /* SHA2_UNROLL_TRANSFORM */ + +void sha512_Update(SHA512_CTX* context, const sha2_byte* data, size_t len) { + unsigned int freespace = 0, usedspace = 0; + + if(len == 0) { + /* Calling with no data is valid - we do nothing */ + return; + } + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + if(usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = SHA512_BLOCK_LENGTH - usedspace; + + if(len >= freespace) { + /* Fill the buffer completely and process it */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j], context->buffer[j]); + } +#endif + sha512_Transform(context->state, context->buffer, context->state); + } else { + /* The buffer is not yet full */ + MEMCPY_BCOPY(((uint8_t*)context->buffer) + usedspace, data, len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + return; + } + } + while(len >= SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + MEMCPY_BCOPY(context->buffer, data, SHA512_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j], context->buffer[j]); + } +#endif + sha512_Transform(context->state, context->buffer, context->state); + ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); + len -= SHA512_BLOCK_LENGTH; + data += SHA512_BLOCK_LENGTH; + } + if(len > 0) { + /* There's left-overs, so save 'em */ + MEMCPY_BCOPY(context->buffer, data, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; +} + +static void sha512_Last(SHA512_CTX* context) { + unsigned int usedspace = 0; + + usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; + /* Begin padding with a 1 bit: */ + ((uint8_t*)context->buffer)[usedspace++] = 0x80; + + if(usedspace > SHA512_SHORT_BLOCK_LENGTH) { + memzero(((uint8_t*)context->buffer) + usedspace, SHA512_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 16; j++) { + REVERSE64(context->buffer[j], context->buffer[j]); + } +#endif + /* Do second-to-last transform: */ + sha512_Transform(context->state, context->buffer, context->state); + + /* And prepare the last transform: */ + usedspace = 0; + } + /* Set-up for the last transform: */ + memzero(((uint8_t*)context->buffer) + usedspace, SHA512_SHORT_BLOCK_LENGTH - usedspace); + +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + for(int j = 0; j < 14; j++) { + REVERSE64(context->buffer[j], context->buffer[j]); + } +#endif + /* Store the length of input data (in bits): */ + context->buffer[14] = context->bitcount[1]; + context->buffer[15] = context->bitcount[0]; + + /* Final transform: */ + sha512_Transform(context->state, context->buffer, context->state); +} + +void sha512_Final(SHA512_CTX* context, sha2_byte digest[SHA512_DIGEST_LENGTH]) { + /* If no digest buffer is passed, we don't bother doing this: */ + if(digest != (sha2_byte*)0) { + sha512_Last(context); + + /* Save the hash data for output: */ +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + for(int j = 0; j < 8; j++) { + REVERSE64(context->state[j], context->state[j]); + } +#endif + MEMCPY_BCOPY(digest, context->state, SHA512_DIGEST_LENGTH); + } + + /* Zero out state data */ + memzero(context, sizeof(SHA512_CTX)); +} + +char* sha512_End(SHA512_CTX* context, char buffer[SHA512_DIGEST_STRING_LENGTH]) { + sha2_byte digest[SHA512_DIGEST_LENGTH] = {0}, *d = digest; + int i = 0; + + if(buffer != (char*)0) { + sha512_Final(context, digest); + + for(i = 0; i < SHA512_DIGEST_LENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { + memzero(context, sizeof(SHA512_CTX)); + } + memzero(digest, SHA512_DIGEST_LENGTH); + return buffer; +} + +void sha512_Raw(const sha2_byte* data, size_t len, uint8_t digest[SHA512_DIGEST_LENGTH]) { + SHA512_CTX context = {0}; + sha512_Init(&context); + sha512_Update(&context, data, len); + sha512_Final(&context, digest); +} + +char* sha512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) { + SHA512_CTX context = {0}; + + sha512_Init(&context); + sha512_Update(&context, data, len); + return sha512_End(&context, digest); +} diff --git a/applications/external/flipbip/lib/crypto/sha2.h b/applications/external/flipbip/lib/crypto/sha2.h new file mode 100644 index 000000000..5f7c1f071 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/sha2.h @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2000-2001 Aaron D. Gifford + * Copyright (c) 2013-2014 Pavol Rusnak + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __SHA2_H__ +#define __SHA2_H__ + +#include +#include +#include "byte_order.h" + +#define SHA1_BLOCK_LENGTH 64 +#define SHA1_DIGEST_LENGTH 20 +#define SHA1_DIGEST_STRING_LENGTH (SHA1_DIGEST_LENGTH * 2 + 1) +#define SHA256_BLOCK_LENGTH 64 +#define SHA256_DIGEST_LENGTH 32 +#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) +#define SHA512_BLOCK_LENGTH 128 +#define SHA512_DIGEST_LENGTH 64 +#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) + +typedef struct _SHA1_CTX { + uint32_t state[5]; + uint64_t bitcount; + uint32_t buffer[SHA1_BLOCK_LENGTH / sizeof(uint32_t)]; +} SHA1_CTX; +typedef struct _SHA256_CTX { + uint32_t state[8]; + uint64_t bitcount; + uint32_t buffer[SHA256_BLOCK_LENGTH / sizeof(uint32_t)]; +} SHA256_CTX; +typedef struct _SHA512_CTX { + uint64_t state[8]; + uint64_t bitcount[2]; + uint64_t buffer[SHA512_BLOCK_LENGTH / sizeof(uint64_t)]; +} SHA512_CTX; + +extern const uint32_t sha256_initial_hash_value[8]; +extern const uint64_t sha512_initial_hash_value[8]; + +void sha1_Transform(const uint32_t* state_in, const uint32_t* data, uint32_t* state_out); +void sha1_Init(SHA1_CTX*); +void sha1_Update(SHA1_CTX*, const uint8_t*, size_t); +void sha1_Final(SHA1_CTX*, uint8_t[SHA1_DIGEST_LENGTH]); +char* sha1_End(SHA1_CTX*, char[SHA1_DIGEST_STRING_LENGTH]); +void sha1_Raw(const uint8_t*, size_t, uint8_t[SHA1_DIGEST_LENGTH]); +char* sha1_Data(const uint8_t*, size_t, char[SHA1_DIGEST_STRING_LENGTH]); + +void sha256_Transform(const uint32_t* state_in, const uint32_t* data, uint32_t* state_out); +void sha256_Init(SHA256_CTX*); +void sha256_Init_ex(SHA256_CTX*, const uint32_t state[8], uint64_t bitcount); +void sha256_Update(SHA256_CTX*, const uint8_t*, size_t); +void sha256_Final(SHA256_CTX*, uint8_t[SHA256_DIGEST_LENGTH]); +char* sha256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); +void sha256_Raw(const uint8_t*, size_t, uint8_t[SHA256_DIGEST_LENGTH]); +char* sha256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); + +void sha512_Transform(const uint64_t* state_in, const uint64_t* data, uint64_t* state_out); +void sha512_Init(SHA512_CTX*); +void sha512_Update(SHA512_CTX*, const uint8_t*, size_t); +void sha512_Final(SHA512_CTX*, uint8_t[SHA512_DIGEST_LENGTH]); +char* sha512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); +void sha512_Raw(const uint8_t*, size_t, uint8_t[SHA512_DIGEST_LENGTH]); +char* sha512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); + +#endif diff --git a/applications/external/flipbip/lib/crypto/sha3.c b/applications/external/flipbip/lib/crypto/sha3.c new file mode 100644 index 000000000..d3a43b6a3 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/sha3.c @@ -0,0 +1,392 @@ +/* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#include +#include + +#include "sha3.h" +#include "memzero.h" +#include "byte_order.h" + +#define I64(x) x##LL +#define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) +#define le2me_64(x) (x) +#define IS_ALIGNED_64(p) (0 == (((uintptr_t)(const void*)(p)&0x7))) +#define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) + +/* constants */ +#define NumberOfRounds 24 + +/* SHA3 (Keccak) constants for 24 rounds */ +static uint64_t keccak_round_constants[NumberOfRounds] = { + I64(0x0000000000000001), I64(0x0000000000008082), I64(0x800000000000808A), + I64(0x8000000080008000), I64(0x000000000000808B), I64(0x0000000080000001), + I64(0x8000000080008081), I64(0x8000000000008009), I64(0x000000000000008A), + I64(0x0000000000000088), I64(0x0000000080008009), I64(0x000000008000000A), + I64(0x000000008000808B), I64(0x800000000000008B), I64(0x8000000000008089), + I64(0x8000000000008003), I64(0x8000000000008002), I64(0x8000000000000080), + I64(0x000000000000800A), I64(0x800000008000000A), I64(0x8000000080008081), + I64(0x8000000000008080), I64(0x0000000080000001), I64(0x8000000080008008)}; + +/* Initializing a sha3 context for given number of output bits */ +static void keccak_Init(SHA3_CTX* ctx, unsigned bits) { + /* NB: The Keccak capacity parameter = bits * 2 */ + unsigned rate = 1600 - bits * 2; + + memzero(ctx, sizeof(SHA3_CTX)); + ctx->block_size = rate / 8; + assert(rate <= 1600 && (rate % 64) == 0); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_224_Init(SHA3_CTX* ctx) { + keccak_Init(ctx, 224); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_256_Init(SHA3_CTX* ctx) { + keccak_Init(ctx, 256); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_384_Init(SHA3_CTX* ctx) { + keccak_Init(ctx, 384); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_512_Init(SHA3_CTX* ctx) { + keccak_Init(ctx, 512); +} + +/* Keccak theta() transformation */ +static void keccak_theta(uint64_t* A) { + unsigned int x = 0; + uint64_t C[5] = {0}, D[5] = {0}; + + for(x = 0; x < 5; x++) { + C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; + } + D[0] = ROTL64(C[1], 1) ^ C[4]; + D[1] = ROTL64(C[2], 1) ^ C[0]; + D[2] = ROTL64(C[3], 1) ^ C[1]; + D[3] = ROTL64(C[4], 1) ^ C[2]; + D[4] = ROTL64(C[0], 1) ^ C[3]; + + for(x = 0; x < 5; x++) { + A[x] ^= D[x]; + A[x + 5] ^= D[x]; + A[x + 10] ^= D[x]; + A[x + 15] ^= D[x]; + A[x + 20] ^= D[x]; + } +} + +/* Keccak pi() transformation */ +static void keccak_pi(uint64_t* A) { + uint64_t A1 = 0; + A1 = A[1]; + A[1] = A[6]; + A[6] = A[9]; + A[9] = A[22]; + A[22] = A[14]; + A[14] = A[20]; + A[20] = A[2]; + A[2] = A[12]; + A[12] = A[13]; + A[13] = A[19]; + A[19] = A[23]; + A[23] = A[15]; + A[15] = A[4]; + A[4] = A[24]; + A[24] = A[21]; + A[21] = A[8]; + A[8] = A[16]; + A[16] = A[5]; + A[5] = A[3]; + A[3] = A[18]; + A[18] = A[17]; + A[17] = A[11]; + A[11] = A[7]; + A[7] = A[10]; + A[10] = A1; + /* note: A[ 0] is left as is */ +} + +/* Keccak chi() transformation */ +static void keccak_chi(uint64_t* A) { + int i = 0; + for(i = 0; i < 25; i += 5) { + uint64_t A0 = A[0 + i], A1 = A[1 + i]; + A[0 + i] ^= ~A1 & A[2 + i]; + A[1 + i] ^= ~A[2 + i] & A[3 + i]; + A[2 + i] ^= ~A[3 + i] & A[4 + i]; + A[3 + i] ^= ~A[4 + i] & A0; + A[4 + i] ^= ~A0 & A1; + } +} + +static void sha3_permutation(uint64_t* state) { +#if BYTE_ORDER == BIG_ENDIAN + int i; + for(i = 0; i < 25; i++) { + REVERSE64(state[i], state[i]); + } +#endif + int round = 0; + for(round = 0; round < NumberOfRounds; round++) { + keccak_theta(state); + + /* apply Keccak rho() transformation */ + state[1] = ROTL64(state[1], 1); + state[2] = ROTL64(state[2], 62); + state[3] = ROTL64(state[3], 28); + state[4] = ROTL64(state[4], 27); + state[5] = ROTL64(state[5], 36); + state[6] = ROTL64(state[6], 44); + state[7] = ROTL64(state[7], 6); + state[8] = ROTL64(state[8], 55); + state[9] = ROTL64(state[9], 20); + state[10] = ROTL64(state[10], 3); + state[11] = ROTL64(state[11], 10); + state[12] = ROTL64(state[12], 43); + state[13] = ROTL64(state[13], 25); + state[14] = ROTL64(state[14], 39); + state[15] = ROTL64(state[15], 41); + state[16] = ROTL64(state[16], 45); + state[17] = ROTL64(state[17], 15); + state[18] = ROTL64(state[18], 21); + state[19] = ROTL64(state[19], 8); + state[20] = ROTL64(state[20], 18); + state[21] = ROTL64(state[21], 2); + state[22] = ROTL64(state[22], 61); + state[23] = ROTL64(state[23], 56); + state[24] = ROTL64(state[24], 14); + + keccak_pi(state); + keccak_chi(state); + + /* apply iota(state, round) */ + *state ^= keccak_round_constants[round]; + } +#if BYTE_ORDER == BIG_ENDIAN + for(i = 0; i < 25; i++) { + REVERSE64(state[i], state[i]); + } +#endif +} + +/** + * The core transformation. Process the specified block of data. + * + * @param hash the algorithm state + * @param block the message block to process + * @param block_size the size of the processed block in bytes + */ +static void sha3_process_block(uint64_t hash[25], const uint64_t* block, size_t block_size) { + /* expanded loop */ + hash[0] ^= le2me_64(block[0]); + hash[1] ^= le2me_64(block[1]); + hash[2] ^= le2me_64(block[2]); + hash[3] ^= le2me_64(block[3]); + hash[4] ^= le2me_64(block[4]); + hash[5] ^= le2me_64(block[5]); + hash[6] ^= le2me_64(block[6]); + hash[7] ^= le2me_64(block[7]); + hash[8] ^= le2me_64(block[8]); + /* if not sha3-512 */ + if(block_size > 72) { + hash[9] ^= le2me_64(block[9]); + hash[10] ^= le2me_64(block[10]); + hash[11] ^= le2me_64(block[11]); + hash[12] ^= le2me_64(block[12]); + /* if not sha3-384 */ + if(block_size > 104) { + hash[13] ^= le2me_64(block[13]); + hash[14] ^= le2me_64(block[14]); + hash[15] ^= le2me_64(block[15]); + hash[16] ^= le2me_64(block[16]); + /* if not sha3-256 */ + if(block_size > 136) { + hash[17] ^= le2me_64(block[17]); +#ifdef FULL_SHA3_FAMILY_SUPPORT + /* if not sha3-224 */ + if(block_size > 144) { + hash[18] ^= le2me_64(block[18]); + hash[19] ^= le2me_64(block[19]); + hash[20] ^= le2me_64(block[20]); + hash[21] ^= le2me_64(block[21]); + hash[22] ^= le2me_64(block[22]); + hash[23] ^= le2me_64(block[23]); + hash[24] ^= le2me_64(block[24]); + } +#endif + } + } + } + /* make a permutation of the hash */ + sha3_permutation(hash); +} + +#define SHA3_FINALIZED 0x80000000 + +/** + * Calculate message hash. + * Can be called repeatedly with chunks of the message to be hashed. + * + * @param ctx the algorithm context containing current hashing state + * @param msg message chunk + * @param size length of the message chunk + */ +void sha3_Update(SHA3_CTX* ctx, const unsigned char* msg, size_t size) { + if(size == 0) return; + + size_t idx = (size_t)ctx->rest; + size_t block_size = (size_t)ctx->block_size; + + if(ctx->rest & SHA3_FINALIZED) return; /* too late for additional input */ + ctx->rest = (unsigned)((ctx->rest + size) % block_size); + + /* fill partial block */ + if(idx) { + size_t left = block_size - idx; + memcpy((char*)ctx->message + idx, msg, (size < left ? size : left)); + if(size < left) return; + + /* process partial block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + msg += left; + size -= left; + } + while(size >= block_size) { + uint64_t* aligned_message_block = NULL; + if(IS_ALIGNED_64(msg)) { + /* the most common case is processing of an already aligned message + without copying it */ + aligned_message_block = (uint64_t*)(void*)msg; + } else { + memcpy(ctx->message, msg, block_size); + aligned_message_block = ctx->message; + } + + sha3_process_block(ctx->hash, aligned_message_block, block_size); + msg += block_size; + size -= block_size; + } + if(size) { + memcpy(ctx->message, msg, size); /* save leftovers */ + } +} + +/** + * Store calculated hash into the given array. + * + * @param ctx the algorithm context containing current hashing state + * @param result calculated hash in binary form + */ +void sha3_Final(SHA3_CTX* ctx, unsigned char* result) { + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if(!(ctx->rest & SHA3_FINALIZED)) { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x06; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if(result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +#if USE_KECCAK +/** +* Store calculated hash into the given array. +* +* @param ctx the algorithm context containing current hashing state +* @param result calculated hash in binary form +*/ +void keccak_Final(SHA3_CTX* ctx, unsigned char* result) { + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if(!(ctx->rest & SHA3_FINALIZED)) { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x01; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if(result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest) { + SHA3_CTX ctx = {0}; + keccak_256_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} + +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest) { + SHA3_CTX ctx = {0}; + keccak_512_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} +#endif /* USE_KECCAK */ + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest) { + SHA3_CTX ctx = {0}; + sha3_256_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} + +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest) { + SHA3_CTX ctx = {0}; + sha3_512_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} diff --git a/applications/external/flipbip/lib/crypto/sha3.h b/applications/external/flipbip/lib/crypto/sha3.h new file mode 100644 index 000000000..01818a4fe --- /dev/null +++ b/applications/external/flipbip/lib/crypto/sha3.h @@ -0,0 +1,88 @@ +/* sha3.h - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#ifndef __SHA3_H__ +#define __SHA3_H__ + +#include +#include "options.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define sha3_224_hash_size 28 +#define sha3_256_hash_size 32 +#define sha3_384_hash_size 48 +#define sha3_512_hash_size 64 +#define sha3_max_permutation_size 25 +#define sha3_max_rate_in_qwords 24 + +#define SHA3_224_BLOCK_LENGTH 144 +#define SHA3_256_BLOCK_LENGTH 136 +#define SHA3_384_BLOCK_LENGTH 104 +#define SHA3_512_BLOCK_LENGTH 72 + +#define SHA3_224_DIGEST_LENGTH sha3_224_hash_size +#define SHA3_256_DIGEST_LENGTH sha3_256_hash_size +#define SHA3_384_DIGEST_LENGTH sha3_384_hash_size +#define SHA3_512_DIGEST_LENGTH sha3_512_hash_size + +/** + * SHA3 Algorithm context. + */ +typedef struct SHA3_CTX { + /* 1600 bits algorithm hashing state */ + uint64_t hash[sha3_max_permutation_size]; + /* 1536-bit buffer for leftovers */ + uint64_t message[sha3_max_rate_in_qwords]; + /* count of bytes in the message[] buffer */ + unsigned rest; + /* size of a message block processed at once */ + unsigned block_size; +} SHA3_CTX; + +/* methods for calculating the hash function */ + +void sha3_224_Init(SHA3_CTX* ctx); +void sha3_256_Init(SHA3_CTX* ctx); +void sha3_384_Init(SHA3_CTX* ctx); +void sha3_512_Init(SHA3_CTX* ctx); +void sha3_Update(SHA3_CTX* ctx, const unsigned char* msg, size_t size); +void sha3_Final(SHA3_CTX* ctx, unsigned char* result); + +#if USE_KECCAK +#define keccak_224_Init sha3_224_Init +#define keccak_256_Init sha3_256_Init +#define keccak_384_Init sha3_384_Init +#define keccak_512_Init sha3_512_Init +#define keccak_Update sha3_Update +void keccak_Final(SHA3_CTX* ctx, unsigned char* result); +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest); +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest); +#endif + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest); +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __SHA3_H__ */ diff --git a/applications/external/flipbip/lib/crypto/shamir.c b/applications/external/flipbip/lib/crypto/shamir.c new file mode 100644 index 000000000..037af99cc --- /dev/null +++ b/applications/external/flipbip/lib/crypto/shamir.c @@ -0,0 +1,338 @@ +/* + * Implementation of the hazardous parts of the SSS library + * + * Copyright (c) 2017 Daan Sprenkels + * Copyright (c) 2019 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * This code contains the actual Shamir secret sharing functionality. The + * implementation of this code is based on the idea that the user likes to + * generate/combine 32 shares (in GF(2^8)) at the same time, because a 256 bit + * key will be exactly 32 bytes. Therefore we bitslice all the input and + * unbitslice the output right before returning. + * + * This bitslice approach optimizes natively on all architectures that are 32 + * bit or more. Care is taken to use not too many registers, to ensure that no + * values have to be leaked to the stack. + * + * All functions in this module are implemented constant time and constant + * lookup operations, as all proper crypto code must be. + */ + +#include "shamir.h" +#include +#include "memzero.h" + +static void bitslice(uint32_t r[8], const uint8_t* x, size_t len) { + size_t bit_idx = 0, arr_idx = 0; + uint32_t cur = 0; + + memset(r, 0, sizeof(uint32_t[8])); + for(arr_idx = 0; arr_idx < len; arr_idx++) { + cur = (uint32_t)x[arr_idx]; + for(bit_idx = 0; bit_idx < 8; bit_idx++) { + r[bit_idx] |= ((cur >> bit_idx) & 1) << arr_idx; + } + } +} + +static void unbitslice(uint8_t* r, const uint32_t x[8], size_t len) { + size_t bit_idx = 0, arr_idx = 0; + uint32_t cur = 0; + + memset(r, 0, sizeof(uint8_t) * len); + for(bit_idx = 0; bit_idx < 8; bit_idx++) { + cur = (uint32_t)x[bit_idx]; + for(arr_idx = 0; arr_idx < len; arr_idx++) { + r[arr_idx] |= ((cur >> arr_idx) & 1) << bit_idx; + } + } +} + +static void bitslice_setall(uint32_t r[8], const uint8_t x) { + size_t idx = 0; + for(idx = 0; idx < 8; idx++) { + r[idx] = -((x >> idx) & 1); + } +} + +/* + * Add (XOR) `r` with `x` and store the result in `r`. + */ +static void gf256_add(uint32_t r[8], const uint32_t x[8]) { + size_t idx = 0; + for(idx = 0; idx < 8; idx++) r[idx] ^= x[idx]; +} + +/* + * Safely multiply two bitsliced polynomials in GF(2^8) reduced by + * x^8 + x^4 + x^3 + x + 1. `r` and `a` may overlap, but overlapping of `r` + * and `b` will produce an incorrect result! If you need to square a polynomial + * use `gf256_square` instead. + */ +static void gf256_mul(uint32_t r[8], const uint32_t a[8], const uint32_t b[8]) { + /* This function implements Russian Peasant multiplication on two + * bitsliced polynomials. + * + * I personally think that these kinds of long lists of operations + * are often a bit ugly. A double for loop would be nicer and would + * take up a lot less lines of code. + * However, some compilers seem to fail in optimizing these kinds of + * loops. So we will just have to do this by hand. + */ + uint32_t a2[8] = {0}; + memcpy(a2, a, sizeof(uint32_t[8])); + + r[0] = a2[0] & b[0]; /* add (assignment, because r is 0) */ + r[1] = a2[1] & b[0]; + r[2] = a2[2] & b[0]; + r[3] = a2[3] & b[0]; + r[4] = a2[4] & b[0]; + r[5] = a2[5] & b[0]; + r[6] = a2[6] & b[0]; + r[7] = a2[7] & b[0]; + a2[0] ^= a2[7]; /* reduce */ + a2[2] ^= a2[7]; + a2[3] ^= a2[7]; + + r[0] ^= a2[7] & b[1]; /* add */ + r[1] ^= a2[0] & b[1]; + r[2] ^= a2[1] & b[1]; + r[3] ^= a2[2] & b[1]; + r[4] ^= a2[3] & b[1]; + r[5] ^= a2[4] & b[1]; + r[6] ^= a2[5] & b[1]; + r[7] ^= a2[6] & b[1]; + a2[7] ^= a2[6]; /* reduce */ + a2[1] ^= a2[6]; + a2[2] ^= a2[6]; + + r[0] ^= a2[6] & b[2]; /* add */ + r[1] ^= a2[7] & b[2]; + r[2] ^= a2[0] & b[2]; + r[3] ^= a2[1] & b[2]; + r[4] ^= a2[2] & b[2]; + r[5] ^= a2[3] & b[2]; + r[6] ^= a2[4] & b[2]; + r[7] ^= a2[5] & b[2]; + a2[6] ^= a2[5]; /* reduce */ + a2[0] ^= a2[5]; + a2[1] ^= a2[5]; + + r[0] ^= a2[5] & b[3]; /* add */ + r[1] ^= a2[6] & b[3]; + r[2] ^= a2[7] & b[3]; + r[3] ^= a2[0] & b[3]; + r[4] ^= a2[1] & b[3]; + r[5] ^= a2[2] & b[3]; + r[6] ^= a2[3] & b[3]; + r[7] ^= a2[4] & b[3]; + a2[5] ^= a2[4]; /* reduce */ + a2[7] ^= a2[4]; + a2[0] ^= a2[4]; + + r[0] ^= a2[4] & b[4]; /* add */ + r[1] ^= a2[5] & b[4]; + r[2] ^= a2[6] & b[4]; + r[3] ^= a2[7] & b[4]; + r[4] ^= a2[0] & b[4]; + r[5] ^= a2[1] & b[4]; + r[6] ^= a2[2] & b[4]; + r[7] ^= a2[3] & b[4]; + a2[4] ^= a2[3]; /* reduce */ + a2[6] ^= a2[3]; + a2[7] ^= a2[3]; + + r[0] ^= a2[3] & b[5]; /* add */ + r[1] ^= a2[4] & b[5]; + r[2] ^= a2[5] & b[5]; + r[3] ^= a2[6] & b[5]; + r[4] ^= a2[7] & b[5]; + r[5] ^= a2[0] & b[5]; + r[6] ^= a2[1] & b[5]; + r[7] ^= a2[2] & b[5]; + a2[3] ^= a2[2]; /* reduce */ + a2[5] ^= a2[2]; + a2[6] ^= a2[2]; + + r[0] ^= a2[2] & b[6]; /* add */ + r[1] ^= a2[3] & b[6]; + r[2] ^= a2[4] & b[6]; + r[3] ^= a2[5] & b[6]; + r[4] ^= a2[6] & b[6]; + r[5] ^= a2[7] & b[6]; + r[6] ^= a2[0] & b[6]; + r[7] ^= a2[1] & b[6]; + a2[2] ^= a2[1]; /* reduce */ + a2[4] ^= a2[1]; + a2[5] ^= a2[1]; + + r[0] ^= a2[1] & b[7]; /* add */ + r[1] ^= a2[2] & b[7]; + r[2] ^= a2[3] & b[7]; + r[3] ^= a2[4] & b[7]; + r[4] ^= a2[5] & b[7]; + r[5] ^= a2[6] & b[7]; + r[6] ^= a2[7] & b[7]; + r[7] ^= a2[0] & b[7]; + + memzero(a2, sizeof(a2)); +} + +/* + * Square `x` in GF(2^8) and write the result to `r`. `r` and `x` may overlap. + */ +static void gf256_square(uint32_t r[8], const uint32_t x[8]) { + uint32_t r8 = 0, r10 = 0, r12 = 0, r14 = 0; + /* Use the Freshman's Dream rule to square the polynomial + * Assignments are done from 7 downto 0, because this allows the user + * to execute this function in-place (e.g. `gf256_square(r, r);`). + */ + r14 = x[7]; + r12 = x[6]; + r10 = x[5]; + r8 = x[4]; + r[6] = x[3]; + r[4] = x[2]; + r[2] = x[1]; + r[0] = x[0]; + + /* Reduce with x^8 + x^4 + x^3 + x + 1 until order is less than 8 */ + r[7] = r14; /* r[7] was 0 */ + r[6] ^= r14; + r10 ^= r14; + /* Skip, because r13 is always 0 */ + r[4] ^= r12; + r[5] = r12; /* r[5] was 0 */ + r[7] ^= r12; + r8 ^= r12; + /* Skip, because r11 is always 0 */ + r[2] ^= r10; + r[3] = r10; /* r[3] was 0 */ + r[5] ^= r10; + r[6] ^= r10; + r[1] = r14; /* r[1] was 0 */ + r[2] ^= r14; /* Substitute r9 by r14 because they will always be equal*/ + r[4] ^= r14; + r[5] ^= r14; + r[0] ^= r8; + r[1] ^= r8; + r[3] ^= r8; + r[4] ^= r8; +} + +/* + * Invert `x` in GF(2^8) and write the result to `r` + */ +static void gf256_inv(uint32_t r[8], uint32_t x[8]) { + uint32_t y[8] = {0}, z[8] = {0}; + + gf256_square(y, x); // y = x^2 + gf256_square(y, y); // y = x^4 + gf256_square(r, y); // r = x^8 + gf256_mul(z, r, x); // z = x^9 + gf256_square(r, r); // r = x^16 + gf256_mul(r, r, z); // r = x^25 + gf256_square(r, r); // r = x^50 + gf256_square(z, r); // z = x^100 + gf256_square(z, z); // z = x^200 + gf256_mul(r, r, z); // r = x^250 + gf256_mul(r, r, y); // r = x^254 + + memzero(y, sizeof(y)); + memzero(z, sizeof(z)); +} + +bool shamir_interpolate( + uint8_t* result, + uint8_t result_index, + const uint8_t* share_indices, + const uint8_t** share_values, + uint8_t share_count, + size_t len) { + size_t i = 0, j = 0; + uint32_t x[8] = {0}; + uint32_t xs[share_count][8]; + memset(xs, 0, sizeof(xs)); + uint32_t ys[share_count][8]; + memset(ys, 0, sizeof(ys)); + uint32_t num[8] = {~0}; /* num is the numerator (=1) */ + uint32_t denom[8] = {0}; + uint32_t tmp[8] = {0}; + uint32_t secret[8] = {0}; + bool ret = true; + + if(len > SHAMIR_MAX_LEN) return false; + + /* Collect the x and y values */ + for(i = 0; i < share_count; i++) { + bitslice_setall(xs[i], share_indices[i]); + bitslice(ys[i], share_values[i], len); + } + bitslice_setall(x, result_index); + + for(i = 0; i < share_count; i++) { + memcpy(tmp, x, sizeof(uint32_t[8])); + gf256_add(tmp, xs[i]); + gf256_mul(num, num, tmp); + } + + /* Use Lagrange basis polynomials to calculate the secret coefficient */ + for(i = 0; i < share_count; i++) { + /* The code below assumes that none of the share_indices are equal to + * result_index. We need to treat that as a special case. */ + if(share_indices[i] != result_index) { + memcpy(denom, x, sizeof(denom)); + gf256_add(denom, xs[i]); + } else { + bitslice_setall(denom, 1); + gf256_add(secret, ys[i]); + } + for(j = 0; j < share_count; j++) { + if(i == j) continue; + memcpy(tmp, xs[i], sizeof(uint32_t[8])); + gf256_add(tmp, xs[j]); + gf256_mul(denom, denom, tmp); + } + if((denom[0] | denom[1] | denom[2] | denom[3] | denom[4] | denom[5] | denom[6] | + denom[7]) == 0) { + /* The share_indices are not unique. */ + ret = false; + break; + } + gf256_inv(tmp, denom); /* inverted denominator */ + gf256_mul(tmp, tmp, num); /* basis polynomial */ + gf256_mul(tmp, tmp, ys[i]); /* scaled coefficient */ + gf256_add(secret, tmp); + } + + if(ret == true) { + unbitslice(result, secret, len); + } + + memzero(x, sizeof(x)); + memzero(xs, sizeof(xs)); + memzero(ys, sizeof(ys)); + memzero(num, sizeof(num)); + memzero(denom, sizeof(denom)); + memzero(tmp, sizeof(tmp)); + memzero(secret, sizeof(secret)); + return ret; +} diff --git a/applications/external/flipbip/lib/crypto/shamir.h b/applications/external/flipbip/lib/crypto/shamir.h new file mode 100644 index 000000000..2250fc672 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/shamir.h @@ -0,0 +1,73 @@ +/* + * Low level API for Daan Sprenkels' Shamir secret sharing library + * Copyright (c) 2017 Daan Sprenkels + * Copyright (c) 2019 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Usage of this API is hazardous and is only reserved for beings with a + * good understanding of the Shamir secret sharing scheme and who know how + * crypto code is implemented. If you are unsure about this, use the + * intermediate level API. You have been warned! + */ + +#ifndef __SHAMIR_H__ +#define __SHAMIR_H__ + +#include +#include +#include + +#define SHAMIR_MAX_LEN 32 + +/* + * Computes f(x) given the Shamir shares (x_1, f(x_1)), ... , (x_m, f(x_m)). + * The x coordinates of the shares must be pairwise distinct. Returns true on + * success, otherwise false. + * result: Array of length len where the evaluations of the polynomials in x + * will be written. + * result_index: The x coordinate of the result. + * share_indices: Points to the array of integers x_1, ... , x_m. + * share_values: Points to the array of y_1, ... , y_m, where each y_i is an + * array of bytes of length len representing the evaluations of the + * polynomials in x_i. + * share_count: The number of shares m. + * len: The length of the result array and of each of the y_1, ... , y_m arrays. + * + * The number of shares used to compute the result may be larger than the + * required threshold. + * + * This function does *not* do *any* checking for integrity. If any of the + * shares are not original, this will result in an invalid restored value. + * All values written to `result` should be treated as secret. Even if some of + * the shares that were provided as input were incorrect, the result *still* + * allows an attacker to gain information about the correct result. + * + * This function treats `shares_values`, `share_indices` and `result` as secret + * values. `share_count` is treated as a public value (for performance reasons). + */ +bool shamir_interpolate( + uint8_t* result, + uint8_t result_index, + const uint8_t* share_indices, + const uint8_t** share_values, + uint8_t share_count, + size_t len); + +#endif /* __SHAMIR_H__ */ diff --git a/applications/external/flipbip/lib/crypto/slip39.c b/applications/external/flipbip/lib/crypto/slip39.c new file mode 100644 index 000000000..268d0f37e --- /dev/null +++ b/applications/external/flipbip/lib/crypto/slip39.c @@ -0,0 +1,150 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "slip39.h" +#include +#include +#include "slip39_wordlist.h" + +/** + * Returns word at position `index`. + */ +const char* get_word(uint16_t index) { + if(index >= WORDS_COUNT) { + return NULL; + } + + return slip39_wordlist[index]; +} + +/** + * Finds the index of a given word. + * Returns true on success and stores result in `index`. + */ +bool word_index(uint16_t* index, const char* word, uint8_t word_length) { + uint16_t lo = 0; + uint16_t hi = WORDS_COUNT; + uint16_t mid = 0; + + while((hi - lo) > 1) { + mid = (hi + lo) / 2; + if(strncmp(slip39_wordlist[mid], word, word_length) > 0) { + hi = mid; + } else { + lo = mid; + } + } + if(strncmp(slip39_wordlist[lo], word, word_length) != 0) { + return false; + } + *index = lo; + return true; +} + +/** + * Returns the index of the first sequence in words_button_seq[] which is not + * less than the given sequence. Returns WORDS_COUNT if there is no such + * sequence. + */ +static uint16_t find_sequence(uint16_t sequence) { + if(sequence <= words_button_seq[0].sequence) { + return 0; + } + + uint16_t lo = 0; + uint16_t hi = WORDS_COUNT; + + while(hi - lo > 1) { + uint16_t mid = (hi + lo) / 2; + if(words_button_seq[mid].sequence >= sequence) { + hi = mid; + } else { + lo = mid; + } + } + + return hi; +} + +/** + * Returns a word matching the button sequence prefix or NULL if no match is + * found. + */ +const char* button_sequence_to_word(uint16_t sequence) { + if(sequence == 0) { + return slip39_wordlist[words_button_seq[0].index]; + } + + uint16_t multiplier = 1; + while(sequence < 1000) { + sequence *= 10; + multiplier *= 10; + } + + uint16_t i = find_sequence(sequence); + if(i >= WORDS_COUNT || words_button_seq[i].sequence - sequence >= multiplier) { + return NULL; + } + + return slip39_wordlist[words_button_seq[i].index]; +} + +/** + * Calculates which buttons on the T9 keyboard can still be pressed after the + * prefix was entered. Returns a 9-bit bitmask, where each bit specifies which + * buttons can be pressed (there are still words in this combination). The least + * significant bit corresponds to the first button. + * + * Example: 110000110 - second, third, eighth and ninth button still can be + * pressed. + */ +uint16_t slip39_word_completion_mask(uint16_t prefix) { + if(prefix >= 1000) { + // Four char prefix -> the mask is zero. + return 0; + } + + // Determine the range of sequences [min, max), which have the given prefix. + uint16_t min = prefix; + uint16_t max = prefix + 1; + uint16_t divider = 1; + while(max <= 1000) { + min *= 10; + max *= 10; + divider *= 10; + } + divider /= 10; + + // Determine the range we will be searching in words_button_seq[]. + min = find_sequence(min); + max = find_sequence(max); + + uint16_t bitmap = 0; + for(uint16_t i = min; i < max; ++i) { + uint8_t digit = (words_button_seq[i].sequence / divider) % 10; + bitmap |= 1 << (digit - 1); + } + + return bitmap; +} diff --git a/applications/external/flipbip/lib/crypto/slip39.h b/applications/external/flipbip/lib/crypto/slip39.h new file mode 100644 index 000000000..0bc24fa74 --- /dev/null +++ b/applications/external/flipbip/lib/crypto/slip39.h @@ -0,0 +1,39 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SLIP39_H__ +#define __SLIP39_H__ + +#include +#include + +const char* get_word(uint16_t index); + +bool word_index(uint16_t* index, const char* word, uint8_t word_length); + +uint16_t slip39_word_completion_mask(uint16_t prefix); + +const char* button_sequence_to_word(uint16_t prefix); + +#endif diff --git a/applications/external/flipbip/lib/crypto/slip39_wordlist.h b/applications/external/flipbip/lib/crypto/slip39_wordlist.h new file mode 100644 index 000000000..17d4edeed --- /dev/null +++ b/applications/external/flipbip/lib/crypto/slip39_wordlist.h @@ -0,0 +1,1203 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SLIP39_WORDLIST_H__ +#define __SLIP39_WORDLIST_H__ + +#include + +#define WORDS_COUNT 1024 + +static const char* const slip39_wordlist[WORDS_COUNT] = { + "academic", "acid", "acne", "acquire", "acrobat", "activity", "actress", "adapt", + "adequate", "adjust", "admit", "adorn", "adult", "advance", "advocate", "afraid", + "again", "agency", "agree", "aide", "aircraft", "airline", "airport", "ajar", + "alarm", "album", "alcohol", "alien", "alive", "alpha", "already", "alto", + "aluminum", "always", "amazing", "ambition", "amount", "amuse", "analysis", "anatomy", + "ancestor", "ancient", "angel", "angry", "animal", "answer", "antenna", "anxiety", + "apart", "aquatic", "arcade", "arena", "argue", "armed", "artist", "artwork", + "aspect", "auction", "august", "aunt", "average", "aviation", "avoid", "award", + "away", "axis", "axle", "beam", "beard", "beaver", "become", "bedroom", + "behavior", "being", "believe", "belong", "benefit", "best", "beyond", "bike", + "biology", "birthday", "bishop", "black", "blanket", "blessing", "blimp", "blind", + "blue", "body", "bolt", "boring", "born", "both", "boundary", "bracelet", + "branch", "brave", "breathe", "briefing", "broken", "brother", "browser", "bucket", + "budget", "building", "bulb", "bulge", "bumpy", "bundle", "burden", "burning", + "busy", "buyer", "cage", "calcium", "camera", "campus", "canyon", "capacity", + "capital", "capture", "carbon", "cards", "careful", "cargo", "carpet", "carve", + "category", "cause", "ceiling", "center", "ceramic", "champion", "change", "charity", + "check", "chemical", "chest", "chew", "chubby", "cinema", "civil", "class", + "clay", "cleanup", "client", "climate", "clinic", "clock", "clogs", "closet", + "clothes", "club", "cluster", "coal", "coastal", "coding", "column", "company", + "corner", "costume", "counter", "course", "cover", "cowboy", "cradle", "craft", + "crazy", "credit", "cricket", "criminal", "crisis", "critical", "crowd", "crucial", + "crunch", "crush", "crystal", "cubic", "cultural", "curious", "curly", "custody", + "cylinder", "daisy", "damage", "dance", "darkness", "database", "daughter", "deadline", + "deal", "debris", "debut", "decent", "decision", "declare", "decorate", "decrease", + "deliver", "demand", "density", "deny", "depart", "depend", "depict", "deploy", + "describe", "desert", "desire", "desktop", "destroy", "detailed", "detect", "device", + "devote", "diagnose", "dictate", "diet", "dilemma", "diminish", "dining", "diploma", + "disaster", "discuss", "disease", "dish", "dismiss", "display", "distance", "dive", + "divorce", "document", "domain", "domestic", "dominant", "dough", "downtown", "dragon", + "dramatic", "dream", "dress", "drift", "drink", "drove", "drug", "dryer", + "duckling", "duke", "duration", "dwarf", "dynamic", "early", "earth", "easel", + "easy", "echo", "eclipse", "ecology", "edge", "editor", "educate", "either", + "elbow", "elder", "election", "elegant", "element", "elephant", "elevator", "elite", + "else", "email", "emerald", "emission", "emperor", "emphasis", "employer", "empty", + "ending", "endless", "endorse", "enemy", "energy", "enforce", "engage", "enjoy", + "enlarge", "entrance", "envelope", "envy", "epidemic", "episode", "equation", "equip", + "eraser", "erode", "escape", "estate", "estimate", "evaluate", "evening", "evidence", + "evil", "evoke", "exact", "example", "exceed", "exchange", "exclude", "excuse", + "execute", "exercise", "exhaust", "exotic", "expand", "expect", "explain", "express", + "extend", "extra", "eyebrow", "facility", "fact", "failure", "faint", "fake", + "false", "family", "famous", "fancy", "fangs", "fantasy", "fatal", "fatigue", + "favorite", "fawn", "fiber", "fiction", "filter", "finance", "findings", "finger", + "firefly", "firm", "fiscal", "fishing", "fitness", "flame", "flash", "flavor", + "flea", "flexible", "flip", "float", "floral", "fluff", "focus", "forbid", + "force", "forecast", "forget", "formal", "fortune", "forward", "founder", "fraction", + "fragment", "frequent", "freshman", "friar", "fridge", "friendly", "frost", "froth", + "frozen", "fumes", "funding", "furl", "fused", "galaxy", "game", "garbage", + "garden", "garlic", "gasoline", "gather", "general", "genius", "genre", "genuine", + "geology", "gesture", "glad", "glance", "glasses", "glen", "glimpse", "goat", + "golden", "graduate", "grant", "grasp", "gravity", "gray", "greatest", "grief", + "grill", "grin", "grocery", "gross", "group", "grownup", "grumpy", "guard", + "guest", "guilt", "guitar", "gums", "hairy", "hamster", "hand", "hanger", + "harvest", "have", "havoc", "hawk", "hazard", "headset", "health", "hearing", + "heat", "helpful", "herald", "herd", "hesitate", "hobo", "holiday", "holy", + "home", "hormone", "hospital", "hour", "huge", "human", "humidity", "hunting", + "husband", "hush", "husky", "hybrid", "idea", "identify", "idle", "image", + "impact", "imply", "improve", "impulse", "include", "income", "increase", "index", + "indicate", "industry", "infant", "inform", "inherit", "injury", "inmate", "insect", + "inside", "install", "intend", "intimate", "invasion", "involve", "iris", "island", + "isolate", "item", "ivory", "jacket", "jerky", "jewelry", "join", "judicial", + "juice", "jump", "junction", "junior", "junk", "jury", "justice", "kernel", + "keyboard", "kidney", "kind", "kitchen", "knife", "knit", "laden", "ladle", + "ladybug", "lair", "lamp", "language", "large", "laser", "laundry", "lawsuit", + "leader", "leaf", "learn", "leaves", "lecture", "legal", "legend", "legs", + "lend", "length", "level", "liberty", "library", "license", "lift", "likely", + "lilac", "lily", "lips", "liquid", "listen", "literary", "living", "lizard", + "loan", "lobe", "location", "losing", "loud", "loyalty", "luck", "lunar", + "lunch", "lungs", "luxury", "lying", "lyrics", "machine", "magazine", "maiden", + "mailman", "main", "makeup", "making", "mama", "manager", "mandate", "mansion", + "manual", "marathon", "march", "market", "marvel", "mason", "material", "math", + "maximum", "mayor", "meaning", "medal", "medical", "member", "memory", "mental", + "merchant", "merit", "method", "metric", "midst", "mild", "military", "mineral", + "minister", "miracle", "mixed", "mixture", "mobile", "modern", "modify", "moisture", + "moment", "morning", "mortgage", "mother", "mountain", "mouse", "move", "much", + "mule", "multiple", "muscle", "museum", "music", "mustang", "nail", "national", + "necklace", "negative", "nervous", "network", "news", "nuclear", "numb", "numerous", + "nylon", "oasis", "obesity", "object", "observe", "obtain", "ocean", "often", + "olympic", "omit", "oral", "orange", "orbit", "order", "ordinary", "organize", + "ounce", "oven", "overall", "owner", "paces", "pacific", "package", "paid", + "painting", "pajamas", "pancake", "pants", "papa", "paper", "parcel", "parking", + "party", "patent", "patrol", "payment", "payroll", "peaceful", "peanut", "peasant", + "pecan", "penalty", "pencil", "percent", "perfect", "permit", "petition", "phantom", + "pharmacy", "photo", "phrase", "physics", "pickup", "picture", "piece", "pile", + "pink", "pipeline", "pistol", "pitch", "plains", "plan", "plastic", "platform", + "playoff", "pleasure", "plot", "plunge", "practice", "prayer", "preach", "predator", + "pregnant", "premium", "prepare", "presence", "prevent", "priest", "primary", "priority", + "prisoner", "privacy", "prize", "problem", "process", "profile", "program", "promise", + "prospect", "provide", "prune", "public", "pulse", "pumps", "punish", "puny", + "pupal", "purchase", "purple", "python", "quantity", "quarter", "quick", "quiet", + "race", "racism", "radar", "railroad", "rainbow", "raisin", "random", "ranked", + "rapids", "raspy", "reaction", "realize", "rebound", "rebuild", "recall", "receiver", + "recover", "regret", "regular", "reject", "relate", "remember", "remind", "remove", + "render", "repair", "repeat", "replace", "require", "rescue", "research", "resident", + "response", "result", "retailer", "retreat", "reunion", "revenue", "review", "reward", + "rhyme", "rhythm", "rich", "rival", "river", "robin", "rocky", "romantic", + "romp", "roster", "round", "royal", "ruin", "ruler", "rumor", "sack", + "safari", "salary", "salon", "salt", "satisfy", "satoshi", "saver", "says", + "scandal", "scared", "scatter", "scene", "scholar", "science", "scout", "scramble", + "screw", "script", "scroll", "seafood", "season", "secret", "security", "segment", + "senior", "shadow", "shaft", "shame", "shaped", "sharp", "shelter", "sheriff", + "short", "should", "shrimp", "sidewalk", "silent", "silver", "similar", "simple", + "single", "sister", "skin", "skunk", "slap", "slavery", "sled", "slice", + "slim", "slow", "slush", "smart", "smear", "smell", "smirk", "smith", + "smoking", "smug", "snake", "snapshot", "sniff", "society", "software", "soldier", + "solution", "soul", "source", "space", "spark", "speak", "species", "spelling", + "spend", "spew", "spider", "spill", "spine", "spirit", "spit", "spray", + "sprinkle", "square", "squeeze", "stadium", "staff", "standard", "starting", "station", + "stay", "steady", "step", "stick", "stilt", "story", "strategy", "strike", + "style", "subject", "submit", "sugar", "suitable", "sunlight", "superior", "surface", + "surprise", "survive", "sweater", "swimming", "swing", "switch", "symbolic", "sympathy", + "syndrome", "system", "tackle", "tactics", "tadpole", "talent", "task", "taste", + "taught", "taxi", "teacher", "teammate", "teaspoon", "temple", "tenant", "tendency", + "tension", "terminal", "testify", "texture", "thank", "that", "theater", "theory", + "therapy", "thorn", "threaten", "thumb", "thunder", "ticket", "tidy", "timber", + "timely", "ting", "tofu", "together", "tolerate", "total", "toxic", "tracks", + "traffic", "training", "transfer", "trash", "traveler", "treat", "trend", "trial", + "tricycle", "trip", "triumph", "trouble", "true", "trust", "twice", "twin", + "type", "typical", "ugly", "ultimate", "umbrella", "uncover", "undergo", "unfair", + "unfold", "unhappy", "union", "universe", "unkind", "unknown", "unusual", "unwrap", + "upgrade", "upstairs", "username", "usher", "usual", "valid", "valuable", "vampire", + "vanish", "various", "vegan", "velvet", "venture", "verdict", "verify", "very", + "veteran", "vexed", "victim", "video", "view", "vintage", "violence", "viral", + "visitor", "visual", "vitamins", "vocal", "voice", "volume", "voter", "voting", + "walnut", "warmth", "warn", "watch", "wavy", "wealthy", "weapon", "webcam", + "welcome", "welfare", "western", "width", "wildlife", "window", "wine", "wireless", + "wisdom", "withdraw", "wits", "wolf", "woman", "work", "worthy", "wrap", + "wrist", "writing", "wrote", "year", "yelp", "yield", "yoga", "zero", +}; + +/** + * This array contains number representations of SLIP-39 words. + * These numbers are determined how the words were entered on a + * T9 keyboard with the following layout: + * ab (1) cd (2) ef (3) + * ghij (4) klm (5) nopq (6) + * rs (7) tuv (8) wxyz (9) + * + * Each word is uniquely defined by four buttons. + */ +static const struct { + uint16_t sequence; + uint16_t index; +} words_button_seq[WORDS_COUNT] = { + {1212, 0}, // academic + {1216, 7}, // adapt + {1236, 8}, // adequate + {1242, 1}, // acid + {1248, 9}, // adjust + {1254, 10}, // admit + {1263, 2}, // acne + {1267, 11}, // adorn + {1268, 3}, // acquire + {1276, 4}, // acrobat + {1281, 13}, // advance + {1284, 5}, // activity + {1285, 12}, // adult + {1286, 14}, // advocate + {1287, 6}, // actress + {1315, 67}, // beam + {1317, 68}, // beard + {1318, 69}, // beaver + {1326, 70}, // become + {1327, 71}, // bedroom + {1341, 72}, // behavior + {1346, 73}, // being + {1354, 74}, // believe + {1356, 75}, // belong + {1363, 76}, // benefit + {1371, 15}, // afraid + {1378, 77}, // best + {1396, 78}, // beyond + {1414, 16}, // again + {1417, 23}, // ajar + {1423, 19}, // aide + {1436, 17}, // agency + {1453, 79}, // bike + {1465, 80}, // biology + {1472, 20}, // aircraft + {1473, 18}, // agree + {1474, 82}, // bishop + {1475, 21}, // airline + {1476, 22}, // airport + {1478, 81}, // birthday + {1512, 83}, // black + {1514, 35}, // ambition + {1516, 84}, // blanket + {1517, 24}, // alarm + {1518, 25}, // album + {1519, 34}, // amazing + {1526, 26}, // alcohol + {1537, 85}, // blessing + {1543, 27}, // alien + {1545, 86}, // blimp + {1546, 87}, // blind + {1548, 28}, // alive + {1564, 29}, // alpha + {1568, 36}, // amount + {1573, 30}, // already + {1583, 88}, // blue + {1585, 32}, // aluminum + {1586, 31}, // alto + {1587, 37}, // amuse + {1591, 33}, // always + {1615, 38}, // analysis + {1617, 48}, // apart + {1618, 39}, // anatomy + {1623, 40}, // ancestor + {1624, 41}, // ancient + {1629, 89}, // body + {1643, 42}, // angel + {1645, 44}, // animal + {1647, 43}, // angry + {1658, 90}, // bolt + {1674, 91}, // boring + {1676, 92}, // born + {1679, 45}, // answer + {1681, 49}, // aquatic + {1683, 46}, // antenna + {1684, 93}, // both + {1686, 94}, // boundary + {1694, 47}, // anxiety + {1712, 95}, // bracelet + {1716, 96}, // branch + {1718, 97}, // brave + {1721, 50}, // arcade + {1731, 98}, // breathe + {1736, 51}, // arena + {1743, 99}, // briefing + {1748, 52}, // argue + {1753, 53}, // armed + {1763, 56}, // aspect + {1765, 100}, // broken + {1768, 101}, // brother + {1769, 102}, // browser + {1784, 54}, // artist + {1789, 55}, // artwork + {1824, 104}, // budget + {1825, 103}, // bucket + {1828, 57}, // auction + {1837, 60}, // average + {1841, 61}, // aviation + {1845, 105}, // building + {1848, 58}, // august + {1851, 106}, // bulb + {1854, 107}, // bulge + {1856, 108}, // bumpy + {1862, 109}, // bundle + {1864, 62}, // avoid + {1868, 59}, // aunt + {1872, 110}, // burden + {1876, 111}, // burning + {1879, 112}, // busy + {1893, 113}, // buyer + {1917, 63}, // award + {1919, 64}, // away + {1947, 65}, // axis + {1953, 66}, // axle + {2143, 114}, // cage + {2147, 185}, // daisy + {2151, 186}, // damage + {2152, 115}, // calcium + {2153, 116}, // camera + {2156, 117}, // campus + {2161, 119}, // capacity + {2162, 187}, // dance + {2164, 120}, // capital + {2168, 121}, // capture + {2169, 118}, // canyon + {2171, 122}, // carbon + {2172, 123}, // cards + {2173, 124}, // careful + {2174, 125}, // cargo + {2175, 188}, // darkness + {2176, 126}, // carpet + {2178, 127}, // carve + {2181, 189}, // database + {2183, 128}, // category + {2184, 190}, // daughter + {2187, 129}, // cause + {2312, 191}, // deadline + {2315, 192}, // deal + {2317, 193}, // debris + {2318, 194}, // debut + {2323, 195}, // decent + {2324, 196}, // decision + {2325, 197}, // declare + {2326, 198}, // decorate + {2327, 199}, // decrease + {2345, 130}, // ceiling + {2351, 201}, // demand + {2354, 200}, // deliver + {2361, 204}, // depart + {2363, 205}, // depend + {2364, 206}, // depict + {2365, 207}, // deploy + {2367, 202}, // density + {2368, 131}, // center + {2369, 203}, // deny + {2371, 132}, // ceramic + {2372, 208}, // describe + {2373, 209}, // desert + {2374, 210}, // desire + {2375, 211}, // desktop + {2378, 212}, // destroy + {2381, 213}, // detailed + {2383, 214}, // detect + {2384, 215}, // device + {2386, 216}, // devote + {2414, 217}, // diagnose + {2415, 133}, // champion + {2416, 134}, // change + {2417, 135}, // charity + {2428, 218}, // dictate + {2432, 136}, // check + {2435, 137}, // chemical + {2437, 138}, // chest + {2438, 219}, // diet + {2439, 139}, // chew + {2453, 220}, // dilemma + {2454, 221}, // diminish + {2463, 141}, // cinema + {2464, 222}, // dining + {2465, 223}, // diploma + {2471, 224}, // disaster + {2472, 225}, // discuss + {2473, 226}, // disease + {2474, 227}, // dish + {2475, 228}, // dismiss + {2476, 229}, // display + {2478, 230}, // distance + {2481, 140}, // chubby + {2483, 231}, // dive + {2484, 142}, // civil + {2486, 232}, // divorce + {2517, 143}, // class + {2519, 144}, // clay + {2531, 145}, // cleanup + {2543, 146}, // client + {2545, 147}, // climate + {2546, 148}, // clinic + {2562, 149}, // clock + {2564, 150}, // clogs + {2567, 151}, // closet + {2568, 152}, // clothes + {2581, 153}, // club + {2587, 154}, // cluster + {2615, 155}, // coal + {2617, 156}, // coastal + {2624, 157}, // coding + {2628, 233}, // document + {2651, 234}, // domain + {2653, 235}, // domestic + {2654, 236}, // dominant + {2656, 159}, // company + {2658, 158}, // column + {2676, 160}, // corner + {2678, 161}, // costume + {2683, 164}, // cover + {2684, 237}, // dough + {2686, 162}, // counter + {2687, 163}, // course + {2691, 165}, // cowboy + {2696, 238}, // downtown + {2712, 166}, // cradle + {2713, 167}, // craft + {2714, 239}, // dragon + {2715, 240}, // dramatic + {2719, 168}, // crazy + {2731, 241}, // dream + {2732, 169}, // credit + {2737, 242}, // dress + {2742, 170}, // cricket + {2743, 243}, // drift + {2745, 171}, // criminal + {2746, 244}, // drink + {2747, 172}, // crisis + {2748, 173}, // critical + {2768, 245}, // drove + {2769, 174}, // crowd + {2782, 175}, // crucial + {2784, 246}, // drug + {2786, 176}, // crunch + {2787, 177}, // crush + {2793, 247}, // dryer + {2797, 178}, // crystal + {2814, 179}, // cubic + {2825, 248}, // duckling + {2853, 249}, // duke + {2858, 180}, // cultural + {2871, 250}, // duration + {2874, 181}, // curious + {2875, 182}, // curly + {2878, 183}, // custody + {2917, 251}, // dwarf + {2954, 184}, // cylinder + {2961, 252}, // dynamic + {3124, 323}, // facility + {3128, 324}, // fact + {3145, 325}, // failure + {3146, 326}, // faint + {3153, 327}, // fake + {3154, 329}, // family + {3156, 330}, // famous + {3157, 328}, // false + {3162, 331}, // fancy + {3164, 332}, // fangs + {3168, 333}, // fantasy + {3173, 255}, // easel + {3175, 253}, // early + {3178, 254}, // earth + {3179, 256}, // easy + {3181, 334}, // fatal + {3184, 335}, // fatigue + {3186, 336}, // favorite + {3196, 337}, // fawn + {3243, 260}, // edge + {3246, 257}, // echo + {3248, 261}, // editor + {3254, 258}, // eclipse + {3265, 259}, // ecology + {3282, 262}, // educate + {3413, 338}, // fiber + {3428, 339}, // fiction + {3458, 340}, // filter + {3461, 341}, // finance + {3462, 342}, // findings + {3464, 343}, // finger + {3472, 346}, // fiscal + {3473, 344}, // firefly + {3474, 347}, // fishing + {3475, 345}, // firm + {3484, 263}, // either + {3486, 348}, // fitness + {3514, 273}, // email + {3515, 349}, // flame + {3516, 264}, // elbow + {3517, 350}, // flash + {3518, 351}, // flavor + {3523, 265}, // elder + {3531, 352}, // flea + {3532, 266}, // election + {3534, 267}, // elegant + {3535, 268}, // element + {3536, 269}, // elephant + {3537, 274}, // emerald + {3538, 270}, // elevator + {3539, 353}, // flexible + {3546, 354}, // flip + {3547, 275}, // emission + {3548, 271}, // elite + {3561, 355}, // float + {3563, 276}, // emperor + {3564, 277}, // emphasis + {3565, 278}, // employer + {3567, 356}, // floral + {3568, 279}, // empty + {3573, 272}, // else + {3583, 357}, // fluff + {3624, 280}, // ending + {3625, 281}, // endless + {3626, 282}, // endorse + {3628, 358}, // focus + {3635, 283}, // enemy + {3636, 285}, // enforce + {3637, 284}, // energy + {3641, 286}, // engage + {3642, 292}, // epidemic + {3646, 287}, // enjoy + {3647, 293}, // episode + {3651, 288}, // enlarge + {3671, 359}, // forbid + {3672, 360}, // force + {3673, 361}, // forecast + {3674, 362}, // forget + {3675, 363}, // formal + {3678, 364}, // fortune + {3679, 365}, // forward + {3681, 294}, // equation + {3683, 290}, // envelope + {3684, 295}, // equip + {3686, 366}, // founder + {3687, 289}, // entrance + {3689, 291}, // envy + {3712, 367}, // fraction + {3714, 368}, // fragment + {3717, 296}, // eraser + {3721, 298}, // escape + {3736, 369}, // frequent + {3737, 370}, // freshman + {3741, 371}, // friar + {3742, 372}, // fridge + {3743, 373}, // friendly + {3762, 297}, // erode + {3767, 374}, // frost + {3768, 375}, // froth + {3769, 376}, // frozen + {3781, 299}, // estate + {3784, 300}, // estimate + {3815, 301}, // evaluate + {3836, 302}, // evening + {3842, 303}, // evidence + {3845, 304}, // evil + {3853, 377}, // fumes + {3862, 378}, // funding + {3865, 305}, // evoke + {3873, 380}, // fused + {3875, 379}, // furl + {3912, 306}, // exact + {3915, 307}, // example + {3923, 308}, // exceed + {3924, 309}, // exchange + {3925, 310}, // exclude + {3928, 311}, // excuse + {3931, 322}, // eyebrow + {3932, 312}, // execute + {3937, 313}, // exercise + {3941, 314}, // exhaust + {3961, 316}, // expand + {3963, 317}, // expect + {3965, 318}, // explain + {3967, 319}, // express + {3968, 315}, // exotic + {3983, 320}, // extend + {3987, 321}, // extra + {4125, 483}, // jacket + {4147, 420}, // hairy + {4151, 381}, // galaxy + {4153, 382}, // game + {4157, 421}, // hamster + {4162, 422}, // hand + {4164, 423}, // hanger + {4171, 383}, // garbage + {4172, 384}, // garden + {4175, 385}, // garlic + {4176, 386}, // gasoline + {4178, 424}, // harvest + {4183, 425}, // have + {4184, 387}, // gather + {4186, 426}, // havoc + {4191, 428}, // hazard + {4195, 427}, // hawk + {4231, 452}, // idea + {4236, 453}, // identify + {4253, 454}, // idle + {4312, 429}, // headset + {4315, 430}, // health + {4317, 431}, // hearing + {4318, 432}, // heat + {4356, 433}, // helpful + {4363, 388}, // general + {4364, 389}, // genius + {4365, 392}, // geology + {4367, 390}, // genre + {4368, 391}, // genuine + {4371, 434}, // herald + {4372, 435}, // herd + {4374, 436}, // hesitate + {4375, 484}, // jerky + {4378, 393}, // gesture + {4393, 485}, // jewelry + {4512, 394}, // glad + {4514, 455}, // image + {4516, 395}, // glance + {4517, 396}, // glasses + {4536, 397}, // glen + {4545, 398}, // glimpse + {4561, 456}, // impact + {4565, 457}, // imply + {4567, 458}, // improve + {4568, 459}, // impulse + {4616, 437}, // hobo + {4618, 399}, // goat + {4623, 463}, // index + {4624, 464}, // indicate + {4625, 460}, // include + {4626, 461}, // income + {4627, 462}, // increase + {4628, 465}, // industry + {4631, 466}, // infant + {4636, 467}, // inform + {4643, 468}, // inherit + {4646, 486}, // join + {4648, 469}, // injury + {4651, 470}, // inmate + {4652, 400}, // golden + {4653, 440}, // home + {4654, 438}, // holiday + {4659, 439}, // holy + {4673, 471}, // insect + {4674, 472}, // inside + {4675, 441}, // hormone + {4676, 442}, // hospital + {4678, 473}, // install + {4681, 476}, // invasion + {4683, 474}, // intend + {4684, 475}, // intimate + {4686, 477}, // involve + {4687, 443}, // hour + {4712, 401}, // graduate + {4716, 402}, // grant + {4717, 403}, // grasp + {4718, 404}, // gravity + {4719, 405}, // gray + {4731, 406}, // greatest + {4743, 407}, // grief + {4745, 408}, // grill + {4746, 409}, // grin + {4747, 478}, // iris + {4751, 479}, // island + {4762, 410}, // grocery + {4765, 480}, // isolate + {4767, 411}, // gross + {4768, 412}, // group + {4769, 413}, // grownup + {4785, 414}, // grumpy + {4817, 415}, // guard + {4824, 487}, // judicial + {4835, 481}, // item + {4837, 416}, // guest + {4842, 488}, // juice + {4843, 444}, // huge + {4845, 417}, // guilt + {4848, 418}, // guitar + {4851, 445}, // human + {4854, 446}, // humidity + {4856, 489}, // jump + {4857, 419}, // gums + {4862, 490}, // junction + {4864, 491}, // junior + {4865, 492}, // junk + {4867, 482}, // ivory + {4868, 447}, // hunting + {4871, 448}, // husband + {4874, 449}, // hush + {4875, 450}, // husky + {4878, 494}, // justice + {4879, 493}, // jury + {4917, 451}, // hybrid + {5123, 502}, // laden + {5124, 549}, // machine + {5125, 503}, // ladle + {5129, 504}, // ladybug + {5141, 550}, // magazine + {5142, 551}, // maiden + {5145, 552}, // mailman + {5146, 553}, // main + {5147, 505}, // lair + {5151, 556}, // mama + {5153, 554}, // makeup + {5154, 555}, // making + {5156, 506}, // lamp + {5161, 557}, // manager + {5162, 558}, // mandate + {5164, 507}, // language + {5167, 559}, // mansion + {5168, 560}, // manual + {5171, 561}, // marathon + {5172, 562}, // march + {5173, 509}, // laser + {5174, 508}, // large + {5175, 563}, // market + {5176, 565}, // mason + {5178, 564}, // marvel + {5183, 566}, // material + {5184, 567}, // math + {5186, 510}, // laundry + {5194, 568}, // maximum + {5196, 569}, // mayor + {5197, 511}, // lawsuit + {5312, 512}, // leader + {5313, 513}, // leaf + {5316, 570}, // meaning + {5317, 514}, // learn + {5318, 515}, // leaves + {5321, 571}, // medal + {5324, 572}, // medical + {5328, 516}, // lecture + {5341, 517}, // legal + {5343, 518}, // legend + {5347, 519}, // legs + {5351, 573}, // member + {5356, 574}, // memory + {5362, 520}, // lend + {5364, 521}, // length + {5368, 575}, // mental + {5372, 576}, // merchant + {5374, 577}, // merit + {5376, 495}, // kernel + {5383, 522}, // level + {5384, 578}, // method + {5387, 579}, // metric + {5391, 496}, // keyboard + {5413, 523}, // liberty + {5417, 524}, // library + {5423, 525}, // license + {5426, 497}, // kidney + {5427, 580}, // midst + {5438, 526}, // lift + {5451, 528}, // lilac + {5452, 581}, // mild + {5453, 527}, // likely + {5454, 582}, // military + {5459, 529}, // lily + {5462, 498}, // kind + {5463, 583}, // mineral + {5464, 584}, // minister + {5467, 530}, // lips + {5468, 531}, // liquid + {5471, 585}, // miracle + {5478, 532}, // listen + {5482, 499}, // kitchen + {5483, 533}, // literary + {5484, 534}, // living + {5491, 535}, // lizard + {5493, 586}, // mixed + {5498, 587}, // mixture + {5613, 537}, // lobe + {5614, 588}, // mobile + {5616, 536}, // loan + {5621, 538}, // location + {5623, 589}, // modern + {5624, 590}, // modify + {5643, 500}, // knife + {5647, 591}, // moisture + {5648, 501}, // knit + {5653, 592}, // moment + {5674, 539}, // losing + {5676, 593}, // morning + {5678, 594}, // mortgage + {5682, 540}, // loud + {5683, 598}, // move + {5684, 595}, // mother + {5686, 596}, // mountain + {5687, 597}, // mouse + {5691, 541}, // loyalty + {5824, 599}, // much + {5825, 542}, // luck + {5853, 600}, // mule + {5858, 601}, // multiple + {5861, 543}, // lunar + {5862, 544}, // lunch + {5864, 545}, // lungs + {5872, 602}, // muscle + {5873, 603}, // museum + {5874, 604}, // music + {5878, 605}, // mustang + {5898, 546}, // luxury + {5946, 547}, // lying + {5974, 548}, // lyrics + {6123, 636}, // paces + {6124, 637}, // pacific + {6125, 638}, // package + {6137, 618}, // obesity + {6141, 641}, // pajamas + {6142, 639}, // paid + {6143, 619}, // object + {6145, 606}, // nail + {6146, 640}, // painting + {6161, 644}, // papa + {6162, 642}, // pancake + {6163, 645}, // paper + {6168, 643}, // pants + {6172, 646}, // parcel + {6173, 620}, // observe + {6174, 617}, // oasis + {6175, 647}, // parking + {6178, 648}, // party + {6181, 621}, // obtain + {6183, 649}, // patent + {6184, 607}, // national + {6187, 650}, // patrol + {6195, 651}, // payment + {6197, 652}, // payroll + {6231, 622}, // ocean + {6312, 653}, // peaceful + {6316, 654}, // peanut + {6317, 655}, // peasant + {6321, 656}, // pecan + {6325, 608}, // necklace + {6341, 609}, // negative + {6361, 657}, // penalty + {6362, 658}, // pencil + {6372, 659}, // percent + {6373, 660}, // perfect + {6375, 661}, // permit + {6378, 610}, // nervous + {6383, 623}, // often + {6384, 662}, // petition + {6389, 611}, // network + {6397, 612}, // news + {6416, 663}, // phantom + {6417, 664}, // pharmacy + {6425, 668}, // pickup + {6428, 669}, // picture + {6432, 670}, // piece + {6453, 671}, // pile + {6463, 673}, // pipeline + {6465, 672}, // pink + {6468, 665}, // photo + {6471, 666}, // phrase + {6478, 674}, // pistol + {6482, 675}, // pitch + {6497, 667}, // physics + {6514, 676}, // plains + {6516, 677}, // plan + {6517, 678}, // plastic + {6518, 679}, // platform + {6519, 680}, // playoff + {6531, 681}, // pleasure + {6548, 625}, // omit + {6568, 682}, // plot + {6586, 683}, // plunge + {6595, 624}, // olympic + {6712, 684}, // practice + {6714, 628}, // orbit + {6715, 626}, // oral + {6716, 627}, // orange + {6719, 685}, // prayer + {6723, 629}, // order + {6724, 630}, // ordinary + {6731, 686}, // preach + {6732, 687}, // predator + {6734, 688}, // pregnant + {6735, 689}, // premium + {6736, 690}, // prepare + {6737, 691}, // presence + {6738, 692}, // prevent + {6741, 631}, // organize + {6743, 693}, // priest + {6745, 694}, // primary + {6746, 695}, // priority + {6747, 696}, // prisoner + {6748, 697}, // privacy + {6749, 698}, // prize + {6761, 699}, // problem + {6762, 700}, // process + {6763, 701}, // profile + {6764, 702}, // program + {6765, 703}, // promise + {6767, 704}, // prospect + {6768, 705}, // provide + {6786, 706}, // prune + {6815, 707}, // public + {6816, 716}, // quantity + {6817, 717}, // quarter + {6825, 613}, // nuclear + {6836, 633}, // oven + {6837, 634}, // overall + {6842, 718}, // quick + {6843, 719}, // quiet + {6851, 614}, // numb + {6853, 615}, // numerous + {6856, 709}, // pumps + {6857, 708}, // pulse + {6861, 712}, // pupal + {6862, 632}, // ounce + {6864, 710}, // punish + {6869, 711}, // puny + {6872, 713}, // purchase + {6876, 714}, // purple + {6956, 616}, // nylon + {6963, 635}, // owner + {6984, 715}, // python + {7121, 722}, // radar + {7123, 720}, // race + {7124, 721}, // racism + {7125, 775}, // sack + {7131, 776}, // safari + {7145, 723}, // railroad + {7146, 724}, // rainbow + {7147, 725}, // raisin + {7151, 777}, // salary + {7156, 778}, // salon + {7158, 779}, // salt + {7162, 726}, // random + {7164, 728}, // rapids + {7165, 727}, // ranked + {7176, 729}, // raspy + {7183, 782}, // saver + {7184, 780}, // satisfy + {7186, 781}, // satoshi + {7197, 783}, // says + {7216, 784}, // scandal + {7217, 785}, // scared + {7218, 786}, // scatter + {7236, 787}, // scene + {7243, 789}, // science + {7246, 788}, // scholar + {7268, 790}, // scout + {7271, 791}, // scramble + {7273, 792}, // screw + {7274, 793}, // script + {7276, 794}, // scroll + {7312, 730}, // reaction + {7313, 795}, // seafood + {7315, 731}, // realize + {7316, 732}, // rebound + {7317, 796}, // season + {7318, 733}, // rebuild + {7321, 734}, // recall + {7323, 735}, // receiver + {7326, 736}, // recover + {7327, 797}, // secret + {7328, 798}, // security + {7343, 739}, // reject + {7345, 799}, // segment + {7347, 737}, // regret + {7348, 738}, // regular + {7351, 740}, // relate + {7353, 741}, // remember + {7354, 742}, // remind + {7356, 743}, // remove + {7361, 745}, // repair + {7362, 744}, // render + {7363, 746}, // repeat + {7364, 800}, // senior + {7365, 747}, // replace + {7368, 748}, // require + {7372, 749}, // rescue + {7373, 750}, // research + {7374, 751}, // resident + {7376, 752}, // response + {7378, 753}, // result + {7381, 754}, // retailer + {7383, 757}, // revenue + {7384, 758}, // review + {7386, 756}, // reunion + {7387, 755}, // retreat + {7391, 759}, // reward + {7412, 801}, // shadow + {7413, 802}, // shaft + {7415, 803}, // shame + {7416, 804}, // shaped + {7417, 805}, // sharp + {7423, 811}, // sidewalk + {7424, 762}, // rich + {7435, 806}, // shelter + {7437, 807}, // sheriff + {7453, 812}, // silent + {7454, 814}, // similar + {7456, 815}, // simple + {7458, 813}, // silver + {7464, 816}, // single + {7467, 808}, // short + {7468, 809}, // should + {7474, 810}, // shrimp + {7478, 817}, // sister + {7481, 763}, // rival + {7483, 764}, // river + {7495, 760}, // rhyme + {7498, 761}, // rhythm + {7516, 820}, // slap + {7517, 827}, // smart + {7518, 821}, // slavery + {7531, 828}, // smear + {7532, 822}, // sled + {7535, 829}, // smell + {7542, 823}, // slice + {7545, 824}, // slim + {7546, 818}, // skin + {7547, 830}, // smirk + {7548, 831}, // smith + {7565, 832}, // smoking + {7569, 825}, // slow + {7584, 833}, // smug + {7586, 819}, // skunk + {7587, 826}, // slush + {7612, 843}, // space + {7614, 765}, // robin + {7615, 834}, // snake + {7616, 835}, // snapshot + {7617, 844}, // spark + {7624, 837}, // society + {7625, 766}, // rocky + {7631, 845}, // speak + {7632, 846}, // species + {7635, 847}, // spelling + {7636, 848}, // spend + {7638, 838}, // software + {7639, 849}, // spew + {7642, 850}, // spider + {7643, 836}, // sniff + {7645, 851}, // spill + {7646, 852}, // spine + {7647, 853}, // spirit + {7648, 854}, // spit + {7651, 767}, // romantic + {7652, 839}, // soldier + {7656, 768}, // romp + {7658, 840}, // solution + {7671, 855}, // spray + {7674, 856}, // sprinkle + {7678, 769}, // roster + {7681, 857}, // square + {7683, 858}, // squeeze + {7685, 841}, // soul + {7686, 770}, // round + {7687, 842}, // source + {7691, 771}, // royal + {7812, 859}, // stadium + {7813, 860}, // staff + {7814, 873}, // subject + {7815, 874}, // submit + {7816, 861}, // standard + {7817, 862}, // starting + {7818, 863}, // station + {7819, 864}, // stay + {7831, 865}, // steady + {7836, 866}, // step + {7841, 875}, // sugar + {7842, 867}, // stick + {7845, 868}, // stilt + {7846, 772}, // ruin + {7848, 876}, // suitable + {7853, 773}, // ruler + {7856, 774}, // rumor + {7863, 878}, // superior + {7865, 877}, // sunlight + {7867, 869}, // story + {7871, 870}, // strategy + {7873, 879}, // surface + {7874, 871}, // strike + {7876, 880}, // surprise + {7878, 881}, // survive + {7895, 872}, // style + {7931, 882}, // sweater + {7945, 883}, // swimming + {7946, 884}, // swing + {7948, 885}, // switch + {7951, 886}, // symbolic + {7956, 887}, // sympathy + {7962, 888}, // syndrome + {7978, 889}, // system + {8125, 890}, // tackle + {8126, 892}, // tadpole + {8128, 891}, // tactics + {8153, 893}, // talent + {8154, 965}, // valid + {8156, 967}, // vampire + {8158, 966}, // valuable + {8164, 968}, // vanish + {8174, 969}, // various + {8175, 894}, // task + {8178, 895}, // taste + {8184, 896}, // taught + {8194, 897}, // taxi + {8312, 898}, // teacher + {8315, 899}, // teammate + {8317, 900}, // teaspoon + {8341, 970}, // vegan + {8356, 901}, // temple + {8358, 971}, // velvet + {8361, 902}, // tenant + {8362, 903}, // tendency + {8367, 904}, // tension + {8368, 972}, // venture + {8372, 973}, // verdict + {8374, 974}, // verify + {8375, 905}, // terminal + {8378, 906}, // testify + {8379, 975}, // very + {8383, 976}, // veteran + {8393, 977}, // vexed + {8398, 907}, // texture + {8416, 908}, // thank + {8418, 909}, // that + {8423, 979}, // video + {8425, 917}, // ticket + {8428, 978}, // victim + {8429, 918}, // tidy + {8431, 910}, // theater + {8436, 911}, // theory + {8437, 912}, // therapy + {8439, 980}, // view + {8451, 919}, // timber + {8453, 920}, // timely + {8459, 946}, // ugly + {8464, 921}, // ting + {8465, 982}, // violence + {8467, 913}, // thorn + {8468, 981}, // vintage + {8471, 983}, // viral + {8473, 914}, // threaten + {8474, 984}, // visitor + {8478, 985}, // visual + {8481, 986}, // vitamins + {8485, 915}, // thumb + {8486, 916}, // thunder + {8517, 948}, // umbrella + {8584, 947}, // ultimate + {8621, 987}, // vocal + {8623, 950}, // undergo + {8626, 949}, // uncover + {8631, 951}, // unfair + {8636, 952}, // unfold + {8638, 922}, // tofu + {8641, 953}, // unhappy + {8642, 988}, // voice + {8643, 923}, // together + {8646, 954}, // union + {8647, 960}, // upgrade + {8648, 955}, // universe + {8653, 924}, // tolerate + {8654, 956}, // unkind + {8656, 957}, // unknown + {8658, 989}, // volume + {8678, 961}, // upstairs + {8681, 925}, // total + {8683, 990}, // voter + {8684, 991}, // voting + {8687, 958}, // unusual + {8694, 926}, // toxic + {8697, 959}, // unwrap + {8712, 927}, // tracks + {8713, 928}, // traffic + {8714, 929}, // training + {8716, 930}, // transfer + {8717, 931}, // trash + {8718, 932}, // traveler + {8731, 933}, // treat + {8736, 934}, // trend + {8737, 962}, // username + {8741, 935}, // trial + {8742, 936}, // tricycle + {8743, 963}, // usher + {8746, 937}, // trip + {8748, 938}, // triumph + {8768, 939}, // trouble + {8781, 964}, // usual + {8783, 940}, // true + {8787, 941}, // trust + {8942, 942}, // twice + {8946, 943}, // twin + {8963, 944}, // type + {8964, 945}, // typical + {9156, 992}, // walnut + {9175, 993}, // warmth + {9176, 994}, // warn + {9182, 995}, // watch + {9189, 996}, // wavy + {9312, 999}, // webcam + {9315, 997}, // wealthy + {9316, 998}, // weapon + {9317, 1019}, // year + {9352, 1000}, // welcome + {9353, 1001}, // welfare + {9356, 1020}, // yelp + {9376, 1023}, // zero + {9378, 1002}, // western + {9428, 1003}, // width + {9435, 1021}, // yield + {9452, 1004}, // wildlife + {9462, 1005}, // window + {9463, 1006}, // wine + {9472, 1008}, // wisdom + {9473, 1007}, // wireless + {9484, 1009}, // withdraw + {9487, 1010}, // wits + {9641, 1022}, // yoga + {9651, 1012}, // woman + {9653, 1011}, // wolf + {9675, 1013}, // work + {9678, 1014}, // worthy + {9716, 1015}, // wrap + {9747, 1016}, // wrist + {9748, 1017}, // writing + {9768, 1018}, // wrote +}; + +#endif diff --git a/applications/external/flipbip/scenes/flipbip_scene.c b/applications/external/flipbip/scenes/flipbip_scene.c new file mode 100644 index 000000000..d3656a868 --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene.c @@ -0,0 +1,30 @@ +#include "flipbip_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const flipbip_on_enter_handlers[])(void*) = { +#include "flipbip_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const flipbip_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "flipbip_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const flipbip_on_exit_handlers[])(void* context) = { +#include "flipbip_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers flipbip_scene_handlers = { + .on_enter_handlers = flipbip_on_enter_handlers, + .on_event_handlers = flipbip_on_event_handlers, + .on_exit_handlers = flipbip_on_exit_handlers, + .scene_num = FlipBipSceneNum, +}; diff --git a/applications/external/flipbip/scenes/flipbip_scene.h b/applications/external/flipbip/scenes/flipbip_scene.h new file mode 100644 index 000000000..fd49b44c0 --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FlipBipScene##id, +typedef enum { +#include "flipbip_scene_config.h" + FlipBipSceneNum, +} FlipBipScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers flipbip_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "flipbip_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "flipbip_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "flipbip_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/flipbip/scenes/flipbip_scene_config.h b/applications/external/flipbip/scenes/flipbip_scene_config.h new file mode 100644 index 000000000..a62832162 --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_config.h @@ -0,0 +1,4 @@ +ADD_SCENE(flipbip, startscreen, Startscreen) +ADD_SCENE(flipbip, menu, Menu) +ADD_SCENE(flipbip, scene_1, Scene_1) +ADD_SCENE(flipbip, settings, Settings) \ No newline at end of file diff --git a/applications/external/flipbip/scenes/flipbip_scene_menu.c b/applications/external/flipbip/scenes/flipbip_scene_menu.c new file mode 100644 index 000000000..04525909d --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_menu.c @@ -0,0 +1,130 @@ +#include "../flipbip.h" +#include "../helpers/flipbip_file.h" + +enum SubmenuIndex { + SubmenuIndexScene1BTC = 10, + SubmenuIndexScene1ETH, + SubmenuIndexScene1DOGE, + SubmenuIndexScene1New, + SubmenuIndexScene1Import, + SubmenuIndexSettings, +}; + +void flipbip_scene_menu_submenu_callback(void* context, uint32_t index) { + FlipBip* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void flipbip_scene_menu_on_enter(void* context) { + FlipBip* app = context; + + if(flipbip_has_file(FlipBipFileKey, NULL, false) && + flipbip_has_file(FlipBipFileDat, NULL, false)) { + submenu_add_item( + app->submenu, + "View BTC wallet", + SubmenuIndexScene1BTC, + flipbip_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "View ETH wallet", + SubmenuIndexScene1ETH, + flipbip_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "View DOGE wallet", + SubmenuIndexScene1DOGE, + flipbip_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Regenerate wallet", + SubmenuIndexScene1New, + flipbip_scene_menu_submenu_callback, + app); + } else { + submenu_add_item( + app->submenu, + "Generate new wallet", + SubmenuIndexScene1New, + flipbip_scene_menu_submenu_callback, + app); + } + submenu_add_item( + app->submenu, + "Import from mnemonic", + SubmenuIndexScene1Import, + flipbip_scene_menu_submenu_callback, + app); + + submenu_add_item( + app->submenu, "Settings", SubmenuIndexSettings, flipbip_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, FlipBipSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); +} + +bool flipbip_scene_menu_on_event(void* context, SceneManagerEvent event) { + FlipBip* app = context; + //UNUSED(app); + if(event.type == SceneManagerEventTypeBack) { + //exit app + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + return true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexScene1BTC) { + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + app->bip44_coin = FlipBipCoinBTC0; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1BTC); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1ETH) { + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + app->bip44_coin = FlipBipCoinETH60; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1ETH); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1DOGE) { + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + app->bip44_coin = FlipBipCoinDOGE3; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1DOGE); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1New) { + app->overwrite_saved_seed = 1; + app->import_from_mnemonic = 0; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1New); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1Import) { + app->import_from_mnemonic = 1; + app->input_state = FlipBipTextInputMnemonic; + text_input_set_header_text(app->text_input, "Enter mnemonic phrase"); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdTextInput); + return true; + } else if(event.event == SubmenuIndexSettings) { + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexSettings); + scene_manager_next_scene(app->scene_manager, FlipBipSceneSettings); + return true; + } + } + return false; +} + +void flipbip_scene_menu_on_exit(void* context) { + FlipBip* app = context; + submenu_reset(app->submenu); +} \ No newline at end of file diff --git a/applications/external/flipbip/scenes/flipbip_scene_scene_1.c b/applications/external/flipbip/scenes/flipbip_scene_scene_1.c new file mode 100644 index 000000000..6f4064cd4 --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_scene_1.c @@ -0,0 +1,50 @@ +#include "../flipbip.h" +#include "../helpers/flipbip_custom_event.h" +#include "../views/flipbip_scene_1.h" + +void flipbip_scene_1_callback(FlipBipCustomEvent event, void* context) { + furi_assert(context); + FlipBip* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void flipbip_scene_scene_1_on_enter(void* context) { + furi_assert(context); + FlipBip* app = context; + flipbip_scene_1_set_callback(app->flipbip_scene_1, flipbip_scene_1_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdScene1); +} + +bool flipbip_scene_scene_1_on_event(void* context, SceneManagerEvent event) { + FlipBip* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case FlipBipCustomEventScene1Left: + case FlipBipCustomEventScene1Right: + break; + case FlipBipCustomEventScene1Up: + case FlipBipCustomEventScene1Down: + break; + case FlipBipCustomEventScene1Back: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, FlipBipSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void flipbip_scene_scene_1_on_exit(void* context) { + FlipBip* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/external/flipbip/scenes/flipbip_scene_settings.c b/applications/external/flipbip/scenes/flipbip_scene_settings.c new file mode 100644 index 000000000..c743c97b8 --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_settings.c @@ -0,0 +1,141 @@ +#include "../flipbip.h" +#include +// From: lib/crypto +#include + +#define TEXT_LABEL_ON "ON" +#define TEXT_LABEL_OFF "OFF" + +const char* const haptic_text[2] = { + TEXT_LABEL_OFF, + TEXT_LABEL_ON, +}; +const uint32_t haptic_value[2] = { + FlipBipHapticOff, + FlipBipHapticOn, +}; + +const char* const led_text[2] = { + TEXT_LABEL_OFF, + TEXT_LABEL_ON, +}; +const uint32_t led_value[2] = { + FlipBipLedOff, + FlipBipLedOn, +}; + +const char* const bip39_strength_text[3] = { + "12", + "18", + "24", +}; +const uint32_t bip39_strength_value[3] = { + FlipBipStrength128, + FlipBipStrength192, + FlipBipStrength256, +}; + +const char* const passphrase_text[2] = { + TEXT_LABEL_OFF, + TEXT_LABEL_ON, +}; +const uint32_t passphrase_value[2] = { + FlipBipPassphraseOff, + FlipBipPassphraseOn, +}; + +static void flipbip_scene_settings_set_haptic(VariableItem* item) { + FlipBip* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, haptic_text[index]); + app->haptic = haptic_value[index]; +} + +static void flipbip_scene_settings_set_led(VariableItem* item) { + FlipBip* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, led_text[index]); + app->led = led_value[index]; +} + +static void flipbip_scene_settings_set_bip39_strength(VariableItem* item) { + FlipBip* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bip39_strength_text[index]); + app->bip39_strength = bip39_strength_value[index]; +} + +static void flipbip_scene_settings_set_passphrase(VariableItem* item) { + FlipBip* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, passphrase_text[index]); + app->passphrase = passphrase_value[index]; + + if(app->passphrase == FlipBipPassphraseOn) { + app->input_state = FlipBipTextInputPassphrase; + text_input_set_header_text(app->text_input, "Enter BIP39 passphrase"); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdTextInput); + } else { + memzero(app->passphrase_text, TEXT_BUFFER_SIZE); + } +} + +void flipbip_scene_settings_submenu_callback(void* context, uint32_t index) { + FlipBip* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void flipbip_scene_settings_on_enter(void* context) { + FlipBip* app = context; + VariableItem* item; + uint8_t value_index; + + // BIP39 strength + item = variable_item_list_add( + app->variable_item_list, "BIP39 Words:", 3, flipbip_scene_settings_set_bip39_strength, app); + value_index = value_index_uint32(app->bip39_strength, bip39_strength_value, 3); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, bip39_strength_text[value_index]); + + // Passphrase + item = variable_item_list_add( + app->variable_item_list, + "BIP39 Passphrase:", + 2, + flipbip_scene_settings_set_passphrase, + app); + value_index = value_index_uint32(app->passphrase, passphrase_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, passphrase_text[value_index]); + + // Vibro on/off + item = variable_item_list_add( + app->variable_item_list, "Vibro/Haptic:", 2, flipbip_scene_settings_set_haptic, app); + value_index = value_index_uint32(app->haptic, haptic_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, haptic_text[value_index]); + + // LED Effects on/off + item = variable_item_list_add( + app->variable_item_list, "LED FX:", 2, flipbip_scene_settings_set_led, app); + value_index = value_index_uint32(app->led, led_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, led_text[value_index]); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdSettings); +} + +bool flipbip_scene_settings_on_event(void* context, SceneManagerEvent event) { + FlipBip* app = context; + UNUSED(app); + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + } + return consumed; +} + +void flipbip_scene_settings_on_exit(void* context) { + FlipBip* app = context; + variable_item_list_set_selected_item(app->variable_item_list, 0); + variable_item_list_reset(app->variable_item_list); +} \ No newline at end of file diff --git a/applications/external/flipbip/scenes/flipbip_scene_startscreen.c b/applications/external/flipbip/scenes/flipbip_scene_startscreen.c new file mode 100644 index 000000000..a9cb8ba5f --- /dev/null +++ b/applications/external/flipbip/scenes/flipbip_scene_startscreen.c @@ -0,0 +1,55 @@ +#include "../flipbip.h" +#include "../helpers/flipbip_custom_event.h" +#include "../views/flipbip_startscreen.h" + +void flipbip_scene_startscreen_callback(FlipBipCustomEvent event, void* context) { + furi_assert(context); + FlipBip* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void flipbip_scene_startscreen_on_enter(void* context) { + furi_assert(context); + FlipBip* app = context; + flipbip_startscreen_set_callback( + app->flipbip_startscreen, flipbip_scene_startscreen_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdStartscreen); +} + +bool flipbip_scene_startscreen_on_event(void* context, SceneManagerEvent event) { + FlipBip* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case FlipBipCustomEventStartscreenLeft: + case FlipBipCustomEventStartscreenRight: + break; + case FlipBipCustomEventStartscreenUp: + case FlipBipCustomEventStartscreenDown: + break; + case FlipBipCustomEventStartscreenOk: + scene_manager_next_scene(app->scene_manager, FlipBipSceneMenu); + consumed = true; + break; + case FlipBipCustomEventStartscreenBack: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, FlipBipSceneStartscreen)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void flipbip_scene_startscreen_on_exit(void* context) { + FlipBip* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/external/flipbip/views/flipbip_scene_1.c b/applications/external/flipbip/views/flipbip_scene_1.c new file mode 100644 index 000000000..361e9c5b4 --- /dev/null +++ b/applications/external/flipbip/views/flipbip_scene_1.c @@ -0,0 +1,723 @@ +#include "../flipbip.h" +#include +#include +#include +#include +//#include +#include +#include +#include "flipbip_icons.h" +#include "../helpers/flipbip_haptic.h" +#include "../helpers/flipbip_led.h" +#include "../helpers/flipbip_string.h" +#include "../helpers/flipbip_file.h" +// From: /lib/crypto +#include +#include +#include +#include +#include + +#define DERIV_PURPOSE 44 +#define DERIV_ACCOUNT 0 +#define DERIV_CHANGE 0 + +#define MAX_ADDR_LEN 42 + 1 // 42 = max length of address + null terminator +#define NUM_ADDRS 6 + +#define PAGE_LOADING 0 +#define PAGE_INFO 1 +#define PAGE_MNEMONIC 2 +#define PAGE_SEED 3 +#define PAGE_XPRV_ROOT 4 +#define PAGE_XPRV_ACCT 5 +#define PAGE_XPUB_ACCT 6 +#define PAGE_XPRV_EXTD 7 +#define PAGE_XPUB_EXTD 8 +#define PAGE_ADDR_BEGIN 9 +#define PAGE_ADDR_END (PAGE_ADDR_BEGIN + NUM_ADDRS - 1) + +#define TEXT_LOADING "Loading..." +#define TEXT_NEW_WALLET "New wallet" +#define TEXT_DEFAULT_COIN "Coin" +#define TEXT_RECEIVE_ADDRESS "receive address:" +#define TEXT_DEFAULT_DERIV "m/44'/X'/0'/0" +const char* TEXT_INFO = "-Scroll pages with up/down-" + "p1,2) Mnemonic/Seed " + "p3) xprv Root Key " + "p4,5) xprv/xpub Accnt Keys" + "p6,7) xprv/xpub Extnd Keys" + "p8+) Receive Addresses "; + +// #define TEXT_SAVE_QR "Save QR" +#define TEXT_QRFILE_EXT ".qrcode" // 7 chars + 1 null + +// bip44_coin, xprv_version, xpub_version, addr_version, wif_version, addr_format +const uint32_t COIN_INFO_ARRAY[3][6] = { + {COIN_BTC, 0x0488ade4, 0x0488b21e, 0x00, 0x80, FlipBipCoinBTC0}, + {COIN_ETH, 0x0488ade4, 0x0488b21e, 0x00, 0x80, FlipBipCoinETH60}, + {COIN_DOGE, 0x02fac398, 0x02facafd, 0x1e, 0x9e, FlipBipCoinBTC0}}; + +// coin_name, derivation_path +const char* COIN_TEXT_ARRAY[3][3] = { + {"BTC", "m/44'/0'/0'/0", "bitcoin:"}, + {"ETH", "m/44'/60'/0'/0", "ethereum:"}, + {"DOGE", "m/44'/3'/0'/0", "dogecoin:"}}; + +struct FlipBipScene1 { + View* view; + FlipBipScene1Callback callback; + void* context; +}; +typedef struct { + int page; + int strength; + uint32_t coin; + bool overwrite; + bool mnemonic_only; + CONFIDENTIAL const char* mnemonic; + CONFIDENTIAL uint8_t seed[64]; + CONFIDENTIAL const HDNode* node; + CONFIDENTIAL const char* xprv_root; + CONFIDENTIAL const char* xprv_account; + CONFIDENTIAL const char* xpub_account; + CONFIDENTIAL const char* xprv_extended; + CONFIDENTIAL const char* xpub_extended; + char* recv_addresses[NUM_ADDRS]; +} FlipBipScene1Model; + +// Node for the receive address +static CONFIDENTIAL HDNode* s_addr_node = NULL; +// Generic display text +static CONFIDENTIAL char* s_disp_text1 = NULL; +static CONFIDENTIAL char* s_disp_text2 = NULL; +static CONFIDENTIAL char* s_disp_text3 = NULL; +static CONFIDENTIAL char* s_disp_text4 = NULL; +static CONFIDENTIAL char* s_disp_text5 = NULL; +static CONFIDENTIAL char* s_disp_text6 = NULL; +// Derivation path text +static const char* s_derivation_text = TEXT_DEFAULT_DERIV; +//static bool s_busy = false; + +void flipbip_scene_1_set_callback( + FlipBipScene1* instance, + FlipBipScene1Callback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +static void flipbip_scene_1_init_address( + char* addr_text, + const HDNode* node, + uint32_t coin_type, + uint32_t addr_index) { + //s_busy = true; + + // Buffer for address serialization + const size_t buflen = 40; + char buf[40 + 1] = {0}; + + // Use static node for address generation + memcpy(s_addr_node, node, sizeof(HDNode)); + memzero(addr_text, MAX_ADDR_LEN); + + hdnode_private_ckd(s_addr_node, addr_index); + hdnode_fill_public_key(s_addr_node); + + // coin info + // bip44_coin, xprv_version, xpub_version, addr_version, wif_version, addr_format + uint32_t coin_info[6] = {0}; + for(size_t i = 0; i < 6; i++) { + coin_info[i] = COIN_INFO_ARRAY[coin_type][i]; + } + + if(coin_info[5] == FlipBipCoinBTC0) { // BTC / DOGE style address + // BTC / DOGE style address + ecdsa_get_address( + s_addr_node->public_key, coin_info[3], HASHER_SHA2_RIPEMD, HASHER_SHA2D, buf, buflen); + strcpy(addr_text, buf); + + //ecdsa_get_wif(addr_node->private_key, WIF_VERSION, HASHER_SHA2D, buf, buflen); + + } else if(coin_info[5] == FlipBipCoinETH60) { // ETH + // ETH style address + hdnode_get_ethereum_pubkeyhash(s_addr_node, (uint8_t*)buf); + addr_text[0] = '0'; + addr_text[1] = 'x'; + // Convert the hash to a hex string + flipbip_btox((uint8_t*)buf, 20, addr_text + 2); + } + + // Clear the address node + memzero(s_addr_node, sizeof(HDNode)); + + //s_busy = false; +} + +static void flipbip_scene_1_draw_generic(const char* text, size_t line_len) { + // Split the text into parts + for(size_t si = 1; si <= 6; si++) { + char* ptr = NULL; + + if(si == 1) + ptr = s_disp_text1; + else if(si == 2) + ptr = s_disp_text2; + else if(si == 3) + ptr = s_disp_text3; + else if(si == 4) + ptr = s_disp_text4; + else if(si == 5) + ptr = s_disp_text5; + else if(si == 6) + ptr = s_disp_text6; + + memzero(ptr, 30 + 1); + if(line_len > 30) { + strncpy(ptr, text + ((si - 1) * 30), 30); + } else { + strncpy(ptr, text + ((si - 1) * line_len), line_len); + } + } +} + +static void flipbip_scene_1_draw_mnemonic(const char* mnemonic) { + // Delineate sections of the mnemonic every 4 words + const size_t mnemonic_working_len = strlen(mnemonic) + 1; + char* mnemonic_working = malloc(mnemonic_working_len); + strcpy(mnemonic_working, mnemonic); + int word = 0; + for(size_t i = 0; i < strlen(mnemonic_working); i++) { + if(mnemonic_working[i] == ' ') { + word++; + if(word % 4 == 0) { + mnemonic_working[i] = ','; + } + } + } + + // Split the mnemonic into parts + char* mnemonic_part = flipbip_strtok(mnemonic_working, ","); + int mi = 0; + while(mnemonic_part != NULL) { + char* ptr = NULL; + mi++; + + if(mi == 1) + ptr = s_disp_text1; + else if(mi == 2) + ptr = s_disp_text2; + else if(mi == 3) + ptr = s_disp_text3; + else if(mi == 4) + ptr = s_disp_text4; + else if(mi == 5) + ptr = s_disp_text5; + else if(mi == 6) + ptr = s_disp_text6; + + memzero(ptr, 30 + 1); + if(strlen(mnemonic_part) > 30) { + strncpy(ptr, mnemonic_part, 30); + } else { + strncpy(ptr, mnemonic_part, strlen(mnemonic_part)); + } + + mnemonic_part = flipbip_strtok(NULL, ","); + } + + // Free the working mnemonic memory + memzero(mnemonic_working, mnemonic_working_len); + free(mnemonic_working); +} + +static void flipbip_scene_1_draw_seed(FlipBipScene1Model* const model) { + const size_t seed_working_len = 64 * 2 + 1; + char* seed_working = malloc(seed_working_len); + // Convert the seed to a hex string + flipbip_btox(model->seed, 64, seed_working); + + flipbip_scene_1_draw_generic(seed_working, 22); + + // Free the working seed memory + memzero(seed_working, seed_working_len); + free(seed_working); +} + +static void flipbip_scene_1_clear_text() { + memzero((void*)s_disp_text1, 30 + 1); + memzero((void*)s_disp_text2, 30 + 1); + memzero((void*)s_disp_text3, 30 + 1); + memzero((void*)s_disp_text4, 30 + 1); + memzero((void*)s_disp_text5, 30 + 1); + memzero((void*)s_disp_text6, 30 + 1); +} + +void flipbip_scene_1_draw(Canvas* canvas, FlipBipScene1Model* model) { + //UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + flipbip_scene_1_clear_text(); + if(model->page == PAGE_INFO) { + flipbip_scene_1_draw_generic(TEXT_INFO, 27); + } else if(model->page == PAGE_MNEMONIC) { + flipbip_scene_1_draw_mnemonic(model->mnemonic); + } else if(model->page == PAGE_SEED) { + flipbip_scene_1_draw_seed(model); + } else if(model->page == PAGE_XPRV_ROOT) { + flipbip_scene_1_draw_generic(model->xprv_root, 20); + } else if(model->page == PAGE_XPRV_ACCT) { + flipbip_scene_1_draw_generic(model->xprv_account, 20); + } else if(model->page == PAGE_XPUB_ACCT) { + flipbip_scene_1_draw_generic(model->xpub_account, 20); + } else if(model->page == PAGE_XPRV_EXTD) { + flipbip_scene_1_draw_generic(model->xprv_extended, 20); + } else if(model->page == PAGE_XPUB_EXTD) { + flipbip_scene_1_draw_generic(model->xpub_extended, 20); + } else if(model->page >= PAGE_ADDR_BEGIN && model->page <= PAGE_ADDR_END) { + uint32_t line_len = 12; + if(model->coin == FlipBipCoinETH60) { + line_len = 14; + } + flipbip_scene_1_draw_generic( + model->recv_addresses[model->page - PAGE_ADDR_BEGIN], line_len); + } + + if(model->page == PAGE_LOADING) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, TEXT_LOADING); + canvas_draw_str(canvas, 7, 30, s_derivation_text); + canvas_draw_icon(canvas, 86, 25, &I_Keychain_39x36); + } else if(model->page >= PAGE_ADDR_BEGIN && model->page <= PAGE_ADDR_END) { + // draw address header + canvas_set_font(canvas, FontSecondary); + // coin_name, derivation_path + const char* receive_text = COIN_TEXT_ARRAY[model->coin][0]; + if(receive_text == NULL) { + receive_text = TEXT_DEFAULT_COIN; + } + const size_t receive_len = strlen(receive_text) * 7; + canvas_draw_str_aligned(canvas, 2, 2, AlignLeft, AlignTop, receive_text); + canvas_draw_str_aligned( + canvas, receive_len + 1, 2, AlignLeft, AlignTop, TEXT_RECEIVE_ADDRESS); + + // draw address number + const unsigned char addr_num[1] = {(unsigned char)(model->page - PAGE_ADDR_BEGIN)}; + char addr_num_text[3] = {0}; + flipbip_btox(addr_num, 1, addr_num_text); + addr_num_text[0] = '/'; + canvas_draw_str_aligned(canvas, 125, 2, AlignRight, AlignTop, addr_num_text); + + // draw QR code file path + char addr_name_text[14] = {0}; + strcpy(addr_name_text, COIN_TEXT_ARRAY[model->coin][0]); + flipbip_btox(addr_num, 1, addr_name_text + strlen(addr_name_text)); + strcpy(addr_name_text + strlen(addr_name_text), TEXT_QRFILE_EXT); + //elements_button_right(canvas, addr_name_text); + canvas_draw_str_aligned(canvas, 125, 53, AlignRight, AlignTop, addr_name_text); + + // draw address + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 7, 22, s_disp_text1); + canvas_draw_str(canvas, 7, 34, s_disp_text2); + canvas_draw_str(canvas, 7, 46, s_disp_text3); + canvas_draw_str(canvas, 7, 58, s_disp_text4); + } else { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 1, 2, AlignLeft, AlignTop, s_disp_text1); + canvas_draw_str_aligned(canvas, 1, 12, AlignLeft, AlignTop, s_disp_text2); + canvas_draw_str_aligned(canvas, 1, 22, AlignLeft, AlignTop, s_disp_text3); + canvas_draw_str_aligned(canvas, 1, 32, AlignLeft, AlignTop, s_disp_text4); + canvas_draw_str_aligned(canvas, 1, 42, AlignLeft, AlignTop, s_disp_text5); + canvas_draw_str_aligned(canvas, 1, 52, AlignLeft, AlignTop, s_disp_text6); + } +} + +static int flipbip_scene_1_model_init( + FlipBipScene1Model* const model, + const int strength, + const uint32_t coin, + const bool overwrite, + const char* passphrase_text) { + model->page = PAGE_LOADING; + model->mnemonic_only = false; + model->strength = strength; + model->coin = coin; + model->overwrite = overwrite; + + // Allocate memory for mnemonic + char* mnemonic = malloc(TEXT_BUFFER_SIZE); + memzero(mnemonic, TEXT_BUFFER_SIZE); + + // Check if the mnemonic key & data is already saved in persistent storage, or overwrite is true + if(overwrite || (!flipbip_has_file(FlipBipFileKey, NULL, false) && + !flipbip_has_file(FlipBipFileDat, NULL, false))) { + // Set mnemonic only mode + model->mnemonic_only = true; + // Generate a random mnemonic using trezor-crypto + const char* mnemonic_gen = mnemonic_generate(strength); + // Check if the mnemonic is valid + if(mnemonic_check(mnemonic_gen) == 0) + return FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error + // Save the mnemonic to persistent storage + else if(!flipbip_save_file_secure(mnemonic_gen)) + return FlipBipStatusSaveError; // 12 = save error + // Clear the generated mnemonic from memory + mnemonic_clear(); + } + + // Load the mnemonic from persistent storage + if(!flipbip_load_file_secure(mnemonic)) { + // Set mnemonic only mode for this error for memory cleanup purposes + model->mnemonic_only = true; + return FlipBipStatusLoadError; // 11 = load error + } + model->mnemonic = mnemonic; + // Check if the mnemonic is valid + if(mnemonic_check(model->mnemonic) == 0) { + // Set mnemonic only mode for this error for memory cleanup purposes + model->mnemonic_only = true; + return FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error + } + + // test return values + //model->mnemonic_only = true; + //return FlipBipStatusMnemonicCheckError; // 13 = mnemonic check error + + // if we are only generating the mnemonic, return + if(model->mnemonic_only) { + return FlipBipStatusReturn; // 10 = mnemonic only, return from parent + } + + // Generate a BIP39 seed from the mnemonic + mnemonic_to_seed(model->mnemonic, passphrase_text, model->seed, 0); + + // Generate a BIP32 root HD node from the mnemonic + HDNode* root = malloc(sizeof(HDNode)); + hdnode_from_seed(model->seed, 64, SECP256K1_NAME, root); + + // buffer for key serialization + const size_t buflen = 128; + char buf[128 + 1] = {0}; + + // coin info + // bip44_coin, xprv_version, xpub_version, addr_version, wif_version, addr_format + uint32_t coin_info[6] = {0}; + for(size_t i = 0; i < 6; i++) { + coin_info[i] = COIN_INFO_ARRAY[coin][i]; + } + + // root + uint32_t fingerprint = 0; + hdnode_serialize_private(root, fingerprint, coin_info[1], buf, buflen); + char* xprv_root = malloc(buflen + 1); + strncpy(xprv_root, buf, buflen); + model->xprv_root = xprv_root; + + HDNode* node = root; + + // purpose m/44' + fingerprint = hdnode_fingerprint(node); + hdnode_private_ckd_prime(node, DERIV_PURPOSE); // purpose + + // coin m/44'/0' or m/44'/60' + fingerprint = hdnode_fingerprint(node); + hdnode_private_ckd_prime(node, coin_info[0]); // coin + + // account m/44'/0'/0' or m/44'/60'/0' + fingerprint = hdnode_fingerprint(node); + hdnode_private_ckd_prime(node, DERIV_ACCOUNT); // account + + hdnode_serialize_private(node, fingerprint, coin_info[1], buf, buflen); + char* xprv_acc = malloc(buflen + 1); + strncpy(xprv_acc, buf, buflen); + model->xprv_account = xprv_acc; + + hdnode_serialize_public(node, fingerprint, coin_info[2], buf, buflen); + char* xpub_acc = malloc(buflen + 1); + strncpy(xpub_acc, buf, buflen); + model->xpub_account = xpub_acc; + + // external/internal (change) m/44'/0'/0'/0 or m/44'/60'/0'/0 + fingerprint = hdnode_fingerprint(node); + hdnode_private_ckd(node, DERIV_CHANGE); // external/internal (change) + + hdnode_serialize_private(node, fingerprint, coin_info[1], buf, buflen); + char* xprv_ext = malloc(buflen + 1); + strncpy(xprv_ext, buf, buflen); + model->xprv_extended = xprv_ext; + + hdnode_serialize_public(node, fingerprint, coin_info[2], buf, buflen); + char* xpub_ext = malloc(buflen + 1); + strncpy(xpub_ext, buf, buflen); + model->xpub_extended = xpub_ext; + + model->node = node; + + // Initialize addresses + for(uint8_t a = 0; a < NUM_ADDRS; a++) { + model->recv_addresses[a] = malloc(MAX_ADDR_LEN); + memzero(model->recv_addresses[a], MAX_ADDR_LEN); + flipbip_scene_1_init_address(model->recv_addresses[a], node, coin, a); + + // Save QR code file + memzero(buf, buflen); + strcpy(buf, COIN_TEXT_ARRAY[coin][0]); + const unsigned char addr_num[1] = {a}; + flipbip_btox(addr_num, 1, buf + strlen(buf)); + strcpy(buf + strlen(buf), TEXT_QRFILE_EXT); + flipbip_save_qrfile(COIN_TEXT_ARRAY[coin][2], model->recv_addresses[a], buf); + memzero(buf, buflen); + } + + model->page = PAGE_INFO; + +#if USE_BIP39_CACHE + // Clear the BIP39 cache + bip39_cache_clear(); +#endif + + // 0 = success + return FlipBipStatusSuccess; +} + +bool flipbip_scene_1_input(InputEvent* event, void* context) { + furi_assert(context); + FlipBipScene1* instance = context; + + // Ignore input if busy + // if(s_busy) { + // return false; + // } + + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + UNUSED(model); + instance->callback(FlipBipCustomEventScene1Back, instance->context); + }, + true); + break; + case InputKeyRight: + case InputKeyDown: + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + //UNUSED(model); + int page = (model->page + 1) % (PAGE_ADDR_END + 1); + if(page == 0) { + page = PAGE_INFO; + } + model->page = page; + }, + true); + break; + case InputKeyLeft: + case InputKeyUp: + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + //UNUSED(model); + int page = (model->page - 1) % (PAGE_ADDR_END + 1); + if(page == 0) { + page = PAGE_ADDR_END; + } + model->page = page; + }, + true); + break; + // case InputKeyRight: + case InputKeyOk: + // with_view_model( + // instance->view, + // FlipBipScene1Model * model, + // { + // if(model->page >= PAGE_ADDR_BEGIN && model->page <= PAGE_ADDR_END) { + + // } + // }, + // true); + // break; + // case InputKeyLeft: + case InputKeyMAX: + break; + } + } + return true; +} + +void flipbip_scene_1_exit(void* context) { + furi_assert(context); + FlipBipScene1* instance = (FlipBipScene1*)context; + + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + model->page = PAGE_LOADING; + model->strength = FlipBipStrength256; + model->coin = FlipBipCoinBTC0; + memzero(model->seed, 64); + // if mnemonic_only is true, then we don't need to free the data here + if(!model->mnemonic_only) { + memzero((void*)model->mnemonic, strlen(model->mnemonic)); + free((void*)model->mnemonic); + memzero((void*)model->node, sizeof(HDNode)); + free((void*)model->node); + memzero((void*)model->xprv_root, strlen(model->xprv_root)); + memzero((void*)model->xprv_account, strlen(model->xprv_account)); + memzero((void*)model->xpub_account, strlen(model->xpub_account)); + memzero((void*)model->xprv_extended, strlen(model->xprv_extended)); + memzero((void*)model->xpub_extended, strlen(model->xpub_extended)); + free((void*)model->xprv_root); + free((void*)model->xprv_account); + free((void*)model->xpub_account); + free((void*)model->xprv_extended); + free((void*)model->xpub_extended); + for(int a = 0; a < NUM_ADDRS; a++) { + memzero((void*)model->recv_addresses[a], MAX_ADDR_LEN); + free((void*)model->recv_addresses[a]); + } + } + }, + true); + + flipbip_scene_1_clear_text(); +} + +void flipbip_scene_1_enter(void* context) { + furi_assert(context); + FlipBipScene1* instance = (FlipBipScene1*)context; + + FlipBip* app = instance->context; + + // BIP39 Strength setting + int strength = 256; // FlipBipStrength256 // 24 words (256 bit) + if(app->bip39_strength == FlipBipStrength128) { + strength = 128; // 12 words (128 bit) + } else if(app->bip39_strength == FlipBipStrength192) { + strength = 192; // 18 words (192 bit) + } + + // BIP39 Passphrase setting + const char* passphrase_text = ""; + if(app->passphrase == FlipBipPassphraseOn && strlen(app->passphrase_text) > 0) { + passphrase_text = app->passphrase_text; + } + + // BIP44 Coin setting + const uint32_t coin = app->bip44_coin; + // coin_name, derivation_path + s_derivation_text = COIN_TEXT_ARRAY[coin][1]; + + // Overwrite the saved seed with a new one setting + bool overwrite = app->overwrite_saved_seed != 0; + if(overwrite) { + s_derivation_text = TEXT_NEW_WALLET; + } + + flipbip_play_happy_bump(app); + //notification_message(app->notification, &sequence_blink_cyan_100); + flipbip_led_set_rgb(app, 255, 0, 0); + + with_view_model( + instance->view, + FlipBipScene1Model * model, + { + // s_busy = true; + + const int status = + flipbip_scene_1_model_init(model, strength, coin, overwrite, passphrase_text); + + // nonzero status, free the mnemonic + if(status != FlipBipStatusSuccess) { + memzero((void*)model->mnemonic, strlen(model->mnemonic)); + free((void*)model->mnemonic); + } + + // if error, set the error message + if(status == FlipBipStatusSaveError) { + model->mnemonic = "ERROR:,Save error"; + model->page = PAGE_MNEMONIC; + flipbip_play_long_bump(app); + } else if(status == FlipBipStatusLoadError) { + model->mnemonic = "ERROR:,Load error"; + model->page = PAGE_MNEMONIC; + flipbip_play_long_bump(app); + } else if(status == FlipBipStatusMnemonicCheckError) { + model->mnemonic = "ERROR:,Mnemonic check error"; + model->page = PAGE_MNEMONIC; + flipbip_play_long_bump(app); + } + + // s_busy = false; + + // if overwrite is set and mnemonic generated, return from scene immediately + if(status == FlipBipStatusReturn) { + instance->callback(FlipBipCustomEventScene1Back, instance->context); + } + }, + true); +} + +FlipBipScene1* flipbip_scene_1_alloc() { + FlipBipScene1* instance = malloc(sizeof(FlipBipScene1)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipBipScene1Model)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)flipbip_scene_1_draw); + view_set_input_callback(instance->view, flipbip_scene_1_input); + view_set_enter_callback(instance->view, flipbip_scene_1_enter); + view_set_exit_callback(instance->view, flipbip_scene_1_exit); + + // allocate the address node + s_addr_node = (HDNode*)malloc(sizeof(HDNode)); + + // allocate the display text + s_disp_text1 = (char*)malloc(30 + 1); + s_disp_text2 = (char*)malloc(30 + 1); + s_disp_text3 = (char*)malloc(30 + 1); + s_disp_text4 = (char*)malloc(30 + 1); + s_disp_text5 = (char*)malloc(30 + 1); + s_disp_text6 = (char*)malloc(30 + 1); + + return instance; +} + +void flipbip_scene_1_free(FlipBipScene1* instance) { + furi_assert(instance); + + with_view_model( + instance->view, FlipBipScene1Model * model, { UNUSED(model); }, true); + + // free the address node + memzero(s_addr_node, sizeof(HDNode)); + free(s_addr_node); + + // free the display text + flipbip_scene_1_clear_text(); + free(s_disp_text1); + free(s_disp_text2); + free(s_disp_text3); + free(s_disp_text4); + free(s_disp_text5); + free(s_disp_text6); + + view_free(instance->view); + free(instance); +} + +View* flipbip_scene_1_get_view(FlipBipScene1* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/flipbip/views/flipbip_scene_1.h b/applications/external/flipbip/views/flipbip_scene_1.h new file mode 100644 index 000000000..6e370633e --- /dev/null +++ b/applications/external/flipbip/views/flipbip_scene_1.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/flipbip_custom_event.h" + +typedef struct FlipBipScene1 FlipBipScene1; + +typedef void (*FlipBipScene1Callback)(FlipBipCustomEvent event, void* context); + +void flipbip_scene_1_set_callback( + FlipBipScene1* flipbip_scene_1, + FlipBipScene1Callback callback, + void* context); + +View* flipbip_scene_1_get_view(FlipBipScene1* flipbip_static); + +FlipBipScene1* flipbip_scene_1_alloc(); + +void flipbip_scene_1_free(FlipBipScene1* flipbip_static); \ No newline at end of file diff --git a/applications/external/flipbip/views/flipbip_startscreen.c b/applications/external/flipbip/views/flipbip_startscreen.c new file mode 100644 index 000000000..76b060e95 --- /dev/null +++ b/applications/external/flipbip/views/flipbip_startscreen.c @@ -0,0 +1,130 @@ +#include "../flipbip.h" +#include +#include +#include +#include +#include "flipbip_icons.h" + +struct FlipBipStartscreen { + View* view; + FlipBipStartscreenCallback callback; + void* context; +}; + +typedef struct { + int some_value; +} FlipBipStartscreenModel; + +void flipbip_startscreen_set_callback( + FlipBipStartscreen* instance, + FlipBipStartscreenCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void flipbip_startscreen_draw(Canvas* canvas, FlipBipStartscreenModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 1, 33, &I_Auth_62x31); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 18, 11, "FlipBIP - BIP32/39/44"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 23, 22, "Crypto toolkit for Flipper"); + canvas_draw_str(canvas, 99, 34, FLIPBIP_VERSION); + + elements_button_right(canvas, "Start"); +} + +static void flipbip_startscreen_model_init(FlipBipStartscreenModel* const model) { + model->some_value = 1; +} + +bool flipbip_startscreen_input(InputEvent* event, void* context) { + furi_assert(context); + FlipBipStartscreen* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + FlipBipStartscreenModel * model, + { + UNUSED(model); + instance->callback(FlipBipCustomEventStartscreenBack, instance->context); + }, + true); + break; + case InputKeyLeft: + case InputKeyRight: + case InputKeyUp: + case InputKeyDown: + case InputKeyOk: + with_view_model( + instance->view, + FlipBipStartscreenModel * model, + { + UNUSED(model); + instance->callback(FlipBipCustomEventStartscreenOk, instance->context); + }, + true); + break; + case InputKeyMAX: + break; + } + } + return true; +} + +void flipbip_startscreen_exit(void* context) { + furi_assert(context); +} + +void flipbip_startscreen_enter(void* context) { + furi_assert(context); + FlipBipStartscreen* instance = (FlipBipStartscreen*)context; + with_view_model( + instance->view, + FlipBipStartscreenModel * model, + { flipbip_startscreen_model_init(model); }, + true); +} + +FlipBipStartscreen* flipbip_startscreen_alloc() { + FlipBipStartscreen* instance = malloc(sizeof(FlipBipStartscreen)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipBipStartscreenModel)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)flipbip_startscreen_draw); + view_set_input_callback(instance->view, flipbip_startscreen_input); + //view_set_enter_callback(instance->view, flipbip_startscreen_enter); + //view_set_exit_callback(instance->view, flipbip_startscreen_exit); + + with_view_model( + instance->view, + FlipBipStartscreenModel * model, + { flipbip_startscreen_model_init(model); }, + true); + + return instance; +} + +void flipbip_startscreen_free(FlipBipStartscreen* instance) { + furi_assert(instance); + + with_view_model( + instance->view, FlipBipStartscreenModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* flipbip_startscreen_get_view(FlipBipStartscreen* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/flipbip/views/flipbip_startscreen.h b/applications/external/flipbip/views/flipbip_startscreen.h new file mode 100644 index 000000000..d6eb1fad8 --- /dev/null +++ b/applications/external/flipbip/views/flipbip_startscreen.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/flipbip_custom_event.h" + +typedef struct FlipBipStartscreen FlipBipStartscreen; + +typedef void (*FlipBipStartscreenCallback)(FlipBipCustomEvent event, void* context); + +void flipbip_startscreen_set_callback( + FlipBipStartscreen* flipbip_startscreen, + FlipBipStartscreenCallback callback, + void* context); + +View* flipbip_startscreen_get_view(FlipBipStartscreen* flipbip_static); + +FlipBipStartscreen* flipbip_startscreen_alloc(); + +void flipbip_startscreen_free(FlipBipStartscreen* flipbip_static); \ No newline at end of file From 8bb30920028930c364b968909fe1c1047d3e5b98 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 14 Jun 2023 03:18:24 +0300 Subject: [PATCH 187/370] Revert BLE gatt characteristics refactoring temporarily --- applications/services/bt/bt_service/bt.c | 2 +- firmware/targets/f7/ble_glue/app_debug.c | 4 +- .../ble_glue/{services => }/battery_service.c | 118 ++--- .../ble_glue/{services => }/battery_service.h | 0 firmware/targets/f7/ble_glue/ble_app.c | 91 ++-- .../targets/f7/ble_glue/dev_info_service.c | 220 +++++++++ .../{services => }/dev_info_service.h | 0 firmware/targets/f7/ble_glue/hid_service.c | 416 ++++++++++++++++++ .../f7/ble_glue/{services => }/hid_service.h | 3 +- .../ble_glue/{services => }/serial_service.c | 195 ++++---- .../ble_glue/{services => }/serial_service.h | 0 .../f7/ble_glue/services/dev_info_service.c | 176 -------- .../services/dev_info_service_uuid.inc | 3 - .../targets/f7/ble_glue/services/gatt_char.c | 123 ------ .../targets/f7/ble_glue/services/gatt_char.h | 96 ---- .../f7/ble_glue/services/hid_service.c | 365 --------------- .../ble_glue/services/serial_service_uuid.inc | 12 - firmware/targets/f7/furi_hal/furi_hal_bt.c | 3 +- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 10 +- .../targets/f7/furi_hal/furi_hal_bt_serial.c | 6 +- .../targets/furi_hal_include/furi_hal_bt.h | 2 +- .../furi_hal_include/furi_hal_bt_serial.h | 2 +- 22 files changed, 877 insertions(+), 970 deletions(-) rename firmware/targets/f7/ble_glue/{services => }/battery_service.c (53%) rename firmware/targets/f7/ble_glue/{services => }/battery_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/dev_info_service.c rename firmware/targets/f7/ble_glue/{services => }/dev_info_service.h (100%) create mode 100644 firmware/targets/f7/ble_glue/hid_service.c rename firmware/targets/f7/ble_glue/{services => }/hid_service.h (89%) rename firmware/targets/f7/ble_glue/{services => }/serial_service.c (57%) rename firmware/targets/f7/ble_glue/{services => }/serial_service.h (100%) delete mode 100644 firmware/targets/f7/ble_glue/services/dev_info_service.c delete mode 100644 firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc delete mode 100644 firmware/targets/f7/ble_glue/services/gatt_char.c delete mode 100644 firmware/targets/f7/ble_glue/services/gatt_char.h delete mode 100644 firmware/targets/f7/ble_glue/services/hid_service.c delete mode 100644 firmware/targets/f7/ble_glue/services/serial_service_uuid.inc diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 191324c9e..a842aea45 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -1,7 +1,7 @@ #include "bt_i.h" +#include "battery_service.h" #include "bt_keys_storage.h" -#include #include #include #include diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index d28852822..b443bee21 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -196,14 +196,14 @@ static void APPD_SetCPU2GpioConfig(void) { gpio_config.Pin = gpiob_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOB); LL_GPIO_Init(GPIOB, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOB, gpiob_pin_list); + LL_GPIO_ResetOutputPin(GPIOB, gpioa_pin_list); } if(gpioc_pin_list != 0) { gpio_config.Pin = gpioc_pin_list; LL_C2_AHB2_GRP1_EnableClock(LL_C2_AHB2_GRP1_PERIPH_GPIOC); LL_GPIO_Init(GPIOC, &gpio_config); - LL_GPIO_ResetOutputPin(GPIOC, gpioc_pin_list); + LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list); } } diff --git a/firmware/targets/f7/ble_glue/services/battery_service.c b/firmware/targets/f7/ble_glue/battery_service.c similarity index 53% rename from firmware/targets/f7/ble_glue/services/battery_service.c rename to firmware/targets/f7/ble_glue/battery_service.c index 63f736b3b..8c371efad 100644 --- a/firmware/targets/f7/ble_glue/services/battery_service.c +++ b/firmware/targets/f7/ble_glue/battery_service.c @@ -1,7 +1,5 @@ #include "battery_service.h" #include "app_common.h" -#include "gatt_char.h" - #include #include @@ -9,6 +7,12 @@ #define TAG "BtBatterySvc" +typedef struct { + uint16_t svc_handle; + uint16_t battery_level_char_handle; + uint16_t power_state_char_handle; +} BatterySvc; + enum { // Common states BatterySvcPowerStateUnknown = 0b00, @@ -36,44 +40,13 @@ typedef struct { _Static_assert(sizeof(BattrySvcPowerState) == 1, "Incorrect structure size"); +static BatterySvc* battery_svc = NULL; + #define BATTERY_POWER_STATE (0x2A1A) static const uint16_t service_uuid = BATTERY_SERVICE_UUID; - -typedef enum { - BatterySvcGattCharacteristicBatteryLevel = 0, - BatterySvcGattCharacteristicPowerState, - BatterySvcGattCharacteristicCount, -} BatterySvcGattCharacteristicId; - -static const FlipperGattCharacteristicParams battery_svc_chars[BatterySvcGattCharacteristicCount] = - {[BatterySvcGattCharacteristicBatteryLevel] = - {.name = "Battery Level", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = BATTERY_LEVEL_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [BatterySvcGattCharacteristicPowerState] = { - .name = "Power State", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = BATTERY_POWER_STATE, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}}; - -typedef struct { - uint16_t svc_handle; - FlipperGattCharacteristicInstance chars[BatterySvcGattCharacteristicCount]; -} BatterySvc; - -static BatterySvc* battery_svc = NULL; +static const uint16_t battery_level_char_uuid = BATTERY_LEVEL_CHAR_UUID; +static const uint16_t power_state_char_uuid = BATTERY_POWER_STATE; void battery_svc_start() { battery_svc = malloc(sizeof(BatterySvc)); @@ -85,19 +58,53 @@ void battery_svc_start() { if(status) { FURI_LOG_E(TAG, "Failed to add Battery service: %d", status); } - for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - battery_svc->svc_handle, &battery_svc_chars[i], &battery_svc->chars[i]); + // Add Battery level characteristic + status = aci_gatt_add_char( + battery_svc->svc_handle, + UUID_TYPE_16, + (Char_UUID_t*)&battery_level_char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &battery_svc->battery_level_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); } - + // Add Power state characteristic + status = aci_gatt_add_char( + battery_svc->svc_handle, + UUID_TYPE_16, + (Char_UUID_t*)&power_state_char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &battery_svc->power_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Battery level characteristic: %d", status); + } + // Update power state charachteristic battery_svc_update_power_state(); } void battery_svc_stop() { tBleStatus status; if(battery_svc) { - for(size_t i = 0; i < BatterySvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_delete(battery_svc->svc_handle, &battery_svc->chars[i]); + // Delete Battery level characteristic + status = + aci_gatt_del_char(battery_svc->svc_handle, battery_svc->battery_level_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); + } + // Delete Power state characteristic + status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->power_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Battery level characteristic: %d", status); } // Delete Battery service status = aci_gatt_del_service(battery_svc->svc_handle); @@ -119,10 +126,13 @@ bool battery_svc_update_level(uint8_t battery_charge) { return false; } // Update battery level characteristic - return flipper_gatt_characteristic_update( - battery_svc->svc_handle, - &battery_svc->chars[BatterySvcGattCharacteristicBatteryLevel], - &battery_charge); + FURI_LOG_D(TAG, "Updating battery level characteristic"); + tBleStatus result = aci_gatt_update_char_value( + battery_svc->svc_handle, battery_svc->battery_level_char_handle, 0, 1, &battery_charge); + if(result) { + FURI_LOG_E(TAG, "Failed updating RX characteristic: %d", result); + } + return result != BLE_STATUS_SUCCESS; } bool battery_svc_update_power_state() { @@ -142,9 +152,15 @@ bool battery_svc_update_power_state() { power_state.charging = BatterySvcPowerStateNotCharging; power_state.discharging = BatterySvcPowerStateDischarging; } - - return flipper_gatt_characteristic_update( + FURI_LOG_D(TAG, "Updating power state characteristic"); + tBleStatus result = aci_gatt_update_char_value( battery_svc->svc_handle, - &battery_svc->chars[BatterySvcGattCharacteristicPowerState], - &power_state); + battery_svc->power_state_char_handle, + 0, + 1, + (uint8_t*)&power_state); + if(result) { + FURI_LOG_E(TAG, "Failed updating Power state characteristic: %d", result); + } + return result != BLE_STATUS_SUCCESS; } diff --git a/firmware/targets/f7/ble_glue/services/battery_service.h b/firmware/targets/f7/ble_glue/battery_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/services/battery_service.h rename to firmware/targets/f7/ble_glue/battery_service.h diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index c0418d9fe..37d8f7cd0 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -33,51 +33,6 @@ static int32_t ble_app_hci_thread(void* context); static void ble_app_hci_event_handler(void* pPayload); static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); -static const HCI_TL_HciInitConf_t hci_tl_config = { - .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, - .StatusNotCallBack = ble_app_hci_status_not_handler, -}; - -static const SHCI_C2_CONFIG_Cmd_Param_t config_param = { - .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, - .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, - .BleNvmRamAddress = (uint32_t)ble_app_nvm, - .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, -}; - -static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { - .Header = {{0, 0, 0}}, // Header unused - .Param = { - .pBleBufferAddress = 0, // pBleBufferAddress not used - .BleBufferSize = 0, // BleBufferSize not used - .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, - .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, - .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, - .NumOfLinks = CFG_BLE_NUM_LINK, - .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, - .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, - .MblockCount = CFG_BLE_MBLOCK_COUNT, - .AttMtu = CFG_BLE_MAX_ATT_MTU, - .SlaveSca = CFG_BLE_SLAVE_SCA, - .MasterSca = CFG_BLE_MASTER_SCA, - .LsSource = CFG_BLE_LSE_SOURCE, - .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, - .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, - .ViterbiEnable = CFG_BLE_VITERBI_MODE, - .Options = CFG_BLE_OPTIONS, - .HwVersion = 0, - .max_coc_initiator_nbr = 32, - .min_tx_power = 0, - .max_tx_power = 0, - .rx_model_config = 1, - /* New stack (13.3->15.0) */ - .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set - .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB - .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB - .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) - }}; - bool ble_app_init() { SHCI_CmdStatus_t status; ble_app = malloc(sizeof(BleApp)); @@ -89,16 +44,58 @@ bool ble_app_init() { furi_thread_start(ble_app->thread); // Initialize Ble Transport Layer + HCI_TL_HciInitConf_t hci_tl_config = { + .p_cmdbuffer = (uint8_t*)&ble_app_cmd_buffer, + .StatusNotCallBack = ble_app_hci_status_not_handler, + }; hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config); // Configure NVM store for pairing data - status = SHCI_C2_Config((SHCI_C2_CONFIG_Cmd_Param_t*)&config_param); + SHCI_C2_CONFIG_Cmd_Param_t config_param = { + .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE, + .Config1 = SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM, + .BleNvmRamAddress = (uint32_t)ble_app_nvm, + .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE, + }; + status = SHCI_C2_Config(&config_param); if(status) { FURI_LOG_E(TAG, "Failed to configure 2nd core: %d", status); } // Start ble stack on 2nd core - status = SHCI_C2_BLE_Init((SHCI_C2_Ble_Init_Cmd_Packet_t*)&ble_init_cmd_packet); + SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { + .Header = {{0, 0, 0}}, // Header unused + .Param = { + .pBleBufferAddress = 0, // pBleBufferAddress not used + .BleBufferSize = 0, // BleBufferSize not used + .NumAttrRecord = CFG_BLE_NUM_GATT_ATTRIBUTES, + .NumAttrServ = CFG_BLE_NUM_GATT_SERVICES, + .AttrValueArrSize = CFG_BLE_ATT_VALUE_ARRAY_SIZE, + .NumOfLinks = CFG_BLE_NUM_LINK, + .ExtendedPacketLengthEnable = CFG_BLE_DATA_LENGTH_EXTENSION, + .PrWriteListSize = CFG_BLE_PREPARE_WRITE_LIST_SIZE, + .MblockCount = CFG_BLE_MBLOCK_COUNT, + .AttMtu = CFG_BLE_MAX_ATT_MTU, + .SlaveSca = CFG_BLE_SLAVE_SCA, + .MasterSca = CFG_BLE_MASTER_SCA, + .LsSource = CFG_BLE_LSE_SOURCE, + .MaxConnEventLength = CFG_BLE_MAX_CONN_EVENT_LENGTH, + .HsStartupTime = CFG_BLE_HSE_STARTUP_TIME, + .ViterbiEnable = CFG_BLE_VITERBI_MODE, + .Options = CFG_BLE_OPTIONS, + .HwVersion = 0, + .max_coc_initiator_nbr = 32, + .min_tx_power = 0, + .max_tx_power = 0, + .rx_model_config = 1, + /* New stack (13.3->15.0) */ + .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB + .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB + .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) + }}; + status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); if(status) { FURI_LOG_E(TAG, "Failed to start ble stack: %d", status); } diff --git a/firmware/targets/f7/ble_glue/dev_info_service.c b/firmware/targets/f7/ble_glue/dev_info_service.c new file mode 100644 index 000000000..d24058632 --- /dev/null +++ b/firmware/targets/f7/ble_glue/dev_info_service.c @@ -0,0 +1,220 @@ +#include "dev_info_service.h" +#include "app_common.h" +#include + +#include +#include +#include + +#define TAG "BtDevInfoSvc" + +typedef struct { + uint16_t service_handle; + uint16_t man_name_char_handle; + uint16_t serial_num_char_handle; + uint16_t firmware_rev_char_handle; + uint16_t software_rev_char_handle; + uint16_t rpc_version_char_handle; + FuriString* version_string; + char hardware_revision[4]; +} DevInfoSvc; + +static DevInfoSvc* dev_info_svc = NULL; + +static const char dev_info_man_name[] = "Flipper Devices Inc."; +static const char dev_info_serial_num[] = "1.0"; +static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); + +static const uint8_t dev_info_rpc_version_uuid[] = + {0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03}; + +void dev_info_svc_start() { + dev_info_svc = malloc(sizeof(DevInfoSvc)); + dev_info_svc->version_string = furi_string_alloc_printf( + "%s %s %s %s", + version_get_githash(NULL), + version_get_version(NULL), + version_get_gitbranchnum(NULL), + version_get_builddate(NULL)); + snprintf( + dev_info_svc->hardware_revision, + sizeof(dev_info_svc->hardware_revision), + "%d", + version_get_target(NULL)); + tBleStatus status; + + // Add Device Information Service + uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; + status = aci_gatt_add_service( + UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 11, &dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); + } + + // Add characteristics + uuid = MANUFACTURER_NAME_UUID; + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_16, + (Char_UUID_t*)&uuid, + strlen(dev_info_man_name), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->man_name_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add manufacturer name char: %d", status); + } + uuid = SERIAL_NUMBER_UUID; + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_16, + (Char_UUID_t*)&uuid, + strlen(dev_info_serial_num), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->serial_num_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add serial number char: %d", status); + } + uuid = FIRMWARE_REVISION_UUID; + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_16, + (Char_UUID_t*)&uuid, + strlen(dev_info_svc->hardware_revision), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->firmware_rev_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add firmware revision char: %d", status); + } + uuid = SOFTWARE_REVISION_UUID; + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_16, + (Char_UUID_t*)&uuid, + furi_string_size(dev_info_svc->version_string), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->software_rev_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add software revision char: %d", status); + } + status = aci_gatt_add_char( + dev_info_svc->service_handle, + UUID_TYPE_128, + (const Char_UUID_t*)dev_info_rpc_version_uuid, + strlen(dev_info_rpc_version), + CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &dev_info_svc->rpc_version_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add rpc version characteristic: %d", status); + } + + // Update characteristics + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->man_name_char_handle, + 0, + strlen(dev_info_man_name), + (uint8_t*)dev_info_man_name); + if(status) { + FURI_LOG_E(TAG, "Failed to update manufacturer name char: %d", status); + } + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->serial_num_char_handle, + 0, + strlen(dev_info_serial_num), + (uint8_t*)dev_info_serial_num); + if(status) { + FURI_LOG_E(TAG, "Failed to update serial number char: %d", status); + } + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->firmware_rev_char_handle, + 0, + strlen(dev_info_svc->hardware_revision), + (uint8_t*)dev_info_svc->hardware_revision); + if(status) { + FURI_LOG_E(TAG, "Failed to update firmware revision char: %d", status); + } + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->software_rev_char_handle, + 0, + furi_string_size(dev_info_svc->version_string), + (uint8_t*)furi_string_get_cstr(dev_info_svc->version_string)); + if(status) { + FURI_LOG_E(TAG, "Failed to update software revision char: %d", status); + } + status = aci_gatt_update_char_value( + dev_info_svc->service_handle, + dev_info_svc->rpc_version_char_handle, + 0, + strlen(dev_info_rpc_version), + (uint8_t*)dev_info_rpc_version); + if(status) { + FURI_LOG_E(TAG, "Failed to update rpc version char: %d", status); + } +} + +void dev_info_svc_stop() { + tBleStatus status; + if(dev_info_svc) { + furi_string_free(dev_info_svc->version_string); + // Delete service characteristics + status = + aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete manufacturer name char: %d", status); + } + status = + aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete serial number char: %d", status); + } + status = aci_gatt_del_char( + dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete firmware revision char: %d", status); + } + status = aci_gatt_del_char( + dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete software revision char: %d", status); + } + status = + aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->rpc_version_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete rpc version char: %d", status); + } + // Delete service + status = aci_gatt_del_service(dev_info_svc->service_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); + } + free(dev_info_svc); + dev_info_svc = NULL; + } +} + +bool dev_info_svc_is_started() { + return dev_info_svc != NULL; +} diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.h b/firmware/targets/f7/ble_glue/dev_info_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/services/dev_info_service.h rename to firmware/targets/f7/ble_glue/dev_info_service.h diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c new file mode 100644 index 000000000..a31d6015f --- /dev/null +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -0,0 +1,416 @@ +#include "hid_service.h" +#include "app_common.h" +#include + +#include + +#define TAG "BtHid" + +typedef struct { + uint16_t svc_handle; + uint16_t protocol_mode_char_handle; + uint16_t report_char_handle[HID_SVC_REPORT_COUNT]; + uint16_t report_ref_desc_handle[HID_SVC_REPORT_COUNT]; + uint16_t report_map_char_handle; + uint16_t info_char_handle; + uint16_t ctrl_point_char_handle; + // led state + uint16_t led_state_char_handle; + uint16_t led_state_desc_handle; + HidLedStateEventCallback led_state_event_callback; + void* led_state_ctx; +} HIDSvc; + +static HIDSvc* hid_svc = NULL; + +static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { + SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; + hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); + evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; + // aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { + if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { + // Process modification events + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { + // Process notification confirmation + ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { + // Process write request + aci_gatt_write_permit_req_event_rp0* req = + (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; + + furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); + + // this check is likely to be incorrect, it will actually work in our case + // but we need to investigate gatt api to see what is the rules + // that specify attibute handle value from char handle (or the reverse) + if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) { + hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); + aci_gatt_write_resp( + req->Connection_Handle, + req->Attribute_Handle, + 0x00, /* write_status = 0 (no error))*/ + 0x00, /* err_code */ + req->Data_Length, + req->Data); + aci_gatt_write_char_value( + req->Connection_Handle, + hid_svc->led_state_char_handle, + req->Data_Length, + req->Data); + ret = SVCCTL_EvtAckFlowEnable; + } + } + } + return ret; +} + +void hid_svc_start() { + tBleStatus status; + hid_svc = malloc(sizeof(HIDSvc)); + Service_UUID_t svc_uuid = {}; + Char_Desc_Uuid_t desc_uuid = {}; + Char_UUID_t char_uuid = {}; + + // Register event handler + SVCCTL_RegisterSvcHandler(hid_svc_event_handler); + // Add service + svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; + /** + * Add Human Interface Device Service + */ + status = aci_gatt_add_service( + UUID_TYPE_16, + &svc_uuid, + PRIMARY_SERVICE, + 2 + /* protocol mode */ + (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + + 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ + &hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add HID service: %d", status); + } + // Add Protocol mode characteristics + char_uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + ATTR_PERMISSION_NONE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_CONSTANT, + &hid_svc->protocol_mode_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add protocol mode characteristic: %d", status); + } + // Update Protocol mode characteristic + uint8_t protocol_mode = 1; + status = aci_gatt_update_char_value( + hid_svc->svc_handle, hid_svc->protocol_mode_char_handle, 0, 1, &protocol_mode); + if(status) { + FURI_LOG_E(TAG, "Failed to update protocol mode characteristic: %d", status); + } + +#if(HID_SVC_REPORT_COUNT != 0) + for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { + if(i < HID_SVC_INPUT_REPORT_COUNT) { //-V547 + uint8_t buf[2] = {i + 1, 1}; // 1 input + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_REPORT_MAX_LEN, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &(hid_svc->report_char_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); + } + + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->report_char_handle[i], + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->report_ref_desc_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); + } + } else if((i - HID_SVC_INPUT_REPORT_COUNT) < HID_SVC_OUTPUT_REPORT_COUNT) { + uint8_t buf[2] = {i + 1, 2}; // 2 output + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_REPORT_MAX_LEN, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &(hid_svc->report_char_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); + } + + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->report_char_handle[i], + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->report_ref_desc_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); + } + } else { + uint8_t buf[2] = {i + 1, 3}; // 3 feature + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_REPORT_MAX_LEN, + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &(hid_svc->report_char_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report characteristic: %d", status); + } + + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->report_char_handle[i], + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->report_ref_desc_handle[i])); + if(status) { + FURI_LOG_E(TAG, "Failed to add report reference descriptor: %d", status); + } + } + } +#endif + // Add led state output report + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, + ATTR_PERMISSION_NONE, + GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, + 10, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->led_state_char_handle)); + if(status) { + FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status); + } + + // Add led state char descriptor specifying it is an output report + uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->led_state_char_handle, + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->led_state_desc_handle)); + if(status) { + FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status); + } + // Add Report Map characteristic + char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_REPORT_MAP_MAX_LEN, + CHAR_PROP_READ, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &hid_svc->report_map_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add report map characteristic: %d", status); + } + + // Add Information characteristic + char_uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_INFO_LEN, + CHAR_PROP_READ, + ATTR_PERMISSION_NONE, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &hid_svc->info_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add information characteristic: %d", status); + } + // Add Control Point characteristic + char_uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + HID_SVC_CONTROL_POINT_LEN, + CHAR_PROP_WRITE_WITHOUT_RESP, + ATTR_PERMISSION_NONE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_CONSTANT, + &hid_svc->ctrl_point_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); + } + + hid_svc->led_state_event_callback = NULL; + hid_svc->led_state_ctx = NULL; +} + +bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + tBleStatus status = aci_gatt_update_char_value( + hid_svc->svc_handle, hid_svc->report_map_char_handle, 0, len, data); + if(status) { + FURI_LOG_E(TAG, "Failed updating report map characteristic: %d", status); + return false; + } + return true; +} + +bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + tBleStatus status = aci_gatt_update_char_value( + hid_svc->svc_handle, hid_svc->report_char_handle[input_report_num], 0, len, data); + if(status) { + FURI_LOG_E(TAG, "Failed updating report characteristic: %d", status); + return false; + } + return true; +} + +bool hid_svc_update_info(uint8_t* data, uint16_t len) { + furi_assert(data); + furi_assert(hid_svc); + + tBleStatus status = + aci_gatt_update_char_value(hid_svc->svc_handle, hid_svc->info_char_handle, 0, len, data); + if(status) { + FURI_LOG_E(TAG, "Failed updating info characteristic: %d", status); + return false; + } + return true; +} + +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { + furi_assert(hid_svc); + furi_assert(callback); + furi_assert(context); + + hid_svc->led_state_event_callback = callback; + hid_svc->led_state_ctx = context; +} + +bool hid_svc_is_started() { + return hid_svc != NULL; +} + +void hid_svc_stop() { + tBleStatus status; + if(hid_svc) { + // Delete characteristics + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_map_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Report Map characteristic: %d", status); + } +#if(HID_SVC_INPUT_REPORT_COUNT != 0) + for(uint8_t i = 0; i < HID_SVC_REPORT_COUNT; i++) { + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->report_char_handle[i]); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Report characteristic: %d", status); + } + } +#endif + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->protocol_mode_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Protocol Mode characteristic: %d", status); + } + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->info_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Information characteristic: %d", status); + } + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->ctrl_point_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); + } + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status); + } + // Delete service + status = aci_gatt_del_service(hid_svc->svc_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); + } + // Delete buffer size mutex + free(hid_svc); + hid_svc = NULL; + } +} diff --git a/firmware/targets/f7/ble_glue/services/hid_service.h b/firmware/targets/f7/ble_glue/hid_service.h similarity index 89% rename from firmware/targets/f7/ble_glue/services/hid_service.h rename to firmware/targets/f7/ble_glue/hid_service.h index 4d0ed4c4f..b8f6b244d 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.h +++ b/firmware/targets/f7/ble_glue/hid_service.h @@ -27,7 +27,6 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); -// Expects data to be of length HID_SVC_INFO_LEN (4 bytes) -bool hid_svc_update_info(uint8_t* data); +bool hid_svc_update_info(uint8_t* data, uint16_t len); void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); diff --git a/firmware/targets/f7/ble_glue/services/serial_service.c b/firmware/targets/f7/ble_glue/serial_service.c similarity index 57% rename from firmware/targets/f7/ble_glue/services/serial_service.c rename to firmware/targets/f7/ble_glue/serial_service.c index ab009bbfc..c6421dc28 100644 --- a/firmware/targets/f7/ble_glue/services/serial_service.c +++ b/firmware/targets/f7/ble_glue/serial_service.c @@ -1,67 +1,17 @@ #include "serial_service.h" #include "app_common.h" #include -#include "gatt_char.h" #include -#include "serial_service_uuid.inc" - #define TAG "BtSerialSvc" -typedef enum { - SerialSvcGattCharacteristicTx = 0, - SerialSvcGattCharacteristicRx, - SerialSvcGattCharacteristicFlowCtrl, - SerialSvcGattCharacteristicStatus, - SerialSvcGattCharacteristicCount, -} SerialSvcGattCharacteristicId; - -static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { - [SerialSvcGattCharacteristicTx] = - {.name = "TX", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, - .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_VARIABLE}, - [SerialSvcGattCharacteristicRx] = - {.name = "RX", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, - .uuid.Char_UUID_128 = SERIAL_SVC_RX_CHAR_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, - .is_variable = CHAR_VALUE_LEN_VARIABLE}, - [SerialSvcGattCharacteristicFlowCtrl] = - {.name = "Flow control", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(uint32_t), - .uuid.Char_UUID_128 = SERIAL_SVC_FLOW_CONTROL_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [SerialSvcGattCharacteristicStatus] = { - .name = "RPC status", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(SerialServiceRpcStatus), - .uuid.Char_UUID_128 = SERIAL_SVC_RPC_STATUS_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, - .is_variable = CHAR_VALUE_LEN_CONSTANT}}; - typedef struct { uint16_t svc_handle; - FlipperGattCharacteristicInstance chars[SerialSvcGattCharacteristicCount]; + uint16_t rx_char_handle; + uint16_t tx_char_handle; + uint16_t flow_ctrl_char_handle; + uint16_t rpc_status_char_handle; FuriMutex* buff_size_mtx; uint32_t buff_size; uint16_t bytes_ready_to_receive; @@ -71,6 +21,17 @@ typedef struct { static SerialSvc* serial_svc = NULL; +static const uint8_t service_uuid[] = + {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; +static const uint8_t char_tx_uuid[] = + {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t char_rx_uuid[] = + {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t flow_ctrl_uuid[] = + {0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; +static const uint8_t rpc_status_uuid[] = + {0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; + static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); @@ -79,14 +40,11 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; - if(attribute_modified->Attr_Handle == - serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 2) { + if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 2) { // Descriptor handle ret = SVCCTL_EvtAckFlowEnable; FURI_LOG_D(TAG, "RX descriptor event"); - } else if( - attribute_modified->Attr_Handle == - serial_svc->chars[SerialSvcGattCharacteristicRx].handle + 1) { + } else if(attribute_modified->Attr_Handle == serial_svc->rx_char_handle + 1) { FURI_LOG_D(TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); if(serial_svc->callback) { furi_check( @@ -112,9 +70,7 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } ret = SVCCTL_EvtAckFlowEnable; - } else if( - attribute_modified->Attr_Handle == - serial_svc->chars[SerialSvcGattCharacteristicStatus].handle + 1) { + } else if(attribute_modified->Attr_Handle == serial_svc->rpc_status_char_handle + 1) { SerialServiceRpcStatus* rpc_status = (SerialServiceRpcStatus*)attribute_modified->Attr_Data; if(*rpc_status == SerialServiceRpcStatusNotActive) { @@ -141,12 +97,18 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void* event) { } static void serial_svc_update_rpc_char(SerialServiceRpcStatus status) { - flipper_gatt_characteristic_update( - serial_svc->svc_handle, &serial_svc->chars[SerialSvcGattCharacteristicStatus], &status); + tBleStatus ble_status = aci_gatt_update_char_value( + serial_svc->svc_handle, + serial_svc->rpc_status_char_handle, + 0, + sizeof(SerialServiceRpcStatus), + (uint8_t*)&status); + if(ble_status) { + FURI_LOG_E(TAG, "Failed to update RPC status char: %d", ble_status); + } } void serial_svc_start() { - UNUSED(serial_svc_chars); tBleStatus status; serial_svc = malloc(sizeof(SerialSvc)); // Register event handler @@ -154,17 +116,72 @@ void serial_svc_start() { // Add service status = aci_gatt_add_service( - UUID_TYPE_128, &service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); + UUID_TYPE_128, (Service_UUID_t*)service_uuid, PRIMARY_SERVICE, 12, &serial_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add Serial service: %d", status); } - // Add characteristics - for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - serial_svc->svc_handle, &serial_svc_chars[i], &serial_svc->chars[i]); + // Add RX characteristics + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)char_rx_uuid, + SERIAL_SVC_DATA_LEN_MAX, + CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, + ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_VARIABLE, + &serial_svc->rx_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add RX characteristic: %d", status); } + // Add TX characteristic + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)char_tx_uuid, + SERIAL_SVC_DATA_LEN_MAX, + CHAR_PROP_READ | CHAR_PROP_INDICATE, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_VARIABLE, + &serial_svc->tx_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add TX characteristic: %d", status); + } + // Add Flow Control characteristic + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)flow_ctrl_uuid, + sizeof(uint32_t), + CHAR_PROP_READ | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ, + GATT_DONT_NOTIFY_EVENTS, + 10, + CHAR_VALUE_LEN_CONSTANT, + &serial_svc->flow_ctrl_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add Flow Control characteristic: %d", status); + } + // Add RPC status characteristic + status = aci_gatt_add_char( + serial_svc->svc_handle, + UUID_TYPE_128, + (const Char_UUID_t*)rpc_status_uuid, + sizeof(SerialServiceRpcStatus), + CHAR_PROP_READ | CHAR_PROP_WRITE | CHAR_PROP_NOTIFY, + ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, + GATT_NOTIFY_ATTRIBUTE_WRITE, + 10, + CHAR_VALUE_LEN_CONSTANT, + &serial_svc->rpc_status_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to add RPC status characteristic: %d", status); + } serial_svc_update_rpc_char(SerialServiceRpcStatusNotActive); // Allocate buffer size mutex serial_svc->buff_size_mtx = furi_mutex_alloc(FuriMutexTypeNormal); @@ -179,12 +196,13 @@ void serial_svc_set_callbacks( serial_svc->context = context; serial_svc->buff_size = buff_size; serial_svc->bytes_ready_to_receive = buff_size; - uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - flipper_gatt_characteristic_update( + aci_gatt_update_char_value( serial_svc->svc_handle, - &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], - &buff_size_reversed); + serial_svc->flow_ctrl_char_handle, + 0, + sizeof(uint32_t), + (uint8_t*)&buff_size_reversed); } void serial_svc_notify_buffer_is_empty() { @@ -195,12 +213,13 @@ void serial_svc_notify_buffer_is_empty() { if(serial_svc->bytes_ready_to_receive == 0) { FURI_LOG_D(TAG, "Buffer is empty. Notifying client"); serial_svc->bytes_ready_to_receive = serial_svc->buff_size; - uint32_t buff_size_reversed = REVERSE_BYTES_U32(serial_svc->buff_size); - flipper_gatt_characteristic_update( + aci_gatt_update_char_value( serial_svc->svc_handle, - &serial_svc->chars[SerialSvcGattCharacteristicFlowCtrl], - &buff_size_reversed); + serial_svc->flow_ctrl_char_handle, + 0, + sizeof(uint32_t), + (uint8_t*)&buff_size_reversed); } furi_check(furi_mutex_release(serial_svc->buff_size_mtx) == FuriStatusOk); } @@ -208,8 +227,22 @@ void serial_svc_notify_buffer_is_empty() { void serial_svc_stop() { tBleStatus status; if(serial_svc) { - for(uint8_t i = 0; i < SerialSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_delete(serial_svc->svc_handle, &serial_svc->chars[i]); + // Delete characteristics + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete TX characteristic: %d", status); + } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete RX characteristic: %d", status); + } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->flow_ctrl_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete Flow Control characteristic: %d", status); + } + status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rpc_status_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete RPC Status characteristic: %d", status); } // Delete service status = aci_gatt_del_service(serial_svc->svc_handle); @@ -240,7 +273,7 @@ bool serial_svc_update_tx(uint8_t* data, uint16_t data_len) { tBleStatus result = aci_gatt_update_char_value_ext( 0, serial_svc->svc_handle, - serial_svc->chars[SerialSvcGattCharacteristicTx].handle, + serial_svc->tx_char_handle, remained ? 0x00 : 0x02, data_len, value_offset, diff --git a/firmware/targets/f7/ble_glue/services/serial_service.h b/firmware/targets/f7/ble_glue/serial_service.h similarity index 100% rename from firmware/targets/f7/ble_glue/services/serial_service.h rename to firmware/targets/f7/ble_glue/serial_service.h diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service.c b/firmware/targets/f7/ble_glue/services/dev_info_service.c deleted file mode 100644 index 5bee97b41..000000000 --- a/firmware/targets/f7/ble_glue/services/dev_info_service.c +++ /dev/null @@ -1,176 +0,0 @@ -#include "dev_info_service.h" -#include "app_common.h" -#include "gatt_char.h" -#include - -#include -#include -#include - -#include "dev_info_service_uuid.inc" - -#define TAG "BtDevInfoSvc" - -typedef enum { - DevInfoSvcGattCharacteristicMfgName = 0, - DevInfoSvcGattCharacteristicSerial, - DevInfoSvcGattCharacteristicFirmwareRev, - DevInfoSvcGattCharacteristicSoftwareRev, - DevInfoSvcGattCharacteristicRpcVersion, - DevInfoSvcGattCharacteristicCount, -} DevInfoSvcGattCharacteristicId; - -#define DEVICE_INFO_HARDWARE_REV_SIZE 4 -typedef struct { - uint16_t service_handle; - FlipperGattCharacteristicInstance characteristics[DevInfoSvcGattCharacteristicCount]; - FuriString* version_string; - char hardware_revision[DEVICE_INFO_HARDWARE_REV_SIZE]; -} DevInfoSvc; - -static DevInfoSvc* dev_info_svc = NULL; - -static const char dev_info_man_name[] = "Flipper Devices Inc."; -static const char dev_info_serial_num[] = "1.0"; -static const char dev_info_rpc_version[] = TOSTRING(PROTOBUF_MAJOR_VERSION.PROTOBUF_MINOR_VERSION); - -static bool dev_info_char_firmware_rev_callback( - const void* context, - const uint8_t** data, - uint16_t* data_len) { - const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; - *data_len = sizeof(dev_info_svc->hardware_revision); - if(data) { - *data = (const uint8_t*)&dev_info_svc->hardware_revision; - } - return false; -} - -static bool dev_info_char_software_rev_callback( - const void* context, - const uint8_t** data, - uint16_t* data_len) { - const DevInfoSvc* dev_info_svc = *(DevInfoSvc**)context; - *data_len = furi_string_size(dev_info_svc->version_string); - if(data) { - *data = (const uint8_t*)furi_string_get_cstr(dev_info_svc->version_string); - } - return false; -} - -static const FlipperGattCharacteristicParams dev_info_svc_chars[DevInfoSvcGattCharacteristicCount] = - {[DevInfoSvcGattCharacteristicMfgName] = - {.name = "Manufacturer Name", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(dev_info_man_name) - 1, - .data.fixed.ptr = (const uint8_t*)&dev_info_man_name, - .uuid.Char_UUID_16 = MANUFACTURER_NAME_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [DevInfoSvcGattCharacteristicSerial] = - {.name = "Serial Number", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(dev_info_serial_num) - 1, - .data.fixed.ptr = (const uint8_t*)&dev_info_serial_num, - .uuid.Char_UUID_16 = SERIAL_NUMBER_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [DevInfoSvcGattCharacteristicFirmwareRev] = - {.name = "Firmware Revision", - .data_prop_type = FlipperGattCharacteristicDataCallback, - .data.callback.context = &dev_info_svc, - .data.callback.fn = dev_info_char_firmware_rev_callback, - .uuid.Char_UUID_16 = FIRMWARE_REVISION_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [DevInfoSvcGattCharacteristicSoftwareRev] = - {.name = "Software Revision", - .data_prop_type = FlipperGattCharacteristicDataCallback, - .data.callback.context = &dev_info_svc, - .data.callback.fn = dev_info_char_software_rev_callback, - .uuid.Char_UUID_16 = SOFTWARE_REVISION_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [DevInfoSvcGattCharacteristicRpcVersion] = { - .name = "RPC Version", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = sizeof(dev_info_rpc_version) - 1, - .data.fixed.ptr = (const uint8_t*)&dev_info_rpc_version, - .uuid.Char_UUID_128 = DEV_INVO_RPC_VERSION_UID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}}; - -void dev_info_svc_start() { - dev_info_svc = malloc(sizeof(DevInfoSvc)); - dev_info_svc->version_string = furi_string_alloc_printf( - "%s %s %s %s", - version_get_githash(NULL), - version_get_version(NULL), - version_get_gitbranchnum(NULL), - version_get_builddate(NULL)); - snprintf( - dev_info_svc->hardware_revision, - sizeof(dev_info_svc->hardware_revision), - "%d", - version_get_target(NULL)); - tBleStatus status; - - // Add Device Information Service - uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; - status = aci_gatt_add_service( - UUID_TYPE_16, - (Service_UUID_t*)&uuid, - PRIMARY_SERVICE, - 1 + 2 * DevInfoSvcGattCharacteristicCount, - &dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add Device Information Service: %d", status); - } - - for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - dev_info_svc->service_handle, - &dev_info_svc_chars[i], - &dev_info_svc->characteristics[i]); - flipper_gatt_characteristic_update( - dev_info_svc->service_handle, &dev_info_svc->characteristics[i], NULL); - } -} - -void dev_info_svc_stop() { - tBleStatus status; - if(dev_info_svc) { - furi_string_free(dev_info_svc->version_string); - // Delete service characteristics - for(size_t i = 0; i < DevInfoSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_delete( - dev_info_svc->service_handle, &dev_info_svc->characteristics[i]); - } - // Delete service - status = aci_gatt_del_service(dev_info_svc->service_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete device info service: %d", status); - } - free(dev_info_svc); - dev_info_svc = NULL; - } -} - -bool dev_info_svc_is_started() { - return dev_info_svc != NULL; -} diff --git a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc b/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc deleted file mode 100644 index ad520f62e..000000000 --- a/firmware/targets/f7/ble_glue/services/dev_info_service_uuid.inc +++ /dev/null @@ -1,3 +0,0 @@ -#define DEV_INVO_RPC_VERSION_UID \ - { 0x33, 0xa9, 0xb5, 0x3e, 0x87, 0x5d, 0x1a, 0x8e, 0xc8, 0x47, 0x5e, 0xae, 0x6d, 0x66, 0xf6, 0x03 } - diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.c b/firmware/targets/f7/ble_glue/services/gatt_char.c deleted file mode 100644 index 9b6a44f61..000000000 --- a/firmware/targets/f7/ble_glue/services/gatt_char.c +++ /dev/null @@ -1,123 +0,0 @@ -#include "gatt_char.h" - -#include - -#define TAG "GattChar" - -#define GATT_MIN_READ_KEY_SIZE (10) - -void flipper_gatt_characteristic_init( - uint16_t svc_handle, - const FlipperGattCharacteristicParams* char_descriptor, - FlipperGattCharacteristicInstance* char_instance) { - furi_assert(char_descriptor); - furi_assert(char_instance); - - // Copy the descriptor to the instance, since it may point to stack memory - // TODO: only copy if really comes from stack - char_instance->characteristic = malloc(sizeof(FlipperGattCharacteristicParams)); - memcpy( - (void*)char_instance->characteristic, - char_descriptor, - sizeof(FlipperGattCharacteristicParams)); - - uint16_t char_data_size = 0; - if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { - char_data_size = char_descriptor->data.fixed.length; - } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { - char_descriptor->data.callback.fn( - char_descriptor->data.callback.context, NULL, &char_data_size); - } - - tBleStatus status = aci_gatt_add_char( - svc_handle, - char_descriptor->uuid_type, - &char_descriptor->uuid, - char_data_size, - char_descriptor->char_properties, - char_descriptor->security_permissions, - char_descriptor->gatt_evt_mask, - GATT_MIN_READ_KEY_SIZE, - char_descriptor->is_variable, - &char_instance->handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); - } - - char_instance->descriptor_handle = 0; - if((status == 0) && char_descriptor->descriptor_params) { - uint8_t const* char_data = NULL; - const FlipperGattCharacteristicDescriptorParams* char_data_descriptor = - char_descriptor->descriptor_params; - bool release_data = char_data_descriptor->data_callback.fn( - char_data_descriptor->data_callback.context, &char_data, &char_data_size); - - status = aci_gatt_add_char_desc( - svc_handle, - char_instance->handle, - char_data_descriptor->uuid_type, - &char_data_descriptor->uuid, - char_data_descriptor->max_length, - char_data_size, - char_data, - char_data_descriptor->security_permissions, - char_data_descriptor->access_permissions, - char_data_descriptor->gatt_evt_mask, - GATT_MIN_READ_KEY_SIZE, - char_data_descriptor->is_variable, - &char_instance->descriptor_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); - } - if(release_data) { - free((void*)char_data); - } - } -} - -void flipper_gatt_characteristic_delete( - uint16_t svc_handle, - FlipperGattCharacteristicInstance* char_instance) { - tBleStatus status = aci_gatt_del_char(svc_handle, char_instance->handle); - if(status) { - FURI_LOG_E( - TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); - } - free((void*)char_instance->characteristic); -} - -bool flipper_gatt_characteristic_update( - uint16_t svc_handle, - FlipperGattCharacteristicInstance* char_instance, - const void* source) { - furi_assert(char_instance); - const FlipperGattCharacteristicParams* char_descriptor = char_instance->characteristic; - FURI_LOG_D(TAG, "Updating %s char", char_descriptor->name); - - const uint8_t* char_data = NULL; - uint16_t char_data_size = 0; - bool release_data = false; - if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataFixed) { - char_data = char_descriptor->data.fixed.ptr; - if(source) { - char_data = (uint8_t*)source; - } - char_data_size = char_descriptor->data.fixed.length; - } else if(char_descriptor->data_prop_type == FlipperGattCharacteristicDataCallback) { - const void* context = char_descriptor->data.callback.context; - if(source) { - context = source; - } - release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); - } - - tBleStatus result = aci_gatt_update_char_value( - svc_handle, char_instance->handle, 0, char_data_size, char_data); - if(result) { - FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); - } - if(release_data) { - free((void*)char_data); - } - return result != BLE_STATUS_SUCCESS; -} \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/gatt_char.h b/firmware/targets/f7/ble_glue/services/gatt_char.h deleted file mode 100644 index 959ab67a4..000000000 --- a/firmware/targets/f7/ble_glue/services/gatt_char.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// Callback signature for getting characteristic data -// Is called when characteristic is created to get max data length. Data ptr is NULL in this case -// The result is passed to aci_gatt_add_char as "Char_Value_Length" -// For updates, called with a context - see flipper_gatt_characteristic_update -// Returns true if *data ownership is transferred to the caller and will be freed -typedef bool (*cbFlipperGattCharacteristicData)( - const void* context, - const uint8_t** data, - uint16_t* data_len); - -typedef enum { - FlipperGattCharacteristicDataFixed, - FlipperGattCharacteristicDataCallback, -} FlipperGattCharacteristicDataType; - -typedef struct { - Char_Desc_Uuid_t uuid; - struct { - cbFlipperGattCharacteristicData fn; - const void* context; - } data_callback; - uint8_t uuid_type; - uint8_t max_length; - uint8_t security_permissions; - uint8_t access_permissions; - uint8_t gatt_evt_mask; - uint8_t is_variable; -} FlipperGattCharacteristicDescriptorParams; - -typedef struct { - const char* name; - FlipperGattCharacteristicDescriptorParams* descriptor_params; - union { - struct { - const uint8_t* ptr; - uint16_t length; - } fixed; - struct { - cbFlipperGattCharacteristicData fn; - const void* context; - } callback; - } data; - Char_UUID_t uuid; - // Some packed bitfields to save space - FlipperGattCharacteristicDataType data_prop_type : 2; - uint8_t is_variable : 2; - uint8_t uuid_type : 2; - uint8_t char_properties; - uint8_t security_permissions; - uint8_t gatt_evt_mask; -} FlipperGattCharacteristicParams; - -_Static_assert( - sizeof(FlipperGattCharacteristicParams) == 36, - "FlipperGattCharacteristicParams size must be 36 bytes"); - -typedef struct { - const FlipperGattCharacteristicParams* characteristic; - uint16_t handle; - uint16_t descriptor_handle; -} FlipperGattCharacteristicInstance; - -// Initialize a characteristic instance; copies the characteristic descriptor into the instance -void flipper_gatt_characteristic_init( - uint16_t svc_handle, - const FlipperGattCharacteristicParams* char_descriptor, - FlipperGattCharacteristicInstance* char_instance); - -// Delete a characteristic instance; frees the copied characteristic descriptor from the instance -void flipper_gatt_characteristic_delete( - uint16_t svc_handle, - FlipperGattCharacteristicInstance* char_instance); - -// Update a characteristic instance; if source==NULL, uses the data from the characteristic -// - For fixed data, fixed.ptr is used as the source if source==NULL -// - For callback-based data, collback.context is passed as the context if source==NULL -bool flipper_gatt_characteristic_update( - uint16_t svc_handle, - FlipperGattCharacteristicInstance* char_instance, - const void* source); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c deleted file mode 100644 index 11f10b7b3..000000000 --- a/firmware/targets/f7/ble_glue/services/hid_service.c +++ /dev/null @@ -1,365 +0,0 @@ -#include "hid_service.h" -#include "app_common.h" -#include -#include "gatt_char.h" - -#include - -#define TAG "BtHid" - -typedef enum { - HidSvcGattCharacteristicProtocolMode = 0, - HidSvcGattCharacteristicReportMap, - HidSvcGattCharacteristicInfo, - HidSvcGattCharacteristicCtrlPoint, - HidSvcGattCharacteristicLed, - HidSvcGattCharacteristicCount, -} HidSvcGattCharacteristicId; - -typedef struct { - uint8_t report_idx; - uint8_t report_type; -} HidSvcReportId; - -static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); - -static bool - hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { - const HidSvcReportId* report_id = context; - *data_len = sizeof(HidSvcReportId); - if(data) { - *data = (const uint8_t*)report_id; - } - return false; -} - -typedef struct { - const void* data_ptr; - uint16_t data_len; -} HidSvcDataWrapper; - -static bool - hid_svc_report_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { - const HidSvcDataWrapper* report_data = context; - if(data) { - *data = report_data->data_ptr; - *data_len = report_data->data_len; - } else { - *data_len = HID_SVC_REPORT_MAP_MAX_LEN; - } - return false; -} - -// LED Descriptor params for BadBT - -static uint8_t led_desc_context_buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; - -static FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_led = { - .uuid_type = UUID_TYPE_16, - .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, - .max_length = HID_SVC_REPORT_REF_LEN, - .data_callback.fn = hid_svc_char_desc_data_callback, - .data_callback.context = led_desc_context_buf, - .security_permissions = ATTR_PERMISSION_NONE, - .access_permissions = ATTR_ACCESS_READ_WRITE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT, -}; - -static const FlipperGattCharacteristicParams hid_svc_chars[HidSvcGattCharacteristicCount] = { - [HidSvcGattCharacteristicProtocolMode] = - {.name = "Protocol Mode", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = PROTOCOL_MODE_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [HidSvcGattCharacteristicReportMap] = - {.name = "Report Map", - .data_prop_type = FlipperGattCharacteristicDataCallback, - .data.callback.fn = hid_svc_report_data_callback, - .data.callback.context = NULL, - .uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_VARIABLE}, - [HidSvcGattCharacteristicInfo] = - {.name = "HID Information", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = HID_SVC_INFO_LEN, - .data.fixed.ptr = NULL, - .uuid.Char_UUID_16 = HID_INFORMATION_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [HidSvcGattCharacteristicCtrlPoint] = - {.name = "HID Control Point", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = HID_SVC_CONTROL_POINT_LEN, - .uuid.Char_UUID_16 = HID_CONTROL_POINT_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_WRITE_WITHOUT_RESP, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, - .is_variable = CHAR_VALUE_LEN_CONSTANT}, - [HidSvcGattCharacteristicLed] = - { - .name = - "HID LED State", // LED Characteristic and descriptor for BadBT to get numlock state for altchars - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = 1, - .uuid.Char_UUID_16 = REPORT_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE | - GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, - .is_variable = CHAR_VALUE_LEN_CONSTANT, - .descriptor_params = &hid_svc_char_descr_led, - }, -}; - -static const FlipperGattCharacteristicDescriptorParams hid_svc_char_descr_template = { - .uuid_type = UUID_TYPE_16, - .uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID, - .max_length = HID_SVC_REPORT_REF_LEN, - .data_callback.fn = hid_svc_char_desc_data_callback, - .security_permissions = ATTR_PERMISSION_NONE, - .access_permissions = ATTR_ACCESS_READ_WRITE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_CONSTANT, -}; - -static const FlipperGattCharacteristicParams hid_svc_report_template = { - .name = "Report", - .data_prop_type = FlipperGattCharacteristicDataCallback, - .data.callback.fn = hid_svc_report_data_callback, - .data.callback.context = NULL, - .uuid.Char_UUID_16 = REPORT_CHAR_UUID, - .uuid_type = UUID_TYPE_16, - .char_properties = CHAR_PROP_READ | CHAR_PROP_NOTIFY, - .security_permissions = ATTR_PERMISSION_NONE, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_VARIABLE, -}; - -typedef struct { - uint16_t svc_handle; - FlipperGattCharacteristicInstance chars[HidSvcGattCharacteristicCount]; - FlipperGattCharacteristicInstance input_report_chars[HID_SVC_INPUT_REPORT_COUNT]; - FlipperGattCharacteristicInstance output_report_chars[HID_SVC_OUTPUT_REPORT_COUNT]; - FlipperGattCharacteristicInstance feature_report_chars[HID_SVC_FEATURE_REPORT_COUNT]; - // led state - HidLedStateEventCallback led_state_event_callback; - void* led_state_ctx; -} HIDSvc; - -static HIDSvc* hid_svc = NULL; - -static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { - SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; - hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); - evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; - // aci_gatt_attribute_modified_event_rp0* attribute_modified; - if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { - if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { - // Process modification events - ret = SVCCTL_EvtAckFlowEnable; - } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { - // Process notification confirmation - ret = SVCCTL_EvtAckFlowEnable; - } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { - // LED Characteristic and descriptor for BadBT to get numlock state for altchars - // - // Process write request - aci_gatt_write_permit_req_event_rp0* req = - (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; - - furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); - - // this check is likely to be incorrect, it will actually work in our case - // but we need to investigate gatt api to see what is the rules - // that specify attibute handle value from char handle (or the reverse) - if(req->Attribute_Handle == (hid_svc->chars[HidSvcGattCharacteristicLed].handle + 1)) { - hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); - aci_gatt_write_resp( - req->Connection_Handle, - req->Attribute_Handle, - 0x00, /* write_status = 0 (no error))*/ - 0x00, /* err_code */ - req->Data_Length, - req->Data); - aci_gatt_write_char_value( - req->Connection_Handle, - hid_svc->chars[HidSvcGattCharacteristicLed].handle, - req->Data_Length, - req->Data); - ret = SVCCTL_EvtAckFlowEnable; - } - } - } - return ret; -} - -void hid_svc_start() { - tBleStatus status; - hid_svc = malloc(sizeof(HIDSvc)); - Service_UUID_t svc_uuid = {}; - - // Register event handler - SVCCTL_RegisterSvcHandler(hid_svc_event_handler); - // Add service - svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; - /** - * Add Human Interface Device Service - */ - status = aci_gatt_add_service( - UUID_TYPE_16, - &svc_uuid, - PRIMARY_SERVICE, - 2 + /* protocol mode */ - (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + - (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + - 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ - &hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to add HID service: %d", status); - } - - for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); - } - uint8_t protocol_mode = 1; - flipper_gatt_characteristic_update( - hid_svc->svc_handle, - &hid_svc->chars[HidSvcGattCharacteristicProtocolMode], - &protocol_mode); - - // reports - FlipperGattCharacteristicDescriptorParams hid_svc_char_descr; - FlipperGattCharacteristicParams report_char; - HidSvcReportId report_id; - - memcpy(&hid_svc_char_descr, &hid_svc_char_descr_template, sizeof(hid_svc_char_descr)); - memcpy(&report_char, &hid_svc_report_template, sizeof(report_char)); - - hid_svc_char_descr.data_callback.context = &report_id; - report_char.descriptor_params = &hid_svc_char_descr; - - typedef struct { - uint8_t report_type; - uint8_t report_count; - FlipperGattCharacteristicInstance* chars; - } HidSvcReportCharProps; - - HidSvcReportCharProps hid_report_chars[] = { - {0x01, HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, - {0x02, HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, - {0x03, HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, - }; - - for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); - report_type_idx++) { - report_id.report_type = hid_report_chars[report_type_idx].report_type; - for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; - report_idx++) { - report_id.report_idx = report_idx + 1; - flipper_gatt_characteristic_init( - hid_svc->svc_handle, - &report_char, - &hid_report_chars[report_type_idx].chars[report_idx]); - } - } -} - -bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - - HidSvcDataWrapper report_data = { - .data_ptr = data, - .data_len = len, - }; - return flipper_gatt_characteristic_update( - hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicReportMap], &report_data); -} - -bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len) { - furi_assert(data); - furi_assert(hid_svc); - furi_assert(input_report_num < HID_SVC_INPUT_REPORT_COUNT); - - HidSvcDataWrapper report_data = { - .data_ptr = data, - .data_len = len, - }; - return flipper_gatt_characteristic_update( - hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); -} - -bool hid_svc_update_info(uint8_t* data) { - furi_assert(data); - furi_assert(hid_svc); - - return flipper_gatt_characteristic_update( - hid_svc->svc_handle, &hid_svc->chars[HidSvcGattCharacteristicInfo], &data); -} - -void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { - furi_assert(hid_svc); - furi_assert(callback); - furi_assert(context); - - hid_svc->led_state_event_callback = callback; - hid_svc->led_state_ctx = context; -} - -bool hid_svc_is_started() { - return hid_svc != NULL; -} - -void hid_svc_stop() { - tBleStatus status; - if(hid_svc) { - // Delete characteristics - for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_delete(hid_svc->svc_handle, &hid_svc->chars[i]); - } - - typedef struct { - uint8_t report_count; - FlipperGattCharacteristicInstance* chars; - } HidSvcReportCharProps; - - HidSvcReportCharProps hid_report_chars[] = { - {HID_SVC_INPUT_REPORT_COUNT, hid_svc->input_report_chars}, - {HID_SVC_OUTPUT_REPORT_COUNT, hid_svc->output_report_chars}, - {HID_SVC_FEATURE_REPORT_COUNT, hid_svc->feature_report_chars}, - }; - - for(size_t report_type_idx = 0; report_type_idx < COUNT_OF(hid_report_chars); - report_type_idx++) { - for(size_t report_idx = 0; report_idx < hid_report_chars[report_type_idx].report_count; - report_idx++) { - flipper_gatt_characteristic_delete( - hid_svc->svc_handle, &hid_report_chars[report_type_idx].chars[report_idx]); - } - } - - // Delete service - status = aci_gatt_del_service(hid_svc->svc_handle); - if(status) { - FURI_LOG_E(TAG, "Failed to delete HID service: %d", status); - } - free(hid_svc); - hid_svc = NULL; - } -} diff --git a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc b/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc deleted file mode 100644 index a297d9ad6..000000000 --- a/firmware/targets/f7/ble_glue/services/serial_service_uuid.inc +++ /dev/null @@ -1,12 +0,0 @@ - -static const Service_UUID_t service_uuid = { .Service_UUID_128 = \ - { 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f }}; - -#define SERIAL_SVC_TX_CHAR_UUID \ - { 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } -#define SERIAL_SVC_RX_CHAR_UUID \ - { 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } -#define SERIAL_SVC_FLOW_CONTROL_UUID \ - { 0x00, 0x00, 0xfe, 0x63, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } -#define SERIAL_SVC_RPC_STATUS_UUID \ - { 0x00, 0x00, 0xfe, 0x64, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index b5fa5cb05..1139add6c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -8,7 +8,8 @@ #include #include #include -#include +#include "battery_service.h" + #include #define TAG "FuriHalBt" diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 43b278578..860edfcd4 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -1,11 +1,11 @@ #include #include -#include -#include -#include +#include "usb_hid.h" +#include "dev_info_service.h" +#include "battery_service.h" +#include "hid_service.h" #include -#include #define FURI_HAL_BT_INFO_BASE_USB_SPECIFICATION (0x0101) #define FURI_HAL_BT_INFO_COUNTRY_CODE (0x00) @@ -220,7 +220,7 @@ void furi_hal_bt_hid_start() { FURI_HAL_BT_HID_INFO_FLAG_REMOTE_WAKE_MSK | FURI_HAL_BT_HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK, }; - hid_svc_update_info(hid_info_val); + hid_svc_update_info(hid_info_val, sizeof(hid_info_val)); } void furi_hal_bt_hid_stop() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c index 2927d946f..2539e6bd0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -1,7 +1,7 @@ #include -#include -#include -#include +#include "dev_info_service.h" +#include "battery_service.h" +#include "serial_service.h" #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index a04b70ecd..f128b1064 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h index 0472d31d1..1b6e79ab0 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_serial.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_serial.h @@ -1,6 +1,6 @@ #pragma once -#include +#include "serial_service.h" #ifdef __cplusplus extern "C" { From b6dbf25f85b55d4058e471b50433bdc87fd5f1f4 Mon Sep 17 00:00:00 2001 From: Leopold Date: Wed, 14 Jun 2023 18:49:26 +0800 Subject: [PATCH 188/370] furi_hal_nfc: fix rfalTransceiveBitsBlockingTx's 4th argument to bits count rather than bytes count (#2773) --- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 8910d887b..c4e7ad9f9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -467,7 +467,7 @@ bool furi_hal_nfc_emulate_nfca( buff_tx, buff_tx_len, buff_rx, - sizeof(buff_rx), + rfalConvBytesToBits(buff_rx_size), &buff_rx_len, data_type, RFAL_FWT_NONE); @@ -491,7 +491,7 @@ bool furi_hal_nfc_emulate_nfca( buff_tx, buff_tx_len, buff_rx, - sizeof(buff_rx), + rfalConvBytesToBits(buff_rx_size), &buff_rx_len, data_type, RFAL_FWT_NONE); From 8088c525518351d1e931c7e8456d8f4c37847c95 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:00:24 +0300 Subject: [PATCH 189/370] fix --- scripts/version.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/version.py b/scripts/version.py index 3b2502af7..be1c51ce7 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -62,9 +62,11 @@ class GitVersion: "GIT_BRANCH": branch, "VERSION": version, "BUILD_DIRTY": dirty and 1 or 0, - "GIT_ORIGIN": ",".join(self._get_git_origins()), + "GIT_ORIGIN": "https://github.com/DarkFlippers/unleashed-firmware.git", "GIT_COMMIT_DATE": commit_date, } + + # "GIT_ORIGIN": ",".join(self._get_git_origins()), def _get_git_origins(self): try: From ee8bbdcfe057e019367b151a6271059e526e2502 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:05:05 +0300 Subject: [PATCH 190/370] remake just a bit, disable external on command end --- applications/main/subghz/subghz_cli.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 22a3ed3c7..1e50dfc25 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -779,15 +779,10 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { FuriString* cmd = furi_string_alloc(); - if(!furi_hal_power_is_otg_enabled()) { - furi_hal_power_enable_otg(); - } - + // Enable power for External CC1101 if it is connected + furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available furi_delay_ms(15); - - furi_hal_subghz_select_radio_type(SubGhzRadioExternal); - furi_hal_subghz_init_radio_type(SubGhzRadioExternal); - if(!furi_hal_subghz_check_radio()) { furi_hal_subghz_select_radio_type(SubGhzRadioInternal); furi_hal_subghz_init_radio_type(SubGhzRadioInternal); @@ -849,6 +844,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { subghz_cli_command_print_usage(); } while(false); + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); + // Reinit SPI handles for internal radio / nfc + furi_hal_subghz_init_radio_type(SubGhzRadioInternal); + furi_string_free(cmd); } From 5d9f4b13338e67f2829311e8d725b77a2ef0f9f1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:29:59 +0300 Subject: [PATCH 191/370] Changelog & more rgb patch info --- .drone.yml | 14 +++++++------- CHANGELOG.md | 46 ++++++---------------------------------------- 2 files changed, 13 insertions(+), 47 deletions(-) diff --git a/.drone.yml b/.drone.yml index b626de5c4..d1659b756 100644 --- a/.drone.yml +++ b/.drone.yml @@ -91,7 +91,7 @@ steps: - echo '' >> CHANGELOG.md - echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md - echo '' >> CHANGELOG.md - - echo '### [Version with RGB patch - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md + - echo '### [Version with RGB patch - only for hardware mod! - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md - echo '' >> CHANGELOG.md - echo '## [Version with Extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md environment: @@ -252,10 +252,10 @@ steps: [-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n) - [-Version with RGB patch - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz&channel=release-cfw&version=${DRONE_TAG}r) + [-Version with RGB patch - only for hardware mod! - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz&channel=release-cfw&version=${DRONE_TAG}r) - [-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz) + [-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz) [-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}e.tgz&channel=release-cfw&version=${DRONE_TAG}e)" @@ -271,7 +271,7 @@ steps: commands: - wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - chmod +x ./discord.sh - - ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' + - ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - only for hardware mod! - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' - name: "Send extra pack build to telegram" image: appleboy/drone-telegram @@ -474,10 +474,10 @@ steps: [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}) - [-Version with RGB patch - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}r) + [-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}r) - [-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz) + [-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz) [-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)" @@ -515,7 +515,7 @@ steps: commands: - wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - chmod +x ./discord.sh - - ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with RGB patch - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' + - ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with RGB patch - only for hardware mod! - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - only for hardware mod! - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' trigger: branch: diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dbc1db79..a03f11808 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,44 +1,10 @@ ### New changes -* If you have copied any apps manually into `apps` folder - remove `apps` folder or that specific apps you copied on your microSD before installing this release to avoid issues due to OFW API version update! If you using regular builds or extra pack builds (e) without your manually added apps, all included apps will be installed automatically, no extra actions needed! -* Settings->LCD and Notifications will be resetted to default due to new Contrast setting from OFW -* Core2 (Crash in idle) issues was reduced to current possible minimum, you can try using DeepSleep again (Sleep Method = Default) (+ more checks was added, if you get `Slow HSE/PLL startup` message more than one time, create issue with steps what to do to reproduce it again) ------ -* Plugins: **New RFID 125KHz and iButton Fuzzers (remake from scratch + new features)** (by @gid9798 | PR #507) -* Plugins: SubGHz Bruteforcer -> Time delay (between signals) setting (hold Up in main screen(says Up to Save)) + allow more repeats (by @gid9798 & @xMasterX) -* Plugins: Update TOTP (Authenticator) [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) -* Plugins: Unitemp SCD30 support (PR in unitemp repo by @divinebird / fixed by @xMasterX) -* Plugins: Fix ProtoView issue #503 -> (Broken saved files with custom modulation) -* SubGHz: Added 430, 431 MHz to default list -* SubGHz: Remove broken modulation that was causing buffer overrun (fixes issue #506) -* SubGHz: Notifications fixes (by @wosk | PR #464) -* GUI: `Byte input` new feature: editor without keyboard (press Up until you get into new input, then use up/down to input values) (by @gid9798 | PR #509) -* CI/CD: Provide builds with RGB patch for modded flippers (with special led board installed) -* Infrared: `RCA` protocol support -* Infrared: Update universal remote assets - add new ACs and TCL TV -* API: Add furi_hal_version_uid_default (+ Fix TOTP) (by @ClaraCrazy | PR #502) -* OFW PR 2760: NFC: Improvements to NFC Magic app (by AloneLiberty) -* OFW PR 2756: fix: make dialog_file_browser_set_basic_options initialize all fields (by JarvisCraft) -* OFW: Fix reading Mifare Classic cards with unusual access conditions and fix emulation of unknown keys -* OFW: fbt: stable build dates -* OFW: weather_station: add oregon3 with THGR221 -* OFW: Services: simplify api (DOLPHIN_DEED->dolphin_deed - function instead of macros + remake all apps in extra pack and main fw to use new API) -> **Breaking API change, API version was changed from 29.x to 30.x** -* OFW: Core2, SRAM2: provide safety gap -* OFW: FuriHal: always clock SMPS from HSI -* OFW: ble: refactored bt gatt characteristics setup (+ remake of BT HID Led descriptor in new way to work with this changes) -* OFW: Scripts: WiFi board updater -* OFW: github: re-enabled f18 build -* OFW: added ISO15693 (NfcV) (was already added before, so we just updated it with latest changes) -* OFW: fbt: added Flipper selection when multiple are connected over USB -* OFW: fbt, ufbt: added checks for appid in app manifests -* OFW: Fix core2 permisions -* OFW: SubGhz: add subghz_protocol_registry external API (was already in our API but in different way) -* OFW: Furi: smaller critical enter and critical exit macro -* OFW: Serial_CLI: Fixing serial cli logger error so it sounds more concise -* OFW: Remove unused resources -* OFW: Dolphin: new animation -* OFW: f7: add PB9 to debug pins -* OFW: Settings: add contrast adjustment -> **Settings->LCD and Notifications will be resetted to default values one time after installing** -* OFW: FuriHal: add system setting to device info, bump device info version +* BLE: Revert BLE gatt characteristics refactoring temporarily -> **Should fix HID issues on older iOS, and maybe some issues with android app** +* SubGHz: Added external cc1101 module at CLI (by @Sil333033 & @xMasterX | PR #513) +* SubGHz: Remove unused global var +* Plugins: Fix ProtoView issue #503 again -> (Broken saved files with custom modulation) +* OFW: furi_hal_nfc: fix rfalTransceiveBitsBlockingTx's 4th argument to bits count rather than bytes count +* OFW: FuriHal: remove clock startup time tracking from clean builds #### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) From 1e41b27c9ab64150f85286cbefb3fd2c0b2eef37 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:25:25 +0100 Subject: [PATCH 192/370] Fix git origin url --- scripts/version.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/scripts/version.py b/scripts/version.py index 84a950f7d..a3732f56b 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -60,23 +60,18 @@ class GitVersion: "GIT_BRANCH": branch, "VERSION": version, "BUILD_DIRTY": dirty and 1 or 0, - "GIT_ORIGIN": ",".join(self._get_git_origins()), + "GIT_ORIGIN": self._get_git_origin(), "GIT_COMMIT_DATE": commit_date, } - def _get_git_origins(self): + def _get_git_origin(self): try: - remotes = self._exec_git("remote -v") + branch = self._exec_git("branch --show-current") + remote = self._exec_git(f"config branch.{branch}.remote") + origin = self._exec_git(f"remote get-url {remote}") + return origin except subprocess.CalledProcessError: - return set() - origins = set() - for line in remotes.split("\n"): - if not line: - continue - _, destination = line.split("\t") - url, _ = destination.split(" ") - origins.add(url) - return origins + return "" def _exec_git(self, args): cmd = ["git"] From 469d7a2b7fc9d11a8e16deda904f37687d34cc20 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:47:19 +0100 Subject: [PATCH 193/370] Update stack upgrade patch --- stack_1.15.0_upgrade.patch | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/stack_1.15.0_upgrade.patch b/stack_1.15.0_upgrade.patch index 2df58a508..8416a9915 100644 --- a/stack_1.15.0_upgrade.patch +++ b/stack_1.15.0_upgrade.patch @@ -1,8 +1,8 @@ diff --git a/fbt_options.py b/fbt_options.py -index 7c5857e09f..acc6b8a982 100644 +index bb4ee592c0..a1b6032b29 100644 --- a/fbt_options.py +++ b/fbt_options.py -@@ -22,7 +22,7 @@ DIST_SUFFIX = "XFW-0048_03062023" +@@ -25,7 +25,7 @@ DIST_SUFFIX = f"XFW-0048_{datetime.datetime.today().strftime('%d%m%Y')}" COPRO_OB_DATA = "scripts/ob.data" # Must match lib/stm32wb_copro version @@ -37,7 +37,7 @@ index aaa755a362..ee5115cfed 100644 * Define Advertising parameters */ diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c -index 35f53ae22c..d288528223 100644 +index 78e789ac30..b443bee21f 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -33,7 +33,8 @@ PLACE_IN_SECTION("MB_MEM2") @@ -51,7 +51,7 @@ index 35f53ae22c..d288528223 100644 /** * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c -index 7a2148b6b8..c0418d9fe8 100644 +index 4fc4d521be..37d8f7cd04 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; @@ -65,19 +65,19 @@ index 7a2148b6b8..c0418d9fe8 100644 typedef struct { FuriMutex* hci_mtx; -@@ -70,6 +70,12 @@ static const SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { - .min_tx_power = 0, - .max_tx_power = 0, - .rx_model_config = 1, -+ /* New stack (13.3->15.0) */ -+ .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set -+ .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set -+ .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB -+ .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB -+ .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) - }}; - - bool ble_app_init() { +@@ -88,6 +88,12 @@ bool ble_app_init() { + .min_tx_power = 0, + .max_tx_power = 0, + .rx_model_config = 1, ++ /* New stack (13.3->15.0) */ ++ .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set ++ .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set ++ .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB ++ .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB ++ .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) + }}; + status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); + if(status) { diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h index 0e4c8b398d..85f734b62c 100644 --- a/firmware/targets/f7/ble_glue/ble_const.h From 4afca67cf56285c3950576aac512a01a53825cc3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 14 Jun 2023 20:19:09 +0100 Subject: [PATCH 194/370] Tweak firmware info about page --- applications/settings/about/about.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 3fc31b1d6..b7ebbd159 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -138,7 +138,7 @@ static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* furi_hal_info_get_api_version(&api_major, &api_minor); furi_string_cat_printf( buffer, - "%s %s\n%s F%d %d.%d %s\nhttps://flipper-xtre.me/", + "%s %s\n%s F%d:%d.%d %s\nhttps://flipper-xtre.me/", version_get_version(ver), version_get_builddate(ver), version_get_githash(ver), From 1f3e621a3167ff00c3bc2a551d3fc0c2738a6104 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 15 Jun 2023 02:53:08 +0300 Subject: [PATCH 195/370] Fix wifi marauder UART deinit --- .../external/wifi_marauder_companion/wifi_marauder_uart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c index 5ce6480f2..b121cfd21 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c @@ -106,6 +106,7 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_thread_free(uart->rx_thread); furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); + furi_hal_uart_deinit(uart->channel); furi_hal_console_enable(); free(uart); From 3c2d56e46be9edb3d544463b734ddec6c3ffea1d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 15 Jun 2023 03:08:56 +0300 Subject: [PATCH 196/370] Add check --- .../external/wifi_marauder_companion/wifi_marauder_uart.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c index b121cfd21..080280b5f 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c @@ -106,7 +106,9 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_thread_free(uart->rx_thread); furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); - furi_hal_uart_deinit(uart->channel); + if(uart->channel == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(uart->channel); + } furi_hal_console_enable(); free(uart); From dd0c53fce1a9d639ebeb0a1b828063c1928ea24e Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 15 Jun 2023 02:33:36 +0100 Subject: [PATCH 197/370] Fix 'WIP' NFC-F --- firmware/targets/f7/api_symbols.csv | 1 - lib/nfc/nfc_device.c | 70 +++--- lib/nfc/protocols/felica.c | 352 ++++++++++++++-------------- 3 files changed, 216 insertions(+), 207 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index f77640f09..ee0dd0c31 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -893,7 +893,6 @@ Function,-,felica_estimate_timing_us,uint_least32_t,"uint_least8_t, uint_least8_ Function,-,felica_get_ic_type,FelicaICType,uint8_t* Function,-,felica_get_service_name,FuriString*,FelicaService* Function,-,felica_get_system_name,FuriString*,FelicaSystem* -Function,-,felica_lite_can_read_without_mac,_Bool,"uint8_t*, uint8_t" Function,-,felica_lite_dump_data,_Bool,"FuriHalNfcTxRxContext*, FelicaReader*, FelicaData*, FelicaSystem*" Function,-,felica_lite_is_issued,_Bool,FelicaLiteInfo* Function,-,felica_lite_prepare_unencrypted_read,uint8_t,"uint8_t*, const FelicaReader*, _Bool, const uint8_t*, uint8_t" diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index c2d866c03..cd1f86de7 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1120,59 +1120,60 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* static bool nfc_device_save_felica_lite(FlipperFormat* file, FelicaLiteInfo* info) { bool saved = false; FuriString* key = furi_string_alloc(); + FuriString* temp = furi_string_alloc(); do { flipper_format_write_comment_cstr(file, "Lite(-S) System"); flipper_format_write_hex( - file, "Data Format Code", info->data_format_code, sizeof(uint16_t)); + file, "Data Format Code", (uint8_t*)&info->data_format_code, sizeof(uint16_t)); flipper_format_write_hex(file, "ID Arbitrary Value", info->ID_value, 6); flipper_format_write_hex(file, "Memory Config", info->memory_config, FELICA_BLOCK_SIZE); for(uint8_t block_num = 0; block_num < 14; block_num++) { - FuriString* spad_str = furi_string_alloc(); + furi_string_reset(temp); for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { if(info->S_PAD[block_num] != NULL) { - furi_string_cat_printf(spad_str, "%02X ", info->S_PAD[block_num][i]); + furi_string_cat_printf(temp, "%02X ", info->S_PAD[block_num][i]); } else { - furi_string_cat_printf(spad_str, "?? "); + furi_string_cat_printf(temp, "?? "); } } furi_string_printf(key, "S_PAD%d", block_num); - flipper_format_write_string(file, furi_string_get_cstr(key), spad_str); + flipper_format_write_string(file, furi_string_get_cstr(key), temp); } - FuriString* reg_str = furi_string_alloc(); + furi_string_reset(temp); for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { if(info->REG != NULL) { - furi_string_cat_printf(reg_str, "%02X ", info->REG[i]); + furi_string_cat_printf(temp, "%02X ", info->REG[i]); } else { - furi_string_cat_printf(reg_str, "?? "); + furi_string_cat_printf(temp, "?? "); } } - flipper_format_write_string(file, "REG", reg_str); + flipper_format_write_string(file, "REG", temp); flipper_format_write_hex( - file, "Card Key Version", &info->card_key_version, sizeof(uint16_t)); - FuriString* ck1_str = furi_string_alloc(); + file, "Card Key Version", (uint8_t*)&info->card_key_version, sizeof(uint16_t)); + furi_string_reset(temp); for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { if(info->REG != NULL) { - furi_string_cat_printf(ck1_str, "%02X ", info->card_key_1[i]); + furi_string_cat_printf(temp, "%02X ", info->card_key_1[i]); } else { - furi_string_cat_printf(ck1_str, "?? "); + furi_string_cat_printf(temp, "?? "); } } - flipper_format_write_string(file, "Card Key 1", ck1_str); + flipper_format_write_string(file, "Card Key 1", temp); - FuriString* ck2_str = furi_string_alloc(); + furi_string_reset(temp); for(size_t i = 0; i < FELICA_BLOCK_SIZE; i++) { if(info->REG != NULL) { - furi_string_cat_printf(ck2_str, "%02X ", info->card_key_2[i]); + furi_string_cat_printf(temp, "%02X ", info->card_key_2[i]); } else { - furi_string_cat_printf(ck2_str, "?? "); + furi_string_cat_printf(temp, "?? "); } } - flipper_format_write_string(file, "Card Key 2", ck2_str); + flipper_format_write_string(file, "Card Key 2", temp); flipper_format_write_hex(file, "Fixed Challenge MAC Response", info->MAC, 8); @@ -1184,18 +1185,22 @@ static bool nfc_device_save_felica_lite(FlipperFormat* file, FelicaLiteInfo* inf } while(false); + furi_string_free(temp); + furi_string_free(key); return saved; } +static bool nfc_device_save_felica_node(FlipperFormat* file, FelicaNode* node); + static bool nfc_device_save_felica_area(FlipperFormat* file, FelicaArea* area) { bool saved = false; FuriString* prefix = furi_string_alloc_printf("Area %d", area->number); FuriString* key = furi_string_alloc(); do { - furi_string_printf(key, "%s Can Create Subareas", prefix); + furi_string_printf(key, "%s Can Create Subareas", furi_string_get_cstr(prefix)); flipper_format_write_bool(file, furi_string_get_cstr(key), &area->can_create_subareas, 1); - furi_string_printf(key, "%s End Service Code", prefix); + furi_string_printf(key, "%s End Service Code", furi_string_get_cstr(prefix)); flipper_format_write_hex( file, furi_string_get_cstr(key), (uint8_t*)&area->end_service_code, sizeof(uint16_t)); @@ -1212,6 +1217,8 @@ static bool nfc_device_save_felica_area(FlipperFormat* file, FelicaArea* area) { saved = true; } while(false); + furi_string_free(prefix); + furi_string_free(key); return saved; } @@ -1221,40 +1228,40 @@ static bool nfc_device_save_felica_service(FlipperFormat* file, FelicaService* s FuriString* key = furi_string_alloc(); do { - furi_string_printf(key, "%s Is Extended Overlap", prefix); + furi_string_printf(key, "%s Is Extended Overlap", furi_string_get_cstr(prefix)); flipper_format_write_bool( file, furi_string_get_cstr(key), &service->is_extended_overlap, 1); if(service->is_extended_overlap) { - furi_string_printf(key, "%s Overlap Target", prefix); + furi_string_printf(key, "%s Overlap Target", furi_string_get_cstr(prefix)); flipper_format_write_hex( file, furi_string_get_cstr(key), (uint8_t*)&service->overlap_target, sizeof(uint16_t)); - furi_string_printf(key, "%s Block Start", prefix); + furi_string_printf(key, "%s Block Start", furi_string_get_cstr(prefix)); const uint32_t block_start = service->block_start; flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_start, 1); - furi_string_printf(key, "%s Block Count", prefix); + furi_string_printf(key, "%s Block Count", furi_string_get_cstr(prefix)); const uint32_t block_count = service->block_count; flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1); uint32_t i = 0; for M_EACH(block, service->blocks, FelicaBlockArray_t) { - furi_string_printf(key, "%s Block %d", prefix, i); + furi_string_printf(key, "%s Block %ld", furi_string_get_cstr(prefix), i); flipper_format_write_hex( file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE); } } else { - furi_string_printf(key, "%s Block Count", prefix); + furi_string_printf(key, "%s Block Count", furi_string_get_cstr(prefix)); uint32_t block_count = FelicaBlockArray_size(service->blocks); flipper_format_write_uint32(file, furi_string_get_cstr(key), &block_count, 1); uint32_t i = 0; for M_EACH(block, service->blocks, FelicaBlockArray_t) { - furi_string_printf(key, "%s Block %d", prefix, i); + furi_string_printf(key, "%s Block %ld", furi_string_get_cstr(prefix), i); flipper_format_write_hex( file, furi_string_get_cstr(key), block->data, FELICA_BLOCK_SIZE); } @@ -1262,20 +1269,23 @@ static bool nfc_device_save_felica_service(FlipperFormat* file, FelicaService* s saved = true; } while(false); + + furi_string_free(prefix); + furi_string_free(key); + return saved; } static bool nfc_device_save_felica_node(FlipperFormat* file, FelicaNode* node) { bool saved = false; - FuriString* key = furi_string_alloc(); do { if(node->type == FelicaNodeTypeArea) { - if(!nfc_device_save_felica_node(file, node->area)) { + if(!nfc_device_save_felica_area(file, node->area)) { saved = false; break; } } else if(node->type == FelicaNodeTypeService) { - if(!nfc_device_save_felica_service(file, node->area)) { + if(!nfc_device_save_felica_service(file, node->service)) { saved = false; break; } diff --git a/lib/nfc/protocols/felica.c b/lib/nfc/protocols/felica.c index bf9da65f5..182893527 100644 --- a/lib/nfc/protocols/felica.c +++ b/lib/nfc/protocols/felica.c @@ -152,214 +152,214 @@ FelicaICType felica_get_ic_type(uint8_t* PMm) { return FelicaICType2K; } -static void felica_lite_diversify_key(uint8_t* id_block, uint8_t* master_key, uint8_t* card_key) { - uint8_t ZERO[8] = {0}; - uint8_t L[8]; - mbedtls_des3_context ctx; - mbedtls_des3_init(&ctx); - mbedtls_des3_set3key_enc(&ctx, master_key); - mbedtls_des3_crypt_ecb(&ctx, ZERO, L); - mbedtls_des3_free(&ctx); +// static void felica_lite_diversify_key(uint8_t* id_block, uint8_t* master_key, uint8_t* card_key) { +// uint8_t ZERO[8] = {0}; +// uint8_t L[8]; +// mbedtls_des3_context ctx; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set3key_enc(&ctx, master_key); +// mbedtls_des3_crypt_ecb(&ctx, ZERO, L); +// mbedtls_des3_free(&ctx); - uint8_t K1[8]; - for(int i = 0; i < 8; i++) { - K1[i] = L[i] << 1; - if(i < 7) { - K1[i] |= (L[i + 1] >> 7); - } - } +// uint8_t K1[8]; +// for(int i = 0; i < 8; i++) { +// K1[i] = L[i] << 1; +// if(i < 7) { +// K1[i] |= (L[i + 1] >> 7); +// } +// } - if((L[0] ^ 0x80) == 0) { - K1[7] ^= 0x1B; - } +// if((L[0] ^ 0x80) == 0) { +// K1[7] ^= 0x1B; +// } - uint8_t M1[8]; - uint8_t M2[8]; - memcpy(M1, id_block, 8); - memcpy(M2, id_block + 8, 8); - for(int i = 0; i < 8; i++) { - M2[i] ^= K1[i]; - } +// uint8_t M1[8]; +// uint8_t M2[8]; +// memcpy(M1, id_block, 8); +// memcpy(M2, id_block + 8, 8); +// for(int i = 0; i < 8; i++) { +// M2[i] ^= K1[i]; +// } - uint8_t C1[8]; - mbedtls_des3_init(&ctx); - mbedtls_des3_set3key_enc(&ctx, master_key); - mbedtls_des3_crypt_ecb(&ctx, M1, C1); - for(int i = 0; i < 8; i++) { - C1[i] ^= M2[i]; - } +// uint8_t C1[8]; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set3key_enc(&ctx, master_key); +// mbedtls_des3_crypt_ecb(&ctx, M1, C1); +// for(int i = 0; i < 8; i++) { +// C1[i] ^= M2[i]; +// } - mbedtls_des3_crypt_ecb(&ctx, C1, card_key); // T +// mbedtls_des3_crypt_ecb(&ctx, C1, card_key); // T - M1[0] ^= 0x80; // M'1 - mbedtls_des3_crypt_ecb(&ctx, M1, C1); // C'1 - for(int i = 0; i < 8; i++) { - C1[i] ^= M2[i]; - } +// M1[0] ^= 0x80; // M'1 +// mbedtls_des3_crypt_ecb(&ctx, M1, C1); // C'1 +// for(int i = 0; i < 8; i++) { +// C1[i] ^= M2[i]; +// } - mbedtls_des3_crypt_ecb(&ctx, C1, card_key + 8); // T' - mbedtls_des3_free(&ctx); -} +// mbedtls_des3_crypt_ecb(&ctx, C1, card_key + 8); // T' +// mbedtls_des3_free(&ctx); +// } -static void felica_lite_generate_session_key( - uint8_t* random_challenge, - uint8_t* card_key, - uint8_t* session_key) { - uint8_t RC1[8]; - uint8_t RC2[8]; - uint8_t CK[16]; +// static void felica_lite_generate_session_key( +// uint8_t* random_challenge, +// uint8_t* card_key, +// uint8_t* session_key) { +// uint8_t RC1[8]; +// uint8_t RC2[8]; +// uint8_t CK[16]; - for(int i = 0; i < 8; i++) { - RC1[i] = random_challenge[7 - i]; - RC2[i] = random_challenge[i]; - CK[i] = card_key[7 - i]; - CK[i + 8] = card_key[15 - i]; - } +// for(int i = 0; i < 8; i++) { +// RC1[i] = random_challenge[7 - i]; +// RC2[i] = random_challenge[i]; +// CK[i] = card_key[7 - i]; +// CK[i + 8] = card_key[15 - i]; +// } - mbedtls_des3_context ctx; +// mbedtls_des3_context ctx; - uint8_t SK1[8]; - mbedtls_des3_init(&ctx); - mbedtls_des3_set2key_enc(&ctx, CK); - mbedtls_des3_crypt_ecb(&ctx, RC1, SK1); +// uint8_t SK1[8]; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set2key_enc(&ctx, CK); +// mbedtls_des3_crypt_ecb(&ctx, RC1, SK1); - uint8_t SK2[8]; - for(int i = 0; i < 8; i++) { - RC2[i] ^= SK1[i]; - } - mbedtls_des3_crypt_ecb(&ctx, RC2, SK2); - mbedtls_des3_free(&ctx); +// uint8_t SK2[8]; +// for(int i = 0; i < 8; i++) { +// RC2[i] ^= SK1[i]; +// } +// mbedtls_des3_crypt_ecb(&ctx, RC2, SK2); +// mbedtls_des3_free(&ctx); - for(int i = 0; i < 8; i++) { - session_key[i] = SK1[7 - i]; - session_key[i + 8] = SK2[7 - i]; - } -} +// for(int i = 0; i < 8; i++) { +// session_key[i] = SK1[7 - i]; +// session_key[i + 8] = SK2[7 - i]; +// } +// } -static void felica_lite_calculate_mac( - uint8_t* random_challenge, - uint8_t* session_key, - uint8_t* block_data, - size_t block_count, - uint8_t* MAC) { - uint8_t SK[16]; +// static void felica_lite_calculate_mac( +// uint8_t* random_challenge, +// uint8_t* session_key, +// uint8_t* block_data, +// size_t block_count, +// uint8_t* MAC) { +// uint8_t SK[16]; - for(int i = 0; i < 8; i++) { - MAC[i] = random_challenge[7 - i]; - SK[i] = session_key[7 - i]; - SK[i + 8] = session_key[15 - i]; - } +// for(int i = 0; i < 8; i++) { +// MAC[i] = random_challenge[7 - i]; +// SK[i] = session_key[7 - i]; +// SK[i + 8] = session_key[15 - i]; +// } - mbedtls_des3_context ctx; - mbedtls_des3_init(&ctx); - mbedtls_des3_set3key_enc(&ctx, SK); +// mbedtls_des3_context ctx; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set3key_enc(&ctx, SK); - for(size_t block_num = 0; block_num < block_count; block_num++) { - for(int i = 0; i < 8; i++) { - MAC[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; - } +// for(size_t block_num = 0; block_num < block_count; block_num++) { +// for(int i = 0; i < 8; i++) { +// MAC[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; +// } - uint8_t intermediate[8]; - mbedtls_des3_crypt_ecb(&ctx, MAC, intermediate); - for(int i = 0; i < 8; i++) { - intermediate[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 15 - i]; - } +// uint8_t intermediate[8]; +// mbedtls_des3_crypt_ecb(&ctx, MAC, intermediate); +// for(int i = 0; i < 8; i++) { +// intermediate[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 15 - i]; +// } - mbedtls_des3_crypt_ecb(&ctx, intermediate, MAC); - } +// mbedtls_des3_crypt_ecb(&ctx, intermediate, MAC); +// } - mbedtls_des3_free(&ctx); -} +// mbedtls_des3_free(&ctx); +// } -static void felica_lite_calculate_mac_a( - uint8_t* random_challenge, - uint8_t* session_key, - uint8_t* iv, - uint8_t* block_data, - size_t block_count, - uint8_t* MAC_A) { - uint8_t SK[16]; - uint8_t intermediate_a[8]; - uint8_t intermediate_b[8]; +// static void felica_lite_calculate_mac_a( +// uint8_t* random_challenge, +// uint8_t* session_key, +// uint8_t* iv, +// uint8_t* block_data, +// size_t block_count, +// uint8_t* MAC_A) { +// uint8_t SK[16]; +// uint8_t intermediate_a[8]; +// uint8_t intermediate_b[8]; - for(int i = 0; i < 8; i++) { - intermediate_a[i] = iv[7 - 1] ^ random_challenge[7 - i]; - SK[i] = session_key[7 - i]; - SK[i + 8] = session_key[15 - i]; - } +// for(int i = 0; i < 8; i++) { +// intermediate_a[i] = iv[7 - 1] ^ random_challenge[7 - i]; +// SK[i] = session_key[7 - i]; +// SK[i + 8] = session_key[15 - i]; +// } - mbedtls_des3_context ctx; - mbedtls_des3_init(&ctx); - mbedtls_des3_set3key_enc(&ctx, SK); - mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); +// mbedtls_des3_context ctx; +// mbedtls_des3_init(&ctx); +// mbedtls_des3_set3key_enc(&ctx, SK); +// mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); - for(size_t block_num = 0; block_num < block_count; block_num++) { - for(int i = 0; i < 8; i++) { - intermediate_b[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; - } +// for(size_t block_num = 0; block_num < block_count; block_num++) { +// for(int i = 0; i < 8; i++) { +// intermediate_b[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; +// } - mbedtls_des3_crypt_ecb(&ctx, intermediate_b, intermediate_a); - for(int i = 0; i < 8; i++) { - intermediate_a[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; - } +// mbedtls_des3_crypt_ecb(&ctx, intermediate_b, intermediate_a); +// for(int i = 0; i < 8; i++) { +// intermediate_a[i] ^= block_data[block_num * FELICA_BLOCK_SIZE + 7 - i]; +// } - mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); - } +// mbedtls_des3_crypt_ecb(&ctx, intermediate_a, intermediate_b); +// } - for(int i = 0; i < 8; i++) { - MAC_A[i] = intermediate_b[7 - 1]; - } -} +// for(int i = 0; i < 8; i++) { +// MAC_A[i] = intermediate_b[7 - 1]; +// } +// } -static void felica_lite_calculate_mac_a_for_write( - uint8_t* random_challenge, - uint8_t* session_key, - uint32_t write_count, - uint8_t block_number, - uint8_t* block_data, - uint8_t* MAC_A) { - uint8_t iv[8]; - nfc_util_num2bytes(write_count, 3, iv); - iv[3] = 0x00; - iv[4] = block_number; - iv[5] = 0x00; - iv[6] = 0x91; - iv[7] = 0x00; +// static void felica_lite_calculate_mac_a_for_write( +// uint8_t* random_challenge, +// uint8_t* session_key, +// uint32_t write_count, +// uint8_t block_number, +// uint8_t* block_data, +// uint8_t* MAC_A) { +// uint8_t iv[8]; +// nfc_util_num2bytes(write_count, 3, iv); +// iv[3] = 0x00; +// iv[4] = block_number; +// iv[5] = 0x00; +// iv[6] = 0x91; +// iv[7] = 0x00; - uint8_t SK[16]; - for(int i = 0; i < 8; i++) { - SK[i] = session_key[i + 8]; - SK[i + 8] = session_key[i]; - } +// uint8_t SK[16]; +// for(int i = 0; i < 8; i++) { +// SK[i] = session_key[i + 8]; +// SK[i + 8] = session_key[i]; +// } - felica_lite_calculate_mac_a(random_challenge, SK, iv, block_data, 1, MAC_A); -} +// felica_lite_calculate_mac_a(random_challenge, SK, iv, block_data, 1, MAC_A); +// } -static void felica_lite_calculate_mac_a_for_read( - uint8_t* random_challenge, - uint8_t* session_key, - uint8_t* block_list, - uint8_t block_list_count, - uint8_t* block_data, - uint8_t block_count, - uint8_t* MAC_A) { - uint8_t iv[8] = {0}; +// static void felica_lite_calculate_mac_a_for_read( +// uint8_t* random_challenge, +// uint8_t* session_key, +// uint8_t* block_list, +// uint8_t block_list_count, +// uint8_t* block_data, +// uint8_t block_count, +// uint8_t* MAC_A) { +// uint8_t iv[8] = {0}; - uint8_t block_list_to_write = MIN(block_list_count, 4); - for(int i = 0; i < block_list_to_write; i++) { - iv[i * 2] = block_list[i]; - } - if(block_list_to_write < 4) { - iv[6] = 0xFF; - iv[7] = 0xFF; - } - if(block_list_to_write < 3) { - iv[4] = 0xFF; - iv[5] = 0xFF; - } +// uint8_t block_list_to_write = MIN(block_list_count, 4); +// for(int i = 0; i < block_list_to_write; i++) { +// iv[i * 2] = block_list[i]; +// } +// if(block_list_to_write < 4) { +// iv[6] = 0xFF; +// iv[7] = 0xFF; +// } +// if(block_list_to_write < 3) { +// iv[4] = 0xFF; +// iv[5] = 0xFF; +// } - felica_lite_calculate_mac_a(random_challenge, session_key, iv, block_data, block_count, MAC_A); -} +// felica_lite_calculate_mac_a(random_challenge, session_key, iv, block_data, block_count, MAC_A); +// } /** Parse common FeliCa response headers. * @@ -940,4 +940,4 @@ void felica_clear(FelicaData* data) { } } FelicaSystemArray_clear(data->systems); -} \ No newline at end of file +} From 6061eef0580ad20964941d61d8d2c3dc24174160 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 15 Jun 2023 02:51:40 +0100 Subject: [PATCH 198/370] Format --- applications/main/nfc/scenes/nfc_scene_felica_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_felica_menu.c b/applications/main/nfc/scenes/nfc_scene_felica_menu.c index 82a45e962..b8e653e20 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_felica_menu.c @@ -53,7 +53,7 @@ bool nfc_scene_felica_menu_on_event(void* context, SceneManagerEvent event) { nfc_device_set_name(nfc->dev, ""); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; - /* + /* } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaEmulate); if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { From 5334a0ab92393d4c93baa25aaf6f7cfb1c571897 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 15 Jun 2023 16:24:47 +0400 Subject: [PATCH 199/370] [FL-3376] Fixed GATT attribute order (#2776) * hal: gatt: swapped rx/tx serial chars order * hal: gatt: reordered HID attrs to maintain previous order Co-authored-by: Aleksandr Kutuzov --- firmware/targets/f7/ble_glue/gap.c | 1 + .../f7/ble_glue/services/hid_service.c | 25 +++++++++++++------ .../f7/ble_glue/services/serial_service.c | 24 +++++++++--------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index f0a9ced3c..360c1f6b6 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -330,6 +330,7 @@ static void gap_init_svc(Gap* gap) { if(status) { FURI_LOG_E(TAG, "Failed updating name characteristic: %d", status); } + uint8_t gap_appearence_char_uuid[2] = { gap->config->appearance_char & 0xff, gap->config->appearance_char >> 8}; status = aci_gatt_update_char_value( diff --git a/firmware/targets/f7/ble_glue/services/hid_service.c b/firmware/targets/f7/ble_glue/services/hid_service.c index cde26b267..cf2aca24e 100644 --- a/firmware/targets/f7/ble_glue/services/hid_service.c +++ b/firmware/targets/f7/ble_glue/services/hid_service.c @@ -22,6 +22,10 @@ typedef struct { static_assert(sizeof(HidSvcReportId) == sizeof(uint16_t), "HidSvcReportId must be 2 bytes"); +static const Service_UUID_t hid_svc_uuid = { + .Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID, +}; + static bool hid_svc_char_desc_data_callback(const void* context, const uint8_t** data, uint16_t* data_len) { const HidSvcReportId* report_id = context; @@ -148,18 +152,15 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { void hid_svc_start() { tBleStatus status; hid_svc = malloc(sizeof(HIDSvc)); - Service_UUID_t svc_uuid = {}; // Register event handler SVCCTL_RegisterSvcHandler(hid_svc_event_handler); - // Add service - svc_uuid.Service_UUID_16 = HUMAN_INTERFACE_DEVICE_SERVICE_UUID; /** * Add Human Interface Device Service */ status = aci_gatt_add_service( UUID_TYPE_16, - &svc_uuid, + &hid_svc_uuid, PRIMARY_SERVICE, 2 + /* protocol mode */ (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + @@ -170,10 +171,12 @@ void hid_svc_start() { FURI_LOG_E(TAG, "Failed to add HID service: %d", status); } - for(size_t i = 0; i < HidSvcGattCharacteristicCount; i++) { - flipper_gatt_characteristic_init( - hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); - } + // Maintain previously defined characteristic order + flipper_gatt_characteristic_init( + hid_svc->svc_handle, + &hid_svc_chars[HidSvcGattCharacteristicProtocolMode], + &hid_svc->chars[HidSvcGattCharacteristicProtocolMode]); + uint8_t protocol_mode = 1; flipper_gatt_characteristic_update( hid_svc->svc_handle, @@ -215,6 +218,12 @@ void hid_svc_start() { &hid_report_chars[report_type_idx].chars[report_idx]); } } + + // Setup remaining characteristics + for(size_t i = HidSvcGattCharacteristicReportMap; i < HidSvcGattCharacteristicCount; i++) { + flipper_gatt_characteristic_init( + hid_svc->svc_handle, &hid_svc_chars[i], &hid_svc->chars[i]); + } } bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { diff --git a/firmware/targets/f7/ble_glue/services/serial_service.c b/firmware/targets/f7/ble_glue/services/serial_service.c index ab009bbfc..0db25b3d3 100644 --- a/firmware/targets/f7/ble_glue/services/serial_service.c +++ b/firmware/targets/f7/ble_glue/services/serial_service.c @@ -10,24 +10,14 @@ #define TAG "BtSerialSvc" typedef enum { - SerialSvcGattCharacteristicTx = 0, - SerialSvcGattCharacteristicRx, + SerialSvcGattCharacteristicRx = 0, + SerialSvcGattCharacteristicTx, SerialSvcGattCharacteristicFlowCtrl, SerialSvcGattCharacteristicStatus, SerialSvcGattCharacteristicCount, } SerialSvcGattCharacteristicId; static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattCharacteristicCount] = { - [SerialSvcGattCharacteristicTx] = - {.name = "TX", - .data_prop_type = FlipperGattCharacteristicDataFixed, - .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, - .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, - .uuid_type = UUID_TYPE_128, - .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, - .security_permissions = ATTR_PERMISSION_AUTHEN_READ, - .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, - .is_variable = CHAR_VALUE_LEN_VARIABLE}, [SerialSvcGattCharacteristicRx] = {.name = "RX", .data_prop_type = FlipperGattCharacteristicDataFixed, @@ -38,6 +28,16 @@ static const FlipperGattCharacteristicParams serial_svc_chars[SerialSvcGattChara .security_permissions = ATTR_PERMISSION_AUTHEN_READ | ATTR_PERMISSION_AUTHEN_WRITE, .gatt_evt_mask = GATT_NOTIFY_ATTRIBUTE_WRITE, .is_variable = CHAR_VALUE_LEN_VARIABLE}, + [SerialSvcGattCharacteristicTx] = + {.name = "TX", + .data_prop_type = FlipperGattCharacteristicDataFixed, + .data.fixed.length = SERIAL_SVC_DATA_LEN_MAX, + .uuid.Char_UUID_128 = SERIAL_SVC_TX_CHAR_UUID, + .uuid_type = UUID_TYPE_128, + .char_properties = CHAR_PROP_READ | CHAR_PROP_INDICATE, + .security_permissions = ATTR_PERMISSION_AUTHEN_READ, + .gatt_evt_mask = GATT_DONT_NOTIFY_EVENTS, + .is_variable = CHAR_VALUE_LEN_VARIABLE}, [SerialSvcGattCharacteristicFlowCtrl] = {.name = "Flow control", .data_prop_type = FlipperGattCharacteristicDataFixed, From 4ddfe05a5965200ea5c418c52376b42e99e575c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Fri, 16 Jun 2023 15:48:57 +0900 Subject: [PATCH 200/370] Debug: sync apps on attach, makes it possible to debug already started app that has crashed (#2778) --- scripts/debug/flipperapps.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/debug/flipperapps.py b/scripts/debug/flipperapps.py index 608c30412..6dba89a56 100644 --- a/scripts/debug/flipperapps.py +++ b/scripts/debug/flipperapps.py @@ -188,6 +188,7 @@ class FlipperAppStateHelper: ) self.app_type_ptr = gdb.lookup_type("FlipperApplication").pointer() self.app_list_entry_type = gdb.lookup_type("struct FlipperApplicationList_s") + self._sync_apps() def handle_stop(self, event) -> None: self._sync_apps() From 8f996f92674c9ed21f88c85ccae1fa2259b29eea Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:43:19 +0100 Subject: [PATCH 201/370] Update ac.ir New additions --- assets/resources/infrared/assets/ac.ir | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 27c70051e..c8f5466a2 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -471,42 +471,42 @@ frequency: 38000 duty_cycle: 0.330000 data: 4467 4390 571 1583 572 505 595 1560 572 1583 572 505 572 505 596 1559 596 482 596 481 597 1559 626 451 655 422 625 1529 596 1559 596 481 572 1582 573 1583 571 505 572 1583 595 1560 594 1561 592 1562 594 1561 593 1562 593 484 593 1563 592 485 592 485 592 485 592 485 593 484 593 485 592 485 592 1562 593 485 592 1563 592 1562 593 1562 594 483 593 485 593 1562 592 485 593 1561 593 484 593 484 593 484 593 1562 593 1562 592 5163 4462 4370 592 1563 593 484 592 1563 592 1563 592 485 592 485 593 1562 593 484 593 485 592 1562 593 484 593 485 592 1562 593 1562 593 485 592 1563 592 1563 592 485 592 1563 592 1562 593 1562 593 1563 592 1563 592 1562 592 485 593 1562 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 1563 592 1563 593 485 592 485 592 1563 592 485 592 1563 591 485 593 485 592 485 592 1563 592 1563 591 # -# Model: Chigo CS-21H3A-B155 +# Model: Chigo CS-21H3A-B155 / KRF-51G/79F name: Off type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6069 7329 602 533 605 506 602 508 576 533 604 507 603 507 603 507 602 508 601 510 599 511 598 512 573 537 597 514 573 537 573 537 573 537 573 537 573 538 572 537 573 538 572 538 572 538 572 538 572 538 572 537 573 537 573 538 572 537 573 538 572 1638 573 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 538 572 1639 571 538 572 538 572 1639 572 538 572 538 572 538 572 538 572 538 572 539 571 538 572 538 572 538 572 1639 571 538 572 539 571 538 572 538 572 539 571 1639 572 539 571 539 571 1639 572 539 571 1639 571 539 571 539 571 1639 572 539 571 1639 571 1639 572 539 571 539 571 1639 571 539 571 539 571 539 571 1639 571 7361 598 -# +data: 6058 7359 592 1635 591 1634 592 1634 592 1634 592 1634 592 1633 592 1634 591 1634 592 515 591 516 590 516 590 516 590 517 589 518 588 518 589 518 589 1637 589 1637 589 1637 589 1637 589 1637 589 1637 589 1638 588 1638 588 518 589 518 588 518 589 518 588 518 589 518 589 518 589 518 588 1637 589 1638 588 1638 588 1638 588 1638 588 1638 588 1638 588 1638 588 518 588 519 588 518 588 519 588 518 589 519 587 519 588 519 587 1638 588 1638 588 519 588 1638 588 519 587 519 588 1638 588 1638 588 519 588 519 587 1639 587 519 587 1638 588 1639 587 519 588 519 587 1639 587 1639 587 1639 587 1639 587 1639 587 520 587 1639 587 1639 587 519 587 520 586 520 586 520 587 520 587 1640 586 521 586 521 586 522 585 1664 562 544 562 1664 562 544 562 1664 562 544 562 544 563 1664 562 544 562 1664 562 545 562 1664 562 545 561 1664 562 1664 562 7386 562 +# name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6073 7299 631 504 632 478 632 478 606 504 606 504 605 504 605 505 604 506 603 508 601 509 600 510 600 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 511 599 1612 598 512 598 511 599 511 599 511 599 512 598 512 598 512 598 512 598 512 598 512 598 1612 598 1612 599 512 598 512 598 511 599 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 1612 598 1613 597 512 598 512 598 1613 598 512 598 512 598 512 598 512 598 512 598 512 598 513 597 512 598 512 598 1613 597 513 597 513 597 513 597 513 597 513 597 1613 597 513 597 513 597 1614 597 513 597 1614 596 513 597 513 597 1614 597 513 597 1614 597 513 597 1614 596 1614 596 1614 597 514 596 513 597 513 597 1614 596 7336 623 -# +data: 6033 7358 591 1634 593 1634 592 1634 592 1634 592 1634 592 1634 592 1634 591 1634 592 515 617 490 616 491 590 517 590 517 590 517 590 517 590 517 590 1637 589 1637 589 1637 589 1637 589 1637 589 1637 589 1637 589 1637 589 518 589 517 590 517 589 518 589 517 616 492 589 517 615 492 590 517 589 518 613 1613 589 1637 614 1612 589 1637 589 1637 589 1637 589 1637 589 1638 588 518 613 493 613 494 596 512 589 518 612 494 589 1637 614 492 589 518 588 1637 614 1612 614 492 615 1612 613 1613 613 493 590 1637 589 1637 589 518 614 492 614 1612 614 492 614 493 613 1613 613 1612 614 1612 614 1612 614 1612 614 492 614 1612 614 1612 614 493 614 493 614 493 614 493 614 492 614 1612 614 493 614 492 615 492 614 1612 614 492 614 1612 614 492 614 1612 614 493 613 493 614 1612 614 492 614 1612 614 493 614 1612 614 493 614 1612 614 1612 614 7334 614 +# name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6098 7325 631 478 606 504 606 504 606 504 606 504 606 505 604 505 604 507 602 508 601 510 600 510 600 511 599 511 599 511 599 512 598 511 599 511 599 511 599 512 598 511 599 511 599 512 598 512 598 511 599 511 599 511 599 512 598 511 599 512 598 1612 599 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 1612 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 512 598 1613 597 1613 598 512 598 512 598 1613 598 513 597 513 597 512 598 512 598 513 597 513 597 513 597 513 597 513 597 1613 598 513 597 1614 597 1613 597 1613 598 513 597 513 597 513 597 1614 597 1614 597 513 597 1614 596 514 596 513 597 1614 596 513 597 1614 597 1614 596 1614 597 514 596 1614 596 1614 597 1614 596 1614 596 1615 596 7336 623 -# +data: 6090 7300 672 1579 646 1579 622 1604 622 1604 622 1605 620 1605 620 1606 619 1607 618 488 618 489 617 490 616 490 616 491 616 491 616 490 617 491 616 1610 616 1610 616 1610 616 1610 616 1610 616 1611 616 1610 616 1610 616 491 616 491 615 491 616 491 615 491 615 491 615 491 616 491 616 1611 615 491 616 1611 615 1611 615 1611 615 1611 615 1611 615 1611 615 491 615 1611 615 491 615 491 616 491 615 492 615 491 615 491 616 1611 615 491 615 492 614 1611 615 1611 615 492 615 1611 615 1611 615 492 614 1611 615 1611 615 492 615 492 615 1611 615 492 615 492 614 1611 615 492 614 492 615 493 615 1611 615 1611 615 1612 614 492 615 492 614 1611 615 1611 615 1612 614 492 615 492 614 492 614 1612 614 492 614 1612 614 492 615 1612 614 492 614 1612 614 492 614 492 615 1612 614 493 614 1612 614 493 614 1612 614 493 614 1612 614 1612 614 7334 614 +# name: Heat_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6095 7331 630 1607 630 1606 630 1607 629 1607 629 1609 626 1612 623 1639 572 1665 572 544 572 544 572 544 572 544 572 544 572 544 572 544 572 544 572 1665 572 1665 572 1665 572 1665 572 1665 572 1665 572 1665 572 1665 572 544 572 544 572 544 572 544 572 545 571 544 572 544 572 544 572 1665 572 545 571 1665 572 1665 572 1666 571 1665 572 1665 572 1665 572 545 571 1666 571 545 571 545 571 545 571 544 572 545 571 545 571 1666 571 545 571 545 571 1666 571 1666 571 545 571 1666 571 1666 571 545 571 1666 571 1666 571 545 571 545 571 1666 571 545 571 545 571 545 571 545 571 545 571 1666 571 1666 571 1666 571 1666 571 545 571 1666 571 1666 571 1666 571 545 571 545 571 545 571 545 571 1666 571 546 570 1666 571 545 571 1666 571 545 571 1667 570 545 571 546 570 1667 570 546 570 1667 570 546 570 1667 570 546 570 1667 570 1667 570 7389 570 -# +data: 6091 7301 646 1604 647 1579 647 1579 622 1604 621 1605 620 1605 620 1606 619 1607 618 488 618 488 618 489 617 490 616 491 615 491 615 491 616 491 616 1610 616 1611 615 1611 615 1611 616 1611 615 1610 616 1611 615 1611 615 491 615 491 616 491 616 491 615 491 615 491 616 491 615 491 615 1611 615 491 615 1611 615 1611 615 1611 615 1611 615 1611 615 1611 615 491 615 1611 615 491 615 492 615 491 615 492 615 492 615 491 616 1611 615 492 614 492 615 1611 615 1611 615 492 614 1611 615 1611 615 492 615 1611 615 1612 614 492 614 492 615 1611 615 492 615 492 614 492 615 492 615 492 614 1611 615 1612 614 1612 614 1612 614 492 615 1612 614 1612 614 1612 614 492 615 492 615 492 614 493 614 1612 614 492 615 1612 614 492 614 1612 614 492 614 1612 614 492 615 492 614 1612 614 493 613 1612 614 493 613 1612 614 493 613 1612 614 1612 614 7334 614 +# name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6095 7303 654 1581 656 1605 631 1606 630 1607 629 1608 628 1609 627 1610 626 1611 626 491 625 491 625 491 625 491 625 491 625 491 625 491 625 491 625 1613 624 1612 625 1612 625 1613 624 1612 625 1612 625 1612 625 1612 625 492 624 491 625 491 625 491 625 491 625 492 624 492 624 492 624 1613 624 492 624 1613 624 1612 625 1613 624 1613 624 1613 624 1613 624 492 624 1613 624 492 624 492 624 492 624 492 624 492 624 492 624 1613 624 492 624 492 624 1613 624 1613 624 492 624 1613 624 1613 624 492 624 1613 624 1613 624 492 624 492 624 1614 623 493 623 492 624 492 624 492 624 493 623 1614 623 1614 623 492 624 1614 623 1614 623 1614 623 1614 623 1614 623 493 623 493 623 1614 623 493 623 493 623 493 623 1614 623 493 623 1614 623 493 623 1614 623 493 623 493 623 1614 623 493 623 1614 623 493 623 1614 623 493 623 1614 623 1614 623 7337 621 -# +data: 6114 7274 647 1604 621 1605 620 1604 622 1604 621 1604 621 1605 620 1605 619 1606 619 488 618 488 618 489 617 490 616 490 616 491 616 490 616 490 616 1610 616 1610 616 1610 615 1610 615 1610 616 1610 616 1610 616 1610 616 491 615 491 616 491 615 491 615 491 615 491 616 491 615 491 615 1610 616 491 615 1610 616 1610 616 1610 616 1610 616 1610 615 1610 616 491 615 1610 616 491 615 491 615 491 616 491 615 491 615 491 616 1610 616 491 615 491 615 1610 616 1610 615 491 615 1610 617 1611 615 491 616 1610 616 1610 616 491 615 491 616 1610 616 491 615 491 615 491 615 491 615 491 615 1611 615 1611 614 491 615 1611 615 1611 615 1611 615 1611 615 1611 614 492 614 492 614 1611 614 492 615 492 615 492 614 1611 615 492 615 1611 615 492 614 1611 616 492 615 492 614 1611 614 492 615 1611 615 492 614 1611 615 492 614 1611 615 1611 615 7332 614 +# name: Dh type: raw frequency: 38000 duty_cycle: 0.330000 -data: 6064 7357 602 1634 602 1635 602 1634 603 1634 603 1634 603 1634 602 1634 602 1635 627 489 626 490 600 516 600 517 599 518 598 517 599 518 598 517 599 1639 598 1639 598 1638 599 1638 599 1639 598 1639 598 1639 598 1639 598 517 599 518 598 518 598 518 598 518 598 518 598 518 598 518 598 1639 598 1639 598 518 598 1639 598 1639 598 1639 598 1639 598 1639 598 518 598 518 598 1639 598 518 598 518 598 518 598 518 598 518 598 1639 598 518 598 518 598 1639 598 1639 598 518 598 518 598 1640 597 518 598 1639 598 1640 597 518 598 518 598 1639 598 1639 598 519 597 518 598 1640 597 1640 597 519 597 1640 597 1640 597 519 597 1640 597 1640 597 519 597 519 597 1641 596 519 597 519 597 1640 597 519 597 519 597 1640 597 519 597 1640 597 519 597 1641 596 520 596 519 597 1641 596 519 597 1641 596 520 596 1641 596 520 596 1642 595 1641 596 7363 596 +data: 6089 7302 644 1582 644 1582 644 1583 643 1584 642 1586 640 1607 618 1608 617 1609 617 490 616 491 615 491 615 492 615 492 614 492 615 492 615 492 614 1612 614 1612 614 1611 615 1611 615 1612 614 1612 614 1611 615 1611 615 492 615 492 615 492 614 492 614 492 614 492 615 492 615 492 615 492 615 1612 614 1612 614 1612 614 1612 614 1612 614 1612 614 1612 614 1612 614 492 614 492 614 492 614 492 615 492 615 492 614 492 615 1612 614 493 614 492 615 1612 614 1612 614 492 614 493 614 1612 614 492 615 1612 614 1612 614 493 614 493 613 1612 614 1612 614 493 614 493 613 1612 614 1612 614 493 613 1612 614 1612 614 493 614 1612 614 1612 614 493 613 493 614 1612 614 493 613 493 614 1613 613 494 613 493 614 1613 613 493 613 1613 613 493 613 1613 613 493 613 493 614 1613 613 494 613 1613 613 493 613 1613 613 494 613 1613 613 1613 613 7335 613 # # Model: Tosot T24H-ILF/I/T24H-ILU/O name: Off @@ -545,7 +545,7 @@ frequency: 38000 duty_cycle: 0.330000 data: 9102 4396 624 476 602 1587 602 478 600 1590 600 1591 599 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 481 599 481 598 1592 598 481 598 481 598 481 599 481 599 481 598 481 599 1592 598 481 598 1592 598 482 597 481 598 1592 598 481 678 20131 599 1592 598 481 598 481 598 481 598 1592 598 481 598 481 598 481 599 481 598 481 598 481 598 481 599 481 598 1592 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 482 597 481 598 481 598 481 598 481 598 481 598 481 598 482 597 1592 598 1592 598 1592 677 40398 9182 4421 599 480 599 1591 599 481 599 1592 598 1592 598 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 1592 598 481 599 481 598 482 597 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 482 597 1592 598 482 703 20131 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 599 481 598 481 598 481 598 481 599 481 598 481 598 1592 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1592 598 1592 598 # -# Model: LG General +# Model: LG Generic name: Off type: parsed protocol: NECext From c2fbc8a846f65332c0818ce77a57d70a0e00bf6e Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:43:45 +0100 Subject: [PATCH 202/370] Update audio.ir New additions --- assets/resources/infrared/assets/audio.ir | 647 +++++++++++++--------- 1 file changed, 393 insertions(+), 254 deletions(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 0aa0dcebe..705753aa4 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 30th May, 2023 -# Last Checked 30th May, 2023 +# Last Updated 16th Jun, 2023 +# Last Checked 16th Jun, 2023 # name: Power type: parsed @@ -176,7 +176,7 @@ type: parsed protocol: NECext address: 02 A0 00 00 command: EA 15 00 00 -# Standby +# name: Power type: parsed protocol: NEC @@ -258,19 +258,19 @@ command: 05 00 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4617 4406 584 448 557 448 557 449 556 449 555 1430 580 1432 577 452 552 454 550 1460 549 1462 548 1462 549 1462 548 457 548 457 548 457 548 457 548 4463 548 457 548 457 548 457 548 458 548 1462 548 1462 548 1462 548 457 548 1463 547 1462 548 1462 548 458 547 458 548 458 548 458 547 1463 547 458 547 458 547 458 548 1463 547 55451 4606 4440 549 457 548 457 548 457 548 457 548 1462 548 1462 548 457 548 457 548 1462 548 1461 549 1462 548 1462 548 457 548 457 548 457 548 457 548 4462 548 457 548 457 548 457 548 457 548 1462 548 1462 548 1462 548 457 548 1462 548 1462 548 1462 547 457 548 458 547 458 547 458 547 1462 548 458 547 458 547 458 547 1462 548 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4614 4408 583 449 555 450 555 451 553 451 554 1432 577 1434 576 453 551 454 550 1460 549 1461 549 1461 549 1461 549 457 548 457 548 457 548 457 548 4461 548 457 548 457 548 457 548 457 548 457 548 457 548 457 548 1462 548 1461 549 1461 549 1461 548 457 548 1462 549 1461 549 1461 548 457 548 457 548 457 548 458 547 1462 548 55443 4606 4440 549 456 549 456 549 456 549 456 549 1461 549 1461 549 457 548 457 548 1461 549 1461 549 1461 549 1461 549 456 549 457 548 457 548 457 548 4461 549 456 549 457 548 457 548 457 548 457 548 457 548 457 548 1462 548 1461 549 1461 549 1461 548 457 548 1461 549 1461 549 1461 549 457 548 457 548 458 547 457 548 1462 548 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4588 4435 556 477 556 449 555 450 555 451 554 1428 582 1429 581 451 553 452 552 1457 552 1458 551 1461 548 1463 547 458 547 458 547 458 547 458 547 4464 547 458 547 458 547 458 547 458 548 458 547 458 547 458 548 458 547 1463 547 1463 547 1464 546 459 547 1464 546 1464 546 1463 547 1464 546 459 546 459 546 459 546 1464 547 55456 4581 4468 546 458 547 458 547 458 547 458 547 1463 547 1463 547 458 547 459 547 1463 547 1464 546 1464 546 1464 547 459 546 459 546 459 546 459 547 4465 546 459 546 459 546 459 546 459 546 459 547 459 546 459 546 459 546 1464 546 1464 546 1465 546 460 545 1465 545 1465 545 1465 546 1465 546 460 545 460 546 460 545 1466 544 # name: Power @@ -374,8 +374,7 @@ type: parsed protocol: NEC address: 20 00 00 00 command: 10 00 00 00 -# -# ON +# name: Power type: parsed protocol: RC5 @@ -397,25 +396,25 @@ command: 09 F6 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3309 1906 410 1178 411 1177 412 416 435 445 406 448 414 440 411 1150 439 415 436 1178 411 1177 412 442 409 444 407 1181 408 419 432 1182 407 420 442 1146 433 448 414 440 411 442 409 444 407 446 416 438 413 441 410 443 408 419 432 1182 407 446 405 422 440 414 437 416 435 445 406 1181 408 446 405 448 414 440 411 442 409 418 433 446 416 438 413 1175 414 413 438 1176 413 414 437 443 408 419 432 421 441 413 438 42493 3308 3343 355 43011 3309 3316 382 43009 3310 3314 384 43007 3303 3347 361 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3307 1879 437 1177 412 1176 413 415 436 417 434 446 405 448 414 1174 415 412 439 1149 440 1147 442 438 413 440 411 1177 412 441 410 1178 411 442 409 1179 410 417 434 419 432 448 414 440 411 442 409 444 407 446 416 438 413 441 410 1151 438 415 436 444 407 446 416 412 439 414 437 1177 412 1176 413 414 437 416 435 1152 437 1177 412 416 435 444 407 446 416 1146 433 421 441 1173 406 422 440 413 438 442 409 444 407 40133 3308 3341 357 42862 3301 3347 361 42859 3304 3319 379 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3310 1876 440 1174 415 1173 405 422 440 414 437 443 408 445 406 1182 407 420 442 1173 406 1182 407 420 442 412 439 1175 414 440 411 1150 439 415 436 1152 437 416 435 419 432 447 415 440 411 442 409 418 433 420 442 438 413 440 411 1177 412 415 436 418 433 420 442 438 413 441 410 1151 438 1150 439 415 436 1178 411 1150 439 1149 440 414 437 417 434 445 406 1155 434 420 442 438 413 1175 414 413 438 442 409 444 407 39503 3303 3320 388 42857 3304 3319 379 42867 3305 3317 381 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3305 1907 409 1178 411 1177 412 442 409 418 433 447 415 439 412 1176 413 441 410 1177 412 1176 413 441 410 443 408 1180 409 445 406 1181 408 446 416 1172 406 448 413 440 411 442 409 445 406 447 414 439 412 442 409 444 407 447 414 1173 405 449 413 441 410 443 408 446 405 448 413 1174 415 440 411 442 409 1178 411 1177 412 1177 412 442 409 444 407 447 415 439 412 441 410 444 407 1180 409 445 406 448 414 440 411 41125 3303 3347 360 42906 3308 3315 382 # name: Mute @@ -475,7 +474,7 @@ command: 06 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 9150 4435 643 1608 643 468 644 469 642 364 749 468 643 447 665 449 663 469 643 452 660 470 642 450 662 442 670 449 662 469 643 1579 672 1608 642 1580 671 1609 641 1607 643 1578 672 1607 643 1608 642 1606 644 1606 644 1606 644 1607 643 1576 675 1579 671 1605 674 438 645 466 673 438 646 466 674 437 673 439 672 439 673 438 646 1604 673 1577 673 1578 673 1577 674 1577 673 23799 9095 4485 616 # name: Vol_up @@ -487,13 +486,13 @@ command: 0C 00 00 00 name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 9151 4434 644 1608 643 376 737 379 733 446 666 449 663 468 644 469 643 468 644 468 644 468 644 447 665 448 664 468 644 450 662 1608 643 1607 644 1576 676 1607 644 1608 643 1578 674 1608 643 1577 674 1579 672 1607 643 1608 643 1607 644 1607 644 1608 643 448 664 1608 643 448 664 468 644 469 643 380 732 468 644 469 643 1607 644 468 644 1608 643 1608 644 1609 643 1608 643 23837 9152 4434 642 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8968 4344 670 460 670 460 670 1566 669 462 668 486 643 487 642 489 641 1595 640 490 640 491 640 1596 640 491 640 1596 640 1596 640 1596 640 491 640 1596 640 1596 640 1596 640 1596 640 1596 640 1596 640 1596 640 1622 640 491 640 491 640 491 640 491 640 491 640 491 640 491 640 491 639 # name: Power @@ -529,61 +528,61 @@ command: 07 F8 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1042 1461 540 1460 541 1460 541 1460 541 1459 542 1459 542 454 536 460 540 456 544 452 538 458 542 454 546 450 540 456 544 1457 544 1456 545 1448 542 50531 1041 1462 539 1462 539 1461 540 1461 540 1460 541 1460 541 455 545 451 539 457 543 480 510 459 541 481 519 451 539 457 543 1457 543 1457 544 1449 541 50515 1037 1467 544 1456 545 1456 545 1455 546 1455 535 1465 536 486 514 483 517 479 511 485 515 481 509 487 513 483 517 478 512 1462 539 1462 539 1454 536 50537 1035 1467 544 1457 544 1457 544 1456 545 1456 544 1456 545 477 513 483 517 479 511 486 514 481 519 477 513 484 516 479 511 1464 536 1463 538 1455 546 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1010 1491 509 1490 510 1488 512 487 513 487 513 1486 514 1485 515 484 516 1483 517 482 518 482 518 1481 509 1489 511 488 512 487 513 1486 514 484 516 50963 1011 1489 511 1488 512 1487 513 485 515 486 514 1485 515 1484 516 484 516 1483 517 482 518 482 518 1481 509 1489 511 489 511 489 511 1487 513 486 514 50986 1008 1492 518 1480 510 1488 512 487 513 487 513 1486 514 1484 516 484 516 1483 517 482 518 481 519 1480 510 1488 512 487 513 487 513 1486 514 484 516 50972 1012 1488 512 1486 514 1484 516 483 517 483 517 1482 518 1480 510 489 511 1487 513 486 514 486 514 1485 515 1483 517 483 517 483 517 1481 509 490 510 50976 1008 1491 509 1489 511 1487 513 485 515 485 515 1484 516 1481 509 491 509 1489 511 488 512 488 512 1487 513 1485 515 484 516 484 516 1483 517 481 509 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1011 1479 517 477 516 481 512 1480 516 1473 513 482 511 485 518 1473 513 482 511 1481 515 1474 512 482 511 485 518 1473 513 1477 509 486 517 1473 513 50728 1014 1475 511 484 509 487 516 1475 511 1479 517 477 516 480 513 1478 508 487 516 1475 511 1479 517 478 514 480 513 1479 517 1473 513 508 484 1479 517 50725 1016 1473 513 481 512 484 519 1473 513 1477 509 485 518 478 515 1476 510 485 518 1473 513 1477 509 486 517 478 515 1476 510 1480 516 479 514 1476 510 50735 1069 1421 513 481 512 484 509 1483 513 1477 509 486 517 479 514 1477 509 486 517 1474 512 1479 507 488 515 480 513 1479 507 1483 513 482 511 1479 517 50733 1011 1478 508 513 490 506 486 1478 508 1483 513 508 485 511 482 1482 514 508 485 1480 516 1473 513 508 485 511 482 1483 513 1477 509 512 491 1473 513 50735 1008 1480 516 479 514 508 485 1480 516 1474 512 509 484 486 517 1472 514 508 485 1480 516 1474 512 509 484 512 481 1482 514 1477 509 512 491 1472 514 50738 1006 1509 487 508 485 486 507 1509 487 1503 483 513 490 505 488 1502 484 512 491 1473 513 1503 483 512 491 505 488 1476 510 1507 489 506 487 1503 483 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1010 1479 517 1474 512 509 484 512 491 505 488 507 486 510 483 512 491 505 488 508 485 1505 491 1474 512 1478 508 1483 513 1477 509 1482 514 1475 511 50713 1005 1483 513 1475 511 511 482 513 490 505 488 507 486 483 510 511 482 487 516 505 488 1501 485 1478 508 1483 513 1476 510 1479 507 1483 513 1474 512 50707 1012 1501 485 1479 506 513 490 505 488 507 486 509 484 511 482 487 516 505 488 508 485 1504 482 1482 514 1476 592 1396 590 1400 513 1476 510 1478 508 50715 1015 1473 513 1476 510 484 508 513 490 504 489 480 513 508 485 483 510 511 482 488 515 1473 513 1477 509 1480 516 1474 512 1477 591 1397 516 1472 514 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1041 1462 538 1462 538 485 515 481 519 478 512 484 516 1458 542 1458 542 1459 541 481 519 1481 519 1455 545 1482 518 1456 544 479 511 486 514 474 516 50532 1039 1464 536 1490 510 487 513 483 517 480 510 486 514 1460 540 1486 514 1460 540 483 517 1457 543 1484 516 1458 542 1458 542 481 519 477 513 476 513 50534 1036 1467 543 1457 543 480 509 460 540 483 517 479 511 1463 537 1463 537 1464 536 486 514 1487 513 1461 539 1461 539 1462 538 484 516 481 519 469 510 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1042 1461 539 457 543 1458 542 1458 542 1458 542 1458 542 454 536 461 539 457 543 1457 543 453 537 460 540 456 544 452 538 1463 537 1463 537 1456 544 50530 1065 1438 572 424 566 1434 566 1435 565 1435 565 1436 544 452 537 459 572 424 545 1456 544 451 539 458 542 454 536 460 540 1461 539 1461 539 1454 536 50538 1036 1467 543 452 537 1464 536 1464 536 1464 536 1465 545 450 540 456 544 452 537 1464 536 460 540 455 545 452 538 458 542 1459 541 1460 540 1452 538 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1044 1456 544 455 545 455 534 1464 536 1462 538 1460 540 1458 542 1456 544 1455 545 1453 537 1461 539 460 540 459 541 459 561 437 542 457 543 456 544 50915 1016 1483 517 482 518 482 518 1481 509 1489 511 1487 513 1486 514 1485 515 1483 517 1482 508 1490 510 489 511 488 512 487 513 487 513 486 514 485 515 50956 1047 1452 538 462 538 462 538 1461 539 1460 540 1458 542 1457 543 1456 544 1454 546 1453 547 1451 539 461 539 460 540 460 540 459 541 459 541 457 543 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1017 1484 516 1482 518 481 519 1480 510 1489 511 1487 513 1486 514 1485 515 1483 517 482 518 1480 510 490 510 489 511 489 511 488 512 488 512 486 514 50956 1015 1486 514 1484 516 484 516 1483 517 1482 518 1480 510 1489 511 1488 512 1486 514 486 514 1485 515 484 516 484 516 483 517 483 517 482 518 481 509 50960 1011 1488 512 1486 514 486 514 1485 515 1483 517 1482 518 1480 510 1488 512 1486 514 486 514 1484 516 483 517 483 517 482 518 481 519 481 508 489 511 50961 1040 1461 539 1459 541 459 541 1458 542 1456 544 1455 545 1454 546 1452 538 1460 540 460 540 1458 542 457 543 456 544 456 544 455 545 455 545 453 547 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1006 1485 511 1479 517 1473 513 509 484 511 482 514 489 506 487 508 485 511 482 513 490 505 488 1477 509 1481 515 1475 511 1480 516 1474 512 1477 509 50734 1007 1483 513 1477 509 1482 514 507 486 509 484 511 492 503 490 506 487 508 485 510 483 513 490 1474 512 1479 517 1473 513 1477 509 1481 515 1474 512 50729 1012 1477 509 1481 515 1474 512 509 484 512 491 504 489 506 487 508 485 511 482 513 490 506 487 1477 509 1481 515 1475 511 1479 517 1473 513 1476 510 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1012 1503 483 514 489 1475 511 483 510 486 517 478 515 481 512 483 510 486 517 1473 513 508 485 1480 516 1475 511 1479 507 1483 513 1477 509 1480 588 50646 1012 1476 510 512 491 1473 513 508 485 485 508 488 515 480 513 482 511 512 481 1482 514 482 511 1480 516 1473 513 1478 508 1482 514 1476 510 1479 590 50651 1007 1507 489 507 485 1478 508 514 489 506 487 509 484 485 508 488 515 506 487 1503 483 513 490 1474 512 1478 508 1483 513 1477 509 1481 515 1473 513 50721 1009 1505 491 478 515 1475 511 510 483 486 517 478 515 507 486 483 510 512 491 1472 514 508 485 1505 491 1473 513 1477 509 1481 587 1403 510 1478 508 50733 1008 1506 490 479 514 1477 509 512 481 514 489 506 487 508 485 484 509 513 490 1473 513 509 483 1481 515 1474 512 1479 507 1482 514 1476 510 1478 590 50642 1006 1508 488 508 485 1478 508 487 516 479 514 481 512 510 483 486 517 504 489 1474 512 510 483 1482 514 1475 511 1479 507 1483 513 1477 509 1480 516 # name: Power @@ -907,13 +906,13 @@ command: 02 FD 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1014 1477 517 478 514 509 483 487 515 480 512 484 508 488 514 1479 515 1476 508 1483 511 1481 513 1478 516 1475 509 1482 512 1479 515 480 512 483 509 50775 1014 1477 517 504 488 508 484 513 489 506 486 510 482 514 488 1478 516 1475 509 1483 511 1480 514 1477 517 1474 510 1482 512 1478 516 505 487 509 483 50770 1009 1481 513 508 484 512 490 506 486 510 482 514 488 508 484 1481 513 1478 516 1475 509 1483 511 1480 514 1477 517 1475 509 1482 512 509 483 513 489 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1010 1508 486 509 483 513 489 507 485 512 490 505 487 510 482 1509 485 511 491 1501 483 1509 485 1507 487 1504 490 1502 482 1509 485 511 491 1500 484 50779 1010 1506 488 508 484 512 490 505 487 509 483 513 489 506 486 1506 488 507 485 1506 488 1503 481 1510 484 1508 486 1505 489 1503 481 514 488 1503 491 # name: Power @@ -931,13 +930,13 @@ command: 01 FD 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1004 1513 481 515 487 1478 516 505 487 510 482 1484 510 1481 513 508 484 512 490 1475 509 513 489 1477 507 1484 510 511 481 515 487 1479 515 1474 510 50774 1005 1484 510 513 489 1476 508 513 489 508 484 1482 512 1479 515 506 486 511 481 1483 511 512 490 1475 509 1483 511 510 482 515 487 1504 490 1475 509 50777 1013 1503 491 506 486 1505 489 507 485 512 490 1501 483 1508 486 510 482 514 488 1503 481 515 487 1504 490 1500 484 512 490 506 486 1506 488 1502 482 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1014 1477 507 514 488 508 484 512 490 505 487 509 483 513 489 507 485 1480 514 1477 517 1475 509 1483 511 1480 514 1477 517 1475 509 1482 512 508 484 50774 1004 1486 508 513 489 507 485 511 491 504 488 508 484 513 489 505 487 1479 515 1476 508 1484 510 1481 513 1478 516 1475 509 1482 512 1480 514 507 485 50771 1007 1507 487 509 483 513 489 507 485 511 481 515 487 508 484 513 489 1502 482 1483 511 1481 513 1479 515 1476 508 1484 510 1481 513 1478 516 506 486 # name: Power @@ -1141,67 +1140,67 @@ command: 0C 00 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4559 4461 546 490 515 495 521 490 515 495 489 1484 516 1482 550 486 519 491 493 1480 520 1478 522 1477 523 1475 546 490 515 495 521 490 515 495 489 4493 545 491 525 486 519 491 514 496 488 1484 516 1483 517 1481 551 486 488 1485 515 1483 517 1482 550 486 519 491 525 486 519 491 493 1479 542 494 522 489 516 467 517 1482 550 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4550 4469 548 462 543 467 549 461 544 466 518 1481 520 1479 542 468 548 462 522 1477 523 1476 514 1485 515 1483 549 461 544 466 550 461 544 466 518 4491 547 463 542 468 548 462 543 467 549 462 543 467 549 461 523 1476 514 1485 515 1484 516 1482 550 461 513 1486 514 1485 515 1483 549 461 544 466 550 461 544 493 491 1481 540 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4496 4442 513 503 488 502 489 501 490 500 491 1505 487 1508 484 505 486 504 487 1508 484 1511 492 1503 489 1499 514 484 486 504 487 502 489 501 490 4449 517 499 492 499 492 497 483 507 484 1511 492 1504 488 1499 514 483 487 1509 483 1512 491 1504 488 503 488 501 490 500 491 499 492 1504 488 501 490 500 491 492 509 1494 488 55126 4496 4446 541 482 488 502 489 501 490 500 491 1505 487 1508 484 505 486 504 487 1508 484 1503 510 1493 489 1480 512 504 487 503 488 502 489 500 491 4449 517 498 493 497 483 507 484 505 486 1502 511 1492 490 1504 488 502 489 1507 485 1509 483 1512 491 473 518 498 493 496 484 505 486 1510 483 499 512 484 486 504 487 1508 484 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4492 4434 510 505 486 505 486 504 487 503 488 1481 511 1484 518 499 492 498 493 1476 516 1479 513 1483 519 1469 596 402 516 500 491 499 492 498 493 4447 518 498 493 497 483 507 484 506 485 504 487 503 488 494 517 1485 486 1483 519 1476 516 1480 512 504 486 1508 484 1486 517 1479 565 425 513 502 488 501 490 492 509 1467 515 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4500 4436 516 504 486 510 490 505 485 510 490 1500 491 1474 517 505 485 484 516 1501 490 1501 490 1501 490 1501 490 505 485 511 489 506 484 485 515 4449 513 482 508 513 487 508 482 514 486 1504 487 1504 487 1504 487 482 508 1509 482 1509 492 1499 492 504 486 509 491 504 486 509 491 1500 491 504 486 510 490 478 512 1505 486 55017 4492 4444 508 512 488 508 482 513 487 508 482 1508 483 1482 509 513 487 508 482 1509 482 1509 482 1483 508 1483 508 513 487 508 482 514 486 509 481 4457 515 506 484 511 489 506 484 511 489 1501 490 1475 516 1501 490 506 484 1480 511 1507 484 1480 511 511 489 506 484 512 488 507 483 1507 484 512 488 507 483 512 488 1503 488 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4493 4443 509 512 488 507 483 512 488 507 483 1482 509 1482 509 512 488 507 483 1482 509 1482 509 1482 509 1482 509 512 488 507 483 513 487 508 482 4457 516 505 485 510 490 505 485 510 490 505 485 511 489 505 485 1480 511 1480 511 1480 511 1481 510 484 516 1476 515 1476 515 1476 515 481 509 486 514 481 509 486 514 1476 515 55014 4498 4438 514 482 508 487 513 482 508 488 512 1477 514 1477 514 482 508 487 513 1477 514 1477 514 1477 514 1477 514 482 518 477 513 482 518 477 513 4451 511 485 515 479 511 485 515 480 510 485 515 480 510 485 515 1475 516 1475 516 1476 515 1476 515 480 510 1481 510 1481 510 1481 510 486 514 481 509 486 514 481 509 1482 509 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4524 4473 512 516 491 511 486 516 491 511 496 1482 522 1482 522 506 491 512 495 1483 521 1483 521 1483 521 1483 521 506 491 512 495 507 490 512 495 4475 521 507 490 513 494 508 489 513 494 1484 520 1484 520 1483 521 507 490 1488 516 1488 516 1488 516 511 496 506 491 511 496 506 491 1488 516 511 496 506 491 512 495 1483 521 55356 4533 4463 512 516 491 511 486 516 491 511 496 1507 487 1492 512 515 492 510 487 1492 512 1492 512 1491 513 1491 513 515 492 510 487 515 492 510 487 4484 512 516 491 511 496 506 491 512 495 1483 521 1482 522 1482 522 506 491 1488 516 1487 517 1487 517 511 496 506 491 512 495 506 491 1488 516 512 495 507 490 512 495 1483 521 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4533 4464 521 507 490 512 495 507 490 513 494 1484 520 1484 520 508 489 513 494 1484 520 1484 520 1483 521 1483 521 506 491 511 496 506 491 512 495 4475 521 507 490 512 495 507 490 513 494 508 489 513 494 508 489 1490 514 1490 514 1490 514 1490 514 514 493 1485 519 1485 519 1485 519 509 488 515 492 510 487 516 491 1487 517 55369 4531 4465 520 508 489 514 493 509 488 515 492 1486 518 1486 518 509 488 514 493 1486 518 1485 519 1485 519 1485 519 509 488 514 493 509 488 515 492 4478 518 511 486 516 491 511 496 506 491 512 495 507 490 512 495 1483 521 1483 521 1483 521 1483 521 507 490 1488 516 1488 516 1488 516 512 495 507 490 513 494 508 489 1490 514 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4530 4465 521 507 490 513 494 507 490 513 494 1509 496 1483 521 506 491 512 495 1508 486 1492 512 1492 513 1491 513 514 493 509 488 514 493 509 488 4483 513 514 493 509 488 515 492 510 487 515 492 510 487 515 492 510 487 1517 488 1491 513 1490 515 513 494 1510 494 1484 520 1483 521 1483 521 506 491 512 495 506 491 1487 517 55357 4528 4468 518 510 487 515 492 510 487 515 492 1512 493 1485 519 509 488 514 493 1510 494 1483 522 1483 521 1482 512 516 491 511 486 516 491 511 486 4484 512 516 491 511 486 516 491 511 486 516 491 511 486 516 491 511 496 1507 487 1491 513 1491 514 514 493 1510 495 1484 521 1483 522 1483 522 506 491 511 486 516 491 1513 491 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4495 4440 512 509 491 504 486 509 491 504 486 1478 513 1479 512 509 491 504 486 1478 513 1479 512 1479 512 1479 512 509 491 504 486 509 491 504 486 4452 511 511 489 506 484 511 489 506 484 511 489 506 484 511 489 506 484 1480 511 1480 511 1480 511 510 491 1500 491 1475 516 1475 516 1475 516 504 486 510 490 505 485 1479 512 55017 4497 4439 513 507 483 513 487 507 483 513 487 1476 515 1477 514 507 483 513 487 1476 515 1476 515 1477 514 1477 514 481 509 512 488 507 483 512 488 4451 512 509 491 504 486 509 491 504 486 483 517 504 486 509 492 504 486 1504 487 1504 487 1478 513 508 482 1509 482 1509 482 1509 482 1483 508 513 487 508 482 513 487 1504 487 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4501 4435 517 504 486 510 490 505 485 510 490 1475 516 1475 516 506 484 511 489 1476 515 1476 515 1476 515 1477 514 507 483 512 488 507 483 513 487 4451 511 510 490 479 511 484 516 506 484 1481 510 511 489 506 484 511 489 1476 515 1476 515 1477 514 507 483 512 488 1477 514 1477 514 1478 513 508 482 513 487 508 482 1484 517 55011 4496 4440 512 509 491 478 512 483 517 504 486 1480 511 1480 511 484 516 505 485 1480 511 1480 511 1481 510 1481 510 484 516 506 484 511 489 506 484 4455 517 504 486 509 491 504 486 483 517 1474 517 505 485 510 490 505 485 1480 511 1480 511 1481 510 511 489 480 510 1481 510 1481 510 1482 509 485 515 507 483 512 488 1477 514 # name: Power @@ -1219,31 +1218,31 @@ command: 1F 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4504 4432 511 509 492 504 486 509 492 504 486 1478 514 1477 515 507 484 512 489 1476 516 1475 517 1475 517 1474 518 503 487 509 492 503 487 508 493 4446 508 513 488 507 483 512 489 507 483 512 489 506 484 511 490 506 484 1481 511 1480 512 1479 513 509 492 1473 519 1473 519 1472 509 1482 510 511 490 506 484 511 490 1475 517 54985 4498 4437 517 504 486 509 492 504 486 509 492 1473 519 1472 509 512 489 506 484 1481 511 1480 512 1480 512 1479 513 508 493 502 488 507 483 512 489 4449 516 506 485 511 490 505 485 510 491 505 485 510 491 504 486 509 492 1473 519 1473 508 1483 561 434 515 1476 516 1475 517 1474 518 1473 519 502 488 507 483 512 489 1476 516 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4531 4406 516 478 512 484 538 457 544 452 538 1453 518 1473 519 476 546 450 540 1451 509 1482 510 1481 511 1480 512 483 539 457 544 452 538 457 544 4420 513 482 540 456 545 450 540 455 546 1445 547 449 541 454 536 458 543 1449 543 1449 543 1448 544 451 539 456 545 1445 547 1445 547 1444 537 458 543 453 537 457 544 1448 544 54957 4495 4440 514 481 520 477 513 482 519 476 514 1476 516 1475 517 478 512 484 517 1474 518 1473 519 1472 520 1471 521 474 516 479 511 484 517 479 511 4452 512 509 492 503 487 508 493 503 487 1477 515 507 483 512 489 506 484 1481 511 1481 511 1480 512 509 492 503 487 1478 514 1478 514 1477 515 506 484 511 490 505 485 1480 512 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4551 4468 549 461 544 493 523 460 545 466 518 1480 520 1479 542 468 548 489 495 1477 523 1475 515 1484 516 1483 549 461 544 493 523 487 518 466 518 4490 548 462 543 493 523 488 517 466 550 461 544 466 550 460 545 465 519 1480 520 1478 522 1477 544 465 519 1480 520 1479 521 1477 523 1476 545 464 541 496 520 490 494 1479 542 55901 4554 4465 542 494 522 489 516 494 522 488 496 1477 523 1476 545 490 515 495 489 1484 516 1483 517 1481 519 1479 542 494 522 489 516 494 522 488 496 4487 541 495 521 490 515 495 521 489 516 494 522 488 517 493 523 488 496 1476 525 1475 515 1483 549 488 496 1476 524 1475 515 1484 516 1482 550 486 519 491 525 486 488 1485 547 55897 4548 4470 548 462 543 468 548 462 543 467 517 1482 518 1480 541 469 547 490 494 1478 575 1423 515 1485 515 1483 549 461 544 466 550 461 544 466 518 4491 547 462 543 467 549 462 543 467 549 461 544 466 539 497 519 492 492 1480 520 1478 522 1477 544 465 519 1480 520 1479 521 1477 513 1486 546 464 541 469 547 464 520 1478 543 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4553 4467 550 459 546 464 541 469 547 464 520 1479 521 1477 544 492 524 486 498 1475 515 1484 516 1482 518 1481 540 496 520 490 515 495 521 490 494 4488 550 486 519 491 525 486 519 491 493 1479 542 494 522 489 516 494 490 1482 518 1481 519 1480 541 494 522 489 495 1478 522 1476 514 1485 547 489 516 494 522 489 495 1477 544 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4555 4483 516 516 488 519 495 512 492 515 489 1525 493 1519 489 518 496 511 493 1521 487 1525 493 1520 488 1524 494 513 491 517 487 520 494 514 490 4501 519 513 491 516 488 520 494 513 491 517 487 521 493 514 490 518 486 1527 491 1522 496 1517 491 516 488 1525 493 1520 488 1525 493 1520 488 519 495 512 492 516 488 1525 493 # name: Vol_up @@ -1285,25 +1284,25 @@ command: 13 00 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4552 4468 574 436 566 444 568 442 570 441 540 1458 546 1453 572 439 573 437 544 1455 539 1460 544 1456 548 1450 575 436 566 444 568 442 570 440 541 4468 594 416 565 445 567 443 569 441 540 1459 545 1453 541 1459 566 444 547 1452 542 1457 547 1452 593 417 564 446 566 444 568 442 539 1460 565 446 566 444 568 442 539 1460 565 55957 4581 4437 543 467 545 466 546 464 538 472 519 1480 514 1485 540 471 541 469 512 1486 518 1481 513 1486 518 1481 575 436 566 444 568 442 570 440 541 4468 543 468 544 465 547 464 538 472 519 1480 514 1485 519 1479 546 465 516 1483 511 1488 516 1483 542 468 544 466 546 464 538 473 519 1480 545 466 546 464 538 472 519 1480 545 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4546 4473 538 473 539 470 542 469 543 467 514 1484 520 1479 545 465 547 463 518 1481 513 1486 518 1481 513 1487 538 472 540 470 542 469 543 466 515 4495 546 463 539 472 540 470 542 468 544 466 546 491 521 462 519 1480 513 1485 519 1480 514 1486 539 472 519 1479 515 1484 520 1479 546 464 548 463 539 471 541 469 512 1487 548 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4549 4469 542 468 544 467 545 465 537 473 519 1481 513 1485 540 471 541 469 512 1487 517 1482 512 1487 517 1482 543 467 545 465 547 463 539 472 519 4489 543 468 544 466 546 464 538 473 539 470 542 468 544 466 546 465 516 1482 512 1487 517 1482 543 468 513 1485 519 1480 514 1485 519 1480 545 465 547 463 539 472 520 1479 546 55899 4549 4470 541 469 574 436 566 445 546 463 549 1450 513 1486 539 471 572 438 543 1456 548 1451 543 1456 548 1451 574 436 566 445 567 443 569 441 540 4469 573 437 575 435 567 443 569 441 571 439 573 437 565 445 567 443 549 1451 543 1456 548 1451 574 436 545 1454 540 1459 545 1454 540 1459 566 444 568 442 570 440 541 1458 567 55878 4580 4439 572 439 573 437 565 445 567 443 548 1451 543 1456 569 441 571 439 542 1457 547 1452 542 1457 547 1452 573 437 575 435 567 443 569 442 539 4469 573 438 574 436 566 444 568 442 570 440 572 438 574 436 566 444 547 1452 542 1457 547 1452 573 437 544 1455 539 1460 544 1455 539 1460 575 435 567 444 568 442 539 1459 566 55879 4578 4442 569 441 571 439 573 437 575 435 546 1453 541 1458 567 444 568 441 540 1459 545 1454 540 1459 545 1454 571 439 573 437 575 435 567 444 548 4461 571 440 572 438 574 436 566 444 568 442 570 440 572 438 574 436 545 1454 540 1459 545 1454 571 439 542 1457 547 1452 542 1457 547 1452 573 437 575 435 567 444 547 1451 574 55871 4554 4465 546 464 538 473 539 471 541 469 512 1487 517 1481 544 467 545 465 516 1483 511 1488 516 1483 511 1488 547 463 539 472 540 470 542 468 513 4496 546 464 538 472 540 470 542 468 544 466 546 464 538 473 539 471 510 1488 516 1483 511 1488 547 463 518 1481 513 1486 518 1481 513 1486 539 472 540 470 542 467 514 1485 540 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4555 4465 546 464 538 472 540 470 542 468 513 1486 518 1481 544 467 545 465 516 1483 511 1488 516 1483 511 1488 537 473 539 472 540 470 542 468 513 4496 546 464 538 473 539 471 541 469 512 1487 538 472 540 470 542 469 512 1486 518 1481 544 1455 570 441 571 439 542 1457 547 1452 542 1457 568 442 570 440 572 438 543 1456 569 55920 4555 4464 547 464 538 472 540 470 542 468 513 1486 518 1481 544 466 546 464 517 1482 512 1487 517 1482 512 1487 538 473 539 471 541 469 543 467 514 4495 547 463 539 472 540 470 542 468 513 1486 539 471 541 469 543 467 514 1485 519 1479 515 1485 540 470 542 468 513 1486 518 1481 513 1486 539 471 541 469 543 467 514 1485 540 # name: Power @@ -1405,25 +1404,25 @@ command: 28 D7 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1048 580 597 1165 596 582 595 1167 594 875 599 578 599 1455 593 585 592 1171 600 577 600 576 601 13191 1042 586 591 1170 601 576 601 1161 600 869 595 582 595 1459 599 578 599 1163 598 580 597 579 598 13195 1048 580 597 1165 596 581 596 1167 594 875 599 578 599 1456 592 585 592 1171 600 577 600 577 600 13192 1052 576 601 1162 599 578 599 1163 598 871 593 584 593 1462 596 581 596 1166 595 582 595 582 595 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1048 553 624 1138 623 554 623 1139 622 848 626 551 626 1427 621 556 652 1403 655 522 655 814 650 12579 1050 552 656 1106 624 553 624 1138 623 847 627 550 627 1427 621 556 621 1434 624 553 624 845 619 12612 1079 523 623 1139 622 555 622 1139 622 848 626 550 627 1427 621 556 621 1434 624 553 624 845 619 12613 1047 556 621 1141 620 556 621 1141 620 850 624 552 625 1429 619 558 619 1435 623 554 623 846 628 12600 1050 552 625 1137 624 553 624 1137 624 846 618 558 619 1435 623 554 623 1431 627 550 627 842 622 12609 1051 551 626 1136 625 551 626 1136 625 844 620 557 620 1434 624 554 623 1431 627 550 627 843 621 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1073 528 649 1114 647 530 647 1116 645 825 649 527 650 1405 653 1402 625 552 646 1409 649 1113 648 11417 1077 524 653 1110 651 526 651 1112 649 820 654 523 654 1400 648 1407 651 526 651 1404 654 1109 652 11414 1079 523 654 1109 652 524 653 1111 650 819 624 554 654 1401 647 1408 650 529 648 1406 652 1111 650 11416 1077 525 652 1110 651 526 651 1112 649 820 654 523 654 1401 647 1408 650 528 649 1405 622 1141 651 11414 1080 521 646 1116 624 553 645 1117 654 816 648 529 648 1406 652 1403 655 523 644 1410 648 1114 647 11418 1075 526 651 1111 650 527 650 1112 649 820 623 554 654 1400 648 1407 651 526 651 1403 655 1107 654 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 1051 550 627 1135 626 551 626 1136 625 845 619 850 624 553 675 794 629 842 622 555 622 555 622 13780 1047 555 622 1140 621 557 620 1141 620 850 624 845 619 558 619 850 624 846 618 559 618 559 618 13782 1045 558 619 1143 618 559 618 1144 627 842 622 847 627 550 627 842 622 848 626 551 626 551 626 13774 1053 549 618 1144 627 550 627 1135 626 843 621 849 625 551 626 844 620 850 624 553 624 552 625 13776 1051 551 626 1137 624 553 624 1138 623 846 618 851 623 554 623 846 628 841 623 554 623 554 623 13776 1051 551 626 1136 625 552 625 1137 624 845 619 850 624 553 624 846 618 852 622 555 622 554 623 13778 1049 554 623 1139 622 555 622 1140 621 849 625 844 620 557 620 850 624 846 618 559 618 558 619 # name: Power @@ -1453,31 +1452,31 @@ command: 1C 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 274 789 254 1792 275 814 250 787 246 816 248 1798 279 784 249 813 251 785 248 788 245 1827 281 1791 275 1825 272 790 253 783 250 43886 277 786 278 1795 272 791 252 783 281 782 251 785 269 1804 273 1800 277 1822 275 1798 279 783 270 766 277 759 274 1825 272 1800 277 43886 277 759 274 1825 272 764 279 756 277 786 278 1795 282 781 272 763 280 755 278 785 279 1794 273 1827 270 1802 275 761 272 791 273 43888 276 761 272 1800 277 786 278 758 275 760 273 790 274 1799 278 1821 276 1796 281 1792 275 788 276 760 273 789 275 1798 279 1794 273 43889 278 785 248 1825 272 790 253 782 272 764 279 1793 274 790 274 761 282 781 273 763 280 1793 273 1825 272 1800 277 813 220 789 275 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 270 793 250 1795 272 818 246 791 252 809 244 766 277 1795 282 781 272 790 253 809 224 1822 275 1797 280 1820 277 785 248 788 245 43889 273 790 274 1799 278 785 248 788 276 787 246 1826 271 792 251 1794 273 1827 270 1802 275 788 245 791 273 790 253 1819 278 1794 273 43889 274 789 254 1818 269 767 276 786 247 789 275 788 245 1827 270 792 251 785 248 814 250 1796 281 1819 248 1825 272 790 253 783 271 43889 245 791 273 1799 278 786 278 784 249 787 246 1827 281 781 252 1821 276 1796 281 1792 274 814 250 786 247 789 244 1829 279 1793 274 43888 275 815 218 1828 280 783 250 786 278 785 248 788 245 1827 280 782 251 785 268 794 249 1797 280 1819 278 1794 272 791 252 810 254 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 277 759 274 1799 278 784 280 783 250 812 242 1804 273 816 227 808 246 791 252 1819 248 1825 273 1826 251 1822 276 786 247 789 244 43888 274 815 249 1797 280 783 250 812 252 784 249 813 241 1805 272 1827 250 1822 275 787 246 816 217 819 255 807 226 1820 278 1795 272 43888 273 789 254 1817 270 793 251 785 248 814 250 1795 282 807 247 790 253 783 250 1822 276 1796 281 1818 249 1824 274 814 229 807 247 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 275 762 271 1800 277 786 278 784 249 813 241 795 248 1824 253 784 270 792 251 1821 246 1826 272 1801 276 1823 275 762 271 790 243 43889 274 789 275 1797 280 783 250 812 252 784 249 1823 275 762 271 1827 250 1822 276 787 246 816 217 818 246 790 253 1819 268 1804 273 43886 277 786 247 1825 273 764 280 783 250 811 253 784 249 1822 276 761 272 816 228 1819 279 1794 273 1826 251 1821 277 786 247 815 249 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 275 762 271 1800 277 786 278 784 249 813 241 795 248 814 219 817 247 789 254 1818 249 1824 274 1798 279 1820 278 759 274 788 245 43887 274 789 275 1798 279 783 250 812 252 784 249 1823 274 1798 279 1820 247 1826 271 765 278 809 224 812 252 784 249 1823 275 1798 279 43880 281 782 251 1821 277 760 273 789 244 818 246 790 253 808 246 791 252 809 224 1822 275 1797 280 1819 248 1825 273 790 253 808 246 # name: Power @@ -1675,25 +1674,25 @@ command: 15 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8437 4188 538 1565 539 1565 539 513 544 508 538 513 544 1559 545 507 539 1564 540 1564 540 1563 541 1563 541 511 546 1557 547 505 542 511 546 505 542 20497 597 1507 545 1559 545 507 539 512 545 507 539 1564 540 512 545 1558 546 1558 546 1558 546 1557 547 505 542 1562 542 510 547 505 542 510 547 20492 540 1564 540 1564 540 512 545 506 540 511 546 1558 546 505 542 1562 542 1562 542 1562 542 1561 543 509 548 1555 538 514 543 509 537 514 543 20495 547 1558 546 1557 547 505 541 511 546 505 542 1562 542 510 547 1556 548 1556 548 1556 548 1556 537 514 543 1560 544 508 538 514 543 508 538 20501 541 1562 542 1562 542 510 547 505 541 510 547 1556 548 504 543 1561 543 1561 543 1560 544 1560 544 508 538 1565 539 513 544 507 539 513 544 20494 548 1556 548 1556 548 504 543 509 548 504 543 1560 544 508 539 1565 539 1565 539 1564 540 1564 540 512 545 1559 545 506 540 512 545 506 540 20499 543 1560 544 1560 544 508 539 513 544 508 538 1564 540 512 545 1559 545 1558 546 1558 546 1558 546 506 541 1563 541 510 547 505 542 510 547 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8430 4194 542 1562 542 1562 542 510 547 505 541 510 547 1556 548 504 542 1561 543 509 548 1556 548 1556 548 1556 548 1556 548 504 542 509 537 514 543 20496 545 1559 545 1559 545 507 539 512 545 507 539 1564 540 512 545 1559 545 507 539 1564 540 1564 540 1563 541 1563 541 511 546 506 540 511 546 20494 546 1557 547 1557 547 505 541 510 547 505 541 1562 542 510 547 1556 548 505 541 1562 542 1562 542 1561 543 1561 543 509 548 504 542 509 537 20501 540 1565 539 1565 539 512 545 507 539 512 545 1559 545 507 539 1564 540 512 545 1559 545 1558 546 1558 546 1558 546 506 540 511 546 506 540 20498 543 1562 542 1562 542 510 547 505 541 510 547 1557 547 505 541 1562 542 509 548 1556 548 1556 548 1556 548 1556 548 504 542 509 548 504 542 20497 543 1560 544 1560 544 508 538 513 544 508 538 1565 539 513 544 1560 544 508 538 1565 539 1565 539 1565 539 1564 540 513 544 507 539 512 545 20495 545 1558 546 1558 546 506 540 511 546 506 540 1563 541 511 546 1558 546 506 540 1563 541 1563 541 1563 541 1562 542 510 547 505 541 510 547 20493 548 1556 548 1556 548 504 542 509 548 504 542 1561 543 509 537 1566 538 514 543 1560 544 1560 544 1560 544 1560 544 508 538 513 544 508 538 20501 539 1564 540 1564 540 512 545 507 539 512 545 1559 545 507 539 1564 540 512 545 1559 545 1558 546 1558 546 1558 546 506 540 511 546 506 540 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8434 4191 545 1559 545 1559 545 534 512 513 544 507 539 1564 540 538 519 1559 545 1558 546 1558 546 1558 546 1558 546 1558 546 533 513 511 546 506 540 19446 547 1557 547 1557 547 532 514 511 546 532 514 1562 593 459 546 1557 547 1557 547 1557 547 1557 547 1557 547 1557 547 531 515 510 547 505 541 19446 548 1556 548 1556 548 530 516 509 548 504 543 1561 543 535 512 1566 538 1565 539 1565 539 1565 539 1565 539 1565 539 540 517 509 537 514 543 19444 539 1565 539 1565 539 513 544 508 538 513 544 1559 545 534 512 1564 540 1564 540 1564 540 1564 540 1564 540 1564 540 539 518 507 539 513 544 19442 541 1563 541 1563 541 538 519 507 539 512 545 1558 546 506 540 1563 541 1563 541 1563 541 1563 541 1563 541 1562 542 537 520 505 541 511 546 19440 595 1509 543 1561 543 536 521 504 542 509 548 1555 538 540 517 1560 544 1560 544 1560 544 1560 544 1560 544 1560 544 534 512 513 544 508 538 19448 546 1559 545 1559 545 533 513 512 545 507 539 1563 541 538 519 1558 546 1558 546 1558 546 1557 547 1558 546 1558 546 533 513 512 545 506 540 19447 546 1557 547 1557 547 532 514 511 546 506 540 1562 542 537 520 1557 547 1557 547 1557 547 1557 547 1557 547 1557 547 505 542 510 547 505 541 19445 548 1556 548 1556 548 531 515 510 547 504 542 1561 543 536 521 1555 538 1566 538 1566 538 1566 538 1566 538 1565 539 514 543 509 537 514 543 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8435 4189 547 1557 547 1557 547 505 541 510 547 505 541 1562 542 510 547 1557 547 505 541 510 547 1557 546 1557 547 1557 547 505 541 510 547 505 541 21550 547 1558 545 1558 546 506 540 511 546 506 540 1563 541 511 546 1558 545 506 540 511 546 1558 546 1558 546 1558 546 506 540 511 546 506 540 21551 546 1558 546 1558 546 506 540 512 545 506 540 1563 540 512 545 1558 546 506 540 512 545 1558 546 1558 546 1558 546 506 540 512 545 506 540 21551 546 1559 545 1559 545 507 539 512 545 507 539 1564 539 512 545 1559 545 507 539 512 545 1559 545 1559 545 1559 545 507 539 512 545 507 539 21552 545 1559 545 1559 545 507 539 513 544 507 539 1564 540 512 545 1559 545 507 539 512 545 1559 545 1559 545 1559 545 507 539 512 545 507 539 21552 545 1559 545 1559 545 507 539 513 544 507 539 1565 538 513 544 1559 545 507 539 513 544 1559 545 1559 544 1559 545 534 512 513 544 507 539 21553 544 1560 544 1560 544 534 512 513 544 508 538 1565 539 539 518 1560 544 534 512 513 544 1560 544 1560 544 1560 544 534 512 513 544 508 538 # name: Power @@ -1723,19 +1722,19 @@ command: 1B 00 00 00 name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8498 4205 651 1471 576 530 550 1572 547 535 545 536 544 1578 541 540 550 1572 547 535 545 1576 543 539 541 1580 549 1572 547 534 546 1576 543 539 541 540 550 1571 548 533 547 1574 545 537 543 539 541 541 549 532 548 1574 545 536 544 1578 541 540 550 1571 548 1572 547 1574 545 1576 543 26533 8497 4203 653 1468 569 538 542 1579 550 531 549 533 547 1573 546 535 545 1576 543 538 542 1579 550 531 549 1571 548 1573 546 536 544 1576 543 539 541 540 550 1571 548 533 547 1574 545 536 544 538 542 540 550 530 550 1572 547 534 546 1575 544 537 543 1578 541 1579 550 1570 549 1572 547 26524 8496 4207 576 1570 549 533 547 1574 545 537 543 539 541 1580 549 532 548 1573 546 536 544 1577 542 539 551 1570 549 1572 547 534 546 1575 544 538 542 540 550 1571 548 534 546 1575 544 538 542 540 550 531 549 533 547 1575 544 537 543 1579 550 531 549 1571 548 1572 547 1574 545 1576 543 26529 8491 4211 573 1573 546 535 545 1576 543 539 551 530 550 1571 548 533 547 1574 545 536 544 1578 541 540 550 1570 549 1572 547 534 546 1575 544 538 542 539 541 1580 549 532 548 1572 547 535 545 537 543 539 541 540 550 1571 548 533 547 1574 545 537 543 1578 541 1579 550 1571 548 1574 545 26522 8498 4202 571 1574 545 537 543 1578 541 541 549 532 548 1573 546 534 546 1575 544 537 543 1577 542 540 550 1570 549 1571 548 533 547 1574 545 537 543 538 542 1579 550 531 549 1572 547 534 546 536 544 537 543 538 542 1579 550 531 549 1572 547 535 545 1575 544 1577 542 1579 550 1571 548 26522 8498 4203 570 1575 544 537 543 1578 541 541 549 532 548 1573 546 535 545 1575 544 538 542 1579 550 531 549 1571 548 1572 547 535 545 1575 544 538 542 539 541 1580 549 532 548 1572 547 534 546 536 544 537 543 539 541 1579 550 531 549 1571 548 533 547 1573 546 1574 545 1575 544 1577 542 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8490 4211 573 1573 546 535 545 1576 543 539 541 540 550 1571 548 532 548 1573 546 535 545 1576 543 539 541 1579 550 1571 548 533 547 1574 545 536 544 1576 543 1578 541 540 550 1570 549 533 547 534 546 536 544 537 543 539 541 540 550 1570 549 532 548 1572 547 1573 546 1574 545 1576 543 26528 8492 4208 648 1475 572 534 546 1575 544 537 543 539 541 1580 549 532 548 1573 546 535 545 1576 543 538 542 1579 550 1571 548 533 547 1575 544 537 543 1577 542 1579 550 532 548 1573 546 535 545 537 543 539 541 541 549 532 548 533 547 1575 544 537 543 1578 541 1579 550 1571 548 1573 546 26523 8496 4203 622 1499 569 537 543 1578 541 541 549 532 548 1572 547 534 546 1574 545 537 543 1578 541 540 550 1570 549 1571 548 534 546 1575 544 538 542 1578 541 1580 549 532 548 1573 546 536 544 538 542 540 550 532 548 533 547 535 545 1576 543 539 541 1579 550 1571 548 1573 546 1574 545 26521 8498 4201 572 1573 546 536 544 1577 542 540 550 531 549 1571 548 533 547 1573 546 536 544 1576 543 539 541 1579 550 1570 549 532 548 1573 546 535 545 1576 543 1578 541 540 550 1571 548 533 547 535 545 537 543 538 542 540 550 531 549 1572 547 534 546 1575 544 1576 543 1577 542 1579 550 26522 8498 4203 570 1575 544 538 542 1579 550 532 548 533 547 1575 544 537 543 1578 541 540 550 1570 549 533 547 1573 546 1575 544 537 543 1578 541 540 550 1571 548 1573 546 536 544 1578 551 531 549 533 547 535 545 537 543 539 541 540 550 1571 548 533 547 1575 544 1576 543 1578 541 1580 549 26521 8498 4203 570 1576 543 538 542 1580 549 532 548 533 547 1574 545 536 544 1577 542 540 550 1570 549 533 547 1573 546 1575 544 538 542 1579 550 531 549 1571 548 1573 546 536 544 1576 543 539 551 531 549 532 548 534 546 536 544 538 542 1579 550 531 549 1572 547 1573 546 1574 545 1576 543 # name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8500 4203 653 1468 569 538 542 1579 550 532 548 534 546 1575 544 536 544 1578 541 540 550 1572 547 534 546 1575 544 1577 542 539 541 1580 550 532 548 534 546 535 545 1576 543 1577 542 1579 551 532 548 534 546 536 544 1577 542 1578 552 531 549 533 547 534 546 1574 545 1575 544 1577 542 26533 8492 4211 573 1573 546 535 545 1576 543 539 551 530 550 1571 548 533 547 1574 545 536 544 1577 542 540 550 1570 549 1572 547 534 546 1576 543 539 541 541 549 532 548 1573 546 1574 545 1576 543 539 551 531 549 533 547 1573 546 1576 543 539 541 541 549 532 548 1573 546 1574 545 1576 543 26532 8493 4209 575 1571 548 533 547 1575 544 537 543 539 541 1580 550 531 549 1572 547 534 546 1576 543 538 542 1579 551 1570 549 532 548 1574 545 537 543 538 542 540 550 1570 549 1572 547 1575 544 537 543 539 541 540 550 1571 548 1573 546 536 544 538 542 539 551 1570 549 1572 547 1574 545 26530 8496 4207 567 1579 551 531 549 1572 547 535 545 536 544 1576 543 538 542 1579 551 530 550 1571 548 534 546 1574 545 1576 543 538 542 1579 551 532 548 533 547 534 546 1576 543 1577 542 1579 551 532 548 534 546 535 545 1576 543 1578 541 540 550 532 548 533 547 1574 545 1575 544 1577 542 26531 8495 4210 574 1571 548 534 546 1575 544 538 542 539 551 1569 550 531 549 1572 547 535 545 1576 543 539 541 1579 550 1571 548 534 546 1575 544 538 542 540 550 531 549 1572 547 1549 570 1551 568 539 551 531 549 532 548 1573 546 1550 569 538 542 540 550 531 549 1571 548 1548 571 1550 569 # name: Power @@ -1783,7 +1782,7 @@ command: 0E 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8441 4184 542 1562 541 1562 542 511 546 506 540 511 546 1557 546 506 540 1563 541 1563 540 1563 541 512 545 507 539 512 545 507 539 512 545 507 539 22605 538 1565 539 1565 538 514 543 509 537 514 543 1560 543 509 548 1555 548 1556 548 1556 547 504 542 510 547 505 541 510 547 505 541 510 547 22597 546 1559 544 1559 545 507 539 513 544 507 539 1564 539 513 544 1559 544 1559 545 545 508 549 513 508 538 513 544 508 538 513 544 22601 542 1562 542 1561 543 510 547 505 541 510 547 1557 547 505 541 1562 542 1562 542 1562 542 510 547 505 541 510 547 505 541 511 546 505 541 22603 540 1564 539 1565 538 513 544 508 538 513 544 1560 544 508 538 1565 538 1565 538 1566 537 514 543 509 548 504 542 509 548 504 542 509 548 22597 546 1558 546 1558 546 506 540 512 545 507 539 1564 540 512 545 1559 545 1559 544 1559 544 507 539 513 544 508 538 513 544 508 538 513 544 22600 543 1561 542 1562 542 510 547 505 541 510 547 1557 547 505 541 1562 541 1563 540 1563 541 511 546 506 540 511 546 506 540 511 546 506 540 22604 539 1565 538 1566 537 514 543 509 548 504 542 1561 543 509 548 1556 548 1556 548 1556 547 504 542 510 547 505 541 510 547 505 541 510 547 22598 545 1559 544 1559 545 507 539 512 545 507 539 1564 540 512 545 1559 544 1559 545 1559 545 508 538 513 544 508 538 513 544 508 538 513 544 22601 542 1562 542 1562 542 511 546 505 541 511 546 1557 546 506 540 1563 541 1563 540 1563 541 511 546 506 540 511 546 506 540 512 545 506 540 22604 539 1565 538 1566 537 514 543 509 548 504 542 1561 543 509 548 1556 547 1556 547 1556 548 504 542 510 547 505 541 510 547 505 541 510 547 # name: Power @@ -1881,8 +1880,7 @@ type: parsed protocol: NECext address: 12 36 00 00 command: 01 FE 00 00 -# -# OFF +# name: Power type: parsed protocol: RC5 @@ -1964,25 +1962,25 @@ command: 06 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4567 4454 549 481 522 481 523 480 498 506 498 1509 499 1509 523 480 499 505 523 1484 523 1484 523 1485 521 1487 520 484 519 485 519 485 519 471 519 4488 518 485 519 485 518 485 519 485 518 485 519 485 519 485 519 485 519 1489 519 1489 518 1489 518 485 519 1489 518 1490 518 1490 517 1490 518 486 518 486 518 486 518 1490 518 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4571 4453 552 478 525 478 526 478 500 504 500 1507 501 1506 526 478 525 477 526 1481 526 1481 526 1482 524 1483 523 481 522 482 521 481 522 468 522 4484 521 482 521 482 521 482 522 481 522 1486 521 1486 521 1486 521 482 521 1486 522 1486 522 1486 522 482 522 482 522 482 522 482 522 1486 522 483 521 482 521 483 521 1487 521 55474 4547 4477 524 479 524 480 524 480 523 480 523 1485 523 1484 524 481 523 481 523 1485 523 1485 522 1485 523 1485 523 480 524 480 524 481 523 467 524 4484 523 481 523 481 523 481 523 481 523 1485 523 1485 523 1485 523 481 523 1485 523 1485 523 1485 523 481 523 481 523 481 523 481 523 1485 523 481 523 481 523 481 523 1486 522 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4572 4453 552 456 548 479 524 479 500 504 500 1482 526 1482 551 478 526 478 525 1483 524 1484 523 1486 521 1487 521 483 521 483 521 483 521 470 520 4487 521 483 521 483 521 483 521 483 521 483 521 483 521 483 521 1488 520 1488 520 1488 520 1488 520 483 521 1488 520 1488 520 1488 520 483 521 483 521 483 521 483 521 1488 520 55457 4565 4483 521 483 521 483 521 483 521 483 520 1488 520 1487 521 483 521 483 520 1488 520 1488 520 1488 520 1488 520 483 520 484 520 484 520 470 521 4487 520 484 520 483 520 484 520 484 520 484 520 484 519 484 520 1488 520 1488 520 1488 519 1488 520 484 520 1488 520 1488 520 1488 520 484 520 484 520 484 520 484 520 1489 519 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4573 4451 552 477 527 478 526 477 526 478 501 1507 501 1506 502 502 527 477 526 1481 527 1481 527 1481 526 1482 525 479 524 480 523 481 523 468 523 4485 522 481 522 482 522 482 522 482 522 1486 522 482 521 482 522 482 522 1486 522 1486 522 1486 522 482 522 482 521 1487 521 1487 521 1487 521 482 521 483 521 483 521 1487 521 55462 4547 4474 524 478 525 479 524 480 523 480 523 1484 523 1484 523 480 523 480 523 1484 523 1484 524 1484 523 1485 522 480 523 480 524 480 523 467 523 4484 522 480 524 481 523 481 523 481 523 1485 523 481 522 481 523 481 523 1485 522 1485 523 1485 523 481 523 481 522 1485 522 1485 522 1485 523 481 522 481 522 481 523 1485 523 # name: Vol_dn @@ -2014,7 +2012,7 @@ type: parsed protocol: NEC address: 00 00 00 00 command: 47 00 00 00 -# +# name: Power type: parsed protocol: NEC @@ -2038,7 +2036,7 @@ type: parsed protocol: NEC42 address: 6E 00 00 00 command: 4C 00 00 00 -# +# name: Vol_up type: parsed protocol: NEC @@ -2054,19 +2052,19 @@ command: 10 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4555 4463 532 473 532 473 531 474 530 474 530 1478 531 1478 531 475 529 476 528 1482 551 1480 529 1480 528 1481 528 477 527 478 527 478 526 478 527 4498 526 477 527 477 527 478 526 478 526 478 526 478 526 478 527 478 526 1483 527 1483 526 1483 526 478 526 1483 527 1483 527 1483 526 1483 526 478 526 478 527 478 527 1484 526 55527 4527 4492 526 478 526 478 526 478 526 479 526 1483 527 1483 526 478 526 478 526 1483 527 1483 526 1483 526 1483 526 478 527 478 526 479 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 478 526 478 526 478 526 1484 525 1483 526 1484 525 478 526 1484 525 1484 525 1484 525 1484 525 478 526 479 526 479 525 1484 525 # name: Vol_up type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4552 4463 531 474 530 474 530 474 530 474 530 1478 531 1478 531 475 529 476 553 1455 554 1478 530 1479 529 1481 527 477 527 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 477 527 1483 526 1482 527 1482 527 478 526 1483 526 1483 526 1483 526 478 526 478 526 478 526 478 526 1483 526 478 526 478 526 478 526 1483 526 # name: Vol_dn type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 325 50440 173 137541 4551 4465 530 475 529 475 529 474 530 474 530 1479 530 1479 529 476 528 477 527 1480 554 1457 551 1480 528 1481 527 477 527 478 526 478 526 478 526 4497 525 478 526 478 526 478 526 478 526 479 525 479 525 479 525 1484 525 1483 526 1484 525 1483 526 479 525 1483 526 1483 526 1483 526 479 525 479 525 479 525 479 525 1484 525 # name: Power @@ -2098,7 +2096,7 @@ type: parsed protocol: NEC address: 00 00 00 00 command: 04 00 00 00 -# +# name: Power type: parsed protocol: NEC @@ -2126,13 +2124,13 @@ command: B9 00 00 00 name: Power type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 2760 833 499 418 470 419 416 887 445 872 893 468 416 469 414 442 443 441 444 440 446 440 446 440 446 440 892 882 445 444 466 446 439 447 438 448 437 449 884 447 437 450 437 894 436 449 437 449 437 449 437 449 437 449 884 448 436 894 437 450 436 116126 2673 887 445 473 440 449 439 893 438 880 884 448 437 449 437 449 437 449 437 449 437 449 437 449 437 449 884 890 437 449 437 449 437 450 436 449 437 449 884 448 436 451 437 894 436 449 437 450 436 450 436 449 437 450 883 448 436 894 436 449 437 116123 2672 888 469 449 439 450 437 893 438 881 883 448 437 449 437 449 437 449 437 449 438 449 437 449 437 449 884 891 437 449 437 449 437 449 437 449 437 449 884 448 436 451 437 894 437 449 437 449 437 449 437 449 437 449 884 448 436 894 437 449 437 # name: Mute type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 2701 861 496 420 445 444 444 885 446 871 892 441 444 441 444 441 471 415 471 415 470 417 467 444 440 446 883 891 436 449 437 449 437 450 436 450 436 449 883 448 437 451 436 894 436 450 436 450 436 450 436 450 436 450 882 449 436 894 883 116552 2698 862 469 448 438 449 438 894 437 880 884 448 437 449 437 448 438 449 437 449 437 449 437 449 437 449 884 890 437 449 437 449 437 449 437 449 437 449 884 448 437 450 437 893 437 449 437 449 437 449 437 449 437 449 884 448 436 894 883 # name: Vol_up @@ -2177,8 +2175,6 @@ protocol: NEC address: 00 00 00 00 command: 46 00 00 00 # -# Audio_Receivers -# name: Prev type: parsed protocol: RC5 @@ -2503,8 +2499,6 @@ protocol: NECext address: 7F 01 00 00 command: 67 98 00 00 # -# CD Players -# name: Next type: parsed protocol: Kaseikyo @@ -2538,13 +2532,13 @@ command: 07 00 00 00 name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8546 4227 563 511 563 1558 566 511 563 483 566 508 567 1558 566 509 566 1558 566 1586 564 483 566 1561 588 1558 566 1560 564 536 539 1558 566 535 539 1558 566 1586 564 1557 567 486 589 1560 564 511 563 510 539 508 567 485 589 484 565 508 567 1559 565 509 566 1560 564 1560 590 1585 539 25430 8575 4251 538 485 590 1557 567 486 588 511 538 536 539 1557 567 509 566 1558 566 1560 590 483 566 1560 589 1559 565 1562 562 536 539 1560 564 509 566 1558 566 1564 586 1586 538 484 590 1559 565 487 588 482 567 509 566 511 563 483 566 508 567 1558 566 509 566 1560 564 1559 591 1586 538 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 8571 4226 564 509 540 1587 563 510 539 510 565 510 564 1558 566 510 565 1584 540 1585 539 535 540 1587 537 1586 564 1585 539 510 564 1584 540 511 563 482 567 535 539 485 590 1584 540 1585 539 509 566 510 564 510 539 1586 564 1585 539 1585 539 536 539 510 564 1585 539 1557 567 1558 592 25434 8571 4199 591 510 539 1586 564 509 540 535 540 485 590 1585 539 484 591 1561 563 1584 540 507 568 1585 539 1586 564 1585 539 486 589 1585 539 484 591 425 624 535 540 486 589 1585 539 1585 539 511 564 511 563 486 563 1586 564 1558 566 1558 566 508 567 486 589 1560 564 1558 566 1561 589 # name: Next @@ -2589,18 +2583,6 @@ protocol: Samsung32 address: 81 00 00 00 command: 01 00 00 00 # -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8546 4227 563 511 563 1558 566 511 563 483 566 508 567 1558 566 509 566 1558 566 1586 564 483 566 1561 588 1558 566 1560 564 536 539 1558 566 535 539 1558 566 1586 564 1557 567 486 589 1560 564 511 563 510 539 508 567 485 589 484 565 508 567 1559 565 509 566 1560 564 1560 590 1585 539 25430 8575 4251 538 485 590 1557 567 486 588 511 538 536 539 1557 567 509 566 1558 566 1560 590 483 566 1560 589 1559 565 1562 562 536 539 1560 564 509 566 1558 566 1564 586 1586 538 484 590 1559 565 487 588 482 567 509 566 511 563 483 566 508 567 1558 566 509 566 1560 564 1559 591 1586 538 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 8571 4226 564 509 540 1587 563 510 539 510 565 510 564 1558 566 510 565 1584 540 1585 539 535 540 1587 537 1586 564 1585 539 510 564 1584 540 511 563 482 567 535 539 485 590 1584 540 1585 539 509 566 510 564 510 539 1586 564 1585 539 1585 539 536 539 510 564 1585 539 1557 567 1558 592 25434 8571 4199 591 510 539 1586 564 509 540 535 540 485 590 1585 539 484 591 1561 563 1584 540 507 568 1585 539 1586 564 1585 539 486 589 1585 539 484 591 425 624 535 540 486 589 1585 539 1585 539 511 564 511 563 486 563 1586 564 1558 566 1558 566 508 567 486 589 1560 564 1558 566 1561 589 -# name: Pause type: parsed protocol: NEC @@ -2673,9 +2655,6 @@ protocol: NECext address: 00 EF 00 00 command: 01 FE 00 00 # -# SoundBars -# -# name: Play type: parsed protocol: NECext @@ -2709,97 +2688,97 @@ command: 24 DB 00 00 name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4572 4451 552 478 526 478 526 478 500 503 501 1483 525 1482 551 478 525 478 525 1481 526 1482 524 1483 523 1485 522 482 521 482 522 482 522 469 521 4486 521 482 522 482 522 483 521 483 521 483 521 1487 521 483 521 1487 521 483 521 483 521 483 521 1487 521 1487 521 483 520 1487 521 483 521 1488 520 1487 521 1487 521 483 521 55487 4544 4482 522 482 522 482 522 482 522 482 522 1487 521 1487 521 482 522 482 522 1487 521 1487 521 1487 521 1487 521 482 522 483 521 483 521 469 522 4486 521 483 521 483 520 483 521 483 521 483 521 1487 521 483 521 1487 521 483 521 483 521 483 521 1488 521 1487 521 483 521 1488 520 483 521 1488 521 1488 521 1488 520 483 521 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4497 4438 513 508 492 503 487 509 491 504 486 1479 512 1479 511 510 490 505 485 1481 510 1481 510 1481 509 1482 509 512 488 507 483 513 487 508 482 4457 515 506 484 511 489 506 484 512 488 507 483 1482 509 513 487 1478 513 508 482 513 487 509 491 1473 518 1474 517 504 486 1479 512 510 490 1475 515 1475 516 1476 514 480 510 55019 4494 4442 509 511 489 507 483 512 488 507 483 1508 483 1508 483 513 487 508 482 1482 509 1483 508 1509 492 1499 492 504 486 510 490 505 485 510 490 4449 513 508 482 487 513 508 482 488 512 483 517 1499 492 504 486 1505 486 510 490 479 511 510 490 1501 490 1501 490 506 484 1506 485 511 489 1502 489 1476 515 1476 515 507 483 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4572 4451 552 478 526 478 526 478 500 503 501 1483 525 1482 551 478 525 478 525 1481 526 1482 524 1483 523 1485 522 482 521 482 522 482 522 469 521 4486 521 482 522 482 522 483 521 483 521 483 521 1487 521 483 521 1487 521 483 521 483 521 483 521 1487 521 1487 521 483 520 1487 521 483 521 1488 520 1487 521 1487 521 483 521 55487 4544 4482 522 482 522 482 522 482 522 482 522 1487 521 1487 521 482 522 482 522 1487 521 1487 521 1487 521 1487 521 482 522 483 521 483 521 469 522 4486 521 483 521 483 520 483 521 483 521 483 521 1487 521 483 521 1487 521 483 521 483 521 483 521 1488 521 1487 521 483 521 1488 520 483 521 1488 521 1488 521 1488 520 483 521 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4497 4438 513 508 492 503 487 509 491 504 486 1479 512 1479 511 510 490 505 485 1481 510 1481 510 1481 509 1482 509 512 488 507 483 513 487 508 482 4457 515 506 484 511 489 506 484 512 488 507 483 1482 509 513 487 1478 513 508 482 513 487 509 491 1473 518 1474 517 504 486 1479 512 510 490 1475 515 1475 516 1476 514 480 510 55019 4494 4442 509 511 489 507 483 512 488 507 483 1508 483 1508 483 513 487 508 482 1482 509 1483 508 1509 492 1499 492 504 486 510 490 505 485 510 490 4449 513 508 482 487 513 508 482 488 512 483 517 1499 492 504 486 1505 486 510 490 479 511 510 490 1501 490 1501 490 506 484 1506 485 511 489 1502 489 1476 515 1476 515 507 483 # name: Next type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4503 4434 517 504 486 510 490 505 485 510 490 1475 515 1476 514 507 483 512 488 1477 513 1477 513 1478 512 1479 511 483 517 479 511 484 516 479 511 4455 516 478 512 483 517 479 511 485 515 480 510 486 514 1477 513 1477 513 482 518 477 513 483 517 1474 516 1474 516 1475 515 480 510 485 515 1476 514 1477 513 1477 513 483 517 55012 4497 4440 511 484 516 480 510 485 515 480 510 1481 509 1482 509 487 513 482 518 1473 517 1474 516 1474 516 1475 515 480 510 486 514 481 509 486 514 4451 510 485 567 428 510 486 514 481 509 487 565 430 570 1421 518 1473 518 478 512 483 517 478 512 1479 511 1480 510 1480 510 486 514 481 509 1481 510 1481 509 1482 509 487 513 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4583 4441 575 454 550 455 549 457 548 456 549 1461 550 1460 549 456 549 457 549 1461 548 1462 549 1461 549 1461 548 457 548 457 549 457 547 457 549 4462 548 458 548 457 548 457 548 457 548 457 548 1462 548 457 548 1461 549 457 548 457 548 457 548 1462 548 1461 549 457 548 1461 549 457 548 1461 549 1461 548 1462 547 458 547 55448 4580 4465 550 456 549 456 549 456 550 456 549 1461 549 1460 549 457 549 456 549 1461 549 1461 549 1461 548 1461 549 457 548 457 549 457 548 457 548 4462 549 457 548 457 548 458 548 457 548 457 548 1462 549 457 547 1462 549 457 548 457 548 457 548 1462 548 1462 549 457 548 1462 548 457 548 1462 548 1461 549 1462 548 457 548 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4583 4441 575 454 550 455 549 457 548 456 549 1461 550 1460 549 456 549 457 549 1461 548 1462 549 1461 549 1461 548 457 548 457 549 457 547 457 549 4462 548 458 548 457 548 457 548 457 548 457 548 1462 548 457 548 1461 549 457 548 457 548 457 548 1462 548 1461 549 457 548 1461 549 457 548 1461 549 1461 548 1462 547 458 547 55448 4580 4465 550 456 549 456 549 456 550 456 549 1461 549 1460 549 457 549 456 549 1461 549 1461 549 1461 548 1461 549 457 548 457 549 457 548 457 548 4462 549 457 548 457 548 458 548 457 548 457 548 1462 549 457 547 1462 549 457 548 457 548 457 548 1462 548 1462 549 457 548 1462 548 457 548 1462 548 1461 549 1462 548 457 548 # name: Next type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4582 4465 551 454 550 455 550 455 549 456 549 1460 550 1460 549 457 548 456 549 1461 549 1461 548 1461 549 1460 549 457 549 456 548 457 548 457 548 4461 549 456 548 457 548 457 548 457 548 457 548 457 548 1462 548 1461 547 457 549 457 548 456 549 1460 549 1461 547 1461 549 457 547 457 548 1461 549 1461 548 1461 548 457 548 55436 4578 4464 549 455 549 456 549 455 549 456 550 1460 549 1460 549 456 550 455 550 1460 550 1460 549 1460 550 1460 549 456 549 457 548 457 548 457 549 4461 548 457 548 457 548 457 548 457 547 457 549 457 547 1462 548 1461 549 457 548 457 548 457 548 1461 548 1461 548 1462 548 457 548 457 549 1461 549 1461 548 1461 548 457 548 # name: Prev type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4554 4466 540 469 547 490 515 495 521 490 494 1478 522 1477 544 466 539 471 513 1512 488 1484 516 1483 570 1429 540 497 519 465 540 470 546 491 493 4490 548 462 543 467 549 488 517 493 491 1481 519 1480 541 495 489 1510 522 462 543 494 521 489 495 1476 545 466 539 471 513 1485 547 464 520 1505 495 1477 513 1487 545 465 540 # name: Next type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4555 4464 542 468 548 462 543 467 549 488 496 1477 523 1476 545 465 551 459 525 1475 515 1484 516 1483 517 1482 550 486 519 465 551 459 546 464 520 4490 547 488 517 467 549 462 543 467 548 462 543 467 517 1482 518 1481 540 496 520 464 541 469 515 1484 516 1483 517 1482 550 460 545 465 519 1480 520 1479 521 1478 543 493 522 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4549 4471 546 464 541 469 547 463 542 469 515 1484 516 1483 549 461 544 466 518 1481 519 1480 520 1479 521 1477 544 466 550 461 544 467 549 461 523 4486 541 469 546 464 541 469 547 464 541 469 515 1483 549 462 522 1477 544 465 540 471 545 465 572 1428 520 1479 542 468 569 1430 549 462 522 1476 514 1486 514 1485 547 463 542 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4552 4464 586 420 584 420 584 420 612 392 529 1478 531 1478 531 476 553 451 553 1455 554 1479 528 1480 528 1481 527 478 526 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 1483 526 478 526 1483 526 478 526 478 526 478 526 1483 526 1483 526 478 526 1483 526 479 525 1483 526 1483 526 1483 526 478 526 # name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4548 4471 540 471 541 468 544 467 545 465 516 1482 512 1488 537 473 539 472 519 1479 514 1484 520 1479 515 1485 539 470 542 468 544 466 546 464 517 4493 538 472 540 470 542 468 544 466 546 464 517 1482 543 468 513 544 471 469 574 436 514 1485 519 1480 545 465 547 1452 573 438 543 1456 548 1451 543 1456 569 442 570 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4549 4471 546 464 541 469 547 463 542 469 515 1484 516 1483 549 461 544 466 518 1481 519 1480 520 1479 521 1477 544 466 550 461 544 467 549 461 523 4486 541 469 546 464 541 469 547 464 541 469 515 1483 549 462 522 1477 544 465 540 471 545 465 572 1428 520 1479 542 468 569 1430 549 462 522 1476 514 1486 514 1485 547 463 542 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4552 4464 586 420 584 420 584 420 612 392 529 1478 531 1478 531 476 553 451 553 1455 554 1479 528 1480 528 1481 527 478 526 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 1483 526 478 526 1483 526 478 526 478 526 478 526 1483 526 1483 526 478 526 1483 526 479 525 1483 526 1483 526 1483 526 478 526 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 4548 4471 540 471 541 468 544 467 545 465 516 1482 512 1488 537 473 539 472 519 1479 514 1484 520 1479 515 1485 539 470 542 468 544 466 546 464 517 4493 538 472 540 470 542 468 544 466 546 464 517 1482 543 468 513 544 471 469 574 436 514 1485 519 1480 545 465 547 1452 573 438 543 1456 548 1451 543 1456 569 442 570 # name: Next @@ -2837,7 +2816,7 @@ type: parsed protocol: NEC address: 00 00 00 00 command: 41 00 00 00 -# +# name: Play type: parsed protocol: NECext @@ -2983,72 +2962,6 @@ address: C8 91 00 00 command: 21 DE 00 00 # name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4583 4441 575 454 550 455 549 457 548 456 549 1461 550 1460 549 456 549 457 549 1461 548 1462 549 1461 549 1461 548 457 548 457 549 457 547 457 549 4462 548 458 548 457 548 457 548 457 548 457 548 1462 548 457 548 1461 549 457 548 457 548 457 548 1462 548 1461 549 457 548 1461 549 457 548 1461 549 1461 548 1462 547 458 547 55448 4580 4465 550 456 549 456 549 456 550 456 549 1461 549 1460 549 457 549 456 549 1461 549 1461 549 1461 548 1461 549 457 548 457 549 457 548 457 548 4462 549 457 548 457 548 458 548 457 548 457 548 1462 549 457 547 1462 549 457 548 457 548 457 548 1462 548 1462 549 457 548 1462 548 457 548 1462 548 1461 549 1462 548 457 548 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4583 4441 575 454 550 455 549 457 548 456 549 1461 550 1460 549 456 549 457 549 1461 548 1462 549 1461 549 1461 548 457 548 457 549 457 547 457 549 4462 548 458 548 457 548 457 548 457 548 457 548 1462 548 457 548 1461 549 457 548 457 548 457 548 1462 548 1461 549 457 548 1461 549 457 548 1461 549 1461 548 1462 547 458 547 55448 4580 4465 550 456 549 456 549 456 550 456 549 1461 549 1460 549 457 549 456 549 1461 549 1461 549 1461 548 1461 549 457 548 457 549 457 548 457 548 4462 549 457 548 457 548 458 548 457 548 457 548 1462 549 457 547 1462 549 457 548 457 548 457 548 1462 548 1462 549 457 548 1462 548 457 548 1462 548 1461 549 1462 548 457 548 -# -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4582 4465 551 454 550 455 550 455 549 456 549 1460 550 1460 549 457 548 456 549 1461 549 1461 548 1461 549 1460 549 457 549 456 548 457 548 457 548 4461 549 456 548 457 548 457 548 457 548 457 548 457 548 1462 548 1461 547 457 549 457 548 456 549 1460 549 1461 547 1461 549 457 547 457 548 1461 549 1461 548 1461 548 457 548 55436 4578 4464 549 455 549 456 549 455 549 456 550 1460 549 1460 549 456 550 455 550 1460 550 1460 549 1460 550 1460 549 456 549 457 548 457 548 457 549 4461 548 457 548 457 548 457 548 457 547 457 549 457 547 1462 548 1461 549 457 548 457 548 457 548 1461 548 1461 548 1462 548 457 548 457 549 1461 549 1461 548 1461 548 457 548 -# -name: Prev -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4554 4466 540 469 547 490 515 495 521 490 494 1478 522 1477 544 466 539 471 513 1512 488 1484 516 1483 570 1429 540 497 519 465 540 470 546 491 493 4490 548 462 543 467 549 488 517 493 491 1481 519 1480 541 495 489 1510 522 462 543 494 521 489 495 1476 545 466 539 471 513 1485 547 464 520 1505 495 1477 513 1487 545 465 540 -# -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4555 4464 542 468 548 462 543 467 549 488 496 1477 523 1476 545 465 551 459 525 1475 515 1484 516 1483 517 1482 550 486 519 465 551 459 546 464 520 4490 547 488 517 467 549 462 543 467 548 462 543 467 517 1482 518 1481 540 496 520 464 541 469 515 1484 516 1483 517 1482 550 460 545 465 519 1480 520 1479 521 1478 543 493 522 -# -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4549 4471 546 464 541 469 547 463 542 469 515 1484 516 1483 549 461 544 466 518 1481 519 1480 520 1479 521 1477 544 466 550 461 544 467 549 461 523 4486 541 469 546 464 541 469 547 464 541 469 515 1483 549 462 522 1477 544 465 540 471 545 465 572 1428 520 1479 542 468 569 1430 549 462 522 1476 514 1486 514 1485 547 463 542 -# -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4552 4464 586 420 584 420 584 420 612 392 529 1478 531 1478 531 476 553 451 553 1455 554 1479 528 1480 528 1481 527 478 526 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 1483 526 478 526 1483 526 478 526 478 526 478 526 1483 526 1483 526 478 526 1483 526 479 525 1483 526 1483 526 1483 526 478 526 -# -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4548 4471 540 471 541 468 544 467 545 465 516 1482 512 1488 537 473 539 472 519 1479 514 1484 520 1479 515 1485 539 470 542 468 544 466 546 464 517 4493 538 472 540 470 542 468 544 466 546 464 517 1482 543 468 513 544 471 469 574 436 514 1485 519 1480 545 465 547 1452 573 438 543 1456 548 1451 543 1456 569 442 570 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4549 4471 546 464 541 469 547 463 542 469 515 1484 516 1483 549 461 544 466 518 1481 519 1480 520 1479 521 1477 544 466 550 461 544 467 549 461 523 4486 541 469 546 464 541 469 547 464 541 469 515 1483 549 462 522 1477 544 465 540 471 545 465 572 1428 520 1479 542 468 569 1430 549 462 522 1476 514 1486 514 1485 547 463 542 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4552 4464 586 420 584 420 584 420 612 392 529 1478 531 1478 531 476 553 451 553 1455 554 1479 528 1480 528 1481 527 478 526 478 526 478 526 478 526 4497 526 478 526 478 526 478 526 478 526 478 526 1483 526 478 526 1483 526 478 526 478 526 478 526 1483 526 1483 526 478 526 1483 526 479 525 1483 526 1483 526 1483 526 478 526 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.33 -data: 4548 4471 540 471 541 468 544 467 545 465 516 1482 512 1488 537 473 539 472 519 1479 514 1484 520 1479 515 1485 539 470 542 468 544 466 546 464 517 4493 538 472 540 470 542 468 544 466 546 464 517 1482 543 468 513 544 471 469 574 436 514 1485 519 1480 545 465 547 1452 573 438 543 1456 548 1451 543 1456 569 442 570 -# -name: Play type: parsed protocol: SIRC20 address: 10 01 00 00 @@ -3096,8 +3009,6 @@ protocol: NEC address: 20 00 00 00 command: 1B 00 00 00 # -# Speakers -# name: Play type: raw frequency: 38000 @@ -3392,42 +3303,6 @@ protocol: NECext address: 2D D3 00 00 command: 06 F9 00 00 # -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1041 1461 540 457 543 453 537 459 541 1459 541 1459 542 1459 541 1459 542 1458 542 1458 543 1458 542 1458 542 453 537 459 541 455 545 451 539 450 540 50518 1041 1461 540 457 543 452 538 459 541 1459 541 1459 542 1459 541 1458 543 1458 542 1458 542 1458 542 1457 544 453 537 459 541 455 545 451 539 449 541 50534 1036 1467 544 452 538 458 542 454 546 1455 545 1454 546 1454 536 1464 537 1464 536 1463 537 1463 537 1463 537 459 541 454 546 450 540 457 543 445 545 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1041 1461 540 457 543 453 537 459 541 1459 541 1459 542 1459 541 1459 542 1458 542 1458 543 1458 542 1458 542 453 537 459 541 455 545 451 539 450 540 50518 1041 1461 540 457 543 452 538 459 541 1459 541 1459 542 1459 541 1458 543 1458 542 1458 542 1458 542 1457 544 453 537 459 541 455 545 451 539 449 541 50534 1036 1467 544 452 538 458 542 454 546 1455 545 1454 546 1454 536 1464 537 1464 536 1463 537 1463 537 1463 537 459 541 454 546 450 540 457 543 445 545 -# -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1044 1460 540 456 544 1457 543 1457 543 453 536 1465 545 450 539 457 543 1458 542 1458 542 454 546 451 539 1462 538 458 542 1459 541 1460 540 448 542 50525 1042 1462 538 458 542 1458 542 1459 541 455 545 1456 544 452 537 459 541 1460 540 1460 540 456 544 452 537 1464 536 460 540 1461 539 1461 539 449 540 50542 1046 1458 542 480 520 1455 545 1455 545 477 512 1463 547 474 515 481 519 1456 544 1457 543 478 522 475 514 1460 540 482 518 1457 543 1458 542 445 544 -# -name: Prev -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1046 1457 543 1458 542 454 546 450 539 1461 539 456 544 1458 542 1458 542 454 546 451 539 1461 539 1462 538 458 542 1459 541 454 546 451 539 1454 546 50538 1043 1461 539 1461 539 456 544 453 547 1454 546 449 541 1461 539 1462 538 457 543 453 547 1455 545 1455 545 451 539 1462 538 458 542 454 546 1447 543 50526 1044 1459 541 1459 541 455 545 451 539 1463 537 458 542 1459 541 1460 540 455 545 452 538 1463 537 1464 536 460 540 1461 539 456 544 453 536 1456 544 -# -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1013 1488 512 486 514 486 514 1485 515 484 516 484 516 1483 517 1482 518 1481 509 1490 510 1488 512 488 512 1487 513 1485 515 484 516 483 517 481 519 50939 1010 1489 511 488 512 488 512 1486 514 486 514 486 514 1485 515 1483 517 1482 518 1481 509 1489 511 489 511 1487 513 1485 515 484 516 484 516 482 518 50958 1013 1488 512 487 513 487 513 1485 515 485 515 485 515 1484 516 1483 517 1482 518 1481 509 1490 510 490 572 1426 512 1487 513 486 514 486 514 485 566 50908 1011 1490 510 489 511 489 511 1487 513 486 514 487 513 1485 515 1484 516 1483 517 1482 518 1480 510 491 571 1427 511 1488 512 488 512 487 513 486 565 50905 1013 1488 512 488 512 488 512 1486 514 486 514 486 514 1485 515 1484 516 1483 517 1481 509 1489 511 489 511 1488 512 1486 514 485 515 485 515 484 516 -# -name: Pause -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1013 1488 512 486 514 486 514 1485 515 484 516 484 516 1483 517 1482 518 1481 509 1490 510 1488 512 488 512 1487 513 1485 515 484 516 483 517 481 519 50939 1010 1489 511 488 512 488 512 1486 514 486 514 486 514 1485 515 1483 517 1482 518 1481 509 1489 511 489 511 1487 513 1485 515 484 516 484 516 482 518 50958 1013 1488 512 487 513 487 513 1485 515 485 515 485 515 1484 516 1483 517 1482 518 1481 509 1490 510 490 572 1426 512 1487 513 486 514 486 514 485 566 50908 1011 1490 510 489 511 489 511 1487 513 486 514 487 513 1485 515 1484 516 1483 517 1482 518 1480 510 491 571 1427 511 1488 512 488 512 487 513 486 565 50905 1013 1488 512 488 512 488 512 1486 514 486 514 486 514 1485 515 1484 516 1483 517 1481 509 1489 511 489 511 1488 512 1486 514 485 515 485 515 484 516 -# name: Next type: parsed protocol: NEC @@ -3476,50 +3351,314 @@ protocol: NEC address: 02 00 00 00 command: 05 00 00 00 # -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 3303 1908 408 1179 410 1177 412 442 409 444 407 446 405 448 414 1173 405 448 414 1173 416 1172 406 447 415 439 412 1175 414 440 411 1175 414 440 411 1175 414 440 411 442 409 444 407 447 415 438 413 440 411 442 409 444 407 446 405 1182 407 447 415 438 413 440 411 442 409 444 407 1180 409 1178 411 443 408 445 406 1180 409 445 406 447 415 438 413 440 411 442 409 444 407 1180 409 444 407 446 405 448 414 440 411 41789 3301 3346 362 42926 3307 3341 357 -# -name: Prev -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 3301 1910 406 1155 434 1180 409 419 432 421 441 439 412 441 410 1178 411 416 435 1152 437 1177 412 442 409 417 434 1180 409 418 433 1180 409 445 406 1155 434 419 432 447 415 413 438 442 409 444 407 419 432 448 414 439 412 442 409 1177 412 442 409 444 407 420 431 448 414 413 438 1176 413 1174 415 413 438 1175 414 1173 405 422 440 440 411 416 435 418 433 447 415 439 412 441 410 1177 412 415 436 417 434 446 405 41164 3301 3321 387 42935 3307 3316 381 42941 3311 3312 385 -# name: Play type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3309 1901 415 1173 405 1182 407 420 442 412 439 441 410 443 408 1179 410 444 407 1180 409 1179 410 444 407 420 431 1183 406 447 415 1147 442 412 439 1148 441 439 412 441 410 443 408 446 405 448 414 440 411 415 436 444 407 446 405 1182 407 447 415 412 439 440 411 443 408 418 433 1181 408 1180 409 418 433 447 415 439 412 441 410 443 408 445 406 1181 408 1180 409 1178 411 417 434 445 406 421 441 439 412 415 436 41161 3303 3320 388 42939 3301 3347 361 # name: Pause type: raw frequency: 38000 -duty_cycle: 0.330000 +duty_cycle: 0.33 data: 3309 1901 415 1173 405 1182 407 420 442 412 439 441 410 443 408 1179 410 444 407 1180 409 1179 410 444 407 420 431 1183 406 447 415 1147 442 412 439 1148 441 439 412 441 410 443 408 446 405 448 414 440 411 415 436 444 407 446 405 1182 407 447 415 412 439 440 411 443 408 418 433 1181 408 1180 409 418 433 447 415 439 412 441 410 443 408 445 406 1181 408 1180 409 1178 411 417 434 445 406 421 441 439 412 415 436 41161 3303 3320 388 42939 3301 3347 361 # -name: Play -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9175 4433 644 1605 645 442 669 466 646 445 666 438 673 466 645 440 671 466 673 410 700 365 720 465 675 367 742 367 744 438 645 1604 675 1574 674 1549 702 1575 673 1576 646 1604 675 1574 676 1574 675 1576 644 1604 645 1606 672 1576 645 466 645 466 645 465 646 1575 675 1605 645 466 645 467 644 467 644 1605 644 1605 645 1606 644 467 644 467 644 1606 643 1578 672 1607 643 -# -name: Next -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9146 4435 642 1577 673 449 662 454 658 451 661 469 642 468 644 469 642 453 659 470 642 470 642 469 643 469 642 469 642 469 642 1608 642 1606 643 1607 642 1607 642 1607 642 1577 673 1607 643 1607 642 1607 642 1607 642 1606 643 1576 674 1606 643 468 643 439 672 1607 642 1607 642 448 663 469 642 439 672 469 642 1577 672 1607 642 468 643 469 642 1607 642 1608 641 1608 640 -# -name: Prev -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9144 4405 671 1607 642 469 642 469 642 469 642 470 641 365 747 469 641 443 669 469 642 468 643 446 665 469 642 469 642 470 642 1580 669 1607 643 1607 642 1608 641 1578 672 1580 670 1608 641 1608 641 1609 590 1660 590 1635 615 1660 590 1660 590 1632 618 1633 617 521 590 1660 590 521 590 521 618 493 590 521 590 520 591 519 592 1657 592 519 592 1658 617 1633 616 1633 615 23844 9114 4461 615 -# name: Next type: parsed protocol: NEC address: FD 00 00 00 command: EA 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 45 00 00 00 +# +name: Power +type: parsed +protocol: NEC42 +address: 51 00 00 00 +command: 00 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC42 +address: 51 00 00 00 +command: 1F 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC42 +address: 51 00 00 00 +command: 1E 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 00 FF 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 10 E7 00 00 +command: 01 FE 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 79 00 00 +command: 80 7F 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 79 00 00 +command: 99 66 00 00 +# +name: Power +type: parsed +protocol: NECext +address: D2 6C 00 00 +command: 47 B8 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 55 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 02 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 01 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 12 00 00 00 +# +name: Power +type: parsed +protocol: RC5X +address: 1B 00 00 00 +command: 0C 00 00 00 +# +name: Mute +type: parsed +protocol: RC5X +address: 1B 00 00 00 +command: 0D 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 01 00 00 00 +# +name: Pause +type: parsed +protocol: Kaseikyo +address: AC 02 20 00 +command: 61 00 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 8560 4222 563 1558 563 511 562 1557 565 537 536 512 561 1558 564 488 585 1558 564 486 587 1559 563 486 587 1558 564 1557 565 509 565 1559 563 510 564 485 588 1557 565 487 586 1557 565 486 587 511 536 514 560 485 588 1557 565 487 586 1559 563 486 587 1558 564 1558 563 1585 562 1557 565 25407 8561 4220 564 1557 564 511 563 1558 563 508 566 485 588 1558 564 512 561 1557 565 486 587 1557 565 487 586 1558 564 1559 562 536 537 1558 564 508 565 487 586 1585 536 485 589 1558 564 486 587 485 563 511 562 487 586 1558 564 488 585 1557 565 485 588 1557 565 1558 564 1586 561 1556 566 25433 8534 4220 565 1584 537 536 538 1585 537 508 565 511 562 1584 538 511 562 1585 537 511 562 1584 538 511 562 1584 537 1584 538 536 537 1585 537 536 538 511 562 1584 538 510 563 1584 537 510 564 510 537 506 568 483 590 1584 538 511 562 1585 537 510 563 1584 538 1557 565 1585 562 1584 538 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 8562 4223 564 1557 591 509 538 1560 588 484 563 508 566 1558 563 508 566 1555 566 510 564 1556 565 536 538 1556 565 1558 590 483 564 1557 591 510 537 1559 589 1583 539 483 590 1555 567 510 563 482 565 536 538 484 589 509 538 510 565 1556 565 536 537 1585 537 1558 590 1583 539 1559 562 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 8571 4203 588 509 539 1561 589 510 538 536 539 510 565 1555 569 511 699 1449 539 1585 539 536 539 1558 566 1562 588 1585 539 511 563 1556 703 350 590 482 567 535 540 1560 564 1559 591 1584 540 485 590 510 539 507 568 1558 566 1586 564 510 539 508 567 511 563 1585 539 1585 539 1559 591 25433 8571 4228 563 510 539 1586 564 510 539 509 565 511 564 1558 566 486 589 1585 539 1585 539 536 539 1585 539 1586 621 1528 596 455 620 1501 623 454 620 453 674 400 596 1528 674 1425 647 1528 655 394 681 393 673 402 656 1468 672 1453 681 393 671 404 655 395 679 1469 655 1470 668 1457 678 +# +name: Mute +type: parsed +protocol: NECext +address: 83 22 00 00 +command: 0C F3 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1E 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9028 3517 173 801 573 559 573 559 573 557 575 583 539 565 567 1676 568 590 542 563 569 1674 570 1675 569 1676 568 1677 567 1678 576 556 566 1679 575 1669 575 557 575 556 566 1678 576 1696 548 557 575 556 566 565 567 564 568 1675 569 1676 568 564 568 563 569 1674 570 1702 542 1703 541 1678 576 40469 9030 2232 568 96806 9035 2229 571 96799 9033 2230 570 +# +name: Power +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 1E 00 00 00 +# +name: Pause +type: parsed +protocol: Samsung32 +address: 2C 00 00 00 +command: 4F 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 B6 00 00 +command: 4D B2 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 B6 00 00 +command: 43 BC 00 00 +# +name: Play +type: parsed +protocol: NECext +address: C8 91 00 00 +command: 21 DE 00 00 +# +name: Pause +type: parsed +protocol: NECext +address: 86 FF 00 00 +command: 2A D5 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 45 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 16 00 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 15 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 03 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 48 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1D 00 00 00 +# +name: Play +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1F 00 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1F 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 11 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 15 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 02 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 09 00 00 00 +# +name: Pause +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 0C 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 0C 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 42 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 43 00 00 00 +# +name: Next +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 40 00 00 00 +# +name: Play +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 44 00 00 00 +# +name: Pause +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 44 00 00 00 From d8162205962e12366dab106f4e8ebf212360e584 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:44:05 +0100 Subject: [PATCH 203/370] Update fans.ir New additions --- assets/resources/infrared/assets/fans.ir | 250 ++++++++++++++++++++++- 1 file changed, 248 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 734de79e0..67646b343 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 17th May, 2023 -# Last Checked 17th May, 2023 +# Last Updated 16th Jun, 2023 +# Last Checked 16th Jun, 2023 # name: Power type: raw @@ -1533,3 +1533,249 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1369 349 1370 319 521 1167 1371 321 1340 349 466 1221 467 1222 467 1221 468 1221 1318 373 466 1220 468 7917 1344 347 1341 349 489 1198 1340 351 1338 352 487 1202 486 1202 486 1202 486 1202 1337 353 486 1202 486 7922 1312 377 1313 377 462 1227 1312 377 1313 378 461 1227 461 1227 462 1227 462 1227 1312 377 462 1227 461 7923 1312 378 1311 378 461 1227 1312 378 1311 377 462 1227 461 1227 461 1227 461 1227 1312 377 462 1227 462 7922 1312 378 1312 377 462 1227 1312 354 1336 354 485 1227 461 1227 461 1227 461 1203 1336 353 486 1203 486 7922 1312 353 1336 354 485 1203 1336 354 1336 354 485 1203 485 1227 461 1227 461 1227 1312 378 461 1227 461 7923 1312 378 1312 378 461 1227 1312 378 1312 378 461 1228 461 1228 461 1228 460 1228 1311 378 461 1227 461 7923 1311 378 1311 378 461 1227 1312 378 1312 378 461 1227 462 1227 461 1227 461 1227 1312 378 461 1228 461 6641 1312 378 1312 355 484 1228 1311 355 1335 378 461 1228 461 1228 460 1228 460 1228 1311 378 461 1228 460 7924 1311 379 1310 379 460 1228 1311 379 1311 379 460 1229 459 1229 459 1229 460 1229 1310 380 459 1229 459 7925 1310 380 1309 381 458 1230 1309 381 1309 381 458 1230 458 1231 457 1231 458 1231 1308 381 458 1231 457 7952 1283 407 1283 407 432 1256 1283 407 1283 407 432 1257 431 1257 432 1257 431 1257 1283 408 431 1257 431 +# +name: Mode +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1372 307 1369 310 524 1149 1370 310 1366 334 500 1151 526 1152 1367 335 499 1155 522 1156 521 1179 498 7895 1339 338 1339 338 496 1181 1339 339 1338 339 496 1182 496 1182 1339 339 496 1182 496 1182 496 1182 496 7897 1338 339 1338 339 496 1182 1339 339 1338 339 496 1182 496 1182 1338 339 496 1182 496 1183 495 1183 495 7897 1338 339 1338 339 496 1182 1339 339 1338 340 495 1183 495 1183 1337 340 495 1183 495 1183 495 1183 495 7898 1337 340 1337 340 495 1183 1337 340 1337 340 495 1183 495 1183 1337 340 495 1183 495 1183 495 1183 495 7898 1337 340 1338 340 495 1183 1337 340 1337 340 494 1183 495 1184 1336 340 495 1184 494 1184 494 1183 495 7898 1337 340 1337 341 494 1183 1338 340 1337 340 495 1183 495 1183 1337 340 494 1184 494 1184 494 1183 495 7898 1337 340 1337 340 494 1184 1337 341 1336 341 494 1184 494 1184 1337 340 494 1184 494 1184 494 1184 494 7898 1337 341 1336 341 494 1184 1336 341 1336 341 494 1184 494 1184 1336 341 494 1184 494 1184 494 1184 494 7899 1336 341 1336 341 494 1184 1336 341 1336 341 494 1184 494 1184 1336 341 494 1184 494 1184 494 1184 494 7899 1335 341 1337 341 494 1184 1336 341 1336 342 493 1185 493 1185 1335 342 493 1185 493 1185 493 1185 493 7899 1336 342 1335 342 493 1185 1335 342 1335 342 493 1185 493 1185 1335 342 493 1185 493 1185 493 1185 493 7899 1336 342 1335 342 493 1185 1335 342 1335 342 493 1185 493 1185 1335 342 493 1185 493 1185 493 1185 493 7900 1335 342 1335 342 493 1185 1335 342 1335 342 493 1185 493 1185 1335 342 493 1186 492 1186 492 1185 492 7900 1335 342 1335 343 492 1186 1334 343 1334 343 492 1186 492 1186 1334 343 492 1186 492 1186 492 1186 492 7901 1334 343 1334 344 491 1186 1334 344 1333 344 491 1187 491 1187 1333 344 491 1186 491 1187 491 1187 491 7901 1334 344 1333 368 467 1211 1309 368 1309 368 466 1211 467 1211 1309 368 467 1211 467 1211 467 1211 467 7926 1309 368 1309 369 466 1212 1308 369 1308 369 465 1212 466 1212 1308 369 465 1212 466 1212 466 1212 466 7927 1308 369 1308 369 466 1212 1308 370 1307 370 464 1213 465 1213 1307 370 464 1213 465 1213 465 1213 465 7927 1307 370 1307 370 464 1213 1308 370 1307 371 464 1214 464 1214 1306 371 464 1214 464 1214 464 1214 463 7928 1306 371 1306 371 463 1215 1305 372 1305 372 463 1215 463 1214 1306 372 463 1214 463 1215 463 1215 463 7929 1305 396 1281 397 437 1240 1280 397 1280 397 437 1241 437 1217 1303 397 437 1240 438 1240 438 1240 438 7954 1280 397 1255 422 412 1266 1255 423 1254 422 412 1266 412 1266 1255 423 411 1266 412 1266 412 1266 412 7980 1255 422 1255 423 411 1266 1255 423 1254 423 411 1267 411 1266 1255 423 411 1266 412 1267 410 1267 411 7980 1254 424 1253 424 410 1267 1254 424 1253 425 409 1267 410 1268 1253 450 384 1268 410 1268 410 1268 410 7982 1252 450 1227 450 384 1294 1227 450 1227 450 384 1294 384 1294 1227 451 383 1294 384 1295 383 1294 383 8008 1227 451 1226 451 383 1295 1226 452 1225 452 382 1296 382 1296 1225 478 355 1321 356 1296 382 1296 381 8010 1225 478 1199 478 355 1322 1200 479 1198 505 192 1459 355 1323 1199 505 145 1506 354 1324 353 1324 275 8116 1191 3869 795 +# +name: Speed_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9230 4449 644 522 617 523 617 524 615 526 613 529 611 530 610 530 610 531 609 1640 610 1640 610 1640 609 1640 609 1640 610 1640 609 1640 609 1640 609 531 609 531 609 1640 609 531 609 531 609 531 609 1640 609 531 609 1640 609 1641 608 532 608 1641 608 1641 608 1641 608 532 608 1641 608 40020 9177 2212 611 +# +name: Timer +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 15 00 00 00 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9215 4421 664 475 664 500 638 500 638 501 662 476 662 477 661 478 661 1588 659 1589 658 1614 633 1615 632 1615 606 1640 606 1641 629 1618 630 508 631 1616 631 1616 632 1616 632 507 632 507 633 482 657 481 658 481 658 480 659 480 659 480 659 1589 658 1589 658 1589 658 1589 658 1589 658 39821 9206 2163 659 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1290 398 1290 397 446 1243 1290 397 1291 397 446 1242 446 1242 446 1243 445 1244 444 1243 445 1243 1262 7180 1262 425 1263 426 445 1243 1262 426 1262 425 446 1243 445 1244 444 1243 445 1242 446 1242 446 1242 1264 7181 1262 425 1263 425 445 1244 1262 424 1264 424 446 1243 445 1243 445 1244 444 1243 445 1243 445 1244 1263 7179 1263 424 1264 424 445 1243 1263 425 1263 425 445 1244 444 1244 444 1243 444 1245 417 1270 418 1269 1264 7180 1262 425 1263 425 418 1270 1263 425 1263 424 419 1270 418 1270 418 1270 418 1270 418 1270 418 1270 1263 7179 1264 424 1264 424 419 1269 1264 424 1264 424 419 1270 418 1269 419 1270 418 1270 418 1270 418 1270 1263 7179 1264 424 1264 424 419 1269 1264 424 1264 424 419 1270 418 1270 418 1270 418 1269 419 1270 418 1269 1264 7180 1262 424 1264 424 419 1269 1264 425 1263 424 419 1270 418 1269 419 1270 418 1270 418 1270 418 1269 1264 +# +name: Speed_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1262 424 1263 424 419 1268 1264 424 1264 424 419 1270 418 1270 418 1270 417 1270 418 1270 1263 424 418 8023 1263 424 1263 424 418 1270 1263 424 1264 424 418 1270 418 1269 419 1270 418 1269 419 1269 1264 424 418 8022 1264 424 1264 424 419 1269 1264 424 1264 424 418 1269 419 1269 419 1269 419 1269 418 1269 1264 424 418 8023 1263 423 1265 423 419 1271 1262 424 1264 423 419 1269 419 1268 420 1270 418 1269 419 1268 1265 424 418 8024 1263 423 1265 423 419 1269 1264 423 1265 424 418 1269 418 1270 418 1269 419 1269 419 1268 1265 424 418 8023 1263 424 1263 423 419 1269 1264 423 1265 423 419 1270 418 1269 419 1269 419 1269 419 1269 1264 423 419 8022 1264 424 1263 424 418 1269 1264 423 1265 424 418 1268 420 1269 419 1269 419 1269 419 1269 1264 424 419 8023 1263 423 1264 424 418 1269 1264 424 1264 424 418 1270 418 1269 419 1269 418 1269 419 1269 1264 424 418 8023 1264 424 1264 424 418 1269 1264 424 1264 423 420 1269 419 1270 418 1268 420 1269 419 1269 1264 423 419 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1264 424 1263 423 419 1268 1265 423 1264 423 419 1269 418 1270 1262 423 419 1270 417 1269 419 1268 420 8022 1262 423 1264 423 419 1268 1264 424 1263 423 419 1268 419 1269 1263 423 419 1269 419 1268 420 1269 418 8022 1263 423 1264 424 418 1268 1264 423 1264 424 419 1269 418 1269 1264 423 419 1269 418 1269 419 1269 419 8021 1264 423 1264 424 418 1268 1264 423 1264 423 419 1268 419 1269 1263 423 419 1268 419 1268 419 1269 419 8020 1264 423 1264 423 419 1268 1265 423 1264 423 419 1269 418 1269 1264 423 419 1270 417 1268 420 1269 418 8022 1263 423 1265 423 419 1267 1266 423 1264 423 419 1268 419 1269 1263 423 419 1269 418 1268 419 1268 420 8022 1263 423 1264 423 419 1268 1265 423 1264 423 419 1268 420 1269 1264 423 419 1268 420 1268 419 1268 420 8021 1264 422 1265 423 419 1269 1263 423 1264 423 419 1269 418 1268 1264 423 419 1269 419 1269 418 1268 419 8021 1264 424 1263 423 419 1269 1263 423 1264 423 419 1269 418 1268 1264 424 418 1270 417 1268 419 1268 419 8022 1262 423 1264 423 420 1269 1263 423 1264 424 418 1269 418 1268 1264 424 418 1269 419 1269 418 1269 419 8021 1263 424 1263 424 418 1269 1263 423 1264 423 419 1269 419 1269 1263 423 419 1269 419 1269 419 1269 419 8021 1264 423 1264 423 419 1269 1263 423 1264 423 419 1268 420 1269 1263 423 419 1268 419 1269 418 1269 419 +# +name: Mode +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1262 423 1264 423 419 1268 1264 423 1264 423 419 1269 419 1268 419 1268 419 1268 1264 423 419 1268 419 8020 1264 423 1264 423 419 1268 1264 423 1264 423 419 1268 419 1269 419 1268 419 1268 1264 423 419 1269 418 8021 1264 423 1264 423 419 1268 1264 424 1263 423 419 1269 419 1268 419 1268 419 1268 1264 423 419 1268 420 8021 1264 423 1264 423 419 1268 1264 423 1264 424 418 1268 420 1268 420 1269 418 1269 1263 423 419 1268 419 8021 1264 423 1264 423 419 1269 1263 423 1264 424 418 1269 418 1269 419 1268 419 1268 1264 423 419 1268 419 8021 1264 423 1264 423 419 1268 1264 423 1264 424 418 1268 419 1268 419 1268 419 1268 1264 424 418 1269 419 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2199 716 759 763 701 742 732 737 727 742 732 737 727 1448 759 738 726 743 732 1444 752 771 724 745 729 1447 760 1417 758 739 746 750 725 1426 760 737 727 743 732 739 725 1452 755 743 732 50977 2206 711 753 1449 758 51061 2226 721 754 1451 724 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2214 833 744 1598 752 822 745 824 743 1584 746 813 743 811 745 829 717 1627 744 835 752 1584 746 824 743 1584 745 813 743 811 745 803 743 100100 2213 835 752 1589 751 824 742 826 751 1576 743 816 750 803 743 805 751 1619 752 827 750 1587 753 816 750 1576 743 816 751 803 743 806 750 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2218 859 723 1594 753 822 749 820 751 1577 749 811 750 804 746 803 747 1625 753 826 756 1583 753 816 755 1573 753 806 755 800 750 799 751 100206 2213 863 729 1588 748 826 756 813 748 1581 755 804 746 808 753 797 753 1618 750 830 752 1587 749 820 751 1577 749 810 751 830 720 829 721 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2221 740 729 736 723 743 747 719 750 716 753 1445 744 722 747 719 719 747 722 1450 749 717 752 741 728 1444 755 1445 754 712 747 746 744 1454 724 741 728 1444 755 1444 755 711 779 1446 722 51387 2222 741 728 1445 754 50964 2197 740 750 1448 751 50939 2221 741 728 1443 746 50962 2198 738 752 1447 721 50965 2217 718 751 1445 754 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2221 754 723 755 722 1415 728 1403 720 717 750 709 747 706 750 698 748 758 750 728 749 1414 719 1413 720 717 750 708 748 1395 727 1384 728 101577 2217 732 745 732 745 1418 725 1406 727 710 747 712 755 698 748 701 745 761 747 731 746 1417 726 1405 727 710 746 711 745 1398 725 1387 725 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 2194 749 719 1455 753 1452 725 718 750 1480 749 720 728 742 726 717 783 792 718 777 723 1533 727 1477 752 743 757 767 722 746 723 1480 749 101228 2200 768 700 1448 750 1454 754 715 722 1482 747 722 746 723 745 723 777 772 728 767 754 1502 748 1482 726 769 731 766 723 745 755 1448 750 49842 2221 746 702 1447 782 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1224 443 1252 441 438 1266 1254 443 1254 450 438 1273 439 1273 438 1277 434 1271 1254 451 437 1274 438 7899 1250 441 1254 443 436 1267 1253 444 1253 450 438 1273 439 1272 439 1273 438 1271 1254 452 436 1275 436 7888 1253 440 1255 441 438 1267 1253 443 1254 450 438 1272 439 1274 437 1273 438 1271 1254 451 437 1274 438 7894 1255 442 1253 442 437 1266 1254 444 1253 451 437 1273 439 1274 437 1274 437 1273 1252 451 437 1275 437 7888 1253 442 1253 441 438 1268 1253 442 1255 449 439 1271 440 1276 435 1275 436 1272 1253 450 438 1271 441 7895 1254 440 1255 442 437 1267 1254 444 1253 450 438 1273 439 1275 437 1273 438 1272 1253 450 438 1275 437 7893 1254 442 1253 442 437 1267 1253 443 1254 451 437 1275 437 1273 438 1274 437 1273 1252 450 438 1272 440 7891 1253 442 1253 442 437 1266 1255 443 1254 451 437 1274 438 1274 437 1274 438 1272 1253 450 438 1274 438 7894 1254 441 1254 441 438 1267 1254 444 1253 450 438 1274 438 1274 437 1274 437 1272 1254 452 436 1273 439 7890 1254 441 1254 442 437 1266 1255 444 1253 450 438 1273 439 1274 437 1274 438 1273 1253 451 437 1274 438 7895 1253 441 1254 441 438 1267 1254 443 1254 451 437 1273 439 1276 435 1273 439 1271 1254 450 438 1274 438 7896 1254 441 1254 441 438 1267 1253 443 1254 451 437 1273 439 1274 437 1273 438 1272 1254 450 438 1274 438 7889 1253 441 1254 442 437 1267 1253 443 1254 450 438 1274 438 1274 437 1274 438 1272 1253 449 439 1273 439 7896 1254 442 1253 441 438 1268 1253 445 1252 451 437 1274 438 1274 437 1275 436 1271 1254 450 438 1274 437 7888 1254 441 1254 442 437 1268 1252 444 1253 450 438 1274 437 1275 437 1276 435 1273 1252 450 438 1274 438 7895 1254 442 1253 441 438 1267 1253 443 1254 451 437 1273 491 1221 491 1221 490 1220 1252 450 438 1274 491 7841 1253 441 1254 444 435 1267 1253 443 1254 450 491 1221 491 1219 492 1221 491 1218 1254 450 438 1273 492 7838 1253 441 1254 442 437 1267 1254 443 1254 451 437 1274 491 1221 490 1221 490 1220 1252 452 436 1274 491 7841 1254 441 1254 441 437 1268 1253 444 1253 450 438 1273 439 1272 439 1274 437 1271 1254 452 436 1275 437 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1280 415 1280 417 436 1267 1280 418 1279 424 1278 425 437 1275 490 1224 488 1223 489 1220 1279 422 1281 7000 1279 419 1276 416 489 1216 1279 419 1278 424 1279 423 492 1224 488 1222 490 1223 489 1220 1280 423 1279 6997 1278 417 1278 419 486 1215 1280 420 1277 423 1279 424 489 1226 485 1225 486 1227 435 1275 1252 450 1252 7029 1249 447 1225 469 406 1299 1226 471 1226 477 1225 479 405 1307 405 1307 405 1307 405 1304 1227 476 1226 7047 1228 469 1226 469 405 1300 1226 472 1226 475 1227 477 406 1307 405 1305 407 1306 406 1305 1226 475 1228 7052 1227 469 1226 468 405 1299 1228 471 1226 476 1226 476 407 1307 405 1306 406 1306 406 1303 1228 474 1228 7042 1227 468 1227 468 406 1299 1227 470 1227 476 1226 478 405 1309 403 1306 406 1307 405 1304 1226 476 1226 7057 1250 445 1250 444 433 1271 1251 446 1251 450 1252 452 435 1277 435 1277 435 1278 434 1275 1277 424 1278 6991 1278 417 1278 416 487 1218 1279 418 1279 423 1279 425 488 1223 489 1223 489 1223 489 1222 1279 424 1278 7004 1280 416 1279 415 489 1220 1276 417 1280 423 1279 425 488 1223 489 1223 489 1223 489 1221 1280 423 1279 6989 1280 415 1280 415 489 1217 1279 417 1280 423 1279 425 488 1223 489 1223 488 1223 489 1222 1279 423 1279 6999 1279 417 1278 416 488 1216 1280 418 1279 424 1278 424 489 1225 487 1223 489 1222 490 1222 1279 423 1279 6995 1278 416 1279 415 489 1216 1280 418 1279 423 1279 426 487 1224 488 1224 487 1223 488 1220 1281 423 1279 6999 1280 415 1280 416 488 1217 1279 417 1280 423 1279 425 488 1224 488 1226 486 1225 486 1220 1281 423 1279 6994 1279 417 1278 416 487 1218 1279 417 1280 422 1280 426 485 1226 486 1226 486 1226 485 1225 1278 423 1279 7001 1251 444 1251 442 435 1270 1252 445 1252 450 1252 452 434 1278 434 1277 435 1277 434 1276 1251 451 1251 7018 1250 445 1250 445 432 1272 1251 446 1251 451 1251 452 435 1278 434 1277 435 1277 435 1274 1253 450 1252 7031 1278 418 1277 416 436 1270 1277 419 1278 424 1278 426 435 1276 436 1275 437 1276 484 1224 1280 423 1279 6989 1279 417 1278 415 487 1219 1279 418 1279 424 1278 425 486 1225 487 1225 486 1225 487 1222 1280 423 1279 7013 1278 416 1279 415 488 1218 1279 418 1279 426 1276 425 487 1224 488 1224 487 1224 487 1222 1279 422 1280 +# +name: Speed_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1254 441 1254 441 438 1272 1249 443 1254 448 1254 450 438 1274 438 1274 437 1276 1249 449 1253 451 437 7865 1253 443 1252 441 438 1268 1252 445 1252 451 1251 451 437 1274 437 1273 438 1275 1250 448 1254 450 438 7862 1253 441 1254 443 436 1268 1252 444 1253 449 1253 450 438 1274 437 1275 436 1272 1253 449 1253 451 437 7864 1252 444 1251 442 437 1266 1254 443 1253 448 1254 451 437 1273 438 1276 435 1272 1253 449 1253 452 436 7861 1254 441 1254 441 438 1268 1252 446 1250 448 1253 450 438 1272 439 1273 438 1273 1251 450 1252 452 436 7863 1253 441 1253 441 438 1267 1253 444 1252 449 1253 450 438 1273 438 1276 435 1270 1254 448 1254 451 437 7856 1253 441 1253 440 439 1266 1254 444 1252 450 1251 451 437 1275 436 1274 437 1272 1252 449 1252 451 437 7868 1252 441 1253 441 438 1266 1253 443 1253 450 1251 451 437 1273 438 1274 437 1270 1254 448 1253 451 437 7854 1254 441 1253 440 439 1267 1252 443 1254 448 1253 450 438 1274 437 1273 438 1272 1252 447 1254 450 438 7868 1253 442 1252 441 438 1266 1253 443 1253 449 1252 450 438 1277 434 1272 439 1273 1251 448 1253 451 437 7856 1252 441 1253 443 436 1267 1252 442 1254 448 1253 451 437 1274 490 1221 490 1219 1252 449 1252 451 437 7864 1252 442 1253 441 490 1215 1252 444 1252 449 1252 450 491 1221 490 1220 491 1218 1253 449 1252 451 490 7807 1254 442 1253 441 491 1215 1251 444 1252 448 1253 451 490 1220 491 1221 490 1218 1253 448 1253 450 491 7810 1253 441 1253 441 491 1213 1254 443 1253 448 1253 451 490 1219 492 1220 491 1219 1252 448 1253 450 491 7808 1254 441 1253 442 489 1214 1253 443 1253 448 1254 450 490 1221 490 1222 489 1219 1253 449 1253 452 488 7813 1279 416 1279 416 487 1218 1278 418 1279 422 1280 425 486 1225 487 1225 486 1223 1279 423 1279 426 435 7896 1279 416 1279 416 437 1268 1279 418 1279 423 1279 425 437 1275 436 1275 436 1273 1279 423 1279 425 436 +# +name: Speed_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1275 405 1279 401 436 1243 1279 401 1273 407 440 1238 436 1244 1278 429 408 1244 440 1239 435 1244 440 8113 1272 434 1250 430 407 1246 1276 430 1254 426 411 1242 432 1247 1275 432 405 1247 437 1242 432 1247 437 8115 1280 426 1248 432 405 1248 1274 433 1251 428 409 1244 440 1239 1272 434 413 1239 435 1245 439 1239 435 8118 1277 429 1245 435 412 1240 1271 436 1248 431 406 1247 437 1242 1280 427 410 1242 432 1247 437 1242 432 8121 1274 406 1278 428 409 1244 1278 402 1272 408 439 1240 434 1245 1277 404 433 1246 438 1240 434 1245 439 8114 1281 399 1275 405 432 1247 1275 406 1278 401 436 1244 430 1249 1273 407 440 1240 434 1245 439 1239 435 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1277 429 1245 435 412 1267 1245 435 1249 431 406 1273 1249 431 406 1247 438 1241 433 1246 439 1267 407 8120 1275 430 1255 426 411 1242 1280 426 1248 432 405 1274 1248 432 405 1248 436 1243 431 1248 436 1242 432 8123 1272 407 1278 429 408 1245 1277 430 1244 436 411 1268 1254 426 411 1243 431 1274 410 1268 406 1274 410 8117 1278 401 1273 433 404 1275 1247 433 1252 429 408 1271 1251 429 408 1271 413 1239 435 1271 413 1265 409 8119 1277 402 1272 408 439 1240 1272 434 1251 430 407 1272 1250 430 407 1246 439 1241 433 1245 439 1240 434 8120 1275 431 1254 426 411 1243 1279 427 1247 433 404 1249 1273 407 440 1265 409 1244 430 1275 409 1269 405 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1317 325 1360 371 472 1214 1291 397 1320 325 527 1224 474 1223 476 1225 473 1222 476 1223 474 1221 1317 7168 1289 356 1329 358 485 1240 1318 372 1316 325 530 1222 475 1224 472 1224 474 1223 475 1225 473 1222 1316 7209 1315 369 1316 368 449 1241 1289 399 1289 409 470 1252 446 1225 474 1250 448 1223 474 1222 475 1222 1316 7181 1315 368 1316 325 491 1242 1316 372 1315 379 475 1223 474 1250 449 1224 474 1222 476 1222 473 1222 1316 7176 1315 367 1316 370 474 1214 1315 324 1338 380 498 1224 474 1222 474 1222 475 1248 449 1222 502 1194 1289 7192 1315 368 1316 324 492 1240 1317 325 1363 377 475 1223 474 1249 449 1224 472 1223 474 1223 473 1223 1316 7204 1314 369 1315 370 473 1239 1289 371 1315 381 473 1224 473 1223 475 1224 473 1226 472 1223 473 1220 1316 7178 1313 366 1319 369 473 1215 1288 397 1316 380 472 1223 474 1225 472 1224 471 1250 448 1224 447 1248 1288 7203 1314 325 1356 324 521 1215 1286 424 1288 379 475 1223 472 1223 473 1224 475 1221 448 1248 476 1245 1289 +# +name: Speed_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1314 348 1360 324 466 1269 1262 368 1345 324 530 1224 475 1222 474 1226 474 1223 474 1220 1316 378 450 8036 1315 394 1290 323 493 1240 1317 372 1316 377 475 1224 476 1222 475 1221 476 1222 473 1221 1290 324 557 8047 1314 369 1316 394 446 1242 1288 373 1314 379 448 1252 471 1226 471 1224 445 1252 472 1222 1287 431 447 8022 1315 369 1314 323 464 1274 1283 399 1315 388 385 1305 473 1223 500 1198 473 1223 474 1221 1315 380 474 8016 1313 324 1362 368 472 1215 1315 324 1364 378 474 1223 473 1223 474 1222 473 1224 473 1220 1315 378 477 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1350 359 1323 359 482 1175 1351 337 1346 345 506 1185 510 1183 1350 371 481 1187 508 1187 508 1189 506 7967 1343 362 1318 364 477 1210 1316 368 1316 376 476 1220 476 1218 1315 377 475 1220 475 1220 475 1220 475 8037 1315 366 1315 366 475 1211 1315 369 1315 377 475 1220 475 1219 1314 377 475 1220 475 1220 475 1221 474 8008 1314 367 1314 367 474 1212 1314 370 1314 378 474 1221 475 1219 1314 378 474 1221 475 1221 474 1221 474 8009 1314 366 1315 367 474 1212 1314 370 1314 378 474 1221 474 1219 1314 378 474 1221 474 1221 475 1221 474 7998 1314 366 1315 367 474 1212 1314 370 1314 378 474 1221 475 1219 1314 378 474 1221 475 1221 474 1221 475 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1347 336 1347 359 482 1175 1351 361 1322 370 481 1184 511 1185 510 1183 1349 370 481 1188 507 1189 506 7963 1319 362 1317 364 477 1210 1316 368 1316 376 476 1220 475 1220 475 1218 1315 376 476 1219 476 1220 475 8005 1315 366 1315 365 476 1211 1315 368 1316 377 475 1220 475 1219 476 1218 1315 377 475 1220 475 1220 475 7979 1315 366 1315 366 475 1211 1315 369 1315 377 475 1220 475 1220 475 1218 1315 377 475 1220 475 1220 475 7984 1314 366 1315 366 475 1211 1315 369 1315 377 475 1221 474 1221 475 1218 1315 377 475 1221 474 1221 475 7970 1314 367 1314 366 475 1212 1314 369 1315 378 474 1221 474 1221 474 1219 1314 378 474 1221 474 1221 475 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1395 358 1369 358 499 1215 1399 331 1341 384 472 1241 474 1240 475 1240 1345 383 473 1240 474 1240 474 8239 1366 387 1339 387 469 1246 1339 388 1339 387 469 1246 469 1246 469 1246 1339 388 469 1246 469 1246 469 8242 1339 387 1339 387 469 1246 1339 387 1339 387 469 1246 469 1246 469 1246 1339 387 469 1246 469 1246 468 8243 1338 388 1338 388 469 1246 1339 388 1338 388 468 1246 468 1246 469 1247 1338 388 468 1246 468 1247 468 8243 1338 388 1338 388 468 1246 1338 388 1338 388 468 1246 469 1246 468 1246 1339 387 469 1246 468 1246 468 8218 1363 363 1363 363 493 1222 1363 363 1363 363 493 1222 493 1222 492 1222 1363 363 493 1221 493 1222 493 8217 1363 363 1363 363 493 1222 1363 363 1363 363 493 1246 468 1223 492 1223 1361 375 481 1247 467 1247 467 8243 1337 388 1338 388 468 1247 1337 389 1337 388 468 1247 468 1247 468 1247 1337 389 467 1248 466 1248 466 8243 1337 389 1337 389 467 1248 1336 389 1337 390 466 1248 466 1248 466 1248 1336 390 466 1248 466 1248 466 8244 1336 390 1311 415 465 1249 1336 390 1336 391 465 1250 465 1249 465 1250 1310 415 465 1250 464 1250 439 8270 1310 416 1310 416 440 1275 1310 417 1309 417 438 1300 414 1301 413 1301 1284 442 414 1301 413 1301 413 8297 1284 442 1284 442 413 1301 1284 443 1283 443 413 1302 412 1302 412 1302 1282 443 413 1302 412 1302 412 8298 1282 443 1283 443 413 1302 1283 443 1283 444 412 1303 411 1303 411 1303 1282 444 412 1303 411 1303 411 8299 1280 445 1281 470 385 1329 1255 470 1256 471 384 1330 384 1329 385 1329 1255 471 385 1330 384 1330 384 8326 1253 472 1254 472 384 1331 1253 473 1253 499 356 1357 357 1358 356 1358 1226 499 356 1358 356 1359 355 8355 1224 502 1224 501 355 1385 1199 527 1199 527 328 1386 328 1386 328 1386 1199 527 328 1387 327 1387 327 8409 1171 554 1172 555 300 1414 1172 555 1171 555 300 1416 299 1416 298 1415 1171 556 298 1442 272 1442 272 8438 1143 583 1143 583 271 1471 1115 611 1115 664 179 1536 178 1563 122 1619 1006 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1333 408 1299 408 449 1268 1302 410 1300 417 449 1274 451 1272 1299 417 449 1276 449 1276 449 1276 449 7978 1324 384 1324 385 472 1247 1322 389 1321 395 471 1256 470 1254 1320 396 470 1280 446 1280 446 1280 446 7977 1296 411 1297 388 469 1273 1296 389 1321 421 445 1280 446 1278 1296 420 446 1280 445 1280 446 1280 445 7982 1296 412 1296 412 445 1273 1296 414 1296 421 445 1280 445 1278 1296 421 445 1281 445 1280 445 1280 445 7976 1295 412 1296 412 445 1273 1296 414 1296 421 445 1280 445 1278 1296 421 445 1281 444 1280 445 1280 445 7987 1295 412 1296 412 445 1273 1296 414 1295 421 445 1280 445 1278 1295 421 445 1280 445 1280 445 1280 445 7952 1319 412 1296 412 445 1273 1296 414 1296 422 444 1280 445 1278 1296 422 444 1281 444 1280 445 1280 445 7980 1296 412 1296 412 445 1273 1296 414 1296 421 445 1281 444 1279 1295 422 444 1281 444 1280 445 1280 445 7952 1318 413 1295 412 445 1273 1296 414 1296 421 445 1280 445 1278 1296 421 445 1280 445 1281 444 1280 445 7956 1319 412 1296 412 445 1273 1295 415 1295 422 444 1280 445 1278 1295 422 444 1280 445 1280 445 1280 445 7957 1319 412 1295 412 445 1273 1295 415 1294 422 444 1280 445 1279 1294 422 444 1280 445 1281 444 1281 444 7956 1318 413 1294 413 444 1273 1295 415 1294 422 444 1281 444 1279 1295 422 444 1281 444 1281 444 1281 444 7974 1295 413 1295 413 444 1274 1294 415 1295 422 444 1281 444 1279 1294 422 444 1281 444 1281 444 1281 444 7979 1294 413 1295 413 444 1274 1294 416 1294 423 443 1281 444 1279 1294 422 444 1281 443 1281 444 1281 444 7973 1294 414 1294 413 444 1274 1294 416 1293 423 443 1281 443 1279 1294 423 443 1282 443 1282 443 1282 443 7986 1293 414 1293 414 443 1274 1294 416 1293 423 443 1282 443 1280 1293 423 443 1282 443 1282 443 1282 443 7975 1293 415 1293 415 442 1275 1293 417 1292 424 442 1282 442 1280 1293 424 442 1283 441 1283 442 1283 442 7980 1292 415 1292 415 442 1276 1292 417 1292 425 441 1283 441 1281 1292 425 441 1283 441 1284 441 1284 441 7975 1292 416 1291 416 441 1277 1291 419 1290 426 440 1284 440 1282 1290 427 439 1285 439 1285 440 1284 440 7981 1291 442 1265 442 415 1302 1265 444 1265 451 415 1310 414 1308 1265 452 414 1310 414 1310 414 1310 415 7977 1289 442 1241 467 414 1303 1240 469 1240 476 414 1310 414 1308 1240 476 414 1311 413 1311 412 1312 414 8013 1240 467 1240 467 413 1305 1239 470 1239 477 412 1312 389 1333 1240 477 413 1311 412 1312 389 1336 388 8027 1239 468 1239 468 388 1329 1239 470 1239 478 387 1337 387 1334 1239 478 388 1336 388 1337 387 1337 388 8032 1239 469 1238 494 362 1331 1237 496 1213 504 362 1362 386 1337 1212 504 362 1362 362 1363 362 1363 361 8054 1212 495 1212 495 361 1356 1212 497 1212 505 360 1364 360 1362 1211 505 360 1364 360 1364 360 1364 360 8060 1212 522 1185 522 334 1384 1185 524 1185 532 333 1391 333 1389 1184 532 333 1391 333 1391 333 1392 333 8089 1184 524 1183 550 306 1411 1158 552 1157 559 305 1420 304 1418 1156 586 278 1446 278 1446 278 1446 278 8143 1130 603 1104 605 249 1493 1077 712 997 4114 1050 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1351 383 1324 383 474 1217 1352 386 1324 392 448 1249 477 1249 477 1246 1353 390 475 1249 475 1250 474 7942 1323 386 1322 386 471 1247 1321 388 1322 396 470 1254 471 1255 470 1253 1320 396 471 1254 471 1255 470 7933 1320 387 1321 387 470 1248 1320 389 1321 396 471 1255 470 1255 470 1253 1321 397 469 1255 470 1255 470 7943 1319 388 1320 388 469 1248 1320 390 1319 397 469 1255 469 1256 468 1254 1319 397 469 1255 469 1256 468 7931 1318 388 1319 388 469 1249 1318 391 1318 398 468 1257 468 1256 468 1255 1318 398 468 1257 468 1256 468 7949 1318 389 1318 390 467 1250 1317 392 1317 422 444 1281 443 1281 443 1279 1294 422 444 1281 443 1281 443 7956 1294 413 1294 413 444 1274 1293 415 1294 423 443 1281 443 1281 443 1279 1294 423 443 1281 443 1281 443 7966 1293 414 1293 414 443 1274 1293 416 1293 423 443 1282 442 1282 443 1280 1293 423 443 1282 442 1282 442 7956 1292 414 1293 414 443 1275 1292 416 1293 424 442 1282 442 1282 442 1280 1293 424 442 1282 442 1282 442 7967 1292 415 1292 415 442 1276 1291 417 1292 424 442 1283 441 1283 441 1281 1292 425 441 1283 441 1283 441 7963 1291 416 1291 416 440 1277 1290 418 1291 425 441 1284 440 1284 440 1282 1290 427 439 1284 440 1285 439 7969 1289 417 1290 418 438 1302 1265 444 1265 451 415 1309 415 1310 414 1308 1264 451 415 1310 414 1310 414 7983 1264 443 1263 443 414 1303 1264 445 1264 452 414 1310 413 1311 413 1309 1239 477 413 1311 413 1311 413 7995 1262 444 1239 468 412 1306 1238 470 1239 478 412 1312 411 1312 412 1310 1237 479 387 1362 362 1362 387 8010 1212 494 1213 495 361 1355 1213 497 1212 504 361 1363 361 1363 361 1361 1211 504 361 1363 360 1364 360 8054 1210 496 1211 521 334 1383 1185 524 1184 532 333 1390 334 1391 333 1389 1183 532 333 1392 332 1391 332 8089 1157 524 1183 549 306 1411 1157 552 1157 585 279 1445 279 1445 278 1444 1130 586 278 1472 251 1447 277 8155 1103 630 1076 657 186 1530 1050 660 1048 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1275 398 1297 369 561 1107 1301 370 1298 397 533 1108 563 1107 563 1106 1274 421 534 1110 560 1111 558 7970 1268 424 1243 425 530 1140 1242 427 1242 428 528 1141 528 1142 527 1142 1241 428 528 1142 528 1142 527 8000 1240 428 1241 428 528 1142 1241 428 1241 428 528 1142 528 1142 527 1142 1241 428 528 1142 527 1142 528 8000 1240 429 1240 429 527 1142 1240 429 1240 429 528 1142 527 1143 527 1143 1240 430 526 1143 526 1144 525 8029 1211 461 1208 485 446 1225 1156 565 1103 620 229 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1283 419 1288 442 417 1258 1288 416 1281 448 411 1264 443 1258 439 1264 443 1258 439 1263 444 1259 1286 7832 1283 420 1287 443 416 1260 1286 417 1280 450 409 1265 442 1260 437 1265 443 1259 438 1265 442 1260 1286 7834 1281 421 1286 444 415 1260 1286 444 1263 440 408 1266 441 1261 436 1266 441 1261 436 1266 441 1261 1285 7833 1282 421 1287 443 416 1259 1287 443 1254 449 410 1265 442 1259 438 1264 443 1259 438 1264 443 1259 1287 7833 1282 420 1287 442 417 1259 1287 442 1255 448 411 1264 443 1258 439 1263 444 1257 440 1262 435 1268 1288 7831 1284 418 1289 441 407 1268 1288 441 1256 446 413 1262 435 1267 440 1261 436 1266 441 1261 436 1266 1290 7829 1286 416 1281 449 410 1265 1281 422 1285 444 415 1259 438 1264 444 1258 439 1263 444 1258 439 1263 1283 7836 1290 413 1284 445 414 1261 1285 444 1253 450 409 1266 442 1260 437 1264 444 1259 438 1263 445 1258 1288 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1308 413 1310 409 426 1277 1313 410 1302 425 431 1279 433 1304 408 1304 408 1277 435 1277 435 1275 1304 7045 1312 408 1304 415 431 1272 1307 415 1308 419 427 1283 429 1308 404 1307 405 1281 431 1280 432 1278 1311 7015 1311 409 1303 415 431 1272 1307 414 1309 419 427 1283 430 1281 431 1306 406 1279 433 1279 433 1276 1303 7043 1304 417 1306 412 434 1269 1310 412 1311 417 429 1281 431 1279 433 1304 408 1277 435 1276 426 1283 1306 7018 1308 412 1311 408 427 1275 1304 417 1306 422 434 1275 427 1284 428 1308 404 1281 431 1280 432 1276 1314 7030 1307 413 1310 409 426 1276 1303 418 1305 423 433 1276 426 1284 428 1309 403 1281 431 1280 432 1277 1313 7009 1307 413 1310 409 426 1275 1304 418 1305 422 434 1275 427 1283 429 1307 405 1279 433 1278 434 1274 1305 7037 1310 410 1302 416 430 1272 1307 414 1309 418 428 1281 431 1305 407 1277 435 1275 427 1283 429 1279 1310 7010 1306 414 1309 409 426 1300 1279 417 1306 421 425 1309 403 1306 407 1278 434 1275 427 1284 428 1280 1309 7031 1306 414 1309 409 426 1300 1279 417 1306 421 425 1308 404 1306 406 1277 435 1274 428 1282 430 1277 1312 7049 1308 411 1312 406 429 1297 1282 413 1310 417 429 1304 408 1275 427 1282 430 1279 433 1276 436 1272 1307 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1318 409 1321 412 453 1248 1326 412 1318 412 442 1256 443 1255 1319 404 450 1249 450 1249 450 1249 450 8000 1319 402 1317 406 448 1249 1315 420 1371 353 501 1196 451 1247 1317 406 448 1249 450 1247 441 1254 445 8002 1306 408 1311 403 441 1254 1309 415 1315 401 443 1253 446 1250 1314 402 442 1254 445 1251 448 1248 440 7999 1310 405 1304 411 443 1252 1312 414 1305 412 442 1254 445 1252 1312 407 447 1249 450 1247 441 1255 444 7998 1310 406 1313 404 450 1246 1307 418 1363 354 449 1247 441 1255 1309 407 447 1249 450 1246 442 1254 445 7998 1311 403 1306 409 445 1250 1303 419 1311 404 450 1244 444 1251 1313 402 442 1253 446 1250 449 1248 440 8002 1306 436 1283 432 422 1245 1308 444 1285 430 414 1254 445 1252 1312 432 422 1245 443 1253 446 1250 449 7998 1311 407 1312 403 451 1242 1311 415 1304 411 443 1250 449 1247 1307 411 443 1249 450 1245 443 1252 447 7998 1311 408 1301 413 441 1279 1285 415 1304 411 443 1276 423 1273 1280 410 444 1275 424 1272 416 1280 419 7998 1311 408 1301 414 440 1251 1313 414 1305 409 445 1248 451 1245 1308 411 443 1249 450 1246 442 1254 445 8003 1306 414 1305 409 445 1248 1306 421 1309 406 448 1246 442 1253 1311 407 447 1246 442 1253 446 1250 449 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1323 406 1313 415 449 1249 1314 420 1309 415 449 1249 450 1248 450 1248 1316 412 442 1258 451 1248 451 7999 1319 408 1311 415 449 1249 1314 420 1320 405 449 1249 450 1249 450 1250 1313 413 451 1248 450 1249 449 8007 1321 406 1313 414 450 1248 1315 419 1311 415 449 1249 450 1249 449 1250 1314 413 441 1257 452 1248 450 8006 1312 415 1314 412 442 1257 1317 417 1312 411 443 1255 444 1256 443 1258 1316 410 444 1255 444 1256 442 8014 1314 411 1318 408 446 1252 1312 421 1319 405 449 1249 450 1248 450 1249 1314 411 443 1256 442 1256 442 8014 1314 410 1319 405 449 1248 1315 416 1313 408 446 1251 448 1250 449 1250 1314 410 444 1253 445 1252 446 8006 1312 412 1317 407 447 1251 1313 420 1309 415 449 1248 440 1257 441 1256 1318 406 448 1251 447 1251 447 8005 1313 412 1317 407 447 1250 1313 418 1311 412 442 1255 443 1254 444 1254 1320 405 449 1249 450 1249 449 8003 1315 409 1310 413 441 1256 1318 414 1315 407 447 1251 447 1250 448 1250 1313 410 444 1255 443 1254 444 8008 1320 406 1313 409 445 1253 1310 422 1318 405 449 1249 449 1249 449 1249 1314 412 442 1256 442 1257 452 8003 1315 413 1316 407 447 1250 1313 421 1319 406 448 1249 449 1250 448 1279 1295 406 448 1246 452 1247 451 8002 1326 407 1322 415 449 1251 1323 424 1326 414 450 1250 448 1256 453 1249 1325 409 445 1253 445 1255 454 8005 1323 413 1327 406 448 1254 1330 419 1321 412 452 1248 450 1250 448 1254 1330 404 450 1250 448 1254 455 8002 1316 408 1311 412 452 1244 1319 413 1316 406 448 1248 450 1248 450 1249 1314 407 447 1249 449 1249 449 8005 1313 411 1308 411 443 1252 1311 417 1312 406 448 1249 449 1248 450 1248 1315 434 420 1251 447 1252 446 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 40 00 00 00 +# +name: Speed_up +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 01 00 00 00 +# +name: Mode +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 09 00 00 00 +# +name: Timer +type: parsed +protocol: NEC +address: 30 00 00 00 +command: 86 00 00 00 +# +name: Mode +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 01 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 19 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 1C 00 00 00 +# +name: Speed_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 19 00 00 00 +# +name: Timer +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 0C 00 00 00 +# +name: Mode +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 07 00 00 00 From 34b5535e005119520e8d7bcecc6603e4952fe22f Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:44:24 +0100 Subject: [PATCH 204/370] Update projectors.ir New additions --- .../resources/infrared/assets/projectors.ir | 82 ++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 673120e61..4842f470a 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 2nd May, 2023 -# Last Checked 17th May, 2023 +# Last Updated 16th Jun, 2023 +# Last Checked 16th Jun, 2023 # # ON name: Power @@ -910,3 +910,81 @@ type: parsed protocol: NEC address: 02 00 00 00 command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 04 B1 00 00 +command: 58 A7 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9107 4376 681 1573 681 472 655 472 654 474 652 475 652 476 651 476 652 476 651 476 651 1604 651 1604 651 1604 651 1603 652 1604 651 1604 651 1604 651 1604 650 476 651 477 650 477 650 476 651 477 651 1604 650 476 651 477 650 1604 651 1604 650 1604 651 1604 650 1604 651 477 650 1604 650 39498 9079 2178 651 +# +name: Power +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 12 ED 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 44 BB 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 46 B9 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 8B CA 00 00 +command: 11 EE 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 81 03 00 00 +command: F0 0F 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 40 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 01 FE 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 10 EF 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 0C F3 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 BD 00 00 +command: 6A 95 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 11 00 00 00 From a414318605adeca6662141c71105c4047eb10b29 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:44:52 +0100 Subject: [PATCH 205/370] Update tv.ir New additions --- assets/resources/infrared/assets/tv.ir | 268 +++++++++++++++++++++---- 1 file changed, 229 insertions(+), 39 deletions(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index fa1e0c67a..2d2d98071 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 9 Jun, 2023 -# Last Checked 17th May, 2023 +# Last Updated 16th Jun, 2023 +# Last Checked 16th Jun, 2023 # name: Power type: parsed @@ -63,12 +63,6 @@ protocol: NEC address: 40 00 00 00 command: 13 00 00 00 # -name: Vol_dn -type: parsed -protocol: NEC -address: 40 00 00 00 -command: 12 00 00 00 -# name: Mute type: parsed protocol: NEC @@ -453,6 +447,12 @@ type: parsed protocol: Samsung32 address: 0E 00 00 00 command: 15 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 12 00 00 00 # name: Ch_next type: parsed @@ -1143,13 +1143,17 @@ type: parsed protocol: RC6 address: 00 00 00 00 command: 0D 00 00 00 +# # Samsung Standby +# name: Power type: parsed protocol: Samsung32 address: 07 00 00 00 command: E0 00 00 00 +# # Samsung Power Off +# name: Power type: parsed protocol: Samsung32 @@ -2049,39 +2053,225 @@ address: 3E 00 00 00 command: 0D 00 00 00 # # TCL LED49D2930 -# +# name: Power -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 3994 3969 552 1945 551 1945 552 1945 551 1945 552 946 551 947 550 1947 548 951 546 1953 542 979 518 1979 517 981 492 1006 491 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 2005 492 1006 492 2005 492 1006 492 2006 491 9051 3963 3999 491 2006 491 2006 491 2006 491 2006 491 1007 491 1007 491 2006 491 1007 491 2006 491 1007 491 2006 491 1007 491 1007 491 1007 491 1006 492 1007 491 2006 491 2006 491 1007 491 2006 491 1007 491 2006 491 1007 491 2006 491 9052 3962 4000 491 2006 491 2006 491 2006 491 2006 491 1007 491 1007 491 2006 491 1007 491 2006 491 1007 491 2006 491 1007 491 1007 491 1007 491 1007 491 1007 491 2006 491 2007 490 1007 491 2006 491 1007 491 2006 491 1007 491 2006 490 +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 54 00 00 00 +# +name: Mute +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 +# +name: Ch_next +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +# +name: Vol_up +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: F4 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 # name: Vol_up -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 4055 3941 605 1891 551 1947 550 1946 551 1946 551 947 551 947 550 1947 549 949 548 1951 545 1977 519 1978 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 981 517 1979 518 980 518 980 518 980 518 980 518 9026 3989 3975 517 1980 517 1980 517 1979 518 1980 517 980 518 980 518 1979 518 981 517 1980 517 1980 517 1980 517 1980 517 980 518 981 517 981 517 981 517 1980 517 1980 517 981 517 1980 517 981 517 981 517 981 517 981 517 9027 3988 3975 517 1980 517 1980 517 1980 517 1980 516 981 517 982 516 1981 516 982 516 1981 516 1981 516 1981 516 1981 516 982 516 982 515 982 516 982 516 1982 515 1981 516 982 515 1981 516 983 515 982 516 982 515 982 516 -# -name: Vol_dn -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 4027 3970 551 1946 550 1946 551 1946 551 1946 551 946 551 947 550 1947 549 949 547 1951 545 1978 518 1979 492 1006 492 1007 491 1006 492 1006 492 1006 492 2006 491 2006 491 1006 492 2006 491 1007 491 1007 491 1006 492 2006 491 9053 3964 4000 491 2006 491 2006 491 2006 491 2006 491 1007 491 1007 491 2006 491 1007 491 2006 491 2006 491 2006 491 1007 491 1007 491 1007 491 1007 491 1007 491 2006 491 2006 491 1007 491 2006 491 1007 491 1007 491 1007 491 2006 491 9053 3963 4001 491 2006 491 2006 491 2007 490 2007 490 1007 491 1007 491 2007 490 1007 491 2006 491 2006 491 2007 490 1008 490 1007 491 1008 490 1007 491 1008 490 2007 490 2007 490 1008 490 2007 490 1008 490 1008 489 1008 490 2007 490 -# -name: Ch_next -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 4021 3941 551 1946 550 1946 551 1946 551 1945 552 946 551 947 550 1947 549 950 547 1952 544 1977 519 979 519 1979 518 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 980 518 9026 3990 3974 517 1980 517 1980 517 1980 517 1980 517 981 517 981 517 1980 517 981 517 1979 518 1979 518 980 517 1980 517 981 517 981 517 981 517 980 518 1980 517 1980 517 980 517 1980 517 981 516 981 517 1980 517 981 516 -# +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 18 00 00 00 +# name: Ch_prev -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 4022 3941 551 1946 551 1946 577 1919 578 1919 578 920 552 946 551 1946 550 947 550 1949 547 1952 544 978 520 979 519 980 518 980 518 980 518 980 518 1979 518 1979 518 980 518 1979 518 980 518 980 518 1979 518 1980 517 9027 3990 3974 517 1980 517 1980 517 1980 517 1980 517 981 517 981 517 1980 517 981 517 1980 517 1980 517 981 517 981 517 981 517 981 517 981 517 981 517 1980 517 1980 517 981 517 1980 517 981 517 982 491 2006 516 1981 516 -# +type: parsed +protocol: NEC +address: 08 00 00 00 +command: DD 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 04 F4 00 00 +command: 08 F7 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 04 F4 00 00 +command: 02 FD 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 04 F4 00 00 +command: 03 FC 00 00 +# name: Mute -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 3995 3968 552 1944 552 1946 550 1946 550 1946 551 947 550 948 549 1947 549 1949 547 1952 544 1978 518 1979 492 2005 492 1006 492 1006 492 1006 492 1006 492 2005 492 2005 492 1006 492 1006 492 1006 492 1006 492 1006 492 1006 492 9051 3963 4000 491 2005 492 2006 491 2006 491 2006 491 1007 491 1007 491 2006 516 1981 516 1980 491 2006 517 1980 517 1980 517 981 517 981 517 981 517 981 517 1980 517 1981 516 981 517 981 517 981 517 981 517 981 491 1007 491 9052 3988 3975 491 2006 491 2006 491 2006 514 1983 490 1007 491 1007 491 2006 491 2006 491 2006 491 2006 491 2006 491 2006 512 986 491 1007 491 1007 491 1007 491 2006 491 2006 491 1007 491 1007 490 1007 491 1007 491 1008 490 1007 491 +type: parsed +protocol: NECext +address: 04 F4 00 00 +command: 09 F6 00 00 +# +name: Ch_prev +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 13 00 00 00 +# +name: Vol_up +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 12 00 00 00 +# +name: Vol_dn +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 13 00 00 00 +# +name: Mute +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 14 00 00 00 +# +name: Ch_next +type: parsed +protocol: SIRC +address: 0D 00 00 00 +command: 10 00 00 00 +# +name: Ch_prev +type: parsed +protocol: SIRC +address: 0D 00 00 00 +command: 11 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 11 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 22 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 12 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 10 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 7F 00 00 +command: 1E E1 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 7F 00 00 +command: 5C A3 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 02 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 09 00 00 00 +# +name: Ch_next +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 03 00 00 00 +# +name: Ch_prev +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 0F F0 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 5A A5 00 00 +# +name: Ch_next +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 0C F3 00 00 +# +name: Ch_prev +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 19 E6 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 40 00 00 00 +command: 10 00 00 00 +# +name: Ch_next +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 10 00 00 00 +# +name: Ch_prev +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 12 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: A0 00 00 00 +command: 1C 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: A0 00 00 00 +command: 5F 00 00 00 From cee49d59b485dfb9847a9a510875fa6f272abd6f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 16 Jun 2023 21:53:22 +0100 Subject: [PATCH 206/370] Fix zerotracker and tamap1 bus issues (#290 #294) By @xMasterX --- .../external/music_tracker/tracker_engine/speaker_hal.c | 6 ++++-- applications/external/tama_p1/tama_p1.c | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/applications/external/music_tracker/tracker_engine/speaker_hal.c b/applications/external/music_tracker/tracker_engine/speaker_hal.c index 0a506a424..112439b1e 100644 --- a/applications/external/music_tracker/tracker_engine/speaker_hal.c +++ b/applications/external/music_tracker/tracker_engine/speaker_hal.c @@ -71,6 +71,8 @@ void tracker_interrupt_init(float freq, FuriHalInterruptISR isr, void* context) tracker_isr = isr; tracker_isr_context = context; + furi_hal_bus_enable(FuriHalBusTIM2); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, tracker_interrupt_cb, NULL); LL_TIM_InitTypeDef TIM_InitStruct = {0}; @@ -88,7 +90,7 @@ void tracker_interrupt_init(float freq, FuriHalInterruptISR isr, void* context) void tracker_interrupt_deinit() { FURI_CRITICAL_ENTER(); - LL_TIM_DeInit(TIM2); + furi_hal_bus_disable(FuriHalBusTIM2); FURI_CRITICAL_EXIT(); furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); @@ -104,4 +106,4 @@ void tracker_debug_set(bool value) { void tracker_debug_deinit() { furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} +} \ No newline at end of file diff --git a/applications/external/tama_p1/tama_p1.c b/applications/external/tama_p1/tama_p1.c index 1d7939a01..93db6775d 100644 --- a/applications/external/tama_p1/tama_p1.c +++ b/applications/external/tama_p1/tama_p1.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -750,6 +751,9 @@ static void tama_p1_init(TamaApp* const ctx) { if(ctx->rom != NULL) { // Init TIM2 // 64KHz + + furi_hal_bus_enable(FuriHalBusTIM2); + LL_TIM_InitTypeDef tim_init = { .Prescaler = 999, .CounterMode = LL_TIM_COUNTERMODE_UP, @@ -782,6 +786,7 @@ static void tama_p1_deinit(TamaApp* const ctx) { if(ctx->rom != NULL) { tamalib_release(); furi_thread_free(ctx->thread); + furi_hal_bus_disable(FuriHalBusTIM2); free(ctx->rom); } } From a039e0216f7f28c0418fb47a03a2482c4086ccde Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 01:16:36 +0100 Subject: [PATCH 207/370] Update and clean apps --- .../external/barcode_gen/application.fam | 1 + .../external/barcode_gen/barcode_app.c | 12 +- .../external/barcode_gen/barcode_app.h | 15 +- .../external/barcode_gen/barcode_utils.c | 15 +- .../external/barcode_gen/barcode_utils.h | 3 +- .../external/barcode_gen/barcode_validator.c | 63 ++++++- .../external/barcode_gen/barcode_validator.h | 1 + .../external/barcode_gen/views/barcode_view.c | 65 +++++++ .../external/barcode_gen/views/create_view.c | 3 +- .../external/barcode_gen/views/message_view.c | 8 +- applications/external/geiger/flipper_geiger.c | 74 ++++---- .../external/ir_remote/infrared_remote_app.c | 172 ++++++++---------- .../nrf24scan/Distr/nrf24scan/addresses.txt | 11 -- .../nrf24scan/Distr/nrf24scan/sniff.txt | 5 - applications/external/pong/flipper_pong.c | 30 ++- .../external/wiiec/_image_tool/README | 30 --- .../external/wiiec/_image_tool/_convert.sh | 79 -------- .../wiiec/_image_tool/_convert_images.c | 137 -------------- .../wiiec/_image_tool/_convert_images.h | 53 ------ applications/external/wiiec/info.sh | 11 -- .../apps_data}/nrf24scan/addr-CO2mini.txt | 0 .../apps_data}/nrf24scan/addr-WCO1.txt | 0 22 files changed, 286 insertions(+), 502 deletions(-) delete mode 100644 applications/external/nrf24scan/Distr/nrf24scan/addresses.txt delete mode 100644 applications/external/nrf24scan/Distr/nrf24scan/sniff.txt delete mode 100644 applications/external/wiiec/_image_tool/README delete mode 100755 applications/external/wiiec/_image_tool/_convert.sh delete mode 100644 applications/external/wiiec/_image_tool/_convert_images.c delete mode 100644 applications/external/wiiec/_image_tool/_convert_images.h delete mode 100755 applications/external/wiiec/info.sh rename {applications/external/nrf24scan/Distr => assets/resources/apps_data}/nrf24scan/addr-CO2mini.txt (100%) rename {applications/external/nrf24scan/Distr => assets/resources/apps_data}/nrf24scan/addr-WCO1.txt (100%) diff --git a/applications/external/barcode_gen/application.fam b/applications/external/barcode_gen/application.fam index 72c8aa114..045db6cc7 100644 --- a/applications/external/barcode_gen/application.fam +++ b/applications/external/barcode_gen/application.fam @@ -8,4 +8,5 @@ App( fap_category="Misc", fap_icon="images/barcode_10.png", fap_icon_assets="images", + fap_icon_assets_symbol="barcode_app", ) diff --git a/applications/external/barcode_gen/barcode_app.c b/applications/external/barcode_gen/barcode_app.c index 581c92fda..99e5769d2 100644 --- a/applications/external/barcode_gen/barcode_app.c +++ b/applications/external/barcode_gen/barcode_app.c @@ -13,7 +13,7 @@ static bool select_file(const char* folder, FuriString* file_path) { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, BARCODE_EXTENSION, &I_barcode_10); + dialog_file_browser_set_basic_options(&browser_options, "", &I_barcode_10); browser_options.base_path = DEFAULT_USER_BARCODES; furi_string_set(file_path, folder); @@ -65,7 +65,7 @@ bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool if(file_path == NULL || file_name == NULL) { return false; } - uint slash_index = furi_string_search_rchar(file_path, '/', 0); + uint32_t slash_index = furi_string_search_rchar(file_path, '/', 0); if(slash_index == FURI_STRING_FAILURE || slash_index >= (furi_string_size(file_path) - 1)) { return false; } @@ -73,7 +73,7 @@ bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool furi_string_set(file_name, file_path); furi_string_right(file_name, slash_index + 1); if(remove_extension) { - uint ext_index = furi_string_search_rchar(file_name, '.', 0); + uint32_t ext_index = furi_string_search_rchar(file_name, '.', 0); if(ext_index != FURI_STRING_FAILURE && ext_index < (furi_string_size(file_path))) { furi_string_left(file_name, ext_index); } @@ -239,6 +239,11 @@ void submenu_callback(void* context, uint32_t index) { } } +uint32_t create_view_callback(void* context) { + UNUSED(context); + return CreateBarcodeView; +} + uint32_t main_menu_callback(void* context) { UNUSED(context); return MainMenuView; @@ -305,6 +310,7 @@ int32_t barcode_main(void* p) { * Creating Text Input View ******************************/ app->text_input = text_input_alloc(); + view_set_previous_callback(text_input_get_view(app->text_input), create_view_callback); view_dispatcher_add_view( app->view_dispatcher, TextInputView, text_input_get_view(app->text_input)); diff --git a/applications/external/barcode_gen/barcode_app.h b/applications/external/barcode_gen/barcode_app.h index 31c805a69..bbc787f1e 100644 --- a/applications/external/barcode_gen/barcode_app.h +++ b/applications/external/barcode_gen/barcode_app.h @@ -15,7 +15,7 @@ #include "barcode_utils.h" #define TAG "BARCODE" -#define VERSION "1.0" +#define VERSION "1.1" #define FILE_VERSION "1" #define TEXT_BUFFER_SIZE 128 @@ -23,10 +23,11 @@ #define BARCODE_HEIGHT 50 #define BARCODE_Y_START 3 -#define APPS_DATA EXT_PATH("apps_data") - //the folder where the encodings are located -#define BARCODE_DATA_FILE_DIR_PATH APPS_DATA "/barcode_data" +#define BARCODE_DATA_FILE_DIR_PATH EXT_PATH("apps_data/barcode_data") + +//the folder where the codabar encoding table is located +#define CODABAR_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/codabar_encodings.txt" //the folder where the code 39 encoding table is located #define CODE39_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/code39_encodings.txt" @@ -35,11 +36,11 @@ #define CODE128_DICT_FILE_PATH BARCODE_DATA_FILE_DIR_PATH "/code128_encodings.txt" //the folder where the user stores their barcodes -#define DEFAULT_USER_BARCODES EXT_PATH("barcodes") +#define DEFAULT_USER_BARCODES EXT_PATH("apps_data/barcodes") //The extension barcode files use -#define BARCODE_EXTENSION ".barcode" -#define BARCODE_EXTENSION_LENGTH 8 +#define BARCODE_EXTENSION ".txt" +#define BARCODE_EXTENSION_LENGTH 4 #include "views/barcode_view.h" #include "views/create_view.h" diff --git a/applications/external/barcode_gen/barcode_utils.c b/applications/external/barcode_gen/barcode_utils.c index 502014d85..d7bb12689 100644 --- a/applications/external/barcode_gen/barcode_utils.c +++ b/applications/external/barcode_gen/barcode_utils.c @@ -43,6 +43,14 @@ void init_types() { code_128->start_pos = 0; barcode_type_objs[CODE128] = code_128; + BarcodeTypeObj* codabar = malloc(sizeof(BarcodeTypeObj)); + codabar->name = "Codabar"; + codabar->type = CODABAR; + codabar->min_digits = 1; + codabar->max_digits = -1; + codabar->start_pos = 0; + barcode_type_objs[CODABAR] = codabar; + BarcodeTypeObj* unknown = malloc(sizeof(BarcodeTypeObj)); unknown->name = "Unknown"; unknown->type = UNKNOWN; @@ -74,6 +82,9 @@ BarcodeTypeObj* get_type(FuriString* type_string) { if(furi_string_cmp_str(type_string, "CODE-128") == 0) { return barcode_type_objs[CODE128]; } + if(furi_string_cmp_str(type_string, "Codabar") == 0) { + return barcode_type_objs[CODABAR]; + } return barcode_type_objs[UNKNOWN]; } @@ -98,7 +109,7 @@ const char* get_error_code_name(ErrorCode error_code) { return "OK"; default: return "Unknown Code"; - } + }; } const char* get_error_code_message(ErrorCode error_code) { @@ -121,5 +132,5 @@ const char* get_error_code_message(ErrorCode error_code) { return "OK"; default: return "Could not read barcode data"; - } + }; } \ No newline at end of file diff --git a/applications/external/barcode_gen/barcode_utils.h b/applications/external/barcode_gen/barcode_utils.h index 212923a89..7bc565cee 100644 --- a/applications/external/barcode_gen/barcode_utils.h +++ b/applications/external/barcode_gen/barcode_utils.h @@ -3,7 +3,7 @@ #include #include -#define NUMBER_OF_BARCODE_TYPES 6 +#define NUMBER_OF_BARCODE_TYPES 7 typedef enum { WrongNumberOfDigits, //There is too many or too few digits in the barcode @@ -22,6 +22,7 @@ typedef enum { EAN13, CODE39, CODE128, + CODABAR, UNKNOWN } BarcodeType; diff --git a/applications/external/barcode_gen/barcode_validator.c b/applications/external/barcode_gen/barcode_validator.c index 6cb3eec4c..51e71ae2e 100644 --- a/applications/external/barcode_gen/barcode_validator.c +++ b/applications/external/barcode_gen/barcode_validator.c @@ -13,6 +13,9 @@ void barcode_loader(BarcodeData* barcode_data) { case CODE128: code_128_loader(barcode_data); break; + case CODABAR: + codabar_loader(barcode_data); + break; case UNKNOWN: barcode_data->reason = UnsupportedType; barcode_data->valid = false; @@ -36,6 +39,7 @@ int calculate_check_digit(BarcodeData* barcode_data) { break; case CODE39: case CODE128: + case CODABAR: case UNKNOWN: default: break; @@ -130,7 +134,6 @@ void code_39_loader(BarcodeData* barcode_data) { int barcode_length = furi_string_size(barcode_data->raw_data); int min_digits = barcode_data->type_obj->min_digits; - // int max_digit = barcode_data->type_obj->max_digits; //check the length of the barcode, must contain atleast a character, //this can have as many characters as it wants, it might not fit on the screen @@ -215,7 +218,6 @@ void code_128_loader(BarcodeData* barcode_data) { const char* stop_code_bits = "1100011101011"; int min_digits = barcode_data->type_obj->min_digits; - // int max_digit = barcode_data->type_obj->max_digits; /** * A sum of all of the characters values @@ -342,3 +344,60 @@ void code_128_loader(BarcodeData* barcode_data) { furi_string_cat(barcode_data->correct_data, barcode_bits); furi_string_free(barcode_bits); } + +void codabar_loader(BarcodeData* barcode_data) { + int barcode_length = furi_string_size(barcode_data->raw_data); + + int min_digits = barcode_data->type_obj->min_digits; + + //check the length of the barcode, must contain atleast a character, + //this can have as many characters as it wants, it might not fit on the screen + if(barcode_length < min_digits) { + barcode_data->reason = WrongNumberOfDigits; + barcode_data->valid = false; + return; + } + + FuriString* barcode_bits = furi_string_alloc(); + + barcode_length = furi_string_size(barcode_data->raw_data); + + //Open Storage + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* ff = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_existing(ff, CODABAR_DICT_FILE_PATH)) { + FURI_LOG_E(TAG, "Could not open file %s", CODABAR_DICT_FILE_PATH); + barcode_data->reason = MissingEncodingTable; + barcode_data->valid = false; + } else { + FuriString* char_bits = furi_string_alloc(); + for(int i = 0; i < barcode_length; i++) { + char barcode_char = toupper(furi_string_get_char(barcode_data->raw_data, i)); + + //convert a char into a string so it used in flipper_format_read_string + char current_character[2]; + snprintf(current_character, 2, "%c", barcode_char); + + if(!flipper_format_read_string(ff, current_character, char_bits)) { + FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); + barcode_data->reason = InvalidCharacters; + barcode_data->valid = false; + break; + } else { + FURI_LOG_I( + TAG, "\"%c\" string: %s", barcode_char, furi_string_get_cstr(char_bits)); + furi_string_cat(barcode_bits, char_bits); + } + flipper_format_rewind(ff); + } + furi_string_free(char_bits); + } + + //Close Storage + flipper_format_free(ff); + furi_record_close(RECORD_STORAGE); + + furi_string_cat(barcode_data->correct_data, barcode_bits); + furi_string_free(barcode_bits); +} \ No newline at end of file diff --git a/applications/external/barcode_gen/barcode_validator.h b/applications/external/barcode_gen/barcode_validator.h index 962d14729..739c80ddf 100644 --- a/applications/external/barcode_gen/barcode_validator.h +++ b/applications/external/barcode_gen/barcode_validator.h @@ -10,4 +10,5 @@ void ean_8_loader(BarcodeData* barcode_data); void ean_13_loader(BarcodeData* barcode_data); void code_39_loader(BarcodeData* barcode_data); void code_128_loader(BarcodeData* barcode_data); +void codabar_loader(BarcodeData* barcode_data); void barcode_loader(BarcodeData* barcode_data); \ No newline at end of file diff --git a/applications/external/barcode_gen/views/barcode_view.c b/applications/external/barcode_gen/views/barcode_view.c index afd727b63..994053801 100644 --- a/applications/external/barcode_gen/views/barcode_view.c +++ b/applications/external/barcode_gen/views/barcode_view.c @@ -322,6 +322,68 @@ static void draw_code_128(Canvas* canvas, BarcodeData* barcode_data) { canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); } +static void draw_codabar(Canvas* canvas, BarcodeData* barcode_data) { + FuriString* raw_data = barcode_data->raw_data; + FuriString* barcode_digits = barcode_data->correct_data; + //BarcodeTypeObj* type_obj = barcode_data->type_obj; + + int barcode_length = furi_string_size(barcode_digits); + int total_pixels = 0; + + for(int i = 0; i < barcode_length; i++) { + //1 for wide, 0 for narrow + char wide_or_narrow = furi_string_get_char(barcode_digits, i); + int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit + + if(wn_digit == 1) { + total_pixels += 3; + } else { + total_pixels += 1; + } + if((i + 1) % 7 == 0) { + total_pixels += 1; + } + } + + int x = (128 - total_pixels) / 2; + int y = BARCODE_Y_START; + int width = 1; + int height = BARCODE_HEIGHT; + bool filled_in = true; + + //set the canvas color to black to print the digit + canvas_set_color(canvas, ColorBlack); + // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); + canvas_draw_str_aligned( + canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); + + for(int i = 0; i < barcode_length; i++) { + //1 for wide, 0 for narrow + char wide_or_narrow = furi_string_get_char(barcode_digits, i); + int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit + + if(filled_in) { + if(wn_digit == 1) { + x = draw_bits(canvas, "111", x, y, width, height); + } else { + x = draw_bits(canvas, "1", x, y, width, height); + } + filled_in = false; + } else { + if(wn_digit == 1) { + x = draw_bits(canvas, "000", x, y, width, height); + } else { + x = draw_bits(canvas, "0", x, y, width, height); + } + filled_in = true; + } + if((i + 1) % 7 == 0) { + x = draw_bits(canvas, "0", x, y, width, height); + filled_in = true; + } + } +} + static void barcode_draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); BarcodeModel* barcode_model = ctx; @@ -346,6 +408,9 @@ static void barcode_draw_callback(Canvas* canvas, void* ctx) { case CODE128: draw_code_128(canvas, data); break; + case CODABAR: + draw_codabar(canvas, data); + break; case UNKNOWN: default: break; diff --git a/applications/external/barcode_gen/views/create_view.c b/applications/external/barcode_gen/views/create_view.c index 23a5fa409..e4e489113 100644 --- a/applications/external/barcode_gen/views/create_view.c +++ b/applications/external/barcode_gen/views/create_view.c @@ -448,7 +448,8 @@ void save_barcode(CreateView* create_view_object) { flipper_format_write_string_cstr(ff, "Version", FILE_VERSION); - flipper_format_write_comment_cstr(ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128"); + flipper_format_write_comment_cstr( + ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128, Codabar"); flipper_format_write_string_cstr(ff, "Type", barcode_type->name); diff --git a/applications/external/barcode_gen/views/message_view.c b/applications/external/barcode_gen/views/message_view.c index 13e238381..3a9aa90b3 100644 --- a/applications/external/barcode_gen/views/message_view.c +++ b/applications/external/barcode_gen/views/message_view.c @@ -53,15 +53,9 @@ MessageView* message_view_allocate(BarcodeApp* barcode_app) { return message_view_object; } -void message_view_free_model(MessageView* message_view_object) { - with_view_model( - message_view_object->view, MessageViewModel * model, { UNUSED(model); }, true); -} - void message_view_free(MessageView* message_view_object) { furi_assert(message_view_object); - message_view_free_model(message_view_object); view_free(message_view_object->view); free(message_view_object); } @@ -69,4 +63,4 @@ void message_view_free(MessageView* message_view_object) { View* message_get_view(MessageView* message_view_object) { furi_assert(message_view_object); return message_view_object->view; -} +} \ No newline at end of file diff --git a/applications/external/geiger/flipper_geiger.c b/applications/external/geiger/flipper_geiger.c index 9c3d0d3fc..17f2b7058 100644 --- a/applications/external/geiger/flipper_geiger.c +++ b/applications/external/geiger/flipper_geiger.c @@ -39,53 +39,33 @@ typedef struct { static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - mutexStruct* mutexVal = ctx; - mutexStruct mutexDraw; - furi_mutex_acquire(mutexVal->mutex, FuriWaitForever); - memcpy(&mutexDraw, mutexVal, sizeof(mutexStruct)); - furi_mutex_release(mutexVal->mutex); + mutexStruct displayStruct; + mutexStruct* geigerMutex = ctx; + furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever); + memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct)); + furi_mutex_release(geigerMutex->mutex); char buffer[32]; - if(mutexDraw.data == 0) - snprintf(buffer, sizeof(buffer), "%ld cps - %ld cpm", mutexDraw.cps, mutexDraw.cpm); - else if(mutexDraw.data == 1) + if(displayStruct.data == 0) + snprintf( + buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm); + else if(displayStruct.data == 1) snprintf( buffer, sizeof(buffer), "%ld cps - %.2f uSv/h", - mutexDraw.cps, - ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR)); - else if(mutexDraw.data == 2) - snprintf( - buffer, - sizeof(buffer), - "%ld cps - %.2f mSv/y", - mutexDraw.cps, - (((double)mutexDraw.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); - else if(mutexDraw.data == 3) - snprintf( - buffer, - sizeof(buffer), - "%ld cps - %.4f Rad/h", - mutexDraw.cps, - ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10000); - else if(mutexDraw.data == 4) - snprintf( - buffer, - sizeof(buffer), - "%ld cps - %.2f mR/h", - mutexDraw.cps, - ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) / (double)10); + displayStruct.cps, + ((double)displayStruct.cpm * (double)CONVERSION_FACTOR)); else snprintf( buffer, sizeof(buffer), - "%ld cps - %.2f uR/h", - mutexDraw.cps, - ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) * (double)100); + "%ld cps - %.2f mSv/y", + displayStruct.cps, + (((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); for(int i = 0; i < SCREEN_SIZE_X; i += 2) { - float Y = SCREEN_SIZE_Y - (mutexDraw.line[i / 2] * mutexDraw.coef); + float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef); canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y); canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y); @@ -109,7 +89,7 @@ static void clock_tick(void* ctx) { randomNumber &= 0xFFF; if(randomNumber == 0) randomNumber = 1; - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, randomNumber, 50); + furi_hal_pwm_set_params(FuriHalPwmOutputIdLptim2PA4, randomNumber, 50); FuriMessageQueue* queue = ctx; EventApp event = {.type = ClockEventTypeTick}; @@ -123,7 +103,8 @@ static void gpiocallback(void* ctx) { furi_message_queue_put(queue, &event, 0); } -int32_t flipper_geiger_app() { +int32_t flipper_geiger_app(void* p) { + UNUSED(p); EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -146,7 +127,7 @@ int32_t flipper_geiger_app() { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &mutexVal.mutex); + view_port_draw_callback_set(view_port, draw_callback, &mutexVal); view_port_input_callback_set(view_port, input_callback, event_queue); furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue); @@ -158,7 +139,13 @@ int32_t flipper_geiger_app() { furi_timer_start(timer, 1000); // ENABLE 5V pin - furi_hal_power_enable_otg(); + + // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } while(1) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); @@ -186,7 +173,7 @@ int32_t flipper_geiger_app() { if(mutexVal.data != 0) mutexVal.data--; else - mutexVal.data = 5; + mutexVal.data = 2; screenRefresh = 1; furi_mutex_release(mutexVal.mutex); @@ -194,7 +181,7 @@ int32_t flipper_geiger_app() { event.input.type == InputTypeShort)) { furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - if(mutexVal.data != 5) + if(mutexVal.data != 2) mutexVal.data++; else mutexVal.data = 0; @@ -235,7 +222,10 @@ int32_t flipper_geiger_app() { if(screenRefresh == 1) view_port_update(view_port); } - furi_hal_power_disable_otg(); + // Disable 5v power + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } furi_hal_gpio_disable_int_callback(&gpio_ext_pa7); furi_hal_gpio_remove_int_callback(&gpio_ext_pa7); diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c index 0e21d5560..01e022755 100644 --- a/applications/external/ir_remote/infrared_remote_app.c +++ b/applications/external/ir_remote/infrared_remote_app.c @@ -1,6 +1,8 @@ #include #include +#include + #include #include #include @@ -13,8 +15,7 @@ #include "infrared_signal.h" #include "infrared_remote.h" #include "infrared_remote_button.h" -#define TAG "IR_Remote" -#define MENU_BTN_TXT_X 36 +#define TAG "ir_remote" #include @@ -32,6 +33,7 @@ typedef struct { FuriString* left_hold_button; FuriString* right_hold_button; FuriString* ok_hold_button; + InfraredWorker* infrared_worker; } IRApp; // Screen is 128x64 px @@ -61,47 +63,17 @@ static void app_draw_callback(Canvas* canvas, void* ctx) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 8, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->up_button)); + canvas, 32, 8, AlignCenter, AlignCenter, furi_string_get_cstr(app->up_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 18, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->down_button)); + canvas, 32, 18, AlignCenter, AlignCenter, furi_string_get_cstr(app->down_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 28, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->left_button)); + canvas, 32, 28, AlignCenter, AlignCenter, furi_string_get_cstr(app->left_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 38, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->right_button)); + canvas, 32, 38, AlignCenter, AlignCenter, furi_string_get_cstr(app->right_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 48, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->ok_button)); + canvas, 32, 48, AlignCenter, AlignCenter, furi_string_get_cstr(app->ok_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 58, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->back_button)); + canvas, 32, 58, AlignCenter, AlignCenter, furi_string_get_cstr(app->back_button)); canvas_draw_line(canvas, 0, 65, 64, 65); @@ -113,41 +85,21 @@ static void app_draw_callback(Canvas* canvas, void* ctx) { canvas_draw_icon(canvas, 0, 118, &I_back_10px); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 73, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->up_hold_button)); + canvas, 32, 73, AlignCenter, AlignCenter, furi_string_get_cstr(app->up_hold_button)); + canvas_draw_str_aligned( + canvas, 32, 83, AlignCenter, AlignCenter, furi_string_get_cstr(app->down_hold_button)); + canvas_draw_str_aligned( + canvas, 32, 93, AlignCenter, AlignCenter, furi_string_get_cstr(app->left_hold_button)); canvas_draw_str_aligned( canvas, - MENU_BTN_TXT_X, - 83, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->down_hold_button)); - canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 93, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->left_hold_button)); - canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, + 32, 103, AlignCenter, AlignCenter, furi_string_get_cstr(app->right_hold_button)); canvas_draw_str_aligned( - canvas, - MENU_BTN_TXT_X, - 113, - AlignCenter, - AlignCenter, - furi_string_get_cstr(app->ok_hold_button)); - canvas_draw_str_aligned(canvas, MENU_BTN_TXT_X, 123, AlignCenter, AlignCenter, "Exit App"); + canvas, 32, 113, AlignCenter, AlignCenter, furi_string_get_cstr(app->ok_hold_button)); + canvas_draw_str_aligned(canvas, 32, 123, AlignCenter, AlignCenter, "Exit App"); } } @@ -177,6 +129,7 @@ int32_t infrared_remote_app(void* p) { app->right_hold_button = furi_string_alloc(); app->ok_hold_button = furi_string_alloc(); app->view_port = view_port_alloc(); + app->infrared_worker = infrared_worker_alloc(); // Configure view port view_port_draw_callback_set(app->view_port, app_draw_callback, app); @@ -247,6 +200,9 @@ int32_t infrared_remote_app(void* p) { InfraredSignal* right_hold_signal = infrared_signal_alloc(); InfraredSignal* ok_hold_signal = infrared_signal_alloc(); + InfraredSignal* active_signal = NULL; + bool is_transmitting = false; + bool up_enabled = false; bool down_enabled = false; bool left_enabled = false; @@ -281,8 +237,6 @@ int32_t infrared_remote_app(void* p) { //set missing filenames to N/A //assign button signals size_t index = 0; - - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "UP", app->up_button)) { FURI_LOG_W(TAG, "Could not read UP string"); furi_string_set(app->up_button, "N/A"); @@ -297,7 +251,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "DOWN", app->down_button)) { FURI_LOG_W(TAG, "Could not read DOWN string"); furi_string_set(app->down_button, "N/A"); @@ -312,7 +265,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "LEFT", app->left_button)) { FURI_LOG_W(TAG, "Could not read LEFT string"); furi_string_set(app->left_button, "N/A"); @@ -327,7 +279,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "RIGHT", app->right_button)) { FURI_LOG_W(TAG, "Could not read RIGHT string"); furi_string_set(app->right_button, "N/A"); @@ -342,7 +293,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "OK", app->ok_button)) { FURI_LOG_W(TAG, "Could not read OK string"); furi_string_set(app->ok_button, "N/A"); @@ -357,7 +307,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "BACK", app->back_button)) { FURI_LOG_W(TAG, "Could not read BACK string"); furi_string_set(app->back_button, "N/A"); @@ -372,7 +321,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "UPHOLD", app->up_hold_button)) { FURI_LOG_W(TAG, "Could not read UPHOLD string"); furi_string_set(app->up_hold_button, "N/A"); @@ -387,7 +335,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "DOWNHOLD", app->down_hold_button)) { FURI_LOG_W(TAG, "Could not read DOWNHOLD string"); furi_string_set(app->down_hold_button, "N/A"); @@ -402,7 +349,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "LEFTHOLD", app->left_hold_button)) { FURI_LOG_W(TAG, "Could not read LEFTHOLD string"); furi_string_set(app->left_hold_button, "N/A"); @@ -417,7 +363,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "RIGHTHOLD", app->right_hold_button)) { FURI_LOG_W(TAG, "Could not read RIGHTHOLD string"); furi_string_set(app->right_hold_button, "N/A"); @@ -432,7 +377,6 @@ int32_t infrared_remote_app(void* p) { } } - flipper_format_rewind(ff); if(!flipper_format_read_string(ff, "OKHOLD", app->ok_hold_button)) { FURI_LOG_W(TAG, "Could not read OKHOLD string"); furi_string_set(app->ok_hold_button, "N/A"); @@ -480,43 +424,37 @@ int32_t infrared_remote_app(void* p) { switch(event.key) { case InputKeyUp: if(up_enabled) { - infrared_signal_transmit(up_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = up_signal; FURI_LOG_I(TAG, "up"); } break; case InputKeyDown: if(down_enabled) { - infrared_signal_transmit(down_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = down_signal; FURI_LOG_I(TAG, "down"); } break; case InputKeyRight: if(right_enabled) { - infrared_signal_transmit(right_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = right_signal; FURI_LOG_I(TAG, "right"); } break; case InputKeyLeft: if(left_enabled) { - infrared_signal_transmit(left_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = left_signal; FURI_LOG_I(TAG, "left"); } break; case InputKeyOk: if(ok_enabled) { - infrared_signal_transmit(ok_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = ok_signal; FURI_LOG_I(TAG, "ok"); } break; case InputKeyBack: if(back_enabled) { - infrared_signal_transmit(back_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = back_signal; FURI_LOG_I(TAG, "back"); } break; @@ -529,36 +467,31 @@ int32_t infrared_remote_app(void* p) { switch(event.key) { case InputKeyUp: if(up_hold_enabled) { - infrared_signal_transmit(up_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = up_hold_signal; FURI_LOG_I(TAG, "up!"); } break; case InputKeyDown: if(down_hold_enabled) { - infrared_signal_transmit(down_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = down_hold_signal; FURI_LOG_I(TAG, "down!"); } break; case InputKeyRight: if(right_hold_enabled) { - infrared_signal_transmit(right_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = right_hold_signal; FURI_LOG_I(TAG, "right!"); } break; case InputKeyLeft: if(left_hold_enabled) { - infrared_signal_transmit(left_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = left_hold_signal; FURI_LOG_I(TAG, "left!"); } break; case InputKeyOk: if(ok_hold_enabled) { - infrared_signal_transmit(ok_hold_signal); - notification_message(notification, &sequence_blink_start_magenta); + active_signal = ok_hold_signal; FURI_LOG_I(TAG, "ok!"); } break; @@ -566,8 +499,39 @@ int32_t infrared_remote_app(void* p) { running = false; break; } - } else if(event.type == InputTypeRelease) { + } else if(event.type == InputTypeRelease && is_transmitting) { notification_message(notification, &sequence_blink_stop); + infrared_worker_tx_stop(app->infrared_worker); + is_transmitting = false; + active_signal = NULL; + } + + if(active_signal != NULL && + (event.type == InputTypeShort || event.type == InputTypeLong)) { + if(is_transmitting) { + infrared_worker_tx_stop(app->infrared_worker); + } + + if(infrared_signal_is_raw(active_signal)) { + InfraredRawSignal* raw_signal = + infrared_signal_get_raw_signal(active_signal); + infrared_worker_set_raw_signal( + app->infrared_worker, + raw_signal->timings, + raw_signal->timings_size, + raw_signal->frequency, + raw_signal->duty_cycle); + } else { + InfraredMessage* message = infrared_signal_get_message(active_signal); + infrared_worker_set_decoded_signal(app->infrared_worker, message); + } + + infrared_worker_tx_set_get_signal_callback( + app->infrared_worker, infrared_worker_tx_get_signal_steady_callback, app); + + infrared_worker_tx_start(app->infrared_worker); + notification_message(notification, &sequence_blink_start_magenta); + is_transmitting = true; } } } @@ -586,6 +550,12 @@ int32_t infrared_remote_app(void* p) { furi_string_free(app->right_hold_button); furi_string_free(app->ok_hold_button); + if(is_transmitting) { + infrared_worker_tx_stop(app->infrared_worker); + notification_message(notification, &sequence_blink_stop); + } + infrared_worker_free(app->infrared_worker); + infrared_remote_free(remote); view_port_enabled_set(app->view_port, false); gui_remove_view_port(gui, app->view_port); diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt b/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt deleted file mode 100644 index e5bd3ed32..000000000 --- a/applications/external/nrf24scan/Distr/nrf24scan/addresses.txt +++ /dev/null @@ -1,11 +0,0 @@ -Rate: 1 -Ch: 2 -ESB: 1 -DPL: 0 -CRC: 2 -Payload: 4 -P0: C8C8C0 -P1: C8C8C1 -P2: C2 -P3: C3 -P4: E5 diff --git a/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt b/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt deleted file mode 100644 index fa8c5ba9f..000000000 --- a/applications/external/nrf24scan/Distr/nrf24scan/sniff.txt +++ /dev/null @@ -1,5 +0,0 @@ -SNIFF -ESB: 1 -CRC: 2 -P0: 00AA -P1: 0055 diff --git a/applications/external/pong/flipper_pong.c b/applications/external/pong/flipper_pong.c index 53c6a7e27..55b371ad5 100644 --- a/applications/external/pong/flipper_pong.c +++ b/applications/external/pong/flipper_pong.c @@ -15,7 +15,8 @@ #define PAD_SIZE_X 3 #define PAD_SIZE_Y 8 -#define PLAYER1_PAD_SPEED 2 +#define PLAYER1_PAD_SPEED 4 + #define PLAYER2_PAD_SPEED 2 #define BALL_SIZE 4 @@ -38,22 +39,29 @@ typedef struct Players { static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - Players* players = ctx; - furi_mutex_acquire(players->mutex, FuriWaitForever); + Players* playersMutex = ctx; + furi_mutex_acquire(playersMutex->mutex, FuriWaitForever); canvas_draw_frame(canvas, 0, 0, 128, 64); - canvas_draw_box(canvas, players->player1_X, players->player1_Y, PAD_SIZE_X, PAD_SIZE_Y); - canvas_draw_box(canvas, players->player2_X, players->player2_Y, PAD_SIZE_X, PAD_SIZE_Y); - canvas_draw_box(canvas, players->ball_X, players->ball_Y, BALL_SIZE, BALL_SIZE); + canvas_draw_box( + canvas, playersMutex->player1_X, playersMutex->player1_Y, PAD_SIZE_X, PAD_SIZE_Y); + canvas_draw_box( + canvas, playersMutex->player2_X, playersMutex->player2_Y, PAD_SIZE_X, PAD_SIZE_Y); + canvas_draw_box(canvas, playersMutex->ball_X, playersMutex->ball_Y, BALL_SIZE, BALL_SIZE); canvas_set_font(canvas, FontPrimary); canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); char buffer[16]; - snprintf(buffer, sizeof(buffer), "%u - %u", players->player1_score, players->player2_score); + snprintf( + buffer, + sizeof(buffer), + "%u - %u", + playersMutex->player1_score, + playersMutex->player2_score); canvas_draw_str_aligned( canvas, SCREEN_SIZE_X / 2 + 15, SCREEN_SIZE_Y / 2 + 2, AlignCenter, AlignTop, buffer); - furi_mutex_release(players->mutex); + furi_mutex_release(playersMutex->mutex); } static void input_callback(InputEvent* input_event, void* ctx) { @@ -93,7 +101,8 @@ uint8_t changeDirection() { return randomuint8[0]; } -int32_t flipper_pong_app() { +int32_t flipper_pong_app(void* p) { + UNUSED(p); EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -120,7 +129,7 @@ int32_t flipper_pong_app() { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &players.mutex); + view_port_draw_callback_set(view_port, draw_callback, &players); view_port_input_callback_set(view_port, input_callback, event_queue); Gui* gui = furi_record_open(RECORD_GUI); @@ -143,6 +152,7 @@ int32_t flipper_pong_app() { if(event.type == EventTypeInput) { if(event.input.key == InputKeyBack) { furi_mutex_release(players.mutex); + notification_message(notification, &sequence_set_only_green_255); break; } else if(event.input.key == InputKeyUp) { if(players.player1_Y >= 1 + PLAYER1_PAD_SPEED) diff --git a/applications/external/wiiec/_image_tool/README b/applications/external/wiiec/_image_tool/README deleted file mode 100644 index 979605a08..000000000 --- a/applications/external/wiiec/_image_tool/README +++ /dev/null @@ -1,30 +0,0 @@ -1. Prepare the image - a. Open your *black and white* image in GIMP - b. File -> Export As - filename: EXAMPLE.c - Type : "C source code" - [Export] - prefixed name: gimp_image - Comment : - [x] Use GLib types - [ ] <> - Opacity : 100% - [Export] - -2. Prepare conversion tool [stored in (eg.) /path/] - a. cp _convert*.* /path/ - b. cp EXAMPLE.c /path/ - -3. Run the conversion tool - a. cd /path/ - b. ./_convert.sh EXAMPLE.c - -4. All being well, you will see an ascii version of your image. - If not, then you're gonna have to submit a bug report - -5. You should now have a directory called img_/ - In that directory should be - img_EXAMPLE.c - The data for your new image - img_*.c - The data for other images - images.h - A header for ALL images that have been created in this directory - images.c - A sample FlipperZero show() function [not optimised] diff --git a/applications/external/wiiec/_image_tool/_convert.sh b/applications/external/wiiec/_image_tool/_convert.sh deleted file mode 100755 index aaa7977b5..000000000 --- a/applications/external/wiiec/_image_tool/_convert.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -[ -z $1 ] && { - echo "Specify an image" - echo "gimp -> export -> c source file -> [x] gunit names" - exit 2 -} - -echo $* - -for N in $* ; do - - [ ! -f $N ] && { - echo "!! File missing $N" - continue - } - - # filename (sans extension) - FN=$(basename -- "$N") - EXT="${FN##*.}" - NAME="${FN%.*}" - - OUTDIR=img_/ - mkdir -p ${OUTDIR} - - HDR=${OUTDIR}/images.h - SRC=${OUTDIR}/images.c - - OUT=${OUTDIR}/img_${NAME}.c - - echo -e "\n¦${N}¦ == ¦${NAME}¦ -> ¦${OUT}¦" - - TESTX=test_${NAME} - TESTC=test_${NAME}.c - - # compile name - CONV=${NAME}_ - - # clean up gimp output - sed -e "s/gimp_image/img/g" \ - -e 's/guint8/unsigned char/g' \ - -e 's/width/w/g' \ - -e 's/height/h/g' \ - -e 's/bytes_per_pixel/bpp/g' \ - -e 's/pixel_data/b/g' \ - -e 's/guint/unsigned int/g' \ - $N \ - | grep -v ^/ \ - | grep -v ^$ \ - > ${CONV}.c - - # append conversion code - cat _convert.c >> ${CONV}.c - - # compile & run converter - rm -f ${CONV} - gcc ${CONV}.c -DIMGTEST -o ${CONV} - ./${CONV} ${NAME} ${OUT} - rm -f ${CONV} ${CONV}.c - - # (create &) update header - [[ ! -f ${HDR} ]] && cp _convert_images.h ${HDR} - sed -i "/ img_${NAME};/d" ${HDR} - sed -i "s#//\[TAG\]#//\[TAG\]\nextern const image_t img_${NAME};#" ${HDR} - - # sample FZ code - [[ ! -f images.c ]] && cp _convert_images.c ${SRC} - - # test - ROOT=${PWD} - pushd ${OUTDIR} >/dev/null - sed "s/zzz/${NAME}/" ${ROOT}/_convert_test.c > ${TESTC} - rm -f ${TESTX} - gcc ${TESTC} ${OUT##*/} -DIMGTEST -o ${TESTX} - ./${TESTX} - rm -f ${TESTX} ${TESTC} - popd >/dev/null - -done diff --git a/applications/external/wiiec/_image_tool/_convert_images.c b/applications/external/wiiec/_image_tool/_convert_images.c deleted file mode 100644 index e8ab899f7..000000000 --- a/applications/external/wiiec/_image_tool/_convert_images.c +++ /dev/null @@ -1,137 +0,0 @@ -#include // GUI (screen/keyboard) API - -#include "images.h" - -//----------------------------------------------------------------------------- ---------------------------------------- -static Canvas* _canvas; -static uint8_t _tlx; -static uint8_t _tly; - -static uint8_t _x; -static uint8_t _y; - -static const image_t* _img; - -static bool _blk; -static Color _set; -static Color _clr; - -//+============================================================================ -static void _showByteSet(const uint8_t b) { - for(uint8_t m = 0x80; m; m >>= 1) { - if(b & m) // plot only SET bits - canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); - if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; - } -} - -//+============================================================================ -static void _showByteClr(const uint8_t b) { - for(uint8_t m = 0x80; m; m >>= 1) { - if(!(b & m)) // plot only CLR bits - canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); - if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; - } -} - -//+============================================================================ -static void _showByteAll(const uint8_t b) { - for(uint8_t m = 0x80; m; m >>= 1) { - if((!!(b & m)) ^ _blk) { // Change colour only when required - canvas_set_color(_canvas, ((b & m) ? _set : _clr)); - _blk = !_blk; - } - canvas_draw_dot(_canvas, (_tlx + _x), (_tly + _y)); - if(((++_x) == _img->w) && !(_x = 0) && ((++_y) == _img->h)) break; - } -} - -//+============================================================================ -// available modes are SHOW_SET_BLK - plot image pixels that are SET in BLACK -// SHOW_XOR - same as SET_BLACK -// SHOW_SET_WHT - plot image pixels that are SET in WHITE -// SHOW_CLR_BLK - plot image pixels that are CLEAR in BLACK -// SHOW_CLR_WHT - plot image pixels that are CLEAR in WHITE -// SHOW_ALL - plot all images pixels as they are -// SHOW_ALL_INV - plot all images pixels inverted -// -void show( - Canvas* const canvas, - const uint8_t tlx, - const uint8_t tly, - const image_t* img, - const showMode_t mode) { - void (*fnShow)(const uint8_t) = NULL; - - const uint8_t* bp = img->data; - - // code size optimisation - switch(mode & SHOW_INV_) { - case SHOW_NRM_: - _set = ColorBlack; - _clr = ColorWhite; - break; - - case SHOW_INV_: - _set = ColorWhite; - _clr = ColorBlack; - break; - - case SHOW_BLK_: - canvas_set_color(canvas, ColorBlack); - break; - - case SHOW_WHT_: - canvas_set_color(canvas, ColorWhite); - break; - } - switch(mode & SHOW_INV_) { - case SHOW_NRM_: - case SHOW_INV_: - fnShow = _showByteAll; - canvas_set_color(canvas, ColorWhite); - _blk = 0; - break; - - case SHOW_BLK_: - case SHOW_WHT_: - switch(mode & SHOW_ALL_) { - case SHOW_SET_: - fnShow = _showByteSet; - break; - case SHOW_CLR_: - fnShow = _showByteClr; - break; - } - break; - } - furi_check(fnShow); - - // I want nested functions! - _canvas = canvas; - _img = img; - _tlx = tlx; - _tly = tly; - _x = 0; - _y = 0; - - // Compressed - if(img->c) { - for(unsigned int i = 0; i < img->len; i++, bp++) { - // Compressed data? {tag, length, value} - if(*bp == img->tag) { - for(uint16_t c = 0; c < bp[1]; c++) fnShow(bp[2]); - bp += 3 - 1; - i += 3 - 1; - - // Uncompressed byte - } else { - fnShow(*bp); - } - } - - // Not compressed - } else { - for(unsigned int i = 0; i < img->len; i++, bp++) fnShow(*bp); - } -} diff --git a/applications/external/wiiec/_image_tool/_convert_images.h b/applications/external/wiiec/_image_tool/_convert_images.h deleted file mode 100644 index 1743cb409..000000000 --- a/applications/external/wiiec/_image_tool/_convert_images.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef IMAGES_H_ -#define IMAGES_H_ - -#include -#include - -//----------------------------------------------------------------------------- ---------------------------------------- -typedef enum showMode { - // {INV:--:WHT:BLK::--:--:CLR:SET} - SHOW_SET_ = 0x01, - SHOW_CLR_ = 0x02, - SHOW_ALL_ = SHOW_SET_ | SHOW_CLR_, - - SHOW_BLK_ = 0x10, - SHOW_WHT_ = 0x20, - SHOW_NRM_ = 0x00, - SHOW_INV_ = SHOW_BLK_ | SHOW_WHT_, - - SHOW_SET_BLK = SHOW_SET_ | SHOW_BLK_, - SHOW_SET_WHT = SHOW_SET_ | SHOW_WHT_, - - SHOW_CLR_BLK = SHOW_CLR_ | SHOW_BLK_, - SHOW_CLR_WHT = SHOW_CLR_ | SHOW_WHT_, - - SHOW_ALL = SHOW_ALL_ | SHOW_NRM_, - SHOW_ALL_INV = SHOW_ALL_ | SHOW_INV_, -} showMode_t; - -//----------------------------------------------------------------------------- ---------------------------------------- -typedef struct image { - uint8_t w; // width - uint8_t h; // height - bool c; // compressed? - uint16_t len; // image data length - uint8_t tag; // rle tag - uint8_t data[]; // image data -} image_t; - -//----------------------------------------------------------------------------- ---------------------------------------- -//[TAG] - -//----------------------------------------------------------------------------- ---------------------------------------- -#ifndef IMGTEST -#include -void show( - Canvas* const canvas, - const uint8_t tlx, - const uint8_t tly, - const image_t* img, - const showMode_t mode); -#endif - -#endif //IMAGES_H_ diff --git a/applications/external/wiiec/info.sh b/applications/external/wiiec/info.sh deleted file mode 100755 index e009eb118..000000000 --- a/applications/external/wiiec/info.sh +++ /dev/null @@ -1,11 +0,0 @@ -echo "MARKED AS TODO" -echo "==============" -grep //! *.c *.h - -echo -e "\nSUPPORTED CONTROLLERS" -echo "=====================" -grep '\[PID_.*{ {' wii_ec.c | head -n -3 | sed 's/\s*\(.*\)/\1/' - -echo -e "\nLOGGING" -echo "=======" -grep LOG_LEVEL *.h | grep -v '#if ' diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addr-CO2mini.txt b/assets/resources/apps_data/nrf24scan/addr-CO2mini.txt similarity index 100% rename from applications/external/nrf24scan/Distr/nrf24scan/addr-CO2mini.txt rename to assets/resources/apps_data/nrf24scan/addr-CO2mini.txt diff --git a/applications/external/nrf24scan/Distr/nrf24scan/addr-WCO1.txt b/assets/resources/apps_data/nrf24scan/addr-WCO1.txt similarity index 100% rename from applications/external/nrf24scan/Distr/nrf24scan/addr-WCO1.txt rename to assets/resources/apps_data/nrf24scan/addr-WCO1.txt From 1d82583f65620233531c5363a27e12694a4d4fd3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 01:17:33 +0100 Subject: [PATCH 208/370] Add text2sam --- .../external/text2sam/application.fam | 17 + applications/external/text2sam/icon.png | Bin 0 -> 1427 bytes applications/external/text2sam/sam_app.cpp | 257 + applications/external/text2sam/stm32_sam.cpp | 5704 +++++++++++++++++ applications/external/text2sam/stm32_sam.h | 96 + 5 files changed, 6074 insertions(+) create mode 100644 applications/external/text2sam/application.fam create mode 100644 applications/external/text2sam/icon.png create mode 100644 applications/external/text2sam/sam_app.cpp create mode 100644 applications/external/text2sam/stm32_sam.cpp create mode 100644 applications/external/text2sam/stm32_sam.h diff --git a/applications/external/text2sam/application.fam b/applications/external/text2sam/application.fam new file mode 100644 index 000000000..d278ef8c3 --- /dev/null +++ b/applications/external/text2sam/application.fam @@ -0,0 +1,17 @@ +App( + appid="text2sam", + name="Text to SAM", + apptype=FlipperAppType.EXTERNAL, + entry_point="sam_app", + cdefines=["APP_SAM"], + # requires=["gui",], + requires=[ + "gui", + "dialogs", + ], + stack_size=4 * 1024, + # stack_size=2 * 1024, + fap_icon="icon.png", + fap_category="Media", + order=20, +) diff --git a/applications/external/text2sam/icon.png b/applications/external/text2sam/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d13fa0c074fa5d276efc6bfc50ab8eb4315bddab GIT binary patch literal 1427 zcmbVMy>HV%6n85jL8Vq=fdWE0xeEwvpPj^QPBo=XLK6v>D2>#x^^tB6-tT?C$JMpf>8XoTk|a%6 z%EcP2$$skeBzz~Ujc>4=3Cnk5Nt*eTyc5r^&0LbCQ|G*TLp1E$#Nob5UEXAB-wy#T zNsCMUkUCpTpeAd3fhGU=@KHvdYsniq8{1)lb-eOk#MbxL>dxMllXvB%8)&glfWT*h zqQ2h^V$!$d0WX1la;(W{01;c3yqqXR4ZDg8JYvXHvEpECp!vM2o7s$suOkB+Si^>< zXB4axJWmV*jUE}aMQ)4Kinm6(;LnmfLWD%qdcB_7%cwkRYkEGPPdE%i0fZ9o27>mL zAf6jD6j|&eystfG6n6h8MWJ7hgRfz=~8bONOjLz6?djb=@So zL2{;oa|Gid)aI_&+C2)I!I0oXP$(If3VJNqbx4axJ_Uz8pSGD625lLoVI&3KoNz=D}_Qe;w`TW7jbQM1yxD~Q_q{GVyODST-zpT+&>acblzz zPRX_wGD>qkn^P7{Y$|##lWo#WK1W+@EMMf#c2WXkdG|l%??fK7Anl%*Cuz*2c1YQa zA*;Ki)>&uwM!T+u2IWGileB>)J4ybTE02!76Gvc3+v_kO{YPC4VKHxs9*x*?8=`e= z-DzOFmNe|p5A9DE2X_aWdklw(k!;gZ1wZMqV*yOK2u7K_dU+4V;9RA+T%Wz*{CaX8 xZ=T(sc$0nj=*9BW?7IiAzQ4Zt{Ql>klJxTHm$})MyYG{+St+d*->z)#{|2xbxBLJA literal 0 HcmV?d00001 diff --git a/applications/external/text2sam/sam_app.cpp b/applications/external/text2sam/sam_app.cpp new file mode 100644 index 000000000..579cd349b --- /dev/null +++ b/applications/external/text2sam/sam_app.cpp @@ -0,0 +1,257 @@ +#include +#include +// #include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "stm32_sam.h" + +#define TAG "SAM" +#define SAM_SAVE_PATH EXT_PATH("sam.txt") +#define TEXT_BUFFER_SIZE 256 +// #define MESSAGES_BUFFER_SIZE 256 +STM32SAM voice; + +// FuriMutex* g_state_mutex; + +// FuriString* message; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + FuriMutex* mutex; + ViewDispatcher* view_dispatcher; + TextInput* text_input; + TextBox* text_box; + char input[TEXT_BUFFER_SIZE]; +} AppState; + +AppState* app_state; + +static void say_something(char* something) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say(something); + furi_hal_speaker_release(); + } +} + +static void text_input_callback(void* ctx) { + AppState* app_state = (AppState*)ctx; + furi_mutex_acquire(app_state->mutex, FuriWaitForever); + //FURI_LOG_D(TAG, "Input text: %s", app_state->input); + + // underscore_to_space(app_state->input); + for(int i = 0; app_state->input[i] != '\0'; i++) { + if(app_state->input[i] == '_') { + app_state->input[i] = ' '; + } else { + app_state->input[i] = app_state->input[i]; + } + } + + text_box_set_text(app_state->text_box, app_state->input); + view_dispatcher_switch_to_view(app_state->view_dispatcher, 1); + + furi_mutex_release(app_state->mutex); +} + +static bool back_event_callback(void* ctx) { + const AppState* app_state = (AppState*)ctx; + view_dispatcher_stop(app_state->view_dispatcher); + furi_mutex_release(app_state->mutex); + return true; +} + +static void sam_state_init(AppState* const app_state) { + app_state->view_dispatcher = view_dispatcher_alloc(); + app_state->text_input = text_input_alloc(); + app_state->text_box = text_box_alloc(); + text_box_set_font(app_state->text_box, TextBoxFontText); +} + +static void sam_state_free(AppState* const app_state) { + text_input_free(app_state->text_input); + text_box_free(app_state->text_box); + view_dispatcher_remove_view(app_state->view_dispatcher, 0); + view_dispatcher_remove_view(app_state->view_dispatcher, 1); + view_dispatcher_free(app_state->view_dispatcher); + free(app_state); +} + +// static void app_draw_callback(Canvas* canvas, void* ctx) {} + +static void app_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +static void save_message(FuriString* save_string) { + Storage* storage = (Storage*)furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + // uint16_t bytes_read = 0; + if(storage_file_open(file, SAM_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + // bytes_read = + // storage_file_write(file, save_string, sizeof(save_string)); + storage_file_write(file, save_string, TEXT_BUFFER_SIZE); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +static bool load_messages() { + Storage* storage = (Storage*)furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + uint16_t bytes_read = 0; + if(storage_file_open(file, SAM_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + bytes_read = storage_file_read(file, app_state->input, TEXT_BUFFER_SIZE); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return bytes_read == TEXT_BUFFER_SIZE; + // FlipperFormat* ff = flipper_format_file_alloc(storage); + + // DialogsApp* dialogs = (DialogsApp*)furi_record_open(RECORD_DIALOGS); + // DialogsFileBrowserOptions browser_options; + // dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL); + // FuriString* map_file = (FuriString*)"/ext/sam"; //furi_string_alloc(); + // // furi_string_set(map_file, "/ext/sam"); + // if(!storage_file_exists(storage, ANY_PATH("sam"))) { + // storage_common_mkdir(storage, ANY_PATH("sam")); //Make Folder If dir not exist + // } + + // bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); + + // furi_record_close(RECORD_DIALOGS); + + // // if user didn't choose anything, free everything and exit + // if(!res) { + // FURI_LOG_I(TAG, "exit"); + // flipper_format_free(ff); + // furi_record_close(RECORD_STORAGE); + + // furi_string_free(message); + + // view_port_enabled_set(view_port, false); + // gui_remove_view_port(gui, view_port); + // view_port_free(view_port); + // free(g_state_mutex); + // furi_message_queue_free(event_queue); + + // furi_record_close(RECORD_GUI); + // return; + // } +} + +extern "C" int32_t sam_app(void* p) { + UNUSED(p); + app_state = (AppState*)malloc(sizeof(AppState)); + + // g_state_mutex = furi_mutex_alloc(FuriMutexTypeRecursive); + + FURI_LOG_D(TAG, "Running sam_state_init"); + sam_state_init(app_state); + + app_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!app_state->mutex) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + free(app_state); + return 255; + } + + // message = furi_string_alloc(); + + // Configure view port + ViewPort* view_port = view_port_alloc(); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + // view_port_draw_callback_set(view_port, app_draw_callback, g_state_mutex); + view_port_input_callback_set(view_port, app_input_callback, event_queue); + + // // Register view port in GUI + // Gui* gui = (Gui*)furi_record_open(RECORD_GUI); + // gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // InputEvent event; + + //TODO: get message from file + FURI_LOG_D(TAG, "Assigning text input callback"); + + load_messages(); + text_input_set_result_callback( + app_state->text_input, + text_input_callback, + app_state, + app_state->input, + TEXT_BUFFER_SIZE, + //clear default text + false); + text_input_set_header_text(app_state->text_input, "Input"); + + // Open GUI and register view_port + Gui* gui = (Gui*)furi_record_open(RECORD_GUI); + //gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FURI_LOG_D(TAG, "Enabling view dispatcher queue"); + view_dispatcher_enable_queue(app_state->view_dispatcher); + + FURI_LOG_D(TAG, "Adding text input view to dispatcher"); + view_dispatcher_add_view( + app_state->view_dispatcher, 0, text_input_get_view(app_state->text_input)); + view_dispatcher_add_view( + app_state->view_dispatcher, 1, text_box_get_view(app_state->text_box)); + + FURI_LOG_D(TAG, "Attaching view dispatcher to GUI"); + view_dispatcher_attach_to_gui(app_state->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + FURI_LOG_D(TAG, "starting view dispatcher"); + view_dispatcher_set_navigation_event_callback(app_state->view_dispatcher, back_event_callback); + view_dispatcher_set_event_callback_context(app_state->view_dispatcher, app_state); + view_dispatcher_switch_to_view(app_state->view_dispatcher, 0); + view_dispatcher_run(app_state->view_dispatcher); + + // for(bool running = true; running;) { + // PluginEvent event; + // FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + // FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); + // if(event_status == FuriStatusOk) { + // if(event.input.key == InputKeyOk) { + say_something(app_state->input); + save_message((FuriString*)app_state->input); + FURI_LOG_D(TAG, "Spoken text: %s", app_state->input); + // } + // if(event.input.key == InputKeyBack) { + // running = false; + // } + // } + // } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_mutex_free(app_state->mutex); + // furi_mutex_free(g_state_mutex); + sam_state_free(app_state); + + return 0; +} diff --git a/applications/external/text2sam/stm32_sam.cpp b/applications/external/text2sam/stm32_sam.cpp new file mode 100644 index 000000000..16f6fcaab --- /dev/null +++ b/applications/external/text2sam/stm32_sam.cpp @@ -0,0 +1,5704 @@ + +#include "stm32_sam.h" +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// All +// +//////////////////////////////////////////////////////////////////////////////////////////// + +char input[256 + 1] = {0}; //tab39445 +//standard sam sound + +unsigned char wait1 = 7; +unsigned char wait2 = 6; + +unsigned char A, X, Y; +unsigned char mem44; +unsigned char mem47; +unsigned char mem49; +unsigned char mem39; +unsigned char mem50; +unsigned char mem51; +unsigned char mem53; +unsigned char mem56; +unsigned char mem59 = 0; + +unsigned char phonemeIndexOutput[60]; //tab47296 +unsigned char stressOutput[60]; //tab47365 +unsigned char phonemeLengthOutput[60]; //tab47416 + +// contains the soundbuffer position +int bufferpos; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam Tabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//tab40672 +const unsigned char stressInputTable[] = {'*', '1', '2', '3', '4', '5', '6', '7', '8'}; + +//tab40682 +const unsigned char signInputTable1[] = { + ' ', '.', '?', ',', '-', 'I', 'I', 'E', 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', 'U', + 'O', 'R', 'L', 'W', 'Y', 'W', 'R', 'L', 'W', 'Y', 'M', 'N', 'N', 'D', 'Q', 'S', 'S', + 'F', 'T', '/', '/', 'Z', 'Z', 'V', 'D', 'C', '*', 'J', '*', '*', '*', 'E', 'A', 'O', + 'A', 'O', 'U', 'B', '*', '*', 'D', '*', '*', 'G', '*', '*', 'G', '*', '*', 'P', '*', + '*', 'T', '*', '*', 'K', '*', '*', 'K', '*', '*', 'U', 'U', 'U'}; + +//tab40763 +const unsigned char signInputTable2[] = { + '*', '*', '*', '*', '*', 'Y', 'H', 'H', 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', 'X', + 'H', 'X', 'X', 'X', 'X', 'H', '*', '*', '*', '*', '*', '*', 'X', 'X', '*', '*', 'H', + '*', 'H', 'H', 'X', '*', 'H', '*', 'H', 'H', '*', '*', '*', '*', '*', 'Y', 'Y', 'Y', + 'W', 'W', 'W', '*', '*', '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', '*', '*', + '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', 'L', 'M', 'N'}; + +//loc_9F8C +const unsigned char flags[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x84, 0x84, 0xA4, + 0xA4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4C, + 0x4C, 0x4C, 0x48, 0x4C, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, 0x44, 0x44, + 0x48, 0x40, 0x4C, 0x44, 0x00, 0x00, 0xB4, 0xB4, 0xB4, 0x94, 0x94, 0x94, 0x4E, 0x4E, + 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4B, 0x4B, 0x4B, 0x4B, + 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x80, 0xC1, 0xC1 + +}; + +//??? flags overlap flags2 +//loc_9FDA +const unsigned char flags2[] = { + 0x80, 0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x08, 0x0C, 0x08, 0x04, 0x40, + 0x24, 0x20, 0x20, 0x24, 0x00, 0x00, 0x24, 0x20, 0x20, 0x24, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +//tab45616??? +const unsigned char phonemeStressedLengthTable[] = { + 0x00, 0x12, 0x12, 0x12, 8, 0xB, 9, 0xB, 0xE, 0xF, 0xB, 0x10, 0xC, 6, 6, 0xE, + 0xC, 0xE, 0xC, 0xB, 8, 8, 0xB, 0xA, 9, 8, 8, 8, 8, 8, 3, 5, + 2, 2, 2, 2, 2, 2, 6, 6, 8, 6, 6, 2, 9, 4, 2, 1, + 0xE, 0xF, 0xF, 0xF, 0xE, 0xE, 8, 2, 2, 7, 2, 1, 7, 2, 2, 7, + 2, 2, 8, 2, 2, 6, 2, 2, 7, 2, 4, 7, 1, 4, 5, 5}; + +//tab45536??? +const unsigned char phonemeLengthTable[] = { + 0, 0x12, 0x12, 0x12, 8, 8, 8, 8, 8, 0xB, 6, 0xC, 0xA, 5, 5, 0xB, 0xA, 0xA, 0xA, 9, + 8, 7, 9, 7, 6, 8, 6, 7, 7, 7, 2, 5, 2, 2, 2, 2, 2, 2, 6, 6, + 7, 6, 6, 2, 8, 3, 1, 0x1E, 0xD, 0xC, 0xC, 0xC, 0xE, 9, 6, 1, 2, 5, 1, 1, + 6, 1, 2, 6, 1, 2, 8, 2, 2, 4, 2, 2, 6, 1, 4, 6, 1, 4, 0xC7, 0xFF}; + +/* + + Ind | phoneme | flags | + -----|---------|----------| + 0 | * | 00000000 | + 1 | .* | 00000000 | + 2 | ?* | 00000000 | + 3 | ,* | 00000000 | + 4 | -* | 00000000 | + + VOWELS + 5 | IY | 10100100 | + 6 | IH | 10100100 | + 7 | EH | 10100100 | + 8 | AE | 10100100 | + 9 | AA | 10100100 | + 10 | AH | 10100100 | + 11 | AO | 10000100 | + 17 | OH | 10000100 | + 12 | UH | 10000100 | + 16 | UX | 10000100 | + 15 | ER | 10000100 | + 13 | AX | 10100100 | + 14 | IX | 10100100 | + + DIPHTONGS + 48 | EY | 10110100 | + 49 | AY | 10110100 | + 50 | OY | 10110100 | + 51 | AW | 10010100 | + 52 | OW | 10010100 | + 53 | UW | 10010100 | + + + 21 | YX | 10000100 | + 20 | WX | 10000100 | + 18 | RX | 10000100 | + 19 | LX | 10000100 | + 37 | /X | 01000000 | + 30 | DX | 01001000 | + + + 22 | WH | 01000100 | + + + VOICED CONSONANTS + 23 | R* | 01000100 | + 24 | L* | 01000100 | + 25 | W* | 01000100 | + 26 | Y* | 01000100 | + 27 | M* | 01001100 | + 28 | N* | 01001100 | + 29 | NX | 01001100 | + 54 | B* | 01001110 | + 57 | D* | 01001110 | + 60 | G* | 01001110 | + 44 | J* | 01001100 | + 38 | Z* | 01000100 | + 39 | ZH | 01000100 | + 40 | V* | 01000100 | + 41 | DH | 01000100 | + + unvoiced CONSONANTS + 32 | S* | 01000000 | + 33 | SH | 01000000 | + 34 | F* | 01000000 | + 35 | TH | 01000000 | + 66 | P* | 01001011 | + 69 | T* | 01001011 | + 72 | K* | 01001011 | + 42 | CH | 01001000 | + 36 | /H | 01000000 | + + 43 | ** | 01000000 | + 45 | ** | 01000100 | + 46 | ** | 00000000 | + 47 | ** | 00000000 | + + + 55 | ** | 01001110 | + 56 | ** | 01001110 | + 58 | ** | 01001110 | + 59 | ** | 01001110 | + 61 | ** | 01001110 | + 62 | ** | 01001110 | + 63 | GX | 01001110 | + 64 | ** | 01001110 | + 65 | ** | 01001110 | + 67 | ** | 01001011 | + 68 | ** | 01001011 | + 70 | ** | 01001011 | + 71 | ** | 01001011 | + 73 | ** | 01001011 | + 74 | ** | 01001011 | + 75 | KX | 01001011 | + 76 | ** | 01001011 | + 77 | ** | 01001011 | + + + SPECIAL + 78 | UL | 10000000 | + 79 | UM | 11000001 | + 80 | UN | 11000001 | + 31 | Q* | 01001100 | + +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// RenderTabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +const unsigned char tab48426[5] = {0x18, 0x1A, 0x17, 0x17, 0x17}; + +const unsigned char tab47492[] = {0, 0, 0xE0, 0xE6, 0xEC, 0xF3, 0xF9, 0, 6, 0xC, 6}; + +const unsigned char amplitudeRescale[] = { + 0, + 1, + 2, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 6, + 8, + 9, + 0xB, + 0xD, + 0xF, + 0 //17 elements? +}; + +// Used to decide which phoneme's blend lengths. The candidate with the lower score is selected. +// tab45856 +const unsigned char blendRank[] = {0, 0x1F, 0x1F, 0x1F, 0x1F, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 5, 5, 2, 0xA, 2, 8, + 5, 5, 0xB, 0xA, 9, 8, 8, 0xA0, 8, 8, + 0x17, 0x1F, 0x12, 0x12, 0x12, 0x12, 0x1E, 0x1E, 0x14, 0x14, + 0x14, 0x14, 0x17, 0x17, 0x1A, 0x1A, 0x1D, 0x1D, 2, 2, + 2, 2, 2, 2, 0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, + 0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, 0x17, 0x1D, 0x17, 0x17, + 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x17}; + +// Number of frames at the end of a phoneme devoted to interpolating to next phoneme's final value +//tab45696 +const unsigned char outBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 3, 2, 4, 4, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 1, 0, 1, 0, 5, + 5, 5, 5, 5, 4, 4, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, + 0, 1, 2, 0, 2, 2, 0, 1, 3, 0, 2, 3, 0, 2, 0xA0, 0xA0}; + +// Number of frames at beginning of a phoneme devoted to interpolating to phoneme's final value +// tab45776 +const unsigned char inBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 1, 2, 3, 2, 1, + 3, 3, 3, 3, 1, 1, 3, 3, 3, 2, 2, 3, 2, 3, 0, 0, + 5, 5, 5, 5, 4, 4, 2, 0, 2, 2, 0, 3, 2, 0, 4, 2, + 0, 3, 2, 0, 2, 2, 0, 2, 3, 0, 3, 3, 0, 3, 0xB0, 0xA0}; + +// Looks like it's used as bit flags +// High bits masked by 248 (11111000) +// +// 32: S* 241 11110001 +// 33: SH 226 11100010 +// 34: F* 211 11010011 +// 35: TH 187 10111011 +// 36: /H 124 01111100 +// 37: /X 149 10010101 +// 38: Z* 1 00000001 +// 39: ZH 2 00000010 +// 40: V* 3 00000011 +// 41: DH 3 00000011 +// 43: ** 114 01110010 +// 45: ** 2 00000010 +// 67: ** 27 00011011 +// 70: ** 25 00011001 +// tab45936 +const unsigned char sampledConsonantFlags[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xF1, 0xE2, 0xD3, 0xBB, 0x7C, 0x95, 1, 2, + 3, 3, 0, 0x72, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1B, 0, 0, 0x19, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +//tab45056 +unsigned char freq1data[] = { + 0x00, 0x13, 0x13, 0x13, 0x13, 0xA, 0xE, 0x12, 0x18, 0x1A, 0x16, 0x14, 0x10, 0x14, 0xE, 0x12, + 0xE, 0x12, 0x12, 0x10, 0xC, 0xE, 0xA, 0x12, 0xE, 0xA, 8, 6, 6, 6, 6, 0x11, + 6, 6, 6, 6, 0xE, 0x10, 9, 0xA, 8, 0xA, 6, 6, 6, 5, 6, 0, + 0x12, 0x1A, 0x14, 0x1A, 0x12, 0xC, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 0xA, 0xA, 6, 6, 6, 0x2C, 0x13}; + +//tab451356 +unsigned char freq2data[] = {0x00, 0x43, 0x43, 0x43, 0x43, 0x54, 0x48, 0x42, 0x3E, 0x28, + 0x2C, 0x1E, 0x24, 0x2C, 0x48, 0x30, 0x24, 0x1E, 0x32, 0x24, + 0x1C, 0x44, 0x18, 0x32, 0x1E, 0x18, 0x52, 0x2E, 0x36, 0x56, + 0x36, 0x43, 0x49, 0x4F, 0x1A, 0x42, 0x49, 0x25, 0x33, 0x42, + 0x28, 0x2F, 0x4F, 0x4F, 0x42, 0x4F, 0x6E, 0x00, 0x48, 0x26, + 0x1E, 0x2A, 0x1E, 0x22, 0x1A, 0x1A, 0x1A, 0x42, 0x42, 0x42, + 0x6E, 0x6E, 0x6E, 0x54, 0x54, 0x54, 0x1A, 0x1A, 0x1A, 0x42, + 0x42, 0x42, 0x6D, 0x56, 0x6D, 0x54, 0x54, 0x54, 0x7F, 0x7F}; +//tab45216 +unsigned char freq3data[] = {0x00, 0x5B, 0x5B, 0x5B, 0x5B, 0x6E, 0x5D, 0x5B, 0x58, 0x59, + 0x57, 0x58, 0x52, 0x59, 0x5D, 0x3E, 0x52, 0x58, 0x3E, 0x6E, + 0x50, 0x5D, 0x5A, 0x3C, 0x6E, 0x5A, 0x6E, 0x51, 0x79, 0x65, + 0x79, 0x5B, 0x63, 0x6A, 0x51, 0x79, 0x5D, 0x52, 0x5D, 0x67, + 0x4C, 0x5D, 0x65, 0x65, 0x79, 0x65, 0x79, 0x00, 0x5A, 0x58, + 0x58, 0x58, 0x58, 0x52, 0x51, 0x51, 0x51, 0x79, 0x79, 0x79, + 0x70, 0x6E, 0x6E, 0x5E, 0x5E, 0x5E, 0x51, 0x51, 0x51, 0x79, + 0x79, 0x79, 0x65, 0x65, 0x70, 0x5E, 0x5E, 0x5E, 0x08, 0x01}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Reciter +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char inputtemp[256]; // secure copy of input tab36096 + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Render +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//timetable for more accurate c64 simulation +int timetable[5][5] = { + {162, 167, 167, 127, 128}, + {226, 60, 60, 0, 0}, + {225, 60, 59, 0, 0}, + {200, 0, 0, 54, 55}, + {199, 0, 0, 54, 54}}; + +unsigned oldtimetableindex; + +const unsigned char ampl1data[] = {0, 0, 0, 0, 0, 0xD, 0xD, 0xE, 0xF, 0xF, 0xF, 0xF, + 0xF, 0xC, 0xD, 0xC, 0xF, 0xF, 0xD, 0xD, 0xD, 0xE, 0xD, 0xC, + 0xD, 0xD, 0xD, 0xC, 9, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0xB, 0xB, 0xB, 0xB, 0, 0, 1, 0xB, 0, 2, + 0xE, 0xF, 0xF, 0xF, 0xF, 0xD, 2, 4, 0, 2, 4, 0, + 1, 4, 0, 1, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0xC, 0, 0, 0, 0, 0xF, 0xF}; + +const unsigned char ampl2data[] = { + 0, 0, 0, 0, 0, 0xA, 0xB, 0xD, 0xE, 0xD, 0xC, 0xC, 0xB, 9, 0xB, 0xB, 0xC, 0xC, 0xC, 8, + 8, 0xC, 8, 0xA, 8, 8, 0xA, 3, 9, 6, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, + 3, 4, 0, 0, 0, 5, 0xA, 2, 0xE, 0xD, 0xC, 0xD, 0xC, 8, 0, 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0xA, 0, 0, 0xA, 0, 0, 0}; + +const unsigned char ampl3data[] = {0, 0, 0, 0, 0, 8, 7, 8, 8, 1, 1, 0, 1, 0, 7, 5, + 1, 0, 6, 1, 0, 7, 0, 5, 1, 0, 8, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0xE, 1, + 9, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 5, 0, 0x13, 0x10}; + +//tab42240 +const signed char sinus[256] = { + 0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, + 49, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, + 90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, 116, + 117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127, + 127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 122, 121, 120, 118, + 117, 116, 115, 113, 112, 111, 109, 107, 106, 104, 102, 100, 98, 96, 94, 92, + 90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51, + 49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 12, 9, 6, 3, + 0, -3, -6, -9, -12, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46, + -49, -51, -54, -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88, + -90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, -112, -113, -115, -116, + -117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127, + -127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118, + -117, -116, -115, -113, -112, -111, -109, -107, -106, -104, -102, -100, -98, -96, -94, -92, + -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51, + -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3}; + +//tab42496 +const unsigned char rectangle[] = { + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, + 0x70}; + +//random data ? +const unsigned char sampleTable[0x500] = { + //00 + + 0x38, + 0x84, + 0x6B, + 0x19, + 0xC6, + 0x63, + 0x18, + 0x86, + 0x73, + 0x98, + 0xC6, + 0xB1, + 0x1C, + 0xCA, + 0x31, + 0x8C, + 0xC7, + 0x31, + 0x88, + 0xC2, + 0x30, + 0x98, + 0x46, + 0x31, + 0x18, + 0xC6, + 0x35, + 0xC, + 0xCA, + 0x31, + 0xC, + 0xC6 + //20 + , + 0x21, + 0x10, + 0x24, + 0x69, + 0x12, + 0xC2, + 0x31, + 0x14, + 0xC4, + 0x71, + 8, + 0x4A, + 0x22, + 0x49, + 0xAB, + 0x6A, + 0xA8, + 0xAC, + 0x49, + 0x51, + 0x32, + 0xD5, + 0x52, + 0x88, + 0x93, + 0x6C, + 0x94, + 0x22, + 0x15, + 0x54, + 0xD2, + 0x25 + //40 + , + 0x96, + 0xD4, + 0x50, + 0xA5, + 0x46, + 0x21, + 8, + 0x85, + 0x6B, + 0x18, + 0xC4, + 0x63, + 0x10, + 0xCE, + 0x6B, + 0x18, + 0x8C, + 0x71, + 0x19, + 0x8C, + 0x63, + 0x35, + 0xC, + 0xC6, + 0x33, + 0x99, + 0xCC, + 0x6C, + 0xB5, + 0x4E, + 0xA2, + 0x99 + //60 + , + 0x46, + 0x21, + 0x28, + 0x82, + 0x95, + 0x2E, + 0xE3, + 0x30, + 0x9C, + 0xC5, + 0x30, + 0x9C, + 0xA2, + 0xB1, + 0x9C, + 0x67, + 0x31, + 0x88, + 0x66, + 0x59, + 0x2C, + 0x53, + 0x18, + 0x84, + 0x67, + 0x50, + 0xCA, + 0xE3, + 0xA, + 0xAC, + 0xAB, + 0x30 + //80 + , + 0xAC, + 0x62, + 0x30, + 0x8C, + 0x63, + 0x10, + 0x94, + 0x62, + 0xB1, + 0x8C, + 0x82, + 0x28, + 0x96, + 0x33, + 0x98, + 0xD6, + 0xB5, + 0x4C, + 0x62, + 0x29, + 0xA5, + 0x4A, + 0xB5, + 0x9C, + 0xC6, + 0x31, + 0x14, + 0xD6, + 0x38, + 0x9C, + 0x4B, + 0xB4 + //A0 + , + 0x86, + 0x65, + 0x18, + 0xAE, + 0x67, + 0x1C, + 0xA6, + 0x63, + 0x19, + 0x96, + 0x23, + 0x19, + 0x84, + 0x13, + 8, + 0xA6, + 0x52, + 0xAC, + 0xCA, + 0x22, + 0x89, + 0x6E, + 0xAB, + 0x19, + 0x8C, + 0x62, + 0x34, + 0xC4, + 0x62, + 0x19, + 0x86, + 0x63 + //C0 + , + 0x18, + 0xC4, + 0x23, + 0x58, + 0xD6, + 0xA3, + 0x50, + 0x42, + 0x54, + 0x4A, + 0xAD, + 0x4A, + 0x25, + 0x11, + 0x6B, + 0x64, + 0x89, + 0x4A, + 0x63, + 0x39, + 0x8A, + 0x23, + 0x31, + 0x2A, + 0xEA, + 0xA2, + 0xA9, + 0x44, + 0xC5, + 0x12, + 0xCD, + 0x42 + //E0 + , + 0x34, + 0x8C, + 0x62, + 0x18, + 0x8C, + 0x63, + 0x11, + 0x48, + 0x66, + 0x31, + 0x9D, + 0x44, + 0x33, + 0x1D, + 0x46, + 0x31, + 0x9C, + 0xC6, + 0xB1, + 0xC, + 0xCD, + 0x32, + 0x88, + 0xC4, + 0x73, + 0x18, + 0x86, + 0x73, + 8, + 0xD6, + 0x63, + 0x58 + //100 + , + 7, + 0x81, + 0xE0, + 0xF0, + 0x3C, + 7, + 0x87, + 0x90, + 0x3C, + 0x7C, + 0xF, + 0xC7, + 0xC0, + 0xC0, + 0xF0, + 0x7C, + 0x1E, + 7, + 0x80, + 0x80, + 0, + 0x1C, + 0x78, + 0x70, + 0xF1, + 0xC7, + 0x1F, + 0xC0, + 0xC, + 0xFE, + 0x1C, + 0x1F + //120 + , + 0x1F, + 0xE, + 0xA, + 0x7A, + 0xC0, + 0x71, + 0xF2, + 0x83, + 0x8F, + 3, + 0xF, + 0xF, + 0xC, + 0, + 0x79, + 0xF8, + 0x61, + 0xE0, + 0x43, + 0xF, + 0x83, + 0xE7, + 0x18, + 0xF9, + 0xC1, + 0x13, + 0xDA, + 0xE9, + 0x63, + 0x8F, + 0xF, + 0x83 + //140 + , + 0x83, + 0x87, + 0xC3, + 0x1F, + 0x3C, + 0x70, + 0xF0, + 0xE1, + 0xE1, + 0xE3, + 0x87, + 0xB8, + 0x71, + 0xE, + 0x20, + 0xE3, + 0x8D, + 0x48, + 0x78, + 0x1C, + 0x93, + 0x87, + 0x30, + 0xE1, + 0xC1, + 0xC1, + 0xE4, + 0x78, + 0x21, + 0x83, + 0x83, + 0xC3 + //160 + , + 0x87, + 6, + 0x39, + 0xE5, + 0xC3, + 0x87, + 7, + 0xE, + 0x1C, + 0x1C, + 0x70, + 0xF4, + 0x71, + 0x9C, + 0x60, + 0x36, + 0x32, + 0xC3, + 0x1E, + 0x3C, + 0xF3, + 0x8F, + 0xE, + 0x3C, + 0x70, + 0xE3, + 0xC7, + 0x8F, + 0xF, + 0xF, + 0xE, + 0x3C + //180 + , + 0x78, + 0xF0, + 0xE3, + 0x87, + 6, + 0xF0, + 0xE3, + 7, + 0xC1, + 0x99, + 0x87, + 0xF, + 0x18, + 0x78, + 0x70, + 0x70, + 0xFC, + 0xF3, + 0x10, + 0xB1, + 0x8C, + 0x8C, + 0x31, + 0x7C, + 0x70, + 0xE1, + 0x86, + 0x3C, + 0x64, + 0x6C, + 0xB0, + 0xE1 + //1A0 + , + 0xE3, + 0xF, + 0x23, + 0x8F, + 0xF, + 0x1E, + 0x3E, + 0x38, + 0x3C, + 0x38, + 0x7B, + 0x8F, + 7, + 0xE, + 0x3C, + 0xF4, + 0x17, + 0x1E, + 0x3C, + 0x78, + 0xF2, + 0x9E, + 0x72, + 0x49, + 0xE3, + 0x25, + 0x36, + 0x38, + 0x58, + 0x39, + 0xE2, + 0xDE + //1C0 + , + 0x3C, + 0x78, + 0x78, + 0xE1, + 0xC7, + 0x61, + 0xE1, + 0xE1, + 0xB0, + 0xF0, + 0xF0, + 0xC3, + 0xC7, + 0xE, + 0x38, + 0xC0, + 0xF0, + 0xCE, + 0x73, + 0x73, + 0x18, + 0x34, + 0xB0, + 0xE1, + 0xC7, + 0x8E, + 0x1C, + 0x3C, + 0xF8, + 0x38, + 0xF0, + 0xE1 + //1E0 + , + 0xC1, + 0x8B, + 0x86, + 0x8F, + 0x1C, + 0x78, + 0x70, + 0xF0, + 0x78, + 0xAC, + 0xB1, + 0x8F, + 0x39, + 0x31, + 0xDB, + 0x38, + 0x61, + 0xC3, + 0xE, + 0xE, + 0x38, + 0x78, + 0x73, + 0x17, + 0x1E, + 0x39, + 0x1E, + 0x38, + 0x64, + 0xE1, + 0xF1, + 0xC1 + //200 + , + 0x4E, + 0xF, + 0x40, + 0xA2, + 2, + 0xC5, + 0x8F, + 0x81, + 0xA1, + 0xFC, + 0x12, + 8, + 0x64, + 0xE0, + 0x3C, + 0x22, + 0xE0, + 0x45, + 7, + 0x8E, + 0xC, + 0x32, + 0x90, + 0xF0, + 0x1F, + 0x20, + 0x49, + 0xE0, + 0xF8, + 0xC, + 0x60, + 0xF0 + //220 + , + 0x17, + 0x1A, + 0x41, + 0xAA, + 0xA4, + 0xD0, + 0x8D, + 0x12, + 0x82, + 0x1E, + 0x1E, + 3, + 0xF8, + 0x3E, + 3, + 0xC, + 0x73, + 0x80, + 0x70, + 0x44, + 0x26, + 3, + 0x24, + 0xE1, + 0x3E, + 4, + 0x4E, + 4, + 0x1C, + 0xC1, + 9, + 0xCC + //240 + , + 0x9E, + 0x90, + 0x21, + 7, + 0x90, + 0x43, + 0x64, + 0xC0, + 0xF, + 0xC6, + 0x90, + 0x9C, + 0xC1, + 0x5B, + 3, + 0xE2, + 0x1D, + 0x81, + 0xE0, + 0x5E, + 0x1D, + 3, + 0x84, + 0xB8, + 0x2C, + 0xF, + 0x80, + 0xB1, + 0x83, + 0xE0, + 0x30, + 0x41 + //260 + , + 0x1E, + 0x43, + 0x89, + 0x83, + 0x50, + 0xFC, + 0x24, + 0x2E, + 0x13, + 0x83, + 0xF1, + 0x7C, + 0x4C, + 0x2C, + 0xC9, + 0xD, + 0x83, + 0xB0, + 0xB5, + 0x82, + 0xE4, + 0xE8, + 6, + 0x9C, + 7, + 0xA0, + 0x99, + 0x1D, + 7, + 0x3E, + 0x82, + 0x8F + //280 + , + 0x70, + 0x30, + 0x74, + 0x40, + 0xCA, + 0x10, + 0xE4, + 0xE8, + 0xF, + 0x92, + 0x14, + 0x3F, + 6, + 0xF8, + 0x84, + 0x88, + 0x43, + 0x81, + 0xA, + 0x34, + 0x39, + 0x41, + 0xC6, + 0xE3, + 0x1C, + 0x47, + 3, + 0xB0, + 0xB8, + 0x13, + 0xA, + 0xC2 + //2A0 + , + 0x64, + 0xF8, + 0x18, + 0xF9, + 0x60, + 0xB3, + 0xC0, + 0x65, + 0x20, + 0x60, + 0xA6, + 0x8C, + 0xC3, + 0x81, + 0x20, + 0x30, + 0x26, + 0x1E, + 0x1C, + 0x38, + 0xD3, + 1, + 0xB0, + 0x26, + 0x40, + 0xF4, + 0xB, + 0xC3, + 0x42, + 0x1F, + 0x85, + 0x32 + //2C0 + , + 0x26, + 0x60, + 0x40, + 0xC9, + 0xCB, + 1, + 0xEC, + 0x11, + 0x28, + 0x40, + 0xFA, + 4, + 0x34, + 0xE0, + 0x70, + 0x4C, + 0x8C, + 0x1D, + 7, + 0x69, + 3, + 0x16, + 0xC8, + 4, + 0x23, + 0xE8, + 0xC6, + 0x9A, + 0xB, + 0x1A, + 3, + 0xE0 + //2E0 + , + 0x76, + 6, + 5, + 0xCF, + 0x1E, + 0xBC, + 0x58, + 0x31, + 0x71, + 0x66, + 0, + 0xF8, + 0x3F, + 4, + 0xFC, + 0xC, + 0x74, + 0x27, + 0x8A, + 0x80, + 0x71, + 0xC2, + 0x3A, + 0x26, + 6, + 0xC0, + 0x1F, + 5, + 0xF, + 0x98, + 0x40, + 0xAE + //300 + , + 1, + 0x7F, + 0xC0, + 7, + 0xFF, + 0, + 0xE, + 0xFE, + 0, + 3, + 0xDF, + 0x80, + 3, + 0xEF, + 0x80, + 0x1B, + 0xF1, + 0xC2, + 0, + 0xE7, + 0xE0, + 0x18, + 0xFC, + 0xE0, + 0x21, + 0xFC, + 0x80, + 0x3C, + 0xFC, + 0x40, + 0xE, + 0x7E + //320 + , + 0, + 0x3F, + 0x3E, + 0, + 0xF, + 0xFE, + 0, + 0x1F, + 0xFF, + 0, + 0x3E, + 0xF0, + 7, + 0xFC, + 0, + 0x7E, + 0x10, + 0x3F, + 0xFF, + 0, + 0x3F, + 0x38, + 0xE, + 0x7C, + 1, + 0x87, + 0xC, + 0xFC, + 0xC7, + 0, + 0x3E, + 4 + //340 + , + 0xF, + 0x3E, + 0x1F, + 0xF, + 0xF, + 0x1F, + 0xF, + 2, + 0x83, + 0x87, + 0xCF, + 3, + 0x87, + 0xF, + 0x3F, + 0xC0, + 7, + 0x9E, + 0x60, + 0x3F, + 0xC0, + 3, + 0xFE, + 0, + 0x3F, + 0xE0, + 0x77, + 0xE1, + 0xC0, + 0xFE, + 0xE0, + 0xC3 + //360 + , + 0xE0, + 1, + 0xDF, + 0xF8, + 3, + 7, + 0, + 0x7E, + 0x70, + 0, + 0x7C, + 0x38, + 0x18, + 0xFE, + 0xC, + 0x1E, + 0x78, + 0x1C, + 0x7C, + 0x3E, + 0xE, + 0x1F, + 0x1E, + 0x1E, + 0x3E, + 0, + 0x7F, + 0x83, + 7, + 0xDB, + 0x87, + 0x83 + //380 + , + 7, + 0xC7, + 7, + 0x10, + 0x71, + 0xFF, + 0, + 0x3F, + 0xE2, + 1, + 0xE0, + 0xC1, + 0xC3, + 0xE1, + 0, + 0x7F, + 0xC0, + 5, + 0xF0, + 0x20, + 0xF8, + 0xF0, + 0x70, + 0xFE, + 0x78, + 0x79, + 0xF8, + 2, + 0x3F, + 0xC, + 0x8F, + 3 + //3a0 + , + 0xF, + 0x9F, + 0xE0, + 0xC1, + 0xC7, + 0x87, + 3, + 0xC3, + 0xC3, + 0xB0, + 0xE1, + 0xE1, + 0xC1, + 0xE3, + 0xE0, + 0x71, + 0xF0, + 0, + 0xFC, + 0x70, + 0x7C, + 0xC, + 0x3E, + 0x38, + 0xE, + 0x1C, + 0x70, + 0xC3, + 0xC7, + 3, + 0x81, + 0xC1 + //3c0 + , + 0xC7, + 0xE7, + 0, + 0xF, + 0xC7, + 0x87, + 0x19, + 9, + 0xEF, + 0xC4, + 0x33, + 0xE0, + 0xC1, + 0xFC, + 0xF8, + 0x70, + 0xF0, + 0x78, + 0xF8, + 0xF0, + 0x61, + 0xC7, + 0, + 0x1F, + 0xF8, + 1, + 0x7C, + 0xF8, + 0xF0, + 0x78, + 0x70, + 0x3C + //3e0 + , + 0x7C, + 0xCE, + 0xE, + 0x21, + 0x83, + 0xCF, + 8, + 7, + 0x8F, + 8, + 0xC1, + 0x87, + 0x8F, + 0x80, + 0xC7, + 0xE3, + 0, + 7, + 0xF8, + 0xE0, + 0xEF, + 0, + 0x39, + 0xF7, + 0x80, + 0xE, + 0xF8, + 0xE1, + 0xE3, + 0xF8, + 0x21, + 0x9F + //400 + , + 0xC0, + 0xFF, + 3, + 0xF8, + 7, + 0xC0, + 0x1F, + 0xF8, + 0xC4, + 4, + 0xFC, + 0xC4, + 0xC1, + 0xBC, + 0x87, + 0xF0, + 0xF, + 0xC0, + 0x7F, + 5, + 0xE0, + 0x25, + 0xEC, + 0xC0, + 0x3E, + 0x84, + 0x47, + 0xF0, + 0x8E, + 3, + 0xF8, + 3 + //420 + , + 0xFB, + 0xC0, + 0x19, + 0xF8, + 7, + 0x9C, + 0xC, + 0x17, + 0xF8, + 7, + 0xE0, + 0x1F, + 0xA1, + 0xFC, + 0xF, + 0xFC, + 1, + 0xF0, + 0x3F, + 0, + 0xFE, + 3, + 0xF0, + 0x1F, + 0, + 0xFD, + 0, + 0xFF, + 0x88, + 0xD, + 0xF9, + 1 + //440 + , + 0xFF, + 0, + 0x70, + 7, + 0xC0, + 0x3E, + 0x42, + 0xF3, + 0xD, + 0xC4, + 0x7F, + 0x80, + 0xFC, + 7, + 0xF0, + 0x5E, + 0xC0, + 0x3F, + 0, + 0x78, + 0x3F, + 0x81, + 0xFF, + 1, + 0xF8, + 1, + 0xC3, + 0xE8, + 0xC, + 0xE4, + 0x64, + 0x8F + ////460 + , + 0xE4, + 0xF, + 0xF0, + 7, + 0xF0, + 0xC2, + 0x1F, + 0, + 0x7F, + 0xC0, + 0x6F, + 0x80, + 0x7E, + 3, + 0xF8, + 7, + 0xF0, + 0x3F, + 0xC0, + 0x78, + 0xF, + 0x82, + 7, + 0xFE, + 0x22, + 0x77, + 0x70, + 2, + 0x76, + 3, + 0xFE, + 0 + //480 + , + 0xFE, + 0x67, + 0, + 0x7C, + 0xC7, + 0xF1, + 0x8E, + 0xC6, + 0x3B, + 0xE0, + 0x3F, + 0x84, + 0xF3, + 0x19, + 0xD8, + 3, + 0x99, + 0xFC, + 9, + 0xB8, + 0xF, + 0xF8, + 0, + 0x9D, + 0x24, + 0x61, + 0xF9, + 0xD, + 0, + 0xFD, + 3, + 0xF0 + //4a0 + , + 0x1F, + 0x90, + 0x3F, + 1, + 0xF8, + 0x1F, + 0xD0, + 0xF, + 0xF8, + 0x37, + 1, + 0xF8, + 7, + 0xF0, + 0xF, + 0xC0, + 0x3F, + 0, + 0xFE, + 3, + 0xF8, + 0xF, + 0xC0, + 0x3F, + 0, + 0xFA, + 3, + 0xF0, + 0xF, + 0x80, + 0xFF, + 1 + //4c0 + , + 0xB8, + 7, + 0xF0, + 1, + 0xFC, + 1, + 0xBC, + 0x80, + 0x13, + 0x1E, + 0, + 0x7F, + 0xE1, + 0x40, + 0x7F, + 0xA0, + 0x7F, + 0xB0, + 0, + 0x3F, + 0xC0, + 0x1F, + 0xC0, + 0x38, + 0xF, + 0xF0, + 0x1F, + 0x80, + 0xFF, + 1, + 0xFC, + 3 + //4e0 + , + 0xF1, + 0x7E, + 1, + 0xFE, + 1, + 0xF0, + 0xFF, + 0, + 0x7F, + 0xC0, + 0x1D, + 7, + 0xF0, + 0xF, + 0xC0, + 0x7E, + 6, + 0xE0, + 7, + 0xE0, + 0xF, + 0xF8, + 6, + 0xC1, + 0xFE, + 1, + 0xFC, + 3, + 0xE0, + 0xF, + 0, + 0xFC}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Render +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char pitches[256]; // tab43008 + +unsigned char frequency1[256]; +unsigned char frequency2[256]; +unsigned char frequency3[256]; + +unsigned char amplitude1[256]; +unsigned char amplitude2[256]; +unsigned char amplitude3[256]; + +unsigned char sampledConsonantFlag[256]; // tab44800 + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam +// +//////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char stress[256]; //numbers from 0 to 8 +unsigned char phonemeLength[256]; //tab40160 +unsigned char phonemeindex[256]; + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// ReciterTabs +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//some flags +const unsigned char tab36376[] = { + 0, 0, 0, 0, 0, 0, 0, 0, // 0-7 + 0, 0, 0, 0, 0, 0, 0, 0, // 8-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 130, // ' ', '!' + 0, 0, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 2, 2, 2, 2, 2, 2, 2, 192, 168, 176, 172, 192, 160, 184, // '@', 'A' + 160, 192, 188, 160, 172, 168, 172, 192, 160, 160, 172, 180, 164, 192, 168, 168, + 176, 192, 188, 0, 0, 0, 2, 0, // 'X', 'Y', 'Z', '[', + 32, 32, 155, 32, 192, 185, 32, 205, 163, 76, 138, 142}; + +const unsigned char rules[] = { + ']', 'A' | 0x80, ' ', '(', 'A', '.', ')', '=', + 'E', 'H', '4', 'Y', '.', ' ' | 0x80, '(', 'A', + ')', ' ', '=', 'A', 'H' | 0x80, ' ', '(', 'A', + 'R', 'E', ')', ' ', '=', 'A', 'A', 'R' | 0x80, + ' ', '(', 'A', 'R', ')', 'O', '=', 'A', + 'X', 'R' | 0x80, '(', 'A', 'R', ')', '#', '=', + 'E', 'H', '4', 'R' | 0x80, ' ', '^', '(', 'A', + 'S', ')', '#', '=', 'E', 'Y', '4', 'S' | 0x80, + '(', 'A', ')', 'W', 'A', '=', 'A', 'X' | 0x80, + '(', 'A', 'W', ')', '=', 'A', 'O', '5' | 0x80, + ' ', ':', '(', 'A', 'N', 'Y', ')', '=', + 'E', 'H', '4', 'N', 'I', 'Y' | 0x80, '(', 'A', + ')', '^', '+', '#', '=', 'E', 'Y', '5' | 0x80, + '#', ':', '(', 'A', 'L', 'L', 'Y', ')', + '=', 'U', 'L', 'I', 'Y' | 0x80, ' ', '(', 'A', + 'L', ')', '#', '=', 'U', 'L' | 0x80, '(', 'A', + 'G', 'A', 'I', 'N', ')', '=', 'A', 'X', + 'G', 'E', 'H', '4', 'N' | 0x80, '#', ':', '(', + 'A', 'G', ')', 'E', '=', 'I', 'H', 'J' | 0x80, + '(', 'A', ')', '^', '%', '=', 'E', 'Y' | 0x80, + '(', 'A', ')', '^', '+', ':', '#', '=', + 'A', 'E' | 0x80, ' ', ':', '(', 'A', ')', '^', + '+', ' ', '=', 'E', 'Y', '4' | 0x80, ' ', '(', + 'A', 'R', 'R', ')', '=', 'A', 'X', 'R' | 0x80, + '(', 'A', 'R', 'R', ')', '=', 'A', 'E', + '4', 'R' | 0x80, ' ', '^', '(', 'A', 'R', ')', + ' ', '=', 'A', 'A', '5', 'R' | 0x80, '(', 'A', + 'R', ')', '=', 'A', 'A', '5', 'R' | 0x80, '(', + 'A', 'I', 'R', ')', '=', 'E', 'H', '4', + 'R' | 0x80, '(', 'A', 'I', ')', '=', 'E', 'Y', + '4' | 0x80, '(', 'A', 'Y', ')', '=', 'E', 'Y', + '5' | 0x80, '(', 'A', 'U', ')', '=', 'A', 'O', + '4' | 0x80, '#', ':', '(', 'A', 'L', ')', ' ', + '=', 'U', 'L' | 0x80, '#', ':', '(', 'A', 'L', + 'S', ')', ' ', '=', 'U', 'L', 'Z' | 0x80, '(', + 'A', 'L', 'K', ')', '=', 'A', 'O', '4', + 'K' | 0x80, '(', 'A', 'L', ')', '^', '=', 'A', + 'O', 'L' | 0x80, ' ', ':', '(', 'A', 'B', 'L', + 'E', ')', '=', 'E', 'Y', '4', 'B', 'U', + 'L' | 0x80, '(', 'A', 'B', 'L', 'E', ')', '=', + 'A', 'X', 'B', 'U', 'L' | 0x80, '(', 'A', ')', + 'V', 'O', '=', 'E', 'Y', '4' | 0x80, '(', 'A', + 'N', 'G', ')', '+', '=', 'E', 'Y', '4', + 'N', 'J' | 0x80, '(', 'A', 'T', 'A', 'R', 'I', + ')', '=', 'A', 'H', 'T', 'A', 'A', '4', + 'R', 'I', 'Y' | 0x80, '(', 'A', ')', 'T', 'O', + 'M', '=', 'A', 'E' | 0x80, '(', 'A', ')', 'T', + 'T', 'I', '=', 'A', 'E' | 0x80, ' ', '(', 'A', + 'T', ')', ' ', '=', 'A', 'E', 'T' | 0x80, ' ', + '(', 'A', ')', 'T', '=', 'A', 'H' | 0x80, '(', + 'A', ')', '=', 'A', 'E' | 0x80, + + ']', 'B' | 0x80, ' ', '(', 'B', ')', ' ', '=', + 'B', 'I', 'Y', '4' | 0x80, ' ', '(', 'B', 'E', + ')', '^', '#', '=', 'B', 'I', 'H' | 0x80, '(', + 'B', 'E', 'I', 'N', 'G', ')', '=', 'B', + 'I', 'Y', '4', 'I', 'H', 'N', 'X' | 0x80, ' ', + '(', 'B', 'O', 'T', 'H', ')', ' ', '=', + 'B', 'O', 'W', '4', 'T', 'H' | 0x80, ' ', '(', + 'B', 'U', 'S', ')', '#', '=', 'B', 'I', + 'H', '4', 'Z' | 0x80, '(', 'B', 'R', 'E', 'A', + 'K', ')', '=', 'B', 'R', 'E', 'Y', '5', + 'K' | 0x80, '(', 'B', 'U', 'I', 'L', ')', '=', + 'B', 'I', 'H', '4', 'L' | 0x80, '(', 'B', ')', + '=', 'B' | 0x80, + + ']', 'C' | 0x80, ' ', '(', 'C', ')', ' ', '=', + 'S', 'I', 'Y', '4' | 0x80, ' ', '(', 'C', 'H', + ')', '^', '=', 'K' | 0x80, '^', 'E', '(', 'C', + 'H', ')', '=', 'K' | 0x80, '(', 'C', 'H', 'A', + ')', 'R', '#', '=', 'K', 'E', 'H', '5' | 0x80, + '(', 'C', 'H', ')', '=', 'C', 'H' | 0x80, ' ', + 'S', '(', 'C', 'I', ')', '#', '=', 'S', + 'A', 'Y', '4' | 0x80, '(', 'C', 'I', ')', 'A', + '=', 'S', 'H' | 0x80, '(', 'C', 'I', ')', 'O', + '=', 'S', 'H' | 0x80, '(', 'C', 'I', ')', 'E', + 'N', '=', 'S', 'H' | 0x80, '(', 'C', 'I', 'T', + 'Y', ')', '=', 'S', 'I', 'H', 'T', 'I', + 'Y' | 0x80, '(', 'C', ')', '+', '=', 'S' | 0x80, '(', + 'C', 'K', ')', '=', 'K' | 0x80, '(', 'C', 'O', + 'M', 'M', 'O', 'D', 'O', 'R', 'E', ')', + '=', 'K', 'A', 'A', '4', 'M', 'A', 'H', + 'D', 'O', 'H', 'R' | 0x80, '(', 'C', 'O', 'M', + ')', '=', 'K', 'A', 'H', 'M' | 0x80, '(', 'C', + 'U', 'I', 'T', ')', '=', 'K', 'I', 'H', + 'T' | 0x80, '(', 'C', 'R', 'E', 'A', ')', '=', + 'K', 'R', 'I', 'Y', 'E', 'Y' | 0x80, '(', 'C', + ')', '=', 'K' | 0x80, + + ']', 'D' | 0x80, ' ', '(', 'D', ')', ' ', '=', + 'D', 'I', 'Y', '4' | 0x80, ' ', '(', 'D', 'R', + '.', ')', ' ', '=', 'D', 'A', 'A', '4', + 'K', 'T', 'E', 'R' | 0x80, '#', ':', '(', 'D', + 'E', 'D', ')', ' ', '=', 'D', 'I', 'H', + 'D' | 0x80, '.', 'E', '(', 'D', ')', ' ', '=', + 'D' | 0x80, '#', ':', '^', 'E', '(', 'D', ')', + ' ', '=', 'T' | 0x80, ' ', '(', 'D', 'E', ')', + '^', '#', '=', 'D', 'I', 'H' | 0x80, ' ', '(', + 'D', 'O', ')', ' ', '=', 'D', 'U', 'W' | 0x80, + ' ', '(', 'D', 'O', 'E', 'S', ')', '=', + 'D', 'A', 'H', 'Z' | 0x80, '(', 'D', 'O', 'N', + 'E', ')', ' ', '=', 'D', 'A', 'H', '5', + 'N' | 0x80, '(', 'D', 'O', 'I', 'N', 'G', ')', + '=', 'D', 'U', 'W', '4', 'I', 'H', 'N', + 'X' | 0x80, ' ', '(', 'D', 'O', 'W', ')', '=', + 'D', 'A', 'W' | 0x80, '#', '(', 'D', 'U', ')', + 'A', '=', 'J', 'U', 'W' | 0x80, '#', '(', 'D', + 'U', ')', '^', '#', '=', 'J', 'A', 'X' | 0x80, + '(', 'D', ')', '=', 'D' | 0x80, + + ']', 'E' | 0x80, ' ', '(', 'E', ')', ' ', '=', + 'I', 'Y', 'I', 'Y', '4' | 0x80, '#', ':', '(', + 'E', ')', ' ', '=' | 0x80, '\'', ':', '^', '(', + 'E', ')', ' ', '=' | 0x80, ' ', ':', '(', 'E', + ')', ' ', '=', 'I', 'Y' | 0x80, '#', '(', 'E', + 'D', ')', ' ', '=', 'D' | 0x80, '#', ':', '(', + 'E', ')', 'D', ' ', '=' | 0x80, '(', 'E', 'V', + ')', 'E', 'R', '=', 'E', 'H', '4', 'V' | 0x80, + '(', 'E', ')', '^', '%', '=', 'I', 'Y', + '4' | 0x80, '(', 'E', 'R', 'I', ')', '#', '=', + 'I', 'Y', '4', 'R', 'I', 'Y' | 0x80, '(', 'E', + 'R', 'I', ')', '=', 'E', 'H', '4', 'R', + 'I', 'H' | 0x80, '#', ':', '(', 'E', 'R', ')', + '#', '=', 'E', 'R' | 0x80, '(', 'E', 'R', 'R', + 'O', 'R', ')', '=', 'E', 'H', '4', 'R', + 'O', 'H', 'R' | 0x80, '(', 'E', 'R', 'A', 'S', + 'E', ')', '=', 'I', 'H', 'R', 'E', 'Y', + '5', 'S' | 0x80, '(', 'E', 'R', ')', '#', '=', + 'E', 'H', 'R' | 0x80, '(', 'E', 'R', ')', '=', + 'E', 'R' | 0x80, ' ', '(', 'E', 'V', 'E', 'N', + ')', '=', 'I', 'Y', 'V', 'E', 'H', 'N' | 0x80, + '#', ':', '(', 'E', ')', 'W', '=' | 0x80, '@', + '(', 'E', 'W', ')', '=', 'U', 'W' | 0x80, '(', + 'E', 'W', ')', '=', 'Y', 'U', 'W' | 0x80, '(', + 'E', ')', 'O', '=', 'I', 'Y' | 0x80, '#', ':', + '&', '(', 'E', 'S', ')', ' ', '=', 'I', + 'H', 'Z' | 0x80, '#', ':', '(', 'E', ')', 'S', + ' ', '=' | 0x80, '#', ':', '(', 'E', 'L', 'Y', + ')', ' ', '=', 'L', 'I', 'Y' | 0x80, '#', ':', + '(', 'E', 'M', 'E', 'N', 'T', ')', '=', + 'M', 'E', 'H', 'N', 'T' | 0x80, '(', 'E', 'F', + 'U', 'L', ')', '=', 'F', 'U', 'H', 'L' | 0x80, + '(', 'E', 'E', ')', '=', 'I', 'Y', '4' | 0x80, + '(', 'E', 'A', 'R', 'N', ')', '=', 'E', + 'R', '5', 'N' | 0x80, ' ', '(', 'E', 'A', 'R', + ')', '^', '=', 'E', 'R', '5' | 0x80, '(', 'E', + 'A', 'D', ')', '=', 'E', 'H', 'D' | 0x80, '#', + ':', '(', 'E', 'A', ')', ' ', '=', 'I', + 'Y', 'A', 'X' | 0x80, '(', 'E', 'A', ')', 'S', + 'U', '=', 'E', 'H', '5' | 0x80, '(', 'E', 'A', + ')', '=', 'I', 'Y', '5' | 0x80, '(', 'E', 'I', + 'G', 'H', ')', '=', 'E', 'Y', '4' | 0x80, '(', + 'E', 'I', ')', '=', 'I', 'Y', '4' | 0x80, ' ', + '(', 'E', 'Y', 'E', ')', '=', 'A', 'Y', + '4' | 0x80, '(', 'E', 'Y', ')', '=', 'I', 'Y' | 0x80, + '(', 'E', 'U', ')', '=', 'Y', 'U', 'W', + '5' | 0x80, '(', 'E', 'Q', 'U', 'A', 'L', ')', + '=', 'I', 'Y', '4', 'K', 'W', 'U', 'L' | 0x80, + '(', 'E', ')', '=', 'E', 'H' | 0x80, + + ']', 'F' | 0x80, ' ', '(', 'F', ')', ' ', '=', + 'E', 'H', '4', 'F' | 0x80, '(', 'F', 'U', 'L', + ')', '=', 'F', 'U', 'H', 'L' | 0x80, '(', 'F', + 'R', 'I', 'E', 'N', 'D', ')', '=', 'F', + 'R', 'E', 'H', '5', 'N', 'D' | 0x80, '(', 'F', + 'A', 'T', 'H', 'E', 'R', ')', '=', 'F', + 'A', 'A', '4', 'D', 'H', 'E', 'R' | 0x80, '(', + 'F', ')', 'F', '=' | 0x80, '(', 'F', ')', '=', + 'F' | 0x80, + + ']', 'G' | 0x80, ' ', '(', 'G', ')', ' ', '=', + 'J', 'I', 'Y', '4' | 0x80, '(', 'G', 'I', 'V', + ')', '=', 'G', 'I', 'H', '5', 'V' | 0x80, ' ', + '(', 'G', ')', 'I', '^', '=', 'G' | 0x80, '(', + 'G', 'E', ')', 'T', '=', 'G', 'E', 'H', + '5' | 0x80, 'S', 'U', '(', 'G', 'G', 'E', 'S', + ')', '=', 'G', 'J', 'E', 'H', '4', 'S' | 0x80, + '(', 'G', 'G', ')', '=', 'G' | 0x80, ' ', 'B', + '#', '(', 'G', ')', '=', 'G' | 0x80, '(', 'G', + ')', '+', '=', 'J' | 0x80, '(', 'G', 'R', 'E', + 'A', 'T', ')', '=', 'G', 'R', 'E', 'Y', + '4', 'T' | 0x80, '(', 'G', 'O', 'N', ')', 'E', + '=', 'G', 'A', 'O', '5', 'N' | 0x80, '#', '(', + 'G', 'H', ')', '=' | 0x80, ' ', '(', 'G', 'N', + ')', '=', 'N' | 0x80, '(', 'G', ')', '=', 'G' | 0x80, + + ']', 'H' | 0x80, ' ', '(', 'H', ')', ' ', '=', + 'E', 'Y', '4', 'C', 'H' | 0x80, ' ', '(', 'H', + 'A', 'V', ')', '=', '/', 'H', 'A', 'E', + '6', 'V' | 0x80, ' ', '(', 'H', 'E', 'R', 'E', + ')', '=', '/', 'H', 'I', 'Y', 'R' | 0x80, ' ', + '(', 'H', 'O', 'U', 'R', ')', '=', 'A', + 'W', '5', 'E', 'R' | 0x80, '(', 'H', 'O', 'W', + ')', '=', '/', 'H', 'A', 'W' | 0x80, '(', 'H', + ')', '#', '=', '/', 'H' | 0x80, '(', 'H', ')', + '=' | 0x80, + + ']', 'I' | 0x80, ' ', '(', 'I', 'N', ')', '=', + 'I', 'H', 'N' | 0x80, ' ', '(', 'I', ')', ' ', + '=', 'A', 'Y', '4' | 0x80, '(', 'I', ')', ' ', + '=', 'A', 'Y' | 0x80, '(', 'I', 'N', ')', 'D', + '=', 'A', 'Y', '5', 'N' | 0x80, 'S', 'E', 'M', + '(', 'I', ')', '=', 'I', 'Y' | 0x80, ' ', 'A', + 'N', 'T', '(', 'I', ')', '=', 'A', 'Y' | 0x80, + '(', 'I', 'E', 'R', ')', '=', 'I', 'Y', + 'E', 'R' | 0x80, '#', ':', 'R', '(', 'I', 'E', + 'D', ')', ' ', '=', 'I', 'Y', 'D' | 0x80, '(', + 'I', 'E', 'D', ')', ' ', '=', 'A', 'Y', + '5', 'D' | 0x80, '(', 'I', 'E', 'N', ')', '=', + 'I', 'Y', 'E', 'H', 'N' | 0x80, '(', 'I', 'E', + ')', 'T', '=', 'A', 'Y', '4', 'E', 'H' | 0x80, + '(', 'I', '\'', ')', '=', 'A', 'Y', '5' | 0x80, + ' ', ':', '(', 'I', ')', '^', '%', '=', + 'A', 'Y', '5' | 0x80, ' ', ':', '(', 'I', 'E', + ')', ' ', '=', 'A', 'Y', '4' | 0x80, '(', 'I', + ')', '%', '=', 'I', 'Y' | 0x80, '(', 'I', 'E', + ')', '=', 'I', 'Y', '4' | 0x80, ' ', '(', 'I', + 'D', 'E', 'A', ')', '=', 'A', 'Y', 'D', + 'I', 'Y', '5', 'A', 'H' | 0x80, '(', 'I', ')', + '^', '+', ':', '#', '=', 'I', 'H' | 0x80, '(', + 'I', 'R', ')', '#', '=', 'A', 'Y', 'R' | 0x80, + '(', 'I', 'Z', ')', '%', '=', 'A', 'Y', + 'Z' | 0x80, '(', 'I', 'S', ')', '%', '=', 'A', + 'Y', 'Z' | 0x80, 'I', '^', '(', 'I', ')', '^', + '#', '=', 'I', 'H' | 0x80, '+', '^', '(', 'I', + ')', '^', '+', '=', 'A', 'Y' | 0x80, '#', ':', + '^', '(', 'I', ')', '^', '+', '=', 'I', + 'H' | 0x80, '(', 'I', ')', '^', '+', '=', 'A', + 'Y' | 0x80, '(', 'I', 'R', ')', '=', 'E', 'R' | 0x80, + '(', 'I', 'G', 'H', ')', '=', 'A', 'Y', + '4' | 0x80, '(', 'I', 'L', 'D', ')', '=', 'A', + 'Y', '5', 'L', 'D' | 0x80, ' ', '(', 'I', 'G', + 'N', ')', '=', 'I', 'H', 'G', 'N' | 0x80, '(', + 'I', 'G', 'N', ')', ' ', '=', 'A', 'Y', + '4', 'N' | 0x80, '(', 'I', 'G', 'N', ')', '^', + '=', 'A', 'Y', '4', 'N' | 0x80, '(', 'I', 'G', + 'N', ')', '%', '=', 'A', 'Y', '4', 'N' | 0x80, + '(', 'I', 'C', 'R', 'O', ')', '=', 'A', + 'Y', '4', 'K', 'R', 'O', 'H' | 0x80, '(', 'I', + 'Q', 'U', 'E', ')', '=', 'I', 'Y', '4', + 'K' | 0x80, '(', 'I', ')', '=', 'I', 'H' | 0x80, + + ']', 'J' | 0x80, ' ', '(', 'J', ')', ' ', '=', + 'J', 'E', 'Y', '4' | 0x80, '(', 'J', ')', '=', + 'J' | 0x80, + + ']', 'K' | 0x80, ' ', '(', 'K', ')', ' ', '=', + 'K', 'E', 'Y', '4' | 0x80, ' ', '(', 'K', ')', + 'N', '=' | 0x80, '(', 'K', ')', '=', 'K' | 0x80, + + ']', 'L' | 0x80, ' ', '(', 'L', ')', ' ', '=', + 'E', 'H', '4', 'L' | 0x80, '(', 'L', 'O', ')', + 'C', '#', '=', 'L', 'O', 'W' | 0x80, 'L', '(', + 'L', ')', '=' | 0x80, '#', ':', '^', '(', 'L', + ')', '%', '=', 'U', 'L' | 0x80, '(', 'L', 'E', + 'A', 'D', ')', '=', 'L', 'I', 'Y', 'D' | 0x80, + ' ', '(', 'L', 'A', 'U', 'G', 'H', ')', + '=', 'L', 'A', 'E', '4', 'F' | 0x80, '(', 'L', + ')', '=', 'L' | 0x80, + + ']', 'M' | 0x80, ' ', '(', 'M', ')', ' ', '=', + 'E', 'H', '4', 'M' | 0x80, ' ', '(', 'M', 'R', + '.', ')', ' ', '=', 'M', 'I', 'H', '4', + 'S', 'T', 'E', 'R' | 0x80, ' ', '(', 'M', 'S', + '.', ')', '=', 'M', 'I', 'H', '5', 'Z' | 0x80, + ' ', '(', 'M', 'R', 'S', '.', ')', ' ', + '=', 'M', 'I', 'H', '4', 'S', 'I', 'X', + 'Z' | 0x80, '(', 'M', 'O', 'V', ')', '=', 'M', + 'U', 'W', '4', 'V' | 0x80, '(', 'M', 'A', 'C', + 'H', 'I', 'N', ')', '=', 'M', 'A', 'H', + 'S', 'H', 'I', 'Y', '5', 'N' | 0x80, 'M', '(', + 'M', ')', '=' | 0x80, '(', 'M', ')', '=', 'M' | 0x80, + + ']', 'N' | 0x80, ' ', '(', 'N', ')', ' ', '=', + 'E', 'H', '4', 'N' | 0x80, 'E', '(', 'N', 'G', + ')', '+', '=', 'N', 'J' | 0x80, '(', 'N', 'G', + ')', 'R', '=', 'N', 'X', 'G' | 0x80, '(', 'N', + 'G', ')', '#', '=', 'N', 'X', 'G' | 0x80, '(', + 'N', 'G', 'L', ')', '%', '=', 'N', 'X', + 'G', 'U', 'L' | 0x80, '(', 'N', 'G', ')', '=', + 'N', 'X' | 0x80, '(', 'N', 'K', ')', '=', 'N', + 'X', 'K' | 0x80, ' ', '(', 'N', 'O', 'W', ')', + ' ', '=', 'N', 'A', 'W', '4' | 0x80, 'N', '(', + 'N', ')', '=' | 0x80, '(', 'N', 'O', 'N', ')', + 'E', '=', 'N', 'A', 'H', '4', 'N' | 0x80, '(', + 'N', ')', '=', 'N' | 0x80, + + ']', 'O' | 0x80, ' ', '(', 'O', ')', ' ', '=', + 'O', 'H', '4', 'W' | 0x80, '(', 'O', 'F', ')', + ' ', '=', 'A', 'H', 'V' | 0x80, ' ', '(', 'O', + 'H', ')', ' ', '=', 'O', 'W', '5' | 0x80, '(', + 'O', 'R', 'O', 'U', 'G', 'H', ')', '=', + 'E', 'R', '4', 'O', 'W' | 0x80, '#', ':', '(', + 'O', 'R', ')', ' ', '=', 'E', 'R' | 0x80, '#', + ':', '(', 'O', 'R', 'S', ')', ' ', '=', + 'E', 'R', 'Z' | 0x80, '(', 'O', 'R', ')', '=', + 'A', 'O', 'R' | 0x80, ' ', '(', 'O', 'N', 'E', + ')', '=', 'W', 'A', 'H', 'N' | 0x80, '#', '(', + 'O', 'N', 'E', ')', ' ', '=', 'W', 'A', + 'H', 'N' | 0x80, '(', 'O', 'W', ')', '=', 'O', + 'W' | 0x80, ' ', '(', 'O', 'V', 'E', 'R', ')', + '=', 'O', 'W', '5', 'V', 'E', 'R' | 0x80, 'P', + 'R', '(', 'O', ')', 'V', '=', 'U', 'W', + '4' | 0x80, '(', 'O', 'V', ')', '=', 'A', 'H', + '4', 'V' | 0x80, '(', 'O', ')', '^', '%', '=', + 'O', 'W', '5' | 0x80, '(', 'O', ')', '^', 'E', + 'N', '=', 'O', 'W' | 0x80, '(', 'O', ')', '^', + 'I', '#', '=', 'O', 'W', '5' | 0x80, '(', 'O', + 'L', ')', 'D', '=', 'O', 'W', '4', 'L' | 0x80, + '(', 'O', 'U', 'G', 'H', 'T', ')', '=', + 'A', 'O', '5', 'T' | 0x80, '(', 'O', 'U', 'G', + 'H', ')', '=', 'A', 'H', '5', 'F' | 0x80, ' ', + '(', 'O', 'U', ')', '=', 'A', 'W' | 0x80, 'H', + '(', 'O', 'U', ')', 'S', '#', '=', 'A', + 'W', '4' | 0x80, '(', 'O', 'U', 'S', ')', '=', + 'A', 'X', 'S' | 0x80, '(', 'O', 'U', 'R', ')', + '=', 'O', 'H', 'R' | 0x80, '(', 'O', 'U', 'L', + 'D', ')', '=', 'U', 'H', '5', 'D' | 0x80, '(', + 'O', 'U', ')', '^', 'L', '=', 'A', 'H', + '5' | 0x80, '(', 'O', 'U', 'P', ')', '=', 'U', + 'W', '5', 'P' | 0x80, '(', 'O', 'U', ')', '=', + 'A', 'W' | 0x80, '(', 'O', 'Y', ')', '=', 'O', + 'Y' | 0x80, '(', 'O', 'I', 'N', 'G', ')', '=', + 'O', 'W', '4', 'I', 'H', 'N', 'X' | 0x80, '(', + 'O', 'I', ')', '=', 'O', 'Y', '5' | 0x80, '(', + 'O', 'O', 'R', ')', '=', 'O', 'H', '5', + 'R' | 0x80, '(', 'O', 'O', 'K', ')', '=', 'U', + 'H', '5', 'K' | 0x80, 'F', '(', 'O', 'O', 'D', + ')', '=', 'U', 'W', '5', 'D' | 0x80, 'L', '(', + 'O', 'O', 'D', ')', '=', 'A', 'H', '5', + 'D' | 0x80, 'M', '(', 'O', 'O', 'D', ')', '=', + 'U', 'W', '5', 'D' | 0x80, '(', 'O', 'O', 'D', + ')', '=', 'U', 'H', '5', 'D' | 0x80, 'F', '(', + 'O', 'O', 'T', ')', '=', 'U', 'H', '5', + 'T' | 0x80, '(', 'O', 'O', ')', '=', 'U', 'W', + '5' | 0x80, '(', 'O', '\'', ')', '=', 'O', 'H' | 0x80, + '(', 'O', ')', 'E', '=', 'O', 'W' | 0x80, '(', + 'O', ')', ' ', '=', 'O', 'W' | 0x80, '(', 'O', + 'A', ')', '=', 'O', 'W', '4' | 0x80, ' ', '(', + 'O', 'N', 'L', 'Y', ')', '=', 'O', 'W', + '4', 'N', 'L', 'I', 'Y' | 0x80, ' ', '(', 'O', + 'N', 'C', 'E', ')', '=', 'W', 'A', 'H', + '4', 'N', 'S' | 0x80, '(', 'O', 'N', '\'', 'T', + ')', '=', 'O', 'W', '4', 'N', 'T' | 0x80, 'C', + '(', 'O', ')', 'N', '=', 'A', 'A' | 0x80, '(', + 'O', ')', 'N', 'G', '=', 'A', 'O' | 0x80, ' ', + ':', '^', '(', 'O', ')', 'N', '=', 'A', + 'H' | 0x80, 'I', '(', 'O', 'N', ')', '=', 'U', + 'N' | 0x80, '#', ':', '(', 'O', 'N', ')', '=', + 'U', 'N' | 0x80, '#', '^', '(', 'O', 'N', ')', + '=', 'U', 'N' | 0x80, '(', 'O', ')', 'S', 'T', + '=', 'O', 'W' | 0x80, '(', 'O', 'F', ')', '^', + '=', 'A', 'O', '4', 'F' | 0x80, '(', 'O', 'T', + 'H', 'E', 'R', ')', '=', 'A', 'H', '5', + 'D', 'H', 'E', 'R' | 0x80, 'R', '(', 'O', ')', + 'B', '=', 'R', 'A', 'A' | 0x80, '^', 'R', '(', + 'O', ')', ':', '#', '=', 'O', 'W', '5' | 0x80, + '(', 'O', 'S', 'S', ')', ' ', '=', 'A', + 'O', '5', 'S' | 0x80, '#', ':', '^', '(', 'O', + 'M', ')', '=', 'A', 'H', 'M' | 0x80, '(', 'O', + ')', '=', 'A', 'A' | 0x80, + + ']', 'P' | 0x80, ' ', '(', 'P', ')', ' ', '=', + 'P', 'I', 'Y', '4' | 0x80, '(', 'P', 'H', ')', + '=', 'F' | 0x80, '(', 'P', 'E', 'O', 'P', 'L', + ')', '=', 'P', 'I', 'Y', '5', 'P', 'U', + 'L' | 0x80, '(', 'P', 'O', 'W', ')', '=', 'P', + 'A', 'W', '4' | 0x80, '(', 'P', 'U', 'T', ')', + ' ', '=', 'P', 'U', 'H', 'T' | 0x80, '(', 'P', + ')', 'P', '=' | 0x80, '(', 'P', ')', 'S', '=' | 0x80, + '(', 'P', ')', 'N', '=' | 0x80, '(', 'P', 'R', + 'O', 'F', '.', ')', '=', 'P', 'R', 'O', + 'H', 'F', 'E', 'H', '4', 'S', 'E', 'R' | 0x80, + '(', 'P', ')', '=', 'P' | 0x80, + + ']', 'Q' | 0x80, ' ', '(', 'Q', ')', ' ', '=', + 'K', 'Y', 'U', 'W', '4' | 0x80, '(', 'Q', 'U', + 'A', 'R', ')', '=', 'K', 'W', 'O', 'H', + '5', 'R' | 0x80, '(', 'Q', 'U', ')', '=', 'K', + 'W' | 0x80, '(', 'Q', ')', '=', 'K' | 0x80, ']', 'R' | 0x80, + ' ', '(', 'R', ')', ' ', '=', 'A', 'A', + '5', 'R' | 0x80, ' ', '(', 'R', 'E', ')', '^', + '#', '=', 'R', 'I', 'Y' | 0x80, '(', 'R', ')', + 'R', '=' | 0x80, '(', 'R', ')', '=', 'R' | 0x80, + + ']', 'S' | 0x80, ' ', '(', 'S', ')', ' ', '=', + 'E', 'H', '4', 'S' | 0x80, '(', 'S', 'H', ')', + '=', 'S', 'H' | 0x80, '#', '(', 'S', 'I', 'O', + 'N', ')', '=', 'Z', 'H', 'U', 'N' | 0x80, '(', + 'S', 'O', 'M', 'E', ')', '=', 'S', 'A', + 'H', 'M' | 0x80, '#', '(', 'S', 'U', 'R', ')', + '#', '=', 'Z', 'H', 'E', 'R' | 0x80, '(', 'S', + 'U', 'R', ')', '#', '=', 'S', 'H', 'E', + 'R' | 0x80, '#', '(', 'S', 'U', ')', '#', '=', + 'Z', 'H', 'U', 'W' | 0x80, '#', '(', 'S', 'S', + 'U', ')', '#', '=', 'S', 'H', 'U', 'W' | 0x80, + '#', '(', 'S', 'E', 'D', ')', '=', 'Z', + 'D' | 0x80, '#', '(', 'S', ')', '#', '=', 'Z' | 0x80, + '(', 'S', 'A', 'I', 'D', ')', '=', 'S', + 'E', 'H', 'D' | 0x80, '^', '(', 'S', 'I', 'O', + 'N', ')', '=', 'S', 'H', 'U', 'N' | 0x80, '(', + 'S', ')', 'S', '=' | 0x80, '.', '(', 'S', ')', + ' ', '=', 'Z' | 0x80, '#', ':', '.', 'E', '(', + 'S', ')', ' ', '=', 'Z' | 0x80, '#', ':', '^', + '#', '(', 'S', ')', ' ', '=', 'S' | 0x80, 'U', + '(', 'S', ')', ' ', '=', 'S' | 0x80, ' ', ':', + '#', '(', 'S', ')', ' ', '=', 'Z' | 0x80, '#', + '#', '(', 'S', ')', ' ', '=', 'Z' | 0x80, ' ', + '(', 'S', 'C', 'H', ')', '=', 'S', 'K' | 0x80, + '(', 'S', ')', 'C', '+', '=' | 0x80, '#', '(', + 'S', 'M', ')', '=', 'Z', 'U', 'M' | 0x80, '#', + '(', 'S', 'N', ')', '\'', '=', 'Z', 'U', + 'M' | 0x80, '(', 'S', 'T', 'L', 'E', ')', '=', + 'S', 'U', 'L' | 0x80, '(', 'S', ')', '=', 'S' | 0x80, + + ']', 'T' | 0x80, ' ', '(', 'T', ')', ' ', '=', + 'T', 'I', 'Y', '4' | 0x80, ' ', '(', 'T', 'H', + 'E', ')', ' ', '#', '=', 'D', 'H', 'I', + 'Y' | 0x80, ' ', '(', 'T', 'H', 'E', ')', ' ', + '=', 'D', 'H', 'A', 'X' | 0x80, '(', 'T', 'O', + ')', ' ', '=', 'T', 'U', 'X' | 0x80, ' ', '(', + 'T', 'H', 'A', 'T', ')', '=', 'D', 'H', + 'A', 'E', 'T' | 0x80, ' ', '(', 'T', 'H', 'I', + 'S', ')', ' ', '=', 'D', 'H', 'I', 'H', + 'S' | 0x80, ' ', '(', 'T', 'H', 'E', 'Y', ')', + '=', 'D', 'H', 'E', 'Y' | 0x80, ' ', '(', 'T', + 'H', 'E', 'R', 'E', ')', '=', 'D', 'H', + 'E', 'H', 'R' | 0x80, '(', 'T', 'H', 'E', 'R', + ')', '=', 'D', 'H', 'E', 'R' | 0x80, '(', 'T', + 'H', 'E', 'I', 'R', ')', '=', 'D', 'H', + 'E', 'H', 'R' | 0x80, ' ', '(', 'T', 'H', 'A', + 'N', ')', ' ', '=', 'D', 'H', 'A', 'E', + 'N' | 0x80, ' ', '(', 'T', 'H', 'E', 'M', ')', + ' ', '=', 'D', 'H', 'A', 'E', 'N' | 0x80, '(', + 'T', 'H', 'E', 'S', 'E', ')', ' ', '=', + 'D', 'H', 'I', 'Y', 'Z' | 0x80, ' ', '(', 'T', + 'H', 'E', 'N', ')', '=', 'D', 'H', 'E', + 'H', 'N' | 0x80, '(', 'T', 'H', 'R', 'O', 'U', + 'G', 'H', ')', '=', 'T', 'H', 'R', 'U', + 'W', '4' | 0x80, '(', 'T', 'H', 'O', 'S', 'E', + ')', '=', 'D', 'H', 'O', 'H', 'Z' | 0x80, '(', + 'T', 'H', 'O', 'U', 'G', 'H', ')', ' ', + '=', 'D', 'H', 'O', 'W' | 0x80, '(', 'T', 'O', + 'D', 'A', 'Y', ')', '=', 'T', 'U', 'X', + 'D', 'E', 'Y' | 0x80, '(', 'T', 'O', 'M', 'O', + ')', 'R', 'R', 'O', 'W', '=', 'T', 'U', + 'M', 'A', 'A', '5' | 0x80, '(', 'T', 'O', ')', + 'T', 'A', 'L', '=', 'T', 'O', 'W', '5' | 0x80, + ' ', '(', 'T', 'H', 'U', 'S', ')', '=', + 'D', 'H', 'A', 'H', '4', 'S' | 0x80, '(', 'T', + 'H', ')', '=', 'T', 'H' | 0x80, '#', ':', '(', + 'T', 'E', 'D', ')', '=', 'T', 'I', 'X', + 'D' | 0x80, 'S', '(', 'T', 'I', ')', '#', 'N', + '=', 'C', 'H' | 0x80, '(', 'T', 'I', ')', 'O', + '=', 'S', 'H' | 0x80, '(', 'T', 'I', ')', 'A', + '=', 'S', 'H' | 0x80, '(', 'T', 'I', 'E', 'N', + ')', '=', 'S', 'H', 'U', 'N' | 0x80, '(', 'T', + 'U', 'R', ')', '#', '=', 'C', 'H', 'E', + 'R' | 0x80, '(', 'T', 'U', ')', 'A', '=', 'C', + 'H', 'U', 'W' | 0x80, ' ', '(', 'T', 'W', 'O', + ')', '=', 'T', 'U', 'W' | 0x80, '&', '(', 'T', + ')', 'E', 'N', ' ', '=' | 0x80, '(', 'T', ')', + '=', 'T' | 0x80, + + ']', 'U' | 0x80, ' ', '(', 'U', ')', ' ', '=', + 'Y', 'U', 'W', '4' | 0x80, ' ', '(', 'U', 'N', + ')', 'I', '=', 'Y', 'U', 'W', 'N' | 0x80, ' ', + '(', 'U', 'N', ')', '=', 'A', 'H', 'N' | 0x80, + ' ', '(', 'U', 'P', 'O', 'N', ')', '=', + 'A', 'X', 'P', 'A', 'O', 'N' | 0x80, '@', '(', + 'U', 'R', ')', '#', '=', 'U', 'H', '4', + 'R' | 0x80, '(', 'U', 'R', ')', '#', '=', 'Y', + 'U', 'H', '4', 'R' | 0x80, '(', 'U', 'R', ')', + '=', 'E', 'R' | 0x80, '(', 'U', ')', '^', ' ', + '=', 'A', 'H' | 0x80, '(', 'U', ')', '^', '^', + '=', 'A', 'H', '5' | 0x80, '(', 'U', 'Y', ')', + '=', 'A', 'Y', '5' | 0x80, ' ', 'G', '(', 'U', + ')', '#', '=' | 0x80, 'G', '(', 'U', ')', '%', + '=' | 0x80, 'G', '(', 'U', ')', '#', '=', 'W' | 0x80, + '#', 'N', '(', 'U', ')', '=', 'Y', 'U', + 'W' | 0x80, '@', '(', 'U', ')', '=', 'U', 'W' | 0x80, + '(', 'U', ')', '=', 'Y', 'U', 'W' | 0x80, + + ']', 'V' | 0x80, ' ', '(', 'V', ')', ' ', '=', + 'V', 'I', 'Y', '4' | 0x80, '(', 'V', 'I', 'E', + 'W', ')', '=', 'V', 'Y', 'U', 'W', '5' | 0x80, + '(', 'V', ')', '=', 'V' | 0x80, + + ']', 'W' | 0x80, ' ', '(', 'W', ')', ' ', '=', + 'D', 'A', 'H', '4', 'B', 'U', 'L', 'Y', + 'U', 'W' | 0x80, ' ', '(', 'W', 'E', 'R', 'E', + ')', '=', 'W', 'E', 'R' | 0x80, '(', 'W', 'A', + ')', 'S', 'H', '=', 'W', 'A', 'A' | 0x80, '(', + 'W', 'A', ')', 'S', 'T', '=', 'W', 'E', + 'Y' | 0x80, '(', 'W', 'A', ')', 'S', '=', 'W', + 'A', 'H' | 0x80, '(', 'W', 'A', ')', 'T', '=', + 'W', 'A', 'A' | 0x80, '(', 'W', 'H', 'E', 'R', + 'E', ')', '=', 'W', 'H', 'E', 'H', 'R' | 0x80, + '(', 'W', 'H', 'A', 'T', ')', '=', 'W', + 'H', 'A', 'H', 'T' | 0x80, '(', 'W', 'H', 'O', + 'L', ')', '=', '/', 'H', 'O', 'W', 'L' | 0x80, + '(', 'W', 'H', 'O', ')', '=', '/', 'H', + 'U', 'W' | 0x80, '(', 'W', 'H', ')', '=', 'W', + 'H' | 0x80, '(', 'W', 'A', 'R', ')', '#', '=', + 'W', 'E', 'H', 'R' | 0x80, '(', 'W', 'A', 'R', + ')', '=', 'W', 'A', 'O', 'R' | 0x80, '(', 'W', + 'O', 'R', ')', '^', '=', 'W', 'E', 'R' | 0x80, + '(', 'W', 'R', ')', '=', 'R' | 0x80, '(', 'W', + 'O', 'M', ')', 'A', '=', 'W', 'U', 'H', + 'M' | 0x80, '(', 'W', 'O', 'M', ')', 'E', '=', + 'W', 'I', 'H', 'M' | 0x80, '(', 'W', 'E', 'A', + ')', 'R', '=', 'W', 'E', 'H' | 0x80, '(', 'W', + 'A', 'N', 'T', ')', '=', 'W', 'A', 'A', + '5', 'N', 'T' | 0x80, 'A', 'N', 'S', '(', 'W', + 'E', 'R', ')', '=', 'E', 'R' | 0x80, '(', 'W', + ')', '=', 'W' | 0x80, + + ']', 'X' | 0x80, ' ', '(', 'X', ')', ' ', '=', + 'E', 'H', '4', 'K', 'R' | 0x80, ' ', '(', 'X', + ')', '=', 'Z' | 0x80, '(', 'X', ')', '=', 'K', + 'S' | 0x80, + + ']', 'Y' | 0x80, ' ', '(', 'Y', ')', ' ', '=', + 'W', 'A', 'Y', '4' | 0x80, '(', 'Y', 'O', 'U', + 'N', 'G', ')', '=', 'Y', 'A', 'H', 'N', + 'X' | 0x80, ' ', '(', 'Y', 'O', 'U', 'R', ')', + '=', 'Y', 'O', 'H', 'R' | 0x80, ' ', '(', 'Y', + 'O', 'U', ')', '=', 'Y', 'U', 'W' | 0x80, ' ', + '(', 'Y', 'E', 'S', ')', '=', 'Y', 'E', + 'H', 'S' | 0x80, ' ', '(', 'Y', ')', '=', 'Y' | 0x80, + 'F', '(', 'Y', ')', '=', 'A', 'Y' | 0x80, 'P', + 'S', '(', 'Y', 'C', 'H', ')', '=', 'A', + 'Y', 'K' | 0x80, '#', ':', '^', '(', 'Y', ')', + '=', 'I', 'Y' | 0x80, '#', ':', '^', '(', 'Y', + ')', 'I', '=', 'I', 'Y' | 0x80, ' ', ':', '(', + 'Y', ')', ' ', '=', 'A', 'Y' | 0x80, ' ', ':', + '(', 'Y', ')', '#', '=', 'A', 'Y' | 0x80, ' ', + ':', '(', 'Y', ')', '^', '+', ':', '#', + '=', 'I', 'H' | 0x80, ' ', ':', '(', 'Y', ')', + '^', '#', '=', 'A', 'Y' | 0x80, '(', 'Y', ')', + '=', 'I', 'H' | 0x80, + + ']', 'Z' | 0x80, ' ', '(', 'Z', ')', ' ', '=', + 'Z', 'I', 'Y', '4' | 0x80, '(', 'Z', ')', '=', + 'Z' | 0x80, 'j' | 0x80}; + +const unsigned char rules2[] = { + '(', 'A', ')', '=' | 0x80, '(', '!', ')', '=', + '.' | 0x80, '(', '"', ')', ' ', '=', '-', 'A', + 'H', '5', 'N', 'K', 'W', 'O', 'W', 'T', + '-' | 0x80, '(', '"', ')', '=', 'K', 'W', 'O', + 'W', '4', 'T', '-' | 0x80, '(', '#', ')', '=', + ' ', 'N', 'A', 'H', '4', 'M', 'B', 'E', + 'R' | 0x80, '(', '$', ')', '=', ' ', 'D', 'A', + 'A', '4', 'L', 'E', 'R' | 0x80, '(', '%', ')', + '=', ' ', 'P', 'E', 'R', 'S', 'E', 'H', + '4', 'N', 'T' | 0x80, '(', '&', ')', '=', ' ', + 'A', 'E', 'N', 'D' | 0x80, '(', '\'', ')', '=' | 0x80, + '(', '*', ')', '=', ' ', 'A', 'E', '4', + 'S', 'T', 'E', 'R', 'I', 'H', 'S', 'K' | 0x80, + '(', '+', ')', '=', ' ', 'P', 'L', 'A', + 'H', '4', 'S' | 0x80, '(', ',', ')', '=', ',' | 0x80, + ' ', '(', '-', ')', ' ', '=', '-' | 0x80, '(', + '-', ')', '=' | 0x80, '(', '.', ')', '=', ' ', + 'P', 'O', 'Y', 'N', 'T' | 0x80, '(', '/', ')', + '=', ' ', 'S', 'L', 'A', 'E', '4', 'S', + 'H' | 0x80, '(', '0', ')', '=', ' ', 'Z', 'I', + 'Y', '4', 'R', 'O', 'W' | 0x80, ' ', '(', '1', + 'S', 'T', ')', '=', 'F', 'E', 'R', '4', + 'S', 'T' | 0x80, ' ', '(', '1', '0', 'T', 'H', + ')', '=', 'T', 'E', 'H', '4', 'N', 'T', + 'H' | 0x80, '(', '1', ')', '=', ' ', 'W', 'A', + 'H', '4', 'N' | 0x80, ' ', '(', '2', 'N', 'D', + ')', '=', 'S', 'E', 'H', '4', 'K', 'U', + 'N', 'D' | 0x80, '(', '2', ')', '=', ' ', 'T', + 'U', 'W', '4' | 0x80, ' ', '(', '3', 'R', 'D', + ')', '=', 'T', 'H', 'E', 'R', '4', 'D' | 0x80, + '(', '3', ')', '=', ' ', 'T', 'H', 'R', + 'I', 'Y', '4' | 0x80, '(', '4', ')', '=', ' ', + 'F', 'O', 'H', '4', 'R' | 0x80, ' ', '(', '5', + 'T', 'H', ')', '=', 'F', 'I', 'H', '4', + 'F', 'T', 'H' | 0x80, '(', '5', ')', '=', ' ', + 'F', 'A', 'Y', '4', 'V' | 0x80, ' ', '(', '6', + '4', ')', ' ', '=', 'S', 'I', 'H', '4', + 'K', 'S', 'T', 'I', 'Y', ' ', 'F', 'O', + 'H', 'R' | 0x80, '(', '6', ')', '=', ' ', 'S', + 'I', 'H', '4', 'K', 'S' | 0x80, '(', '7', ')', + '=', ' ', 'S', 'E', 'H', '4', 'V', 'U', + 'N' | 0x80, ' ', '(', '8', 'T', 'H', ')', '=', + 'E', 'Y', '4', 'T', 'H' | 0x80, '(', '8', ')', + '=', ' ', 'E', 'Y', '4', 'T' | 0x80, '(', '9', + ')', '=', ' ', 'N', 'A', 'Y', '4', 'N' | 0x80, + '(', ':', ')', '=', '.' | 0x80, '(', ';', ')', + '=', '.' | 0x80, '(', '<', ')', '=', ' ', 'L', + 'E', 'H', '4', 'S', ' ', 'D', 'H', 'A', + 'E', 'N' | 0x80, '(', '=', ')', '=', ' ', 'I', + 'Y', '4', 'K', 'W', 'U', 'L', 'Z' | 0x80, '(', + '>', ')', '=', ' ', 'G', 'R', 'E', 'Y', + '4', 'T', 'E', 'R', ' ', 'D', 'H', 'A', + 'E', 'N' | 0x80, '(', '?', ')', '=', '?' | 0x80, '(', + '@', ')', '=', ' ', 'A', 'E', '6', 'T' | 0x80, + '(', '^', ')', '=', ' ', 'K', 'A', 'E', + '4', 'R', 'I', 'X', 'T' | 0x80, ']', 'A' | 0x80}; + +//26 items. From 'A' to 'Z' +// positions for mem62 and mem63 for each character +const unsigned char tab37489[] = {0, 149, 247, 162, 57, 197, 6, 126, 199, 38, 55, 78, 145, + 241, 85, 161, 254, 36, 69, 45, 167, 54, 83, 46, 71, 218}; + +const unsigned char tab37515[] = {125, 126, 126, 127, 128, 129, 130, 130, 130, 132, 132, 132, 132, + 132, 133, 135, 135, 136, 136, 137, 138, 139, 139, 140, 140, 140}; + +void STM32SAM::Output8BitAry(int index, unsigned char ary[5]) { + int k; + + uint32_t bufferposOld = bufferpos; + + bufferpos += timetable[oldtimetableindex][index]; + oldtimetableindex = index; + + int sample_uS = bufferpos - bufferposOld; + + uint32_t f = 0; + + // write a little bit in advance + for(k = 0; k < 5; k++) { + // buffer[bufferpos / 50 + k] = ary[k]; + + // f = micros() + sample_uS / (_STM32SAM_SPEED + 1); + // while(micros() < f) { + // }; + f = sample_uS / (_STM32SAM_SPEED + 1); + furi_delay_us(f); + SetAUDIO(ary[k]); + // delayMicroseconds(sample_uS / 5 ); + } + + // SetAUDIO(ary[0]); +} + +void STM32SAM::Output8Bit(int index, unsigned char A) { + unsigned char ary[5] = {A, A, A, A, A}; + Output8BitAry(index, ary); +} + +//written by me because of different table positions. +// mem[47] = ... +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 +unsigned char STM32SAM::Read(unsigned char p, unsigned char Y) { + switch(p) { + case 168: + return pitches[Y]; + case 169: + return frequency1[Y]; + case 170: + return frequency2[Y]; + case 171: + return frequency3[Y]; + case 172: + return amplitude1[Y]; + case 173: + return amplitude2[Y]; + case 174: + return amplitude3[Y]; + } + // Serial1.println("Error reading to tables"); + return 0; +} + +void STM32SAM::Write(unsigned char p, unsigned char Y, unsigned char value) { + switch(p) { + case 168: + pitches[Y] = value; + return; + case 169: + frequency1[Y] = value; + return; + case 170: + frequency2[Y] = value; + return; + case 171: + frequency3[Y] = value; + return; + case 172: + amplitude1[Y] = value; + return; + case 173: + amplitude2[Y] = value; + return; + case 174: + amplitude3[Y] = value; + return; + } + //Serial1.println("Error writing to tables\n"); +} + +// ------------------------------------------------------------------------- +//Code48227 +// Render a sampled sound from the sampleTable. +// +// Phoneme Sample Start Sample End +// 32: S* 15 255 +// 33: SH 257 511 +// 34: F* 559 767 +// 35: TH 583 767 +// 36: /H 903 1023 +// 37: /X 1135 1279 +// 38: Z* 84 119 +// 39: ZH 340 375 +// 40: V* 596 639 +// 41: DH 596 631 +// +// 42: CH +// 43: ** 399 511 +// +// 44: J* +// 45: ** 257 276 +// 46: ** +// +// 66: P* +// 67: ** 743 767 +// 68: ** +// +// 69: T* +// 70: ** 231 255 +// 71: ** +// +// The SampledPhonemesTable[] holds flags indicating if a phoneme is +// voiced or not. If the upper 5 bits are zero, the sample is voiced. +// +// Samples in the sampleTable are compressed, with bits being converted to +// bytes from high bit to low, as follows: +// +// unvoiced 0 bit -> X +// unvoiced 1 bit -> 5 +// +// voiced 0 bit -> 6 +// voiced 1 bit -> 24 +// +// Where X is a value from the table: +// +// { 0x18, 0x1A, 0x17, 0x17, 0x17 }; +// +// The index into this table is determined by masking off the lower +// 3 bits from the SampledPhonemesTable: +// +// index = (SampledPhonemesTable[i] & 7) - 1; +// +// For voices samples, samples are interleaved between voiced output. + +// Code48227() +void STM32SAM::RenderSample(unsigned char* mem66) { + int tempA; + // current phoneme's index + mem49 = Y; + + // mask low three bits and subtract 1 get value to + // convert 0 bits on unvoiced samples. + A = mem39 & 7; + X = A - 1; + + // store the result + mem56 = X; + + // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 } + // T, S, Z 0 0x18 + // CH, J, SH, ZH 1 0x1A + // P, F*, V, TH, DH 2 0x17 + // /H 3 0x17 + // /X 4 0x17 + + // get value from the table + mem53 = tab48426[X]; + mem47 = X; //46016+mem[56]*256 + + // voiced sample? + A = mem39 & 248; + if(A == 0) { + // voiced phoneme: Z*, ZH, V*, DH + Y = mem49; + A = pitches[mem49] >> 4; + + // jump to voiced portion + goto pos48315; + } + + Y = A ^ 255; +pos48274: + + // step through the 8 bits in the sample + mem56 = 8; + + // get the next sample from the table + // mem47*256 = offset to start of samples + A = sampleTable[mem47 * 256 + Y]; +pos48280: + + // left shift to get the high bit + tempA = A; + A = A << 1; + //48281: BCC 48290 + + // bit not set? + if((tempA & 128) == 0) { + // convert the bit to value from table + X = mem53; + //mem[54296] = X; + // output the byte + Output8Bit(1, (X & 0x0f) * 16); + // if X != 0, exit loop + if(X != 0) goto pos48296; + } + + // output a 5 for the on bit + Output8Bit(2, 5 * 16); + + //48295: NOP +pos48296: + + X = 0; + + // decrement counter + mem56--; + + // if not done, jump to top of loop + if(mem56 != 0) goto pos48280; + + // increment position + Y++; + if(Y != 0) goto pos48274; + + // restore values and return + mem44 = 1; + Y = mem49; + return; + + unsigned char phase1; + +pos48315: + // handle voiced samples here + + // number of samples? + phase1 = A ^ 255; + + Y = *mem66; + do { + //pos48321: + + // shift through all 8 bits + mem56 = 8; + //A = Read(mem47, Y); + + // fetch value from table + A = sampleTable[mem47 * 256 + Y]; + + // loop 8 times + //pos48327: + do { + //48327: ASL A + //48328: BCC 48337 + + // left shift and check high bit + tempA = A; + A = A << 1; + if((tempA & 128) != 0) { + // if bit set, output 26 + X = 26; + Output8Bit(3, (X & 0xf) * 16); + } else { + //timetable 4 + // bit is not set, output a 6 + X = 6; + Output8Bit(4, (X & 0xf) * 16); + } + + mem56--; + } while(mem56 != 0); + + // move ahead in the table + Y++; + + // continue until counter done + phase1++; + + } while(phase1 != 0); + // if (phase1 != 0) goto pos48321; + + // restore values and return + A = 1; + mem44 = 1; + *mem66 = Y; + Y = mem49; + return; +} + +// RENDER THE PHONEMES IN THE LIST +// +// The phoneme list is converted into sound through the steps: +// +// 1. Copy each phoneme number of times into the frames list, +// where each frame represents 10 milliseconds of sound. +// +// 2. Determine the transitions lengths between phonemes, and linearly +// interpolate the values across the frames. +// +// 3. Offset the pitches by the fundamental frequency. +// +// 4. Render the each frame. + +//void Code47574() +void STM32SAM::Render() { + unsigned char phase1 = 0; //mem43 + unsigned char phase2 = 0; + unsigned char phase3 = 0; + unsigned char mem66 = 0; + unsigned char mem38 = 0; + unsigned char mem40 = 0; + unsigned char speedcounter = 0; //mem45 + unsigned char mem48 = 0; + int i; + if(phonemeIndexOutput[0] == 255) return; //exit if no data + + A = 0; + X = 0; + mem44 = 0; + + // CREATE FRAMES + // + // The length parameter in the list corresponds to the number of frames + // to expand the phoneme to. Each frame represents 10 milliseconds of time. + // So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration. + // + // The parameters are copied from the phoneme to the frame verbatim. + + // pos47587: + do { + // get the index + Y = mem44; + // get the phoneme at the index + A = phonemeIndexOutput[mem44]; + mem56 = A; + + // if terminal phoneme, exit the loop + if(A == 255) break; + + // period phoneme *. + if(A == 1) { + // add rising inflection + A = 1; + mem48 = 1; + //goto pos48376; + AddInflection(mem48, phase1); + } + /* + if (A == 2) goto pos48372; + */ + + // question mark phoneme? + if(A == 2) { + // create falling inflection + mem48 = 255; + AddInflection(mem48, phase1); + } + // pos47615: + + // get the stress amount (more stress = higher pitch) + phase1 = tab47492[stressOutput[Y] + 1]; + + // get number of frames to write + phase2 = phonemeLengthOutput[Y]; + Y = mem56; + + // copy from the source to the frames list + do { + frequency1[X] = freq1data[Y]; // F1 frequency + frequency2[X] = freq2data[Y]; // F2 frequency + frequency3[X] = freq3data[Y]; // F3 frequency + amplitude1[X] = ampl1data[Y]; // F1 amplitude + amplitude2[X] = ampl2data[Y]; // F2 amplitude + amplitude3[X] = ampl3data[Y]; // F3 amplitude + sampledConsonantFlag[X] = + sampledConsonantFlags[Y]; // phoneme data for sampled consonants + pitches[X] = pitch + phase1; // pitch + X++; + phase2--; + } while(phase2 != 0); + mem44++; + } while(mem44 != 0); + // ------------------- + //pos47694: + + // CREATE TRANSITIONS + // + // Linear transitions are now created to smoothly connect the + // end of one sustained portion of a phoneme to the following + // phoneme. + // + // To do this, three tables are used: + // + // Table Purpose + // ========= ================================================== + // blendRank Determines which phoneme's blend values are used. + // + // blendOut The number of frames at the end of the phoneme that + // will be used to transition to the following phoneme. + // + // blendIn The number of frames of the following phoneme that + // will be used to transition into that phoneme. + // + // In creating a transition between two phonemes, the phoneme + // with the HIGHEST rank is used. Phonemes are ranked on how much + // their identity is based on their transitions. For example, + // vowels are and diphthongs are identified by their sustained portion, + // rather than the transitions, so they are given low values. In contrast, + // stop consonants (P, B, T, K) and glides (Y, L) are almost entirely + // defined by their transitions, and are given high rank values. + // + // Here are the rankings used by SAM: + // + // Rank Type Phonemes + // 2 All vowels IY, IH, etc. + // 5 Diphthong endings YX, WX, ER + // 8 Terminal liquid consonants LX, WX, YX, N, NX + // 9 Liquid consonants L, RX, W + // 10 Glide R, OH + // 11 Glide WH + // 18 Voiceless fricatives S, SH, F, TH + // 20 Voiced fricatives Z, ZH, V, DH + // 23 Plosives, stop consonants P, T, K, KX, DX, CH + // 26 Stop consonants J, GX, B, D, G + // 27-29 Stop consonants (internal) ** + // 30 Unvoiced consonants /H, /X and Q* + // 160 Nasal M + // + // To determine how many frames to use, the two phonemes are + // compared using the blendRank[] table. The phoneme with the + // higher rank is selected. In case of a tie, a blend of each is used: + // + // if blendRank[phoneme1] == blendRank[phomneme2] + // // use lengths from each phoneme + // outBlendFrames = outBlend[phoneme1] + // inBlendFrames = outBlend[phoneme2] + // else if blendRank[phoneme1] > blendRank[phoneme2] + // // use lengths from first phoneme + // outBlendFrames = outBlendLength[phoneme1] + // inBlendFrames = inBlendLength[phoneme1] + // else + // // use lengths from the second phoneme + // // note that in and out are SWAPPED! + // outBlendFrames = inBlendLength[phoneme2] + // inBlendFrames = outBlendLength[phoneme2] + // + // Blend lengths can't be less than zero. + // + // Transitions are assumed to be symetrical, so if the transition + // values for the second phoneme are used, the inBlendLength and + // outBlendLength values are SWAPPED. + // + // For most of the parameters, SAM interpolates over the range of the last + // outBlendFrames-1 and the first inBlendFrames. + // + // The exception to this is the Pitch[] parameter, which is interpolates the + // pitch from the CENTER of the current phoneme to the CENTER of the next + // phoneme. + // + // Here are two examples. First, For example, consider the word "SUN" (S AH N) + // + // Phoneme Duration BlendWeight OutBlendFrames InBlendFrames + // S 2 18 1 3 + // AH 8 2 4 4 + // N 7 8 1 2 + // + // The formant transitions for the output frames are calculated as follows: + // + // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch + // ------------------------------------------------ + // S + // 241 0 6 0 73 0 99 61 Use S (weight 18) for transition instead of AH (weight 2) + // 241 0 6 0 73 0 99 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames + // AH + // 0 2 10 2 66 0 96 59 * <-- InBlendFrames = 3 frames + // 0 4 14 3 59 0 93 57 * + // 0 8 18 5 52 0 90 55 * + // 0 15 22 9 44 1 87 53 + // 0 15 22 9 44 1 87 53 + // 0 15 22 9 44 1 87 53 Use N (weight 8) for transition instead of AH (weight 2). + // 0 15 22 9 44 1 87 53 Since N is second phoneme, reverse the IN and OUT values. + // 0 11 17 8 47 1 98 56 * <-- (InBlendFrames-1) = (2-1) = 1 frames + // N + // 0 8 12 6 50 1 109 58 * <-- OutBlendFrames = 1 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // + // Now, consider the reverse "NUS" (N AH S): + // + // flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch + // ------------------------------------------------ + // N + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 + // 0 5 6 5 54 0 121 61 Use N (weight 8) for transition instead of AH (weight 2) + // 0 5 6 5 54 0 121 61 <-- (OutBlendFrames-1) = (1-1) = 0 frames + // AH + // 0 8 11 6 51 0 110 59 * <-- InBlendFrames = 2 + // 0 11 16 8 48 0 99 56 * + // 0 15 22 9 44 1 87 53 Use S (weight 18) for transition instead of AH (weight 2) + // 0 15 22 9 44 1 87 53 Since S is second phoneme, reverse the IN and OUT values. + // 0 9 18 5 51 1 90 55 * <-- (InBlendFrames-1) = (3-1) = 2 + // 0 4 14 3 58 1 93 57 * + // S + // 241 2 10 2 65 1 96 59 * <-- OutBlendFrames = 1 + // 241 0 6 0 73 0 99 61 + + A = 0; + mem44 = 0; + mem49 = 0; // mem49 starts at as 0 + X = 0; + while(1) //while No. 1 + { + // get the current and following phoneme + Y = phonemeIndexOutput[X]; + A = phonemeIndexOutput[X + 1]; + X++; + + // exit loop at end token + if(A == 255) break; //goto pos47970; + + // get the ranking of each phoneme + X = A; + mem56 = blendRank[A]; + A = blendRank[Y]; + + // compare the rank - lower rank value is stronger + if(A == mem56) { + // same rank, so use out blend lengths from each phoneme + phase1 = outBlendLength[Y]; + phase2 = outBlendLength[X]; + } else if(A < mem56) { + // first phoneme is stronger, so us it's blend lengths + phase1 = inBlendLength[X]; + phase2 = outBlendLength[X]; + } else { + // second phoneme is stronger, so use it's blend lengths + // note the out/in are swapped + phase1 = outBlendLength[Y]; + phase2 = inBlendLength[Y]; + } + + Y = mem44; + A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length + mem49 = A; // mem49 now holds length + position + A = A + phase2; //Maybe Problem because of carry flag + + //47776: ADC 42 + speedcounter = A; + mem47 = 168; + phase3 = mem49 - phase1; // what is mem49 + A = phase1 + phase2; // total transition? + mem38 = A; + + X = A; + X -= 2; + if((X & 128) == 0) + do //while No. 2 + { + //pos47810: + + // mem47 is used to index the tables: + // 168 pitches[] + // 169 frequency1 + // 170 frequency2 + // 171 frequency3 + // 172 amplitude1 + // 173 amplitude2 + // 174 amplitude3 + + mem40 = mem38; + + if(mem47 == 168) // pitch + { + // unlike the other values, the pitches[] interpolates from + // the middle of the current phoneme to the middle of the + // next phoneme + + unsigned char mem36, mem37; + // half the width of the current phoneme + mem36 = phonemeLengthOutput[mem44] >> 1; + // half the width of the next phoneme + mem37 = phonemeLengthOutput[mem44 + 1] >> 1; + // sum the values + mem40 = mem36 + mem37; // length of both halves + mem37 += mem49; // center of next phoneme + mem36 = mem49 - mem36; // center index of current phoneme + A = Read( + mem47, mem37); // value at center of next phoneme - end interpolation value + //A = mem[address]; + + Y = mem36; // start index of interpolation + mem53 = A - Read(mem47, mem36); // value to center of current phoneme + } else { + // value to interpolate to + A = Read(mem47, speedcounter); + // position to start interpolation from + Y = phase3; + // value to interpolate from + mem53 = A - Read(mem47, phase3); + } + + //Code47503(mem40); + // ML : Code47503 is division with remainder, and mem50 gets the sign + + // calculate change per frame + signed char m53 = (signed char)mem53; + mem50 = mem53 & 128; + unsigned char m53abs = abs(m53); + mem51 = m53abs % mem40; //abs((char)m53) % mem40; + mem53 = (unsigned char)((signed char)(m53) / mem40); + + // interpolation range + X = mem40; // number of frames to interpolate over + Y = phase3; // starting frame + + // linearly interpolate values + + mem56 = 0; + //47907: CLC + //pos47908: + while(1) //while No. 3 + { + A = Read(mem47, Y) + mem53; //carry alway cleared + + mem48 = A; + Y++; + X--; + if(X == 0) break; + + mem56 += mem51; + if(mem56 >= mem40) //??? + { + mem56 -= mem40; //carry? is set + //if ((mem56 & 128)==0) + if((mem50 & 128) == 0) { + //47935: BIT 50 + //47937: BMI 47943 + if(mem48 != 0) mem48++; + } else + mem48--; + } + //pos47945: + Write(mem47, Y, mem48); + } //while No. 3 + + //pos47952: + mem47++; + //if (mem47 != 175) goto pos47810; + } while(mem47 != 175); //while No. 2 + //pos47963: + mem44++; + X = mem44; + } //while No. 1 + + //goto pos47701; + //pos47970: + + // add the length of this phoneme + mem48 = mem49 + phonemeLengthOutput[mem44]; + + // ASSIGN PITCH CONTOUR + // + // This subtracts the F1 frequency from the pitch to create a + // pitch contour. Without this, the output would be at a single + // pitch level (monotone). + + // don't adjust pitch if in sing mode + if(!singmode) { + // iterate through the buffer + for(i = 0; i < 256; i++) { + // subtract half the frequency of the formant 1. + // this adds variety to the voice + pitches[i] -= (frequency1[i] >> 1); + } + } + + phase1 = 0; + phase2 = 0; + phase3 = 0; + mem49 = 0; + speedcounter = 72; //sam standard speed + + // RESCALE AMPLITUDE + // + // Rescale volume from a linear scale to decibels. + // + + //amplitude rescaling + for(i = 255; i >= 0; i--) { + amplitude1[i] = amplitudeRescale[amplitude1[i]]; + amplitude2[i] = amplitudeRescale[amplitude2[i]]; + amplitude3[i] = amplitudeRescale[amplitude3[i]]; + } + + Y = 0; + A = pitches[0]; + mem44 = A; + X = A; + mem38 = A - (A >> 2); // 3/4*A ??? + + // PROCESS THE FRAMES + // + // In traditional vocal synthesis, the glottal pulse drives filters, which + // are attenuated to the frequencies of the formants. + // + // SAM generates these formants directly with sin and rectangular waves. + // To simulate them being driven by the glottal pulse, the waveforms are + // reset at the beginning of each glottal pulse. + + //finally the loop for sound output + //pos48078: + while(1) { + // get the sampled information on the phoneme + A = sampledConsonantFlag[Y]; + mem39 = A; + + // unvoiced sampled phoneme? + A = A & 248; + if(A != 0) { + // render the sample for the phoneme + RenderSample(&mem66); + + // skip ahead two in the phoneme buffer + Y += 2; + mem48 -= 2; + } else { + // simulate the glottal pulse and formants + unsigned char ary[5]; + unsigned int p1 = + phase1 * 256; // Fixed point integers because we need to divide later on + unsigned int p2 = phase2 * 256; + unsigned int p3 = phase3 * 256; + int k; + for(k = 0; k < 5; k++) { + signed char sp1 = (signed char)sinus[0xff & (p1 >> 8)]; + signed char sp2 = (signed char)sinus[0xff & (p2 >> 8)]; + signed char rp3 = (signed char)rectangle[0xff & (p3 >> 8)]; + signed int sin1 = sp1 * ((unsigned char)amplitude1[Y] & 0x0f); + signed int sin2 = sp2 * ((unsigned char)amplitude2[Y] & 0x0f); + signed int rect = rp3 * ((unsigned char)amplitude3[Y] & 0x0f); + signed int mux = sin1 + sin2 + rect; + mux /= 32; + mux += 128; // Go from signed to unsigned amplitude + ary[k] = mux; + p1 += frequency1[Y] * 256 / 4; // Compromise, this becomes a shift and works well + p2 += frequency2[Y] * 256 / 4; + p3 += frequency3[Y] * 256 / 4; + } + // output the accumulated value + Output8BitAry(0, ary); + speedcounter--; + if(speedcounter != 0) goto pos48155; + Y++; //go to next amplitude + + // decrement the frame count + mem48--; + } + + // if the frame count is zero, exit the loop + if(mem48 == 0) return; + speedcounter = speed; + pos48155: + + // decrement the remaining length of the glottal pulse + mem44--; + + // finished with a glottal pulse? + if(mem44 == 0) { + pos48159: + // fetch the next glottal pulse length + A = pitches[Y]; + mem44 = A; + A = A - (A >> 2); + mem38 = A; + + // reset the formant wave generators to keep them in + // sync with the glottal pulse + phase1 = 0; + phase2 = 0; + phase3 = 0; + continue; + } + + // decrement the count + mem38--; + + // is the count non-zero and the sampled flag is zero? + if((mem38 != 0) || (mem39 == 0)) { + // reset the phase of the formants to match the pulse + phase1 += frequency1[Y]; + phase2 += frequency2[Y]; + phase3 += frequency3[Y]; + continue; + } + + // voiced sampled phonemes interleave the sample with the + // glottal pulse. The sample flag is non-zero, so render + // the sample for the phoneme. + RenderSample(&mem66); + goto pos48159; + } //while + + // The following code is never reached. It's left over from when + // the voiced sample code was part of this loop, instead of part + // of RenderSample(); + + //pos48315: + int tempA; + phase1 = A ^ 255; + Y = mem66; + do { + //pos48321: + + mem56 = 8; + A = Read(mem47, Y); + + //pos48327: + do { + //48327: ASL A + //48328: BCC 48337 + tempA = A; + A = A << 1; + if((tempA & 128) != 0) { + X = 26; + // mem[54296] = X; + bufferpos += 150; + // + // + // buffer[bufferpos / 50] = (X & 15) * 16; + // + // + + } else { + //mem[54296] = 6; + X = 6; + bufferpos += 150; + // + // buffer[bufferpos / 50] = (X & 15) * 16; + // + // + } + + for(X = wait2; X > 0; X--) + ; //wait + mem56--; + } while(mem56 != 0); + + Y++; + phase1++; + + } while(phase1 != 0); + // if (phase1 != 0) goto pos48321; + A = 1; + mem44 = 1; + mem66 = Y; + Y = mem49; + return; +} + +// Create a rising or falling inflection 30 frames prior to +// index X. A rising inflection is used for questions, and +// a falling inflection is used for statements. + +void STM32SAM::AddInflection(unsigned char mem48, unsigned char phase1) { + //pos48372: + // mem48 = 255; + //pos48376: + + // store the location of the punctuation + mem49 = X; + A = X; + int Atemp = A; + + // backup 30 frames + A = A - 30; + // if index is before buffer, point to start of buffer + if(Atemp <= 30) A = 0; + X = A; + + // FIXME: Explain this fix better, it's not obvious + // ML : A =, fixes a problem with invalid pitch with '.' + while((A = pitches[X]) == 127) X++; + +pos48398: + //48398: CLC + //48399: ADC 48 + + // add the inflection direction + A += mem48; + phase1 = A; + + // set the inflection + pitches[X] = A; +pos48406: + + // increment the position + X++; + + // exit if the punctuation has been reached + if(X == mem49) return; //goto pos47615; + if(pitches[X] == 255) goto pos48406; + A = phase1; + goto pos48398; +} + +/* + SAM's voice can be altered by changing the frequencies of the + mouth formant (F1) and the throat formant (F2). Only the voiced + phonemes (5-29 and 48-53) are altered. +*/ +void STM32SAM::SetMouthThroat() { + unsigned char initialFrequency; + unsigned char newFrequency = 0; + //unsigned char mouth; //mem38880 + //unsigned char throat; //mem38881 + + // mouth formants (F1) 5..29 + unsigned char mouthFormants5_29[30] = {0, 0, 0, 0, 0, 10, 14, 19, 24, 27, + 23, 21, 16, 20, 14, 18, 14, 18, 18, 16, + 13, 15, 11, 18, 14, 11, 9, 6, 6, 6}; + + // throat formants (F2) 5..29 + unsigned char throatFormants5_29[30] = {255, 255, 255, 255, 255, 84, 73, 67, 63, 40, + 44, 31, 37, 45, 73, 49, 36, 30, 51, 37, + 29, 69, 24, 50, 30, 24, 83, 46, 54, 86}; + + // there must be no zeros in this 2 tables + // formant 1 frequencies (mouth) 48..53 + unsigned char mouthFormants48_53[6] = {19, 27, 21, 27, 18, 13}; + + // formant 2 frequencies (throat) 48..53 + unsigned char throatFormants48_53[6] = {72, 39, 31, 43, 30, 34}; + + unsigned char pos = 5; //mem39216 + //pos38942: + // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2) + while(pos != 30) { + // recalculate mouth frequency + initialFrequency = mouthFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate throat frequency + initialFrequency = throatFormants5_29[pos]; + if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + pos++; + } + + //pos39059: + // recalculate formant frequencies 48..53 + pos = 48; + Y = 0; + while(pos != 54) { + // recalculate F1 (mouth formant) + initialFrequency = mouthFormants48_53[Y]; + newFrequency = trans(mouth, initialFrequency); + freq1data[pos] = newFrequency; + + // recalculate F2 (throat formant) + initialFrequency = throatFormants48_53[Y]; + newFrequency = trans(throat, initialFrequency); + freq2data[pos] = newFrequency; + Y++; + pos++; + } +} + +//return = (mem39212*mem39213) >> 1 +unsigned char STM32SAM::trans(unsigned char mem39212, unsigned char mem39213) { + //pos39008: + unsigned char carry; + int temp; + unsigned char mem39214, mem39215; + A = 0; + mem39215 = 0; + mem39214 = 0; + X = 8; + do { + carry = mem39212 & 1; + mem39212 = mem39212 >> 1; + if(carry != 0) { + /* + 39018: LSR 39212 + 39021: BCC 39033 + */ + carry = 0; + A = mem39215; + temp = (int)A + (int)mem39213; + A = A + mem39213; + if(temp > 255) carry = 1; + mem39215 = A; + } + temp = mem39215 & 1; + mem39215 = (mem39215 >> 1) | (carry ? 128 : 0); + carry = temp; + //39033: ROR 39215 + X--; + } while(X != 0); + temp = mem39214 & 128; + mem39214 = (mem39214 << 1) | (carry ? 1 : 0); + carry = temp; + temp = mem39215 & 128; + mem39215 = (mem39215 << 1) | (carry ? 1 : 0); + carry = temp; + + return mem39215; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Sam +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//char input[]={"/HAALAOAO MAYN NAAMAEAE IHSTT SAEBAASTTIHAAN \x9b\x9b\0"}; +//unsigned char input[]={"/HAALAOAO \x9b\0"}; +//unsigned char input[]={"AA \x9b\0"}; +//unsigned char input[] = {"GUH5DEHN TAEG\x9b\0"}; + +//unsigned char input[]={"AY5 AEM EY TAO4LXKIHNX KAX4MPYUX4TAH. GOW4 AH/HEH3D PAHNK.MEYK MAY8 DEY.\x9b\0"}; +//unsigned char input[]={"/HEH3LOW2, /HAW AH YUX2 TUXDEY. AY /HOH3P YUX AH FIYLIHNX OW4 KEY.\x9b\0"}; +//unsigned char input[]={"/HEY2, DHIHS IH3Z GREY2T. /HAH /HAH /HAH.AYL BIY5 BAEK.\x9b\0"}; +//unsigned char input[]={"/HAH /HAH /HAH \x9b\0"}; +//unsigned char input[]={"/HAH /HAH /HAH.\x9b\0"}; +//unsigned char input[]={".TUW BIY5Y3,, OHR NAA3T - TUW BIY5IYIY., DHAE4T IHZ DHAH KWEH4SCHAHN.\x9b\0"}; +//unsigned char input[]={"/HEY2, DHIHS \x9b\0"}; + +//unsigned char input[]={" IYIHEHAEAAAHAOOHUHUXERAXIX \x9b\0"}; +//unsigned char input[]={" RLWWYMNNXBDGJZZHVDH \x9b\0"}; +//unsigned char input[]={" SSHFTHPTKCH/H \x9b\0"}; + +//unsigned char input[]={" EYAYOYAWOWUW ULUMUNQ YXWXRXLX/XDX\x9b\0"}; + +void STM32SAM::SetInput(char* _input) { + int i, l; + l = strlen(_input); + if(l > 254) l = 254; + for(i = 0; i < l; i++) { + input[i] = _input[i]; + } + input[l] = 0; +} + +// 168=pitches +// 169=frequency1 +// 170=frequency2 +// 171=frequency3 +// 172=amplitude1 +// 173=amplitude2 +// 174=amplitude3 + +void STM32SAM::Init() { + bufferpos = 0; + int i; + SetMouthThroat(); + + bufferpos = 0; + // TODO, check for free the memory, 10 seconds of output should be more than enough + //buffer = malloc(22050*10); + + // buffer = (char*) calloc(1, sizeof(char)); + + /* + freq2data = &mem[45136]; + freq1data = &mem[45056]; + freq3data = &mem[45216]; + */ + //pitches = &mem[43008]; + /* + frequency1 = &mem[43264]; + frequency2 = &mem[43520]; + frequency3 = &mem[43776]; + */ + /* + amplitude1 = &mem[44032]; + amplitude2 = &mem[44288]; + amplitude3 = &mem[44544]; + */ + //phoneme = &mem[39904]; + /* + ampl1data = &mem[45296]; + ampl2data = &mem[45376]; + ampl3data = &mem[45456]; + */ + + for(i = 0; i < 256; i++) { + stress[i] = 0; + phonemeLength[i] = 0; + } + + for(i = 0; i < 60; i++) { + phonemeIndexOutput[i] = 0; + stressOutput[i] = 0; + phonemeLengthOutput[i] = 0; + } + phonemeindex[255] = + 255; //to prevent buffer overflow // ML : changed from 32 to 255 to stop freezing with long inputs +} + +//int Code39771() +int STM32SAM::SAMMain() { + Init(); + phonemeindex[255] = 32; //to prevent buffer overflow + + if(!Parser1()) { + return 0; + } + + Parser2(); + CopyStress(); + SetPhonemeLength(); + AdjustLengths(); + Code41240(); + do { + A = phonemeindex[X]; + if(A > 80) { + phonemeindex[X] = 255; + break; // error: delete all behind it + } + X++; + } while(X != 0); + + //pos39848: + InsertBreath(); + + //mem[40158] = 255; + + PrepareOutput(); + + return 1; +} + +//void Code48547() +void STM32SAM::PrepareOutput() { + A = 0; + X = 0; + Y = 0; + + //pos48551: + while(1) { + A = phonemeindex[X]; + if(A == 255) { + A = 255; + phonemeIndexOutput[Y] = 255; + Render(); + return; + } + if(A == 254) { + X++; + int temp = X; + //mem[48546] = X; + phonemeIndexOutput[Y] = 255; + Render(); + //X = mem[48546]; + X = temp; + Y = 0; + continue; + } + + if(A == 0) { + X++; + continue; + } + + phonemeIndexOutput[Y] = A; + phonemeLengthOutput[Y] = phonemeLength[X]; + stressOutput[Y] = stress[X]; + X++; + Y++; + } +} + +//void Code41014() +void STM32SAM::Insert( + unsigned char position /*var57*/, + unsigned char mem60, + unsigned char mem59, + unsigned char mem58) { + int i; + for(i = 253; i >= position; i--) // ML : always keep last safe-guarding 255 + { + phonemeindex[i + 1] = phonemeindex[i]; + phonemeLength[i + 1] = phonemeLength[i]; + stress[i + 1] = stress[i]; + } + + phonemeindex[position] = mem60; + phonemeLength[position] = mem59; + stress[position] = mem58; + return; +} + +//void Code48431() +void STM32SAM::InsertBreath() { + unsigned char mem54; + unsigned char mem55; + unsigned char index; //variable Y + mem54 = 255; + X++; + mem55 = 0; + unsigned char mem66 = 0; + while(1) { + //pos48440: + X = mem66; + index = phonemeindex[X]; + if(index == 255) return; + mem55 += phonemeLength[X]; + + if(mem55 < 232) { + if(index != 254) // ML : Prevents an index out of bounds problem + { + A = flags2[index] & 1; + if(A != 0) { + X++; + mem55 = 0; + Insert(X, 254, mem59, 0); + mem66++; + mem66++; + continue; + } + } + if(index == 0) mem54 = X; + mem66++; + continue; + } + X = mem54; + phonemeindex[X] = 31; // 'Q*' glottal stop + phonemeLength[X] = 4; + stress[X] = 0; + X++; + mem55 = 0; + Insert(X, 254, mem59, 0); + X++; + mem66 = X; + } +} + +// Iterates through the phoneme buffer, copying the stress value from +// the following phoneme under the following circumstance: + +// 1. The current phoneme is voiced, excluding plosives and fricatives +// 2. The following phoneme is voiced, excluding plosives and fricatives, and +// 3. The following phoneme is stressed +// +// In those cases, the stress value+1 from the following phoneme is copied. +// +// For example, the word LOITER is represented as LOY5TER, with as stress +// of 5 on the diphtong OY. This routine will copy the stress value of 6 (5+1) +// to the L that precedes it. + +//void Code41883() +void STM32SAM::CopyStress() { + // loop thought all the phonemes to be output + unsigned char pos = 0; //mem66 + while(1) { + // get the phomene + Y = phonemeindex[pos]; + + // exit at end of buffer + if(Y == 255) return; + + // if CONSONANT_FLAG set, skip - only vowels get stress + if((flags[Y] & 64) == 0) { + pos++; + continue; + } + // get the next phoneme + Y = phonemeindex[pos + 1]; + if(Y == 255) //prevent buffer overflow + { + pos++; + continue; + } else + // if the following phoneme is a vowel, skip + if((flags[Y] & 128) == 0) { + pos++; + continue; + } + + // get the stress value at the next position + Y = stress[pos + 1]; + + // if next phoneme is not stressed, skip + if(Y == 0) { + pos++; + continue; + } + + // if next phoneme is not a VOWEL OR ER, skip + if((Y & 128) != 0) { + pos++; + continue; + } + + // copy stress from prior phoneme to this one + stress[pos] = Y + 1; + + // advance pointer + pos++; + } +} + +// The input[] buffer contains a string of phonemes and stress markers along +// the lines of: +// +// DHAX KAET IHZ AH5GLIY. <0x9B> +// +// The byte 0x9B marks the end of the buffer. Some phonemes are 2 bytes +// long, such as "DH" and "AX". Others are 1 byte long, such as "T" and "Z". +// There are also stress markers, such as "5" and ".". +// +// The first character of the phonemes are stored in the table signInputTable1[]. +// The second character of the phonemes are stored in the table signInputTable2[]. +// The stress characters are arranged in low to high stress order in stressInputTable[]. +// +// The following process is used to parse the input[] buffer: +// +// Repeat until the <0x9B> character is reached: +// +// First, a search is made for a 2 character match for phonemes that do not +// end with the '*' (wildcard) character. On a match, the index of the phoneme +// is added to phonemeIndex[] and the buffer position is advanced 2 bytes. +// +// If this fails, a search is made for a 1 character match against all +// phoneme names ending with a '*' (wildcard). If this succeeds, the +// phoneme is added to phonemeIndex[] and the buffer position is advanced +// 1 byte. +// +// If this fails, search for a 1 character match in the stressInputTable[]. +// If this succeeds, the stress value is placed in the last stress[] table +// at the same index of the last added phoneme, and the buffer position is +// advanced by 1 byte. +// +// If this fails, return a 0. +// +// On success: +// +// 1. phonemeIndex[] will contain the index of all the phonemes. +// 2. The last index in phonemeIndex[] will be 255. +// 3. stress[] will contain the stress value for each phoneme + +// input[] holds the string of phonemes, each two bytes wide +// signInputTable1[] holds the first character of each phoneme +// signInputTable2[] holds te second character of each phoneme +// phonemeIndex[] holds the indexes of the phonemes after parsing input[] +// +// The parser scans through the input[], finding the names of the phonemes +// by searching signInputTable1[] and signInputTable2[]. On a match, it +// copies the index of the phoneme into the phonemeIndexTable[]. +// +// The character <0x9B> marks the end of text in input[]. When it is reached, +// the index 255 is placed at the end of the phonemeIndexTable[], and the +// function returns with a 1 indicating success. +int STM32SAM::Parser1() { + int i; + unsigned char sign1; + unsigned char sign2; + unsigned char position = 0; + X = 0; + A = 0; + Y = 0; + + // CLEAR THE STRESS TABLE + for(i = 0; i < 256; i++) stress[i] = 0; + + // THIS CODE MATCHES THE PHONEME LETTERS TO THE TABLE + // pos41078: + while(1) { + // GET THE FIRST CHARACTER FROM THE PHONEME BUFFER + sign1 = input[X]; + // TEST FOR 155 (�) END OF LINE MARKER + if(sign1 == 155) { + // MARK ENDPOINT AND RETURN + phonemeindex[position] = 255; //mark endpoint + // REACHED END OF PHONEMES, SO EXIT + return 1; //all ok + } + + // GET THE NEXT CHARACTER FROM THE BUFFER + X++; + sign2 = input[X]; + + // NOW sign1 = FIRST CHARACTER OF PHONEME, AND sign2 = SECOND CHARACTER OF PHONEME + + // TRY TO MATCH PHONEMES ON TWO TWO-CHARACTER NAME + // IGNORE PHONEMES IN TABLE ENDING WITH WILDCARDS + + // SET INDEX TO 0 + Y = 0; + pos41095: + + // GET FIRST CHARACTER AT POSITION Y IN signInputTable + // --> should change name to PhonemeNameTable1 + A = signInputTable1[Y]; + + // FIRST CHARACTER MATCHES? + if(A == sign1) { + // GET THE CHARACTER FROM THE PhonemeSecondLetterTable + A = signInputTable2[Y]; + // NOT A SPECIAL AND MATCHES SECOND CHARACTER? + if((A != '*') && (A == sign2)) { + // STORE THE INDEX OF THE PHONEME INTO THE phomeneIndexTable + phonemeindex[position] = Y; + + // ADVANCE THE POINTER TO THE phonemeIndexTable + position++; + // ADVANCE THE POINTER TO THE phonemeInputBuffer + X++; + + // CONTINUE PARSING + continue; + } + } + + // NO MATCH, TRY TO MATCH ON FIRST CHARACTER TO WILDCARD NAMES (ENDING WITH '*') + + // ADVANCE TO THE NEXT POSITION + Y++; + // IF NOT END OF TABLE, CONTINUE + if(Y != 81) goto pos41095; + + // REACHED END OF TABLE WITHOUT AN EXACT (2 CHARACTER) MATCH. + // THIS TIME, SEARCH FOR A 1 CHARACTER MATCH AGAINST THE WILDCARDS + + // RESET THE INDEX TO POINT TO THE START OF THE PHONEME NAME TABLE + Y = 0; + pos41134: + // DOES THE PHONEME IN THE TABLE END WITH '*'? + if(signInputTable2[Y] == '*') { + // DOES THE FIRST CHARACTER MATCH THE FIRST LETTER OF THE PHONEME + if(signInputTable1[Y] == sign1) { + // SAVE THE POSITION AND MOVE AHEAD + phonemeindex[position] = Y; + + // ADVANCE THE POINTER + position++; + + // CONTINUE THROUGH THE LOOP + continue; + } + } + Y++; + if(Y != 81) goto pos41134; //81 is size of PHONEME NAME table + + // FAILED TO MATCH WITH A WILDCARD. ASSUME THIS IS A STRESS + // CHARACTER. SEARCH THROUGH THE STRESS TABLE + + // SET INDEX TO POSITION 8 (END OF STRESS TABLE) + Y = 8; + + // WALK BACK THROUGH TABLE LOOKING FOR A MATCH + while((sign1 != stressInputTable[Y]) && (Y > 0)) { + // DECREMENT INDEX + Y--; + } + + // REACHED THE END OF THE SEARCH WITHOUT BREAKING OUT OF LOOP? + if(Y == 0) { + //mem[39444] = X; + //41181: JSR 42043 //Error + // FAILED TO MATCH ANYTHING, RETURN 0 ON FAILURE + return 0; + } + // SET THE STRESS FOR THE PRIOR PHONEME + stress[position - 1] = Y; + } //while +} + +//change phonemelength depedendent on stress +//void Code41203() +void STM32SAM::SetPhonemeLength() { + unsigned char A; + int position = 0; + while(phonemeindex[position] != 255) { + A = stress[position]; + //41218: BMI 41229 + if((A == 0) || ((A & 128) != 0)) { + phonemeLength[position] = phonemeLengthTable[phonemeindex[position]]; + } else { + phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]]; + } + position++; + } +} + +void STM32SAM::Code41240() { + unsigned char pos = 0; + + while(phonemeindex[pos] != 255) { + unsigned char index; //register AC + X = pos; + index = phonemeindex[pos]; + if((flags[index] & 2) == 0) { + pos++; + continue; + } else if((flags[index] & 1) == 0) { + Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); + Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); + pos += 3; + continue; + } + + do { + X++; + A = phonemeindex[X]; + } while(A == 0); + + if(A != 255) { + if((flags[A] & 8) != 0) { + pos++; + continue; + } + if((A == 36) || (A == 37)) { + pos++; // '/H' '/X' + continue; + } + } + + Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]); + Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]); + pos += 3; + }; +} + +// Rewrites the phonemes using the following rules: +// +// -> WX +// -> YX +// UL -> AX L +// UM -> AX M +// -> Q +// T R -> CH R +// D R -> J R +// R -> RX +// L -> LX +// G S -> G Z +// K -> KX +// G -> GX +// S P -> S B +// S T -> S D +// S K -> S G +// S KX -> S GX +// UW -> UX +// CH -> CH CH' (CH requires two phonemes to represent it) +// J -> J J' (J requires two phonemes to represent it) +// T -> DX +// D -> DX + +//void Code41397() +void STM32SAM::Parser2() { + unsigned char pos = 0; //mem66; + unsigned char mem58 = 0; + + // Loop through phonemes + while(1) { + // SET X TO THE CURRENT POSITION + X = pos; + // GET THE PHONEME AT THE CURRENT POSITION + A = phonemeindex[pos]; + + // Is phoneme pause? + if(A == 0) { + // Move ahead to the + pos++; + continue; + } + + // If end of phonemes flag reached, exit routine + if(A == 255) return; + + // Copy the current phoneme index to Y + Y = A; + + // RULE: + // -> WX + // -> YX + // Example: OIL, COW + + // Check for DIPHTONG + if((flags[A] & 16) == 0) goto pos41457; + + // Not a diphthong. Get the stress + mem58 = stress[pos]; + + // End in IY sound? + A = flags[Y] & 32; + + // If ends with IY, use YX, else use WX + if(A == 0) + A = 20; + else + A = 21; // 'WX' = 20 'YX' = 21 + //pos41443: + // Insert at WX or YX following, copying the stress + + Insert(pos + 1, A, mem59, mem58); + X = pos; + // Jump to ??? + goto pos41749; + + pos41457: + + // RULE: + // UL -> AX L + // Example: MEDDLE + + // Get phoneme + A = phonemeindex[X]; + // Skip this rule if phoneme is not UL + if(A != 78) goto pos41487; // 'UL' + A = 24; // 'L' //change 'UL' to 'AX L' + + pos41466: + // Get current phoneme stress + mem58 = stress[X]; + + // Change UL to AX + phonemeindex[X] = 13; // 'AX' + // Perform insert. Note code below may jump up here with different values + Insert(X + 1, A, mem59, mem58); + pos++; + // Move to next phoneme + continue; + + pos41487: + + // RULE: + // UM -> AX M + // Example: ASTRONOMY + + // Skip rule if phoneme != UM + if(A != 79) goto pos41495; // 'UM' + // Jump up to branch - replaces current phoneme with AX and continues + A = 27; // 'M' //change 'UM' to 'AX M' + + goto pos41466; + pos41495: + + // RULE: + // UN -> AX N + // Example: FUNCTION + + // Skip rule if phoneme != UN + if(A != 80) goto pos41503; // 'UN' + + // Jump up to branch - replaces current phoneme with AX and continues + A = 28; // 'N' //change UN to 'AX N' + + goto pos41466; + pos41503: + + // RULE: + // -> Q + // EXAMPLE: AWAY EIGHT + + Y = A; + // VOWEL set? + A = flags[A] & 128; + + // Skip if not a vowel + if(A != 0) { + // Get the stress + A = stress[X]; + + // If stressed... + if(A != 0) { + // Get the following phoneme + X++; + A = phonemeindex[X]; + // If following phoneme is a pause + + if(A == 0) { + // Get the phoneme following pause + X++; + Y = phonemeindex[X]; + + // Check for end of buffer flag + if(Y == 255) //buffer overflow + // ??? Not sure about these flags + A = 65 & 128; + else + // And VOWEL flag to current phoneme's flags + A = flags[Y] & 128; + + // If following phonemes is not a pause + if(A != 0) { + // If the following phoneme is not stressed + A = stress[X]; + if(A != 0) { + // 31 = 'Q' + Insert(X, 31, mem59, 0); + pos++; + continue; + } + } + } + } + } + + // RULES FOR PHONEMES BEFORE R + // T R -> CH R + // Example: TRACK + + // Get current position and phoneme + X = pos; + A = phonemeindex[pos]; + if(A != 23) goto pos41611; // 'R' + + // Look at prior phoneme + X--; + A = phonemeindex[pos - 1]; + //pos41567: + if(A == 69) // 'T' + { + phonemeindex[pos - 1] = 42; + goto pos41779; + } + + // RULES FOR PHONEMES BEFORE R + // D R -> J R + // Example: DRY + + // Prior phonemes D? + if(A == 57) // 'D' + { + // Change D to J + phonemeindex[pos - 1] = 44; + + goto pos41788; + } + + // RULES FOR PHONEMES BEFORE R + // R -> RX + // Example: ART + + // If vowel flag is set change R to RX + A = flags[A] & 128; + + if(A != 0) phonemeindex[pos] = 18; // 'RX' + + // continue to next phoneme + pos++; + continue; + + pos41611: + + // RULE: + // L -> LX + // Example: ALL + + // Is phoneme L? + if(A == 24) // 'L' + { + // If prior phoneme does not have VOWEL flag set, move to next phoneme + if((flags[phonemeindex[pos - 1]] & 128) == 0) { + pos++; + continue; + } + // Prior phoneme has VOWEL flag set, so change L to LX and move to next phoneme + + phonemeindex[X] = 19; // 'LX' + pos++; + continue; + } + + // RULE: + // G S -> G Z + // + // Can't get to fire - + // 1. The G -> GX rule intervenes + // 2. Reciter already replaces GS -> GZ + + // Is current phoneme S? + if(A == 32) // 'S' + { + // If prior phoneme is not G, move to next phoneme + if(phonemeindex[pos - 1] != 60) { + pos++; + continue; + } + // Replace S with Z and move on + + phonemeindex[pos] = 38; // 'Z' + pos++; + continue; + } + + // RULE: + // K -> KX + // Example: COW + + // Is current phoneme K? + if(A == 72) // 'K' + { + // Get next phoneme + Y = phonemeindex[pos + 1]; + // If at end, replace current phoneme with KX + if(Y == 255) + phonemeindex[pos] = 75; // ML : prevents an index out of bounds problem + else { + // VOWELS AND DIPHTONGS ENDING WITH IY SOUND flag set? + A = flags[Y] & 32; + + // Replace with KX + if(A == 0) phonemeindex[pos] = 75; // 'KX' + } + } else + + // RULE: + // G -> GX + // Example: GO + + // Is character a G? + if(A == 60) // 'G' + { + // Get the following character + unsigned char index = phonemeindex[pos + 1]; + + // At end of buffer? + if(index == 255) //prevent buffer overflow + { + pos++; + continue; + } else + // If diphtong ending with YX, move continue processing next phoneme + if((flags[index] & 32) != 0) { + pos++; + continue; + } + // replace G with GX and continue processing next phoneme + + phonemeindex[pos] = 63; // 'GX' + pos++; + continue; + } + + // RULE: + // S P -> S B + // S T -> S D + // S K -> S G + // S KX -> S GX + // Examples: SPY, STY, SKY, SCOWL + + Y = phonemeindex[pos]; + //pos41719: + // Replace with softer version? + A = flags[Y] & 1; + if(A == 0) goto pos41749; + A = phonemeindex[pos - 1]; + if(A != 32) // 'S' + { + A = Y; + goto pos41812; + } + // Replace with softer version + + phonemeindex[pos] = Y - 12; + pos++; + continue; + + pos41749: + + // RULE: + // UW -> UX + // + // Example: NEW, DEW, SUE, ZOO, THOO, TOO + + // UW -> UX + + A = phonemeindex[X]; + if(A == 53) // 'UW' + { + // ALVEOLAR flag set? + Y = phonemeindex[X - 1]; + A = flags2[Y] & 4; + // If not set, continue processing next phoneme + if(A == 0) { + pos++; + continue; + } + + phonemeindex[X] = 16; + pos++; + continue; + } + pos41779: + + // RULE: + // CH -> CH CH' (CH requires two phonemes to represent it) + // Example: CHEW + + if(A == 42) // 'CH' + { + // pos41783: + + Insert(X + 1, A + 1, mem59, stress[X]); + pos++; + continue; + } + + pos41788: + + // RULE: + // J -> J J' (J requires two phonemes to represent it) + // Example: JAY + + if(A == 44) // 'J' + { + Insert(X + 1, A + 1, mem59, stress[X]); + pos++; + continue; + } + + // Jump here to continue + pos41812: + + // RULE: Soften T following vowel + // NOTE: This rule fails for cases such as "ODD" + // T -> DX + // D -> DX + // Example: PARTY, TARDY + + // Past this point, only process if phoneme is T or D + + if(A != 69) // 'T' + if(A != 57) { + pos++; // 'D' + continue; + } + //pos41825: + + // If prior phoneme is not a vowel, continue processing phonemes + if((flags[phonemeindex[X - 1]] & 128) == 0) { + pos++; + continue; + } + + // Get next phoneme + X++; + A = phonemeindex[X]; + //pos41841 + // Is the next phoneme a pause? + if(A != 0) { + // If next phoneme is not a pause, continue processing phonemes + if((flags[A] & 128) == 0) { + pos++; + continue; + } + // If next phoneme is stressed, continue processing phonemes + // FIXME: How does a pause get stressed? + if(stress[X] != 0) { + pos++; + continue; + } + //pos41856: + // Set phonemes to DX + + phonemeindex[pos] = 30; // 'DX' + } else { + A = phonemeindex[X + 1]; + if(A == 255) //prevent buffer overflow + A = 65 & 128; + else + // Is next phoneme a vowel or ER? + A = flags[A] & 128; + + if(A != 0) phonemeindex[pos] = 30; // 'DX' + } + + pos++; + + } // while +} // parser 2 + +// Applies various rules that adjust the lengths of phonemes +// +// Lengthen or between and by 1.5 +// - decrease length by 1 +// - decrease vowel by 1/8th +// - increase vowel by 1/2 + 1 +// - set nasal = 5, consonant = 6 +// {optional silence} - shorten both to 1/2 + 1 +// - decrease by 2 + +//void Code48619() +void STM32SAM::AdjustLengths() { + // LENGTHEN VOWELS PRECEDING PUNCTUATION + // + // Search for punctuation. If found, back up to the first vowel, then + // process all phonemes between there and up to (but not including) the punctuation. + // If any phoneme is found that is a either a fricative or voiced, the duration is + // increased by (length * 1.5) + 1 + + // loop index + X = 0; + unsigned char index; + + // iterate through the phoneme list + unsigned char loopIndex = 0; + while(1) { + // get a phoneme + index = phonemeindex[X]; + + // exit loop if end on buffer token + if(index == 255) break; + + // not punctuation? + if((flags2[index] & 1) == 0) { + // skip + X++; + continue; + } + + // hold index + loopIndex = X; + + // Loop backwards from this point + pos48644: + + // back up one phoneme + X--; + + // stop once the beginning is reached + if(X == 0) break; + + // get the preceding phoneme + index = phonemeindex[X]; + + if(index != 255) //inserted to prevent access overrun + if((flags[index] & 128) == 0) goto pos48644; // if not a vowel, continue looping + + //pos48657: + do { + // test for vowel + index = phonemeindex[X]; + + if(index != 255) //inserted to prevent access overrun + // test for fricative/unvoiced or not voiced + if(((flags2[index] & 32) == 0) || ((flags[index] & 4) != 0)) //nochmal �berpr�fen + { + //A = flags[Y] & 4; + //if(A == 0) goto pos48688; + + // get the phoneme length + A = phonemeLength[X]; + + // change phoneme length to (length * 1.5) + 1 + A = (A >> 1) + A + 1; + + phonemeLength[X] = A; + } + // keep moving forward + X++; + } while(X != loopIndex); + // if (X != loopIndex) goto pos48657; + X++; + } // while + + // Similar to the above routine, but shorten vowels under some circumstances + + // Loop throught all phonemes + loopIndex = 0; + //pos48697 + + while(1) { + // get a phoneme + X = loopIndex; + index = phonemeindex[X]; + + // exit routine at end token + if(index == 255) return; + + // vowel? + A = flags[index] & 128; + if(A != 0) { + // get next phoneme + X++; + index = phonemeindex[X]; + + // get flags + if(index == 255) + mem56 = 65; // use if end marker + else + mem56 = flags[index]; + + // not a consonant + if((flags[index] & 64) == 0) { + // RX or LX? + if((index == 18) || (index == 19)) // 'RX' & 'LX' + { + // get the next phoneme + X++; + index = phonemeindex[X]; + + // next phoneme a consonant? + if((flags[index] & 64) != 0) { + // RULE: RX | LX + + // decrease length of vowel by 1 frame + phonemeLength[loopIndex]--; + } + // move ahead + loopIndex++; + continue; + } + // move ahead + loopIndex++; + continue; + } + + // Got here if not + + // not voiced + if((mem56 & 4) == 0) { + // Unvoiced + // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX + + // not an unvoiced plosive? + if((mem56 & 1) == 0) { + // move ahead + loopIndex++; + continue; + } + + // P*, T*, K*, KX + + // RULE: + // + + // move back + X--; + + // decrease length by 1/8th + mem56 = phonemeLength[X] >> 3; + phonemeLength[X] -= mem56; + + // move ahead + loopIndex++; + continue; + } + + // RULE: + // + + // decrease length + A = phonemeLength[X - 1]; + phonemeLength[X - 1] = (A >> 2) + A + 1; // 5/4*A + 1 + + // move ahead + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, M*, N*, NX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX + + //pos48821: + + // RULE: + // Set punctuation length to 6 + // Set stop consonant length to 5 + + // nasal? + if((flags2[index] & 8) != 0) { + // M*, N*, NX, + + // get the next phoneme + X++; + index = phonemeindex[X]; + + // end of buffer? + if(index == 255) + A = 65 & 2; //prevent buffer overflow + else + A = flags[index] & 2; // check for stop consonant + + // is next phoneme a stop consonant? + if(A != 0) + + // B*, D*, G*, GX, P*, T*, K*, KX + + { + // set stop consonant length to 6 + phonemeLength[X] = 6; + + // set nasal length to 5 + phonemeLength[X - 1] = 5; + } + // move to next phoneme + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX + + // RULE: {optional silence} + // Shorten both to (length/2 + 1) + + // (voiced) stop consonant? + if((flags[index] & 2) != 0) { + // B*, D*, G*, GX + + // move past silence + do { + // move ahead + X++; + index = phonemeindex[X]; + } while(index == 0); + + // check for end of buffer + if(index == 255) //buffer overflow + { + // ignore, overflow code + if((65 & 2) == 0) { + loopIndex++; + continue; + } + } else if((flags[index] & 2) == 0) { + // if another stop consonant, move ahead + loopIndex++; + continue; + } + + // RULE: {optional silence} + + // X gets overwritten, so hold prior X value for debug statement + // int debugX = X; + // shorten the prior phoneme length to (length/2 + 1) + phonemeLength[X] = (phonemeLength[X] >> 1) + 1; + X = loopIndex; + + // also shorten this phoneme length to (length/2 +1) + phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1; + + // move ahead + loopIndex++; + continue; + } + + // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, **, + + // RULE: + // Decrease by 2 + + // liquic consonant? + if((flags2[index] & 16) != 0) { + // R*, L*, W*, Y* + + // get the prior phoneme + index = phonemeindex[X - 1]; + + // prior phoneme a stop consonant> + if((flags[index] & 2) != 0) { + // Rule: + + // decrease the phoneme length by 2 frames (20 ms) + phonemeLength[X] -= 2; + } + } + + // move to next phoneme + loopIndex++; + continue; + } + // goto pos48701; +} + +// ------------------------------------------------------------------------- +// ML : Code47503 is division with remainder, and mem50 gets the sign +void STM32SAM::Code47503(unsigned char mem52) { + Y = 0; + if((mem53 & 128) != 0) { + mem53 = -mem53; + Y = 128; + } + mem50 = Y; + A = 0; + for(X = 8; X > 0; X--) { + int temp = mem53; + mem53 = mem53 << 1; + A = A << 1; + if(temp >= 128) A++; + if(A >= mem52) { + A = A - mem52; + mem53++; + } + } + + mem51 = A; + if((mem50 & 128) != 0) mem53 = -mem53; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Reciter +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::Code37055(unsigned char mem59) { + X = mem59; + X--; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; + return; +} + +void STM32SAM::Code37066(unsigned char mem58) { + X = mem58; + X++; + A = inputtemp[X]; + Y = A; + A = tab36376[Y]; +} + +unsigned char STM32SAM::GetRuleByte(unsigned short mem62, unsigned char Y) { + unsigned int address = mem62; + + if(mem62 >= 37541) { + address -= 37541; + return rules2[address + Y]; + } + address -= 32000; + return rules[address + Y]; +} + +int STM32SAM::TextToPhonemes(unsigned char* input) // Code36484 +{ + //unsigned char *tab39445 = &mem[39445]; //input and output + //unsigned char mem29; + unsigned char mem56; //output position for phonemes + unsigned char mem57; + unsigned char mem58; + unsigned char mem59; + unsigned char mem60; + unsigned char mem61; + unsigned short mem62; // memory position of current rule + + unsigned char mem64; // position of '=' or current character + unsigned char mem65; // position of ')' + unsigned char mem66; // position of '(' + unsigned char mem36653; + + inputtemp[0] = 32; + + // secure copy of input + // because input will be overwritten by phonemes + X = 1; + Y = 0; + do { + //pos36499: + A = input[Y] & 127; + if(A >= 112) + A = A & 95; + else if(A >= 96) + A = A & 79; + + inputtemp[X] = A; + X++; + Y++; + } while(Y != 255); + + X = 255; + inputtemp[X] = 27; + mem61 = 255; + +pos36550: + A = 255; + mem56 = 255; + +pos36554: + while(1) { + mem61++; + X = mem61; + A = inputtemp[X]; + mem64 = A; + if(A == '[') { + mem56++; + X = mem56; + A = 155; + input[X] = 155; + //goto pos36542; + // Code39771(); //Code39777(); + return 1; + } + + //pos36579: + if(A != '.') break; + X++; + Y = inputtemp[X]; + A = tab36376[Y] & 1; + if(A != 0) break; + mem56++; + X = mem56; + A = '.'; + input[X] = '.'; + } //while + + //pos36607: + A = mem64; + Y = A; + A = tab36376[A]; + mem57 = A; + if((A & 2) != 0) { + mem62 = 37541; + goto pos36700; + } + + //pos36630: + A = mem57; + if(A != 0) goto pos36677; + A = 32; + inputtemp[X] = ' '; + mem56++; + X = mem56; + if(X > 120) goto pos36654; + input[X] = A; + goto pos36554; + + // ----- + + //36653 is unknown. Contains position + +pos36654: + input[X] = 155; + A = mem61; + mem36653 = A; + // mem29 = A; // not used + // Code36538(); das ist eigentlich + return 1; + //Code39771(); + //go on if there is more input ??? + mem61 = mem36653; + goto pos36550; + +pos36677: + A = mem57 & 128; + if(A == 0) { + //36683: BRK + return 0; + } + + // go to the right rules for this character. + X = mem64 - 'A'; + mem62 = tab37489[X] | (tab37515[X] << 8); + + // ------------------------------------- + // go to next rule + // ------------------------------------- + +pos36700: + + // find next rule + Y = 0; + do { + mem62 += 1; + A = GetRuleByte(mem62, Y); + } while((A & 128) == 0); + Y++; + + //pos36720: + // find '(' + while(1) { + A = GetRuleByte(mem62, Y); + if(A == '(') break; + Y++; + } + mem66 = Y; + + //pos36732: + // find ')' + do { + Y++; + A = GetRuleByte(mem62, Y); + } while(A != ')'); + mem65 = Y; + + //pos36741: + // find '=' + do { + Y++; + A = GetRuleByte(mem62, Y); + A = A & 127; + } while(A != '='); + mem64 = Y; + + X = mem61; + mem60 = X; + + // compare the string within the bracket + Y = mem66; + Y++; + //pos36759: + while(1) { + mem57 = inputtemp[X]; + A = GetRuleByte(mem62, Y); + if(A != mem57) goto pos36700; + Y++; + if(Y == mem65) break; + X++; + mem60 = X; + } + + // the string in the bracket is correct + + //pos36787: + A = mem61; + mem59 = mem61; + +pos36791: + while(1) { + mem66--; + Y = mem66; + A = GetRuleByte(mem62, Y); + mem57 = A; + //36800: BPL 36805 + if((A & 128) != 0) goto pos37180; + X = A & 127; + A = tab36376[X] & 128; + if(A == 0) break; + X = mem59 - 1; + A = inputtemp[X]; + if(A != mem57) goto pos36700; + mem59 = X; + } + + //pos36833: + A = mem57; + if(A == ' ') goto pos36895; + if(A == '#') goto pos36910; + if(A == '.') goto pos36920; + if(A == '&') goto pos36935; + if(A == '@') goto pos36967; + if(A == '^') goto pos37004; + if(A == '+') goto pos37019; + if(A == ':') goto pos37040; + // Code42041(); //Error + //36894: BRK + return 0; + + // -------------- + +pos36895: + Code37055(mem59); + A = A & 128; + if(A != 0) goto pos36700; +pos36905: + mem59 = X; + goto pos36791; + + // -------------- + +pos36910: + Code37055(mem59); + A = A & 64; + if(A != 0) goto pos36905; + goto pos36700; + + // -------------- + +pos36920: + Code37055(mem59); + A = A & 8; + if(A == 0) goto pos36700; +pos36930: + mem59 = X; + goto pos36791; + + // -------------- + +pos36935: + Code37055(mem59); + A = A & 16; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if(A != 72) goto pos36700; + X--; + A = inputtemp[X]; + if((A == 67) || (A == 83)) goto pos36930; + goto pos36700; + + // -------------- + +pos36967: + Code37055(mem59); + A = A & 4; + if(A != 0) goto pos36930; + A = inputtemp[X]; + if(A != 72) goto pos36700; + if((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem59 = X; + goto pos36791; + + // -------------- + +pos37004: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36700; + +pos37014: + mem59 = X; + goto pos36791; + + // -------------- + +pos37019: + X = mem59; + X--; + A = inputtemp[X]; + if((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014; + goto pos36700; + // -------------- + +pos37040: + Code37055(mem59); + A = A & 32; + if(A == 0) goto pos36791; + mem59 = X; + goto pos37040; + + //--------------------------------------- + +pos37077: + X = mem58 + 1; + A = inputtemp[X]; + if(A != 'E') goto pos37157; + X++; + Y = inputtemp[X]; + X--; + A = tab36376[Y] & 128; + if(A == 0) goto pos37108; + X++; + A = inputtemp[X]; + if(A != 'R') goto pos37113; +pos37108: + mem58 = X; + goto pos37184; +pos37113: + if((A == 83) || (A == 68)) goto pos37108; // 'S' 'D' + if(A != 76) goto pos37135; // 'L' + X++; + A = inputtemp[X]; + if(A != 89) goto pos36700; + goto pos37108; + +pos37135: + if(A != 70) goto pos36700; + X++; + A = inputtemp[X]; + if(A != 85) goto pos36700; + X++; + A = inputtemp[X]; + if(A == 76) goto pos37108; + goto pos36700; + +pos37157: + if(A != 73) goto pos36700; + X++; + A = inputtemp[X]; + if(A != 78) goto pos36700; + X++; + A = inputtemp[X]; + if(A == 71) goto pos37108; + //pos37177: + goto pos36700; + + // ----------------------------------------- + +pos37180: + + A = mem60; + mem58 = A; + +pos37184: + Y = mem65 + 1; + + //37187: CPY 64 + // if(? != 0) goto pos37194; + if(Y == mem64) goto pos37455; + mem65 = Y; + //37196: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + X = A; + A = tab36376[X] & 128; + if(A == 0) goto pos37226; + X = mem58 + 1; + A = inputtemp[X]; + if(A != mem57) goto pos36700; + mem58 = X; + goto pos37184; +pos37226: + A = mem57; + if(A == 32) goto pos37295; // ' ' + if(A == 35) goto pos37310; // '#' + if(A == 46) goto pos37320; // '.' + if(A == 38) goto pos37335; // '&' + if(A == 64) goto pos37367; // '' + if(A == 94) goto pos37404; // '' + if(A == 43) goto pos37419; // '+' + if(A == 58) goto pos37440; // ':' + if(A == 37) goto pos37077; // '%' + //pos37291: + // Code42041(); //Error + //37294: BRK + return 0; + + // -------------- +pos37295: + Code37066(mem58); + A = A & 128; + if(A != 0) goto pos36700; +pos37305: + mem58 = X; + goto pos37184; + + // -------------- + +pos37310: + Code37066(mem58); + A = A & 64; + if(A != 0) goto pos37305; + goto pos36700; + + // -------------- + +pos37320: + Code37066(mem58); + A = A & 8; + if(A == 0) goto pos36700; + +pos37330: + mem58 = X; + goto pos37184; + + // -------------- + +pos37335: + Code37066(mem58); + A = A & 16; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if(A != 72) goto pos36700; + X++; + A = inputtemp[X]; + if((A == 67) || (A == 83)) goto pos37330; + goto pos36700; + + // -------------- + +pos37367: + Code37066(mem58); + A = A & 4; + if(A != 0) goto pos37330; + A = inputtemp[X]; + if(A != 72) goto pos36700; + if((A != 84) && (A != 67) && (A != 83)) goto pos36700; + mem58 = X; + goto pos37184; + + // -------------- + +pos37404: + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos36700; +pos37414: + mem58 = X; + goto pos37184; + + // -------------- + +pos37419: + X = mem58; + X++; + A = inputtemp[X]; + if((A == 69) || (A == 73) || (A == 89)) goto pos37414; + goto pos36700; + + // ---------------------- + +pos37440: + + Code37066(mem58); + A = A & 32; + if(A == 0) goto pos37184; + mem58 = X; + goto pos37440; +pos37455: + Y = mem64; + mem61 = mem60; + +pos37461: + //37461: LDA (62),y + A = GetRuleByte(mem62, Y); + mem57 = A; + A = A & 127; + if(A != '=') { + mem56++; + X = mem56; + input[X] = A; + } + + //37478: BIT 57 + //37480: BPL 37485 //not negative flag + if((mem57 & 128) == 0) goto pos37485; //??? + goto pos36554; +pos37485: + Y++; + goto pos37461; +} + +// Constructor + +STM32SAM::STM32SAM(uint32_t STM32SAM_SPEED /* = 5 */) { + STM32SAM_SPEED = STM32SAM_SPEED & 0x1f; // limit it from 0 to 31 + + _STM32SAM_SPEED = STM32SAM_SPEED; + + // set default voice + + speed = 72; + pitch = 64; + mouth = 128; + throat = 128; + + phonetic = 0; + singmode = 0; + + wait1 = 7; + wait2 = 6; + + mem59 = 0; + + oldtimetableindex = 0; +} + +STM32SAM::STM32SAM() { + _STM32SAM_SPEED = 7; + + // set default voice + + speed = 72; + pitch = 64; + mouth = 128; + throat = 128; + + phonetic = 0; + singmode = 0; + + wait1 = 7; + wait2 = 6; + + mem59 = 0; + + oldtimetableindex = 0; +} + +/* + STM32SAM::~STM32SAM() { + { + // TODO: end(); + } +*/ + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (variable string, phonetic, sing, pitch, speed, mouth, throat) +// STM32SAM say (sing off, phonetic off) (const string) +// STM32SAM say (sing off, phonetic off) (variable string) +// STM32SAM sing (sing on, phonetic off) (const string) +// STM32SAM sing (sing on, phonetic off) (variable string) +// STM32SAM sayPhonetic (sing off, phonetic on) (const string) +// STM32SAM sayPhonetic (sing off, phonetic on) (variable string) +// STM32SAM singPhonetic (sing on, phonetic on) (const string) +// STM32SAM singPhonetic (sing on, phonetic on) (variable string) +// STM32SAM voice (pitch, speed, mouth, throat) +// STM32SAM setPitch (pitch) +// STM32SAM setSpeed (speed) +// STM32SAM setMouth (mouth) +// STM32SAM setThroat (throat) +// +// +//////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (const string, phonetic, sing, pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +char to_upper_case(char c) { + if(c >= 'a' && c <= 'z') { + return c - 'a' + 'A'; + } + return c; +} + +void STM32SAM::sam( + const char* argv, + unsigned char _phonetic, + unsigned char _singmode, + unsigned char _pitch, + unsigned char _speed, + unsigned char _mouth, + unsigned char _throat) { + phonetic = _phonetic; + singmode = _singmode; + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; + + int i; + + for(i = 0; i < 256; i++) { + input[i] = argv[i]; + } + + for(i = 0; input[i] != 0; i++) { + if(i != 0) { + input[i] = to_upper_case((int)argv[i]); + } + } + + if(!phonetic) { + strncat(input, "[", 256); + if(!TextToPhonemes((unsigned char*)input)) { + // PrintUsage(); + return; + } + + } else { + strncat(input, "\x9b", 256); + } + + SetInput(input); + + if(!SAMMain()) { + return; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sam (variable string, phonetic, sing, pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sam( + char* argv, + unsigned char _phonetic, + unsigned char _singmode, + unsigned char _pitch, + unsigned char _speed, + unsigned char _mouth, + unsigned char _throat) { + phonetic = _phonetic; + singmode = _singmode; + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; + + int i; + + for(i = 0; i < 256; i++) { + input[i] = argv[i]; + } + + for(i = 0; input[i] != 0; i++) { + if(i != 0) { + input[i] = to_upper_case((int)argv[i]); + } + } + + if(!phonetic) { + strncat(input, "[", 256); + if(!TextToPhonemes((unsigned char*)input)) { + // PrintUsage(); + return; + } + + } else { + strncat(input, "\x9b", 256); + } + + SetInput(input); + + if(!SAMMain()) { + return; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM say(sing off, phonetic off) (const string) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::say(const char* argv) { + int i; + + phonetic = 0; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::say(char* argv) { + int i; + + phonetic = 0; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sing (sing on, phonetic off) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sing(const char* argv) { + int i; + + phonetic = 0; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::sing(char* argv) { + int i; + + phonetic = 0; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM sayPhonetic (sing off, phonetic on) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::sayPhonetic(const char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::sayPhonetic(char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM singPhonetic (sing on, phonetic on) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::singPhonetic(const char* argv) { + int i; + + phonetic = 1; + singmode = 1; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +void STM32SAM::singPhonetic(char* argv) { + int i; + + phonetic = 1; + singmode = 0; + + char const_input[256]; + + for(i = 0; i < 256; i++) { + const_input[i] = argv[i]; + } + + sam(const_input, phonetic, singmode, pitch, speed, mouth, throat); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM voice (pitch, speed, mouth, throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setVoice( + unsigned char _pitch /* = 64 */, + unsigned char _speed /* = 72 */, + unsigned char _mouth /* = 128 */, + unsigned char _throat /* = 128 */) { + pitch = _pitch; + speed = _speed; + mouth = _mouth; + throat = _throat; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setPitch (pitch) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setPitch(unsigned char _pitch /* = 64 */) { + pitch = _pitch; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setSpeed (speed) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setSpeed(unsigned char _speed /* = 72 */) { + speed = _speed; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setMouth (mouth) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setMouth(unsigned char _mouth /* = 128 */) { + mouth = _mouth; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// STM32SAM setThroat (throat) +// +//////////////////////////////////////////////////////////////////////////////////////////// + +void STM32SAM::setThroat(unsigned char _throat /* = 128 */) { + throat = _throat; +} +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Hardware +// +//////////////////////////////////////////////////////////////////////////////////////////// +// Hardware specifics, for easier porting to other microcontrollers + +// +// Set PA8 pin as PWM, at 256 timer ticks overflow (8bit resolution) + +#include +#include + +#define FURI_HAL_SPEAKER_TIMER TIM16 +#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 + +void STM32SAM::begin(void) { +#ifdef USE_ROGER_CORE + + pinMode(PA8, PWM); // audio output pin + + Timer1.setPeriod( + 4); // Can't set at 256 ticks, only in uS. First nearest uS is 4 (Roger core is only for bluepill, that means 72*4=288 ticks, or 128*4=512 ticks when overclocked. It's ok, just overall volume will be lower, because maximum volume will be 256/288 or 256/512) + +#endif + +#ifdef USE_STM32duino_CORE + pinMode(PA8, OUTPUT); + + PWM->pause(); + PWM->setMode(1, TIMER_OUTPUT_COMPARE_PWM1, PA8); // TIM1 CH1 (PA8) + PWM->setPrescaleFactor(1); + PWM->setOverflow(256, TICK_FORMAT); // 256 ticks overflow, no matter the CPU (timer) speed + PWM->resume(); + +#endif + + LL_TIM_InitTypeDef TIM_InitStruct; + memset(&TIM_InitStruct, 0, sizeof(LL_TIM_InitTypeDef)); + TIM_InitStruct.Prescaler = 4; + TIM_InitStruct.Autoreload = 255; + LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct; + memset(&TIM_OC_InitStruct, 0, sizeof(LL_TIM_OC_InitTypeDef)); + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 127; + LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); + LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); +} // begin + +inline void STM32SAM::SetAUDIO(unsigned char main_volume) { +#ifdef USE_ROGER_CORE + Timer1.setCompare(TIMER_CH1, main_volume); +#endif + +#ifdef USE_STM32duino_CORE + PWM->setCaptureCompare(1, main_volume, TICK_COMPARE_FORMAT); +#endif + + // if(main_volume > 64) { + // LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, 127); + // } else { + // LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, main_volume); + // } + + float data = main_volume; + data /= 255.0f; + data -= 0.5f; + data *= 4.0f; + data = tanhf(data); + + data += 0.5f; + data *= 255.0f; + + if(data < 0) { + data = 0; + } else if(data > 255) { + data = 255; + } + + LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, data); +} \ No newline at end of file diff --git a/applications/external/text2sam/stm32_sam.h b/applications/external/text2sam/stm32_sam.h new file mode 100644 index 000000000..910227ac3 --- /dev/null +++ b/applications/external/text2sam/stm32_sam.h @@ -0,0 +1,96 @@ +#include + +#ifndef __STM32SAM__ +#define __STM32SAM__ + +// SAM Text-To-Speech (TTS), ported from https://github.com/s-macke/SAM + +class STM32SAM { +public: + STM32SAM(uint32_t STM32SAM_SPEED); + STM32SAM(); + + void begin(void); + + void + sam(const char* argv, + unsigned char phonetic, + unsigned char singmode, + unsigned char pitch, + unsigned char speed, + unsigned char mouth, + unsigned char throat); + void + sam(char* argv, + unsigned char phonetic, + unsigned char singmode, + unsigned char pitch, + unsigned char speed, + unsigned char mouth, + unsigned char throat); + + void say(const char* argv); + void say(char* argv); + void sing(const char* argv); + void sing(char* argv); + void sayPhonetic(const char* argv); + void sayPhonetic(char* argv); + void singPhonetic(const char* argv); + void singPhonetic(char* argv); + void setVoice( + unsigned char _pitch = 64, + unsigned char _speed = 72, + unsigned char _mouth = 128, + unsigned char _throat = 128); + void setPitch(unsigned char _pitch = 64); + void setSpeed(unsigned char _speed = 72); + void setMouth(unsigned char _mouth = 128); + void setThroat(unsigned char _throat = 128); + +private: + void SetAUDIO(unsigned char main_volume); + + void Output8BitAry(int index, unsigned char ary[5]); + void Output8Bit(int index, unsigned char A); + unsigned char Read(unsigned char p, unsigned char Y); + void Write(unsigned char p, unsigned char Y, unsigned char value); + void RenderSample(unsigned char* mem66); + void Render(); + void AddInflection(unsigned char mem48, unsigned char phase1); + void SetMouthThroat(); + unsigned char trans(unsigned char mem39212, unsigned char mem39213); + void SetInput(char* _input); + void Init(); + int SAMMain(); + void PrepareOutput(); + void Insert( + unsigned char position /*var57*/, + unsigned char mem60, + unsigned char mem59, + unsigned char mem58); + void InsertBreath(); + void CopyStress(); + int Parser1(); + void SetPhonemeLength(); + void Code41240(); + void Parser2(); + void AdjustLengths(); + void Code47503(unsigned char mem52); + void Code37055(unsigned char mem59); + void Code37066(unsigned char mem58); + unsigned char GetRuleByte(unsigned short mem62, unsigned char Y); + int TextToPhonemes(unsigned char* input); // Code36484 + + uint32_t _STM32SAM_SPEED; + + unsigned char speed; + unsigned char pitch; + unsigned char mouth; + unsigned char throat; + + unsigned char phonetic; + unsigned char singmode; + +}; // STM32SAM class + +#endif \ No newline at end of file From e688f81b53b0138d80de4b609daf1f9fca5be647 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 01:43:28 +0100 Subject: [PATCH 209/370] Better text2sam --- applications/external/text2sam/sam_app.cpp | 117 +-------------------- 1 file changed, 2 insertions(+), 115 deletions(-) diff --git a/applications/external/text2sam/sam_app.cpp b/applications/external/text2sam/sam_app.cpp index 579cd349b..e28e9e1bc 100644 --- a/applications/external/text2sam/sam_app.cpp +++ b/applications/external/text2sam/sam_app.cpp @@ -1,6 +1,5 @@ #include #include -// #include #include #include @@ -11,20 +10,14 @@ #include #include #include -#include #include "stm32_sam.h" #define TAG "SAM" #define SAM_SAVE_PATH EXT_PATH("sam.txt") #define TEXT_BUFFER_SIZE 256 -// #define MESSAGES_BUFFER_SIZE 256 STM32SAM voice; -// FuriMutex* g_state_mutex; - -// FuriString* message; - typedef enum { EventTypeTick, EventTypeKey, @@ -36,10 +29,8 @@ typedef struct { } PluginEvent; typedef struct { - FuriMutex* mutex; ViewDispatcher* view_dispatcher; TextInput* text_input; - TextBox* text_box; char input[TEXT_BUFFER_SIZE]; } AppState; @@ -55,63 +46,40 @@ static void say_something(char* something) { static void text_input_callback(void* ctx) { AppState* app_state = (AppState*)ctx; - furi_mutex_acquire(app_state->mutex, FuriWaitForever); //FURI_LOG_D(TAG, "Input text: %s", app_state->input); // underscore_to_space(app_state->input); for(int i = 0; app_state->input[i] != '\0'; i++) { if(app_state->input[i] == '_') { app_state->input[i] = ' '; - } else { - app_state->input[i] = app_state->input[i]; } } - text_box_set_text(app_state->text_box, app_state->input); - view_dispatcher_switch_to_view(app_state->view_dispatcher, 1); - - furi_mutex_release(app_state->mutex); + say_something(app_state->input); } static bool back_event_callback(void* ctx) { const AppState* app_state = (AppState*)ctx; view_dispatcher_stop(app_state->view_dispatcher); - furi_mutex_release(app_state->mutex); return true; } static void sam_state_init(AppState* const app_state) { app_state->view_dispatcher = view_dispatcher_alloc(); app_state->text_input = text_input_alloc(); - app_state->text_box = text_box_alloc(); - text_box_set_font(app_state->text_box, TextBoxFontText); } static void sam_state_free(AppState* const app_state) { text_input_free(app_state->text_input); - text_box_free(app_state->text_box); view_dispatcher_remove_view(app_state->view_dispatcher, 0); - view_dispatcher_remove_view(app_state->view_dispatcher, 1); view_dispatcher_free(app_state->view_dispatcher); free(app_state); } -// static void app_draw_callback(Canvas* canvas, void* ctx) {} - -static void app_input_callback(InputEvent* input_event, void* ctx) { - furi_assert(ctx); - - FuriMessageQueue* event_queue = ctx; - furi_message_queue_put(event_queue, input_event, FuriWaitForever); -} - static void save_message(FuriString* save_string) { Storage* storage = (Storage*)furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); - // uint16_t bytes_read = 0; if(storage_file_open(file, SAM_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - // bytes_read = - // storage_file_write(file, save_string, sizeof(save_string)); storage_file_write(file, save_string, TEXT_BUFFER_SIZE); } storage_file_close(file); @@ -130,71 +98,15 @@ static bool load_messages() { storage_file_free(file); furi_record_close(RECORD_STORAGE); return bytes_read == TEXT_BUFFER_SIZE; - // FlipperFormat* ff = flipper_format_file_alloc(storage); - - // DialogsApp* dialogs = (DialogsApp*)furi_record_open(RECORD_DIALOGS); - // DialogsFileBrowserOptions browser_options; - // dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL); - // FuriString* map_file = (FuriString*)"/ext/sam"; //furi_string_alloc(); - // // furi_string_set(map_file, "/ext/sam"); - // if(!storage_file_exists(storage, ANY_PATH("sam"))) { - // storage_common_mkdir(storage, ANY_PATH("sam")); //Make Folder If dir not exist - // } - - // bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); - - // furi_record_close(RECORD_DIALOGS); - - // // if user didn't choose anything, free everything and exit - // if(!res) { - // FURI_LOG_I(TAG, "exit"); - // flipper_format_free(ff); - // furi_record_close(RECORD_STORAGE); - - // furi_string_free(message); - - // view_port_enabled_set(view_port, false); - // gui_remove_view_port(gui, view_port); - // view_port_free(view_port); - // free(g_state_mutex); - // furi_message_queue_free(event_queue); - - // furi_record_close(RECORD_GUI); - // return; - // } } extern "C" int32_t sam_app(void* p) { UNUSED(p); app_state = (AppState*)malloc(sizeof(AppState)); - // g_state_mutex = furi_mutex_alloc(FuriMutexTypeRecursive); - FURI_LOG_D(TAG, "Running sam_state_init"); sam_state_init(app_state); - app_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!app_state->mutex) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - free(app_state); - return 255; - } - - // message = furi_string_alloc(); - - // Configure view port - ViewPort* view_port = view_port_alloc(); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - // view_port_draw_callback_set(view_port, app_draw_callback, g_state_mutex); - view_port_input_callback_set(view_port, app_input_callback, event_queue); - - // // Register view port in GUI - // Gui* gui = (Gui*)furi_record_open(RECORD_GUI); - // gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // InputEvent event; - - //TODO: get message from file FURI_LOG_D(TAG, "Assigning text input callback"); load_messages(); @@ -204,22 +116,16 @@ extern "C" int32_t sam_app(void* p) { app_state, app_state->input, TEXT_BUFFER_SIZE, - //clear default text - false); + false); //clear default text text_input_set_header_text(app_state->text_input, "Input"); - // Open GUI and register view_port Gui* gui = (Gui*)furi_record_open(RECORD_GUI); - //gui_add_view_port(gui, view_port, GuiLayerFullscreen); - FURI_LOG_D(TAG, "Enabling view dispatcher queue"); view_dispatcher_enable_queue(app_state->view_dispatcher); FURI_LOG_D(TAG, "Adding text input view to dispatcher"); view_dispatcher_add_view( app_state->view_dispatcher, 0, text_input_get_view(app_state->text_input)); - view_dispatcher_add_view( - app_state->view_dispatcher, 1, text_box_get_view(app_state->text_box)); FURI_LOG_D(TAG, "Attaching view dispatcher to GUI"); view_dispatcher_attach_to_gui(app_state->view_dispatcher, gui, ViewDispatcherTypeFullscreen); @@ -229,28 +135,9 @@ extern "C" int32_t sam_app(void* p) { view_dispatcher_switch_to_view(app_state->view_dispatcher, 0); view_dispatcher_run(app_state->view_dispatcher); - // for(bool running = true; running;) { - // PluginEvent event; - // FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - // FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); - // if(event_status == FuriStatusOk) { - // if(event.input.key == InputKeyOk) { - say_something(app_state->input); save_message((FuriString*)app_state->input); - FURI_LOG_D(TAG, "Spoken text: %s", app_state->input); - // } - // if(event.input.key == InputKeyBack) { - // running = false; - // } - // } - // } - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); - view_port_free(view_port); - furi_mutex_free(app_state->mutex); - // furi_mutex_free(g_state_mutex); sam_state_free(app_state); return 0; From 46ad4594ebfac4b020e416e06ce4f417b0b04c34 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 01:43:53 +0100 Subject: [PATCH 210/370] Fix manifest --- applications/external/text2sam/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/text2sam/application.fam b/applications/external/text2sam/application.fam index d278ef8c3..7239938a6 100644 --- a/applications/external/text2sam/application.fam +++ b/applications/external/text2sam/application.fam @@ -12,6 +12,6 @@ App( stack_size=4 * 1024, # stack_size=2 * 1024, fap_icon="icon.png", - fap_category="Media", + fap_category="Music", order=20, ) From 5dc410b7ae80ea629dc754912b3ddcb5815d27c2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 03:09:33 +0100 Subject: [PATCH 211/370] Add some new games --- applications/external/4inrow/4inrow.c | 320 ++++++++ applications/external/4inrow/4inrow_10px.png | Bin 0 -> 200 bytes applications/external/4inrow/application.fam | 13 + applications/external/reversi/LICENSE | 21 + applications/external/reversi/application.fam | 15 + applications/external/reversi/game_reversi.c | 356 +++++++++ .../external/reversi/game_reversi.png | Bin 0 -> 235 bytes applications/external/reversi/reversi.c | 168 ++++ applications/external/reversi/reversi.h | 21 + applications/external/rootoflife/LICENSE | 21 + .../external/rootoflife/application.fam | 14 + .../rootoflife/images/place_error.png | Bin 0 -> 159 bytes .../external/rootoflife/images/place_ok.png | Bin 0 -> 163 bytes .../rootoflife/images/root_reroll.png | Bin 0 -> 174 bytes .../external/rootoflife/images/score.png | Bin 0 -> 192 bytes .../external/rootoflife/images/tree.png | Bin 0 -> 165 bytes .../rootoflife/roots_of_life_10px.png | Bin 0 -> 1679 bytes .../external/rootoflife/roots_of_life_game.c | 750 ++++++++++++++++++ applications/external/scorched_tanks/LICENSE | 674 ++++++++++++++++ .../external/scorched_tanks/application.fam | 12 + .../scorched_tanks/scorchedTanks_10px.png | Bin 0 -> 614 bytes .../scorched_tanks/scorched_tanks_game_app.c | 546 +++++++++++++ applications/external/simonsays/LICENSE | 674 ++++++++++++++++ .../external/simonsays/application.fam | 15 + .../simonsays/images/Connected_62x31.png | Bin 0 -> 3765 bytes .../simonsays/images/Cry_dolph_55x52.png | Bin 0 -> 3898 bytes .../simonsays/images/DolphinLeft_56x48.png | Bin 0 -> 1416 bytes .../simonsays/images/DolphinMafia_115x62.png | Bin 0 -> 2504 bytes .../images/DolphinNiceLeft_96x59.png | Bin 0 -> 2459 bytes .../images/DolphinNiceRight_96x59.png | Bin 0 -> 1066 bytes .../simonsays/images/DolphinRight_56x48.png | Bin 0 -> 601 bytes .../simonsays/images/DolphinTalking_59x63.png | Bin 0 -> 1177 bytes .../simonsays/images/DolphinWait_61x59.png | Bin 0 -> 2023 bytes .../simonsays/images/WarningDolphin_45x42.png | Bin 0 -> 1139 bytes .../external/simonsays/images/board.png | Bin 0 -> 826 bytes .../external/simonsays/images/down.png | Bin 0 -> 954 bytes .../external/simonsays/images/left.png | Bin 0 -> 921 bytes .../external/simonsays/images/right.png | Bin 0 -> 948 bytes applications/external/simonsays/images/up.png | Bin 0 -> 867 bytes applications/external/simonsays/simon_says.c | 666 ++++++++++++++++ .../external/simonsays/simon_says.png | Bin 0 -> 133 bytes .../{snake_game => snake_2}/application.fam | 9 +- applications/external/snake_2/snake_10px.png | Bin 0 -> 178 bytes .../snake_game.c => snake_2/snake_20.c} | 161 +++- .../external/snake_game/snake_10px.png | Bin 158 -> 0 bytes applications/external/t_rex_runner/LICENSE | 674 ++++++++++++++++ .../external/t_rex_runner/application.fam | 13 + .../external/t_rex_runner/assets/Cactus.png | Bin 0 -> 110 bytes .../external/t_rex_runner/assets/Dino.png | Bin 0 -> 4339 bytes .../external/t_rex_runner/assets/DinoRun0.png | Bin 0 -> 149 bytes .../external/t_rex_runner/assets/DinoRun1.png | Bin 0 -> 152 bytes .../t_rex_runner/assets/HorizonLine0.png | Bin 0 -> 5289 bytes .../external/t_rex_runner/trexrunner.c | 271 +++++++ .../external/t_rex_runner/trexrunner_icon.png | Bin 0 -> 110 bytes .../uncut_assets/HorizonLine0.png | Bin 0 -> 203 bytes .../uncut_assets/HorizonLine1.png | Bin 0 -> 272 bytes 56 files changed, 5390 insertions(+), 24 deletions(-) create mode 100644 applications/external/4inrow/4inrow.c create mode 100644 applications/external/4inrow/4inrow_10px.png create mode 100644 applications/external/4inrow/application.fam create mode 100644 applications/external/reversi/LICENSE create mode 100644 applications/external/reversi/application.fam create mode 100644 applications/external/reversi/game_reversi.c create mode 100644 applications/external/reversi/game_reversi.png create mode 100644 applications/external/reversi/reversi.c create mode 100644 applications/external/reversi/reversi.h create mode 100644 applications/external/rootoflife/LICENSE create mode 100644 applications/external/rootoflife/application.fam create mode 100644 applications/external/rootoflife/images/place_error.png create mode 100644 applications/external/rootoflife/images/place_ok.png create mode 100644 applications/external/rootoflife/images/root_reroll.png create mode 100644 applications/external/rootoflife/images/score.png create mode 100644 applications/external/rootoflife/images/tree.png create mode 100644 applications/external/rootoflife/roots_of_life_10px.png create mode 100644 applications/external/rootoflife/roots_of_life_game.c create mode 100644 applications/external/scorched_tanks/LICENSE create mode 100644 applications/external/scorched_tanks/application.fam create mode 100644 applications/external/scorched_tanks/scorchedTanks_10px.png create mode 100644 applications/external/scorched_tanks/scorched_tanks_game_app.c create mode 100644 applications/external/simonsays/LICENSE create mode 100644 applications/external/simonsays/application.fam create mode 100644 applications/external/simonsays/images/Connected_62x31.png create mode 100644 applications/external/simonsays/images/Cry_dolph_55x52.png create mode 100644 applications/external/simonsays/images/DolphinLeft_56x48.png create mode 100644 applications/external/simonsays/images/DolphinMafia_115x62.png create mode 100644 applications/external/simonsays/images/DolphinNiceLeft_96x59.png create mode 100644 applications/external/simonsays/images/DolphinNiceRight_96x59.png create mode 100644 applications/external/simonsays/images/DolphinRight_56x48.png create mode 100644 applications/external/simonsays/images/DolphinTalking_59x63.png create mode 100644 applications/external/simonsays/images/DolphinWait_61x59.png create mode 100644 applications/external/simonsays/images/WarningDolphin_45x42.png create mode 100644 applications/external/simonsays/images/board.png create mode 100644 applications/external/simonsays/images/down.png create mode 100644 applications/external/simonsays/images/left.png create mode 100644 applications/external/simonsays/images/right.png create mode 100644 applications/external/simonsays/images/up.png create mode 100644 applications/external/simonsays/simon_says.c create mode 100644 applications/external/simonsays/simon_says.png rename applications/external/{snake_game => snake_2}/application.fam (55%) create mode 100644 applications/external/snake_2/snake_10px.png rename applications/external/{snake_game/snake_game.c => snake_2/snake_20.c} (61%) delete mode 100644 applications/external/snake_game/snake_10px.png create mode 100644 applications/external/t_rex_runner/LICENSE create mode 100644 applications/external/t_rex_runner/application.fam create mode 100644 applications/external/t_rex_runner/assets/Cactus.png create mode 100644 applications/external/t_rex_runner/assets/Dino.png create mode 100644 applications/external/t_rex_runner/assets/DinoRun0.png create mode 100644 applications/external/t_rex_runner/assets/DinoRun1.png create mode 100644 applications/external/t_rex_runner/assets/HorizonLine0.png create mode 100644 applications/external/t_rex_runner/trexrunner.c create mode 100644 applications/external/t_rex_runner/trexrunner_icon.png create mode 100644 applications/external/t_rex_runner/uncut_assets/HorizonLine0.png create mode 100644 applications/external/t_rex_runner/uncut_assets/HorizonLine1.png diff --git a/applications/external/4inrow/4inrow.c b/applications/external/4inrow/4inrow.c new file mode 100644 index 000000000..e9b7b6c69 --- /dev/null +++ b/applications/external/4inrow/4inrow.c @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include +#include + +static int matrix[6][7] = {0}; +static int cursorx = 3; +static int cursory = 5; +static int player = 1; +static int scoreX = 0; +static int scoreO = 0; + +typedef struct { + FuriMutex* mutex; +} FourInRowState; + +void init() { + for(size_t i = 0; i < 6; i++) { + for(size_t j = 0; j < 7; j++) { + matrix[i][j] = 0; + } + } + cursorx = 3; + cursory = 5; + player = 1; +} + +const NotificationSequence end = { + &message_vibro_on, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_note_ds4, + &message_delay_10, + &message_sound_off, + &message_delay_10, + + &message_vibro_off, + NULL, +}; + +void intToStr(int num, char* str) { + int i = 0, sign = 0; + + if(num < 0) { + num = -num; + sign = 1; + } + + do { + str[i++] = num % 10 + '0'; + num /= 10; + } while(num > 0); + + if(sign) { + str[i++] = '-'; + } + + str[i] = '\0'; + + // Reverse the string + int j, len = i; + char temp; + for(j = 0; j < len / 2; j++) { + temp = str[j]; + str[j] = str[len - j - 1]; + str[len - j - 1] = temp; + } +} + +int next_height(int x) { + if(matrix[0][x] != 0) { + return -1; + } + for(size_t y = 1; y < 6; y++) { + if(matrix[y][x] != 0) { + return y - 1; + } + } + return 5; +} + +int wincheck() { + for(size_t y = 0; y <= 2; y++) { + for(size_t x = 0; x <= 6; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y + 1][x] && + matrix[y][x] == matrix[y + 2][x] && matrix[y][x] == matrix[y + 3][x]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 0; y <= 5; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y][x + 1] && + matrix[y][x] == matrix[y][x + 2] && matrix[y][x] == matrix[y][x + 3]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 0; y <= 2; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y + 1][x + 1] && + matrix[y][x] == matrix[y + 2][x + 2] && matrix[y][x] == matrix[y + 3][x + 3]) { + return matrix[y][x]; + } + } + } + + for(size_t y = 3; y <= 5; y++) { + for(size_t x = 0; x <= 3; x++) { + if(matrix[y][x] != 0 && matrix[y][x] == matrix[y - 1][x + 1] && + matrix[y][x] == matrix[y - 2][x + 2] && matrix[y][x] == matrix[y - 3][x + 3]) { + return matrix[y][x]; + } + } + } + + bool tf = true; + for(size_t y = 0; y < 6; y++) { + for(size_t x = 0; x < 7; x++) { + if(matrix[y][x] == 0) { + tf = false; + } + } + } + if(tf) { + return 0; + } + + return -1; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const FourInRowState* fourinrow_state = ctx; + + furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever); + canvas_clear(canvas); + + if(wincheck() != -1) { + canvas_set_font(canvas, FontPrimary); + + if(wincheck() == 0) { + canvas_draw_str(canvas, 30, 35, "Draw! O_o"); + } + if(wincheck() == 1) { + canvas_draw_str(canvas, 30, 35, "Player X win!"); + } + if(wincheck() == 2) { + canvas_draw_str(canvas, 30, 35, "Player O win!"); + } + + furi_mutex_release(fourinrow_state->mutex); + + return; + } + + for(size_t i = 0; i < 6; i++) { + for(size_t j = 0; j < 7; j++) { + char el[2]; + switch(matrix[i][j]) { + case 0: + strcpy(el, "_\0"); + break; + + case 1: + strcpy(el, "X\0"); + break; + + case 2: + strcpy(el, "O\0"); + break; + } + canvas_draw_str(canvas, j * 10 + 10, i * 10 + 10, el); + } + } + canvas_draw_str(canvas, cursorx * 10 + 8, cursory * 10 + 10, "[ ]"); + + if(player == 1) { + canvas_draw_str(canvas, 80, 10, "Turn: X"); + } + if(player == 2) { + canvas_draw_str(canvas, 80, 10, "Turn: O"); + } + char scX[1]; + intToStr(scoreX, scX); + char scO[1]; + intToStr(scoreO, scO); + + canvas_draw_str(canvas, 80, 20, "X:"); + canvas_draw_str(canvas, 90, 20, scX); + + canvas_draw_str(canvas, 80, 30, "O:"); + canvas_draw_str(canvas, 90, 30, scO); + + furi_mutex_release(fourinrow_state->mutex); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t four_in_row_app(void* p) { + UNUSED(p); + + // Текущее событие типа InputEvent + InputEvent event; + // Очередь событий на 8 элементов размера InputEvent + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + FourInRowState* fourinrow_state = malloc(sizeof(FourInRowState)); + + fourinrow_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex + if(!fourinrow_state->mutex) { + FURI_LOG_E("4inRow", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(fourinrow_state); + return 255; + } + + // dolphin_deed(DolphinDeedPluginGameStart); + + // Создаем новый view port + ViewPort* view_port = view_port_alloc(); + // Создаем callback отрисовки, без контекста + view_port_draw_callback_set(view_port, draw_callback, fourinrow_state); + // Создаем callback нажатий на клавиши, в качестве контекста передаем + // нашу очередь сообщений, чтоб запихивать в неё эти события + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Создаем GUI приложения + Gui* gui = furi_record_open(RECORD_GUI); + // Подключаем view port к GUI в полноэкранном режиме + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notification, &sequence_display_backlight_enforce_on); + + // Бесконечный цикл обработки очереди событий + while(1) { + // Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста) + // и проверяем, что у нас получилось это сделать + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + furi_mutex_acquire(fourinrow_state->mutex, FuriWaitForever); + // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения + if(wincheck() != -1) { + notification_message(notification, &end); + furi_delay_ms(1000); + if(wincheck() == 1) { + scoreX++; + } + if(wincheck() == 2) { + scoreO++; + } + init(); + } + + if(event.type == InputTypePress) { + if(event.key == InputKeyOk) { + int nh = next_height(cursorx); + if(nh != -1) { + matrix[nh][cursorx] = player; + player = 3 - player; + } + } + if(event.key == InputKeyUp) { + //cursory--; + } + if(event.key == InputKeyDown) { + //cursory++; + } + if(event.key == InputKeyLeft) { + if(cursorx > 0) { + cursorx--; + } + } + if(event.key == InputKeyRight) { + if(cursorx < 6) { + cursorx++; + } + } + if(event.key == InputKeyBack) { + break; + } + } + view_port_update(view_port); + furi_mutex_release(fourinrow_state->mutex); + } + + // Clear notification + notification_message_block(notification, &sequence_display_backlight_enforce_auto); + furi_record_close(RECORD_NOTIFICATION); + + // Специальная очистка памяти, занимаемой очередью + furi_message_queue_free(event_queue); + + // Чистим созданные объекты, связанные с интерфейсом + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_mutex_free(fourinrow_state->mutex); + furi_record_close(RECORD_GUI); + free(fourinrow_state); + + return 0; +} diff --git a/applications/external/4inrow/4inrow_10px.png b/applications/external/4inrow/4inrow_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..ef3e18f20d04c696f5cb2ae5757168b216888dd1 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ih{XE z)7O>#Ig5xmyIQ8VV=Pcevcxr_#5q4VH#M(>!MP|ku_QG`p**uBL&4qCHz2%`PaLSo z$ +#include +#include +#include +#include +#include "reversi.h" + +#define FRAME_LEFT 3 +#define FRAME_TOP 3 +#define FRAME_CELL_SIZE 7 + +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/game_reversi.save" + +typedef enum { AppScreenGame, AppScreenMenu } AppScreen; + +typedef struct { + GameState game; + AppScreen screen; + uint8_t selected_menu_item; + FuriMutex* mutex; +} AppState; + +#define MENU_ITEMS_COUNT 2 +static const char* popup_menu_strings[] = {"Resume", "New Game"}; + +static void draw_menu(Canvas* const canvas, const AppState* app_state); +static void gray_canvas(Canvas* const canvas); + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +static void draw_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const AppState* app_state = ctx; + furi_mutex_acquire(app_state->mutex, FuriWaitForever); + + const GameState* game_state = &app_state->game; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + for(uint8_t i = 0; i <= BOARD_SIZE; i++) { + canvas_draw_line( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i, + FRAME_TOP, + FRAME_LEFT + FRAME_CELL_SIZE * i, + FRAME_TOP + FRAME_CELL_SIZE * BOARD_SIZE); + canvas_draw_line( + canvas, + FRAME_LEFT, + FRAME_TOP + FRAME_CELL_SIZE * i, + FRAME_LEFT + FRAME_CELL_SIZE * BOARD_SIZE, + FRAME_TOP + FRAME_CELL_SIZE * i); + } + // + // draw cursor + canvas_set_color(canvas, ColorWhite); + canvas_draw_frame( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * game_state->cursor_x, + FRAME_TOP + FRAME_CELL_SIZE * game_state->cursor_y, + FRAME_CELL_SIZE + 1, + FRAME_CELL_SIZE + 1); + + canvas_set_color(canvas, ColorBlack); + // draw pieces + int blacks = 0, whites = 0; + const int radius = FRAME_CELL_SIZE >> 1; + for(uint8_t i = 0; i < BOARD_SIZE; i++) { + for(uint8_t j = 0; j < BOARD_SIZE; j++) { + if(!game_state->board[i][j]) { + continue; + } + if(game_state->board[i][j] == BLACK) { + canvas_draw_disc( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i + radius + 1, + FRAME_TOP + FRAME_CELL_SIZE * j + radius + 1, + radius); + blacks++; + } else { + canvas_draw_circle( + canvas, + FRAME_LEFT + FRAME_CELL_SIZE * i + radius + 1, + FRAME_TOP + FRAME_CELL_SIZE * j + radius + 1, + radius); + whites++; + } + } + } + + canvas_set_font(canvas, FontPrimary); + // draw score + char score_str[25]; + memset(score_str, 0, sizeof(score_str)); + snprintf(score_str, sizeof(score_str), "%d - %d", whites, blacks); + + canvas_draw_str_aligned(canvas, 70, 3, AlignLeft, AlignTop, score_str); + + canvas_set_font(canvas, FontSecondary); + if(game_state->is_game_over) { + canvas_draw_str_aligned(canvas, 70, 20, AlignLeft, AlignTop, "Game over"); + + canvas_draw_str_aligned( + canvas, + 70, + FRAME_TOP + FRAME_CELL_SIZE * BOARD_SIZE, + AlignLeft, + AlignBottom, + "Press OK"); + + canvas_set_font(canvas, FontPrimary); + + if(whites == blacks) { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "DRAW"); + } else if( + ((game_state->human_color == WHITE) && whites > blacks) || + ((game_state->human_color == BLACK) && blacks > whites)) { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "YOU WIN"); + } else { + canvas_draw_str_aligned(canvas, 70, 30, AlignLeft, AlignTop, "YOU LOSE"); + } + } else if(game_state->current_player == game_state->human_color) { + canvas_draw_str_aligned(canvas, 70, 12, AlignLeft, AlignTop, "Your turn"); + } else { + canvas_draw_str_aligned(canvas, 70, 12, AlignLeft, AlignTop, "Computer turn"); + } + + if(app_state->screen == AppScreenMenu) { + draw_menu(canvas, app_state); + } + + furi_mutex_release(app_state->mutex); +} + +static void draw_menu(Canvas* const canvas, const AppState* app_state) { + gray_canvas(canvas); + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, 28, 16, 72, 32, 4); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 28, 16, 72, 32, 4); + + for(int i = 0; i < MENU_ITEMS_COUNT; i++) { + if(i == app_state->selected_menu_item) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 34, 20 + 12 * i, 60, 12); + } + + canvas_set_color(canvas, i == app_state->selected_menu_item ? ColorWhite : ColorBlack); + canvas_draw_str_aligned( + canvas, 64, 26 + 12 * i, AlignCenter, AlignCenter, popup_menu_strings[i]); + } +} + +static void gray_canvas(Canvas* const canvas) { + canvas_set_color(canvas, ColorWhite); + for(int x = 0; x < 128; x += 2) { + for(int y = 0; y < 64; y++) { + canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y); + } + } +} + +bool load_game(GameState* game_state) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* file = storage_file_alloc(storage); + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { + bytes_readed = storage_file_read(file, game_state, sizeof(GameState)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); + + return bytes_readed == sizeof(GameState); +} + +void save_game(const GameState* game_state) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, game_state, sizeof(GameState)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +bool handle_key_game(GameState* game_state, InputKey key) { + switch(key) { + case InputKeyBack: + save_game(game_state); + return false; + break; + case InputKeyOk: + if(game_state->is_game_over) { + init_game(game_state); + save_game(game_state); + } else { + human_move(game_state); + } + break; + case InputKeyUp: + if(game_state->cursor_y > 0) { + game_state->cursor_y--; + } else { + game_state->cursor_y = BOARD_SIZE - 1; + } + break; + case InputKeyDown: + if(game_state->cursor_y < BOARD_SIZE - 1) { + game_state->cursor_y++; + } else { + game_state->cursor_y = 0; + } + break; + case InputKeyLeft: + if(game_state->cursor_x > 0) { + game_state->cursor_x--; + } else { + game_state->cursor_x = BOARD_SIZE - 1; + } + break; + case InputKeyRight: + if(game_state->cursor_x < BOARD_SIZE - 1) { + game_state->cursor_x++; + } else { + game_state->cursor_x = 0; + } + break; + default: + break; + } + return true; +} + +bool handle_key_menu(AppState* app_state, InputKey key) { + switch(key) { + case InputKeyUp: + if(app_state->selected_menu_item > 0) { + app_state->selected_menu_item--; + } + break; + case InputKeyDown: + if(app_state->selected_menu_item < MENU_ITEMS_COUNT - 1) { + app_state->selected_menu_item++; + } + break; + case InputKeyOk: + if(app_state->selected_menu_item == 1) { + // new game + init_game(&app_state->game); + save_game(&app_state->game); + } + app_state->screen = AppScreenGame; + break; + default: + break; + } + return true; +} + +// returns `true` if the event loop should keep going +bool handle_key(AppState* app_state, InputKey key) { + GameState* game_state = &app_state->game; + + switch(app_state->screen) { + case AppScreenGame: + return handle_key_game(game_state, key); + break; + case AppScreenMenu: + return handle_key_menu(app_state, key); + break; + } + return true; +} + +int32_t game_reversi_app() { + AppState app_state; + app_state.screen = AppScreenGame; + if(!load_game(&app_state.game)) { + init_game(&app_state.game); + } + + app_state.mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!app_state.mutex) { + return 255; + } + InputEvent input; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, &app_state); + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + bool is_finished = false; + + while(!is_finished) { + // check if it's computer's turn + if(!app_state.game.is_game_over && + (app_state.game.current_player != app_state.game.human_color)) { + computer_move(&app_state.game); + } + FuriStatus event_status = furi_message_queue_get(event_queue, &input, FuriWaitForever); + if(event_status == FuriStatusOk) { + // handle only press event, ignore repeat/release events + + if(input.type == InputTypeLong && input.key == InputKeyOk && + app_state.screen == AppScreenGame) { + furi_mutex_acquire(app_state.mutex, FuriWaitForever); + app_state.selected_menu_item = 0; + app_state.screen = AppScreenMenu; + view_port_update(view_port); + furi_mutex_release(app_state.mutex); + continue; + } + if(input.type != InputTypePress) continue; + + furi_mutex_acquire(app_state.mutex, FuriWaitForever); + is_finished = !handle_key(&app_state, input.key); + view_port_update(view_port); + furi_mutex_release(app_state.mutex); + } + } + + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + + view_port_free(view_port); + + furi_message_queue_free(event_queue); + + furi_mutex_free(app_state.mutex); + + return 0; +} diff --git a/applications/external/reversi/game_reversi.png b/applications/external/reversi/game_reversi.png new file mode 100644 index 0000000000000000000000000000000000000000..3a321471b96c16186e80b43c8659d1b26b5d5bd5 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0Jq(*qA z`T8 zhmtY(m4zHeNhYV)vz~NWa`wf$W0ST$_|x(0y3*^~dgFLrS=B#57F+v3HhQ}HxvX= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && board[r][c] == opponent) { + int k = 2; + while(true) { + r += i; + c += j; + if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break; + if(board[r][c] == player) return true; + if(board[r][c] == 0) break; + k++; + } + } + } + } + return false; +} + +// Check if the game is over by checking if there are no more moves left for +// either player +bool is_game_over(int8_t board[BOARD_SIZE][BOARD_SIZE]) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(is_legal_move(board, i, j, BLACK) || is_legal_move(board, i, j, WHITE)) { + return false; + } + } + } + return true; +} + +bool has_legal_moves(int8_t board[BOARD_SIZE][BOARD_SIZE], int8_t player_color) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(is_legal_move(board, i, j, player_color)) { + return true; + } + } + } + return false; +} + +// Calculate the heuristic value of the current board. This function can +// be replaced with a more complex evaluation function that takes into +// account factors such as mobility, piece square tables, etc. +int heuristic(int8_t board[BOARD_SIZE][BOARD_SIZE]) { + int white = 0, black = 0; + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(board[i][j] == 1) white++; + if(board[i][j] == -1) black++; + } + } + return white - black; +} + +// Make a move on the board and capture any opponent pieces +void make_move(GameState* state, int x, int y, int player) { + state->board[x][y] = player; + int opponent = -player; + for(int i = -1; i <= 1; i++) { + for(int j = -1; j <= 1; j++) { + if(i == 0 && j == 0) continue; + int r = x + i, c = y + j; + if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && + state->board[r][c] == opponent) { + int k = 2; + while(true) { + r += i; + c += j; + if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break; + if(state->board[r][c] == player) { + r -= i; + c -= j; + while(r != x || c != y) { + state->board[r][c] = player; + r -= i; + c -= j; + } + break; + } + if(state->board[r][c] == 0) break; + k++; + } + } + } + } + state->is_game_over = is_game_over(state->board); +} + +void init_game(GameState* state) { + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + state->board[i][j] = 0; + } + } + + // Place the initial pieces + int mid = BOARD_SIZE / 2; + state->board[mid - 1][mid - 1] = WHITE; + state->board[mid][mid] = WHITE; + state->board[mid - 1][mid] = BLACK; + state->board[mid][mid - 1] = BLACK; + + state->cursor_x = mid - 1; + state->cursor_y = mid + 1; + + // Set up turn order + state->human_color = WHITE; + state->current_player = WHITE; + + state->is_game_over = false; +} + +void human_move(GameState* game_state) { + if(game_state->current_player != game_state->human_color) { + return; + } + + if(is_legal_move( + game_state->board, + game_state->cursor_x, + game_state->cursor_y, + game_state->current_player)) { + make_move( + game_state, game_state->cursor_x, game_state->cursor_y, game_state->current_player); + game_state->current_player = -game_state->current_player; + } +} + +void computer_move(GameState* game_state) { + if(game_state->current_player == game_state->human_color) { + return; + } + int best_row = -1, best_col = -1, best_score = -1000000; + for(int i = 0; i < BOARD_SIZE; i++) { + for(int j = 0; j < BOARD_SIZE; j++) { + if(!is_legal_move(game_state->board, i, j, game_state->current_player)) { + continue; + } + int score = heuristic(game_state->board); + if(score > best_score) { + best_score = score; + best_row = i; + best_col = j; + } + } + } + if(best_row != -1) { + make_move(game_state, best_row, best_col, game_state->current_player); + } + if(has_legal_moves(game_state->board, game_state->human_color)) { + game_state->current_player = -game_state->current_player; + } +} diff --git a/applications/external/reversi/reversi.h b/applications/external/reversi/reversi.h new file mode 100644 index 000000000..ee4588838 --- /dev/null +++ b/applications/external/reversi/reversi.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#define BLACK 1 +#define WHITE -1 +#define BOARD_SIZE 8 + +typedef struct { + int8_t board[BOARD_SIZE][BOARD_SIZE]; + int8_t current_player; + int8_t human_color; + uint8_t cursor_x; + uint8_t cursor_y; + uint8_t is_game_over; +} GameState; + +void init_game(GameState* state); +void computer_move(GameState* game_state); +void human_move(GameState* game_state); diff --git a/applications/external/rootoflife/LICENSE b/applications/external/rootoflife/LICENSE new file mode 100644 index 000000000..765f2a64b --- /dev/null +++ b/applications/external/rootoflife/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Kirill Korepanov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/rootoflife/application.fam b/applications/external/rootoflife/application.fam new file mode 100644 index 000000000..8dfe98599 --- /dev/null +++ b/applications/external/rootoflife/application.fam @@ -0,0 +1,14 @@ +App( + appid="roots_of_life", + name="Roots of Life", + apptype=FlipperAppType.EXTERNAL, + entry_point="roots_of_life_game_app", + cdefines=["APP_ROOTS_OF_LIFE_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=30, + fap_icon="roots_of_life_10px.png", + fap_category="Games", + fap_icon_assets="images", + fap_icon_assets_symbol="roots_of_life_game", +) diff --git a/applications/external/rootoflife/images/place_error.png b/applications/external/rootoflife/images/place_error.png new file mode 100644 index 0000000000000000000000000000000000000000..9032c65322a0a85f7364c67737cdc56a4b721fbb GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcfQS0b$0e+I-SLK}Sy)#}JL+CQSej~LyBFO(}d#0NeWUo ze@cWM7FqmT&{@$(aMMf4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcfQS0b$0e+I-SLL03-~#}JL+wdWoA7zB7+E?)clzDF>4%9PgqnM(dEp3SnE@@vmG z{#9AC6gTe~ HDWM4fl&>_% literal 0 HcmV?d00001 diff --git a/applications/external/rootoflife/images/root_reroll.png b/applications/external/rootoflife/images/root_reroll.png new file mode 100644 index 0000000000000000000000000000000000000000..996b5ff8110079f1df73192e97512fad71417ac9 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcfQS0b$0e+I-SL!2nMe#}JL+-U*(33$yQE#-Ur_Jv4$-pWrDz0u?uVU&0j5v{#Uc(CKE4{j(G3H Se^x-V89ZJ6T-G@yGywn@BsNa~ literal 0 HcmV?d00001 diff --git a/applications/external/rootoflife/images/score.png b/applications/external/rootoflife/images/score.png new file mode 100644 index 0000000000000000000000000000000000000000..73bf140d4db41eafb435c50e99c24713dc4485f4 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^AT~D#8<2F%laT;YEa{HEjtmSN`?>!lvI6;>1s;*b z3=DinK$vl=HlH+5Fv-)!F+?M{bb_N$g8~n8^#A|qsoBboh5UY=(+!g6HD?%YUM?Md zR9WdKla2K~#?7-%ntYahuKD-u)g8{0B3!23xmJ8`xnQLD9M?Ohoxf~3wuD^RH2*cL k3;&#@4oWM0&x9UfZL9HS;(TiJ6lfcRr>mdKI;Vst0L7_2=Kufz literal 0 HcmV?d00001 diff --git a/applications/external/rootoflife/images/tree.png b/applications/external/rootoflife/images/tree.png new file mode 100644 index 0000000000000000000000000000000000000000..4f424270ca65e9fd59ecc276244a6da07f2c7c8f GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4v7|ftIx;Y9?C1WI$O`0h7I;J! zGcfQS0b$0e+I-SLL3d9V#}JL+-X2CS1_ci0zyFu}uDv;>g>Ti)buRo$*1t|lx;$Dm zxxb>qaew&9=L>&}2yV(qx;M4a<=vy6yDx;qcAT7{@;o>swrQvE@9Rg*{D4L>c)I$z JtaD0e0ssaIHrfCH literal 0 HcmV?d00001 diff --git a/applications/external/rootoflife/roots_of_life_10px.png b/applications/external/rootoflife/roots_of_life_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..49d72986ddd0ed5bd1b7fccab89e796b1ef23339 GIT binary patch literal 1679 zcmcIlOOM-B6m|uz+Nmm`@(@CmFz#j$WBdBXkJ!`9f`q99lBk-3ijby2&pR9%MKg4kFYkhvQtw@fv(_+S z>ITdBPDz%me~&0Hm2p7X5B?-9rB^(>misi%zO zJZ*8_n-*$q$|7Q{AaVXm7zezcJy|F!4OoNZ%$1;OSVp$2=`CDwG}|%oQ>fw~G=X~1 z2LZ{+(ZD)=k-G*q1A&Rn-W! z81m^@7uYvA-6_&n*iYIv;1oD!8FDPW9U#-9ahS<_UMad6*un2gm_g}=<@d44UMW|-P#ia%Luk?Ku0)F>vp^~zhj+=| z(R5B|`&|a4hZKJT%XmBQla$pukm>(yF7;mGGqQWQ^rIF2f0SOPm3B$oVK6vVsR%|b zM$Dur>c8tXes{+>n!;hm70cOhh0Sa{vlysT7`_jz|MoHr?zLv4)|%gZ@A~>#;fa$= z2Y>wd`TO6!_2O%Li{^c&Zr*;Kw*PY8zV=i0;rzAH$B%!q{;gNj(o6f#{_ynKhtpSY s9lUw<(_#Jl(dgWz=fD2!cYflL(XR`CK4dN2DE +#include +#include +#include +#include +#include +#include + +#include "roots_of_life_game_icons.h" + +#define TAG "RootsOfLife" + +// Flipper +#define FLIPPER_LCD_WIDTH 128 +#define FLIPPER_LCD_HEIGHT 64 + +// General +#define GROUND_HEIGHT 10 +#define CELL_SIZE 3 +#define FIELD_START_X 0 +#define FIELD_START_Y (GROUND_HEIGHT + 1) +#define CELLS_X (FLIPPER_LCD_WIDTH / CELL_SIZE) +#define CELLS_Y ((FLIPPER_LCD_HEIGHT - GROUND_HEIGHT) / CELL_SIZE) +#define CELLS_TOTAL (CELLS_Y * CELLS_X) +#define CELL(Y, X) (Y * CELLS_X + X) + +// Root Spawn +#define ROOT_SIZE_X 7 +#define ROOT_SIZE_Y 7 +#define ROOT(Y, X) ((Y)*ROOT_SIZE_X + (X)) + +#define SPAWN_DIRECTIONS 2 +#define GROW_STEPS 4 +#define GROW_SAME_DIRECTION_CHANCE 70 +#define RANDOM_GROW_ATTEMPTS 4 +#define RANDOM_GROW_CHANCE 50 + +// UI +#define BLINK_PERIOD 12 +#define BLINK_HIDE_FRAMES 5 +#define TREE_HEIGHT 10 +#define PICKUP_FREQUENCY 10 + +// Game +#define REROLLS_MAX 5 +#define SCORE_FACTOR 10 + +#define PICKUPS_MIN 1 +#define PICKUPS_MAX 5 +#define PICKUPS_POINTS_FACTOR 10 + +typedef enum { EventTypeTick, EventTypeKey } EventType; + +typedef enum { + R_NONE = 0, + R_UP = 0b1000, + R_DOWN = 0b0100, + R_LEFT = 0b0010, + R_RIGHT = 0b0001 +} Direction; + +typedef enum { StageStart, StageRun, StageOver } GameStage; + +typedef struct { + bool initialDraw; + + GameStage stage; + int tick; + + bool* filledCells; + char* cells; + bool* pickups; + int collectedPickups; + + bool* filledRootBase; + char* rootBase; + + int rootSizeX; + int rootSizeY; + bool* filledRoot; + char* root; + + int pX, pY; + + int rerolls; + int score; + FuriMutex* mutex; +} GameState; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +static Direction rand_dir() { + int r = rand() % 4; + return 1 << r; +} + +static Direction reverse_dir(Direction dir) { + switch(dir) { + case R_UP: + return R_DOWN; + case R_DOWN: + return R_UP; + case R_LEFT: + return R_RIGHT; + case R_RIGHT: + return R_LEFT; + + default: + return R_NONE; + } +} + +static int rand_range(int min, int max) { + return min + rand() % (max - min); +} +static bool rand_chance(int chance) { + return (rand() % 100) < chance; +} + +static bool has_intersection(char cellA, char cellB) { + return cellA & cellB; +} + +static int root_index(GameState* state, int y, int x) { + return y * state->rootSizeX + x; +} + +static void set_cell(GameState* state, int y, int x, char cellRoot) { + int c = CELL(y, x); + state->filledCells[c] = true; + state->cells[c] = cellRoot; +} + +static void game_state_init(GameState* state) { + state->initialDraw = false; + state->tick = 0; + + // Init field arrays + state->filledCells = (bool*)malloc(CELLS_TOTAL * sizeof(bool)); + state->cells = (char*)malloc(CELLS_TOTAL * sizeof(char)); + state->pickups = (bool*)malloc(CELLS_TOTAL * sizeof(char)); + + state->rootBase = (char*)malloc(ROOT_SIZE_X * ROOT_SIZE_Y * sizeof(char)); + state->filledRootBase = (bool*)malloc(ROOT_SIZE_X * ROOT_SIZE_Y * sizeof(bool)); + state->root = NULL; + state->filledRoot = NULL; + + for(int i = 0; i < CELLS_TOTAL; i++) { + state->filledCells[i] = false; + state->cells[i] = R_NONE; + state->pickups[i] = false; + } +} + +static void free_root(GameState* state) { + if(state->root) free(state->root); + if(state->filledRoot) free(state->filledRoot); +} + +static void game_state_free(GameState* state) { + free(state->filledCells); + free(state->cells); + free(state->pickups); + + free(state->rootBase); + free(state->filledRootBase); + + free_root(state); +} + +/*static bool has_root(GameState* state, int x, int y) { + return x >= 0 && x < ROOT_SIZE_X && y >= 0 && y < ROOT_SIZE_Y && + state->filledRootBase[ROOT(y, x)]; +}*/ + +static void generate_new_root(GameState* state) { + for(int i = 0; i < ROOT_SIZE_X * ROOT_SIZE_Y; i++) { + state->filledRootBase[i] = false; + state->rootBase[i] = R_NONE; + } + + int cX = ROOT_SIZE_X / 2; + int cY = ROOT_SIZE_Y / 2; + int c = ROOT(cY, cX); + state->filledRootBase[c] = true; + + for(int i = 0; i < SPAWN_DIRECTIONS; i++) { + int pX = cX, pY = cY; + Direction oldDir = rand_dir(); + for(int g = 0; g < GROW_STEPS; g++) { + Direction dir = rand_chance(GROW_SAME_DIRECTION_CHANCE) ? oldDir : rand_dir(); + oldDir = dir; + + int nX = pX - (dir & R_LEFT ? 1 : 0) + (dir & R_RIGHT ? 1 : 0); + int nY = pY - (dir & R_UP ? 1 : 0) + (dir & R_DOWN ? 1 : 0); + if(nX < 0 || nY < 0 || nX >= ROOT_SIZE_X || nY >= ROOT_SIZE_Y) continue; + + int n = ROOT(nY, nX); + state->filledRootBase[n] = true; + + // Connect points + int p = ROOT(pY, pX); + state->rootBase[p] |= dir; + state->rootBase[n] |= reverse_dir(dir); + + // Grow from new point + pX = nX; + pY = nY; + } + } + + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int c = ROOT(y, x); + if(!state->filledRootBase[c]) continue; + + /* + if(has_root(state, x - 1, y)) state->rootBase[c] |= R_LEFT; + if(has_root(state, x + 1, y)) state->rootBase[c] |= R_RIGHT; + if(has_root(state, x, y - 1)) state->rootBase[c] |= R_UP; + if(has_root(state, x, y + 1)) state->rootBase[c] |= R_DOWN; + */ + + for(int r = 0; r < RANDOM_GROW_ATTEMPTS; r++) { + if(!rand_chance(RANDOM_GROW_CHANCE)) continue; + state->rootBase[c] |= rand_dir(); + } + } + } + + // Copy root to real root + int minX = cX, maxX = cX, minY = cY, maxY = cY; + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int r = ROOT(y, x); + if(!state->filledRootBase[r]) continue; + + minX = MIN(minX, x); + maxX = MAX(maxX, x); + minY = MIN(minY, y); + maxY = MAX(maxY, y); + } + } + + // Clone to real root + state->rootSizeX = maxX - minX + 1; + state->rootSizeY = maxY - minY + 1; + free_root(state); + + state->root = (char*)malloc(state->rootSizeX * state->rootSizeY * sizeof(char)); + state->filledRoot = (bool*)malloc(state->rootSizeX * state->rootSizeY * sizeof(bool)); + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int c = root_index(state, y, x); + int r = ROOT(y + minY, x + minX); + state->filledRoot[c] = state->filledRootBase[r]; + state->root[c] = state->rootBase[r]; + } + } +} + +static bool in_borders(int x, int y) { + return x >= 0 && y >= 0 && x < CELLS_X && y < CELLS_Y; +} +static char get_cell(GameState* state, int x, int y) { + if(!in_borders(x, y)) return R_NONE; + return state->cells[CELL(y, x)]; +} + +static bool get_filled_cell(GameState* state, int x, int y) { + if(!in_borders(x, y)) return false; + return state->filledCells[CELL(y, x)]; +} + +static bool can_place_root(GameState* state) { + bool hasConnection = false; + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int r = root_index(state, y, x); + if(!state->filledRoot[r]) { + continue; + } + char root = state->root[r]; + + int rY = y + state->pY; + int rX = x + state->pX; + + // Check if colliding + if(get_filled_cell(state, rX, rY)) { + char cell = get_cell(state, rX, rY); + if(has_intersection(cell, root)) { + return false; + } + hasConnection = true; + } + + // Check neighbours + hasConnection |= (root & R_RIGHT) && (get_cell(state, rX + 1, rY) & R_LEFT); + hasConnection |= (root & R_LEFT) && (get_cell(state, rX - 1, rY) & R_RIGHT); + hasConnection |= (root & R_UP) && (get_cell(state, rX, rY - 1) & R_DOWN); + hasConnection |= (root & R_DOWN) && (get_cell(state, rX, rY + 1) & R_UP); + } + } + + return hasConnection; +} + +static bool try_place_root(GameState* state) { + if(!can_place_root(state)) return false; + + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int r = root_index(state, y, x); + if(!state->filledRoot[r]) continue; + + int rY = y + state->pY; + int rX = x + state->pX; + + // Root may be out of borders in rare cases (after new cpawn changed its size), just ignore that part + if(in_borders(rX, rY)) { + int c = CELL(rY, rX); + + state->filledCells[c] = true; + state->cells[c] |= state->root[r]; + } + } + } + + return true; +} + +static void reset_level(GameState* state) { + state->stage = StageStart; + state->tick = 0; + + for(int i = 0; i < CELLS_TOTAL; i++) { + state->filledCells[i] = false; + state->cells[i] = R_NONE; + } + + generate_new_root(state); + + // Starting cells + int midX = CELLS_X / 2; + set_cell(state, 0, midX, R_UP | R_DOWN); + set_cell(state, 1, midX, R_UP | R_DOWN | R_LEFT | R_RIGHT); + set_cell(state, 1, midX - 1, R_RIGHT | R_DOWN); + set_cell(state, 1, midX + 1, R_LEFT | R_DOWN); + set_cell(state, 2, midX, R_UP); + + state->pX = midX; + state->pY = 4; + + state->rerolls = REROLLS_MAX; + state->score = 0; + + state->collectedPickups = 0; + for(int i = 0, n = rand_range(PICKUPS_MIN, PICKUPS_MAX); i < n; i++) { + int x = rand_range(0, CELLS_X); + int y = rand_range(0, CELLS_Y); + state->pickups[CELL(y, x)] = true; + } +} + +static void recalculate_score(GameState* state) { + int score = 0; + for(int i = 0; i < CELLS_TOTAL; i++) { + if(state->filledCells[i]) score++; + } + + for(int i = 0; i < CELLS_TOTAL; i++) { + if(!state->pickups[i] || !state->filledCells[i]) continue; + + state->pickups[i] = false; + state->collectedPickups++; + state->rerolls++; + } + + state->score = (score + state->collectedPickups * PICKUPS_POINTS_FACTOR) * SCORE_FACTOR; +} + +static void draw_root_cell(Canvas* canvas, char root, int y, int x, bool isHidden) { + int posX = FIELD_START_X + x * CELL_SIZE + 1, posY = FIELD_START_Y + y * CELL_SIZE + 1; + canvas_draw_dot(canvas, posX, posY); + + if(isHidden) { + canvas_set_color(canvas, ColorXOR); + } + + if(root & R_UP) canvas_draw_dot(canvas, posX, posY - 1); + if(root & R_DOWN) canvas_draw_dot(canvas, posX, posY + 1); + if(root & R_LEFT) canvas_draw_dot(canvas, posX - 1, posY); + if(root & R_RIGHT) canvas_draw_dot(canvas, posX + 1, posY); + + if(isHidden) { + canvas_set_color(canvas, ColorBlack); + } +} + +static void draw_placed_roots(Canvas* canvas, GameState* state) { + for(int y = 0; y < CELLS_Y; y++) { + for(int x = 0; x < CELLS_X; x++) { + int c = CELL(y, x); + if(!state->filledCells[c]) continue; + draw_root_cell(canvas, state->cells[c], y, x, false); + } + } +} + +static void draw_pickup(Canvas* canvas, GameState* state, int y, int x) { + int posX = FIELD_START_X + x * CELL_SIZE + 1, posY = FIELD_START_Y + y * CELL_SIZE + 1; + + int stage = state->tick / PICKUP_FREQUENCY; + + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX + 1, posY); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX, posY + 1); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX - 1, posY); + if(stage++ % 4 < 3) canvas_draw_dot(canvas, posX, posY - 1); +} + +static void draw_pickups(Canvas* canvas, GameState* state) { + for(int y = 0; y < CELLS_Y; y++) { + for(int x = 0; x < CELLS_X; x++) { + int c = CELL(y, x); + if(!state->pickups[c]) continue; + draw_pickup(canvas, state, y, x); + } + } +} + +static void draw_active_root(Canvas* canvas, GameState* state) { + bool isHidden = (state->tick % BLINK_PERIOD) < BLINK_HIDE_FRAMES; + + for(int y = 0; y < state->rootSizeY; y++) { + for(int x = 0; x < state->rootSizeX; x++) { + int c = root_index(state, y, x); + if(!state->filledRoot[c]) continue; + + int realX = x + state->pX; + int realY = y + state->pY; + draw_root_cell(canvas, state->root[c], realY, realX, isHidden); + } + } +} + +#if DRAW_DEBUG +static void draw_generated_root(Canvas* canvas, GameState* state) { + bool isHidden = (state->tick % BLINK_PERIOD) < BLINK_HIDE_FRAMES; + + for(int y = 0; y < ROOT_SIZE_Y; y++) { + for(int x = 0; x < ROOT_SIZE_X; x++) { + int c = ROOT(y, x); + if(!state->filledRootBase[c]) continue; + + int realX = x + 1; + int realY = y + 1; + draw_root_cell(canvas, state->rootBase[c], realY, realX, isHidden); + } + } +} +#endif + +static void draw_ground(Canvas* canvas, GameState* state) { + canvas_draw_line(canvas, 0, GROUND_HEIGHT, FLIPPER_LCD_WIDTH, GROUND_HEIGHT); + UNUSED(state); +} + +static void draw_tree(Canvas* canvas, GameState* state) { + canvas_draw_icon(canvas, FLIPPER_LCD_WIDTH / 2 - 5, GROUND_HEIGHT - TREE_HEIGHT, &I_tree); + UNUSED(state); +} + +static void draw_placement(Canvas* canvas, GameState* state) { + bool canPlace = can_place_root(state); + canvas_draw_icon(canvas, FLIPPER_LCD_WIDTH - 10, 0, canPlace ? &I_place_ok : &I_place_error); +} + +static void draw_rerolls(Canvas* canvas, GameState* state) { + UNUSED(canvas); + UNUSED(state); + + canvas_draw_icon(canvas, 0, 0, &I_root_reroll); + + // Ugh + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "%d", MAX(0, state->rerolls)); + canvas_draw_str(canvas, 11, 9, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); +} + +static void draw_score(Canvas* canvas, GameState* state) { + UNUSED(canvas); + UNUSED(state); + + int x = FLIPPER_LCD_WIDTH / 2 + 15; + canvas_draw_icon(canvas, x, 0, &I_score); + + // Ugh + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "%d", MAX(0, state->score)); + canvas_draw_str(canvas, x + 11, 9, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); +} + +static void draw_gui(Canvas* canvas, GameState* state) { + draw_ground(canvas, state); + draw_tree(canvas, state); + draw_placement(canvas, state); + draw_rerolls(canvas, state); + draw_score(canvas, state); +} + +static void draw_center_box(Canvas* canvas, int w2, int h2, int margin) { + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, x - margin - 1, y - margin - 1, (w2 + margin + 1) * 2, (h2 + margin + 1) * 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, x - margin, y - margin, (w2 + margin) * 2, (h2 + margin) * 2); +} + +static void draw_start_ui(Canvas* canvas, GameState* state) { + int w2 = 40; + int margin = 3; + int h2 = 10; + draw_center_box(canvas, w2, h2, margin); + + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + canvas_draw_str(canvas, x + 1, y + 9, " Grow your roots "); + canvas_draw_str(canvas, x + 1, y + 18, "Press [OK] to start"); + + UNUSED(state); +} + +static void draw_end_ui(Canvas* canvas, GameState* state) { + int w2 = 46; + int margin = 3; + int h2 = 15; + draw_center_box(canvas, w2, h2, margin); + + int x = FLIPPER_LCD_WIDTH / 2 - w2; + int y = FLIPPER_LCD_HEIGHT / 2 - h2; + + canvas_draw_str(canvas, x + 1, y + 9, " Game Over "); + + FuriString* tmp_string = furi_string_alloc(); + furi_string_printf(tmp_string, "You've got %d points", MAX(0, state->score)); + canvas_draw_str(canvas, x + 1, y + 19, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); + + canvas_draw_str(canvas, x + 2, y + 29, "Press [OK] to restart"); + + int h = 13, w = 54; + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 0, FLIPPER_LCD_HEIGHT - h, w + 1, h + 1); + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 0, FLIPPER_LCD_HEIGHT - h, w, h); + canvas_draw_str(canvas, 2, FLIPPER_LCD_HEIGHT - 3, "by @Xorboo"); + UNUSED(state); +} + +static void roots_draw_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + GameState* state = ctx; + furi_mutex_acquire(state->mutex, FuriWaitForever); + + if(!state->initialDraw) { + state->initialDraw = true; + + canvas_set_font(canvas, FontSecondary); + reset_level(state); + } + + state->tick++; + + draw_gui(canvas, state); + draw_placed_roots(canvas, state); + draw_pickups(canvas, state); + + switch(state->stage) { + case StageStart: + draw_start_ui(canvas, state); + break; + + case StageRun: + draw_active_root(canvas, state); +#if DRAW_DEBUG + draw_generated_root(canvas, state); +#endif + break; + + case StageOver: + draw_end_ui(canvas, state); + break; + } + + furi_mutex_release(state->mutex); +} + +static void roots_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void roots_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void ProcessStartInput(GameState* state, InputKey key) { + if(key == InputKeyOk) { + state->stage = StageRun; + } +} + +static void ProcessRunInput(GameState* state, InputKey key) { + switch(key) { + case InputKeyRight: + state->pX = MIN(state->pX + 1, CELLS_X - state->rootSizeX); + break; + case InputKeyLeft: + state->pX = MAX(state->pX - 1, 0); + break; + case InputKeyUp: + state->pY = MAX(state->pY - 1, 0); + break; + case InputKeyDown: + state->pY = MIN(state->pY + 1, CELLS_Y - state->rootSizeY); + break; + case InputKeyOk: { + bool rootPlaced = try_place_root(state); + if(rootPlaced) { + recalculate_score(state); + generate_new_root(state); + } else { + state->rerolls--; + if(state->rerolls >= 0) { + generate_new_root(state); + } else { + state->stage = StageOver; + } + } + break; + } + default: + break; + } +} + +static void ProcessOverInput(GameState* state, InputKey key) { + if(key == InputKeyOk) { + state->stage = StageStart; + reset_level(state); + } +} + +int32_t roots_of_life_game_app(void* p) { + FURI_LOG_D(TAG, "Starting game..."); + + UNUSED(p); + int32_t return_code = 0; + + // Set random seed from interrR_UPts + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + + GameState* state = malloc(sizeof(GameState)); + game_state_init(state); + + state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!state->mutex) { + FURI_LOG_E(TAG, "Cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, roots_draw_callback, state); + view_port_input_callback_set(view_port, roots_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(roots_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FURI_LOG_D(TAG, "Entering game loop..."); + GameEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + furi_mutex_acquire(state->mutex, FuriWaitForever); + + if(event_status == FuriStatusOk) { + // Key events + if(event.type == EventTypeKey) { + //FURI_LOG_D(TAG, "Got key: %d", event.input.key); + if(event.input.type == InputTypePress || event.input.type == InputTypeLong || + event.input.type == InputTypeRepeat) { + if(event.input.key == InputKeyBack) { + processing = false; + } + + switch(state->stage) { + case StageStart: + ProcessStartInput(state, event.input.key); + break; + case StageRun: + ProcessRunInput(state, event.input.key); + break; + case StageOver: + ProcessOverInput(state, event.input.key); + break; + } + } + } + } + + view_port_update(view_port); + furi_mutex_release(state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + view_port_free(view_port); + furi_mutex_free(state->mutex); +free_and_exit: + furi_message_queue_free(event_queue); + //FURI_LOG_D(TAG, "Quitting game..."); + game_state_free(state); + free(state); + + return return_code; +} \ No newline at end of file diff --git a/applications/external/scorched_tanks/LICENSE b/applications/external/scorched_tanks/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/scorched_tanks/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/scorched_tanks/application.fam b/applications/external/scorched_tanks/application.fam new file mode 100644 index 000000000..c32a07bf2 --- /dev/null +++ b/applications/external/scorched_tanks/application.fam @@ -0,0 +1,12 @@ +App( + appid="scorched_tanks", + name="Scorched Tanks", + apptype=FlipperAppType.EXTERNAL, + entry_point="scorched_tanks_game_app", + cdefines=["APP_SCORCHED_TANKS_GAME"], + requires=["gui"], + stack_size=1 * 1024, + order=100, + fap_icon="scorchedTanks_10px.png", + fap_category="Games", +) diff --git a/applications/external/scorched_tanks/scorchedTanks_10px.png b/applications/external/scorched_tanks/scorchedTanks_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..6e1ae4c04b973c4015b6c4bb84ee6e806c4cde04 GIT binary patch literal 614 zcmV-s0-61ZP)EX>4Tx04R}tkv&MmKpe$iQ$;Bi5j%)DWT;LSM5Q`P6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<`9$B7gyeFoUSXOg)ia%)oPe-NVP%y9m$nKKJM7Q}QMQd?N82(+!JwgE+cr z>74h8L#!kz#OK8023?T&k?XR{Z=8z`3p_JqWK#3QA!4!E!Ey()lA#h$6NeR5qkJLj zvch?bvs$UK);;+PgL!Qw&2^e1h+_!}Bq2gZ4P{hdAwsK0iis5M$2|PQjz38*nOtQs zax9<<6_Voz|AXJ%nuV!JHz^ncx?gPjV-)Dw1)6o+{yw(t<_X|`2ClTWzuEw1KS{5* zweS(pzYSbmw>5bWxZDATo^;8O9LY~pC=`JAGy0|+Fn9|D*4*A&`#607GSt=b4RCM> zj1?(+-Q(T8oxS~grq$mM6zy{8hfi!q00006VoOIv00000008+zyMF)x010qNS#tmY zE+YT{E+YYWr9XB6000McNliru<_Z!C4+Jw!Hq!tA02y>eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{002HoL_t&-(~Xcx3IH$+0yXsiU!I-{g>=zVz(I@#P@qa6 z+pYuxs05OvaWdDv_pck&lzjc|*yeeqF3;)U6GWsW10ayR(*OVf07*qoM6N<$g4ARE A`Tzg` literal 0 HcmV?d00001 diff --git a/applications/external/scorched_tanks/scorched_tanks_game_app.c b/applications/external/scorched_tanks/scorched_tanks_game_app.c new file mode 100644 index 000000000..6ebcf9030 --- /dev/null +++ b/applications/external/scorched_tanks/scorched_tanks_game_app.c @@ -0,0 +1,546 @@ +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define PLAYER_INIT_LOCATION_X 20 +#define PLAYER_INIT_AIM 45 +#define PLAYER_INIT_POWER 50 +#define ENEMY_INIT_LOCATION_X 108 +#define TANK_BARREL_LENGTH 8 +#define GRAVITY_FORCE (double)0.5 +#define MIN_GROUND_HEIGHT 35 +#define MAX_GROUND_HEIGHT 55 +#define MAX_FIRE_POWER 100 +#define MIN_FIRE_POWER 0 +#define TANK_COLLIDER_SIZE 3 +#define MAX_WIND 10 +#define MAX_PLAYER_DIFF_X 20 +#define MAX_ENEMY_DIFF_X 20 + +// That's a filthy workaround but sin(player.aimAngle) breaks it all... If you're able to fix it, please do create a PR! +double scorched_tanks_sin[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.122, -0.139, -0.156, -0.174, -0.191, + -0.208, -0.225, -0.242, -0.259, -0.276, -0.292, -0.309, -0.326, -0.342, -0.358, -0.375, -0.391, + -0.407, -0.423, -0.438, -0.454, -0.469, -0.485, -0.500, -0.515, -0.530, -0.545, -0.559, -0.574, + -0.588, -0.602, -0.616, -0.629, -0.643, -0.656, -0.669, -0.682, -0.695, -0.707, -0.719, -0.731, + -0.743, -0.755, -0.766, -0.777, -0.788, -0.799, -0.809, -0.819, -0.829, -0.839, -0.848, -0.857, + -0.866, -0.875, -0.883, -0.891, -0.899, -0.906, -0.914, -0.921, -0.927, -0.934, -0.940, -0.946, + -0.951, -0.956, -0.961, -0.966, -0.970, -0.974, -0.978, -0.982, -0.985, -0.988, -0.990, -0.993, + -0.995, -0.996, -0.998, -0.999, -0.999, -1.000, -1.000}; +double scorched_tanks_cos[91] = { + 1.000, 1.000, 0.999, 0.999, 0.998, 0.996, 0.995, 0.993, 0.990, 0.988, 0.985, 0.982, 0.978, + 0.974, 0.970, 0.966, 0.961, 0.956, 0.951, 0.946, 0.940, 0.934, 0.927, 0.921, 0.914, 0.906, + 0.899, 0.891, 0.883, 0.875, 0.866, 0.857, 0.848, 0.839, 0.829, 0.819, 0.809, 0.799, 0.788, + 0.777, 0.766, 0.755, 0.743, 0.731, 0.719, 0.707, 0.695, 0.682, 0.669, 0.656, 0.643, 0.629, + 0.616, 0.602, 0.588, 0.574, 0.559, 0.545, 0.530, 0.515, 0.500, 0.485, 0.469, 0.454, 0.438, + 0.423, 0.407, 0.391, 0.375, 0.358, 0.342, 0.326, 0.309, 0.292, 0.276, 0.259, 0.242, 0.225, + 0.208, 0.191, 0.174, 0.156, 0.139, 0.122, 0.105, 0.087, 0.070, 0.052, 0.035, 0.017, 0.000}; +double scorched_tanks_tan[91] = { + 0.000, -0.017, -0.035, -0.052, -0.070, -0.087, -0.105, -0.123, -0.141, -0.158, -0.176, + -0.194, -0.213, -0.231, -0.249, -0.268, -0.287, -0.306, -0.325, -0.344, -0.364, -0.384, + -0.404, -0.424, -0.445, -0.466, -0.488, -0.510, -0.532, -0.554, -0.577, -0.601, -0.625, + -0.649, -0.674, -0.700, -0.727, -0.754, -0.781, -0.810, -0.839, -0.869, -0.900, -0.932, + -0.966, -1.000, -1.036, -1.072, -1.111, -1.150, -1.192, -1.235, -1.280, -1.327, -1.376, + -1.428, -1.483, -1.540, -1.600, -1.664, -1.732, -1.804, -1.881, -1.963, -2.050, -2.144, + -2.246, -2.356, -2.475, -2.605, -2.747, -2.904, -3.078, -3.271, -3.487, -3.732, -4.011, + -4.331, -4.704, -5.144, -5.671, -6.313, -7.115, -8.144, -9.513, -11.429, -14.298, -19.077, + -28.627, -57.254, -90747.269}; +uint8_t scorched_tanks_ground_modifiers[SCREEN_WIDTH] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 28, 26, 24, 22, 20, + 18, 16, 14, 12, 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +typedef struct { + // +-----x + // | + // | + // y + uint8_t x; + uint8_t y; +} Point; + +typedef struct { + // +-----x + // | + // | + // y + double x; + double y; +} PointDetailed; + +typedef struct { + uint8_t locationX; + uint8_t hp; + int aimAngle; + uint8_t firePower; +} Tank; + +typedef struct { + Point ground[SCREEN_WIDTH]; + Tank player; + Tank enemy; + bool isPlayerTurn; + bool isShooting; + int windSpeed; + Point trajectory[SCREEN_WIDTH]; + uint8_t trajectoryAnimationStep; + PointDetailed bulletPosition; + PointDetailed bulletVector; + FuriMutex* mutex; +} Game; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} ScorchedTanksEvent; + +int scorched_tanks_random(int min, int max) { + return min + rand() % ((max + 1) - min); +} + +void scorched_tanks_generate_ground(Game* game_state) { + int lastHeight = 45; + + for(uint8_t a = 0; a < SCREEN_WIDTH; a++) { + int diffHeight = scorched_tanks_random(-2, 3); + int changeLength = scorched_tanks_random(1, 6); + + if(diffHeight == 0) { + changeLength = 1; + } + + for(int b = 0; b < changeLength; b++) { + if(a + b < SCREEN_WIDTH) { + int index = a + b; + int newPoint = lastHeight + diffHeight; + newPoint = newPoint < MIN_GROUND_HEIGHT ? MIN_GROUND_HEIGHT : newPoint; + newPoint = newPoint > MAX_GROUND_HEIGHT ? MAX_GROUND_HEIGHT : newPoint; + game_state->ground[index].x = index; + game_state->ground[index].y = newPoint - scorched_tanks_ground_modifiers[a]; + lastHeight = newPoint; + } else { + a += b; + break; + } + } + + a += changeLength - 1; + } +} + +void scorched_tanks_init_game(Game* game_state) { + game_state->player.locationX = PLAYER_INIT_LOCATION_X + + scorched_tanks_random(0, MAX_PLAYER_DIFF_X) - + MAX_PLAYER_DIFF_X / 2; + game_state->player.aimAngle = PLAYER_INIT_AIM; + game_state->player.firePower = PLAYER_INIT_POWER; + game_state->enemy.aimAngle = PLAYER_INIT_AIM; + game_state->enemy.firePower = PLAYER_INIT_POWER; + game_state->enemy.locationX = + ENEMY_INIT_LOCATION_X + scorched_tanks_random(0, MAX_ENEMY_DIFF_X) - MAX_ENEMY_DIFF_X / 2; + game_state->isPlayerTurn = true; + + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + scorched_tanks_generate_ground(game_state); +} + +void scorched_tanks_calculate_trajectory(Game* game_state) { + if(game_state->isShooting) { + game_state->bulletVector.x += ((double)game_state->windSpeed - MAX_WIND / 2) / 40; + game_state->bulletVector.y += GRAVITY_FORCE; + + game_state->bulletPosition.x += game_state->bulletVector.x; + game_state->bulletPosition.y += game_state->bulletVector.y; + + int totalDistanceToEnemy = 100; + + if(game_state->isPlayerTurn) { + double distanceToEnemyX = game_state->enemy.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->enemy.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } else { + double distanceToEnemyX = game_state->player.locationX - game_state->bulletPosition.x; + double distanceToEnemyY = game_state->ground[game_state->player.locationX].y - + TANK_COLLIDER_SIZE - game_state->bulletPosition.y; + totalDistanceToEnemy = + sqrt(distanceToEnemyX * distanceToEnemyX + distanceToEnemyY * distanceToEnemyY); + } + + if(totalDistanceToEnemy <= TANK_COLLIDER_SIZE) { + game_state->isShooting = false; + scorched_tanks_init_game(game_state); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.x > SCREEN_WIDTH || + game_state->bulletPosition.y > + game_state->ground[(int)round(game_state->bulletPosition.x)].y) { + game_state->isShooting = false; + game_state->bulletPosition.x = 0; + game_state->bulletPosition.y = 0; + game_state->windSpeed = scorched_tanks_random(0, MAX_WIND); + game_state->isPlayerTurn = !game_state->isPlayerTurn; + return; + } + + if(game_state->bulletPosition.y > 0) { + game_state->trajectory[game_state->trajectoryAnimationStep].x = + round(game_state->bulletPosition.x); + game_state->trajectory[game_state->trajectoryAnimationStep].y = + round(game_state->bulletPosition.y); + game_state->trajectoryAnimationStep++; + } + } +} + +static void scorched_tanks_draw_tank(Canvas* const canvas, uint8_t x, uint8_t y, bool isPlayer) { + uint8_t lineIndex = 0; + + if(isPlayer) { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + + // draw turret + canvas_draw_line(canvas, x - 2, y - lineIndex, x + 1, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 2, y - lineIndex, x, y - lineIndex); + lineIndex++; + } else { + // Draw tank base + canvas_draw_line(canvas, x - 3, y - lineIndex, x + 3, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x - 4, y - lineIndex, x + 4, y - lineIndex); + lineIndex++; + + // draw turret + canvas_draw_line(canvas, x - 1, y - lineIndex, x + 2, y - lineIndex); + lineIndex++; + canvas_draw_line(canvas, x, y - lineIndex, x + 2, y - lineIndex); + lineIndex++; + } +} + +static void scorched_tanks_render_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const Game* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + canvas_draw_frame(canvas, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + canvas_set_color(canvas, ColorBlack); + + if(game_state->isShooting) { + canvas_draw_dot(canvas, game_state->bulletPosition.x, game_state->bulletPosition.y); + } + + for(int a = 1; a < SCREEN_WIDTH; a++) { + canvas_draw_line( + canvas, + game_state->ground[a - 1].x, + game_state->ground[a - 1].y, + game_state->ground[a].x, + game_state->ground[a].y); + + if(game_state->trajectory[a].y != 0) { + canvas_draw_dot(canvas, game_state->trajectory[a].x, game_state->trajectory[a].y); + } + } + + scorched_tanks_draw_tank( + canvas, + game_state->enemy.locationX, + game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE, + true); + + scorched_tanks_draw_tank( + canvas, + game_state->player.locationX, + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE, + false); + + int aimX1 = 0; + int aimY1 = 0; + int aimX2 = 0; + int aimY2 = 0; + + if(game_state->isPlayerTurn) { + aimX1 = game_state->player.locationX; + aimY1 = game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX1 += 1; + aimX2 += 1; + } else { + aimX1 = game_state->enemy.locationX; + aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + + aimX2 = aimX1 - (aimX2 - aimX1); + + aimX1 -= 1; + aimX2 -= 1; + } + + canvas_draw_line(canvas, aimX1, aimY1 - 3, aimX2, aimY2 - 3); + + canvas_set_font(canvas, FontSecondary); + + char buffer2[18]; + snprintf(buffer2, sizeof(buffer2), "wind: %i", game_state->windSpeed - MAX_WIND / 2); + canvas_draw_str(canvas, 55, 10, buffer2); + + if(game_state->isPlayerTurn) { + canvas_draw_str(canvas, 93, 10, "player1"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->player.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->player.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } else { + canvas_draw_str(canvas, 93, 10, "player2"); + + char buffer[12]; + snprintf(buffer, sizeof(buffer), "a: %u", game_state->enemy.aimAngle); + canvas_draw_str(canvas, 2, 10, buffer); + + snprintf(buffer, sizeof(buffer), "p: %u", game_state->enemy.firePower); + canvas_draw_str(canvas, 27, 10, buffer); + } + + furi_mutex_release(game_state->mutex); +} + +static void scorched_tanks_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void scorched_tanks_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + ScorchedTanksEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void scorched_tanks_increase_power(Game* game_state) { + if(game_state->player.firePower < MAX_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower < MAX_FIRE_POWER) { + game_state->player.firePower++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower < MAX_FIRE_POWER) { + game_state->enemy.firePower++; + } + } +} + +static void scorched_tanks_decrease_power(Game* game_state) { + if(game_state->player.firePower > MIN_FIRE_POWER && !game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.firePower > MIN_FIRE_POWER) { + game_state->player.firePower--; + } + + if(!game_state->isPlayerTurn && game_state->enemy.firePower > MIN_FIRE_POWER) { + game_state->enemy.firePower--; + } + } +} + +static void scorched_tanks_aim_up(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn && game_state->player.aimAngle < 90) { + game_state->player.aimAngle++; + } + + if(!game_state->isPlayerTurn && game_state->enemy.aimAngle < 90) { + game_state->enemy.aimAngle++; + } + } +} + +static void scorched_tanks_aim_down(Game* game_state) { + if(game_state->player.aimAngle > 0 && !game_state->isShooting) { + if(game_state->isPlayerTurn) { + game_state->player.aimAngle--; + } else { + game_state->enemy.aimAngle--; + } + } +} + +const NotificationSequence sequence_long_vibro = { + &message_vibro_on, + &message_delay_500, + &message_vibro_off, + NULL, +}; + +static void scorched_tanks_fire(Game* game_state) { + if(!game_state->isShooting) { + if(game_state->isPlayerTurn) { + double sinFromAngle = scorched_tanks_sin[game_state->player.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->player.aimAngle]; + uint8_t aimX1 = game_state->player.locationX; + uint8_t aimY1 = + game_state->ground[game_state->player.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = scorched_tanks_cos[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->player.aimAngle] * + ((double)game_state->player.firePower / 10); + } else { + double sinFromAngle = scorched_tanks_sin[game_state->enemy.aimAngle]; + double cosFromAngle = scorched_tanks_cos[game_state->enemy.aimAngle]; + uint8_t aimX1 = game_state->enemy.locationX; + uint8_t aimY1 = game_state->ground[game_state->enemy.locationX].y - TANK_COLLIDER_SIZE; + int aimX2 = aimX1 + TANK_BARREL_LENGTH * cosFromAngle; + int aimY2 = aimY1 + TANK_BARREL_LENGTH * sinFromAngle; + aimX2 = aimX1 - (aimX2 - aimX1); + + game_state->bulletPosition.x = aimX2; + game_state->bulletPosition.y = aimY2; + game_state->bulletVector.x = -scorched_tanks_cos[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + game_state->bulletVector.y = scorched_tanks_sin[game_state->enemy.aimAngle] * + ((double)game_state->enemy.firePower / 10); + } + + game_state->trajectoryAnimationStep = 0; + + for(int x = 0; x < SCREEN_WIDTH; x++) { + game_state->trajectory[x].x = 0; + game_state->trajectory[x].y = 0; + } + + game_state->isShooting = true; + + NotificationApp* notification = furi_record_open("notification"); + notification_message(notification, &sequence_long_vibro); + notification_message(notification, &sequence_blink_white_100); + furi_record_close("notification"); + } +} + +int32_t scorched_tanks_game_app(void* p) { + UNUSED(p); + srand(DWT->CYCCNT); + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(ScorchedTanksEvent)); + + Game* game_state = malloc(sizeof(Game)); + scorched_tanks_init_game(game_state); + + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { + FURI_LOG_E("ScorchedTanks", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(game_state); + return 255; + } + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, scorched_tanks_render_callback, game_state); + view_port_input_callback_set(view_port, scorched_tanks_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(scorched_tanks_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, 2000); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + ScorchedTanksEvent event; + for(bool processing = true; processing;) { + furi_message_queue_get(event_queue, &event, 50); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(event.type == EventTypeKey) { // && game->isPlayerTurn + if(event.input.type == InputTypeRepeat || event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + scorched_tanks_aim_up(game_state); + break; + case InputKeyDown: + scorched_tanks_aim_down(game_state); + break; + case InputKeyRight: + scorched_tanks_increase_power(game_state); + break; + case InputKeyLeft: + scorched_tanks_decrease_power(game_state); + break; + case InputKeyOk: + scorched_tanks_fire(game_state); + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + scorched_tanks_calculate_trajectory(game_state); + } + + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(game_state->mutex); + free(game_state); + + return 0; +} diff --git a/applications/external/simonsays/LICENSE b/applications/external/simonsays/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/simonsays/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/simonsays/application.fam b/applications/external/simonsays/application.fam new file mode 100644 index 000000000..8165eb22b --- /dev/null +++ b/applications/external/simonsays/application.fam @@ -0,0 +1,15 @@ +App( + appid="simon_says", # Must be unique + name="Simon Says", # Displayed in UI + apptype=FlipperAppType.EXTERNAL, + entry_point="simon_says_app_entry", + stack_size=2 * 1024, + fap_category="Games", + # Optional values + # fap_version=(0, 1), # (major, minor) + fap_icon="simon_says.png", # 10x10 1-bit PNG + fap_description="A Simon Says Game", + fap_author="SimplyMinimal,ShehabAttia96", + fap_weburl="https://github.com/SimplyMinimal/FlipperZero-SimonSays", + fap_icon_assets="images", # Image assets to compile for this application +) diff --git a/applications/external/simonsays/images/Connected_62x31.png b/applications/external/simonsays/images/Connected_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..eeaf660b12ea4647154c8d616b468a3098203356 GIT binary patch literal 3765 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 literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinMafia_115x62.png b/applications/external/simonsays/images/DolphinMafia_115x62.png new file mode 100644 index 0000000000000000000000000000000000000000..66fdb40ff2651916faed4a2ae1d564cafdbf7bcb GIT binary patch literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinNiceLeft_96x59.png b/applications/external/simonsays/images/DolphinNiceLeft_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1HVZ`K~#8N)t2F6 zBOwTcn|uFv&P{g+A+EZhxHaDoPgG!>+U`A`&*x7nkHB!heHqV5>*uGU)#2vyVsDH}AVsy~ezoEL*Kpbe!+iXUd?~Rwtzo6LrLj)?o;W z%B}S*E^b1t5`5Cx0{z`8Oesd$bKHNRqlaG6PD{IGuR*vKQq03T%4+akggQEz{fhB2 zkVWib2;?%WTF?p3547%+^O>KQHRH=tcKLr$mMokh|q`fz5XzF(Mj!9;jWZ>LNB{745!LW$pD zkadJ}O#GL$L~Z>7gVYy}SQ)m4MI`o7H;6j^7HZ&?>gY2!p)=#O&x04p+kp$E=eQP@#}!k|J2nC}-w9XXHK)ky)? zWi|(UNpSEHpL=IDPQiMlBeznh7JPrj3U{`$Mmf7v$R4KZ4yb?TsX&5lDBPjK>aC)* z+yP>cwL=w1ycy~aM_HYqf7$W{+1^+Ss^Ce3gNuqN9m$B8+)qUj0$sJ@{B+6{erZg9c6DntWH_-f%8n1_=>N5 zLBQ(NkXs#`$1LCaf`ID8{8B28@w~)t#2{v}pB=foCw`qg1|*K~@fFj4I?Bd=oONQt zEejU)z!>HGWH}wNGE;piDz9ommiS5)b*I&P6RHXka*`#xG{|;kv#d@lusTV)+7J6l k;Zb_aMAVv9B7c7V17g|vS5K+|GXMYp07*qoM6N<$f|p|O7XSbN literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinRight_56x48.png b/applications/external/simonsays/images/DolphinRight_56x48.png new file mode 100644 index 0000000000000000000000000000000000000000..a0fcba21838c09095daae8551025b8e2f8030a90 GIT binary patch literal 601 zcmV-f0;c_mP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0q#jeK~!i%&6Haf zgD?mMwfo<*rQRHhfCPr0+Hl$M=O-~xWa1riV$rM9qJP`7MI@V}uDQTO}5Xl9QS% nZe`HUO%wHVx9HqBw{80Z5rL-iUB=Na00000NkvXXu0mjfU|j(! literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinTalking_59x63.png b/applications/external/simonsays/images/DolphinTalking_59x63.png new file mode 100644 index 0000000000000000000000000000000000000000..46f559f65f11194c94c15bb7f195a1d72ef2a295 GIT binary patch literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/DolphinWait_61x59.png b/applications/external/simonsays/images/DolphinWait_61x59.png new file mode 100644 index 0000000000000000000000000000000000000000..423e079199b00df0d910981caf8944cbaf8ee67e GIT binary patch literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/WarningDolphin_45x42.png b/applications/external/simonsays/images/WarningDolphin_45x42.png new file mode 100644 index 0000000000000000000000000000000000000000..d766ffbb444db1739f2ccd030e506e8bada11ee8 GIT binary patch literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/board.png b/applications/external/simonsays/images/board.png new file mode 100644 index 0000000000000000000000000000000000000000..7640db5946908dfced504134d72fc6bad481f55b GIT binary patch literal 826 zcmV-A1I7G_P)KtS*B?{IK%KR-Wze}9jUkH5daZ*OleFE0=f z5Km7}udlCPUtgb}pF~7NBqSv6?(QQaBRV=dCnqO4IXUg^?IX*~ zHd19_~QlfI^JY$IVs^?Hr zpE*W(SHf;L~KfQf`yD`fR5J0xEm6%3hd4wL#L`75b}( zTb&z<#5$d%vFshBm5Vie?)j734R_-}T6FeWxcX9loNLyF5xa>Tn}^Z3`brMhMywJe z#UzhYM&WR9d2QOJn#;Ho#T^d+uIzAB7+{osZX;l`A%bWe$YR96Q7L1?a@NJcR(#V- zwEQz*-TQZ>lmA1>*cX7MGXOHtVgjWGYb)HbPG8|pE*Re2Y8xb%P~*Vj#7^azos`17 zaE<%o+}+F12lL`I`!#&m^cF7MW2Sx7qPh3ab`A2Dpw-z9*C|0rO6n|ldtBNn zc&fZUJIbrV^~x*D8gDU2+>87*ou;Z<$1g|gKpb^b=Dvm_aSO6hG4>nu0~ZV%^ebg~ z14~RdVSL^Ep;yF4%cFkFkGR9bR`PeWZ$AuO_OV6fdyfrcPSFy^(^2?WN;pz)FJ5?_ zGjNKE6H*<1uO&reW18adQ?1=nlPx#1ZP1_K>z@;j|==^1poj5%uq~JMgRZ*&(F`0kdRRV zDVqm3=V2p__fz;0dlK(OBPn_kSilYV#j$?tD2hJEe!}zk!I2zJ#DJq?I1ciKt8efk zsujog;MGjIRqO8wU3G2Z!Mv2b#256m>#9Qvozt`S*MZk-R(?(BMD?~h;3PB&1vQMQ z&vu=t4lAX2?cJH?isH|1)zvDMkuf)o40pB>OLWJcu2GrSH`Tx&4Nr8pcgA5{mAt_8;!;ED{M}#Vw6PRR0 zJH9$$)qbdwodUB}COhS3i!A!cr>x?#M}y^z8ex(ZPRXf=i0kx}#0&7oXqc6+qXn>Q zOm)44DO|mVM7Z#DbT`;8oDIN~uS~w>($$e~2gJM2DP=xVJkA>(&q%$$hwjFa1@k$r zbyU7XnC1Y&SDIxGIfl;PrtF#SY*B^0G&=uKsE?qOO6wP_3yvV^j`l-ZrS)^t(E_`j z;dt#*Y5godUR6nxsZob1IAs`Is{Gl1L9|okqS}#In&_f3C8qZ!nxd`=7jr{*@C1ng z=E_f!yr8pPWVEo;@kdjRkrXlR_?;mN``hC&@@k`%V!-R_FdS?b30dBg{}(zpdU?l~ c{%Mr@1JGzPQsxqccK`qY07*qoM6N<$f~Y5<(EtDd literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/images/left.png b/applications/external/simonsays/images/left.png new file mode 100644 index 0000000000000000000000000000000000000000..1eb6b58ee05def3c5e0fac6c7a8300a487fdd540 GIT binary patch literal 921 zcmV;K17`e*P)Px#1ZP1_K>z@;j|==^1poj5%uq~JMgRZ*&(F`0kdRx zK~zXf)s|~_(=ZH%9Ux(3tQ2PnD}j}*<+9NY1}k?Mm;e8tkYu^BBzNKP3Ep$sW664w z*pe^qzg6M;SxK5rmlZolRJA(+iCX({vg$clsJ|YpTaJ6)d*3wP_qW*5)*O;i5k60h zr&>f3*7w&g#8PyBy zH{V&Hw=55~aP`N~uZB?Xf$uK-@*Gj8@cSVefe|){>9)cB7!W(lyjK- zf}JRu->A7Nhz&&(&<7_feHln{^#^bf6_PYm!)=Nlv`Dzi3ve(+&mTsoR!UhuJK-L8 z@)pcPrksN^TXQq!EY%C1mk!@`v7y`#Fyo})#A>}Ht~-F{`%8)<)nG5VP1UIN1URDh z-fsHrKM~a z&eKhx;?jjIuumO;p3AnNWAW`q7qJTc(lC+Ju(z? zs}83?o-H+Qvbcnv7jAAP<}COzV%DX5!npgs;2KfVNzo vvLhCPx#1ZP1_K>z@;j|==^1poj5%uq~JMgRZ*&(F`0kdRV~azOuQVbdXR(y4jd9;@yd2D%0*8Cvsr zi#dGZcp-z+7JkB9;^_m`=D~x1=GfubEIV$R<4(chd-KM)PGgCU`$feD3| z^quPTLs1y~@^E3;&+7Ce$pvQm&l)-_B^Jtkq0Lsl#+^c$0s11V&svLlm{11V(1GMUUV zkV959C89dYPCtR9qyQ+KzH8DVYyqg;))YSOQBcYqHNU}@o14p9>=&QNM{~WD#1Q*J zVJmOeqIsPmT?HoU9~WwdxdX5MaVL^bFR=o4(OZZprRpxdR-UKGaB zkyp9rOin*JN>4{rq?7*49H>`3-oZ(byMXP+fNva`Wl#224&jlb)*Shl!4xyKxhsqu z`R;!9PT>PE7i72^|!gs5V(Ux+;DkfYV{XaHG$p-7sJC_Ukv{_isCn% WhcZ%O?_L)G0000KtS*B?^IM&1qB5V5D=fApF~7NPft%98ykOr zf4{%KBqStnZ*T7I?i?H(IyyQ(KR+!kEni<>EG#T5D=R4}DK<7XC@3heudgR3CnY5% zkB^TgCMFIJ4lge+3JMD2~83_pq=H})f zA0Of2;Rpx_Dk>^FJ3H;|?NCrqIXO8!Jv~Q9M>I4v4-XHpu&{7&aKOO84JNt}00009 za7bBm000ie000ie0hKEb8vp5@T+hkj%i zhNyAK!E^S-N6i=v$}yGj7jr_`S06>lod4r|DH^xgHyLAJKa{0ve zZPWpi-QJTdf)qs>?!2aUHA=&#MCUGm zI@2kUC_Ze0!|cNJ`eQRCsa#I#k^psiVBrupHv$0E1rvWWX8?Gj#Rl~&X&P5bxMP{V zrIuVU_&VvDH@C76lB;amfuc$6T3bh(FrS*rUBOkjaMT6;zT=PgxNxfO8@Nn$8E&um zi$9t4QHqW>1yj6-eaWqPT9TehHb+UGQJ-1RFXKb$|<6_by z2i0&;^Obg}Hb;k9DTuRGia4lN4vKSPpRgRL;ZP>U7)_q8Oi tRHn-NuWI#_<#<^m%W?V1i{U?Le*q}3GEzALUP}M~002ovPDHLkV1iojbSeM< literal 0 HcmV?d00001 diff --git a/applications/external/simonsays/simon_says.c b/applications/external/simonsays/simon_says.c new file mode 100644 index 000000000..f537cb364 --- /dev/null +++ b/applications/external/simonsays/simon_says.c @@ -0,0 +1,666 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Header-file for boolean data-type. +#include +#include + +/* generated by fbt from .png files in images folder */ +#include + +#define TAG "Simon" // Used for logging +#define DEBUG_MSG 1 +#define SCREEN_XRES 128 +#define SCREEN_YRES 64 +#define BOARD_X 72 // Used for board placement +#define BOARD_Y 8 +#define GAME_START_LIVES 3 +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/game_simon_says.save" + +// Define Notes +// Shamelessly stolen from Ocarina application +// https://github.com/invalidna-me/flipperzero-ocarina +#define NOTE_UP 587.33f +#define NOTE_LEFT 493.88f +#define NOTE_RIGHT 440.00f +#define NOTE_DOWN 349.23 +#define NOTE_OK 293.66f + +/* ============================ Data structures ============================= */ + +typedef enum game_state { preloading, mainMenu, inGame, gameOver, gameVictory } game_state; + +typedef enum difficulty_mode { normal, hard } difficulty_mode; + +typedef enum shape_names { up, down, left, right, number_of_shapes } Direction; + +typedef enum currently_playing { simon, player } currently_playing; + +typedef struct { + /* Game state. */ + enum game_state gameState; // This is the current game state + bool gameover; /* if true then switch to the game over state */ + bool is_wrong_direction; /* Is the last direction wrong? */ + enum currently_playing activePlayer; // This is used to track who is playing at the moment + uint32_t lives; /* Number of lives in the current game. */ + + enum difficulty_mode difficultyMode; // This is the difficulty mode for the current game + bool sound_enabled; // This is the sound enabled flag for the current game + float volume; // This is the volume for the current game + + /* Handle Score */ + int currentScore; // This is the score for the current + int highScore; /* Highscore. Shown on Game Over Screen */ + bool is_new_highscore; /* Is the last score a new highscore? */ + + /* Handle Shape Display */ + uint32_t numberOfMillisecondsBeforeShapeDisappears; // This defines the speed of the game + enum shape_names simonMoves[1000]; // Store the sequence of shapes that Simon plays + enum shape_names selectedShape; // This is used to track the shape that the player has selected + bool set_board_neutral; // This is used to track if the board should be neutral or not + int moveIndex; // This is used to track the current move in the sequence + + uint32_t last_button_press_tick; + NotificationApp* notification; + FuriMutex* mutex; +} SimonData; + +/* ============================== Sequences ============================== */ + +const NotificationSequence sequence_wrong_move = { + &message_red_255, + + &message_vibro_on, + // &message_note_g5, // Play sound but currently disabled + &message_delay_25, + // &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_player_submit_move = { + &message_vibro_on, + // &message_note_g5, // Play sound but currently disabled. Need On/Off menu setting + &message_delay_10, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + &message_delay_1, + + // &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_up = { + // &message_vibro_on, + &message_note_g4, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_down = { + // &message_vibro_on, + &message_note_c3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_left = { + // &message_vibro_on, + &message_note_e3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +const NotificationSequence sequence_right = { + // &message_vibro_on, + &message_note_g3, + &message_delay_100, + // &message_vibro_off, + &message_sound_off, + NULL, +}; + +// Indicate that it's Simon's turn +const NotificationSequence sequence_simon_is_playing = { + &message_red_255, + &message_do_not_reset, + NULL, +}; + +// Indicate that it's the Player's turn +const NotificationSequence sequence_player_is_playing = { + &message_red_0, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_cleanup = { + &message_red_0, + &message_green_0, + &message_blue_0, + &message_sound_off, + &message_vibro_off, + NULL, +}; + +/* ============================ 2D drawing ================================== */ + +/* Display remaining lives in the center of the board */ +void draw_remaining_lives(Canvas* canvas, const SimonData* simon_state) { + // Convert score to string + // int length = snprintf(NULL, 0, "%lu", simon_state->lives); + // char* str_lives_remaining = malloc(length + 1); + // snprintf(str_lives_remaining, length + 1, "%lu", simon_state->lives); + + // TODO: Make it a Simon Says icon on top right + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + int x = SCREEN_XRES - 6; + int lives = simon_state->lives; + while(lives--) { + canvas_draw_str(canvas, x, 8, "*"); + x -= 7; + } +} + +void draw_current_score(Canvas* canvas, const SimonData* simon_data) { + /* Draw Game Score. */ + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontSecondary); + char str_score[32]; + snprintf(str_score, sizeof(str_score), "%i", simon_data->currentScore); + canvas_draw_str_aligned(canvas, SCREEN_XRES / 2 + 4, 2, AlignCenter, AlignTop, str_score); +} + +void play_sound_up(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_UP, app->volume); + } +} + +void play_sound_down(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_DOWN, app->volume); + } +} + +void play_sound_left(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_LEFT, app->volume); + } +} + +void play_sound_right(const SimonData* app) { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_RIGHT, app->volume); + } +} + +void stop_sound() { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} + +/* Main Render Function */ +void simon_draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const SimonData* simon_state = ctx; + furi_mutex_acquire(simon_state->mutex, FuriWaitForever); + + canvas_clear(canvas); + + // ######################### Main Menu ######################### + // Show Main Menu + if(simon_state->gameState == mainMenu) { + // Draw border frame + canvas_draw_frame(canvas, 1, 1, SCREEN_XRES - 1, SCREEN_YRES - 1); // Border + + // Draw Simon text banner + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 - 4, + AlignCenter, + AlignCenter, + "Welcome to Simon Says"); + + // Display Press OK to start below title + canvas_set_color(canvas, ColorXOR); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 + 10, + AlignCenter, + AlignCenter, + "Press OK to start"); + } + + // ######################### in Game ######################### + //@todo Render Callback + // We're in an active game + if(simon_state->gameState == inGame) { + // Draw Current Score + draw_current_score(canvas, simon_state); + + // Draw Lives + draw_remaining_lives(canvas, simon_state); + + // Draw Simon Pose + if(simon_state->activePlayer == player) { + // Player's turn + canvas_draw_icon(canvas, 0, 4, &I_DolphinWait_61x59); + } else { + // Simon's turn + canvas_draw_icon(canvas, 0, 4, &I_DolphinTalking_59x63); + } + + if(simon_state->set_board_neutral) { + // Draw Neutral Board + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_board); // Draw Board + + // Stop Sound TODO: Move this to a better place + //@todo Sound + stop_sound(); + } else { + switch(simon_state->selectedShape) { + case up: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_up); // Draw Up + play_sound_up(simon_state); + break; + case down: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_down); // Draw Down + play_sound_down(simon_state); + break; + case left: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_left); // Draw Left + play_sound_left(simon_state); + break; + case right: + canvas_draw_icon(canvas, BOARD_X, BOARD_Y, &I_right); // Draw Right + play_sound_right(simon_state); + break; + default: + if(DEBUG_MSG) + FURI_LOG_E( + TAG, "Invalid shape: %d", simon_state->simonMoves[simon_state->moveIndex]); + break; + } + } + } + + // ######################### Game Over ######################### + if(simon_state->gameState == gameOver) { + stop_sound(); //TODO: Make a game over sequence + canvas_set_color(canvas, ColorXOR); + canvas_set_font(canvas, FontPrimary); + + // TODO: if new highscore, display blinking "New High Score" + // Display High Score Text + if(simon_state->is_new_highscore) { + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 6, AlignCenter, AlignTop, "New High Score!"); + } else { + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 6, AlignCenter, AlignTop, "High Score"); + } + + // Convert highscore to string + int length = snprintf(NULL, 0, "%i", simon_state->highScore); + char* str_high_score = malloc(length + 1); + snprintf(str_high_score, length + 1, "%i", simon_state->highScore); + + // Display High Score + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, 22, AlignCenter, AlignCenter, str_high_score); + free(str_high_score); + + // Display Game Over + canvas_draw_str_aligned( + canvas, SCREEN_XRES / 2, SCREEN_YRES / 2 + 2, AlignCenter, AlignCenter, "GAME OVER"); + + // Display Press OK to restart below title + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + SCREEN_XRES / 2, + SCREEN_YRES / 2 + 15, + AlignCenter, + AlignCenter, + "Press OK to restart"); + } + + // ######################### Victory ######################### + //Player Beat Simon beyond limit! A word record holder here! + //TODO + + //release the mutex + furi_mutex_release(simon_state->mutex); +} + +/* ======================== Input Handling ============================== */ + +void simon_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +/* ======================== Simon Game Engine ======================== */ + +bool load_game(SimonData* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + File* file = storage_file_alloc(storage); + + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) { + if(storage_file_size(file) > sizeof(SimonData)) { + storage_simply_remove(storage, SAVING_FILENAME); + FURI_LOG_E( + TAG, "Error: file is larger than the data structure! The file has been deleted."); + } else { + bytes_readed = storage_file_read(file, app, sizeof(SimonData)); + } + storage_file_close(file); + storage_file_free(file); + } + + furi_record_close(RECORD_STORAGE); + return bytes_readed == sizeof(SimonData); +} + +void save_game(SimonData* app) { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, app, sizeof(SimonData)); + } + storage_file_close(file); + storage_file_free(file); + + furi_record_close(RECORD_STORAGE); +} + +int getRandomIntInRange(int lower, int upper) { + return (rand() % (upper - lower + 1)) + lower; +} + +void play_sound_sequence_correct() { + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_success); +} + +void play_sound_wrong_move() { + //TODO: play wrong sound: Try sequence_audiovisual_alert + notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_error); +} + +/* Restart game and give player a chance to try again on same sequence */ +// @todo restartGame +void resetGame(SimonData* app) { + app->moveIndex = 0; + app->numberOfMillisecondsBeforeShapeDisappears = 500; + app->activePlayer = simon; + app->is_wrong_direction = false; + app->last_button_press_tick = 0; + app->set_board_neutral = true; + app->activePlayer = simon; +} + +/* Set gameover state */ +void game_over(SimonData* app) { + if(app->is_new_highscore) save_game(app); // Save highscore but only on change + app->gameover = true; + app->lives = GAME_START_LIVES; // Show 3 lives in game over screen to match new game start + app->gameState = gameOver; +} + +/* Called after gameover to restart the game. This function + * also calls restart_game(). */ +void restart_game_after_gameover(SimonData* app) { + app->volume = 1.0f; //TODO: make this a setting + app->gameState = inGame; + app->gameover = false; + app->currentScore = 0; + app->is_new_highscore = false; + app->lives = GAME_START_LIVES; + app->simonMoves[0] = rand() % number_of_shapes; + resetGame(app); +} + +void addNewSimonMove(int addAtIndex, SimonData* app) { + app->simonMoves[addAtIndex] = getRandomIntInRange(0, 3); +} + +void startNewRound(SimonData* app) { + addNewSimonMove(app->currentScore, app); + app->moveIndex = 0; + app->activePlayer = simon; +} + +void onPlayerAnsweredCorrect(SimonData* app) { + app->moveIndex++; +} + +void onPlayerAnsweredWrong(SimonData* app) { + if(app->lives > 0) { + app->lives--; + + // Play the wrong sound + if(app->sound_enabled) { + play_sound_wrong_move(); + } + resetGame(app); + } else { + // The player has no lives left + // Game over + game_over(app); + //TODO: Play unique game over sound + } +} + +bool isRoundComplete(SimonData* app) { + return app->moveIndex == app->currentScore; +} + +enum shape_names getCurrentSimonMove(SimonData* app) { + return app->simonMoves[app->moveIndex]; +} + +void onPlayerSelectedShapeCallback(enum shape_names shape, SimonData* app) { + if(shape == getCurrentSimonMove(app)) { + onPlayerAnsweredCorrect(app); + } else { + onPlayerAnsweredWrong(app); + } +} + +//@todo gametick +void game_tick(SimonData* simon_state) { + if(simon_state->gameState == inGame) { + if(simon_state->activePlayer == simon) { + // ############### Simon Turn ############### + notification_message(simon_state->notification, &sequence_simon_is_playing); + + //@todo Gameplay + if(simon_state->set_board_neutral) { + if(simon_state->moveIndex < simon_state->currentScore) { + simon_state->selectedShape = getCurrentSimonMove(simon_state); + simon_state->set_board_neutral = false; + simon_state->moveIndex++; + } else { + simon_state->activePlayer = player; + simon_state->set_board_neutral = true; + simon_state->moveIndex = 0; + } + } else { + simon_state->set_board_neutral = true; + } + } else { + // ############### Player Turn ############### + notification_message(simon_state->notification, &sequence_player_is_playing); + + // It's Player's Turn + if(isRoundComplete(simon_state)) { + simon_state->activePlayer = simon; + simon_state->currentScore++; + // app->numberOfMillisecondsBeforeShapeDisappears -= 50; + //TODO: Hacky way of handling highscore by subtracting 1 to account for the first move + if(simon_state->currentScore - 1 > simon_state->highScore) { + simon_state->highScore = simon_state->currentScore - 1; + simon_state->is_new_highscore = true; + } + if(simon_state->sound_enabled) { + play_sound_sequence_correct(); + } + startNewRound(simon_state); + } + } + } +} + +/* ======================== Main Entry Point ============================== */ + +int32_t simon_says_app_entry(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + SimonData* simon_state = malloc(sizeof(SimonData)); + + simon_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!simon_state->mutex) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + free(simon_state); + return -1; + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, simon_draw_callback, simon_state); + view_port_input_callback_set(view_port, simon_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + simon_state->notification = notification; + + InputEvent input; + + // Show Main Menu Screen + //load_game(simon_state); + restart_game_after_gameover(simon_state); + simon_state->gameState = mainMenu; + + while(true) { + game_tick(simon_state); + + FuriStatus q_status = furi_message_queue_get( + event_queue, &input, simon_state->numberOfMillisecondsBeforeShapeDisappears); + furi_mutex_acquire(simon_state->mutex, FuriWaitForever); + + if(q_status == FuriStatusOk) { + //FURI_LOG_D(TAG, "Got input event: %d", input.key); + //break out of the loop if the back key is pressed + if(input.key == InputKeyBack && input.type == InputTypeLong) { + // Save high score before quitting + //if(simon_state->is_new_highscore) { + // save_game(simon_state); + //} + break; + } + + //@todo Set Game States + if(input.key == InputKeyOk && simon_state->gameState != inGame) { + restart_game_after_gameover(simon_state); + // Set Simon Board state + startNewRound(simon_state); + view_port_update(view_port); + } + + // Keep LED on if it is Simon's turn + if(simon_state->activePlayer == player) { + notification_message(notification, &sequence_player_is_playing); + + if(input.type == InputTypePress) { + simon_state->set_board_neutral = false; + + switch(input.key) { + case InputKeyUp: + simon_state->selectedShape = up; + onPlayerSelectedShapeCallback(up, simon_state); + break; + case InputKeyDown: + simon_state->selectedShape = down; + onPlayerSelectedShapeCallback(down, simon_state); + break; + case InputKeyLeft: + simon_state->selectedShape = left; + onPlayerSelectedShapeCallback(left, simon_state); + break; + case InputKeyRight: + simon_state->selectedShape = right; + onPlayerSelectedShapeCallback(right, simon_state); + break; + default: + simon_state->set_board_neutral = true; + break; + } + } else { + //FURI_LOG_D(TAG, "Input type is not short"); + simon_state->set_board_neutral = true; + } + } + } + + // @todo Animation Loop for debug + // if(simon_state->gameState == inGame && simon_state->activePlayer == simon) { + // simon_state->currentScore++; + // simon_state->set_board_neutral = !simon_state->set_board_neutral; + // } + + view_port_update(view_port); + furi_mutex_release(simon_state->mutex); + } + + stop_sound(); + notification_message(notification, &sequence_cleanup); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(simon_state->mutex); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + free(simon_state); + + return 0; +} diff --git a/applications/external/simonsays/simon_says.png b/applications/external/simonsays/simon_says.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe9ba3e6f2acf28e94bdb001606248d1ea0e96a GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>vG;Uw4ABTq z{`3F;|9Unh24x>bWo6~Crv}9VB1+PHmpuX$cYRzE-FvoC;dSRGspU=+R=$usl%-_$ f+E7G@tC(San8)KerT;B~MlpE0`njxgN@xNAKO-r( literal 0 HcmV?d00001 diff --git a/applications/external/snake_game/application.fam b/applications/external/snake_2/application.fam similarity index 55% rename from applications/external/snake_game/application.fam rename to applications/external/snake_2/application.fam index 1e1ee48a6..f35642b7c 100644 --- a/applications/external/snake_game/application.fam +++ b/applications/external/snake_2/application.fam @@ -1,11 +1,12 @@ App( - appid="snake", - name="Snake Game", + appid="snake20", + name="Snake 2.0", apptype=FlipperAppType.EXTERNAL, - entry_point="snake_game_app", + entry_point="snake_20_app", + cdefines=["APP_SNAKE_20"], requires=["gui"], stack_size=1 * 1024, - order=210, + order=30, fap_icon="snake_10px.png", fap_category="Games", ) diff --git a/applications/external/snake_2/snake_10px.png b/applications/external/snake_2/snake_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..125cfb3341b00f9c3cf93a6ae88fb2042722bbf0 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ihk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfQS24TkI`72U@g6^I!jv*SswY`CS3ysNL|NBE6XVjK0w?j96T5q6LB$mUSx$ok&E|(OC&e_JdFCDjd Uc|mutex, FuriWaitForever); + // Before the function is called, the state is set with the canvas_reset(canvas) + // Frame canvas_draw_frame(canvas, 0, 0, 128, 64); @@ -105,6 +111,9 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { f.x = f.x * 4 + 1; f.y = f.y * 4 + 1; canvas_draw_rframe(canvas, f.x, f.y, 6, 6, 2); + canvas_draw_dot(canvas, f.x + 3, f.y - 1); + canvas_draw_dot(canvas, f.x + 4, f.y - 2); + //canvas_draw_dot(canvas,f.x+4,f.y-3); // Snake for(uint16_t i = 0; i < snake_state->len; i++) { @@ -112,24 +121,70 @@ static void snake_game_render_callback(Canvas* const canvas, void* ctx) { p.x = p.x * 4 + 2; p.y = p.y * 4 + 2; canvas_draw_box(canvas, p.x, p.y, 4, 4); + if(i == 0) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, p.x + 1, p.y + 1, 2, 2); + canvas_set_color(canvas, ColorBlack); + } } - // Game Over banner - if(snake_state->state == GameStateGameOver) { + // Pause and GameOver banner + if(snake_state->state == GameStatePause || snake_state->state == GameStateGameOver) { // Screen is 128x64 px canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 34, 20, 62, 24); + canvas_draw_box(canvas, 33, 19, 64, 26); canvas_set_color(canvas, ColorBlack); canvas_draw_frame(canvas, 34, 20, 62, 24); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 37, 31, "Game Over"); + if(snake_state->state == GameStateGameOver) { + canvas_draw_str_aligned(canvas, 65, 31, AlignCenter, AlignBottom, "Game Over"); + } + if(snake_state->state == GameStatePause) { + canvas_draw_str_aligned(canvas, 65, 31, AlignCenter, AlignBottom, "Pause"); + } canvas_set_font(canvas, FontSecondary); - char buffer[12]; + char buffer[20]; snprintf(buffer, sizeof(buffer), "Score: %u", snake_state->len - 7U); - canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); + canvas_draw_str_aligned(canvas, 65, 41, AlignCenter, AlignBottom, buffer); + + // Painting "back"-symbol, Help message for Exit App, ProgressBar + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 25, 2, 81, 11); + canvas_draw_box(canvas, 28, 54, 73, 9); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned( + canvas, 65, 10, AlignCenter, AlignBottom, "Hold to Exit App"); + snprintf( + buffer, sizeof(buffer), "Complete: %-5.1f%%", (double)((snake_state->len - 7U) / 4.58)); + canvas_draw_str_aligned(canvas, 65, 62, AlignCenter, AlignBottom, buffer); + { + canvas_draw_dot(canvas, x_back_symbol + 0, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 1, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 2, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 3, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 4, y_back_symbol); + canvas_draw_dot(canvas, x_back_symbol + 5, y_back_symbol - 1); + canvas_draw_dot(canvas, x_back_symbol + 6, y_back_symbol - 2); + canvas_draw_dot(canvas, x_back_symbol + 6, y_back_symbol - 3); + canvas_draw_dot(canvas, x_back_symbol + 5, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol + 4, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 3, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 2, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 1, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol + 0, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 3, y_back_symbol - 5); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 6); + canvas_draw_dot(canvas, x_back_symbol - 2, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 6); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 4); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 7); + canvas_draw_dot(canvas, x_back_symbol - 1, y_back_symbol - 3); + } } furi_mutex_release(snake_state->mutex); @@ -297,6 +352,7 @@ static void if(eatFruit) { snake_state->len++; if(snake_state->len >= MAX_SNAKE_LEN) { + //You win!!! snake_state->state = GameStateGameOver; notification_message_block(notification, &sequence_fail); return; @@ -308,10 +364,11 @@ static void if(eatFruit) { snake_state->fruit = snake_game_get_new_fruit(snake_state); notification_message(notification, &sequence_eat); + notification_message(notification, &sequence_blink_red_100); } } -int32_t snake_game_app(void* p) { +int32_t snake_20_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SnakeEvent)); @@ -320,7 +377,6 @@ int32_t snake_game_app(void* p) { snake_game_init_game(snake_state); snake_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!snake_state->mutex) { FURI_LOG_E("SnakeGame", "cannot create mutex\r\n"); furi_message_queue_free(event_queue); @@ -343,6 +399,8 @@ int32_t snake_game_app(void* p) { notification_message_block(notification, &sequence_display_backlight_enforce_on); + // dolphin_deed(DolphinDeedPluginGameStart); + SnakeEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); @@ -350,26 +408,83 @@ int32_t snake_game_app(void* p) { furi_mutex_acquire(snake_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { - // press events if(event.type == EventTypeKey) { + // press events if(event.input.type == InputTypePress) { switch(event.input.key) { case InputKeyUp: - snake_state->nextMovement = DirectionUp; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionUp; + } break; case InputKeyDown: - snake_state->nextMovement = DirectionDown; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionDown; + } break; case InputKeyRight: - snake_state->nextMovement = DirectionRight; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionRight; + } break; case InputKeyLeft: - snake_state->nextMovement = DirectionLeft; + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionLeft; + } break; case InputKeyOk: if(snake_state->state == GameStateGameOver) { snake_game_init_game(snake_state); } + if(snake_state->state == GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + snake_state->state = GameStateLife; + } + break; + case InputKeyBack: + if(snake_state->state == GameStateLife) { + furi_timer_stop(timer); + snake_state->state = GameStatePause; + break; + } + if(snake_state->state == GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + snake_state->state = GameStateLife; + break; + } + if(snake_state->state == GameStateGameOver) { + snake_game_init_game(snake_state); + } + default: + break; + } + } + //LongPress Events + if(event.input.type == InputTypeLong) { + switch(event.input.key) { + case InputKeyUp: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionUp; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyDown: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionDown; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyRight: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionRight; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } + break; + case InputKeyLeft: + if(snake_state->state != GameStatePause) { + snake_state->nextMovement = DirectionLeft; + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); + } break; case InputKeyBack: processing = false; @@ -378,6 +493,12 @@ int32_t snake_game_app(void* p) { break; } } + //ReleaseKey Event + if(event.input.type == InputTypeRelease) { + if(snake_state->state != GameStatePause) { + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 4); + } + } } else if(event.type == EventTypeTick) { snake_game_process_game_step(snake_state, notification); } @@ -389,8 +510,8 @@ int32_t snake_game_app(void* p) { furi_mutex_release(snake_state->mutex); } - // Return backlight to normal state - notification_message(notification, &sequence_display_backlight_enforce_auto); + // Wait for all notifications to be played and return backlight to normal state + notification_message_block(notification, &sequence_display_backlight_enforce_auto); furi_timer_free(timer); view_port_enabled_set(view_port, false); diff --git a/applications/external/snake_game/snake_10px.png b/applications/external/snake_game/snake_10px.png deleted file mode 100644 index 52d9fa7e0e1b884774a6e58abb1965b1b1905767..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxc>kDAIJlX?eOh zhE&W+PDn^dVNp_G*rbrd!ONE5$kL$2+HH6!MA=j6#)(e`V#>-4Tp0`o%bUNP1L~43 zag8Vm&QB{TPb^AhaL6gmODsst%q!6^$V=Bv&QD2A{^~3#2UN)5>FVdQ&MBb@0GSOf APyhe` diff --git a/applications/external/t_rex_runner/LICENSE b/applications/external/t_rex_runner/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/t_rex_runner/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/t_rex_runner/application.fam b/applications/external/t_rex_runner/application.fam new file mode 100644 index 000000000..88623b9c4 --- /dev/null +++ b/applications/external/t_rex_runner/application.fam @@ -0,0 +1,13 @@ +App( + appid="t_rex_runner", + name="T-Rex runner", + apptype=FlipperAppType.EXTERNAL, + entry_point="trexrunner_app", + cdefines=["APP_TREXRUNNER"], + requires=["gui"], + stack_size=8 * 1024, + fap_category="Games", + fap_icon="trexrunner_icon.png", + fap_icon_assets="assets", + order=36, +) diff --git a/applications/external/t_rex_runner/assets/Cactus.png b/applications/external/t_rex_runner/assets/Cactus.png new file mode 100644 index 0000000000000000000000000000000000000000..0b832932760cca3d8844bca7960926960db06ecc GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIaRt)<|NmclbN*c*OWD)K zF@)oK@{ihvhl`mHyRx-)^6&^tNJz;@@W~`paEP!dGOU=$qhxGhwg{-4!PC{xWt~$( F69C)u9Nqu` literal 0 HcmV?d00001 diff --git a/applications/external/t_rex_runner/assets/Dino.png b/applications/external/t_rex_runner/assets/Dino.png new file mode 100644 index 0000000000000000000000000000000000000000..9e33092d6e97d0bb70365d5b88d902e21173d5df GIT binary patch literal 4339 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 xFg#-aBQIfhqCVk=<$vbG^M53A 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 literal 0 HcmV?d00001 diff --git a/applications/external/t_rex_runner/trexrunner.c b/applications/external/t_rex_runner/trexrunner.c new file mode 100644 index 000000000..d7db91fad --- /dev/null +++ b/applications/external/t_rex_runner/trexrunner.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "t_rex_runner_icons.h" + +#define DINO_START_X 10 +#define DINO_START_Y 34 // 64 - 22 - BACKGROUND_H / 2 - 2 + +#define FPS 20 + +#define DINO_RUNNING_MS_PER_FRAME 500 + +#define GRAVITY 60 +#define JUMP_SPEED 30 + +#define CACTUS_W 10 +#define CACTUS_H 10 +#define START_x_speed 25 + +#define BACKGROUND_W 128 +#define BACKGROUND_H 12 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + FuriTimer* timer; + uint32_t last_tick; + const Icon* dino_icon; + int dino_frame_ms; + FuriMutex* mutex; + + // Dino info + float y_position; + float y_speed; + int y_acceleration; + float x_speed; + + // Cactus info + int cactus_position; + int has_cactus; + + // Horizontal line + int background_position; + + int lost; + + int score; +} GameState; + +static void timer_callback(void* ctx) { + GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state == NULL) { + return; + } + + uint32_t ticks_elapsed = furi_get_tick() - game_state->last_tick; + game_state->last_tick = furi_get_tick(); + int delta_time_ms = ticks_elapsed * 1000 / furi_kernel_get_tick_frequency(); + + // dino update + game_state->dino_frame_ms += delta_time_ms; + // TODO: switch by dino state + if(game_state->dino_frame_ms >= DINO_RUNNING_MS_PER_FRAME) { + if(game_state->dino_icon == &I_DinoRun0) { + game_state->dino_icon = &I_DinoRun1; + } else { + game_state->dino_icon = &I_DinoRun0; + } + game_state->dino_frame_ms = 0; + } + + // Compute dino dynamics + game_state->y_acceleration = game_state->y_acceleration - GRAVITY * delta_time_ms / 1000; + game_state->y_speed = game_state->y_speed + game_state->y_acceleration * delta_time_ms / 1000; + game_state->y_position = game_state->y_position - game_state->y_speed * delta_time_ms / 1000; + + // Touch ground + if(game_state->y_position >= DINO_START_Y) { + game_state->y_acceleration = 0; + game_state->y_speed = 0; + game_state->y_position = DINO_START_Y; + } + + // Update Cactus state + if(game_state->has_cactus) { + game_state->cactus_position = + game_state->cactus_position - game_state->x_speed * delta_time_ms / 1000; + if(game_state->cactus_position <= 0) { + game_state->has_cactus = 0; + game_state->score = game_state->score + 1; + } + } + // Create cactus (not random) + else { + game_state->has_cactus = 1; + game_state->cactus_position = 120; + } + + // Move horizontal line + if(game_state->background_position <= -BACKGROUND_W) + game_state->background_position += BACKGROUND_W; + game_state->background_position = + game_state->background_position - game_state->x_speed * delta_time_ms / 1000; + + // Lose condition + if((game_state->y_position + 22 >= (64 - CACTUS_H)) && + ((DINO_START_X + 20) >= game_state->cactus_position) && + (DINO_START_X <= (game_state->cactus_position + CACTUS_W))) + game_state->lost = 1; + + furi_mutex_release(game_state->mutex); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state == NULL) { + return; + } + + char score_string[12]; + if(!game_state->lost) { + // Show Ground + canvas_draw_icon( + canvas, game_state->background_position, 64 - BACKGROUND_H, &I_HorizonLine0); + canvas_draw_icon( + canvas, + game_state->background_position + BACKGROUND_W, + 64 - BACKGROUND_H, + &I_HorizonLine0); + + // Show DINO + canvas_draw_icon(canvas, DINO_START_X, game_state->y_position, game_state->dino_icon); + + // Show cactus + if(game_state->has_cactus) + //canvas_draw_triangle(canvas, game_state->cactus_position, 64 - BACKGROUND_H + CACTUS_W, CACTUS_W, CACTUS_H, CanvasDirectionBottomToTop); + canvas_draw_icon( + canvas, + game_state->cactus_position, + 64 - BACKGROUND_H / 2 - CACTUS_H - 2, + &I_Cactus); + + // Show score + if(game_state->score == 0) canvas_set_font(canvas, FontSecondary); + snprintf(score_string, 12, "Score: %d", game_state->score); + canvas_draw_str_aligned(canvas, 85, 5, AlignLeft, AlignTop, score_string); + + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, "You lost :c"); + } + + furi_mutex_release(game_state->mutex); +} + +static void game_state_init(GameState* const game_state) { + game_state->last_tick = furi_get_tick(); + game_state->dino_frame_ms = 0; + game_state->dino_icon = &I_Dino; + game_state->y_acceleration = game_state->y_speed = 0; + game_state->y_position = DINO_START_Y; + game_state->has_cactus = 0; + game_state->background_position = 0; + game_state->lost = 0; + game_state->x_speed = START_x_speed; + game_state->score = 0; + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); +} + +int32_t trexrunner_app() { + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + game_state_init(game_state); + + if(!game_state->mutex) { + FURI_LOG_E("T-rex runner", "cannot create mutex\r\n"); + free(game_state); + return 255; + } + // BEGIN IMPLEMENTATION + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, game_state); + view_port_input_callback_set(view_port, input_callback, event_queue); + game_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, game_state); + + furi_timer_start(game_state->timer, (uint32_t)furi_kernel_get_tick_frequency() / FPS); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + for(bool processing = true; processing && !game_state->lost;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyLeft: + break; + case InputKeyRight: + break; + case InputKeyOk: + if(game_state->y_position == DINO_START_Y) + game_state->y_speed = JUMP_SPEED; + break; + case InputKeyMAX: + break; + case InputKeyBack: + // Exit the app + processing = false; + break; + } + } + } + } else { + // event timeout + ; + } + if(game_state->lost) { + furi_message_queue_get( + event_queue, &event, 1500); //Sleep to show the "you lost" message + } + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_mutex_free(game_state->mutex); + furi_timer_free(game_state->timer); + free(game_state); + + return 0; +} diff --git a/applications/external/t_rex_runner/trexrunner_icon.png b/applications/external/t_rex_runner/trexrunner_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0b832932760cca3d8844bca7960926960db06ecc GIT binary patch literal 110 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIaRt)<|NmclbN*c*OWD)K zF@)oK@{ihvhl`mHyRx-)^6&^tNJz;@@W~`paEP!dGOU=$qhxGhwg{-4!PC{xWt~$( F69C)u9Nqu` literal 0 HcmV?d00001 diff --git a/applications/external/t_rex_runner/uncut_assets/HorizonLine0.png b/applications/external/t_rex_runner/uncut_assets/HorizonLine0.png new file mode 100644 index 0000000000000000000000000000000000000000..b326cbb7f470f4d0796e18cbe8babfd065303422 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0y~yV2S{;c^H|2WQ#^%IFMos@Ck7R(%-*-&pmM_9>{9& zba4#PIG_B(-}Zok(3wSQ5_cYcJuj#D#b552Ro}85vfC{ce0l00Ec^5CKf4}l(qHCB z`wJ%iRCx67zkI;YDvrDSYUTgxeJ=F>Pw4rsap?Jfl9R zTq{#`%DM6hk$)2UKOR2v?s1ullm7p5m5=8OE;H=iqdDtX^Y(K`N<9fa_k_4qS!W_|n# zLio)(|A#oUp8u;q|F0g^EOa3MkAcDd0Rut6|DQn~T{kv6=b!&i4!~;G0eMQy`e%<6 z+i;VPqbY93lSF`;82-Rb`TZXe>>$=ZApVQ+H;B0Z{|8ha8tVOEa_9fYIQ490km z8rA=i#2=&kaX;84hmjoc5vJhpc?_>Yfju$@lx!LJ!5)KWocp*PiO|cBoZvshO#=Xk WcPDMu%7Baj0000 Date: Sat, 17 Jun 2023 03:09:51 +0100 Subject: [PATCH 212/370] Rename ifttt button --- applications/external/ifttt/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/ifttt/application.fam b/applications/external/ifttt/application.fam index cdb439f26..1a86000a4 100644 --- a/applications/external/ifttt/application.fam +++ b/applications/external/ifttt/application.fam @@ -1,6 +1,6 @@ App( appid="esp8266_ifttt_virtual_button", - name="[ESP8266] IFTTT Virtual Button", + name="[ESP8266] IFTTT Button", apptype=FlipperAppType.EXTERNAL, entry_point="ifttt_virtual_button_app", cdefines=["APP_IFTTT_VIRTUAL_BUTTON"], From 80a3b2eaed8215459d3181972ee69ee0bc0b1b14 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 03:10:42 +0100 Subject: [PATCH 213/370] Change 4 in a row name --- applications/external/4inrow/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/4inrow/application.fam b/applications/external/4inrow/application.fam index 02c005b1e..01e2df644 100644 --- a/applications/external/4inrow/application.fam +++ b/applications/external/4inrow/application.fam @@ -1,6 +1,6 @@ App( appid="4inrow", - name="4 in row", + name="4 in a Row", apptype=FlipperAppType.EXTERNAL, entry_point="four_in_row_app", requires=[ From bd743dc1d8ea2c55ba0a838055941b57caf993b1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 17 Jun 2023 21:50:24 +0300 Subject: [PATCH 214/370] Update changelog and readme --- CHANGELOG.md | 32 ++++++++++++++++++-------------- ReadMe.md | 10 +++++----- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a03f11808..9f6ac7196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,9 @@ ### New changes -* BLE: Revert BLE gatt characteristics refactoring temporarily -> **Should fix HID issues on older iOS, and maybe some issues with android app** -* SubGHz: Added external cc1101 module at CLI (by @Sil333033 & @xMasterX | PR #513) -* SubGHz: Remove unused global var -* Plugins: Fix ProtoView issue #503 again -> (Broken saved files with custom modulation) -* OFW: furi_hal_nfc: fix rfalTransceiveBitsBlockingTx's 4th argument to bits count rather than bytes count -* OFW: FuriHal: remove clock startup time tracking from clean builds +* Infrared: Updated universal remote asstes (by @amec0e | PR #522) +* Plugins: Fixed ESP32 WiFi Marauder crashes when reopening app +* OFW: Debug: sync apps on attach, makes it possible to debug already started app that has crashed -#### [🎲 Download latest extra apps pack](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip) +---- [-> How to install firmware](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md) @@ -25,19 +22,26 @@ * XMR (Monero): `41xUz92suUu1u5Mu4qkrcs52gtfpu9rnZRdBpCJ244KRHf6xXSvVFevdf2cnjS7RAeYr5hn9MsEfxKoFDRSctFjG5fv1Mhn` * TON: `EQCOqcnYkvzOZUV_9bPE_8oTbOrOF03MnF-VcJyjisTZmpGf` -### Thanks to our sponsors: +#### Thanks to our sponsors: callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron, UterGrooll, LUCFER, Northpirate, zloepuzo, T.Rat, Alexey B., ionelife, ... and all other great people who supported our project and me (xMasterX), thanks to you all! -**Note: To avoid issues with .dfu, prefer installing using .tgz with qFlipper, web updater or by self update package, all needed assets will be installed** -**Recommended option - Web Updater** +### **Recommended update option - Web Updater** ### What `n`, `r`, `e` means? What I need to download if I don't want to use Web updater? -What means `n` or `e` in - `flipper-z-f7-update-(version)(n / r / e).tgz` ? - `n` means this build comes without our custom animations, only official flipper animations, -`e` means build has extra apps pack preinstalled, -`r` means RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) +What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e).tgz` ? +`flipper-z` = for Flipper Zero device +`f7` = Hardware version - same for all flipper zero devices +`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself +`(version)` = Firmware version +`n` = this build comes without our custom animations (we have only 3 of them), only official flipper animations +`e` = build has 🎲 [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled +`r` = RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) (do not install on non modded device!) + +Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web +Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz` +SDK files for plugins development and uFBT - `flipper-z-f7-sdk-(version).zip` -Self-update package (update from microSD) - `flipper-z-f7-update-(version).zip` or download `.tgz` for mobile app / qFlipper / web diff --git a/ReadMe.md b/ReadMe.md index 19d74fed8..3ab8e4b4a 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -6,15 +6,15 @@ ### Welcome to the Flipper Zero Unleashed Firmware repo! -**This firmware is a fork from** [flipperdevices/flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware) +#### **This firmware is a fork from** [flipperdevices/flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware)
-Most stable custom firmware focused on new features and improvements of original firmware components, with almost no UI changes +### Most stable custom firmware focused on new features and improvements of original firmware components, with almost no UI changes
-### This software is for experimental purposes only and is not meant for any illegal activity/purposes.
We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law.
Also, this software is made without any support from Flipper Devices and is in no way related to the official devs. +##### This software is for experimental purposes only and is not meant for any illegal activity/purposes.
We do not condone illegal activity and strongly encourage keeping transmissions to legal/valid uses allowed by law.
Also, this software is made without any support from Flipper Devices and is in no way related to the official devs.
Our Discord Community: @@ -79,7 +79,7 @@ Keeloq [Not ALL systems supported for decode or emulation yet!] - [Supported man Encoders or sending made by @xMasterX: - Nero Radio 57bit (+ 56bit encoder improvements) -- CAME 12bit/24bit encoder fixes (already merged in OFW) +- CAME 12bit/24bit encoder fixes (Fixes now merged in OFW) - Keeloq: HCS101 - Keeloq: AN-Motors - Keeloq: JCM Tech @@ -97,7 +97,7 @@ Encoders or sending made by @xMasterX: Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMasterX (current version): - CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - Nice Flor S -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) -- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)] +- FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: @mmx7)] - Keeloq: BFT Mitto -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - Star Line - Security+ v1 & v2 (encoders was made in OFW) From 851aabdcb5cfd6bbec510fa759248e8bfb28f91e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 17 Jun 2023 21:52:27 +0300 Subject: [PATCH 215/370] Update changelog --- CHANGELOG.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f6ac7196..81edf5ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### New changes +## New changes * Infrared: Updated universal remote asstes (by @amec0e | PR #522) * Plugins: Fixed ESP32 WiFi Marauder crashes when reopening app * OFW: Debug: sync apps on attach, makes it possible to debug already started app that has crashed @@ -27,16 +27,16 @@ callmezimbra, Quen0n, MERRON, grvpvl (lvpvrg), art_col, ThurstonWaffles, Moneron and all other great people who supported our project and me (xMasterX), thanks to you all! -### **Recommended update option - Web Updater** +## **Recommended update option - Web Updater** ### What `n`, `r`, `e` means? What I need to download if I don't want to use Web updater? -What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e).tgz` ? -`flipper-z` = for Flipper Zero device -`f7` = Hardware version - same for all flipper zero devices -`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself -`(version)` = Firmware version -`n` = this build comes without our custom animations (we have only 3 of them), only official flipper animations -`e` = build has 🎲 [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled +What build I should download and what this name means - `flipper-z-f7-update-(version)(n / r / e).tgz` ?
+`flipper-z` = for Flipper Zero device
+`f7` = Hardware version - same for all flipper zero devices
+`update` = Update package, contains updater, all assets (plugins, IR libs, etc.), and firmware itself
+`(version)` = Firmware version
+`n` = this build comes without our custom animations (we have only 3 of them), only official flipper animations
+`e` = build has 🎲 [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled
`r` = RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) (do not install on non modded device!) Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web From 1c06ac1a82b083ae8c673b618eb4080c04f58400 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 17 Jun 2023 21:53:03 +0300 Subject: [PATCH 216/370] ... --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81edf5ec4..e4249c94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,8 +39,8 @@ What build I should download and what this name means - `flipper-z-f7-update-(ve `e` = build has 🎲 [extra apps pack](https://github.com/xMasterX/all-the-plugins) preinstalled
`r` = RGB patch (+ extra apps) for flippers with rgb backlight mod (this is hardware mod!) (Works only on modded flippers!) (do not install on non modded device!) -Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web -Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz` +Firmware Self-update package (update from microSD) - `flipper-z-f7-update-(version).tgz` for mobile app / qFlipper / web
+Archive of `scripts` folder (contains scripts for FW/plugins development) - `flipper-z-any-scripts-(version).tgz`
SDK files for plugins development and uFBT - `flipper-z-f7-sdk-(version).zip` From 9781ae4891b5578dbece267405569841161726e3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 21:34:06 +0100 Subject: [PATCH 217/370] Update apps --- .../esp32cam_marauder_companion/wifi_marauder_app.c | 13 ++++++++----- .../wifi_marauder_uart.c | 3 +++ applications/external/nrf24scan/nrf24scan.c | 7 ++++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c b/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c index cb41468fd..0f3d4e00a 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_app.c @@ -158,11 +158,12 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { int32_t wifi_marauder_app(void* p) { UNUSED(p); - furi_hal_power_disable_external_3_3v(); - furi_hal_power_disable_otg(); + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } furi_delay_ms(200); - furi_hal_power_enable_external_3_3v(); - furi_hal_power_enable_otg(); for(int i = 0; i < 2; i++) { furi_delay_ms(500); furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t[1]){'w'}, 1); @@ -181,7 +182,9 @@ int32_t wifi_marauder_app(void* p) { wifi_marauder_app_free(wifi_marauder_app); - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } return 0; } diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_uart.c b/applications/external/esp32cam_marauder_companion/wifi_marauder_uart.c index 37304f06a..77ad239b9 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_uart.c +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_uart.c @@ -106,6 +106,9 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_thread_free(uart->rx_thread); furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); + if(uart->channel == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(uart->channel); + } furi_hal_console_enable(); free(uart); diff --git a/applications/external/nrf24scan/nrf24scan.c b/applications/external/nrf24scan/nrf24scan.c index f1182cff9..73d93e673 100644 --- a/applications/external/nrf24scan/nrf24scan.c +++ b/applications/external/nrf24scan/nrf24scan.c @@ -1436,8 +1436,13 @@ int32_t nrf24scan_app(void* p) { else menu_selected = 0; } else if(what_doing == 1) { + if(log_arr_idx == 0) + view_log_arr_idx = 0; + else { view_log_arr_idx += event.input.type == InputTypeRepeat ? 10 : 1; - if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = log_arr_idx - 1; + if(view_log_arr_idx >= log_arr_idx) + view_log_arr_idx = log_arr_idx - 1; + } } else if(what_doing == 2) { if(view_found < found_total / 7) view_found++; } From c086eab06b80786a78741c528977613db2205a98 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 22:12:41 +0100 Subject: [PATCH 218/370] Add some apps --- applications/external/etch_a_sketch/LICENSE | 674 ++++++ .../external/etch_a_sketch/application.fam | 13 + .../etch_a_sketch/etch-a-sketch-icon.png | Bin 0 -> 239 bytes .../external/etch_a_sketch/etch_a_sketch.c | 274 +++ applications/external/flizzer_tracker/LICENSE | 21 + .../external/flizzer_tracker/application.fam | 16 + .../external/flizzer_tracker/audio_modes.c | 8 + .../external/flizzer_tracker/diskop.c | 259 +++ .../external/flizzer_tracker/diskop.h | 13 + .../flizzer_tracker/flizzer_tracker.c | 218 ++ .../flizzer_tracker/flizzer_tracker.h | 226 ++ .../flizzer_tracker/flizzer_tracker.png | Bin 0 -> 1147 bytes .../flizzer_tracker/flizzer_tracker_hal.c | 313 +++ .../flizzer_tracker/flizzer_tracker_hal.h | 43 + applications/external/flizzer_tracker/font.h | 31 + .../flizzer_tracker/images/channel_off.png | Bin 0 -> 141 bytes .../flizzer_tracker/images/channel_on.png | Bin 0 -> 141 bytes .../images/checkbox_checked.png | Bin 0 -> 143 bytes .../flizzer_tracker/images/checkbox_empty.png | Bin 0 -> 140 bytes .../images/flizzer_tracker_instrument.png | Bin 0 -> 1139 bytes .../images/flizzer_tracker_module.png | Bin 0 -> 1158 bytes .../external/flizzer_tracker/images/help.png | Bin 0 -> 511 bytes .../flizzer_tracker/images/note_release.png | Bin 0 -> 142 bytes .../external/flizzer_tracker/init_deinit.c | 298 +++ .../external/flizzer_tracker/init_deinit.h | 14 + .../flizzer_tracker/input/instrument.c | 536 +++++ .../flizzer_tracker/input/instrument.h | 12 + .../input/instrument_program.c | 239 ++ .../input/instrument_program.h | 12 + .../external/flizzer_tracker/input/pattern.c | 410 ++++ .../external/flizzer_tracker/input/pattern.h | 14 + .../external/flizzer_tracker/input/sequence.c | 209 ++ .../external/flizzer_tracker/input/sequence.h | 12 + .../external/flizzer_tracker/input/songinfo.c | 224 ++ .../external/flizzer_tracker/input/songinfo.h | 14 + .../external/flizzer_tracker/input_event.c | 501 +++++ .../external/flizzer_tracker/input_event.h | 40 + .../external/flizzer_tracker/macros.h | 2 + .../flizzer_tracker/sound_engine/freqs.c | 36 + .../flizzer_tracker/sound_engine/freqs.h | 11 + .../sound_engine/sound_engine.c | 201 ++ .../sound_engine/sound_engine.h | 23 + .../sound_engine/sound_engine_adsr.c | 58 + .../sound_engine/sound_engine_adsr.h | 9 + .../sound_engine/sound_engine_defs.h | 102 + .../sound_engine/sound_engine_filter.c | 28 + .../sound_engine/sound_engine_filter.h | 9 + .../sound_engine/sound_engine_osc.c | 278 +++ .../sound_engine/sound_engine_osc.h | 8 + .../flizzer_tracker/tracker_engine/diskop.c | 127 ++ .../flizzer_tracker/tracker_engine/diskop.h | 12 + .../tracker_engine/do_effects.c | 466 ++++ .../tracker_engine/do_effects.h | 10 + .../tracker_engine/tracker_engine.c | 696 ++++++ .../tracker_engine/tracker_engine.h | 28 + .../tracker_engine/tracker_engine_defs.h | 232 ++ applications/external/flizzer_tracker/util.c | 202 ++ applications/external/flizzer_tracker/util.h | 26 + .../flizzer_tracker/view/char_array.c | 4 + .../flizzer_tracker/view/instrument_editor.c | 669 ++++++ .../flizzer_tracker/view/instrument_editor.h | 11 + .../flizzer_tracker/view/opcode_description.c | 72 + .../flizzer_tracker/view/opcode_description.h | 12 + .../flizzer_tracker/view/pattern_editor.c | 442 ++++ .../flizzer_tracker/view/pattern_editor.h | 25 + applications/external/nrf24_batch/LICENSE | 674 ++++++ .../external/nrf24_batch/application.fam | 20 + .../external/nrf24_batch/lib/nrf24/nrf24.c | 365 +++ .../external/nrf24_batch/lib/nrf24/nrf24.h | 386 ++++ .../external/nrf24_batch/nrf24batch.c | 1957 +++++++++++++++++ .../external/nrf24_batch/nrf24batch.h | 32 + .../external/nrf24_batch/nrf24batch_10px.png | Bin 0 -> 1771 bytes 72 files changed, 11877 insertions(+) create mode 100644 applications/external/etch_a_sketch/LICENSE create mode 100644 applications/external/etch_a_sketch/application.fam create mode 100644 applications/external/etch_a_sketch/etch-a-sketch-icon.png create mode 100644 applications/external/etch_a_sketch/etch_a_sketch.c create mode 100644 applications/external/flizzer_tracker/LICENSE create mode 100644 applications/external/flizzer_tracker/application.fam create mode 100644 applications/external/flizzer_tracker/audio_modes.c create mode 100644 applications/external/flizzer_tracker/diskop.c create mode 100644 applications/external/flizzer_tracker/diskop.h create mode 100644 applications/external/flizzer_tracker/flizzer_tracker.c create mode 100644 applications/external/flizzer_tracker/flizzer_tracker.h create mode 100644 applications/external/flizzer_tracker/flizzer_tracker.png create mode 100644 applications/external/flizzer_tracker/flizzer_tracker_hal.c create mode 100644 applications/external/flizzer_tracker/flizzer_tracker_hal.h create mode 100644 applications/external/flizzer_tracker/font.h create mode 100644 applications/external/flizzer_tracker/images/channel_off.png create mode 100644 applications/external/flizzer_tracker/images/channel_on.png create mode 100644 applications/external/flizzer_tracker/images/checkbox_checked.png create mode 100644 applications/external/flizzer_tracker/images/checkbox_empty.png create mode 100644 applications/external/flizzer_tracker/images/flizzer_tracker_instrument.png create mode 100644 applications/external/flizzer_tracker/images/flizzer_tracker_module.png create mode 100644 applications/external/flizzer_tracker/images/help.png create mode 100644 applications/external/flizzer_tracker/images/note_release.png create mode 100644 applications/external/flizzer_tracker/init_deinit.c create mode 100644 applications/external/flizzer_tracker/init_deinit.h create mode 100644 applications/external/flizzer_tracker/input/instrument.c create mode 100644 applications/external/flizzer_tracker/input/instrument.h create mode 100644 applications/external/flizzer_tracker/input/instrument_program.c create mode 100644 applications/external/flizzer_tracker/input/instrument_program.h create mode 100644 applications/external/flizzer_tracker/input/pattern.c create mode 100644 applications/external/flizzer_tracker/input/pattern.h create mode 100644 applications/external/flizzer_tracker/input/sequence.c create mode 100644 applications/external/flizzer_tracker/input/sequence.h create mode 100644 applications/external/flizzer_tracker/input/songinfo.c create mode 100644 applications/external/flizzer_tracker/input/songinfo.h create mode 100644 applications/external/flizzer_tracker/input_event.c create mode 100644 applications/external/flizzer_tracker/input_event.h create mode 100644 applications/external/flizzer_tracker/macros.h create mode 100644 applications/external/flizzer_tracker/sound_engine/freqs.c create mode 100644 applications/external/flizzer_tracker/sound_engine/freqs.h create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine.c create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine.h create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.c create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.h create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine_defs.h create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine_filter.c create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine_filter.h create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine_osc.c create mode 100644 applications/external/flizzer_tracker/sound_engine/sound_engine_osc.h create mode 100644 applications/external/flizzer_tracker/tracker_engine/diskop.c create mode 100644 applications/external/flizzer_tracker/tracker_engine/diskop.h create mode 100644 applications/external/flizzer_tracker/tracker_engine/do_effects.c create mode 100644 applications/external/flizzer_tracker/tracker_engine/do_effects.h create mode 100644 applications/external/flizzer_tracker/tracker_engine/tracker_engine.c create mode 100644 applications/external/flizzer_tracker/tracker_engine/tracker_engine.h create mode 100644 applications/external/flizzer_tracker/tracker_engine/tracker_engine_defs.h create mode 100644 applications/external/flizzer_tracker/util.c create mode 100644 applications/external/flizzer_tracker/util.h create mode 100644 applications/external/flizzer_tracker/view/char_array.c create mode 100644 applications/external/flizzer_tracker/view/instrument_editor.c create mode 100644 applications/external/flizzer_tracker/view/instrument_editor.h create mode 100644 applications/external/flizzer_tracker/view/opcode_description.c create mode 100644 applications/external/flizzer_tracker/view/opcode_description.h create mode 100644 applications/external/flizzer_tracker/view/pattern_editor.c create mode 100644 applications/external/flizzer_tracker/view/pattern_editor.h create mode 100644 applications/external/nrf24_batch/LICENSE create mode 100644 applications/external/nrf24_batch/application.fam create mode 100644 applications/external/nrf24_batch/lib/nrf24/nrf24.c create mode 100644 applications/external/nrf24_batch/lib/nrf24/nrf24.h create mode 100644 applications/external/nrf24_batch/nrf24batch.c create mode 100644 applications/external/nrf24_batch/nrf24batch.h create mode 100644 applications/external/nrf24_batch/nrf24batch_10px.png diff --git a/applications/external/etch_a_sketch/LICENSE b/applications/external/etch_a_sketch/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/etch_a_sketch/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/etch_a_sketch/application.fam b/applications/external/etch_a_sketch/application.fam new file mode 100644 index 000000000..4dc6dd4b8 --- /dev/null +++ b/applications/external/etch_a_sketch/application.fam @@ -0,0 +1,13 @@ +App( + appid="etch", + name="Etch A Sketch", + apptype=FlipperAppType.EXTERNAL, + entry_point="etch_a_sketch_app", + cdefines=["APP_ETCH_A_SKETCH"], + requires=["gui"], + stack_size=2 * 1024, + order=175, + fap_icon="etch-a-sketch-icon.png", + fap_category="Misc", + fap_libs=["assets"], +) diff --git a/applications/external/etch_a_sketch/etch-a-sketch-icon.png b/applications/external/etch_a_sketch/etch-a-sketch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..567c16900e0368518dee59c52cc8f2dcd25600cb GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&1qS$pxbE4rCowUxu&}VTwROji z9smFTPfbnr^Ye>|i8+1xw6(SMoH=v0ZQIt?*4EI_P*70d<>l4c**SOaTn`VAzkmOl zo13SlrA0+W<>%*TW@g&i*&XIpr~?|#S>O>_%)r2R1cVurPy zctC-N<-p_?hZJ_1-}+aKO&-+hI@kz0JW^BKaQWoN`q~XEq;xBm#Q0qG@>{W1|Mj-b iAw6YtuK(RrS`#;$>9mph#iu~?7(8A5T-G@yGywokYFp+2 literal 0 HcmV?d00001 diff --git a/applications/external/etch_a_sketch/etch_a_sketch.c b/applications/external/etch_a_sketch/etch_a_sketch.c new file mode 100644 index 000000000..6fd6d3596 --- /dev/null +++ b/applications/external/etch_a_sketch/etch_a_sketch.c @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Header-file for boolean data-type. +#include +#include + +#define WIDTH 64 +#define HEIGHT 32 + +const int brush_size = 2; + +typedef struct selected_position { + int x; + int y; +} selected_position; + +typedef struct { + FuriMutex* mutex; + selected_position selected; + bool board[64][32]; + bool isDrawing; + bool showWelcome; +} EtchData; + +// Sequence to indicate that drawing is enabled. +const NotificationSequence sequence_begin_draw = { + &message_display_backlight_on, + + // Vibrate to indicate that drawing is enabled. + &message_vibro_on, + &message_note_g5, + &message_delay_50, + &message_note_c6, + &message_delay_50, + &message_note_e5, + &message_vibro_off, + &message_sound_off, + NULL, +}; + +// sequence to indicate that drawing is disabled +const NotificationSequence sequence_end_draw = { + &message_red_0, + // Indicate that drawing is disabled. + &message_vibro_on, + &message_note_g5, + &message_delay_50, + &message_note_e5, + &message_delay_50, + &message_vibro_off, + &message_sound_off, + &message_do_not_reset, + NULL, +}; + +// Indicate that drawing is enabled. +const NotificationSequence sequence_draw_enabled = { + &message_red_255, + &message_do_not_reset, + NULL, +}; + +// Indicate that drawing is disabled. +const NotificationSequence sequence_draw_disabled = { + &message_red_0, + &message_do_not_reset, + NULL, +}; + +const NotificationSequence sequence_cleanup = { + &message_red_0, + &message_green_0, + &message_blue_0, + &message_sound_off, + &message_vibro_off, + NULL, +}; + +void etch_draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + const EtchData* etch_state = ctx; + furi_mutex_acquire(etch_state->mutex, FuriWaitForever); + + canvas_clear(canvas); + + // Show Welcome Message + if(etch_state->showWelcome) { + // Draw Etch A Sketch frame + canvas_draw_frame(canvas, 5, 3, 119, 55); // Border + canvas_draw_icon(canvas, 8, 50, &I_Ok_btn_pressed_13x13); // Left Knob + canvas_draw_icon(canvas, 107, 50, &I_Ok_btn_pressed_13x13); // Right Knob + + // Draw Etch A Sketch text banner + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 36, 15, "Etch A Sketch"); + + // Draw Etch A Sketch instructions "Hold Back to clear" + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 31, 26, "* Hold "); + canvas_draw_icon(canvas, 59, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 72, 26, "to clear"); + + // Draw Etch A Sketch instructions "Hold OK button to draw" + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 31, 37, "* Hold"); + canvas_draw_icon(canvas, 61, 30, &I_ButtonCenter_7x7); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 72, 37, "to draw"); + } + + canvas_set_color(canvas, ColorBlack); + //draw the canvas(64x32) on screen(144x64) using brush_size*brush_size tiles + for(int y = 0; y < 32; y++) { + for(int x = 0; x < 64; x++) { + if(etch_state->board[x][y]) { + canvas_draw_box(canvas, x * brush_size, y * brush_size, 2, 2); + } + } + } + + //draw cursor as a brush_size by brush_size black box + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + etch_state->selected.x * brush_size, + etch_state->selected.y * brush_size, + brush_size, + brush_size); + + //release the mutex + furi_mutex_release(etch_state->mutex); +} + +void etch_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t etch_a_sketch_app(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + EtchData* etch_state = malloc(sizeof(EtchData)); + etch_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!etch_state->mutex) { + FURI_LOG_E("etch", "cannot create mutex\r\n"); + free(etch_state); + return -1; + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, etch_draw_callback, etch_state); + view_port_input_callback_set(view_port, etch_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + InputEvent event; + + // Show Welcome Banner + etch_state->showWelcome = true; + + while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + //break out of the loop if the back key is pressed + if(event.key == InputKeyBack && event.type == InputTypeShort) { + break; + } + + // Clear + // TODO: Do animation of shaking board + if(event.key == InputKeyBack && event.type == InputTypeLong) { + etch_state->showWelcome = false; + etch_state->board[1][1] = true; + for(int y = 0; y < 32; y++) { + for(int x = 0; x < 64; x++) { + etch_state->board[x][y] = false; + } + } + view_port_update(view_port); + } + + // Keep LED on while drawing + if(etch_state->isDrawing) { + notification_message(notification, &sequence_draw_enabled); + } else { + notification_message(notification, &sequence_draw_disabled); + } + + // Single Dot Select + if(event.key == InputKeyOk && event.type == InputTypeShort) { + etch_state->board[etch_state->selected.x][etch_state->selected.y] = + !etch_state->board[etch_state->selected.x][etch_state->selected.y]; + } + + // Start Drawing + if(event.key == InputKeyOk && event.type == InputTypeLong) { + // notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_begin_draw); + notification_message(notification, &sequence_begin_draw); + + if(etch_state->isDrawing) { + // We're ending the drawing + notification_message(notification, &sequence_end_draw); + } + + etch_state->isDrawing = !etch_state->isDrawing; + etch_state->board[etch_state->selected.x][etch_state->selected.y] = true; + + view_port_update(view_port); + } + + //check the key pressed and change x and y accordingly + if(event.type == InputTypeShort || event.type == InputTypeRepeat || + event.type == InputTypeLong) { + switch(event.key) { + case InputKeyUp: + etch_state->selected.y -= 1; + break; + case InputKeyDown: + etch_state->selected.y += 1; + break; + case InputKeyLeft: + etch_state->selected.x -= 1; + break; + case InputKeyRight: + etch_state->selected.x += 1; + break; + default: + break; + } + + //check if cursor position is out of bounds and reset it to the closest position + if(etch_state->selected.x < 0) { + etch_state->selected.x = 0; + } + if(etch_state->selected.x > 61) { + etch_state->selected.x = 61; + } + if(etch_state->selected.y < 0) { + etch_state->selected.y = 0; + } + if(etch_state->selected.y > 31) { + etch_state->selected.y = 31; + } + if(etch_state->isDrawing == true) { + etch_state->board[etch_state->selected.x][etch_state->selected.y] = true; + } + view_port_update(view_port); + } + } + + notification_message(notification, &sequence_cleanup); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_mutex_free(etch_state->mutex); + furi_message_queue_free(event_queue); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + free(etch_state); + + return 0; +} diff --git a/applications/external/flizzer_tracker/LICENSE b/applications/external/flizzer_tracker/LICENSE new file mode 100644 index 000000000..cd5059b4c --- /dev/null +++ b/applications/external/flizzer_tracker/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 LTVA1 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/external/flizzer_tracker/application.fam b/applications/external/flizzer_tracker/application.fam new file mode 100644 index 000000000..d5215df29 --- /dev/null +++ b/applications/external/flizzer_tracker/application.fam @@ -0,0 +1,16 @@ +App( + appid="flizzer_tracker", + name="Flizzer Tracker", + apptype=FlipperAppType.EXTERNAL, + entry_point="flizzer_tracker_app", + cdefines=["APP_FLIZZER_TRACKER"], + stack_size=2 * 1024, + order=90, + fap_version=(0, 2), + fap_description="An advanced Flipper Zero chiptune tracker with 4 channels", + fap_author="LTVA", + fap_weburl="https://github.com/LTVA1/flizzer_tracker", + fap_icon="flizzer_tracker.png", + fap_icon_assets="images", + fap_category="Music", +) diff --git a/applications/external/flizzer_tracker/audio_modes.c b/applications/external/flizzer_tracker/audio_modes.c new file mode 100644 index 000000000..07bb1df53 --- /dev/null +++ b/applications/external/flizzer_tracker/audio_modes.c @@ -0,0 +1,8 @@ +#include +#include + +char* audio_modes_text[2] = { + "Internal", + "External", +}; +bool audio_modes_values[2] = {false, true}; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/diskop.c b/applications/external/flizzer_tracker/diskop.c new file mode 100644 index 000000000..146282f82 --- /dev/null +++ b/applications/external/flizzer_tracker/diskop.c @@ -0,0 +1,259 @@ +#include "diskop.h" + +#define CFG_FILENAME "settings.cfg" + +void save_instrument_inner(Stream* stream, Instrument* inst) { + size_t rwops = stream_write(stream, (uint8_t*)inst->name, sizeof(inst->name)); + rwops = stream_write(stream, (uint8_t*)&inst->waveform, sizeof(inst->waveform)); + rwops = stream_write(stream, (uint8_t*)&inst->flags, sizeof(inst->flags)); + rwops = stream_write( + stream, (uint8_t*)&inst->sound_engine_flags, sizeof(inst->sound_engine_flags)); + + rwops = stream_write(stream, (uint8_t*)&inst->base_note, sizeof(inst->base_note)); + rwops = stream_write(stream, (uint8_t*)&inst->finetune, sizeof(inst->finetune)); + + rwops = stream_write(stream, (uint8_t*)&inst->slide_speed, sizeof(inst->slide_speed)); + + rwops = stream_write(stream, (uint8_t*)&inst->adsr, sizeof(inst->adsr)); + rwops = stream_write(stream, (uint8_t*)&inst->pw, sizeof(inst->pw)); + + if(inst->sound_engine_flags & SE_ENABLE_RING_MOD) { + rwops = stream_write(stream, (uint8_t*)&inst->ring_mod, sizeof(inst->ring_mod)); + } + + if(inst->sound_engine_flags & SE_ENABLE_HARD_SYNC) { + rwops = stream_write(stream, (uint8_t*)&inst->hard_sync, sizeof(inst->hard_sync)); + } + + uint8_t progsteps = 0; + + for(uint8_t i = 0; i < INST_PROG_LEN; i++) { + if((inst->program[i] & 0x7fff) != TE_PROGRAM_NOP) { + progsteps = i + 1; + } + } + + rwops = stream_write(stream, (uint8_t*)&progsteps, sizeof(progsteps)); + + if(progsteps > 0) { + rwops = + stream_write(stream, (uint8_t*)inst->program, progsteps * sizeof(inst->program[0])); + } + + rwops = stream_write(stream, (uint8_t*)&inst->program_period, sizeof(inst->program_period)); + + if(inst->flags & TE_ENABLE_VIBRATO) { + rwops = stream_write(stream, (uint8_t*)&inst->vibrato_speed, sizeof(inst->vibrato_speed)); + rwops = stream_write(stream, (uint8_t*)&inst->vibrato_depth, sizeof(inst->vibrato_depth)); + rwops = stream_write(stream, (uint8_t*)&inst->vibrato_delay, sizeof(inst->vibrato_delay)); + } + + if(inst->flags & TE_ENABLE_PWM) { + rwops = stream_write(stream, (uint8_t*)&inst->pwm_speed, sizeof(inst->pwm_speed)); + rwops = stream_write(stream, (uint8_t*)&inst->pwm_depth, sizeof(inst->pwm_depth)); + rwops = stream_write(stream, (uint8_t*)&inst->pwm_delay, sizeof(inst->pwm_delay)); + } + + if(inst->sound_engine_flags & SE_ENABLE_FILTER) { + rwops = stream_write(stream, (uint8_t*)&inst->filter_cutoff, sizeof(inst->filter_cutoff)); + rwops = stream_write( + stream, (uint8_t*)&inst->filter_resonance, sizeof(inst->filter_resonance)); + rwops = stream_write(stream, (uint8_t*)&inst->filter_type, sizeof(inst->filter_type)); + } + + UNUSED(rwops); +} + +bool save_instrument(FlizzerTrackerApp* tracker, FuriString* filepath) { + bool file_removed = + storage_simply_remove(tracker->storage, furi_string_get_cstr(filepath)); // just in case + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_WRITE, FSOM_OPEN_ALWAYS); + + uint8_t version = TRACKER_ENGINE_VERSION; + size_t rwops = + stream_write(tracker->stream, (uint8_t*)INST_FILE_SIG, sizeof(INST_FILE_SIG) - 1); + rwops = stream_write(tracker->stream, (uint8_t*)&version, sizeof(uint8_t)); + + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + + save_instrument_inner(tracker->stream, inst); + + file_stream_close(tracker->stream); + tracker->is_saving_instrument = false; + furi_string_free(filepath); + + UNUSED(file_removed); + UNUSED(open_file); + UNUSED(rwops); + return false; +} + +bool save_song(FlizzerTrackerApp* tracker, FuriString* filepath) { + bool file_removed = + storage_simply_remove(tracker->storage, furi_string_get_cstr(filepath)); // just in case + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_WRITE, FSOM_OPEN_ALWAYS); + + uint8_t version = TRACKER_ENGINE_VERSION; + size_t rwops = + stream_write(tracker->stream, (uint8_t*)SONG_FILE_SIG, sizeof(SONG_FILE_SIG) - 1); + rwops = stream_write(tracker->stream, (uint8_t*)&version, sizeof(uint8_t)); + + TrackerSong* song = &tracker->song; + + /*for(uint32_t i = 0; i < 23444; i++) + { + rwops = stream_write(tracker->stream, (uint8_t*)&song->loop_end, sizeof(uint8_t)); + }*/ + + rwops = stream_write(tracker->stream, (uint8_t*)song->song_name, sizeof(song->song_name)); + rwops = stream_write(tracker->stream, (uint8_t*)&song->loop_start, sizeof(song->loop_start)); + rwops = stream_write(tracker->stream, (uint8_t*)&song->loop_end, sizeof(song->loop_end)); + rwops = stream_write( + tracker->stream, (uint8_t*)&song->pattern_length, sizeof(song->pattern_length)); + + rwops = stream_write(tracker->stream, (uint8_t*)&song->speed, sizeof(song->speed)); + rwops = stream_write(tracker->stream, (uint8_t*)&song->rate, sizeof(song->rate)); + + rwops = stream_write( + tracker->stream, (uint8_t*)&song->num_sequence_steps, sizeof(song->num_sequence_steps)); + + for(uint16_t i = 0; i < song->num_sequence_steps; i++) { + rwops = stream_write( + tracker->stream, + (uint8_t*)&song->sequence.sequence_step[i], + sizeof(song->sequence.sequence_step[0])); + } + + rwops = + stream_write(tracker->stream, (uint8_t*)&song->num_patterns, sizeof(song->num_patterns)); + + for(uint16_t i = 0; i < song->num_patterns; i++) { + rwops = stream_write( + tracker->stream, + (uint8_t*)song->pattern[i].step, + sizeof(TrackerSongPatternStep) * (song->pattern_length)); + } + + rwops = stream_write( + tracker->stream, (uint8_t*)&song->num_instruments, sizeof(song->num_instruments)); + + for(uint16_t i = 0; i < song->num_instruments; i++) { + save_instrument_inner(tracker->stream, song->instrument[i]); + } + + file_stream_close(tracker->stream); + tracker->is_saving = false; + furi_string_free(filepath); + + UNUSED(file_removed); + UNUSED(open_file); + UNUSED(rwops); + return false; +} + +bool load_song_util(FlizzerTrackerApp* tracker, FuriString* filepath) { + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_READ, FSOM_OPEN_ALWAYS); + + bool result = load_song(&tracker->song, tracker->stream); + + tracker->is_loading = false; + file_stream_close(tracker->stream); + furi_string_free(filepath); + UNUSED(open_file); + return result; +} + +bool load_instrument_disk(TrackerSong* song, uint8_t inst, Stream* stream) { + set_default_instrument(song->instrument[inst]); + + char header[sizeof(INST_FILE_SIG) + 2] = {0}; + size_t rwops = stream_read(stream, (uint8_t*)&header, sizeof(INST_FILE_SIG) - 1); + header[sizeof(INST_FILE_SIG)] = '\0'; + + uint8_t version = 0; + + if(strcmp(header, INST_FILE_SIG) == 0) { + rwops = stream_read(stream, (uint8_t*)&version, sizeof(version)); + + if(version <= TRACKER_ENGINE_VERSION) { + load_instrument_inner(stream, song->instrument[inst], version); + } + } + + UNUSED(rwops); + return false; +} + +bool load_instrument_util(FlizzerTrackerApp* tracker, FuriString* filepath) { + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_READ, FSOM_OPEN_ALWAYS); + + bool result = + load_instrument_disk(&tracker->song, tracker->current_instrument, tracker->stream); + + tracker->is_loading_instrument = false; + file_stream_close(tracker->stream); + furi_string_free(filepath); + UNUSED(open_file); + return result; +} + +void save_config(FlizzerTrackerApp* tracker) { + // stream_read_line + FuriString* filepath = furi_string_alloc(); + FuriString* config_line = furi_string_alloc(); + furi_string_cat_printf(filepath, "%s/%s", FLIZZER_TRACKER_FOLDER, CFG_FILENAME); + + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_WRITE, FSOM_OPEN_ALWAYS); + UNUSED(open_file); + + furi_string_cat_printf( + config_line, "%s = %s\n", "external_audio", tracker->external_audio ? "true" : "false"); + stream_write_string(tracker->stream, config_line); + + file_stream_close(tracker->stream); + furi_string_free(filepath); + furi_string_free(config_line); +} + +void load_config(FlizzerTrackerApp* tracker) { + FuriString* filepath = furi_string_alloc(); + FuriString* config_line = furi_string_alloc(); + furi_string_cat_printf(filepath, "%s/%s", FLIZZER_TRACKER_FOLDER, CFG_FILENAME); + + bool open_file = file_stream_open( + tracker->stream, furi_string_get_cstr(filepath), FSAM_READ, FSOM_OPEN_ALWAYS); + UNUSED(open_file); + + stream_read_line(tracker->stream, config_line); + + sscanf( + furi_string_get_cstr(config_line), "%s%s%s", tracker->param, tracker->eq, tracker->value); + + if(strcmp(tracker->param, "external_audio") == 0) { + if(strcmp(tracker->value, "false") == 0) { + tracker->external_audio = false; + // strcpy(tracker->value, "false_"); + } + + if(strcmp(tracker->value, "true") == 0) { + tracker->external_audio = true; + // strcpy(tracker->value, "true_"); + } + + sound_engine_init( + &tracker->sound_engine, + tracker->sound_engine.sample_rate, + tracker->external_audio, + tracker->sound_engine.audio_buffer_size); + // sound_engine_set_audio_output(tracker->external_audio); + } + + file_stream_close(tracker->stream); + furi_string_free(filepath); + furi_string_free(config_line); +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/diskop.h b/applications/external/flizzer_tracker/diskop.h new file mode 100644 index 000000000..ed8b52a11 --- /dev/null +++ b/applications/external/flizzer_tracker/diskop.h @@ -0,0 +1,13 @@ +#pragma once + +#include "flizzer_tracker.h" +#include "tracker_engine/diskop.h" + +bool save_song(FlizzerTrackerApp* tracker, FuriString* filepath); +bool save_instrument(FlizzerTrackerApp* tracker, FuriString* filepath); + +bool load_song_util(FlizzerTrackerApp* tracker, FuriString* filepath); +bool load_instrument_util(FlizzerTrackerApp* tracker, FuriString* filepath); + +void save_config(FlizzerTrackerApp* tracker); +void load_config(FlizzerTrackerApp* tracker); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/flizzer_tracker.c b/applications/external/flizzer_tracker/flizzer_tracker.c new file mode 100644 index 000000000..2c0729a43 --- /dev/null +++ b/applications/external/flizzer_tracker/flizzer_tracker.c @@ -0,0 +1,218 @@ +#include "flizzer_tracker.h" +#include "diskop.h" +#include "init_deinit.h" +#include "input_event.h" +#include "util.h" +#include "view/instrument_editor.h" +#include "view/pattern_editor.h" + +#include "font.h" +#include + +void draw_callback(Canvas* canvas, void* ctx) { + TrackerViewModel* model = (TrackerViewModel*)ctx; + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)(model->tracker); + + canvas_set_color(canvas, ColorXOR); + + if(tracker->is_loading || tracker->is_loading_instrument) { + canvas_draw_str(canvas, 10, 10, "Loading..."); + return; + } + + if(tracker->is_saving || tracker->is_saving_instrument) { + canvas_draw_str(canvas, 10, 10, "Saving..."); + return; + } + + if(tracker->showing_help) { + canvas_draw_icon(canvas, 0, 0, &I_help); + return; + } + + canvas_set_custom_u8g2_font(canvas, u8g2_font_tom_thumb_4x6_tr); + + switch(tracker->mode) { + case PATTERN_VIEW: { + if(tracker->tracker_engine.song == NULL) { + stop(); + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + } + + if(tracker->focus != EDIT_PATTERN) { + draw_songinfo_view(canvas, tracker); + } + + if(tracker->focus != EDIT_PATTERN) { + draw_sequence_view(canvas, tracker); + } + + draw_pattern_view(canvas, tracker); + break; + } + + case INST_EDITOR_VIEW: { + draw_instrument_view(canvas, tracker); + draw_instrument_program_view(canvas, tracker); + break; + } + + default: + break; + } +} + +bool input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + TrackerView* tracker_view = (TrackerView*)ctx; + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)(tracker_view->context); + + bool consumed = false; + + if(input_event->key == InputKeyBack && input_event->type == InputTypeShort) { + tracker->period = furi_get_tick() - tracker->current_time; + tracker->current_time = furi_get_tick(); + + tracker->was_it_back_keypress = true; + } + + else if(input_event->type == InputTypeShort || input_event->type == InputTypeLong) { + tracker->was_it_back_keypress = false; + tracker->period = 0; + } + + uint32_t final_period = (tracker->was_it_back_keypress ? tracker->period : 0); + + FlizzerTrackerEvent event = { + .type = EventTypeInput, .input = *input_event, .period = final_period}; + + if(!(tracker->is_loading) && !(tracker->is_saving)) { + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } + + consumed = true; + return consumed; +} + +int32_t flizzer_tracker_app(void* p) { + UNUSED(p); + + Storage* storage = furi_record_open(RECORD_STORAGE); + bool st = storage_simply_mkdir(storage, APPSDATA_FOLDER); + st = storage_simply_mkdir(storage, FLIZZER_TRACKER_FOLDER); + st = storage_simply_mkdir(storage, FLIZZER_TRACKER_INSTRUMENTS_FOLDER); + UNUSED(st); + furi_record_close(RECORD_STORAGE); + + FlizzerTrackerApp* tracker = init_tracker(44100, 50, true, 1024); + + // Текущее событие типа кастомного типа FlizzerTrackerEvent + FlizzerTrackerEvent event; + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + + // Бесконечный цикл обработки очереди событий + while(!(tracker->quit)) { + // Выбираем событие из очереди в переменную event (ждём бесконечно долго, если очередь пуста) + // и проверяем, что у нас получилось это сделать + furi_check( + furi_message_queue_get(tracker->event_queue, &event, FuriWaitForever) == FuriStatusOk); + + // Наше событие — это нажатие кнопки + if(event.type == EventTypeInput) { + process_input_event(tracker, &event); + } + + if(event.type == EventTypeSaveSong) { + save_song(tracker, tracker->filepath); + } + + if(event.type == EventTypeSaveInstrument) { + save_instrument(tracker, tracker->filepath); + } + + if(event.type == EventTypeLoadSong) { + stop_song(tracker); + + tracker->tracker_engine.sequence_position = tracker->tracker_engine.pattern_position = + tracker->current_instrument = 0; + + tracker->dialogs = furi_record_open(RECORD_DIALOGS); + tracker->is_loading = true; + + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, FLIZZER_TRACKER_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, SONG_FILE_EXT, &I_flizzer_tracker_module); + browser_options.base_path = FLIZZER_TRACKER_FOLDER; + browser_options.hide_ext = false; + + bool ret = dialog_file_browser_show(tracker->dialogs, path, path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + + const char* cpath = furi_string_get_cstr(path); + + if(ret && strcmp(&cpath[strlen(cpath) - 4], SONG_FILE_EXT) == 0) { + bool result = load_song_util(tracker, path); + UNUSED(result); + } + + else { + furi_string_free(path); + tracker->is_loading = false; + } + } + + if(event.type == EventTypeLoadInstrument) { + stop_song(tracker); + + tracker->dialogs = furi_record_open(RECORD_DIALOGS); + tracker->is_loading_instrument = true; + + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, FLIZZER_TRACKER_INSTRUMENTS_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, INST_FILE_EXT, &I_flizzer_tracker_instrument); + browser_options.base_path = FLIZZER_TRACKER_FOLDER; + browser_options.hide_ext = false; + + bool ret = dialog_file_browser_show(tracker->dialogs, path, path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + + const char* cpath = furi_string_get_cstr(path); + + if(ret && strcmp(&cpath[strlen(cpath) - 4], INST_FILE_EXT) == 0) { + bool result = load_instrument_util(tracker, path); + UNUSED(result); + } + + else { + furi_string_free(path); + tracker->is_loading = false; + } + } + + if(event.type == EventTypeSetAudioMode) { + sound_engine_PWM_timer_init(tracker->external_audio); + + tracker->sound_engine.external_audio_output = tracker->external_audio; + } + } + + stop(); + + save_config(tracker); + + deinit_tracker(tracker); + + return 0; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/flizzer_tracker.h b/applications/external/flizzer_tracker/flizzer_tracker.h new file mode 100644 index 000000000..97269a98e --- /dev/null +++ b/applications/external/flizzer_tracker/flizzer_tracker.h @@ -0,0 +1,226 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "flizzer_tracker_hal.h" +#include "sound_engine/freqs.h" +#include "sound_engine/sound_engine_defs.h" +#include "sound_engine/sound_engine_filter.h" +#include "tracker_engine/tracker_engine_defs.h" + +#define APPSDATA_FOLDER "/ext/apps_data" +#define FLIZZER_TRACKER_FOLDER "/ext/apps_data/flizzer_tracker" +#define FLIZZER_TRACKER_INSTRUMENTS_FOLDER "/ext/apps_data/flizzer_tracker/instruments" +#define FILE_NAME_LEN 64 + +typedef enum { + EventTypeInput, + EventTypeSaveSong, + EventTypeLoadSong, + EventTypeLoadInstrument, + EventTypeSaveInstrument, + EventTypeSetAudioMode, +} EventType; + +typedef struct { + EventType type; + InputEvent input; + uint32_t period; +} FlizzerTrackerEvent; + +typedef enum { + PATTERN_VIEW, + INST_EDITOR_VIEW, + EXPORT_WAV_VIEW, +} TrackerMode; + +typedef enum { + EDIT_PATTERN, + EDIT_SEQUENCE, + EDIT_SONGINFO, + EDIT_INSTRUMENT, + EDIT_PROGRAM, +} TrackerFocus; + +typedef enum { + SI_PATTERNPOS, + SI_SEQUENCEPOS, + SI_SONGSPEED, + SI_SONGRATE, + SI_MASTERVOL, + + SI_SONGNAME, + SI_CURRENTINSTRUMENT, + SI_INSTRUMENTNAME, + /* ======== */ + SI_PARAMS, +} SongInfoParam; + +typedef enum { + INST_CURRENTINSTRUMENT, + INST_INSTRUMENTNAME, + + INST_CURRENT_NOTE, + INST_FINETUNE, + + INST_SLIDESPEED, + INST_SETPW, + INST_PW, + INST_SETCUTOFF, + + INST_WAVE_NOISE, + INST_WAVE_PULSE, + INST_WAVE_TRIANGLE, + INST_WAVE_SAWTOOTH, + INST_WAVE_NOISE_METAL, + INST_WAVE_SINE, + + INST_ATTACK, + INST_DECAY, + INST_SUSTAIN, + INST_RELEASE, + INST_VOLUME, + + INST_ENABLEFILTER, + INST_FILTERCUTOFF, + INST_FILTERRESONANCE, + + INST_FILTERTYPE, + INST_ENABLERINGMOD, + INST_RINGMODSRC, + INST_ENABLEHARDSYNC, + INST_HARDSYNCSRC, + + INST_RETRIGGERONSLIDE, + INST_ENABLEKEYSYNC, + + INST_ENABLEVIBRATO, + INST_VIBRATOSPEED, + INST_VIBRATODEPTH, + INST_VIBRATODELAY, + + INST_ENABLEPWM, + INST_PWMSPEED, + INST_PWMDEPTH, + INST_PWMDELAY, + + INST_PROGRESTART, + INST_PROGRAMEPERIOD, + /* ========= */ + INST_PARAMS, +} InstrumentParam; + +typedef struct { + View* view; + void* context; +} TrackerView; + +typedef enum { + VIEW_TRACKER, + VIEW_KEYBOARD, + VIEW_SUBMENU_PATTERN, + VIEW_SUBMENU_PATTERN_COPYPASTE, + VIEW_SUBMENU_INSTRUMENT, + VIEW_FILE_OVERWRITE, + VIEW_INSTRUMENT_FILE_OVERWRITE, + VIEW_SETTINGS, +} FlizzerTrackerViews; + +typedef enum { + SUBMENU_PATTERN_LOAD_SONG, + SUBMENU_PATTERN_SAVE_SONG, + SUBMENU_PATTERN_SETTINGS, + SUBMENU_PATTERN_HELP, + SUBMENU_PATTERN_EXIT, +} PatternSubmenuParams; + +typedef enum { + SUBMENU_PATTERN_COPYPASTE_COPY, + SUBMENU_PATTERN_COPYPASTE_PASTE, + SUBMENU_PATTERN_COPYPASTE_CUT, + SUBMENU_PATTERN_COPYPASTE_CLEAR, +} PatternCopypasteSubmenuParams; + +typedef enum { + SUBMENU_INSTRUMENT_LOAD, + SUBMENU_INSTRUMENT_SAVE, + SUBMENU_INSTRUMENT_EXIT, +} InstrumentSubmenuParams; + +typedef struct { + NotificationApp* notification; + FuriMessageQueue* event_queue; + Gui* gui; + TrackerView* tracker_view; + ViewDispatcher* view_dispatcher; + TextInput* text_input; + Storage* storage; + Stream* stream; + FuriString* filepath; + DialogsApp* dialogs; + Submenu* pattern_submenu; + Submenu* pattern_copypaste_submenu; + Submenu* instrument_submenu; + VariableItemList* settings_list; + Widget* overwrite_file_widget; + Widget* overwrite_instrument_file_widget; + char filename[FILE_NAME_LEN + 1]; + bool was_it_back_keypress; + uint32_t current_time; + uint32_t period; + + bool external_audio; + + SoundEngine sound_engine; + TrackerEngine tracker_engine; + + TrackerSong song; + + uint8_t selected_param; + + uint8_t mode, focus; + uint8_t patternx, current_channel, current_digit, program_position, current_program_step, + current_instrument, current_note, current_volume; + + uint8_t inst_editor_shift; + + int16_t source_pattern_index; + + bool editing; + bool was_editing; + + bool is_loading; + bool is_saving; + bool is_loading_instrument; + bool is_saving_instrument; + bool showing_help; + + bool cut_pattern; //if we need to clear the pattern we pasted from + + bool quit; + + char eq[2]; + char param[80]; + char value[10]; +} FlizzerTrackerApp; + +typedef struct { + FlizzerTrackerApp* tracker; +} TrackerViewModel; + +void draw_callback(Canvas* canvas, void* ctx); +bool input_callback(InputEvent* input_event, void* ctx); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/flizzer_tracker.png b/applications/external/flizzer_tracker/flizzer_tracker.png new file mode 100644 index 0000000000000000000000000000000000000000..f691ab6309aa39d293080d0b0b8dd0b72b1f9e17 GIT binary patch literal 1147 zcmaJ=O=uHA6rR?KH6>nzBBh6AJy_K2U$mNGH*J%xP0)=YO)O{VFRWOvz}khMo| zqR_i|(W_8U@gO}YR`4Kt@}eTB2SE>d^`O)@n~)#dInY&X>N!G~^c+QfTwd8 zrpcO>S@+1|dahVruay%OekK`>r~w9r2B9VwH0rjG1C0-bF|66K$a5it)-=9kJX~mi|xd4-XQ1yd~SB zNu_hsKgWZd%brER25w^m z%Bo^gQ#UC|DiYL$EX9;FMMc4~jN{X%q&Q}F?!peJq&))sJv5{QRucUheaPJkj#qS? zdNMSiVN%EA@}9%Rp}@g4mO;qwpxSg*WX0K6tnEe!PoWc7SBc%WV!Li@5*zx_gT{NS z9Qp)JJDb(#ic^b^Z+=Qyot;N_zdQ^+W;6HaXZ4iv8gIw9Qgx5skdtLu literal 0 HcmV?d00001 diff --git a/applications/external/flizzer_tracker/flizzer_tracker_hal.c b/applications/external/flizzer_tracker/flizzer_tracker_hal.c new file mode 100644 index 000000000..a2f483f08 --- /dev/null +++ b/applications/external/flizzer_tracker/flizzer_tracker_hal.c @@ -0,0 +1,313 @@ +#include "flizzer_tracker_hal.h" +#include "flizzer_tracker.h" + +void sound_engine_dma_isr(void* ctx) { + SoundEngine* sound_engine = (SoundEngine*)ctx; + + // sound_engine->counter++; + + // half of transfer + if(LL_DMA_IsActiveFlag_HT1(DMA1)) { + LL_DMA_ClearFlag_HT1(DMA1); + // fill first half of buffer + uint16_t* audio_buffer = sound_engine->audio_buffer; + uint32_t audio_buffer_length = sound_engine->audio_buffer_size / 2; + sound_engine_fill_buffer(sound_engine, audio_buffer, audio_buffer_length); + } + + // transfer complete + if(LL_DMA_IsActiveFlag_TC1(DMA1)) { + LL_DMA_ClearFlag_TC1(DMA1); + // fill second half of buffer + uint32_t audio_buffer_length = sound_engine->audio_buffer_size / 2; + uint16_t* audio_buffer = &sound_engine->audio_buffer[audio_buffer_length]; + sound_engine_fill_buffer(sound_engine, audio_buffer, audio_buffer_length); + } +} + +void tracker_engine_timer_isr( + void* ctx) // the tracker engine interrupt is of higher priority than sound engine one so it can run at the middle of filling the buffer, thus allowing sample-accurate tight effect timing +{ + TrackerEngine* tracker_engine = (TrackerEngine*)ctx; + // tracker_engine->counter++; + + if(LL_TIM_IsActiveFlag_UPDATE(TRACKER_ENGINE_TIMER)) { + LL_TIM_ClearFlag_UPDATE(TRACKER_ENGINE_TIMER); + tracker_engine_advance_tick(tracker_engine); + } +} + +void sound_engine_PWM_timer_init(bool external_audio_output) // external audio on pin PA6 +{ + if(external_audio_output) { + /*if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_release(); + }*/ + + //LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER); + //LL_TIM_DisableCounter(SPEAKER_PWM_TIMER); + + if(!(furi_hal_speaker_is_mine())) { + if(furi_hal_speaker_acquire(1000)) { + LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_DisableCounter(SPEAKER_PWM_TIMER); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; + TIM_InitStruct.Autoreload = + 1023; // 10-bit PWM resolution at around 60 kHz PWM rate + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(SPEAKER_PWM_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 0; + LL_TIM_OC_Init(SPEAKER_PWM_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + SPEAKER_PWM_TIMER->CNT = 0; + + LL_TIM_EnableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_EnableCounter(SPEAKER_PWM_TIMER); + } + } + + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; + TIM_InitStruct.Autoreload = 1023; // 10-bit PWM resolution at around 60 kHz PWM rate + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(SPEAKER_PWM_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 0; + LL_TIM_OC_Init(SPEAKER_PWM_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + SPEAKER_PWM_TIMER->CNT = 0; + + LL_TIM_EnableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_EnableCounter(SPEAKER_PWM_TIMER); + + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + //furi_hal_gpio_init_ex(&gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + } + + else { + if(!(furi_hal_speaker_is_mine())) { + if(furi_hal_speaker_acquire(1000)) { + LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_DisableCounter(SPEAKER_PWM_TIMER); + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; + TIM_InitStruct.Autoreload = + 1023; // 10-bit PWM resolution at around 60 kHz PWM rate + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(SPEAKER_PWM_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 0; + LL_TIM_OC_Init(SPEAKER_PWM_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + SPEAKER_PWM_TIMER->CNT = 0; + + LL_TIM_EnableAllOutputs(SPEAKER_PWM_TIMER); + LL_TIM_EnableCounter(SPEAKER_PWM_TIMER); + } + } + + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + //furi_hal_gpio_init_ex(&gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + } + + furi_hal_gpio_init_ex( + &gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); +} + +void sound_engine_set_audio_output(bool external_audio_output) { + if(external_audio_output) { + furi_hal_gpio_init_ex( + &gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16); + + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_release(); + } + } + + else { + if(!(furi_hal_speaker_is_mine())) { + bool unu = furi_hal_speaker_acquire(1000); + UNUSED(unu); + } + + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } +} + +void sound_engine_timer_init(uint32_t sample_rate) // external audio on pin PA6 +{ + if(!furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + furi_hal_bus_enable(FuriHalBusTIM1); + } + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; + TIM_InitStruct.Autoreload = + TIMER_BASE_CLOCK / sample_rate - 1; // to support various sample rates + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(SAMPLE_RATE_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + LL_TIM_OC_Init(SAMPLE_RATE_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableAllOutputs(SAMPLE_RATE_TIMER); + + SAMPLE_RATE_TIMER->CNT = 0; +} + +void tracker_engine_timer_init(uint8_t rate) // 0-255 hz +{ + if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_enable(FuriHalBusTIM2); + } + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; // using 32-bit timer + TIM_InitStruct.Autoreload = + (uint32_t)TIMER_BASE_CLOCK / (uint32_t)rate - 1; // to support various tracker engine rates + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(TRACKER_ENGINE_TIMER, &TIM_InitStruct); + + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + LL_TIM_OC_Init(TRACKER_ENGINE_TIMER, SPEAKER_PWM_TIMER_CHANNEL, &TIM_OC_InitStruct); + + LL_TIM_EnableIT_UPDATE(TRACKER_ENGINE_TIMER); + + TRACKER_ENGINE_TIMER->CNT = 0; +} + +void tracker_engine_set_rate(uint8_t rate) { + if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_enable(FuriHalBusTIM2); + } + + LL_TIM_InitTypeDef TIM_InitStruct = {0}; + + TIM_InitStruct.Prescaler = 0; // using 32-bit timer + TIM_InitStruct.Autoreload = + (uint32_t)TIMER_BASE_CLOCK / (uint32_t)rate - 1; // to support various tracker engine rates + TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; + LL_TIM_Init(TRACKER_ENGINE_TIMER, &TIM_InitStruct); + + TRACKER_ENGINE_TIMER->CNT = 0; +} + +void tracker_engine_init_hardware(uint8_t rate) { + tracker_engine_timer_init(rate); +} + +void sound_engine_dma_init(uint32_t address, uint32_t size) { + uint32_t dma_dst = (uint32_t) & (SPEAKER_PWM_TIMER->CCR1); + + LL_DMA_ConfigAddresses(DMA_INSTANCE, address, dma_dst, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); + LL_DMA_SetDataLength(DMA_INSTANCE, size); + + LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM1_UP); + LL_DMA_SetDataTransferDirection(DMA_INSTANCE, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); + LL_DMA_SetChannelPriorityLevel(DMA_INSTANCE, LL_DMA_PRIORITY_VERYHIGH); + LL_DMA_SetMode(DMA_INSTANCE, LL_DMA_MODE_CIRCULAR); + LL_DMA_SetPeriphIncMode(DMA_INSTANCE, LL_DMA_PERIPH_NOINCREMENT); + LL_DMA_SetMemoryIncMode(DMA_INSTANCE, LL_DMA_MEMORY_INCREMENT); + LL_DMA_SetPeriphSize(DMA_INSTANCE, LL_DMA_PDATAALIGN_HALFWORD); + LL_DMA_SetMemorySize(DMA_INSTANCE, LL_DMA_MDATAALIGN_HALFWORD); + + LL_DMA_EnableIT_TC(DMA_INSTANCE); + LL_DMA_EnableIT_HT(DMA_INSTANCE); +} + +void sound_engine_init_hardware( + uint32_t sample_rate, + bool external_audio_output, + uint16_t* audio_buffer, + uint32_t audio_buffer_size) { + sound_engine_dma_init((uint32_t)audio_buffer, audio_buffer_size); + sound_engine_timer_init(sample_rate); + sound_engine_PWM_timer_init(external_audio_output); +} + +void sound_engine_dma_start() { + LL_DMA_EnableChannel(DMA_INSTANCE); + LL_TIM_EnableDMAReq_UPDATE(SAMPLE_RATE_TIMER); +} + +void sound_engine_dma_stop() { + LL_DMA_DisableChannel(DMA_INSTANCE); +} + +void sound_engine_start() { + SAMPLE_RATE_TIMER->CNT = 0; + LL_TIM_EnableCounter(SAMPLE_RATE_TIMER); + + sound_engine_dma_start(); +} + +void sound_engine_stop() { + LL_TIM_DisableAllOutputs(SAMPLE_RATE_TIMER); + LL_TIM_DisableCounter(SAMPLE_RATE_TIMER); + + sound_engine_dma_stop(); +} + +void sound_engine_deinit_timer() { + LL_TIM_DisableAllOutputs(SAMPLE_RATE_TIMER); + LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER); + + LL_TIM_DisableCounter(SPEAKER_PWM_TIMER); + + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_release(); + } + + if(furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_disable(FuriHalBusTIM2); + } + if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + furi_hal_bus_disable(FuriHalBusTIM1); + } +} + +void tracker_engine_start() { + TRACKER_ENGINE_TIMER->CNT = 0; + + LL_TIM_EnableAllOutputs(TRACKER_ENGINE_TIMER); + LL_TIM_EnableCounter(TRACKER_ENGINE_TIMER); +} + +void tracker_engine_stop() { + LL_TIM_DisableAllOutputs(TRACKER_ENGINE_TIMER); + LL_TIM_DisableCounter(TRACKER_ENGINE_TIMER); +} + +void play() { + tracker_engine_start(); + sound_engine_start(); +} + +void stop() { + sound_engine_stop(); + tracker_engine_stop(); +} diff --git a/applications/external/flizzer_tracker/flizzer_tracker_hal.h b/applications/external/flizzer_tracker/flizzer_tracker_hal.h new file mode 100644 index 000000000..e99e10c3c --- /dev/null +++ b/applications/external/flizzer_tracker/flizzer_tracker_hal.h @@ -0,0 +1,43 @@ +#pragma once + +#include "sound_engine/sound_engine.h" +#include "tracker_engine/tracker_engine.h" + +#include +#include +#include + +#include +#include +#include + +#define SPEAKER_PWM_TIMER TIM16 +#define SAMPLE_RATE_TIMER TIM1 +#define TRACKER_ENGINE_TIMER TIM2 + +#define SPEAKER_PWM_TIMER_CHANNEL LL_TIM_CHANNEL_CH1 + +#define TIMER_BASE_CLOCK 64000000 /* CPU frequency, 64 MHz */ + +#define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1 + +void sound_engine_dma_isr(void* ctx); +void tracker_engine_timer_isr(void* ctx); +void sound_engine_init_hardware( + uint32_t sample_rate, + bool external_audio_output, + uint16_t* audio_buffer, + uint32_t audio_buffer_size); +void sound_engine_dma_init(uint32_t address, uint32_t size); +void sound_engine_PWM_timer_init(bool external_audio_output); +void sound_engine_set_audio_output(bool external_audio_output); +void tracker_engine_init_hardware(uint8_t rate); +void tracker_engine_timer_init(uint8_t rate); +void tracker_engine_set_rate(uint8_t rate); +void sound_engine_start(); +void sound_engine_stop(); +void stop(); +void play(); +void tracker_engine_stop(); +void sound_engine_deinit_timer(); +void tracker_engine_start(); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/font.h b/applications/external/flizzer_tracker/font.h new file mode 100644 index 000000000..bf2625d6b --- /dev/null +++ b/applications/external/flizzer_tracker/font.h @@ -0,0 +1,31 @@ +#include + +/* +Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 +Copyright: +Glyphs: 95/203 +BBX Build Mode: 0 +*/ +// this is a modified version with dot and semicolon moved 1 pixel to the left; lowercase symbols removed to save space +// changed "G", "N" and "V" glyphs +const uint8_t u8g2_font_tom_thumb_4x6_tr[610] = + "a\0\2\2\2\3\2\3\4\3\5\0\0\5\0\5\0\1`\0\0\2E\0\4@\62\1\4@\62\2" + "\4@\62\3\4@\62\4\4@\62\5\4@\62\6\4@\62\7\4@\62\10\4@\62\11\4@\62\12" + "\4@\62\13\4@\62\14\4@\62\15\4@\62\16\4@\62\17\4@\62\20\4@\62\21\4@\62\22" + "\4@\62\23\4@\62\24\4@\62\25\4@\62\26\4@\62\27\4@\62\30\4@\62\31\4@\62\32" + "\4@\62\33\4@\62\34\4@\62\35\4@\62\36\4@\62\37\4@\62 \4@\62!\5u\62+" + "\42\6\313\63I\5#\10W\62i\250\241\2$\10Wr#\216\230\0%\10W\62\31\265Q\0&\10" + "W\62J\215\224\4'\5\351\63\2(\6vr\252\14)\7V\62\61%\5*\6O\63\251\3+\7" + "\317ri%\0,\5Jr\12-\5G\63\3.\5E\62\1/\7W\262U\31\1\60\7Wr\313" + "Z\0\61\6Vr\253\1\62\7W\62\32\244r\63\11W\62\32\244\14\26\0\64\7W\62I\215X\65" + "\10W\62#j\260\0\66\7Wrs\244\21\67\7W\62\63\225\21\70\10W\62#\15\65\2\71\10W" + "\62#\215\270\0:\5\315\62);\7Rr\31(\0<\10W\262\251\6\31\4=\6\317\62\33\14>" + "\11W\62\31d\220J\0\77\10W\62\63e\230\0@\7Wr\325\320@A\7Wr\325P*B\10" + "W\62*\255\264\0C\7Wr\263\6\2D\7W\62*Y\13E\7W\62#\216\70F\10W\62#" + "\216\30\1G\7Wr\63\251$H\10W\62I\15\245\2I\7W\62+V\3J\7W\262\245\252\0" + "K\10W\62I\255\244\2L\6W\62\261\71M\10W\62i\14\245\2N\7W\62*\271\2O\7W" + "r\225U\1P\10W\62*\255\30\1Q\7Wr\225\32IR\7W\62*\215US\10Wr\33d" + "\260\0T\7W\62+\266\0U\7W\62\311\225\4V\7W\62\311U\1W\10W\62I\215\241\2X" + "\10W\62I\265T\0Y\10W\62I\225\25\0Z\7W\62\63\225\3[\7W\62#\226\3\134\7\317" + "\62\31d\20]\7W\62\263\34\1^\5\313s\15_\5G\62\3`\5\312\63\61\0\0\0\4\377\377" + "\0"; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/images/channel_off.png b/applications/external/flizzer_tracker/images/channel_off.png new file mode 100644 index 0000000000000000000000000000000000000000..e4b283ae9fcd61442977d074ae8191e29bd0b800 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I$P6SaIJVmXDaPU;cPEB*=VV@jWYQgd9T^xl z_H+M9WCils0(?ST|Ns9FWQHEPTnD5$3p^r=85sBugD~Uq{1qucK><$}#}J9j$pHx- bKo|fthl7zZy8OB&P>#XV)z4*}Q$iB}Z`&c= literal 0 HcmV?d00001 diff --git a/applications/external/flizzer_tracker/images/channel_on.png b/applications/external/flizzer_tracker/images/channel_on.png new file mode 100644 index 0000000000000000000000000000000000000000..f1473e797220bb879c7b9721827492c13f52fc60 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I$P6SaIJVmXDaPU;cPEB*=VV@jWYQgd9T^xl z_H+M9WCils0(?ST|Ns9FWQHEPTnD5$3p^r=85sBugD~Uq{1qucK><$}#}J9j$rlnl c5VM%xNb!1@J z*w6hZ5u}nWz$e5NNdN!;f9cKncY!R<0*}aI1_r*vAk26?e?VM%xNb!1@J z*w6hZ5u}nWz$e5NNdN!;f9cKncY!R<0*}aI1_r*vAk26?e?)5R$91mu_zav9N9AS1D9MIl&Z}^r;~8!WN|tmZW~aFZL6n?Y1Q) znM_KuDyeE55OMFeMGZf0d1pr=V;PNjP1kg&X00R})O4{zKe*YhdgJ5+4 literal 0 HcmV?d00001 diff --git a/applications/external/flizzer_tracker/images/flizzer_tracker_module.png b/applications/external/flizzer_tracker/images/flizzer_tracker_module.png new file mode 100644 index 0000000000000000000000000000000000000000..57899a4ec911103dde28594cf997ed5ff80bb729 GIT binary patch literal 1158 zcmaJ=O=uHA6rR?qH6dW}A|kqMFM?+OqSXvZYMQN0(A1DdOgyy2?dS> z5JI6$TFt}E)`8w`_;=@uUt#KU^CLrO?K^b3_+_{<3#Wb8_)>F20dFVPZP?Gld5jU`EM^?@tj_X^Hn7B&9YM#d^Ii)FXoBR7E)&jf#>YDoPj-VRzP~ znin?R{)R|XMkQ|9(QT?*CdXv8achRgdC0&|jp`Fy;-=d)0IU#MM3e=ICF?_pr|$w~ zkq#(4zm;)^sHfQtp#M$|MvUEqOL^j2wHcOW;;bUU5M}m{`Dt{7nL%5`;{S>5;ZEWf z?~19@gwnj}pW{x>Wk)AqvY(~gB($Hk(995=(>+ovsk%W-m%U4d*X$VBfmYm&))r{d zz-^p`vZ`oQQ#DEwiUc(wOJU`7K~bPx#1ZP1_K>z@;j|==^1poj522e~?MgRZ*00010!qa{L000SaNLh0L01m?d01m?e z$8V@)0004kNklVF>3-b6bJCT<*uXMlA#8~Lg`m0SF$pe4s99x`SQ`Sn&%fg0DEttvIL(6fj6Jl2N9$OrK?G)RQMi6 z9?+l06w?_UN053x-F6@@nD8C@EC_0odcC-VMw9{!M?*A(_1Y2K7Jfiq`<;~>?{$0( zyAGh4%MciLe8(2z6a)J1reR_=Kw?tLLXZF=G`eYo)c_ouNNgkk3e#!F#cDvCuF+d_ zq6X+K#R77i01LanMprEp+i(`mHc=DhB|LvLU8sb?4#8TLlmCGhC jc;qsZ@o6F}4-Z4ySE={Ae#WT+l{0v{`njxgN@xNAv416| literal 0 HcmV?d00001 diff --git a/applications/external/flizzer_tracker/init_deinit.c b/applications/external/flizzer_tracker/init_deinit.c new file mode 100644 index 000000000..33e42fe36 --- /dev/null +++ b/applications/external/flizzer_tracker/init_deinit.c @@ -0,0 +1,298 @@ +#include "init_deinit.h" +#include "input_event.h" + +#include "diskop.h" + +#define AUDIO_MODES_COUNT 2 + +TrackerView* tracker_view_alloc(FlizzerTrackerApp* tracker) { + TrackerView* tracker_view = malloc(sizeof(TrackerView)); + tracker_view->view = view_alloc(); + tracker_view->context = tracker; + view_set_context(tracker_view->view, tracker_view); + view_allocate_model(tracker_view->view, ViewModelTypeLocking, sizeof(TrackerViewModel)); + view_set_draw_callback(tracker_view->view, draw_callback); + view_set_input_callback(tracker_view->view, input_callback); + + return tracker_view; +} + +void tracker_view_free(TrackerView* tracker_view) { + furi_assert(tracker_view); + view_free(tracker_view->view); + free(tracker_view); +} + +uint8_t my_value_index_bool( + const bool value, + const bool values[], + uint8_t + values_count) // why the fuck it gives unresolved symbol if I include it from toolbox???!!! +{ + uint8_t index = 0; + + for(uint8_t i = 0; i < values_count; i++) { + if(value == values[i]) { + index = i; + break; + } + } + + return index; +} + +FlizzerTrackerApp* init_tracker( + uint32_t sample_rate, + uint8_t rate, + bool external_audio_output, + uint32_t audio_buffer_size) { + FlizzerTrackerApp* tracker = malloc(sizeof(FlizzerTrackerApp)); + memset(tracker, 0, sizeof(FlizzerTrackerApp)); + + tracker->external_audio = external_audio_output; + + sound_engine_init( + &tracker->sound_engine, sample_rate, external_audio_output, audio_buffer_size); + tracker_engine_init(&tracker->tracker_engine, rate, &tracker->sound_engine); + + tracker->tracker_engine.song = &tracker->song; + + tracker->current_note = MIDDLE_C; + + // Очередь событий на 8 элементов размера FlizzerTrackerEvent + tracker->event_queue = furi_message_queue_alloc(8, sizeof(FlizzerTrackerEvent)); + + tracker->gui = furi_record_open(RECORD_GUI); + tracker->view_dispatcher = view_dispatcher_alloc(); + + tracker->tracker_view = tracker_view_alloc(tracker); + + view_dispatcher_add_view(tracker->view_dispatcher, VIEW_TRACKER, tracker->tracker_view->view); + view_dispatcher_attach_to_gui( + tracker->view_dispatcher, tracker->gui, ViewDispatcherTypeFullscreen); + + with_view_model( + tracker->tracker_view->view, TrackerViewModel * model, { model->tracker = tracker; }, true); + + tracker->storage = furi_record_open(RECORD_STORAGE); + tracker->stream = file_stream_alloc(tracker->storage); + + tracker->text_input = text_input_alloc(); + view_dispatcher_add_view( + tracker->view_dispatcher, VIEW_KEYBOARD, text_input_get_view(tracker->text_input)); + + tracker->pattern_submenu = submenu_alloc(); + tracker->pattern_copypaste_submenu = submenu_alloc(); + tracker->instrument_submenu = submenu_alloc(); + + view_set_previous_callback(submenu_get_view(tracker->pattern_submenu), submenu_exit_callback); + view_set_previous_callback( + submenu_get_view(tracker->pattern_copypaste_submenu), submenu_exit_callback); + view_set_previous_callback( + submenu_get_view(tracker->instrument_submenu), submenu_exit_callback); + + submenu_add_item( + tracker->pattern_submenu, + "Load song", + SUBMENU_PATTERN_LOAD_SONG, + submenu_callback, + tracker); + submenu_add_item( + tracker->pattern_submenu, + "Save song", + SUBMENU_PATTERN_SAVE_SONG, + submenu_callback, + tracker); + submenu_add_item( + tracker->pattern_submenu, "Settings", SUBMENU_PATTERN_SETTINGS, submenu_callback, tracker); + submenu_add_item( + tracker->pattern_submenu, "Help", SUBMENU_PATTERN_HELP, submenu_callback, tracker); + submenu_add_item( + tracker->pattern_submenu, "Exit", SUBMENU_PATTERN_EXIT, submenu_callback, tracker); + + submenu_add_item( + tracker->instrument_submenu, + "Load instrument", + SUBMENU_INSTRUMENT_LOAD, + submenu_callback, + tracker); + submenu_add_item( + tracker->instrument_submenu, + "Save instrument", + SUBMENU_INSTRUMENT_SAVE, + submenu_callback, + tracker); + submenu_add_item( + tracker->instrument_submenu, "Exit", SUBMENU_INSTRUMENT_EXIT, submenu_callback, tracker); + + submenu_add_item( + tracker->pattern_copypaste_submenu, + "Copy", + SUBMENU_PATTERN_COPYPASTE_COPY, + submenu_copypaste_callback, + tracker); + submenu_add_item( + tracker->pattern_copypaste_submenu, + "Paste", + SUBMENU_PATTERN_COPYPASTE_PASTE, + submenu_copypaste_callback, + tracker); + submenu_add_item( + tracker->pattern_copypaste_submenu, + "Cut", + SUBMENU_PATTERN_COPYPASTE_CUT, + submenu_copypaste_callback, + tracker); + submenu_add_item( + tracker->pattern_copypaste_submenu, + "Clear", + SUBMENU_PATTERN_COPYPASTE_CLEAR, + submenu_copypaste_callback, + tracker); + + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_SUBMENU_PATTERN, + submenu_get_view(tracker->pattern_submenu)); + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_SUBMENU_PATTERN_COPYPASTE, + submenu_get_view(tracker->pattern_copypaste_submenu)); + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_SUBMENU_INSTRUMENT, + submenu_get_view(tracker->instrument_submenu)); + + load_config(tracker); + + tracker->settings_list = variable_item_list_alloc(); + View* view = variable_item_list_get_view(tracker->settings_list); + view_set_previous_callback(view, submenu_settings_exit_callback); + + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + tracker->settings_list, + "Audio output", + AUDIO_MODES_COUNT, + audio_output_changed_callback, + tracker); + value_index = + my_value_index_bool(tracker->external_audio, audio_modes_values, AUDIO_MODES_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, audio_modes_text[value_index]); + + view_dispatcher_add_view(tracker->view_dispatcher, VIEW_SETTINGS, view); + + tracker->overwrite_file_widget = widget_alloc(); + + widget_add_button_element( + tracker->overwrite_file_widget, + GuiButtonTypeLeft, + "No", + (ButtonCallback)overwrite_file_widget_no_input_callback, + tracker); + widget_add_button_element( + tracker->overwrite_file_widget, + GuiButtonTypeRight, + "Yes", + (ButtonCallback)overwrite_file_widget_yes_input_callback, + tracker); + + widget_add_text_scroll_element( + tracker->overwrite_file_widget, + 0, + 0, + 128, + 64, + "This song file already exists,\n do you want to overwrite it?"); + + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_FILE_OVERWRITE, + widget_get_view(tracker->overwrite_file_widget)); + + tracker->overwrite_instrument_file_widget = widget_alloc(); + + widget_add_button_element( + tracker->overwrite_instrument_file_widget, + GuiButtonTypeLeft, + "No", + (ButtonCallback)overwrite_instrument_file_widget_no_input_callback, + tracker); + widget_add_button_element( + tracker->overwrite_instrument_file_widget, + GuiButtonTypeRight, + "Yes", + (ButtonCallback)overwrite_instrument_file_widget_yes_input_callback, + tracker); + + widget_add_text_scroll_element( + tracker->overwrite_instrument_file_widget, + 0, + 0, + 128, + 64, + "This instrument file already\nexists, do you want to\noverwrite it?"); + + view_dispatcher_add_view( + tracker->view_dispatcher, + VIEW_INSTRUMENT_FILE_OVERWRITE, + widget_get_view(tracker->overwrite_instrument_file_widget)); + + tracker->notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(tracker->notification, &sequence_display_backlight_enforce_on); + + set_default_song(tracker); + + tracker->focus = EDIT_SONGINFO; + tracker->source_pattern_index = -1; + + return tracker; +} + +void deinit_tracker(FlizzerTrackerApp* tracker) { + notification_message(tracker->notification, &sequence_display_backlight_enforce_auto); + furi_record_close(RECORD_NOTIFICATION); + + // Специальная очистка памяти, занимаемой очередью + furi_message_queue_free(tracker->event_queue); + + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SETTINGS); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_FILE_OVERWRITE); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_INSTRUMENT); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN_COPYPASTE); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_KEYBOARD); + view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_TRACKER); + + text_input_free(tracker->text_input); + + variable_item_list_free(tracker->settings_list); + + submenu_free(tracker->pattern_submenu); + submenu_free(tracker->pattern_copypaste_submenu); + submenu_free(tracker->instrument_submenu); + + widget_free(tracker->overwrite_file_widget); + widget_free(tracker->overwrite_instrument_file_widget); + + view_dispatcher_free(tracker->view_dispatcher); + + tracker_view_free(tracker->tracker_view); + furi_record_close(RECORD_GUI); + + stream_free(tracker->stream); + furi_record_close(RECORD_STORAGE); + + sound_engine_deinit(&tracker->sound_engine); + + if(tracker->tracker_engine.song == NULL) { + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + } + + tracker_engine_deinit(&tracker->tracker_engine, false); + + free(tracker); +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/init_deinit.h b/applications/external/flizzer_tracker/init_deinit.h new file mode 100644 index 000000000..ebc80f9be --- /dev/null +++ b/applications/external/flizzer_tracker/init_deinit.h @@ -0,0 +1,14 @@ +#pragma once + +#include "flizzer_tracker.h" +#include "flizzer_tracker_hal.h" + +extern bool audio_modes_values[]; +extern char* audio_modes_text[]; + +FlizzerTrackerApp* init_tracker( + uint32_t sample_rate, + uint8_t rate, + bool external_audio_output, + uint32_t audio_buffer_size); +void deinit_tracker(FlizzerTrackerApp* tracker); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/instrument.c b/applications/external/flizzer_tracker/input/instrument.c new file mode 100644 index 000000000..cb55ebbf7 --- /dev/null +++ b/applications/external/flizzer_tracker/input/instrument.c @@ -0,0 +1,536 @@ +#include "instrument.h" +#include "songinfo.h" + +void edit_instrument_param(FlizzerTrackerApp* tracker, uint8_t selected_param, int8_t delta) { + if(!(tracker->current_digit)) { + delta *= 16; + } + + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + + switch(selected_param) { + case INST_CURRENTINSTRUMENT: { + int16_t inst = tracker->current_instrument; + + int8_t inst_delta = delta > 0 ? 1 : -1; + + inst += inst_delta; + + clamp(inst, 0, 0, tracker->song.num_instruments); + + if(check_and_allocate_instrument(&tracker->song, (uint8_t)inst)) { + tracker->current_instrument = inst; + } + + break; + } + + case INST_INSTRUMENTNAME: { + text_input_set_header_text(tracker->text_input, "Instrument name:"); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&inst->name, + MUS_INST_NAME_LEN + 1, + false); + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + case INST_CURRENT_NOTE: { + int8_t note_delta = 0; + + if(delta < 0) { + if(tracker->current_digit) { + note_delta = -12; + } + + else { + note_delta = -1; + } + } + + if(delta > 0) { + if(tracker->current_digit) { + note_delta = 12; + } + + else { + note_delta = 1; + } + } + + clamp(inst->base_note, note_delta, 0, MAX_NOTE); + + break; + } + + case INST_FINETUNE: { + int8_t fine_delta = 0; + + if(delta < 0) { + if(tracker->current_digit) { + fine_delta = -1; + } + + else { + fine_delta = -10; + } + } + + if(delta > 0) { + if(tracker->current_digit) { + fine_delta = 1; + } + + else { + fine_delta = 10; + } + } + + inst->finetune += fine_delta; + + break; + } + + case INST_SLIDESPEED: { + if((int16_t)inst->slide_speed + (int16_t)delta >= 0 && + (int16_t)inst->slide_speed + (int16_t)delta <= 0xff) { + inst->slide_speed += delta; + } + + break; + } + + case INST_SETPW: { + flipbit(inst->flags, TE_SET_PW); + break; + } + + case INST_PW: { + if((int16_t)inst->pw + (int16_t)delta >= 0 && (int16_t)inst->pw + (int16_t)delta <= 0xff) { + inst->pw += delta; + } + + break; + } + + case INST_SETCUTOFF: { + flipbit(inst->flags, TE_SET_CUTOFF); + break; + } + + case INST_WAVE_NOISE: { + flipbit(inst->waveform, SE_WAVEFORM_NOISE); + break; + } + + case INST_WAVE_PULSE: { + flipbit(inst->waveform, SE_WAVEFORM_PULSE); + break; + } + + case INST_WAVE_TRIANGLE: { + flipbit(inst->waveform, SE_WAVEFORM_TRIANGLE); + break; + } + + case INST_WAVE_SAWTOOTH: { + flipbit(inst->waveform, SE_WAVEFORM_SAW); + break; + } + + case INST_WAVE_NOISE_METAL: { + flipbit(inst->waveform, SE_WAVEFORM_NOISE_METAL); + break; + } + + case INST_WAVE_SINE: { + flipbit(inst->waveform, SE_WAVEFORM_SINE); + break; + } + + case INST_ATTACK: { + if((int16_t)inst->adsr.a + (int16_t)delta >= 0 && + (int16_t)inst->adsr.a + (int16_t)delta <= 0xff) { + inst->adsr.a += delta; + } + + break; + } + + case INST_DECAY: { + if((int16_t)inst->adsr.d + (int16_t)delta >= 0 && + (int16_t)inst->adsr.d + (int16_t)delta <= 0xff) { + inst->adsr.d += delta; + } + + break; + } + + case INST_SUSTAIN: { + if((int16_t)inst->adsr.s + (int16_t)delta >= 0 && + (int16_t)inst->adsr.s + (int16_t)delta <= 0xff) { + inst->adsr.s += delta; + } + + break; + } + + case INST_RELEASE: { + if((int16_t)inst->adsr.r + (int16_t)delta >= 0 && + (int16_t)inst->adsr.r + (int16_t)delta <= 0xff) { + inst->adsr.r += delta; + } + + break; + } + + case INST_VOLUME: { + if((int16_t)inst->adsr.volume + (int16_t)delta >= 0 && + (int16_t)inst->adsr.volume + (int16_t)delta <= 0xff) { + inst->adsr.volume += delta; + } + + break; + } + + case INST_ENABLEFILTER: { + flipbit(inst->sound_engine_flags, SE_ENABLE_FILTER); + break; + } + + case INST_FILTERCUTOFF: { + if((int16_t)inst->filter_cutoff + (int16_t)delta >= 0 && + (int16_t)inst->filter_cutoff + (int16_t)delta <= 0xff) { + inst->filter_cutoff += delta; + } + + break; + } + + case INST_FILTERRESONANCE: { + if((int16_t)inst->filter_resonance + (int16_t)delta >= 0 && + (int16_t)inst->filter_resonance + (int16_t)delta <= 0xff) { + inst->filter_resonance += delta; + } + + break; + } + + case INST_FILTERTYPE: { + int8_t flt_delta = (delta > 0 ? 1 : -1); + + if((int16_t)inst->filter_type + (int16_t)flt_delta >= 0 && + (int16_t)inst->filter_type + (int16_t)flt_delta < FIL_MODES) { + inst->filter_type += flt_delta; + } + + break; + } + + case INST_ENABLERINGMOD: { + flipbit(inst->sound_engine_flags, SE_ENABLE_RING_MOD); + break; + } + + case INST_RINGMODSRC: { + if((int16_t)inst->ring_mod + (int16_t)delta >= 0 && + (int16_t)inst->ring_mod + (int16_t)delta < SONG_MAX_CHANNELS) { + inst->ring_mod += delta; + } + + if((int16_t)inst->ring_mod + (int16_t)delta < 0) { + inst->ring_mod = 0xff; // 0xff = self + } + + if((int16_t)inst->ring_mod == 0xff && (int16_t)delta > 0) { + inst->ring_mod = 0; + } + + break; + } + + case INST_ENABLEHARDSYNC: { + flipbit(inst->sound_engine_flags, SE_ENABLE_HARD_SYNC); + break; + } + + case INST_HARDSYNCSRC: { + if((int16_t)inst->hard_sync + (int16_t)delta >= 0 && + (int16_t)inst->hard_sync + (int16_t)delta < SONG_MAX_CHANNELS) { + inst->hard_sync += delta; + } + + if((int16_t)inst->hard_sync + (int16_t)delta < 0) { + inst->hard_sync = 0xff; // 0xff = self + } + + if((int16_t)inst->hard_sync == 0xff && (int16_t)delta > 0) { + inst->hard_sync = 0; + } + + break; + } + + case INST_RETRIGGERONSLIDE: { + flipbit(inst->flags, TE_RETRIGGER_ON_SLIDE); + break; + } + + case INST_ENABLEKEYSYNC: { + flipbit(inst->sound_engine_flags, SE_ENABLE_KEYDOWN_SYNC); + break; + } + + case INST_ENABLEVIBRATO: { + flipbit(inst->flags, TE_ENABLE_VIBRATO); + break; + } + + case INST_VIBRATOSPEED: { + if((int16_t)inst->vibrato_speed + (int16_t)delta >= 0 && + (int16_t)inst->vibrato_speed + (int16_t)delta <= 0xff) { + inst->vibrato_speed += delta; + } + + break; + } + + case INST_VIBRATODEPTH: { + if((int16_t)inst->vibrato_depth + (int16_t)delta >= 0 && + (int16_t)inst->vibrato_depth + (int16_t)delta <= 0xff) { + inst->vibrato_depth += delta; + } + + break; + } + + case INST_VIBRATODELAY: { + if((int16_t)inst->vibrato_delay + (int16_t)delta >= 0 && + (int16_t)inst->vibrato_delay + (int16_t)delta <= 0xff) { + inst->vibrato_delay += delta; + } + + break; + } + + case INST_ENABLEPWM: { + flipbit(inst->flags, TE_ENABLE_PWM); + break; + } + + case INST_PWMSPEED: { + if((int16_t)inst->pwm_speed + (int16_t)delta >= 0 && + (int16_t)inst->pwm_speed + (int16_t)delta <= 0xff) { + inst->pwm_speed += delta; + } + + break; + } + + case INST_PWMDEPTH: { + if((int16_t)inst->pwm_depth + (int16_t)delta >= 0 && + (int16_t)inst->pwm_depth + (int16_t)delta <= 0xff) { + inst->pwm_depth += delta; + } + + break; + } + + case INST_PWMDELAY: { + if((int16_t)inst->pwm_delay + (int16_t)delta >= 0 && + (int16_t)inst->pwm_delay + (int16_t)delta <= 0xff) { + inst->pwm_delay += delta; + } + + break; + } + + case INST_PROGRESTART: { + flipbit(inst->flags, TE_PROG_NO_RESTART); + break; + } + + case INST_PROGRAMEPERIOD: { + if((int16_t)inst->program_period + (int16_t)delta >= 0 && + (int16_t)inst->program_period + (int16_t)delta <= 0xff) { + inst->program_period += delta; + } + + break; + } + } +} + +void instrument_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && + !tracker->tracker_engine.playing) { + tracker->editing = !(tracker->editing); + return; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong && !tracker->editing) { + reset_buffer(&tracker->sound_engine); + tracker_engine_set_song(&tracker->tracker_engine, NULL); + + for(int i = 1; i < SONG_MAX_CHANNELS; i++) { + tracker->tracker_engine.channel[i].channel_flags &= TEC_PLAYING; + tracker->tracker_engine.sound_engine->channel[i].frequency = 0; + tracker->tracker_engine.sound_engine->channel[i].waveform = 0; + } + + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + tracker_engine_trigger_instrument_internal( + &tracker->tracker_engine, 0, inst, (MIDDLE_C << 8)); + tracker->tracker_engine.playing = true; + play(); + return; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeRelease && + !tracker->editing) { + SoundEngineChannel* se_channel = &tracker->sound_engine.channel[0]; + sound_engine_enable_gate(&tracker->sound_engine, se_channel, false); + return; + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) { + switch(tracker->selected_param) { + default: { + tracker->current_digit++; + + if(tracker->current_digit > 1) { + tracker->selected_param++; + + tracker->current_digit = 0; + + if(tracker->selected_param > INST_PARAMS - 1) { + tracker->selected_param = 0; + } + } + + break; + } + + case INST_CURRENTINSTRUMENT: + case INST_INSTRUMENTNAME: + case INST_SETPW: + case INST_SETCUTOFF: + case INST_WAVE_NOISE: + case INST_WAVE_PULSE: + case INST_WAVE_TRIANGLE: + case INST_WAVE_SAWTOOTH: + case INST_WAVE_NOISE_METAL: + case INST_WAVE_SINE: + case INST_ENABLEFILTER: + case INST_FILTERTYPE: + case INST_ENABLERINGMOD: + case INST_RINGMODSRC: + case INST_ENABLEHARDSYNC: + case INST_HARDSYNCSRC: + case INST_RETRIGGERONSLIDE: + case INST_ENABLEKEYSYNC: + case INST_ENABLEVIBRATO: + case INST_ENABLEPWM: + case INST_PROGRESTART: { + tracker->selected_param++; + + tracker->current_digit = 1; + + if(tracker->selected_param > INST_PARAMS - 1) { + tracker->selected_param = 0; + } + + break; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort) { + switch(tracker->selected_param) { + default: { + tracker->current_digit--; + + if(tracker->current_digit > 1) // unsigned int overflow + { + tracker->selected_param--; + + tracker->current_digit = 1; + + if(tracker->selected_param > INST_PARAMS - 1) // unsigned int overflow + { + tracker->selected_param = INST_PARAMS - 1; + } + } + + break; + } + + case INST_CURRENTINSTRUMENT: + case INST_INSTRUMENTNAME: + case INST_SETPW: + case INST_SETCUTOFF: + case INST_WAVE_NOISE: + case INST_WAVE_PULSE: + case INST_WAVE_TRIANGLE: + case INST_WAVE_SAWTOOTH: + case INST_WAVE_NOISE_METAL: + case INST_WAVE_SINE: + case INST_ENABLEFILTER: + case INST_FILTERTYPE: + case INST_ENABLERINGMOD: + case INST_RINGMODSRC: + case INST_ENABLEHARDSYNC: + case INST_HARDSYNCSRC: + case INST_RETRIGGERONSLIDE: + case INST_ENABLEKEYSYNC: + case INST_ENABLEVIBRATO: + case INST_ENABLEPWM: + case INST_PROGRESTART: { + tracker->selected_param--; + + tracker->current_digit = 1; + + if(tracker->selected_param > INST_PARAMS - 1) // unsigned int overflow + { + tracker->selected_param = INST_PARAMS - 1; + } + + break; + } + } + + return; + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(tracker->editing) { + edit_instrument_param(tracker, tracker->selected_param, -1); + } + + return; + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(tracker->editing) { + edit_instrument_param(tracker, tracker->selected_param, 1); + } + + return; + } + + if(tracker->selected_param > INST_VIBRATODELAY) { + tracker->inst_editor_shift = 6; + } + + if(tracker->selected_param > INST_PWMDELAY) { + tracker->inst_editor_shift = 12; + } + + if(tracker->selected_param < INST_CURRENT_NOTE) { + tracker->inst_editor_shift = 0; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/instrument.h b/applications/external/flizzer_tracker/input/instrument.h new file mode 100644 index 000000000..af5d86fc0 --- /dev/null +++ b/applications/external/flizzer_tracker/input/instrument.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +void instrument_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/instrument_program.c b/applications/external/flizzer_tracker/input/instrument_program.c new file mode 100644 index 000000000..5143ec3eb --- /dev/null +++ b/applications/external/flizzer_tracker/input/instrument_program.c @@ -0,0 +1,239 @@ +#include "instrument_program.h" +#include "../macros.h" + +void instrument_program_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort) { + tracker->editing = !(tracker->editing); + return; + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort && + tracker->editing) { + tracker->current_digit = my_min(2, tracker->current_digit + 1); + return; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong && tracker->editing) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + + if(tracker->current_program_step < INST_PROG_LEN - 1) { + if((inst->program[tracker->current_program_step] & 0x7fff) < TE_PROGRAM_LOOP_BEGIN && + ((inst->program[tracker->current_program_step + 1] & 0x7fff) < + TE_PROGRAM_LOOP_BEGIN || + (inst->program[tracker->current_program_step + 1] & 0x7f00) == + TE_PROGRAM_LOOP_END)) // so we can unite with loop end as in klystrack + { + inst->program[tracker->current_program_step] ^= 0x8000; // flipping unite bit + } + } + + return; + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort && + tracker->editing) { + tracker->current_digit = fmax(0, (int16_t)tracker->current_digit - 1); + return; + } + + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + tracker->editing) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + inst->program[tracker->current_program_step] = TE_PROGRAM_NOP; + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + if((int16_t)tracker->current_program_step - 1 >= 0) { + tracker->current_program_step--; + + if(tracker->program_position > tracker->current_program_step) { + tracker->program_position = tracker->current_program_step; + } + } + + else { + tracker->current_program_step = INST_PROG_LEN - 1; + + tracker->program_position = INST_PROG_LEN - 1 - 7; + } + } + + if(tracker->editing) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + uint16_t opcode = inst->program[tracker->current_program_step]; + + switch(tracker->current_digit) { + case 0: // MSB + { + uint8_t param = ((opcode & 0x7f00) >> 8); + + if(param < 0xff) { + param++; + } + + if((inst->program[tracker->current_program_step] & 0x7fff) == TE_PROGRAM_NOP) { + param = 0; + inst->program[tracker->current_program_step] = 0; + } + + param &= 0x7f; + + inst->program[tracker->current_program_step] &= 0x80ff; + inst->program[tracker->current_program_step] |= ((uint16_t)param << 8); + + break; + } + + case 1: // upper digit of param, e.g. eXx + { + int8_t nibble = ((opcode & 0x00f0) >> 4); + + if(nibble + 1 <= 0xf) { + nibble++; + } + + else { + nibble = 0; + } + + inst->program[tracker->current_program_step] &= 0xff0f; + inst->program[tracker->current_program_step] |= (nibble << 4); + + break; + } + + case 2: // lower digit of param, e.g. exX + { + int8_t nibble = (opcode & 0x000f); + + if(nibble + 1 <= 0xf) { + nibble++; + } + + else { + nibble = 0; + } + + inst->program[tracker->current_program_step] &= 0xfff0; + inst->program[tracker->current_program_step] |= nibble; + + break; + } + + default: + break; + } + } + + return; + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + if(tracker->current_program_step + 1 < INST_PROG_LEN) { + tracker->current_program_step++; + + if(tracker->program_position < tracker->current_program_step - 7) { + tracker->program_position = tracker->current_program_step - 7; + } + } + + else { + tracker->current_program_step = 0; + + tracker->program_position = 0; + } + } + + if(tracker->editing) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + uint16_t opcode = inst->program[tracker->current_program_step]; + + switch(tracker->current_digit) { + case 0: // MSB + { + uint8_t param = ((opcode & 0x7f00) >> 8); + + if(param < (TE_PROGRAM_JUMP >> 8) && param > 0) { + param--; + + inst->program[tracker->current_program_step] &= 0x80ff; + inst->program[tracker->current_program_step] |= ((uint16_t)param << 8); + } + + if((inst->program[tracker->current_program_step] & 0x7f00) == TE_PROGRAM_JUMP && + (inst->program[tracker->current_program_step] & 0x7fff) != TE_PROGRAM_END && + (inst->program[tracker->current_program_step] & 0x7fff) != TE_PROGRAM_NOP) { + inst->program[tracker->current_program_step] = + TE_PROGRAM_LOOP_END | + (inst->program[tracker->current_program_step] & 0x8000); + } + + if((inst->program[tracker->current_program_step] & 0x7fff) == TE_PROGRAM_END) { + // param = (TE_PROGRAM_JUMP >> 8); + inst->program[tracker->current_program_step] = + TE_PROGRAM_JUMP | (inst->program[tracker->current_program_step] & 0x8000); + } + + if((inst->program[tracker->current_program_step] & 0x7fff) == TE_PROGRAM_NOP) { + // param = (TE_PROGRAM_END >> 8); + inst->program[tracker->current_program_step] = + TE_PROGRAM_END | (inst->program[tracker->current_program_step] & 0x8000); + } + + if((inst->program[tracker->current_program_step] & 0x7f00) == + (TE_PROGRAM_LOOP_BEGIN - 0x100)) { + // param = (TE_PROGRAM_END >> 8); + inst->program[tracker->current_program_step] = + TE_EFFECT_TRIGGER_RELEASE | + (inst->program[tracker->current_program_step] & 0x8000); + } + + break; + } + + case 1: // upper digit of param, e.g. eXx + { + int8_t nibble = ((opcode & 0x00f0) >> 4); + + if(nibble - 1 >= 0) { + nibble--; + } + + else { + nibble = 0xf; + } + + inst->program[tracker->current_program_step] &= 0xff0f; + inst->program[tracker->current_program_step] |= (nibble << 4); + + break; + } + + case 2: // lower digit of param, e.g. exX + { + int8_t nibble = (opcode & 0x000f); + + if(nibble - 1 >= 0) { + nibble--; + } + + else { + nibble = 0xf; + } + + inst->program[tracker->current_program_step] &= 0xfff0; + inst->program[tracker->current_program_step] |= nibble; + + break; + } + + default: + break; + } + } + + return; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/instrument_program.h b/applications/external/flizzer_tracker/input/instrument_program.h new file mode 100644 index 000000000..88009406d --- /dev/null +++ b/applications/external/flizzer_tracker/input/instrument_program.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +void instrument_program_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/pattern.c b/applications/external/flizzer_tracker/input/pattern.c new file mode 100644 index 000000000..43bfdca11 --- /dev/null +++ b/applications/external/flizzer_tracker/input/pattern.c @@ -0,0 +1,410 @@ +#include "pattern.h" + +uint8_t get_field(uint8_t patternx) { + uint8_t field = 0; + + if(patternx <= 1) field = 0; + if(patternx == 2) field = 1; + if(patternx == 3) field = 2; + if(patternx > 3) field = 3; + + return field; +} + +void edit_note( + FlizzerTrackerApp* tracker, + TrackerSongPatternStep* step, + int8_t delta) // here we need data about last note if we place a new note +{ + int16_t note = tracker_engine_get_note(step); + + if(note == MUS_NOTE_RELEASE) { + if(delta < 0) { + set_note(step, MUS_NOTE_CUT); + } + + return; + } + + if(note == MUS_NOTE_CUT) { + if(delta > 0) { + set_note(step, MUS_NOTE_RELEASE); + } + + return; + } + + if(note == MUS_NOTE_NONE) { + note = + tracker->current_note; // remember which note we entered earlier and use it as reference + } + + clamp(note, delta, 0, MAX_NOTE); + + set_note(step, (uint8_t)note); + set_instrument(step, tracker->current_instrument); + + tracker->current_note = (uint8_t)note; +} + +void edit_instrument(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta) { + int16_t inst = tracker_engine_get_instrument(step); + + if(inst == MUS_NOTE_INSTRUMENT_NONE) { + if(delta > 0) { + inst = tracker->current_instrument; + } + + else { + inst = MUS_NOTE_INSTRUMENT_NONE - 1; + } + } + + clamp(inst, delta, 0, tracker->song.num_instruments - 1); + tracker->current_instrument = inst; // remember last instrument + set_instrument(step, (uint8_t)inst); +} + +void edit_volume(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta) { + int16_t vol = tracker_engine_get_volume(step); + + vol = tracker->current_volume; + + if(vol + delta < 0) { + vol = MUS_NOTE_VOLUME_NONE - 1 - delta; + } + + if(vol + delta >= MUS_NOTE_VOLUME_NONE) { + vol = 0 - delta; + } + + clamp(vol, delta, 0, MUS_NOTE_VOLUME_NONE - 1); + + set_volume(step, (uint8_t)vol); + + tracker->current_volume = vol; +} + +void edit_command(TrackerSongPatternStep* step, uint8_t digit, int8_t delta) { + int32_t command = tracker_engine_get_command(step); + + switch(digit) { + case 0: // upper 7 bits + { + int16_t fx_name = ((command & 0x7f00) >> 8); + + if(fx_name + delta > 35) // loop + { // 0-9 and then A-Z + fx_name = 0; + } + + else if(fx_name + delta < 0) { + fx_name = 35; + } + + else { + fx_name += delta; + } + + command &= 0x00ff; + + command |= (fx_name << 8); + + set_command(step, (uint16_t)command); + + break; + } + + case 1: // upper digit of command param + { + int8_t upper_digit = ((command & 0x00f0) >> 4); + + if(upper_digit + delta > 0xf) // loop + { + upper_digit = 0; + } + + else if(upper_digit + delta < 0) { + upper_digit = 0xf; + } + + else { + upper_digit += delta; + } + + command &= 0xff0f; + + command |= (upper_digit << 4); + + set_command(step, (uint16_t)command); + + break; + } + + case 2: // lower digit of command param + { + int8_t lower_digit = (command & 0x000f); + + if(lower_digit + delta > 0xf) // loop + { + lower_digit = 0; + } + + else if(lower_digit + delta < 0) { + lower_digit = 0xf; + } + + else { + lower_digit += delta; + } + + command &= 0xfff0; + + command |= lower_digit; + + set_command(step, (uint16_t)command); + + break; + } + + default: + break; + } +} + +void delete_field(TrackerSongPatternStep* step, uint8_t field) { + switch(field) { + case 0: // note + { + set_note(step, MUS_NOTE_NONE); + set_instrument(step, MUS_NOTE_INSTRUMENT_NONE); // also delete instrument + break; + } + + case 1: // instrument + { + set_instrument(step, MUS_NOTE_INSTRUMENT_NONE); + break; + } + + case 2: // volume + { + set_volume(step, MUS_NOTE_VOLUME_NONE); + break; + } + + case 3: // command + { + set_command(step, 0); + break; + } + + default: + break; + } +} + +void edit_pattern_step(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta) { + switch(get_field(tracker->patternx)) { + case 0: // note + { + if(tracker->patternx) // editing octave + { + edit_note(tracker, step, 12 * delta); + } + + else // editing note + { + edit_note(tracker, step, delta); + } + + break; + } + + case 1: // instrument + { + edit_instrument(tracker, step, delta); + break; + } + + case 2: // volume + { + edit_volume(tracker, step, delta); + break; + } + + case 3: // command + { + uint8_t digit = 0; + if(tracker->patternx == 4) digit = 0; + if(tracker->patternx == 5) digit = 1; + if(tracker->patternx == 6) digit = 2; + edit_command(step, digit, delta); + break; + } + + default: + break; + } +} + +void pattern_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyLeft && event->input.type == InputTypeLong && + !(tracker->editing)) { + flipbit( + tracker->tracker_engine.channel[tracker->current_channel].channel_flags, TEC_DISABLED); + return; + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeLong && + !(tracker->editing)) { + tracker->tracker_engine.pattern_position = + tracker->tracker_engine.song->pattern_length - 1; // go to pattern last row + return; + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeLong && + !(tracker->editing)) { + tracker->tracker_engine.pattern_position = 0; // return to pattern 1st row + return; + } + + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t current_pattern = + tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + uint16_t pattern_step = tracker->tracker_engine.pattern_position; + + uint16_t pattern_length = tracker->tracker_engine.song->pattern_length; + + TrackerSongPattern* pattern = &tracker->tracker_engine.song->pattern[current_pattern]; + + TrackerSongPatternStep* step = NULL; + + if(pattern_step < pattern_length) { + step = &pattern->step[pattern_step]; + } + + if(!(step)) return; + + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && + !tracker->tracker_engine.playing) { + tracker->editing = !tracker->editing; + + if(tracker->editing) { + // stop_song(tracker); + } + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong) { + if(!(tracker->editing)) { + if(tracker->tracker_engine.playing) { + stop_song(tracker); + } + + else { + if(tracker->tracker_engine.pattern_position == tracker->song.pattern_length - 1 && + tracker->tracker_engine.sequence_position == + tracker->song.num_sequence_steps - + 1) // if we are at the very end of the song + { + stop_song(tracker); + } + + else { + play_song(tracker, true); + } + } + } + + else { + if(get_field(tracker->patternx) == 0) { + set_note(step, MUS_NOTE_RELEASE); + } + } + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) { + tracker->patternx++; + + if(tracker->patternx > MAX_PATTERNX - 1) { + tracker->current_channel++; + + tracker->patternx = 0; + + if(tracker->current_channel > SONG_MAX_CHANNELS - 1) { + tracker->current_channel = 0; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort) { + tracker->patternx--; + + if(tracker->patternx > MAX_PATTERNX - 1) // unsigned int overflow + { + tracker->current_channel--; + + tracker->patternx = MAX_PATTERNX - 1; + + if(tracker->current_channel > SONG_MAX_CHANNELS - 1) // unsigned int overflow + { + tracker->current_channel = SONG_MAX_CHANNELS - 1; + } + } + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + tracker->tracker_engine.pattern_position++; + + if(tracker->tracker_engine.pattern_position > + tracker->tracker_engine.song->pattern_length - 1 && + tracker->tracker_engine.sequence_position < + tracker->tracker_engine.song->num_sequence_steps - 1) { + tracker->tracker_engine.pattern_position = 0; + tracker->tracker_engine.sequence_position++; + } + + else if( + tracker->tracker_engine.pattern_position > + tracker->tracker_engine.song->pattern_length - 1) { + tracker->tracker_engine.pattern_position = + tracker->tracker_engine.song->pattern_length - 1; + } + } + + if(tracker->editing) { + edit_pattern_step(tracker, step, -1); + } + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + int16_t temp_pattern_position = tracker->tracker_engine.pattern_position - 1; + + if(temp_pattern_position < 0) { + if(tracker->tracker_engine.sequence_position > 0) { + tracker->tracker_engine.sequence_position--; + tracker->tracker_engine.pattern_position = + tracker->tracker_engine.song->pattern_length - 1; + } + } + + else { + tracker->tracker_engine.pattern_position--; + } + } + + if(tracker->editing) { + edit_pattern_step(tracker, step, 1); + } + } + + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + tracker->editing) { + uint8_t field = get_field(tracker->patternx); + + delete_field(step, field); + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/pattern.h b/applications/external/flizzer_tracker/input/pattern.h new file mode 100644 index 000000000..5f2e7b70b --- /dev/null +++ b/applications/external/flizzer_tracker/input/pattern.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +#define MAX_PATTERNX (2 + 1 + 1 + 3) + +void pattern_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/sequence.c b/applications/external/flizzer_tracker/input/sequence.c new file mode 100644 index 000000000..3db4daa55 --- /dev/null +++ b/applications/external/flizzer_tracker/input/sequence.c @@ -0,0 +1,209 @@ +#include "sequence.h" + +void delete_sequence_step(FlizzerTrackerApp* tracker) { + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t* pattern = &tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + *pattern = 0; +} + +void edit_sequence_step(FlizzerTrackerApp* tracker, int8_t delta) { + uint8_t digit = tracker->current_digit; + + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t pattern_index = tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + + uint8_t* pattern = &tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + uint8_t temp_pattern = *pattern; + + switch(digit) { + case 0: // upper nibble + { + int8_t nibble = ((pattern_index & 0xf0) >> 4); + + if(nibble + delta < 0) { + nibble = 0xf; + } + + else if(nibble + delta > 0xf) { + nibble = 0; + } + + else { + nibble += delta; + } + + temp_pattern &= 0x0f; + temp_pattern |= (nibble << 4); + + break; + } + + case 1: // lower nibble + { + int8_t nibble = (pattern_index & 0x0f); + + if(nibble + delta < 0) { + nibble = 0xf; + } + + else if(nibble + delta > 0xf) { + nibble = 0; + } + + else { + nibble += delta; + } + + temp_pattern &= 0xf0; + temp_pattern |= nibble; + + break; + } + } + + if(check_and_allocate_pattern(&tracker->song, temp_pattern)) { + *pattern = temp_pattern; + } +} + +void sequence_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && + !tracker->tracker_engine.playing) { + tracker->editing = !tracker->editing; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong) { + if(!(tracker->editing)) { + if(tracker->tracker_engine.playing) { + stop_song(tracker); + } + + else { + if(tracker->tracker_engine.pattern_position == tracker->song.pattern_length - 1 && + tracker->tracker_engine.sequence_position == + tracker->song.num_sequence_steps - + 1) // if we are at the very end of the song + { + stop_song(tracker); + } + + else { + play_song(tracker, true); + } + } + } + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) { + tracker->current_digit++; + + if(tracker->current_digit > 1) { + tracker->current_channel++; + + tracker->current_digit = 0; + + if(tracker->current_channel > SONG_MAX_CHANNELS - 1) { + tracker->current_channel = 0; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort) { + tracker->current_digit--; + + if(tracker->current_digit > 1) // unsigned int overflow + { + tracker->current_channel--; + + tracker->current_digit = 1; + + if(tracker->current_channel > SONG_MAX_CHANNELS - 1) // unsigned int overflow + { + tracker->current_channel = SONG_MAX_CHANNELS - 1; + } + } + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + tracker->tracker_engine.sequence_position++; + + if(tracker->tracker_engine.sequence_position >= + tracker->tracker_engine.song->num_sequence_steps) { + tracker->tracker_engine.sequence_position = 0; + } + } + + if(tracker->editing) { + edit_sequence_step(tracker, -1); + } + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(!(tracker->editing)) { + int16_t temp_sequence_position = tracker->tracker_engine.sequence_position - 1; + + if(temp_sequence_position < 0) { + tracker->tracker_engine.sequence_position = + tracker->tracker_engine.song->num_sequence_steps - 1; + } + + else { + tracker->tracker_engine.sequence_position--; + } + } + + if(tracker->editing) { + edit_sequence_step(tracker, 1); + } + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeLong && + !(tracker->editing)) // set loop begin or loop end for the song + { + TrackerSong* song = &tracker->song; + + if(song->loop_start == song->loop_end && song->loop_end == 0) // if both are 0 + { + song->loop_end = tracker->tracker_engine.sequence_position; + } + + else { + if(tracker->tracker_engine.sequence_position < song->loop_end) { + song->loop_start = tracker->tracker_engine.sequence_position; + } + + if(tracker->tracker_engine.sequence_position > song->loop_start) { + song->loop_end = tracker->tracker_engine.sequence_position; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeLong && + !(tracker->editing)) // erase loop begin and loop end points + { + TrackerSong* song = &tracker->song; + + song->loop_start = song->loop_end = 0; + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeLong && + !(tracker->editing)) // jump to the beginning + { + tracker->tracker_engine.sequence_position = 0; + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeLong && + !(tracker->editing)) // jump to the end + { + tracker->tracker_engine.sequence_position = tracker->song.num_sequence_steps - 1; + } + + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + tracker->editing) { + delete_sequence_step(tracker); + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/sequence.h b/applications/external/flizzer_tracker/input/sequence.h new file mode 100644 index 000000000..057b0de6b --- /dev/null +++ b/applications/external/flizzer_tracker/input/sequence.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +void sequence_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/songinfo.c b/applications/external/flizzer_tracker/input/songinfo.c new file mode 100644 index 000000000..a10ed7bec --- /dev/null +++ b/applications/external/flizzer_tracker/input/songinfo.c @@ -0,0 +1,224 @@ +#include "songinfo.h" + +#include "../diskop.h" + +void edit_songinfo_param(FlizzerTrackerApp* tracker, uint8_t selected_param, int8_t delta) { + if(!(tracker->current_digit)) { + delta *= 16; + } + + switch(selected_param) { + case SI_PATTERNPOS: { + uint16_t new_length = tracker->song.pattern_length; + + if((int16_t)new_length + (int16_t)delta > 0 && + (int16_t)new_length + (int16_t)delta <= 0x100) { + new_length += delta; + change_pattern_length(&tracker->song, new_length); + + if(tracker->tracker_engine.pattern_position >= new_length) { + tracker->tracker_engine.pattern_position = new_length - 1; + } + } + + break; + } + + case SI_SEQUENCEPOS: { + if((int16_t)tracker->song.num_sequence_steps + (int16_t)delta > 0 && + (int16_t)tracker->song.num_sequence_steps + (int16_t)delta <= 0x100) { + tracker->song.num_sequence_steps += delta; + + if(tracker->tracker_engine.sequence_position >= tracker->song.num_sequence_steps) { + tracker->tracker_engine.sequence_position = tracker->song.num_sequence_steps - 1; + } + } + + break; + } + + case SI_SONGSPEED: { + if((int16_t)tracker->song.speed + (int16_t)delta > 1 && + (int16_t)tracker->song.speed + (int16_t)delta <= 0xff) { + tracker->song.speed += delta; + } + + break; + } + + case SI_SONGRATE: { + if((int16_t)tracker->song.rate + (int16_t)delta > 1 && + (int16_t)tracker->song.rate + (int16_t)delta <= 0xff) { + tracker->song.rate += delta; + } + + break; + } + + case SI_MASTERVOL: { + if((int16_t)tracker->tracker_engine.master_volume + (int16_t)delta > 0 && + (int16_t)tracker->tracker_engine.master_volume + (int16_t)delta <= 0xff) { + tracker->tracker_engine.master_volume += delta; + } + + break; + } + + case SI_SONGNAME: { + text_input_set_header_text(tracker->text_input, "Song name:"); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&tracker->song.song_name, + MUS_SONG_NAME_LEN + 1, + false); + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + case SI_CURRENTINSTRUMENT: { + int16_t inst = tracker->current_instrument; + + int8_t inst_delta = delta > 0 ? 1 : -1; + + inst += inst_delta; + + clamp(inst, 0, 0, tracker->song.num_instruments - 1); + + tracker->current_instrument = inst; + + break; + } + + case SI_INSTRUMENTNAME: { + text_input_set_header_text(tracker->text_input, "Instrument name:"); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&tracker->song.instrument[tracker->current_instrument]->name, + MUS_INST_NAME_LEN + 1, + false); + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + default: + break; + } +} + +void songinfo_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && + !tracker->tracker_engine.playing) { + tracker->editing = !tracker->editing; + } + + if(event->input.key == InputKeyOk && event->input.type == InputTypeLong) { + if(!(tracker->editing)) { + if(tracker->tracker_engine.playing) { + stop_song(tracker); + } + + else { + if(tracker->tracker_engine.pattern_position == tracker->song.pattern_length - 1 && + tracker->tracker_engine.sequence_position == + tracker->song.num_sequence_steps - + 1) // if we are at the very end of the song + { + stop_song(tracker); + } + + else { + play_song(tracker, true); + } + } + } + } + + if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) { + switch(tracker->selected_param) { + default: { + tracker->current_digit++; + + if(tracker->current_digit > 1) { + tracker->selected_param++; + + tracker->current_digit = 0; + + if(tracker->selected_param > SI_PARAMS - 1) { + tracker->selected_param = 0; + } + } + + break; + } + + case SI_CURRENTINSTRUMENT: + case SI_SONGNAME: + case SI_INSTRUMENTNAME: { + tracker->selected_param++; + + tracker->current_digit = 0; + + if(tracker->selected_param > SI_PARAMS - 1) { + tracker->selected_param = 0; + } + + break; + } + } + } + + if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort) { + switch(tracker->selected_param) { + default: { + tracker->current_digit--; + + if(tracker->current_digit > 1) // unsigned int overflow + { + tracker->selected_param--; + + tracker->current_digit = 1; + + if(tracker->selected_param > SI_PARAMS - 1) // unsigned int overflow + { + tracker->selected_param = SI_PARAMS - 1; + } + } + + break; + } + + case SI_CURRENTINSTRUMENT: + case SI_SONGNAME: + case SI_INSTRUMENTNAME: { + tracker->selected_param--; + + tracker->current_digit = 0; + + if(tracker->selected_param > SI_PARAMS - 1) // unsigned int overflow + { + tracker->selected_param = SI_PARAMS - 1; + } + + break; + } + } + } + + if(event->input.key == InputKeyDown && event->input.type == InputTypeShort) { + if(tracker->editing) { + edit_songinfo_param(tracker, tracker->selected_param, -1); + } + } + + if(event->input.key == InputKeyUp && event->input.type == InputTypeShort) { + if(tracker->editing) { + edit_songinfo_param(tracker, tracker->selected_param, 1); + } + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input/songinfo.h b/applications/external/flizzer_tracker/input/songinfo.h new file mode 100644 index 000000000..3add936eb --- /dev/null +++ b/applications/external/flizzer_tracker/input/songinfo.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include + +#include "../flizzer_tracker.h" +#include "../sound_engine/sound_engine_defs.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "../util.h" + +void songinfo_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); +void return_from_keyboard_callback(void* ctx); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input_event.c b/applications/external/flizzer_tracker/input_event.c new file mode 100644 index 000000000..55b9e7d62 --- /dev/null +++ b/applications/external/flizzer_tracker/input_event.c @@ -0,0 +1,501 @@ +#include "input_event.h" + +#include "diskop.h" + +#define AUDIO_MODES_COUNT 2 + +void return_from_keyboard_callback(void* ctx) { + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(!tracker->is_loading && !tracker->is_saving && !tracker->is_loading_instrument && + !tracker->is_saving_instrument) { + uint8_t string_length = 0; + char* string = NULL; + + if(tracker->focus == EDIT_SONGINFO && tracker->mode == PATTERN_VIEW) { + switch(tracker->selected_param) { + case SI_SONGNAME: { + string_length = MUS_SONG_NAME_LEN; + string = (char*)&tracker->song.song_name; + break; + } + + case SI_INSTRUMENTNAME: { + string_length = MUS_INST_NAME_LEN; + string = (char*)&tracker->song.instrument[tracker->current_instrument]->name; + break; + } + } + } + + if(tracker->focus == EDIT_INSTRUMENT && tracker->mode == INST_EDITOR_VIEW) { + switch(tracker->selected_param) { + case INST_INSTRUMENTNAME: { + string_length = MUS_INST_NAME_LEN; + string = (char*)&tracker->song.instrument[tracker->current_instrument]->name; + break; + } + } + } + + if(string == NULL || string_length == 0) return; + + for(uint8_t i = 0; i < string_length; + i++) // I tinyfied the font by deleting lowercase chars, and I don't like the lowercase chars of any 3x5 pixels font + { + string[i] = toupper(string[i]); + } + } + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + + if(tracker->is_saving) { + stop_song(tracker); + + tracker->filepath = furi_string_alloc(); + furi_string_cat_printf( + tracker->filepath, "%s/%s%s", FLIZZER_TRACKER_FOLDER, tracker->filename, SONG_FILE_EXT); + + if(storage_file_exists(tracker->storage, furi_string_get_cstr(tracker->filepath))) { + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_FILE_OVERWRITE); + return; + } + + else { + FlizzerTrackerEvent event = {.type = EventTypeSaveSong, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } + } + + if(tracker->is_saving_instrument) { + stop_song(tracker); + + tracker->filepath = furi_string_alloc(); + furi_string_cat_printf( + tracker->filepath, + "%s/%s%s", + FLIZZER_TRACKER_INSTRUMENTS_FOLDER, + tracker->filename, + INST_FILE_EXT); + + if(storage_file_exists(tracker->storage, furi_string_get_cstr(tracker->filepath))) { + view_dispatcher_switch_to_view( + tracker->view_dispatcher, VIEW_INSTRUMENT_FILE_OVERWRITE); + return; + } + + else { + FlizzerTrackerEvent event = { + .type = EventTypeSaveInstrument, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } + } +} + +void overwrite_file_widget_yes_input_callback(GuiButtonType result, InputType type, void* ctx) { + UNUSED(result); + + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(type == InputTypeShort) { + tracker->is_saving = true; + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + // save_song(tracker, tracker->filepath); + static FlizzerTrackerEvent event = { + .type = EventTypeSaveSong, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } +} + +void overwrite_file_widget_no_input_callback(GuiButtonType result, InputType type, void* ctx) { + UNUSED(result); + + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(type == InputTypeShort) { + tracker->is_saving = false; + furi_string_free(tracker->filepath); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + } +} + +void overwrite_instrument_file_widget_yes_input_callback( + GuiButtonType result, + InputType type, + void* ctx) { + UNUSED(result); + + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(type == InputTypeShort) { + tracker->is_saving_instrument = true; + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + // save_song(tracker, tracker->filepath); + static FlizzerTrackerEvent event = { + .type = EventTypeSaveInstrument, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + } +} + +void overwrite_instrument_file_widget_no_input_callback( + GuiButtonType result, + InputType type, + void* ctx) { + UNUSED(result); + + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx; + + if(type == InputTypeShort) { + tracker->is_saving_instrument = false; + furi_string_free(tracker->filepath); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + } +} + +uint32_t submenu_settings_exit_callback(void* context) { + UNUSED(context); + return VIEW_SUBMENU_PATTERN; +} + +uint32_t submenu_exit_callback(void* context) { + UNUSED(context); + return VIEW_TRACKER; +} + +void submenu_callback(void* context, uint32_t index) { + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)context; + + switch(tracker->mode) { + case PATTERN_VIEW: { + switch(index) { + case SUBMENU_PATTERN_EXIT: { + tracker->quit = true; + + static InputEvent inevent = {.sequence = 0, .key = InputKeyLeft, .type = InputTypeMAX}; + FlizzerTrackerEvent event = { + .type = EventTypeInput, + .input = inevent, + .period = + 0}; // making an event so tracker does not wait for next keypress and exits immediately + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + case SUBMENU_PATTERN_HELP: { + tracker->showing_help = true; + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + case SUBMENU_PATTERN_SAVE_SONG: { + text_input_set_header_text(tracker->text_input, "Song filename:"); + memset(&tracker->filename, 0, FILE_NAME_LEN); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&tracker->filename, + FILE_NAME_LEN, + true); + + tracker->is_saving = true; + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + case SUBMENU_PATTERN_LOAD_SONG: { + FlizzerTrackerEvent event = {.type = EventTypeLoadSong, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + case SUBMENU_PATTERN_SETTINGS: { + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SETTINGS); + break; + } + + default: + break; + } + + break; + } + + case INST_EDITOR_VIEW: { + switch(index) { + case SUBMENU_INSTRUMENT_EXIT: { + tracker->quit = true; + + static InputEvent inevent = {.sequence = 0, .key = InputKeyLeft, .type = InputTypeMAX}; + FlizzerTrackerEvent event = { + .type = EventTypeInput, + .input = inevent, + .period = + 0}; // making an event so tracker does not wait for next keypress and exits immediately + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + case SUBMENU_INSTRUMENT_SAVE: { + text_input_set_header_text(tracker->text_input, "Instrument filename:"); + memset(&tracker->filename, 0, FILE_NAME_LEN); + text_input_set_result_callback( + tracker->text_input, + return_from_keyboard_callback, + tracker, + (char*)&tracker->filename, + FILE_NAME_LEN, + true); + + tracker->is_saving_instrument = true; + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD); + break; + } + + case SUBMENU_INSTRUMENT_LOAD: { + FlizzerTrackerEvent event = { + .type = EventTypeLoadInstrument, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); + break; + } + + default: + break; + } + + break; + } + + default: + break; + } +} + +void submenu_copypaste_callback(void* context, uint32_t index) { + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)context; + + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t current_pattern_index = + tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[tracker->current_channel]; + + TrackerSongPattern* source_pattern; + + if(tracker->source_pattern_index >= 0) { + source_pattern = &tracker->song.pattern[tracker->source_pattern_index]; + } + + TrackerSongPattern* current_pattern = &tracker->song.pattern[current_pattern_index]; + + uint16_t pattern_length = tracker->tracker_engine.song->pattern_length; + + switch(index) { + case SUBMENU_PATTERN_COPYPASTE_COPY: { + tracker->source_pattern_index = current_pattern_index; + tracker->cut_pattern = false; + break; + } + + case SUBMENU_PATTERN_COPYPASTE_PASTE: { + if(tracker->source_pattern_index >= 0) { + memcpy( + current_pattern->step, + source_pattern->step, + sizeof(TrackerSongPatternStep) * pattern_length); + + if(tracker->cut_pattern) { + set_empty_pattern(source_pattern, pattern_length); + tracker->cut_pattern = false; + } + } + break; + } + + case SUBMENU_PATTERN_COPYPASTE_CUT: { + tracker->source_pattern_index = current_pattern_index; + tracker->cut_pattern = true; + break; + } + + case SUBMENU_PATTERN_COPYPASTE_CLEAR: { + set_empty_pattern(current_pattern, pattern_length); + break; + } + + default: + break; + } + + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER); +} + +void audio_output_changed_callback(VariableItem* item) { + FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, audio_modes_text[(index > 1 ? 1 : index)]); + + if(tracker) { + tracker->external_audio = (bool)index; + + tracker->external_audio = audio_modes_values[(index > 1 ? 1 : index)]; + + // sound_engine_init(&tracker->sound_engine, tracker->sound_engine.sample_rate, tracker->external_audio, tracker->sound_engine.audio_buffer_size); + // sound_engine_init_hardware(tracker->sound_engine.sample_rate, tracker->external_audio, tracker->sound_engine.audio_buffer, tracker->sound_engine.audio_buffer_size); + + FlizzerTrackerEvent event = {.type = EventTypeSetAudioMode, .input = {{0}}, .period = 0}; + furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever); + + UNUSED(event); + } +} + +void cycle_focus(FlizzerTrackerApp* tracker) { + switch(tracker->mode) { + case PATTERN_VIEW: { + tracker->focus++; + + if(tracker->focus > EDIT_SONGINFO) { + tracker->focus = EDIT_PATTERN; + } + + break; + } + + case INST_EDITOR_VIEW: { + tracker->focus++; + + if(tracker->focus > EDIT_PROGRAM) { + tracker->focus = EDIT_INSTRUMENT; + + if(tracker->current_digit > 1) { + tracker->current_digit = 1; + } + } + + break; + } + + default: + break; + } +} + +void cycle_view(FlizzerTrackerApp* tracker) { + if(tracker->mode == PATTERN_VIEW) { + tracker->mode = INST_EDITOR_VIEW; + tracker->focus = EDIT_INSTRUMENT; + + tracker->selected_param = 0; + tracker->current_digit = 0; + + return; + } + + if(tracker->mode == INST_EDITOR_VIEW) { + tracker->mode = PATTERN_VIEW; + tracker->focus = EDIT_PATTERN; + + if(tracker->tracker_engine.song == NULL) { + stop_song(tracker); + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + } + + tracker->selected_param = 0; + tracker->current_digit = 0; + + return; + } +} + +void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event) { + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + tracker->showing_help) { + tracker->showing_help = false; + return; + } + + if(tracker->showing_help || tracker->is_loading || tracker->is_saving || + tracker->is_loading_instrument || tracker->is_saving_instrument) + return; //do not react until these are finished + + if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && + event->period > 0 && event->period < 300 && !(tracker->editing)) { + cycle_view(tracker); + stop_song(tracker); + return; + } + + else if( + event->input.key == InputKeyBack && event->input.type == InputTypeShort && + !(tracker->editing)) { + cycle_focus(tracker); + //stop_song(tracker); + return; + } + + if(event->input.key == InputKeyBack && event->input.type == InputTypeLong) { + switch(tracker->mode) { + case PATTERN_VIEW: { + if(tracker->focus == EDIT_PATTERN) { + submenu_set_selected_item( + tracker->pattern_copypaste_submenu, SUBMENU_PATTERN_COPYPASTE_COPY); + view_dispatcher_switch_to_view( + tracker->view_dispatcher, VIEW_SUBMENU_PATTERN_COPYPASTE); + } + + else { + submenu_set_selected_item(tracker->pattern_submenu, SUBMENU_PATTERN_LOAD_SONG); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN); + } + break; + } + + case INST_EDITOR_VIEW: { + submenu_set_selected_item(tracker->instrument_submenu, SUBMENU_INSTRUMENT_LOAD); + view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SUBMENU_INSTRUMENT); + break; + } + + default: + break; + } + + return; + } + + switch(tracker->focus) { + case EDIT_PATTERN: { + pattern_edit_event(tracker, event); + break; + } + + case EDIT_SEQUENCE: { + sequence_edit_event(tracker, event); + break; + } + + case EDIT_SONGINFO: { + songinfo_edit_event(tracker, event); + break; + } + + case EDIT_INSTRUMENT: { + instrument_edit_event(tracker, event); + break; + } + + case EDIT_PROGRAM: { + instrument_program_edit_event(tracker, event); + break; + } + + default: + break; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/input_event.h b/applications/external/flizzer_tracker/input_event.h new file mode 100644 index 000000000..c2b195054 --- /dev/null +++ b/applications/external/flizzer_tracker/input_event.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#include "flizzer_tracker.h" +#include "sound_engine/sound_engine_defs.h" +#include "tracker_engine/tracker_engine_defs.h" +#include "util.h" + +#include "input/instrument.h" +#include "input/instrument_program.h" +#include "input/pattern.h" +#include "input/sequence.h" +#include "input/songinfo.h" + +extern bool audio_modes_values[]; +extern char* audio_modes_text[]; + +void return_from_keyboard_callback(void* ctx); + +void overwrite_file_widget_yes_input_callback(GuiButtonType result, InputType type, void* ctx); +void overwrite_file_widget_no_input_callback(GuiButtonType result, InputType type, void* ctx); + +void overwrite_instrument_file_widget_yes_input_callback( + GuiButtonType result, + InputType type, + void* ctx); +void overwrite_instrument_file_widget_no_input_callback( + GuiButtonType result, + InputType type, + void* ctx); + +uint32_t submenu_exit_callback(void* context); +uint32_t submenu_settings_exit_callback(void* context); +void submenu_callback(void* context, uint32_t index); +void submenu_copypaste_callback(void* context, uint32_t index); +void audio_output_changed_callback(VariableItem* item); +void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/macros.h b/applications/external/flizzer_tracker/macros.h new file mode 100644 index 000000000..0144705c9 --- /dev/null +++ b/applications/external/flizzer_tracker/macros.h @@ -0,0 +1,2 @@ +#define my_min(a, b) (((a) < (b)) ? (a) : (b)) +#define my_max(a, b) (((a) > (b)) ? (a) : (b)) \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/freqs.c b/applications/external/flizzer_tracker/sound_engine/freqs.c new file mode 100644 index 000000000..371dfc210 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/freqs.c @@ -0,0 +1,36 @@ +#include "freqs.h" + +const uint32_t frequency_table[FREQ_TAB_SIZE] = { + (uint32_t)(2093.00 * 1024), // 7th octave, the highest in this tracker + (uint32_t)(2217.46 * 1024), // frequency precision is 1 / 1024th of Hz + (uint32_t)(2349.32 * 1024), + (uint32_t)(2489.02 * 1024), + (uint32_t)(2637.02 * 1024), + (uint32_t)(2793.83 * 1024), + (uint32_t)(2959.96 * 1024), + (uint32_t)(3135.96 * 1024), + (uint32_t)(3322.44 * 1024), + (uint32_t)(3520.00 * 1024), + (uint32_t)(3729.31 * 1024), + (uint32_t)(3951.07 * 1024), +}; + +uint32_t get_freq(uint16_t note) { + if(note >= ((FREQ_TAB_SIZE * 8) << 8)) { + return frequency_table[FREQ_TAB_SIZE - 1]; + } + + if((note & 0xff) == 0) { + return frequency_table[((note >> 8) % 12)] / + (2 << (((NUM_OCTAVES) - ((note >> 8) / 12)) - 2)); // wrap to one octave + } + + else { + uint64_t f1 = frequency_table[((note >> 8) % 12)] / + (uint64_t)(2 << (((NUM_OCTAVES) - ((note >> 8) / 12)) - 2)); + uint64_t f2 = frequency_table[(((note >> 8) + 1) % 12)] / + (uint64_t)(2 << (((NUM_OCTAVES) - (((note >> 8) + 1) / 12)) - 2)); + + return f1 + (uint64_t)((f2 - f1) * (uint64_t)(note & 0xff)) / (uint64_t)256; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/freqs.h b/applications/external/flizzer_tracker/sound_engine/freqs.h new file mode 100644 index 000000000..2b0706739 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/freqs.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +#define FREQ_TAB_SIZE 12 /* one octave */ +#define NUM_OCTAVES 8 /* 0-7th octaves */ + +extern const uint32_t frequency_table[FREQ_TAB_SIZE]; + +uint32_t get_freq(uint16_t note); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine.c b/applications/external/flizzer_tracker/sound_engine/sound_engine.c new file mode 100644 index 000000000..4908c9226 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine.c @@ -0,0 +1,201 @@ +#include "sound_engine.h" +#include "../flizzer_tracker_hal.h" + +#include + +#define PI 3.1415 + +void sound_engine_init( + SoundEngine* sound_engine, + uint32_t sample_rate, + bool external_audio_output, + uint32_t audio_buffer_size) { + if(sound_engine->audio_buffer) { + free(sound_engine->audio_buffer); + } + + if(sound_engine->sine_lut) { + free(sound_engine->sine_lut); + } + + memset(sound_engine, 0, sizeof(SoundEngine)); + + sound_engine->audio_buffer = malloc(audio_buffer_size * sizeof(sound_engine->audio_buffer[0])); + memset(sound_engine->audio_buffer, 0, sizeof(SoundEngine)); + sound_engine->audio_buffer_size = audio_buffer_size; + sound_engine->sample_rate = sample_rate; + sound_engine->external_audio_output = external_audio_output; + + for(int i = 0; i < NUM_CHANNELS; ++i) { + sound_engine->channel[i].lfsr = RANDOM_SEED; + } + + for(int i = 0; i < SINE_LUT_SIZE; ++i) { + sound_engine->sine_lut[i] = (uint8_t)((sinf(i / 64.0 * PI) + 1.0) * 127.0); + } + + furi_hal_interrupt_set_isr_ex( + FuriHalInterruptIdDma1Ch1, 15, sound_engine_dma_isr, sound_engine); + + sound_engine_init_hardware( + sample_rate, external_audio_output, sound_engine->audio_buffer, audio_buffer_size); +} + +void sound_engine_deinit(SoundEngine* sound_engine) { + free(sound_engine->audio_buffer); + + if(!(sound_engine->external_audio_output)) { + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_release(); + } + } + + else { + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } + + furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdDma1Ch1, 13, NULL, NULL); + sound_engine_stop(); + sound_engine_deinit_timer(); +} + +void sound_engine_set_channel_frequency( + SoundEngine* sound_engine, + SoundEngineChannel* channel, + uint16_t note) { + uint32_t frequency = get_freq(note); + + if(frequency != 0) { + channel->frequency = (uint64_t)(ACC_LENGTH) / (uint64_t)1024 * (uint64_t)(frequency) / + (uint64_t)sound_engine->sample_rate; + } + + else { + channel->frequency = 0; + } +} + +void sound_engine_enable_gate(SoundEngine* sound_engine, SoundEngineChannel* channel, bool enable) { + if(enable) { + channel->adsr.envelope = 0; + channel->adsr.envelope_speed = envspd(sound_engine, channel->adsr.a); + channel->adsr.envelope_state = ATTACK; + + channel->flags |= SE_ENABLE_GATE; + + if(channel->flags & SE_ENABLE_KEYDOWN_SYNC) { + channel->accumulator = 0; + } + } + + else { + channel->adsr.envelope_state = RELEASE; + channel->adsr.envelope_speed = envspd(sound_engine, channel->adsr.r); + } +} + +void sound_engine_fill_buffer( + SoundEngine* sound_engine, + uint16_t* audio_buffer, + uint32_t audio_buffer_size) { + int32_t channel_output[NUM_CHANNELS]; + int32_t channel_output_final[NUM_CHANNELS]; + + for(uint32_t i = 0; i < audio_buffer_size; ++i) { + int32_t output = WAVE_AMP * 2; + + for(uint32_t chan = 0; chan < NUM_CHANNELS; ++chan) { + SoundEngineChannel* channel = &sound_engine->channel[chan]; + + if(channel->frequency > 0) { + uint32_t prev_acc = channel->accumulator; + + channel->accumulator += channel->frequency; + + channel->sync_bit |= (channel->accumulator & ACC_LENGTH); + + channel->accumulator &= ACC_LENGTH - 1; + + if(channel->flags & SE_ENABLE_HARD_SYNC) { + uint8_t hard_sync_src = channel->hard_sync == 0xff ? i : channel->hard_sync; + + if(sound_engine->channel[hard_sync_src].sync_bit) { + channel->accumulator = 0; + } + } + + channel_output[chan] = + sound_engine_osc(sound_engine, channel, prev_acc) - WAVE_AMP / 2; + + if(channel->flags & SE_ENABLE_RING_MOD) { + uint8_t ring_mod_src = channel->ring_mod == 0xff ? i : channel->ring_mod; + channel_output[chan] = + channel_output[chan] * channel_output[ring_mod_src] / WAVE_AMP; + } + + channel_output_final[chan] = sound_engine_cycle_and_output_adsr( + channel_output[chan], sound_engine, &channel->adsr, &channel->flags); + + if(channel->flags & SE_ENABLE_FILTER) { + if(channel->filter_mode != 0) { + sound_engine_filter_cycle(&channel->filter, channel_output_final[chan]); + + switch(channel->filter_mode) { + case FIL_OUTPUT_LOWPASS: { + channel_output_final[chan] = + sound_engine_output_lowpass(&channel->filter); + break; + } + + case FIL_OUTPUT_HIGHPASS: { + channel_output_final[chan] = + sound_engine_output_highpass(&channel->filter); + break; + } + + case FIL_OUTPUT_BANDPASS: { + channel_output_final[chan] = + sound_engine_output_bandpass(&channel->filter); + break; + } + + case FIL_OUTPUT_LOW_HIGH: { + channel_output_final[chan] = + sound_engine_output_lowpass(&channel->filter) + + sound_engine_output_highpass(&channel->filter); + break; + } + + case FIL_OUTPUT_HIGH_BAND: { + channel_output_final[chan] = + sound_engine_output_highpass(&channel->filter) + + sound_engine_output_bandpass(&channel->filter); + break; + } + + case FIL_OUTPUT_LOW_BAND: { + channel_output_final[chan] = + sound_engine_output_lowpass(&channel->filter) + + sound_engine_output_bandpass(&channel->filter); + break; + } + + case FIL_OUTPUT_LOW_HIGH_BAND: { + channel_output_final[chan] = + sound_engine_output_lowpass(&channel->filter) + + sound_engine_output_highpass(&channel->filter) + + sound_engine_output_bandpass(&channel->filter); + break; + } + } + } + } + + output += channel_output_final[chan]; + } + } + + //audio_buffer[i] = output / (64 * 4); + audio_buffer[i] = output >> 8; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine.h b/applications/external/flizzer_tracker/sound_engine/sound_engine.h new file mode 100644 index 000000000..a16d02f6d --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine.h @@ -0,0 +1,23 @@ +#pragma once + +#include "freqs.h" +#include "sound_engine_adsr.h" +#include "sound_engine_defs.h" +#include "sound_engine_filter.h" +#include "sound_engine_osc.h" + +void sound_engine_init( + SoundEngine* sound_engine, + uint32_t sample_rate, + bool external_audio_output, + uint32_t audio_buffer_size); +void sound_engine_deinit(SoundEngine* sound_engine); +void sound_engine_set_channel_frequency( + SoundEngine* sound_engine, + SoundEngineChannel* channel, + uint16_t note); +void sound_engine_fill_buffer( + SoundEngine* sound_engine, + uint16_t* audio_buffer, + uint32_t audio_buffer_size); +void sound_engine_enable_gate(SoundEngine* sound_engine, SoundEngineChannel* channel, bool enable); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.c b/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.c new file mode 100644 index 000000000..21eb8f76b --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.c @@ -0,0 +1,58 @@ +#include "sound_engine_adsr.h" + +int32_t sound_engine_cycle_and_output_adsr( + int32_t input, + SoundEngine* eng, + SoundEngineADSR* adsr, + uint16_t* flags) { + switch(adsr->envelope_state) { + case ATTACK: { + adsr->envelope += adsr->envelope_speed; + + if(adsr->envelope >= MAX_ADSR) { + adsr->envelope_state = DECAY; + adsr->envelope = MAX_ADSR; + + adsr->envelope_speed = envspd(eng, adsr->d); + } + + break; + } + + case DECAY: { + if(adsr->envelope > ((uint32_t)adsr->s << 17) + adsr->envelope_speed) { + adsr->envelope -= adsr->envelope_speed; + } + + else { + adsr->envelope = (uint32_t)adsr->s << 17; + adsr->envelope_state = (adsr->s == 0) ? RELEASE : SUSTAIN; + + adsr->envelope_speed = envspd(eng, adsr->r); + } + + break; + } + + case SUSTAIN: + case DONE: { + break; + } + + case RELEASE: { + if(adsr->envelope > adsr->envelope_speed) { + adsr->envelope -= adsr->envelope_speed; + } + + else { + adsr->envelope_state = DONE; + *flags &= ~SE_ENABLE_GATE; + adsr->envelope = 0; + } + + break; + } + } + + return (int32_t)((int32_t)input * (int32_t)(adsr->envelope >> 10) / (int32_t)(MAX_ADSR >> 10) * (int32_t)adsr->volume / (int32_t)MAX_ADSR_VOLUME); +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.h b/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.h new file mode 100644 index 000000000..9b915c68d --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_adsr.h @@ -0,0 +1,9 @@ +#pragma once + +#include "sound_engine_defs.h" + +int32_t sound_engine_cycle_and_output_adsr( + int32_t input, + SoundEngine* eng, + SoundEngineADSR* adsr, + uint16_t* flags); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_defs.h b/applications/external/flizzer_tracker/sound_engine/sound_engine_defs.h new file mode 100644 index 000000000..bfc9b14a7 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_defs.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include + +#define NUM_CHANNELS 4 + +#define RANDOM_SEED 0xf31782ce + +#define ACC_BITS 23 +#define ACC_LENGTH (1 << (ACC_BITS - 1)) + +#define OUTPUT_BITS 16 +#define WAVE_AMP (1 << OUTPUT_BITS) + +#define SINE_LUT_SIZE 256 +#define SINE_LUT_BITDEPTH 8 + +#define MAX_ADSR (0xff << 17) +#define MAX_ADSR_VOLUME 0x80 +#define BASE_FREQ 22050 +#define envspd(eng, slope) \ + ((slope) != 0 ? \ + (((uint64_t)MAX_ADSR / ((slope) * (slope)*256 / 8)) * BASE_FREQ / eng->sample_rate) : \ + ((uint64_t)MAX_ADSR * BASE_FREQ / eng->sample_rate)) + +typedef enum { + SE_WAVEFORM_NONE = 0, + SE_WAVEFORM_NOISE = 1, + SE_WAVEFORM_PULSE = 2, + SE_WAVEFORM_TRIANGLE = 4, + SE_WAVEFORM_SAW = 8, + SE_WAVEFORM_NOISE_METAL = 16, + SE_WAVEFORM_SINE = 32, +} SoundEngineWaveformType; + +typedef enum { + SE_ENABLE_FILTER = 1, + SE_ENABLE_GATE = 2, + SE_ENABLE_RING_MOD = 4, + SE_ENABLE_HARD_SYNC = 8, + SE_ENABLE_KEYDOWN_SYNC = 16, // sync oscillators on keydown +} SoundEngineFlags; + +typedef enum { + FIL_OUTPUT_LOWPASS = 1, + FIL_OUTPUT_HIGHPASS = 2, + FIL_OUTPUT_BANDPASS = 3, + FIL_OUTPUT_LOW_HIGH = 4, + FIL_OUTPUT_HIGH_BAND = 5, + FIL_OUTPUT_LOW_BAND = 6, + FIL_OUTPUT_LOW_HIGH_BAND = 7, + /* ============ */ + FIL_MODES = 8, +} SoundEngineFilterModes; + +typedef enum { + ATTACK = 1, + DECAY = 2, + SUSTAIN = 3, + RELEASE = 4, + DONE = 5, +} SoundEngineEnvelopeStates; + +typedef struct { + uint8_t a, d, s, r, volume, envelope_state; + uint32_t envelope, envelope_speed; +} SoundEngineADSR; + +typedef struct { + int32_t cutoff, resonance, low, high, band; +} SoundEngineFilter; + +typedef struct { + uint32_t accumulator; + uint32_t frequency; + uint8_t waveform; + uint16_t pw; + uint32_t lfsr; + SoundEngineADSR adsr; + + uint16_t flags; + + uint8_t ring_mod, hard_sync; // 0xff = self + uint8_t sync_bit; + + uint8_t filter_mode; + + SoundEngineFilter filter; +} SoundEngineChannel; + +typedef struct { + SoundEngineChannel channel[NUM_CHANNELS]; + uint32_t sample_rate; + uint16_t* audio_buffer; + uint32_t audio_buffer_size; + bool external_audio_output; + uint8_t sine_lut[SINE_LUT_SIZE]; + + // uint32_t counter; //for debug +} SoundEngine; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.c b/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.c new file mode 100644 index 000000000..6a02aa15a --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.c @@ -0,0 +1,28 @@ +#include "sound_engine_filter.h" + +void sound_engine_filter_set_coeff(SoundEngineFilter* flt, uint32_t frequency, uint16_t resonance) { + flt->cutoff = (frequency << 5); + flt->resonance = ((int32_t)resonance * 11 / 6) - 200; +} + +void sound_engine_filter_cycle( + SoundEngineFilter* flt, + int32_t input) // don't ask me how it works, stolen from Furnace tracker TSU synth +{ + input /= 8; + flt->low = flt->low + ((flt->cutoff * flt->band) >> 16); + flt->high = input - flt->low - (((256 - flt->resonance) * flt->band) >> 8); + flt->band = ((flt->cutoff * flt->high) >> 16) + flt->band; +} + +int32_t sound_engine_output_lowpass(SoundEngineFilter* flt) { + return flt->low * 8; +} + +int32_t sound_engine_output_highpass(SoundEngineFilter* flt) { + return flt->high * 8; +} + +int32_t sound_engine_output_bandpass(SoundEngineFilter* flt) { + return flt->band * 8; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.h b/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.h new file mode 100644 index 000000000..43d4e3966 --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_filter.h @@ -0,0 +1,9 @@ +#pragma once + +#include "sound_engine_defs.h" + +void sound_engine_filter_set_coeff(SoundEngineFilter* flt, uint32_t frequency, uint16_t resonance); +void sound_engine_filter_cycle(SoundEngineFilter* flt, int32_t input); +int32_t sound_engine_output_lowpass(SoundEngineFilter* flt); +int32_t sound_engine_output_highpass(SoundEngineFilter* flt); +int32_t sound_engine_output_bandpass(SoundEngineFilter* flt); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.c b/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.c new file mode 100644 index 000000000..b3fd2fe7b --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.c @@ -0,0 +1,278 @@ +#include "sound_engine_osc.h" + +static inline uint16_t sound_engine_pulse(uint32_t acc, uint32_t pw) // 0-FFF pulse width range +{ + return ( + ((acc >> (((uint32_t)ACC_BITS - 17))) >= ((pw == 0xfff ? pw + 1 : pw) << 4) ? + (WAVE_AMP - 1) : + 0)); +} + +static inline uint16_t sound_engine_saw(uint32_t acc) { + return (acc >> (ACC_BITS - OUTPUT_BITS - 1)) & (WAVE_AMP - 1); +} + +uint16_t sound_engine_triangle(uint32_t acc) { + return ( + (((acc & (ACC_LENGTH / 2)) ? ~acc : acc) >> (ACC_BITS - OUTPUT_BITS - 2)) & + (WAVE_AMP * 2 - 1)); +} + +static inline uint16_t sound_engine_sine(uint32_t acc, SoundEngine* sound_engine) { + return ( + (uint16_t)sound_engine->sine_lut[(acc >> (ACC_BITS - SINE_LUT_BITDEPTH))] + << (OUTPUT_BITS - SINE_LUT_BITDEPTH)); +} + +inline static void shift_lfsr(uint32_t* v, uint32_t tap_0, uint32_t tap_1) { + typedef uint32_t T; + const T zero = (T)(0); + const T lsb = zero + (T)(1); + const T feedback = ((lsb << (tap_0)) ^ (lsb << (tap_1))); + + *v = (*v >> 1) ^ ((zero - (*v & lsb)) & feedback); +} + +static inline uint16_t sound_engine_noise(SoundEngineChannel* channel, uint32_t prev_acc) { + if((prev_acc & (ACC_LENGTH / 32)) != (channel->accumulator & (ACC_LENGTH / 32))) { + if(channel->waveform & SE_WAVEFORM_NOISE_METAL) { + shift_lfsr(&channel->lfsr, 14, 8); + channel->lfsr &= (1 << (14 + 1)) - 1; + } + + else { + shift_lfsr(&channel->lfsr, 22, 17); + channel->lfsr &= (1 << (22 + 1)) - 1; + } + } + + return (channel->lfsr) & (WAVE_AMP - 1); +} + +uint16_t + sound_engine_osc(SoundEngine* sound_engine, SoundEngineChannel* channel, uint32_t prev_acc) { + switch(channel->waveform) { + case SE_WAVEFORM_NOISE: + case SE_WAVEFORM_NOISE_METAL: + case(SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_noise(channel, prev_acc); + break; + } + + case SE_WAVEFORM_PULSE: { + return sound_engine_pulse(channel->accumulator, channel->pw); + break; + } + + case SE_WAVEFORM_TRIANGLE: { + return sound_engine_triangle(channel->accumulator); + break; + } + + case SE_WAVEFORM_SAW: { + return sound_engine_saw(channel->accumulator); + break; + } + + case SE_WAVEFORM_SINE: { + return sound_engine_sine(channel->accumulator, sound_engine); + break; + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_triangle(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator); + } + + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_noise(channel, prev_acc) & sound_engine_triangle(channel->accumulator); + } + + case(SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_saw(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_saw(channel->accumulator); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_saw(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW): { + return sound_engine_triangle(channel->accumulator) & + sound_engine_saw(channel->accumulator); + } + + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_triangle(channel->accumulator) & + sound_engine_saw(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_saw(channel->accumulator); + } + + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | + SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_saw(channel->accumulator) & sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE): { + return sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_PULSE | SE_WAVEFORM_NOISE | + SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_SAW): { + return sound_engine_saw(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_saw(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_saw(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | + SE_WAVEFORM_NOISE_METAL): { + return sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_saw(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW): { + return sound_engine_saw(channel->accumulator) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE): + case(SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | SE_WAVEFORM_NOISE | + SE_WAVEFORM_NOISE_METAL): { + return sound_engine_saw(channel->accumulator) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + case(SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW): { + return sound_engine_saw(channel->accumulator) & + sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine); + } + + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | + SE_WAVEFORM_NOISE): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | + SE_WAVEFORM_NOISE_METAL): + case( + SE_WAVEFORM_SINE | SE_WAVEFORM_PULSE | SE_WAVEFORM_TRIANGLE | SE_WAVEFORM_SAW | + SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL): { + return sound_engine_saw(channel->accumulator) & + sound_engine_pulse(channel->accumulator, channel->pw) & + sound_engine_triangle(channel->accumulator) & + sound_engine_sine(channel->accumulator, sound_engine) & + sound_engine_noise(channel, prev_acc); + } + + default: + break; + } + + return WAVE_AMP / 2; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.h b/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.h new file mode 100644 index 000000000..97973372e --- /dev/null +++ b/applications/external/flizzer_tracker/sound_engine/sound_engine_osc.h @@ -0,0 +1,8 @@ +#pragma once + +#include "sound_engine_defs.h" + +uint16_t sound_engine_triangle(uint32_t acc); + +uint16_t + sound_engine_osc(SoundEngine* sound_engine, SoundEngineChannel* channel, uint32_t prev_acc); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/diskop.c b/applications/external/flizzer_tracker/tracker_engine/diskop.c new file mode 100644 index 000000000..46de56774 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/diskop.c @@ -0,0 +1,127 @@ +#include "diskop.h" + +void load_instrument_inner(Stream* stream, Instrument* inst, uint8_t version) { + UNUSED(version); + + size_t rwops = stream_read(stream, (uint8_t*)inst->name, sizeof(inst->name)); + rwops = stream_read(stream, (uint8_t*)&inst->waveform, sizeof(inst->waveform)); + rwops = stream_read(stream, (uint8_t*)&inst->flags, sizeof(inst->flags)); + rwops = + stream_read(stream, (uint8_t*)&inst->sound_engine_flags, sizeof(inst->sound_engine_flags)); + + rwops = stream_read(stream, (uint8_t*)&inst->base_note, sizeof(inst->base_note)); + rwops = stream_read(stream, (uint8_t*)&inst->finetune, sizeof(inst->finetune)); + + rwops = stream_read(stream, (uint8_t*)&inst->slide_speed, sizeof(inst->slide_speed)); + + rwops = stream_read(stream, (uint8_t*)&inst->adsr, sizeof(inst->adsr)); + rwops = stream_read(stream, (uint8_t*)&inst->pw, sizeof(inst->pw)); + + if(inst->sound_engine_flags & SE_ENABLE_RING_MOD) { + rwops = stream_read(stream, (uint8_t*)&inst->ring_mod, sizeof(inst->ring_mod)); + } + + if(inst->sound_engine_flags & SE_ENABLE_HARD_SYNC) { + rwops = stream_read(stream, (uint8_t*)&inst->hard_sync, sizeof(inst->hard_sync)); + } + + uint8_t progsteps = 0; + + rwops = stream_read(stream, (uint8_t*)&progsteps, sizeof(progsteps)); + + if(progsteps > 0) { + rwops = stream_read(stream, (uint8_t*)inst->program, progsteps * sizeof(inst->program[0])); + } + + rwops = stream_read(stream, (uint8_t*)&inst->program_period, sizeof(inst->program_period)); + + if(inst->flags & TE_ENABLE_VIBRATO) { + rwops = stream_read(stream, (uint8_t*)&inst->vibrato_speed, sizeof(inst->vibrato_speed)); + rwops = stream_read(stream, (uint8_t*)&inst->vibrato_depth, sizeof(inst->vibrato_depth)); + rwops = stream_read(stream, (uint8_t*)&inst->vibrato_delay, sizeof(inst->vibrato_delay)); + } + + if(inst->flags & TE_ENABLE_PWM) { + rwops = stream_read(stream, (uint8_t*)&inst->pwm_speed, sizeof(inst->pwm_speed)); + rwops = stream_read(stream, (uint8_t*)&inst->pwm_depth, sizeof(inst->pwm_depth)); + rwops = stream_read(stream, (uint8_t*)&inst->pwm_delay, sizeof(inst->pwm_delay)); + } + + if(inst->sound_engine_flags & SE_ENABLE_FILTER) { + rwops = stream_read(stream, (uint8_t*)&inst->filter_cutoff, sizeof(inst->filter_cutoff)); + rwops = + stream_read(stream, (uint8_t*)&inst->filter_resonance, sizeof(inst->filter_resonance)); + rwops = stream_read(stream, (uint8_t*)&inst->filter_type, sizeof(inst->filter_type)); + } + + UNUSED(rwops); +} + +bool load_song_inner(TrackerSong* song, Stream* stream) { + uint8_t version = 0; + size_t rwops = stream_read(stream, (uint8_t*)&version, sizeof(version)); + + if(version > + TRACKER_ENGINE_VERSION) // if song is of newer version this version of tracker engine can't support + { + return false; + } + + tracker_engine_deinit_song(song, false); + memset(song, 0, sizeof(TrackerSong)); + + rwops = stream_read(stream, (uint8_t*)song->song_name, sizeof(song->song_name)); + rwops = stream_read(stream, (uint8_t*)&song->loop_start, sizeof(song->loop_start)); + rwops = stream_read(stream, (uint8_t*)&song->loop_end, sizeof(song->loop_end)); + rwops = stream_read(stream, (uint8_t*)&song->pattern_length, sizeof(song->pattern_length)); + + rwops = stream_read(stream, (uint8_t*)&song->speed, sizeof(song->speed)); + rwops = stream_read(stream, (uint8_t*)&song->rate, sizeof(song->rate)); + + rwops = + stream_read(stream, (uint8_t*)&song->num_sequence_steps, sizeof(song->num_sequence_steps)); + + for(uint16_t i = 0; i < song->num_sequence_steps; i++) { + rwops = stream_read( + stream, + (uint8_t*)&song->sequence.sequence_step[i], + sizeof(song->sequence.sequence_step[0])); + } + + rwops = stream_read(stream, (uint8_t*)&song->num_patterns, sizeof(song->num_patterns)); + + for(uint16_t i = 0; i < song->num_patterns; i++) { + song->pattern[i].step = (TrackerSongPatternStep*)malloc( + sizeof(TrackerSongPatternStep) * (song->pattern_length)); + set_empty_pattern(&song->pattern[i], song->pattern_length); + rwops = stream_read( + stream, + (uint8_t*)song->pattern[i].step, + sizeof(TrackerSongPatternStep) * (song->pattern_length)); + } + + rwops = stream_read(stream, (uint8_t*)&song->num_instruments, sizeof(song->num_instruments)); + + for(uint16_t i = 0; i < song->num_instruments; i++) { + song->instrument[i] = (Instrument*)malloc(sizeof(Instrument)); + set_default_instrument(song->instrument[i]); + load_instrument_inner(stream, song->instrument[i], version); + } + + UNUSED(rwops); + return false; +} + +bool load_song(TrackerSong* song, Stream* stream) { + char header[sizeof(SONG_FILE_SIG) + 2] = {0}; + size_t rwops = stream_read(stream, (uint8_t*)&header, sizeof(SONG_FILE_SIG) - 1); + header[sizeof(SONG_FILE_SIG)] = '\0'; + + if(strcmp(header, SONG_FILE_SIG) == 0) { + bool result = load_song_inner(song, stream); + UNUSED(result); + } + + UNUSED(rwops); + return false; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/diskop.h b/applications/external/flizzer_tracker/tracker_engine/diskop.h new file mode 100644 index 000000000..bed7c0190 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/diskop.h @@ -0,0 +1,12 @@ +#pragma once + +#include "tracker_engine.h" +#include "tracker_engine_defs.h" +#include +#include +#include +#include + +bool load_song(TrackerSong* song, Stream* stream); +bool load_instrument(Instrument* inst, Stream* stream); +void load_instrument_inner(Stream* stream, Instrument* inst, uint8_t version); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/do_effects.c b/applications/external/flizzer_tracker/tracker_engine/do_effects.c new file mode 100644 index 000000000..a556840c2 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/do_effects.c @@ -0,0 +1,466 @@ +#include "do_effects.h" +#include + +#include "../sound_engine/sound_engine.h" +#include "../sound_engine/sound_engine_filter.h" +#include "tracker_engine.h" + +void do_command( + uint16_t opcode, + TrackerEngine* tracker_engine, + uint8_t channel, + uint8_t tick, + bool from_program) { + UNUSED(from_program); + + TrackerEngineChannel* te_channel = &tracker_engine->channel[channel]; + SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[channel]; + + switch(opcode & 0x7f00) { + case TE_EFFECT_ARPEGGIO: { + if(tick == 0) { + if(te_channel->fixed_note != 0xffff) { + te_channel->note = te_channel->last_note; + te_channel->fixed_note = 0xffff; + } + + if((opcode & 0xff) == 0xf0) + te_channel->arpeggio_note = te_channel->extarp1; + else if((opcode & 0xff) == 0xf1) + te_channel->arpeggio_note = te_channel->extarp2; + else + te_channel->arpeggio_note = (opcode & 0xff); + } + break; + } + + case TE_EFFECT_PORTAMENTO_UP: { + uint32_t prev = te_channel->note; + + te_channel->note += ((opcode & 0xff) << 2); + if(prev > te_channel->note) te_channel->note = 0xffff; + + te_channel->target_note = te_channel->note; + break; + } + + case TE_EFFECT_PORTAMENTO_DOWN: { + int32_t prev = te_channel->note; + + te_channel->note -= ((opcode & 0xff) << 2); + if(prev < te_channel->note) te_channel->note = 0; + + te_channel->target_note = te_channel->note; + break; + } + + case TE_EFFECT_VIBRATO: { + if(tick == 0) { + if(opcode & 0xff) { + te_channel->flags |= TE_ENABLE_VIBRATO; + + te_channel->vibrato_speed = (opcode & 0xf0); + te_channel->vibrato_depth = ((opcode & 0x0f) << 4); + } + + else { + te_channel->flags &= ~(TE_ENABLE_VIBRATO); + } + } + + break; + } + + case TE_EFFECT_PWM: { + if(tick == 0) { + if(opcode & 0xff) { + te_channel->flags |= TE_ENABLE_PWM; + + te_channel->pwm_speed = (opcode & 0xf0); + te_channel->pwm_depth = ((opcode & 0x0f) << 4); + } + + else { + te_channel->flags &= ~(TE_ENABLE_PWM); + } + } + + break; + } + + case TE_EFFECT_SET_PW: { + if(tick == 0) { + te_channel->pw = ((opcode & 0xff) << 4); + } + + break; + } + + case TE_EFFECT_PW_UP: { + int16_t temp_pw = te_channel->pw + (int16_t)(opcode & 0xff); + + if(temp_pw < 0) temp_pw = 0; + if(temp_pw > 0xfff) temp_pw = 0xfff; + + te_channel->pw = temp_pw; + + break; + } + + case TE_EFFECT_PW_DOWN: { + int16_t temp_pw = te_channel->pw - (int16_t)(opcode & 0xff); + + if(temp_pw < 0) temp_pw = 0; + if(temp_pw > 0xfff) temp_pw = 0xfff; + + te_channel->pw = temp_pw; + + break; + } + + case TE_EFFECT_SET_CUTOFF: { + if(tick == 0) { + te_channel->filter_cutoff = ((opcode & 0xff) << 3); + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + } + + break; + } + + case TE_EFFECT_VOLUME_FADE: { + if(!(te_channel->channel_flags & TEC_DISABLED)) { + te_channel->volume -= (opcode & 0xf); + if(te_channel->volume > MAX_ADSR_VOLUME) te_channel->volume = 0; + te_channel->volume += ((opcode >> 4) & 0xf); + if(te_channel->volume > MAX_ADSR_VOLUME) te_channel->volume = MAX_ADSR_VOLUME; + + se_channel->adsr.volume = (int32_t)te_channel->volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + + break; + } + + case TE_EFFECT_SET_WAVEFORM: { + if(tick == 0) { + se_channel->waveform = (opcode & 0x3f); + } + + break; + } + + case TE_EFFECT_SET_VOLUME: { + if(tick == 0) { + if(!(te_channel->channel_flags & TEC_DISABLED)) { + te_channel->volume = opcode & 0xff; + + se_channel->adsr.volume = (int32_t)te_channel->volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + } + + break; + } + + case TE_EFFECT_EXT: { + switch(opcode & 0x7ff0) { + case TE_EFFECT_EXT_TOGGLE_FILTER: { + if(tick == 0) { + if(opcode & 0xf) { + se_channel->flags |= SE_ENABLE_FILTER; + } + + else { + se_channel->flags &= ~SE_ENABLE_FILTER; + } + } + + break; + } + + case TE_EFFECT_EXT_PORTA_DN: { + if(tick == 0) { + int32_t prev = te_channel->note; + + te_channel->note -= (opcode & 0xf); + if(prev < te_channel->note) te_channel->note = 0; + + te_channel->target_note = te_channel->note; + } + + break; + } + + case TE_EFFECT_EXT_PORTA_UP: { + if(tick == 0) { + uint32_t prev = te_channel->note; + + te_channel->note += (opcode & 0xf); + if(prev > te_channel->note) te_channel->note = 0xffff; + + te_channel->target_note = te_channel->note; + } + + break; + } + + case TE_EFFECT_EXT_FILTER_MODE: { + if(tick == 0) { + se_channel->filter_mode = (opcode & 0xf); + } + + break; + } + + case TE_EFFECT_EXT_RETRIGGER: { + if((opcode & 0xf) > 0 && (tick % (opcode & 0xf)) == 0) { + uint8_t prev_vol_tr = te_channel->volume; + uint8_t prev_vol_cyd = se_channel->adsr.volume; + tracker_engine_trigger_instrument_internal( + tracker_engine, channel, te_channel->instrument, te_channel->last_note); + te_channel->volume = prev_vol_tr; + se_channel->adsr.volume = prev_vol_cyd; + } + + break; + } + + case TE_EFFECT_EXT_FINE_VOLUME_DOWN: { + if(tick == 0) { + te_channel->volume -= (opcode & 0xf); + + if(te_channel->volume > MAX_ADSR_VOLUME) te_channel->volume = 0; + + se_channel->adsr.volume = (int32_t)te_channel->volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + + break; + } + + case TE_EFFECT_EXT_FINE_VOLUME_UP: { + if(tick == 0) { + te_channel->volume += (opcode & 0xf); + + if(te_channel->volume > MAX_ADSR_VOLUME) te_channel->volume = MAX_ADSR_VOLUME; + + se_channel->adsr.volume = (int32_t)te_channel->volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + + break; + } + + case TE_EFFECT_EXT_NOTE_CUT: { + if((opcode & 0xf) <= tick) { + se_channel->adsr.volume = 0; + te_channel->volume = 0; + } + + break; + } + + case TE_EFFECT_EXT_PHASE_RESET: { + if(tick == (opcode & 0xf)) { + se_channel->accumulator = 0; + se_channel->lfsr = RANDOM_SEED; + } + + break; + } + } + + break; + } + + case TE_EFFECT_SET_SPEED_PROG_PERIOD: { + if(tick == 0) { + if(from_program) { + te_channel->program_period = opcode & 0xff; + } + + else { + tracker_engine->song->speed = opcode & 0xff; + } + } + + break; + } + + case TE_EFFECT_CUTOFF_UP: { + te_channel->filter_cutoff += (opcode & 0xff); + + if(te_channel->filter_cutoff > 0x7ff) { + te_channel->filter_cutoff = 0x7ff; + } + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + + break; + } + + case TE_EFFECT_CUTOFF_DOWN: { + te_channel->filter_cutoff -= (opcode & 0xff); + + if(te_channel->filter_cutoff > 0x7ff) // unsigned int overflow + { + te_channel->filter_cutoff = 0; + } + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + + break; + } + + case TE_EFFECT_SET_RESONANCE: { + if(tick == 0) { + te_channel->filter_resonance = (opcode & 0xff); + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + } + + break; + } + + case TE_EFFECT_RESONANCE_UP: { + te_channel->filter_resonance += (opcode & 0xff); + + if(te_channel->filter_resonance > 0xff) { + te_channel->filter_resonance = 0xff; + } + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + break; + } + + case TE_EFFECT_RESONANCE_DOWN: { + te_channel->filter_resonance -= (opcode & 0xff); + + if(te_channel->filter_resonance > 0xff) { + te_channel->filter_resonance = 0; + } + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + break; + } + + case TE_EFFECT_SET_RING_MOD_SRC: { + if(tick == 0) { + se_channel->ring_mod = (opcode & 0xff); + } + + break; + } + + case TE_EFFECT_SET_HARD_SYNC_SRC: { + if(tick == 0) { + se_channel->hard_sync = (opcode & 0xff); + } + + break; + } + + case TE_EFFECT_SET_ATTACK: { + if(tick == 0) { + se_channel->adsr.a = (opcode & 0xff); + + if(se_channel->adsr.envelope_state == ATTACK) { + se_channel->adsr.envelope_speed = + envspd(tracker_engine->sound_engine, se_channel->adsr.a); + } + } + + break; + } + + case TE_EFFECT_SET_DECAY: { + if(tick == 0) { + se_channel->adsr.d = (opcode & 0xff); + + if(se_channel->adsr.envelope_state == DECAY) { + se_channel->adsr.envelope_speed = + envspd(tracker_engine->sound_engine, se_channel->adsr.d); + } + } + + break; + } + + case TE_EFFECT_SET_SUSTAIN: { + if(tick == 0) { + se_channel->adsr.s = (opcode & 0xff); + } + + break; + } + + case TE_EFFECT_SET_RELEASE: { + if(tick == 0) { + se_channel->adsr.r = (opcode & 0xff); + + if(se_channel->adsr.envelope_state == RELEASE) { + se_channel->adsr.envelope_speed = + envspd(tracker_engine->sound_engine, se_channel->adsr.r); + } + } + + break; + } + + case TE_EFFECT_PROGRAM_RESTART: { + if(tick == 0) { + te_channel->program_counter = 0; + te_channel->program_loop = 0; + te_channel->program_period = 0; + te_channel->program_tick = 0; + } + + break; + } + + case TE_EFFECT_PORTA_UP_SEMITONE: { + uint32_t prev = te_channel->note; + + te_channel->note += ((opcode & 0xff) << 8); + if(prev > te_channel->note) te_channel->note = 0xffff; + + te_channel->target_note = te_channel->note; + break; + } + + case TE_EFFECT_PORTA_DOWN_SEMITONE: { + int32_t prev = te_channel->note; + + te_channel->note -= ((opcode & 0xff) << 8); + if(prev < te_channel->note) te_channel->note = 0; + + te_channel->target_note = te_channel->note; + break; + } + + case TE_EFFECT_ARPEGGIO_ABS: { + te_channel->arpeggio_note = 0; + te_channel->fixed_note = ((opcode & 0xff) << 8); + + break; + } + + case TE_EFFECT_TRIGGER_RELEASE: { + sound_engine_enable_gate(tracker_engine->sound_engine, se_channel, 0); + + break; + } + + default: + break; + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/do_effects.h b/applications/external/flizzer_tracker/tracker_engine/do_effects.h new file mode 100644 index 000000000..654a61765 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/do_effects.h @@ -0,0 +1,10 @@ +#include "tracker_engine_defs.h" +#include +#include + +void do_command( + uint16_t opcode, + TrackerEngine* tracker_engine, + uint8_t channel, + uint8_t tick, + bool from_program); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/tracker_engine.c b/applications/external/flizzer_tracker/tracker_engine/tracker_engine.c new file mode 100644 index 000000000..5958e3a05 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/tracker_engine.c @@ -0,0 +1,696 @@ +#include "tracker_engine.h" + +#include "../flizzer_tracker_hal.h" +#include "../macros.h" + +#include "../sound_engine/sound_engine_osc.h" +#include + +void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngine* sound_engine) { + memset(tracker_engine, 0, sizeof(TrackerEngine)); + + furi_hal_interrupt_set_isr_ex( + FuriHalInterruptIdTIM2, 14, tracker_engine_timer_isr, (void*)tracker_engine); + tracker_engine_init_hardware(rate); + + tracker_engine->sound_engine = sound_engine; + tracker_engine->rate = rate; +} + +void tracker_engine_deinit_song(TrackerSong* song, bool free_song) { + for(int i = 0; i < MAX_PATTERNS; i++) { + if(song->pattern[i].step != NULL) { + free(song->pattern[i].step); + } + } + + for(int i = 0; i < MAX_INSTRUMENTS; i++) { + if(song->instrument[i] != NULL) { + free(song->instrument[i]); + } + } + + if(free_song) { + free(song); + } +} + +void tracker_engine_deinit(TrackerEngine* tracker_engine, bool free_song) { + tracker_engine_deinit_song(tracker_engine->song, free_song); + + furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdTIM2, 13, NULL, NULL); + tracker_engine_stop(); +} + +void set_note(TrackerSongPatternStep* step, uint8_t note) { + step->note &= 0x80; + step->note |= (note & 0x7f); +} + +void set_instrument(TrackerSongPatternStep* step, uint8_t inst) { + step->note &= 0x7f; + step->inst_vol &= 0x0f; + + step->note |= ((inst & 0x10) << 3); + step->inst_vol |= ((inst & 0xf) << 4); +} + +void set_volume(TrackerSongPatternStep* step, uint8_t vol) { + step->command &= 0x7fff; + step->inst_vol &= 0xf0; + + step->command |= ((vol & 0x10) << 11); + step->inst_vol |= (vol & 0xf); +} + +void set_command(TrackerSongPatternStep* step, uint16_t command) { + step->command &= 0x8000; + step->command |= command & (0x7fff); +} + +void set_default_instrument(Instrument* inst) { + memset(inst, 0, sizeof(Instrument)); + + inst->flags = TE_SET_CUTOFF | TE_SET_PW | TE_ENABLE_VIBRATO; + inst->sound_engine_flags = SE_ENABLE_KEYDOWN_SYNC; + + inst->base_note = MIDDLE_C; + + inst->waveform = SE_WAVEFORM_PULSE; + inst->pw = 0x80; + + inst->adsr.a = 0x4; + inst->adsr.d = 0x28; + inst->adsr.volume = 0x80; + + inst->filter_type = FIL_OUTPUT_LOWPASS; + inst->filter_cutoff = 0xff; + + inst->program_period = 1; + + for(int i = 0; i < INST_PROG_LEN; i++) { + inst->program[i] = TE_PROGRAM_NOP; + } + + inst->vibrato_speed = 0x60; + inst->vibrato_depth = 0x20; + inst->vibrato_delay = 0x20; +} + +void set_empty_pattern(TrackerSongPattern* pattern, uint16_t pattern_length) { + for(uint16_t i = 0; i < pattern_length; i++) { + TrackerSongPatternStep* step = &pattern->step[i]; + + set_note(step, MUS_NOTE_NONE); + set_instrument(step, MUS_NOTE_INSTRUMENT_NONE); + set_volume(step, MUS_NOTE_VOLUME_NONE); + set_command(step, 0); + } +} + +uint8_t tracker_engine_get_note(TrackerSongPatternStep* step) { + return (step->note & 0x7f); +} + +uint8_t tracker_engine_get_instrument(TrackerSongPatternStep* step) { + return ((step->note & 0x80) >> 3) | ((step->inst_vol & 0xf0) >> 4); +} + +uint8_t tracker_engine_get_volume(TrackerSongPatternStep* step) { + return (step->inst_vol & 0xf) | ((step->command & 0x8000) >> 11); +} + +uint16_t tracker_engine_get_command(TrackerSongPatternStep* step) { + return (step->command & 0x7fff); +} + +void tracker_engine_set_note( + TrackerEngine* tracker_engine, + uint8_t chan, + uint16_t note, + bool update_note) { + if(update_note) tracker_engine->channel[chan].note = note; + + sound_engine_set_channel_frequency( + tracker_engine->sound_engine, &tracker_engine->sound_engine->channel[chan], note); +} + +void tracker_engine_set_song(TrackerEngine* tracker_engine, TrackerSong* song) { + tracker_engine->song = song; +} + +void tracker_engine_trigger_instrument_internal( + TrackerEngine* tracker_engine, + uint8_t chan, + Instrument* pinst, + uint16_t note) { + SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[chan]; + TrackerEngineChannel* te_channel = &tracker_engine->channel[chan]; + + te_channel->channel_flags = TEC_PLAYING | (te_channel->channel_flags & TEC_DISABLED); + + te_channel->program_period = pinst->program_period; + + if(!(pinst->flags & TE_PROG_NO_RESTART) && pinst->program_period > 0) { + te_channel->channel_flags |= TEC_PROGRAM_RUNNING; + + te_channel->program_counter = 0; + te_channel->program_loop = 1; + te_channel->program_tick = 0; + } + + te_channel->instrument = pinst; + + se_channel->waveform = pinst->waveform; + se_channel->flags = pinst->sound_engine_flags; + + te_channel->flags = pinst->flags; + + te_channel->arpeggio_note = 0; + te_channel->fixed_note = 0xffff; + + note += (uint16_t)(((int16_t)pinst->base_note - MIDDLE_C) << 8); + tracker_engine_set_note(tracker_engine, chan, note + (int16_t)pinst->finetune, true); + + te_channel->last_note = te_channel->target_note = note + (int16_t)pinst->finetune; + + te_channel->extarp1 = te_channel->extarp2 = 0; + + if(pinst->flags & TE_ENABLE_VIBRATO) { + te_channel->vibrato_speed = pinst->vibrato_speed; + te_channel->vibrato_depth = pinst->vibrato_depth; + te_channel->vibrato_delay = pinst->vibrato_delay; + } + + if(pinst->flags & TE_ENABLE_PWM) { + te_channel->pwm_speed = pinst->pwm_speed; + te_channel->pwm_depth = pinst->pwm_depth; + te_channel->pwm_delay = pinst->pwm_delay; + } + + if(pinst->sound_engine_flags & SE_ENABLE_KEYDOWN_SYNC) { + te_channel->vibrato_position = ((ACC_LENGTH / 2 / 2) << 9); + te_channel->pwm_position = ((ACC_LENGTH / 2 / 2) << 9); + + se_channel->accumulator = 0; + se_channel->lfsr = RANDOM_SEED; + } + + if(pinst->flags & TE_SET_CUTOFF) { + te_channel->filter_cutoff = ((uint16_t)pinst->filter_cutoff << 3); + te_channel->filter_resonance = (uint16_t)pinst->filter_resonance; + + se_channel->filter.low = 0; + se_channel->filter.high = 0; + se_channel->filter.band = 0; + + sound_engine_filter_set_coeff( + &se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance); + } + + if(pinst->sound_engine_flags & SE_ENABLE_FILTER) { + te_channel->filter_type = pinst->filter_type; + se_channel->filter_mode = te_channel->filter_type; + } + + if(pinst->flags & TE_SET_PW) { + te_channel->pw = (pinst->pw << 4); + se_channel->pw = (pinst->pw << 4); + } + + se_channel->ring_mod = pinst->ring_mod; + se_channel->hard_sync = pinst->hard_sync; + + te_channel->slide_speed = pinst->slide_speed; + + se_channel->adsr.a = pinst->adsr.a; + se_channel->adsr.d = pinst->adsr.d; + se_channel->adsr.s = pinst->adsr.s; + se_channel->adsr.r = pinst->adsr.r; + se_channel->adsr.volume = pinst->adsr.volume; + se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * + (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + + te_channel->volume = pinst->adsr.volume; + te_channel->volume = + (int32_t)te_channel->volume * (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + + sound_engine_enable_gate( + tracker_engine->sound_engine, &tracker_engine->sound_engine->channel[chan], true); +} + +void tracker_engine_execute_track_command( + TrackerEngine* tracker_engine, + uint8_t chan, + TrackerSongPatternStep* step, + bool first_tick) { + UNUSED(first_tick); + UNUSED(tracker_engine); + UNUSED(chan); + + uint8_t vol = tracker_engine_get_volume(step); + uint16_t opcode = tracker_engine_get_command(step); + + if(vol != MUS_NOTE_VOLUME_NONE && + !(tracker_engine->channel[chan].channel_flags & TEC_DISABLED)) { + tracker_engine->sound_engine->channel[chan].adsr.volume = + (int32_t)tracker_engine->channel[chan].volume * (int32_t)vol / (MUS_NOTE_VOLUME_NONE); + // tracker_engine->sound_engine->channel[chan].adsr.volume = (int32_t)tracker_engine->sound_engine->channel[chan].adsr.volume * (int32_t)tracker_engine->channel[chan].instrument->adsr.volume / MAX_ADSR_VOLUME * (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME; + } + + if(tracker_engine->channel[chan].instrument != NULL && opcode != 0) { + if((opcode & 0x7f00) == TE_EFFECT_ARPEGGIO) { + tracker_engine->channel[chan].extarp1 = ((opcode & 0xf0) >> 4); + tracker_engine->channel[chan].extarp2 = (opcode & 0xf); + } + + else { + do_command(opcode, tracker_engine, chan, tracker_engine->current_tick, false); + } + } + + if(tracker_engine->channel[chan].channel_flags & TEC_DISABLED) { + tracker_engine->sound_engine->channel[chan].adsr.volume = 0; + } +} + +void tracker_engine_execute_program_tick( + TrackerEngine* tracker_engine, + uint8_t chan, + uint8_t advance) { + TrackerEngineChannel* te_channel = &tracker_engine->channel[chan]; + uint8_t tick = te_channel->program_tick; + uint8_t visited[INST_PROG_LEN] = {0}; + +do_it_again:; + + const uint16_t inst = te_channel->instrument->program[tick]; + + if((inst & 0x7fff) == TE_PROGRAM_END) { + te_channel->channel_flags &= ~(TEC_PROGRAM_RUNNING); + return; + } + + uint8_t dont_reloop = 0; + + if((inst & 0x7fff) != TE_PROGRAM_NOP) { + switch(inst & 0x7f00) { + case TE_PROGRAM_JUMP: { + if(!visited[tick]) { + visited[tick] = 1; + tick = inst & (INST_PROG_LEN - 1); + } + + else + return; + + break; + } + + case TE_PROGRAM_LOOP_BEGIN: + break; + + case TE_PROGRAM_LOOP_END: { + if(te_channel->program_loop == (inst & 0xff)) { + if(advance) te_channel->program_loop = 1; + } + + else { + if(advance) ++te_channel->program_loop; + + uint8_t l = 0; + + while((te_channel->instrument->program[tick] & 0x7f00) != TE_PROGRAM_LOOP_BEGIN && + tick > 0) { + --tick; + if(!(te_channel->instrument->program[tick] & 0x8000)) ++l; + } + + --tick; + + dont_reloop = l <= 1; + } + + break; + } + + default: { + do_command(inst, tracker_engine, chan, te_channel->program_counter, true); + break; + } + } + } + + if((inst & 0x7fff) == TE_PROGRAM_NOP || (inst & 0x7f00) != TE_PROGRAM_JUMP) { + ++tick; + if(tick >= INST_PROG_LEN) { + tick = 0; + } + } + + // skip to next on msb + + if(((inst & 0x8000) || ((inst & 0x7f00) == TE_PROGRAM_LOOP_BEGIN) || + ((inst & 0x7f00) == TE_PROGRAM_JUMP)) && + (inst & 0x7fff) != TE_PROGRAM_NOP && !dont_reloop) { + goto do_it_again; + } + + if(advance) { + te_channel->program_tick = tick; + } +} + +void tracker_engine_advance_channel(TrackerEngine* tracker_engine, uint8_t chan) { + SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[chan]; + TrackerEngineChannel* te_channel = &tracker_engine->channel[chan]; + + if(te_channel->channel_flags & TEC_PLAYING) { + if(!(se_channel->flags & SE_ENABLE_GATE)) { + te_channel->flags &= ~(TEC_PLAYING); + } + + if(te_channel->slide_speed != 0) { + if(te_channel->target_note > te_channel->note) { + te_channel->note += my_min( + te_channel->slide_speed * 4, te_channel->target_note - te_channel->note); + } + + else if(te_channel->target_note < te_channel->note) { + te_channel->note -= my_min( + te_channel->slide_speed * 4, te_channel->note - te_channel->target_note); + } + } + + if(te_channel->channel_flags & TEC_PROGRAM_RUNNING) { + uint8_t u = (te_channel->program_counter + 1) >= te_channel->program_period; + tracker_engine_execute_program_tick(tracker_engine, chan, u); + ++te_channel->program_counter; + + if(u) te_channel->program_counter = 0; + } + + int16_t vib = 0; + int32_t pwm = 0; + + if(te_channel->flags & TE_ENABLE_VIBRATO) { + if(te_channel->vibrato_delay > 0) { + te_channel->vibrato_delay--; + } + + else { + te_channel->vibrato_position += ((uint32_t)te_channel->vibrato_speed << 21); + vib = + (int32_t)(sound_engine_triangle(te_channel->vibrato_position >> 9) - WAVE_AMP / 2) * + (int32_t)te_channel->vibrato_depth / (256 * 128); + } + } + + if(te_channel->flags & TE_ENABLE_PWM) { + if(te_channel->pwm_delay > 0) { + te_channel->pwm_delay--; + } + + else { + te_channel->pwm_position += + ((uint32_t)te_channel->pwm_speed + << 20); // so minimum PWM speed is even lower than minimum vibrato speed + pwm = ((int32_t)sound_engine_triangle((te_channel->pwm_position) >> 9) - + WAVE_AMP / 2) * + (int32_t)te_channel->pwm_depth / (256 * 16); + } + + int16_t final_pwm = (int16_t)tracker_engine->channel[chan].pw + pwm; + + if(final_pwm < 0) { + final_pwm = 0; + } + + if(final_pwm > 0xfff) { + final_pwm = 0xfff; + } + + tracker_engine->sound_engine->channel[chan].pw = final_pwm; + } + + else { + tracker_engine->sound_engine->channel[chan].pw = tracker_engine->channel[chan].pw; + } + + int32_t chn_note = + (int16_t)(te_channel->fixed_note != 0xffff ? te_channel->fixed_note : te_channel->note) + + vib + ((int16_t)te_channel->arpeggio_note << 8); + + if(chn_note < 0) { + chn_note = 0; + } + + if(chn_note > ((12 * 7 + 11) << 8)) { + chn_note = ((12 * 7 + 11) << 8); // highest note is B-7 + } + + tracker_engine_set_note(tracker_engine, chan, (uint16_t)chn_note, false); + } + + if(tracker_engine->channel[chan].channel_flags & + TEC_DISABLED) // so we can't set some non-zero volme from inst program too + { + tracker_engine->sound_engine->channel[chan].adsr.volume = 0; + } +} + +void tracker_engine_advance_tick(TrackerEngine* tracker_engine) { + if(!(tracker_engine->playing)) return; + + if(!(tracker_engine->sound_engine)) return; + + TrackerSong* song = tracker_engine->song; + + uint16_t opcode = 0; + + for(uint8_t chan = 0; chan < SONG_MAX_CHANNELS; chan++) { + SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[chan]; + TrackerEngineChannel* te_channel = &tracker_engine->channel[chan]; + + if(tracker_engine->song) { + uint16_t sequence_position = tracker_engine->sequence_position; + uint8_t current_pattern = + song->sequence.sequence_step[sequence_position].pattern_indices[chan]; + uint8_t pattern_step = tracker_engine->pattern_position; + + TrackerSongPattern* pattern = &song->pattern[current_pattern]; + + uint8_t note_delay = 0; + + opcode = tracker_engine_get_command(&pattern->step[pattern_step]); + + if((opcode & 0x7ff0) == TE_EFFECT_EXT_NOTE_DELAY) { + note_delay = (opcode & 0xf); + } + + if(tracker_engine->current_tick == note_delay) { + uint8_t note = tracker_engine_get_note(&pattern->step[pattern_step]); + uint8_t inst = tracker_engine_get_instrument(&pattern->step[pattern_step]); + + Instrument* pinst = NULL; + + if(inst == MUS_NOTE_INSTRUMENT_NONE) { + pinst = te_channel->instrument; + } + + else { + if(inst < song->num_instruments) { + pinst = song->instrument[inst]; + te_channel->instrument = pinst; + } + } + + if(note == MUS_NOTE_CUT) { + sound_engine_enable_gate(tracker_engine->sound_engine, se_channel, 0); + se_channel->adsr.volume = 0; + te_channel->volume = 0; + } + + if(note == MUS_NOTE_RELEASE) { + sound_engine_enable_gate(tracker_engine->sound_engine, se_channel, 0); + } + + else if( + pinst && note != MUS_NOTE_RELEASE && note != MUS_NOTE_CUT && + note != MUS_NOTE_NONE) { + uint8_t prev_adsr_volume = se_channel->adsr.volume; + + if((opcode & 0x7f00) == TE_EFFECT_SLIDE) { + if(pinst->flags & TE_RETRIGGER_ON_SLIDE) { + uint16_t temp_note = te_channel->note; + tracker_engine_trigger_instrument_internal( + tracker_engine, chan, pinst, note << 8); + te_channel->note = temp_note; + } + + te_channel->target_note = + ((note + pinst->base_note - MIDDLE_C) << 8) + pinst->finetune; + te_channel->slide_speed = (opcode & 0xff); + } + + else if((opcode & 0x7f00) == TE_EFFECT_LEGATO) { + te_channel->note = te_channel->target_note = te_channel->last_note = + ((note + pinst->base_note - MIDDLE_C) << 8) + pinst->finetune; + } + + else { + tracker_engine_trigger_instrument_internal( + tracker_engine, chan, pinst, note << 8); + te_channel->note = + ((note + pinst->base_note - MIDDLE_C) << 8) + pinst->finetune; + + te_channel->target_note = + ((note + pinst->base_note - MIDDLE_C) << 8) + pinst->finetune; + } + + if(inst == MUS_NOTE_INSTRUMENT_NONE) { + se_channel->adsr.volume = prev_adsr_volume; + } + } + } + + tracker_engine_execute_track_command( + tracker_engine, + chan, + &pattern->step[pattern_step], + tracker_engine->current_tick == note_delay); + } + + tracker_engine_advance_channel( + tracker_engine, + chan); // this will be executed even if the song pointer is NULL; handy for live instrument playback from inst editor ("jams") + } + + if(tracker_engine->song) { + tracker_engine->current_tick++; + + if(tracker_engine->current_tick >= song->speed) { + bool flag = true; + + for(int chan = 0; chan < SONG_MAX_CHANNELS; ++chan) { + uint16_t sequence_position = tracker_engine->sequence_position; + uint8_t current_pattern = + song->sequence.sequence_step[sequence_position].pattern_indices[chan]; + uint8_t pattern_step = tracker_engine->pattern_position; + + TrackerSongPattern* pattern = &song->pattern[current_pattern]; + + opcode = tracker_engine_get_command(&pattern->step[pattern_step]); + + if((opcode & 0x7ff0) == TE_EFFECT_EXT_PATTERN_LOOP) { + if(opcode & 0xf) // loop end + { + if(!(tracker_engine->in_loop)) { + tracker_engine->loops_left = (opcode & 0xf); + tracker_engine->in_loop = true; + + for(int j = tracker_engine->pattern_position; j >= 0; j--) { + if(tracker_engine_get_command(&pattern->step[j]) == + TE_EFFECT_EXT_PATTERN_LOOP) // search for loop start + { + tracker_engine->pattern_position = + fmax((int16_t)j - 1, 0); // jump to loop start + + goto out; + } + } + } + + else { + tracker_engine->loops_left--; + + if(tracker_engine->loops_left == 0) { + tracker_engine->in_loop = false; + goto out; + } + + for(int j = tracker_engine->pattern_position; j >= 0; j--) { + if(tracker_engine_get_command(&pattern->step[j]) == + TE_EFFECT_EXT_PATTERN_LOOP) // search for loop start + { + tracker_engine->pattern_position = + fmax((int16_t)j - 1, 0); // jump to loop start + + goto out; + } + } + } + } + + else // loop start + { + } + + out:; + } + + if((opcode & 0x7f00) == TE_EFFECT_SKIP_PATTERN) { + tracker_engine->sequence_position++; + tracker_engine->pattern_position = 0; + + flag = false; + + if(tracker_engine->sequence_position >= song->num_sequence_steps) { + tracker_engine->playing = false; + tracker_engine->sequence_position--; + tracker_engine->pattern_position = song->pattern_length - 1; + + for(int i = 0; i < SONG_MAX_CHANNELS; i++) { + sound_engine_enable_gate( + tracker_engine->sound_engine, + &tracker_engine->sound_engine->channel[i], + false); + } + + goto end_process; + } + } + } + + if(flag) { + tracker_engine->pattern_position++; + } + + tracker_engine->current_tick = 0; + + if(tracker_engine->pattern_position >= song->pattern_length) { + tracker_engine->pattern_position = 0; + + if(song->loop_start != 0 || song->loop_end != 0) { + if(tracker_engine->sequence_position == song->loop_end) { + tracker_engine->sequence_position = + song->loop_start; // infinite loop between loop start and loop end + } + + else { + tracker_engine->sequence_position++; + } + } + + else { + tracker_engine->sequence_position++; + } + + if(tracker_engine->sequence_position >= song->num_sequence_steps) { + tracker_engine->playing = false; + tracker_engine->sequence_position--; + tracker_engine->pattern_position = song->pattern_length - 1; + + for(int i = 0; i < SONG_MAX_CHANNELS; i++) { + sound_engine_enable_gate( + tracker_engine->sound_engine, + &tracker_engine->sound_engine->channel[i], + false); + } + } + } + } + } + +end_process:; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/tracker_engine.h b/applications/external/flizzer_tracker/tracker_engine/tracker_engine.h new file mode 100644 index 000000000..694fe8f42 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/tracker_engine.h @@ -0,0 +1,28 @@ +#pragma once + +#include "do_effects.h" +#include "tracker_engine_defs.h" + +void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngine* sound_engine); +void tracker_engine_deinit(TrackerEngine* tracker_engine, bool free_song); +void tracker_engine_advance_tick(TrackerEngine* tracker_engine); +void tracker_engine_set_song(TrackerEngine* tracker_engine, TrackerSong* song); +void tracker_engine_deinit_song(TrackerSong* song, bool free_song); +void tracker_engine_trigger_instrument_internal( + TrackerEngine* tracker_engine, + uint8_t chan, + Instrument* pinst, + uint16_t note); + +uint8_t tracker_engine_get_note(TrackerSongPatternStep* step); +uint8_t tracker_engine_get_instrument(TrackerSongPatternStep* step); +uint8_t tracker_engine_get_volume(TrackerSongPatternStep* step); +uint16_t tracker_engine_get_command(TrackerSongPatternStep* step); + +void set_note(TrackerSongPatternStep* step, uint8_t note); +void set_instrument(TrackerSongPatternStep* step, uint8_t inst); +void set_volume(TrackerSongPatternStep* step, uint8_t vol); +void set_command(TrackerSongPatternStep* step, uint16_t command); + +void set_default_instrument(Instrument* inst); +void set_empty_pattern(TrackerSongPattern* pattern, uint16_t pattern_length); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/tracker_engine/tracker_engine_defs.h b/applications/external/flizzer_tracker/tracker_engine/tracker_engine_defs.h new file mode 100644 index 000000000..ef92b1a44 --- /dev/null +++ b/applications/external/flizzer_tracker/tracker_engine/tracker_engine_defs.h @@ -0,0 +1,232 @@ +#pragma once + +#include +#include +#include + +#include "../sound_engine/sound_engine_defs.h" + +#define INST_PROG_LEN 16 +#define MUS_SONG_NAME_LEN 16 +#define MUS_INST_NAME_LEN (MUS_SONG_NAME_LEN - 3) + +#define SONG_MAX_CHANNELS NUM_CHANNELS +#define MAX_INSTRUMENTS 31 +#define MAX_PATTERN_LENGTH 256 +#define MAX_PATTERNS 256 +#define MAX_SEQUENCE_LENGTH 256 + +#define MUS_NOTE_NONE 127 +#define MUS_NOTE_RELEASE 126 +#define MUS_NOTE_CUT 125 + +#define MUS_NOTE_INSTRUMENT_NONE 31 +#define MUS_NOTE_VOLUME_NONE 31 + +#define SONG_FILE_SIG "FZT!SONG" +#define SONG_FILE_EXT ".fzt" +#define INST_FILE_SIG "FZT!INST" +#define INST_FILE_EXT ".fzi" + +#define TRACKER_ENGINE_VERSION 1 + +#define MIDDLE_C (12 * 4) +#define MAX_NOTE (12 * 7 + 11) + +typedef enum { + TE_ENABLE_VIBRATO = 1, + TE_ENABLE_PWM = 2, + TE_PROG_NO_RESTART = 4, + TE_SET_CUTOFF = 8, + TE_SET_PW = 16, + TE_RETRIGGER_ON_SLIDE = 32, // call trigger instrument function even if slide command is there +} TrackerEngineFlags; + +typedef enum { + TEC_PLAYING = 1, + TEC_PROGRAM_RUNNING = 2, + TEC_DISABLED = 4, +} TrackerEngineChannelFlags; + +typedef enum { + TE_EFFECT_ARPEGGIO = 0x0000, + TE_EFFECT_PORTAMENTO_UP = 0x0100, + TE_EFFECT_PORTAMENTO_DOWN = 0x0200, + TE_EFFECT_SLIDE = 0x0300, + TE_EFFECT_VIBRATO = 0x0400, + TE_EFFECT_PWM = 0x0500, + TE_EFFECT_SET_PW = 0x0600, + TE_EFFECT_PW_DOWN = 0x0700, + TE_EFFECT_PW_UP = 0x0800, + TE_EFFECT_SET_CUTOFF = 0x0900, + TE_EFFECT_VOLUME_FADE = 0x0a00, + TE_EFFECT_SET_WAVEFORM = 0x0b00, + TE_EFFECT_SET_VOLUME = 0x0c00, + TE_EFFECT_SKIP_PATTERN = 0x0d00, + + TE_EFFECT_EXT = 0x0e00, + TE_EFFECT_EXT_TOGGLE_FILTER = 0x0e00, + TE_EFFECT_EXT_PORTA_UP = 0x0e10, + TE_EFFECT_EXT_PORTA_DN = 0x0e20, + TE_EFFECT_EXT_FILTER_MODE = 0x0e30, + TE_EFFECT_EXT_PATTERN_LOOP = + 0x0e60, // e60 = start, e61-e6f = end and indication how many loops you want + TE_EFFECT_EXT_RETRIGGER = 0x0e90, + TE_EFFECT_EXT_FINE_VOLUME_DOWN = 0x0ea0, + TE_EFFECT_EXT_FINE_VOLUME_UP = 0x0eb0, + TE_EFFECT_EXT_NOTE_CUT = 0x0ec0, + TE_EFFECT_EXT_NOTE_DELAY = 0x0ed0, + TE_EFFECT_EXT_PHASE_RESET = 0x0ef0, + + TE_EFFECT_SET_SPEED_PROG_PERIOD = 0x0f00, + TE_EFFECT_CUTOFF_UP = 0x1000, // Gxx + TE_EFFECT_CUTOFF_DOWN = 0x1100, // Hxx + TE_EFFECT_SET_RESONANCE = 0x1200, // Ixx + TE_EFFECT_RESONANCE_UP = 0x1300, // Jxx + TE_EFFECT_RESONANCE_DOWN = 0x1400, // Kxx + + TE_EFFECT_SET_ATTACK = 0x1500, // Lxx + TE_EFFECT_SET_DECAY = 0x1600, // Mxx + TE_EFFECT_SET_SUSTAIN = 0x1700, // Nxx + TE_EFFECT_SET_RELEASE = 0x1800, // Oxx + TE_EFFECT_PROGRAM_RESTART = 0x1900, // Pxx + /* + TE_EFFECT_ = 0x1a00, //Qxx + */ + + TE_EFFECT_SET_RING_MOD_SRC = 0x1b00, // Rxx + TE_EFFECT_SET_HARD_SYNC_SRC = 0x1c00, // Sxx + + TE_EFFECT_PORTA_UP_SEMITONE = 0x1d00, // Txx + TE_EFFECT_PORTA_DOWN_SEMITONE = 0x1e00, // Uxx + /* + TE_EFFECT_ = 0x1f00, //Vxx + TE_EFFECT_ = 0x2000, //Wxx + */ + + TE_EFFECT_LEGATO = 0x2100, // Xxx + TE_EFFECT_ARPEGGIO_ABS = 0x2200, // Yxx + TE_EFFECT_TRIGGER_RELEASE = 0x2300, // Zxx + + /* These effects work only in instrument program */ + TE_PROGRAM_LOOP_BEGIN = 0x7d00, + TE_PROGRAM_LOOP_END = 0x7e00, + TE_PROGRAM_JUMP = 0x7f00, + TE_PROGRAM_NOP = 0x7ffe, + TE_PROGRAM_END = 0x7fff, +} EffectCommandsOpcodes; + +typedef struct { + uint8_t a, d, s, r, volume; +} InstrumentAdsr; + +typedef struct { + char name[MUS_INST_NAME_LEN + 1]; + + uint8_t waveform; + uint16_t flags; + uint16_t sound_engine_flags; + + uint8_t slide_speed; + + InstrumentAdsr adsr; + + uint8_t ring_mod, hard_sync; // 0xff = self + + uint8_t pw; // store only one byte since we don't have the luxury of virtually unlimited memory! + + uint16_t program + [INST_PROG_LEN]; // MSB is unite bit (indicates this and next command must be executed at once) + uint8_t program_period; + + uint8_t vibrato_speed, vibrato_depth, vibrato_delay; + uint8_t pwm_speed, pwm_depth, pwm_delay; + + uint8_t filter_cutoff, filter_resonance, filter_type; + + uint8_t base_note; + int8_t finetune; +} Instrument; + +typedef struct { + Instrument* instrument; + + uint16_t flags; + + uint8_t channel_flags; + + uint16_t note, target_note, last_note, fixed_note; + int16_t arpeggio_note; + + uint8_t volume; + + uint8_t program_counter, program_tick, program_loop, program_period; + + uint16_t filter_cutoff, filter_resonance; + uint8_t filter_type; + + uint8_t vibrato_speed, vibrato_depth, vibrato_delay; + uint8_t pwm_speed, pwm_depth, pwm_delay; + + uint32_t vibrato_position, pwm_position; // basically accumulators + + uint8_t extarp1, extarp2; + + uint16_t pw; + + uint8_t slide_speed; +} TrackerEngineChannel; + +typedef struct { + uint8_t note; // MSB is used for instrument number MSB + uint8_t inst_vol; // high nibble + MSB from note = instrument, low nibble = 4 volume LSBs + uint16_t command; // MSB used as volume MSB +} TrackerSongPatternStep; + +typedef struct { + TrackerSongPatternStep* step; +} TrackerSongPattern; + +typedef struct { + uint8_t pattern_indices[SONG_MAX_CHANNELS]; +} TrackerSongSequenceStep; + +typedef struct { + TrackerSongSequenceStep sequence_step[MAX_SEQUENCE_LENGTH]; +} TrackerSongSequence; + +typedef struct { + Instrument* instrument[MAX_INSTRUMENTS]; + TrackerSongPattern pattern[MAX_PATTERNS]; + TrackerSongSequence sequence; + + uint8_t num_patterns, num_instruments; + uint16_t num_sequence_steps; + uint16_t pattern_length; + + char song_name[MUS_SONG_NAME_LEN + 1]; + uint8_t speed, rate; + + uint8_t loop_start, loop_end; +} TrackerSong; + +typedef struct { + TrackerEngineChannel channel[SONG_MAX_CHANNELS]; + + TrackerSong* song; + SoundEngine* sound_engine; + + uint16_t pattern_position, sequence_position; + int16_t current_tick; + uint16_t absolute_position; // sequence_position * pattern_length + pattern_position + + uint8_t speed, rate; + uint8_t master_volume; + + bool playing; // if we reach the end of the song and song does not loop we just stop there + + bool in_loop; // for E6X (pattern loop) command + uint8_t loops_left; + + // uint32_t counter; //for debug +} TrackerEngine; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/util.c b/applications/external/flizzer_tracker/util.c new file mode 100644 index 000000000..bbf3119ee --- /dev/null +++ b/applications/external/flizzer_tracker/util.c @@ -0,0 +1,202 @@ +#include "util.h" +#include "macros.h" + +void reset_buffer(SoundEngine* sound_engine) { + for(uint16_t i = 0; i < sound_engine->audio_buffer_size; i++) { + sound_engine->audio_buffer[i] = 512; + } +} + +void stop_song(FlizzerTrackerApp* tracker) { + tracker->tracker_engine.playing = false; + tracker->editing = tracker->was_editing; + + for(int i = 0; i < SONG_MAX_CHANNELS; i++) { + tracker->sound_engine.channel[i].adsr.volume = 0; + tracker->tracker_engine.channel[i].channel_flags &= ~(TEC_PROGRAM_RUNNING); + } + + stop(); + + reset_buffer(&tracker->sound_engine); +} + +void play_song(FlizzerTrackerApp* tracker, bool from_cursor) { + uint16_t temppos = tracker->tracker_engine.pattern_position; + + stop_song(tracker); + + sound_engine_dma_init( + (uint32_t)tracker->sound_engine.audio_buffer, tracker->sound_engine.audio_buffer_size); + + tracker->tracker_engine.playing = true; + + tracker->was_editing = tracker->editing; + tracker->editing = false; + + if(!(from_cursor)) { + tracker->tracker_engine.pattern_position = 0; + temppos = 0; + } + + tracker_engine_timer_init(tracker->song.rate); + + /*sound_engine_init_hardware(tracker->sound_engine.sample_rate, + tracker->sound_engine.external_audio_output, + tracker->sound_engine.audio_buffer, + tracker->sound_engine.audio_buffer_size); + tracker_engine_init_hardware(tracker->song.rate);*/ + + tracker->tracker_engine.current_tick = 0; + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + + for(uint8_t i = 0; i < SONG_MAX_CHANNELS; i++) { + bool was_disabled = tracker->tracker_engine.channel[i].channel_flags & TEC_DISABLED; + + memset(&tracker->sound_engine.channel[i], 0, sizeof(SoundEngineChannel)); + memset(&tracker->tracker_engine.channel[i], 0, sizeof(TrackerEngineChannel)); + + if(was_disabled) { + tracker->tracker_engine.channel[i].channel_flags |= TEC_DISABLED; + } + } + + tracker->tracker_engine.pattern_position = temppos; + + play(); +} + +bool is_pattern_empty(TrackerSong* song, uint8_t pattern) { + TrackerSongPattern song_pattern = song->pattern[pattern]; + + for(int i = 0; i < song->pattern_length; i++) { + TrackerSongPatternStep* step = &song_pattern.step[i]; + + if(tracker_engine_get_note(step) != MUS_NOTE_NONE || + tracker_engine_get_instrument(step) != MUS_NOTE_INSTRUMENT_NONE || + tracker_engine_get_volume(step) != MUS_NOTE_VOLUME_NONE || + tracker_engine_get_command(step) != 0) { + return false; + } + } + + return true; +} + +bool check_and_allocate_pattern(TrackerSong* song, uint8_t pattern) { + if(pattern < song->num_patterns) // we can set this pattern since it already exists + { + return true; + } + + else { + if(song->pattern[pattern - 1].step == NULL) + return false; // if we hop through several patterns (e.g. editing upper digit) + + if(!(is_pattern_empty( + song, pattern - 1))) // don't let the user flood the song with empty patterns + { + song->pattern[pattern].step = + malloc(sizeof(TrackerSongPatternStep) * song->pattern_length); + set_empty_pattern(&song->pattern[pattern], song->pattern_length); + song->num_patterns++; + return true; + } + + else { + return false; + } + } +} + +void resize_pattern(TrackerSongPattern* pattern, uint16_t old_length, uint16_t new_length) { + TrackerSongPattern temp; + temp.step = malloc((new_length) * sizeof(TrackerSongPatternStep)); + + set_empty_pattern(&temp, new_length); + memcpy( + temp.step, pattern->step, my_min(old_length, new_length) * sizeof(TrackerSongPatternStep)); + + free(pattern->step); + pattern->step = temp.step; +} + +void change_pattern_length(TrackerSong* song, uint16_t new_length) { + for(int i = 0; i < MAX_PATTERNS; i++) { + if(song->pattern[i].step) { + resize_pattern(&song->pattern[i], song->pattern_length, new_length); + } + } + + song->pattern_length = new_length; +} + +bool is_default_instrument(Instrument* inst) { + Instrument* ref = malloc(sizeof(Instrument)); + set_default_instrument(ref); + bool is_default = memcmp(ref, inst, sizeof(Instrument)) != 0 ? false : true; + free(ref); + return is_default; +} + +bool check_and_allocate_instrument(TrackerSong* song, uint8_t inst) { + if(inst < song->num_instruments) // we can go to this instrument since it already exists + { + return true; + } + + else { + if(inst >= MAX_INSTRUMENTS) return false; + + if(!(is_default_instrument( + song->instrument + [inst - 1]))) // don't let the user flood the song with default instrument + { + song->instrument[inst] = malloc(sizeof(Instrument)); + set_default_instrument(song->instrument[inst]); + song->num_instruments++; + return true; + } + + else { + return false; + } + } +} + +void set_default_song(FlizzerTrackerApp* tracker) { + tracker->tracker_engine.master_volume = 0x80; + + tracker->song.speed = 6; + tracker->song.rate = tracker->tracker_engine.rate; + tracker->song.num_instruments = 1; + tracker->song.num_patterns = 5; + tracker->song.num_sequence_steps = 1; + tracker->song.pattern_length = 64; + + tracker->song.sequence.sequence_step[0].pattern_indices[0] = 1; + tracker->song.sequence.sequence_step[0].pattern_indices[1] = 2; + tracker->song.sequence.sequence_step[0].pattern_indices[2] = 3; + tracker->song.sequence.sequence_step[0].pattern_indices[3] = 4; + + for(int i = 0; i < 5; i++) { + tracker->song.pattern[i].step = malloc(64 * sizeof(TrackerSongPatternStep)); + memset(tracker->song.pattern[i].step, 0, 64 * sizeof(TrackerSongPatternStep)); + } + + for(int i = 0; i < 64; ++i) { + for(int j = 0; j < 5; j++) { + set_note(&tracker->song.pattern[j].step[i], MUS_NOTE_NONE); + + set_instrument(&tracker->song.pattern[j].step[i], MUS_NOTE_INSTRUMENT_NONE); + + set_volume(&tracker->song.pattern[j].step[i], MUS_NOTE_VOLUME_NONE); + } + } + + tracker->song.instrument[0] = malloc(sizeof(Instrument)); + + set_default_instrument(tracker->song.instrument[0]); + + tracker->tracker_engine.playing = false; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/util.h b/applications/external/flizzer_tracker/util.h new file mode 100644 index 000000000..45a2b0f07 --- /dev/null +++ b/applications/external/flizzer_tracker/util.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "flizzer_tracker.h" +#include "sound_engine/sound_engine_defs.h" +#include "tracker_engine/tracker_engine.h" +#include "tracker_engine/tracker_engine_defs.h" + +#include "macros.h" + +#define clamp(val, add, _min, _max) val = my_min(_max, my_max(_min, (int32_t)val + add)) +#define flipbit(val, bit) \ + { val ^= bit; }; + +void reset_buffer(SoundEngine* sound_engine); +void play_song(FlizzerTrackerApp* tracker, bool from_cursor); +void stop_song(FlizzerTrackerApp* tracker); + +bool is_pattern_empty(TrackerSong* song, uint8_t pattern); +bool check_and_allocate_pattern(TrackerSong* song, uint8_t pattern); +void change_pattern_length(TrackerSong* song, uint16_t new_length); + +bool check_and_allocate_instrument(TrackerSong* song, uint8_t inst); +void set_default_song(FlizzerTrackerApp* tracker); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/char_array.c b/applications/external/flizzer_tracker/view/char_array.c new file mode 100644 index 000000000..7c17138ae --- /dev/null +++ b/applications/external/flizzer_tracker/view/char_array.c @@ -0,0 +1,4 @@ +const char to_char_array[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', +}; \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/instrument_editor.c b/applications/external/flizzer_tracker/view/instrument_editor.c new file mode 100644 index 000000000..b931a5de5 --- /dev/null +++ b/applications/external/flizzer_tracker/view/instrument_editor.c @@ -0,0 +1,669 @@ +#include "instrument_editor.h" +#include "pattern_editor.h" + +#include "../macros.h" +#include "opcode_description.h" + +#include + +void draw_inst_flag( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint16_t flags, + uint16_t mask) { + canvas_draw_icon(canvas, x, y - 5, ((flags & mask) ? &I_checkbox_checked : &I_checkbox_empty)); + canvas_draw_str(canvas, x + 6, y, text); + + if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) { + if(text[strlen(text) - 1] == ':') { + canvas_draw_box(canvas, x + 5, y - 6, strlen(text) * 4 - 1, 7); + } + + else { + canvas_draw_box(canvas, x + 5, y - 6, strlen(text) * 4 + 1, 7); + } + } + + if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) { + if(text[strlen(text) - 1] == ':') { + canvas_draw_frame(canvas, x + 5, y - 6, strlen(text) * 4 - 1, 7); + } + + else { + canvas_draw_frame(canvas, x + 5, y - 6, strlen(text) * 4 + 1, 7); + } + } +} + +void draw_inst_text_one_digit( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint8_t value) // text MUST end with semicolon +{ + canvas_draw_str(canvas, x, y, text); + char buffer[4]; + snprintf(buffer, sizeof(buffer), "%01X", (value & 0xF)); + canvas_draw_str(canvas, x + strlen(text) * 4 - 2, y, buffer); + + if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) { + canvas_draw_box(canvas, x + strlen(text) * 4 - 3, y - 6, 5, 7); + } + + if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) { + canvas_draw_frame(canvas, x + strlen(text) * 4 - 3, y - 6, 5, 7); + } +} + +void draw_inst_text_two_digits( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint8_t value) // text MUST end with semicolon +{ + canvas_draw_str(canvas, x, y, text); + char buffer[4]; + snprintf(buffer, sizeof(buffer), "%02X", value); + canvas_draw_str(canvas, x + strlen(text) * 4 - 2, y, buffer); + + if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) { + canvas_draw_box( + canvas, x + strlen(text) * 4 + 4 * tracker->current_digit - 3, y - 6, 5, 7); + } + + if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) { + canvas_draw_frame( + canvas, x + strlen(text) * 4 + 4 * tracker->current_digit - 3, y - 6, 5, 7); + } +} + +static const char* filter_types[] = { + "NONE", + "LOW", + "HIGH", + "BAND", + "LOHI", + "HIBD", + "LOBD", + "ALL", +}; + +static const char* instrument_editor_params_description[] = { + "CURRENT INSTRUMENT", + "CURRENT INSTRUMENT NAME", + "INSTRUMENT BASE NOTE", + "INSTRUMENT FINETUNE", + "SLIDE SPEED", + "SET PULSE WIDTH ON KEYDOWN", + "PULSE WIDTH", + "SET FILTER PARAMETERS ON KEYDOWN", + "NOISE WAVEFORM", + "PULSE WAVEFORM", + "TRIANGLE WAVEFORM", + "SAWTOOTH WAVEFORM", + "METALLIC NOISE WAVEFORM", + "SINE WAVEFORM", + "ENVELOPE ATTACK", + "ENVELOPE DECAY", + "ENVELOPE SUSTAIN", + "ENVELOPE RELEASE", + "ENVELOPE VOLUME", + "ENABLE FILTER", + "FILTER CUTOFF FREQUENCY", + "FILTER RESONANCE", + "FILTER TYPE (NONE=OFF)", + "ENABLE RING MODULATION", + "RINGMOD SOURCE CHANNEL (F=SELF)", + "ENABLE HARD SYNC", + "HARDSYNC SOURCE CHANNEL (F=SELF)", + "RETRIGGER INSTRUMENT ON SLIDE", + "SYNC OSCILLATORS ON KEYDOWN", + "ENABLE VIBRATO", + "VIBRATO SPEED", + "VIBRATO DEPTH", + "VIBRATO DELAY (IN TICKS)", + "ENABLE PWM", + "PWM SPEED", + "PWM DEPTH", + "PWM DELAY (IN TICKS)", + "DON'T RESTART PROGRAM ON KEYDOWN", + "PROG.PERIOD (00 = PROGRAM OFF)", +}; + +void draw_instrument_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + SoundEngineChannel* se_channel = &tracker->sound_engine.channel[0]; + if(!(se_channel->flags & SE_ENABLE_GATE) && tracker->tracker_engine.song == NULL) { + stop(); + tracker->tracker_engine.playing = false; + tracker_engine_set_song(&tracker->tracker_engine, &tracker->song); + } + + char buffer[30]; + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + uint8_t shift = tracker->inst_editor_shift; + + if(shift < 6) { + snprintf(buffer, sizeof(buffer), "INST:%c", to_char(tracker->current_instrument)); + draw_generic_n_digit_field( + tracker, canvas, EDIT_INSTRUMENT, INST_CURRENTINSTRUMENT, buffer, 0, 5 - shift, 1); + snprintf( + buffer, + sizeof(buffer), + "%s", + tracker->song.instrument[tracker->current_instrument]->name); + draw_generic_n_digit_field( + tracker, canvas, EDIT_INSTRUMENT, INST_INSTRUMENTNAME, buffer, 4 * 7 - 1, 5 - shift, 1); + } + + if(shift < 12) { + snprintf(buffer, sizeof(buffer), "NOTE:%s", notename(inst->base_note)); + canvas_draw_str(canvas, 0, 11 - shift, buffer); + + if(tracker->editing && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_CURRENT_NOTE) { + if(tracker->current_digit) { + canvas_draw_box(canvas, 19 + 2 * 4, 5 - shift, 5, 7); + } + + else { + canvas_draw_box(canvas, 19, 5 - shift, 5 + 4, 7); + } + } + + if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_CURRENT_NOTE) { + if(tracker->current_digit) { + canvas_draw_frame(canvas, 19 + 2 * 4, 5 - shift, 5, 7); + } + + else { + canvas_draw_frame(canvas, 19, 5 - shift, 5 + 4, 7); + } + } + + snprintf(buffer, sizeof(buffer), "FINE:%+02d", inst->finetune); + canvas_draw_str(canvas, 37, 11 - shift, buffer); + + if(tracker->editing && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_FINETUNE) { + if(tracker->current_digit) { + canvas_draw_box(canvas, 60 + 4, 5 - shift, 5, 7); + } + + else { + canvas_draw_box(canvas, 60, 5 - shift, 5, 7); + } + } + + if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_FINETUNE) { + if(tracker->current_digit) { + canvas_draw_frame(canvas, 60 + 4, 5 - shift, 5, 7); + } + + else { + canvas_draw_frame(canvas, 60, 5 - shift, 5, 7); + } + } + } + + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_SLIDESPEED, + "SL.SPD:", + 0, + 17 - shift, + inst->slide_speed); + + draw_inst_flag( + tracker, canvas, EDIT_INSTRUMENT, INST_SETPW, "PW:", 36, 17 - shift, inst->flags, TE_SET_PW); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_PW, "", 54, 17 - shift, inst->pw); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_SETCUTOFF, + "CUT", + 61, + 17 - shift, + inst->flags, + TE_SET_CUTOFF); + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_NOISE, + "N", + 0, + 23 - shift, + inst->waveform, + SE_WAVEFORM_NOISE); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_PULSE, + "P", + 10, + 23 - shift, + inst->waveform, + SE_WAVEFORM_PULSE); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_TRIANGLE, + "T", + 20, + 23 - shift, + inst->waveform, + SE_WAVEFORM_TRIANGLE); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_SAWTOOTH, + "S", + 30, + 23 - shift, + inst->waveform, + SE_WAVEFORM_SAW); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_NOISE_METAL, + "M", + 40, + 23 - shift, + inst->waveform, + SE_WAVEFORM_NOISE_METAL); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_WAVE_SINE, + "SINE", + 50, + 23 - shift, + inst->waveform, + SE_WAVEFORM_SINE); + + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_ATTACK, "A:", 0, 29 - shift, inst->adsr.a); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_DECAY, "D:", 16, 29 - shift, inst->adsr.d); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_SUSTAIN, "S:", 32, 29 - shift, inst->adsr.s); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_RELEASE, "R:", 48, 29 - shift, inst->adsr.r); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_VOLUME, "V:", 64, 29 - shift, inst->adsr.volume); + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEFILTER, + "FIL", + 0, + 35 - shift, + inst->sound_engine_flags, + SE_ENABLE_FILTER); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_FILTERCUTOFF, + "CUT:", + 20, + 35 - shift, + inst->filter_cutoff); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_FILTERRESONANCE, + "RES:", + 44, + 35 - shift, + inst->filter_resonance); + + snprintf(buffer, sizeof(buffer), "TYPE:%s", filter_types[inst->filter_type]); + canvas_draw_str(canvas, 0, 41 - shift, buffer); + + if(tracker->editing && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_FILTERTYPE) { + canvas_draw_box( + canvas, 19, 35 - shift, strlen(filter_types[inst->filter_type]) * 4 + 1, 7); + } + + if(!(tracker->editing) && tracker->focus == EDIT_INSTRUMENT && + tracker->selected_param == INST_FILTERTYPE) { + canvas_draw_frame( + canvas, 19, 35 - shift, strlen(filter_types[inst->filter_type]) * 4 + 1, 7); + } + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLERINGMOD, + "R:", + 38, + 41 - shift, + inst->sound_engine_flags, + SE_ENABLE_RING_MOD); + draw_inst_text_one_digit( + tracker, canvas, EDIT_INSTRUMENT, INST_RINGMODSRC, "", 52, 41 - shift, inst->ring_mod); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEHARDSYNC, + "H:", + 56, + 41 - shift, + inst->sound_engine_flags, + SE_ENABLE_HARD_SYNC); + draw_inst_text_one_digit( + tracker, canvas, EDIT_INSTRUMENT, INST_HARDSYNCSRC, "", 70, 41 - shift, inst->hard_sync); + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_RETRIGGERONSLIDE, + "SL.RETRIG", + 0, + 47 - shift, + inst->flags, + TE_RETRIGGER_ON_SLIDE); + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEKEYSYNC, + "KSYNC", + 44, + 47 - shift, + inst->sound_engine_flags, + SE_ENABLE_KEYDOWN_SYNC); + + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEVIBRATO, + "VIB", + 0, + 53 - shift, + inst->flags, + TE_ENABLE_VIBRATO); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_VIBRATOSPEED, + "S:", + 20, + 53 - shift, + inst->vibrato_speed); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_VIBRATODEPTH, + "D:", + 36, + 53 - shift, + inst->vibrato_depth); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_VIBRATODELAY, + "DEL:", + 52, + 53 - shift, + inst->vibrato_delay); + + if(shift >= 6) { + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_ENABLEPWM, + "PWM", + 0, + 59 - shift, + inst->flags, + TE_ENABLE_PWM); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_PWMSPEED, "S:", 20, 59 - shift, inst->pwm_speed); + draw_inst_text_two_digits( + tracker, canvas, EDIT_INSTRUMENT, INST_PWMDEPTH, "D:", 36, 59 - shift, inst->pwm_depth); + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_PWMDELAY, + "DEL:", + 52, + 59 - shift, + inst->pwm_delay); + } + + if(shift >= 12) { + draw_inst_flag( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_PROGRESTART, + "NO PROG.RESTART", + 0, + 65 - shift, + inst->flags, + TE_PROG_NO_RESTART); + } + + draw_inst_text_two_digits( + tracker, + canvas, + EDIT_INSTRUMENT, + INST_PROGRAMEPERIOD, + "P.PERIOD:", + 81, + 56, + inst->program_period); + + canvas_draw_line(canvas, 0, 57, 127, 57); + canvas_draw_line(canvas, 79, 0, 79, 56); + canvas_draw_line(canvas, 80, 49, 127, 49); + + if(tracker->focus == EDIT_INSTRUMENT) { + canvas_draw_str( + canvas, 0, 64, instrument_editor_params_description[tracker->selected_param]); + } +} + +char command_get_char(uint16_t command) { + if((command >> 8) < 36) { + return to_char_array[(command >> 8)]; + } + + if(command == TE_PROGRAM_END) { + return ':'; + } + + if((command & 0xff00) == TE_PROGRAM_JUMP) { + return '^'; + } + + if((command & 0xff00) == TE_PROGRAM_LOOP_END) { + return '>'; + } + + if((command & 0xff00) == TE_PROGRAM_LOOP_BEGIN) { + return '<'; + } + + return '?'; +} + +void draw_program_step(Canvas* canvas, uint8_t y, FlizzerTrackerApp* tracker, uint8_t index) { + char buffer[15]; + + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + uint16_t opcode = inst->program[index]; + + if(opcode != TE_PROGRAM_NOP) { + if((opcode & 0x7f00) == TE_EFFECT_ARPEGGIO) { + if((opcode & 0xff) != 0xf0 && (opcode & 0xff) != 0xf1) { + snprintf( + buffer, + sizeof(buffer), + "%01X %c%02X %s", + index, + command_get_char(opcode & 0x7fff), + (opcode & 0xff), + notename(my_min( + 12 * 7 + 11, + (opcode & 0xff) + + tracker->song.instrument[tracker->current_instrument]->base_note))); + } + + else { + snprintf( + buffer, + sizeof(buffer), + "%01X %c%02X %s", + index, + command_get_char(opcode & 0x7fff), + (opcode & 0xff), + notename((opcode & 0xff))); + } + } + + else if((opcode & 0x7f00) == TE_EFFECT_ARPEGGIO_ABS) { + snprintf( + buffer, + sizeof(buffer), + "%01X %c%02X F.%s", + index, + command_get_char(opcode & 0x7fff), + (opcode & 0xff), + notename(opcode & 0xff)); + } + + else { + snprintf( + buffer, + sizeof(buffer), + "%01X %c%02X %s", + index, + command_get_char(opcode & 0x7fff), + (opcode & 0xff), + get_opcode_description(opcode, true) ? get_opcode_description(opcode, true) : ""); + } + + if(opcode & 0x8000) { + if(index == 0) { + canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 3); + canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 4); + } + + if(index > 0 && !(inst->program[index - 1] & 0x8000)) { + canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 3); + canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 4); + } + + if(index > 0 && (inst->program[index - 1] & 0x8000)) { + canvas_draw_line(canvas, 84 + 4 * 4 + 2, y, 84 + 4 * 4 + 2, y - 5); + } + } + + else { + if(index > 0 && (inst->program[index - 1] & 0x8000)) { + canvas_draw_line(canvas, 84 + 4 * 4 + 2, y - 3, 84 + 4 * 4 + 2, y - 5); + canvas_draw_dot(canvas, 84 + 4 * 4 + 1, y - 2); + } + } + } + + else { + snprintf(buffer, sizeof(buffer), "%01X ---", index); + } + + canvas_draw_str(canvas, 81, y, buffer); +} + +void draw_instrument_program_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + Instrument* inst = tracker->song.instrument[tracker->current_instrument]; + + for(uint8_t i = tracker->program_position; + i < my_min(INST_PROG_LEN, tracker->program_position + 8); + i++) { + draw_program_step(canvas, 6 + 6 * i - tracker->program_position * 6, tracker, i); + + if(i == tracker->current_program_step && tracker->focus == EDIT_PROGRAM) { + if(tracker->editing) { + canvas_draw_box( + canvas, + 80 + 8 + tracker->current_digit * 4, + 6 * i - tracker->program_position * 6, + 5, + 7); + } + + else { + canvas_draw_box(canvas, 80, 6 * i - tracker->program_position * 6, 5, 7); + } + } + } + + // draw arrow pointing at current program step + + for(uint8_t i = 0; i < SONG_MAX_CHANNELS; i++) { + if(tracker->tracker_engine.channel[i].instrument == inst && + (tracker->tracker_engine.channel[i].channel_flags & TEC_PROGRAM_RUNNING) && + (tracker->tracker_engine.sound_engine->channel[i].flags & SE_ENABLE_GATE)) { + if(tracker->tracker_engine.channel[i].program_tick >= tracker->program_position && + tracker->tracker_engine.channel[i].program_tick < tracker->program_position + 8) { + canvas_draw_str( + canvas, + 85, + 6 * tracker->tracker_engine.channel[i].program_tick - + tracker->program_position * 6 + 6, + ">"); + break; + } + } + } + + if(tracker->focus == EDIT_PROGRAM) { + uint16_t opcode = (inst->program[tracker->current_program_step] & 0x7fff); + canvas_draw_str( + canvas, + 0, + 64, + get_opcode_description(opcode, false) ? get_opcode_description(opcode, false) : ""); + } +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/instrument_editor.h b/applications/external/flizzer_tracker/view/instrument_editor.h new file mode 100644 index 000000000..b8656c011 --- /dev/null +++ b/applications/external/flizzer_tracker/view/instrument_editor.h @@ -0,0 +1,11 @@ +#pragma once + +#include "../flizzer_tracker.h" +#include "../tracker_engine/tracker_engine_defs.h" +#include "pattern_editor.h" + +#include +#include + +void draw_instrument_view(Canvas* canvas, FlizzerTrackerApp* tracker); +void draw_instrument_program_view(Canvas* canvas, FlizzerTrackerApp* tracker); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/opcode_description.c b/applications/external/flizzer_tracker/view/opcode_description.c new file mode 100644 index 000000000..f42ef207f --- /dev/null +++ b/applications/external/flizzer_tracker/view/opcode_description.c @@ -0,0 +1,72 @@ +#include "opcode_description.h" +#include + +static const OpcodeDescription opcode_desc[] = { + {TE_PROGRAM_LOOP_BEGIN, 0x7f00, "PROGRAM LOOP BEGIN", "L.BEG."}, + {TE_PROGRAM_LOOP_END, 0x7f00, "PROGRAM LOOP END", "L.END"}, + + {TE_PROGRAM_NOP, 0x7fff, "NO OPERATION", ""}, + {TE_PROGRAM_END, 0x7fff, "PROGRAM END", "PR.END"}, + {TE_PROGRAM_JUMP, 0x7f00, "JUMP TO POSITION", "GOTO"}, + + //==================================================== + + {TE_EFFECT_ARPEGGIO, 0x7f00, "RELATIVE ARPEGGIO NOTE", ""}, + {TE_EFFECT_PORTAMENTO_UP, 0x7f00, "PORTAMENTO UP", "PORTUP"}, + {TE_EFFECT_PORTAMENTO_DOWN, 0x7f00, "PORTAMENTO DOWN", "PORTDN"}, + {TE_EFFECT_SLIDE, 0x7f00, "SLIDE", "SLIDE"}, + {TE_EFFECT_VIBRATO, 0x7f00, "VIBRATO", "VIB"}, + {TE_EFFECT_PWM, 0x7f00, "PULSE WIDTH MODIFICATION", "PWM"}, + + {TE_EFFECT_SET_PW, 0x7f00, "SET PULSE WIDTH", "SET PW"}, + {TE_EFFECT_PW_DOWN, 0x7f00, "PULSE WIDTH DOWN", "PWDOWN"}, + {TE_EFFECT_PW_UP, 0x7f00, "PULSE WIDTH UP", "PW UP"}, + {TE_EFFECT_SET_CUTOFF, 0x7f00, "SET FILTER CUTOFF", "F.CUT"}, + + {TE_EFFECT_VOLUME_FADE, 0x7f00, "VOLUME FADE", "V.FADE"}, + {TE_EFFECT_SET_WAVEFORM, 0x7f00, "SET WAVEFORM", "S.WAVE"}, + {TE_EFFECT_SET_VOLUME, 0x7f00, "SET VOLUME", "VOLUME"}, + {TE_EFFECT_SKIP_PATTERN, 0x7f00, "SKIP PATTERN", "P.SKIP"}, + + {TE_EFFECT_EXT_TOGGLE_FILTER, 0x7ff0, "TOGGLE FILTER (0=OFF,1-F=ON)", "T.FILT"}, + {TE_EFFECT_EXT_PORTA_UP, 0x7ff0, "FINE PORTAMENTO UP", "PUP F."}, + {TE_EFFECT_EXT_PORTA_DN, 0x7ff0, "FINE PORTAMENTO DOWN", "PDN F."}, + {TE_EFFECT_EXT_FILTER_MODE, 0x7ff0, "SET FILTER MODE", "F.MODE"}, + {TE_EFFECT_EXT_PATTERN_LOOP, 0x7ff0, "PATTERN LOOP:E60=BEGIN,E6X=END", "PAT.L."}, + {TE_EFFECT_EXT_RETRIGGER, 0x7ff0, "RETRIGGER AT TICK X (X>0)", "RETRIG"}, + {TE_EFFECT_EXT_FINE_VOLUME_DOWN, 0x7ff0, "FINE VOLUME DOWN", "VDN F."}, + {TE_EFFECT_EXT_FINE_VOLUME_UP, 0x7ff0, "FINE VOLUME UP", "VUP F."}, + {TE_EFFECT_EXT_NOTE_CUT, 0x7ff0, "NOTE CUT", "N.CUT"}, + {TE_EFFECT_EXT_NOTE_DELAY, 0x7ff0, "NOTE DELAY", "N.DEL."}, + {TE_EFFECT_EXT_PHASE_RESET, 0x7ff0, "PHASE RESET ON TICK X", "PH.RES."}, + + {TE_EFFECT_SET_SPEED_PROG_PERIOD, 0x7f00, "SET SPEED (PROG.PER.IN PROGRAM)", "P.PER."}, + {TE_EFFECT_CUTOFF_UP, 0x7f00, "FILTER CUTOFF UP", "CUT.UP"}, + {TE_EFFECT_CUTOFF_DOWN, 0x7f00, "FILTER CUTOFF DOWN", "CUT.DN"}, + {TE_EFFECT_SET_RESONANCE, 0x7f00, "SET FILTER RESONANCE", "F.RES."}, + {TE_EFFECT_RESONANCE_UP, 0x7f00, "FILTER RESONANCE UP", "F.R.UP"}, + {TE_EFFECT_RESONANCE_DOWN, 0x7f00, "FILTER RESONANCE DOWN", "F.R.DN"}, + {TE_EFFECT_SET_ATTACK, 0x7f00, "SET ENVELOPE ATTACK", "ADSR A"}, + {TE_EFFECT_SET_DECAY, 0x7f00, "SET ENVELOPE DECAY", "ADSR D"}, + {TE_EFFECT_SET_SUSTAIN, 0x7f00, "SET ENVELOPE SUSTAIN", "ADSR S"}, + {TE_EFFECT_SET_RELEASE, 0x7f00, "SET ENVELOPE RELEASE", "ADSR R"}, + {TE_EFFECT_PROGRAM_RESTART, 0x7f00, "RESTART INSTRUMENT PROGRAM", "P.RES."}, + {TE_EFFECT_SET_RING_MOD_SRC, 0x7f00, "SET RING MODULATION SOURCE CH.", "R.SRC"}, + {TE_EFFECT_SET_HARD_SYNC_SRC, 0x7f00, "SET HARD SYNC SOURCE CHANNEL", "S.SRC"}, + {TE_EFFECT_PORTA_UP_SEMITONE, 0x7f00, "PORTAMENTO UP (SEMITONES)", "PU.SEM"}, + {TE_EFFECT_PORTA_DOWN_SEMITONE, 0x7f00, "PORTAMENTO DOWN (SEMITONES)", "PD.SEM"}, + {TE_EFFECT_LEGATO, 0x7f00, "LEGATO", "LEGATO"}, + {TE_EFFECT_ARPEGGIO_ABS, 0x7f00, "ABSOLUTE ARPEGGIO NOTE", ""}, + {TE_EFFECT_TRIGGER_RELEASE, 0x7f00, "TRIGGER RELEASE", "TR.REL"}, + {0, 0, NULL, NULL}, +}; + +char* get_opcode_description(uint16_t opcode, bool short_description) { + for(int i = 0; opcode_desc[i].name != NULL; i++) { + if(opcode_desc[i].opcode == (opcode & opcode_desc[i].mask)) { + return short_description ? opcode_desc[i].shortname : opcode_desc[i].name; + } + } + + return NULL; +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/opcode_description.h b/applications/external/flizzer_tracker/view/opcode_description.h new file mode 100644 index 000000000..2d98673bc --- /dev/null +++ b/applications/external/flizzer_tracker/view/opcode_description.h @@ -0,0 +1,12 @@ +#pragma once + +#include "../tracker_engine/tracker_engine_defs.h" +#include + +typedef struct { + uint16_t opcode; + uint16_t mask; + char *name, *shortname; +} OpcodeDescription; + +char* get_opcode_description(uint16_t opcode, bool short_description); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/pattern_editor.c b/applications/external/flizzer_tracker/view/pattern_editor.c new file mode 100644 index 000000000..19c62d9f9 --- /dev/null +++ b/applications/external/flizzer_tracker/view/pattern_editor.c @@ -0,0 +1,442 @@ +#include "pattern_editor.h" +#include "../macros.h" + +#include + +#define PATTERN_EDITOR_Y ((tracker->focus == EDIT_PATTERN) ? 4 : (64 - (6 * 5) - 1)) + +static const char* notenames[] = { + "C-", + "C#", + "D-", + "D#", + "E-", + "F-", + "F#", + "G-", + "G#", + "A-", + "A#", + "B-", +}; + +char* notename(uint8_t note) { + static char buffer[6]; + + if(note == MUS_NOTE_CUT) { + snprintf(buffer, sizeof(buffer), "%s", "OFF"); + return buffer; + } + + if(note == MUS_NOTE_RELEASE) { + snprintf(buffer, sizeof(buffer), "%s", " "); + return buffer; + } + + if(note == 0xf0) // external arpeggio notes + { + snprintf(buffer, sizeof(buffer), "%s", "EXT.0"); + return buffer; + } + + if(note == 0xf1) { + snprintf(buffer, sizeof(buffer), "%s", "EXT.1"); + return buffer; + } + + else { + uint8_t final_note = my_min(12 * 7 + 11, note); + snprintf(buffer, sizeof(buffer), "%s%d", notenames[final_note % 12], final_note / 12); + } + + return buffer; +} + +char to_char(uint8_t number) { + return to_char_array[number]; +} + +void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + char command_buffer[6] = {0}; + char buffer[11] = {0}; + + canvas_draw_line(canvas, 0, PATTERN_EDITOR_Y, 127, PATTERN_EDITOR_Y); + + for(int i = 0; i < SONG_MAX_CHANNELS; ++i) { + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + uint8_t current_pattern = + tracker->tracker_engine.song->sequence.sequence_step[sequence_position] + .pattern_indices[i]; + uint16_t pattern_step = tracker->tracker_engine.pattern_position; + + uint16_t pattern_length = tracker->tracker_engine.song->pattern_length; + + TrackerSongPattern* pattern = &tracker->tracker_engine.song->pattern[current_pattern]; + + for(uint8_t pos = 0; pos < ((tracker->focus == EDIT_PATTERN) ? 9 : 5); ++pos) { + TrackerSongPatternStep* step = NULL; + + if(pattern_step - ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + pos >= 0 && + pattern_step - ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + pos < pattern_length) { + step = + &pattern->step[pattern_step + pos - ((tracker->focus == EDIT_PATTERN) ? 4 : 2)]; + } + + uint8_t string_x = i * 32; + uint8_t string_y = + PATTERN_EDITOR_Y + 6 * pos + 6 + ((tracker->focus == EDIT_PATTERN) ? 3 : 1); + + if(step) { + uint8_t note = tracker_engine_get_note(step); + uint8_t inst = tracker_engine_get_instrument(step); + uint8_t vol = tracker_engine_get_volume(step); + uint16_t command = tracker_engine_get_command(step); + + char inst_ch = to_char(inst); + char vol_ch = to_char(vol); + char command_ch = to_char(command >> 8); + + if(inst == MUS_NOTE_INSTRUMENT_NONE) { + inst_ch = '-'; + } + + if(vol == MUS_NOTE_VOLUME_NONE) { + vol_ch = '-'; + } + + if(command == 0) { + snprintf(command_buffer, sizeof(command_buffer), "---"); + } + + else { + snprintf( + command_buffer, + sizeof(command_buffer), + "%c%02X", + command_ch, + (command & 0xff)); + } + + snprintf( + buffer, + sizeof(buffer), + "%s%c%c%s", + (note == MUS_NOTE_NONE ? "---" : notename(note)), + inst_ch, + vol_ch, + command_buffer); + + canvas_draw_str(canvas, string_x, string_y, buffer); + + if(note == MUS_NOTE_RELEASE) { + canvas_draw_icon(canvas, string_x, string_y - 5, &I_note_release); + } + } + } + } + + if(tracker->editing && tracker->focus == EDIT_PATTERN) { + uint16_t x = tracker->current_channel * 32 + tracker->patternx * 4 + + (tracker->patternx > 0 ? 4 : 0) - 1; + uint16_t y = PATTERN_EDITOR_Y + 6 * ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + + ((tracker->focus == EDIT_PATTERN) ? 3 : 1); + + canvas_draw_box(canvas, x, y, (tracker->patternx > 0 ? 5 : 9), 7); + } + + if(!(tracker->editing) && tracker->focus == EDIT_PATTERN) { + uint16_t x = tracker->current_channel * 32 + tracker->patternx * 4 + + (tracker->patternx > 0 ? 4 : 0) - 1; + uint16_t y = PATTERN_EDITOR_Y + 6 * ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + + ((tracker->focus == EDIT_PATTERN) ? 3 : 1); + + canvas_draw_frame(canvas, x, y, (tracker->patternx > 0 ? 5 : 9), 7); + } + + canvas_set_color(canvas, ColorBlack); + + for(int i = 1; i < SONG_MAX_CHANNELS; ++i) { + for(int y = PATTERN_EDITOR_Y + 1; y < 64; y += 2) { + canvas_draw_dot(canvas, i * 32 - 1, y); + } + } + + for(int i = 0; i < SONG_MAX_CHANNELS; ++i) { + if(tracker->tracker_engine.channel[i].channel_flags & TEC_DISABLED) { + canvas_draw_icon(canvas, 13 + 32 * i, PATTERN_EDITOR_Y - 3, &I_channel_off); + } + + else { + canvas_draw_icon(canvas, 13 + 32 * i, PATTERN_EDITOR_Y - 3, &I_channel_on); + } + } + + canvas_set_color(canvas, ColorXOR); +} + +#define SEQ_SLIDER_X (4 * (4 * 2 + 1) + 2) +#define SEQ_SLIDER_Y (32) + +void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + char buffer[4]; + + uint8_t sequence_position = tracker->tracker_engine.sequence_position; + TrackerSong* song = &tracker->song; + + for(int pos = sequence_position - 2; pos < sequence_position + 3; pos++) { + if(pos >= 0 && pos < tracker->song.num_sequence_steps) { + for(int i = 0; i < SONG_MAX_CHANNELS; ++i) { + uint8_t current_pattern = + tracker->tracker_engine.song->sequence.sequence_step[pos].pattern_indices[i]; + + uint8_t x = i * (4 * 2 + 1) + 3; + uint8_t y = (pos - (sequence_position - 2)) * 6 + 5; + + snprintf(buffer, sizeof(buffer), "%02X", current_pattern); + canvas_draw_str(canvas, x, y, buffer); + } + } + } + + if(song->loop_start != 0 || song->loop_end != 0) { + canvas_set_color(canvas, ColorBlack); + + for(int pos = sequence_position - 2; pos < sequence_position + 3; pos++) { + if(pos >= 0 && pos < tracker->song.num_sequence_steps) { + if(pos == song->loop_start) { + int16_t y = (pos - (sequence_position - 2)) * 6; + + canvas_draw_line(canvas, 0, fmax(y, 0), 1, fmax(y, 0)); + canvas_draw_line(canvas, 0, fmax(y, 0), 0, fmax(y + 4, 0)); + } + + if(pos > song->loop_start && pos < song->loop_end) { + int16_t y = (pos - (sequence_position - 2)) * 6; + + canvas_draw_line(canvas, 0, fmax(y - 1, 0), 0, fmax(y + 4, 0)); + } + + if(pos == song->loop_end) { + int16_t y = (pos - (sequence_position - 2)) * 6; + + canvas_draw_line(canvas, 0, fmax(y + 4, 0), 1, fmax(y + 4, 0)); + canvas_draw_line(canvas, 0, fmax(y - 1, 0), 0, fmax(y + 4, 0)); + + break; + } + } + } + + canvas_set_color(canvas, ColorXOR); + } + + canvas_set_color(canvas, ColorBlack); + + canvas_draw_line(canvas, SEQ_SLIDER_X, 0, SEQ_SLIDER_X + 2, 0); + canvas_draw_line(canvas, SEQ_SLIDER_X, SEQ_SLIDER_Y, SEQ_SLIDER_X + 2, SEQ_SLIDER_Y); + + canvas_draw_line(canvas, SEQ_SLIDER_X, 0, SEQ_SLIDER_X, SEQ_SLIDER_Y); + canvas_draw_line(canvas, SEQ_SLIDER_X + 2, 0, SEQ_SLIDER_X + 2, SEQ_SLIDER_Y); + + uint8_t start_pos = + sequence_position * (SEQ_SLIDER_Y - 2) / tracker->song.num_sequence_steps + 1; + uint8_t slider_length = (SEQ_SLIDER_Y - 2) / tracker->song.num_sequence_steps + 1; + + canvas_draw_line( + canvas, SEQ_SLIDER_X + 1, start_pos, SEQ_SLIDER_X + 1, (start_pos + slider_length)); + + canvas_set_color(canvas, ColorXOR); + + if(tracker->editing && tracker->focus == EDIT_SEQUENCE) { + uint8_t x = tracker->current_channel * (4 + 4 + 1) + (tracker->current_digit ? 4 : 0) + 2; + uint8_t y = 11; + + canvas_draw_box(canvas, x, y, 5, 7); + } + + if(!(tracker->editing) && tracker->focus == EDIT_SEQUENCE) { + uint8_t x = tracker->current_channel * (4 + 4 + 1) + (tracker->current_digit ? 4 : 0) + 2; + uint8_t y = 11; + + canvas_draw_frame(canvas, x, y, 5, 7); + } +} + +#define member_size(type, member) sizeof(((type*)0)->member) + +#define SONG_HEADER_SIZE \ + (member_size(TrackerSong, song_name) + member_size(TrackerSong, speed) + \ + member_size(TrackerSong, rate) + member_size(TrackerSong, loop_start) + \ + member_size(TrackerSong, loop_end) + member_size(TrackerSong, num_patterns) + \ + member_size(TrackerSong, num_sequence_steps) + member_size(TrackerSong, num_instruments) + \ + member_size(TrackerSong, pattern_length)) + +uint32_t calculate_song_size(TrackerSong* song) { + uint32_t song_size = + SONG_HEADER_SIZE + sizeof(Instrument) * song->num_instruments + + sizeof(TrackerSongPatternStep) * song->num_patterns * song->pattern_length + + sizeof(TrackerSongSequenceStep) * song->num_sequence_steps; + return song_size; +} + +void draw_generic_n_digit_field( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint8_t digits) // last 1-2 symbols are digits we are editing +{ + canvas_draw_str(canvas, x, y, text); + + if(tracker->focus == focus && tracker->selected_param == param && tracker->editing) { + bool select_string = true; + + if(tracker->focus == EDIT_SONGINFO) { + if(param != SI_SONGNAME && param != SI_INSTRUMENTNAME) { + select_string = false; + } + } + + if(tracker->focus == EDIT_INSTRUMENT) { + if(param != INST_INSTRUMENTNAME) { + select_string = false; + } + } + + if(!(select_string)) { + if(tracker->focus == EDIT_INSTRUMENT && param == INST_CURRENTINSTRUMENT) { + canvas_draw_box(canvas, x + strlen(text) * 4 - digits * 4 - 1, y - 6, 5, 7); + } + + else { + canvas_draw_box( + canvas, + x + strlen(text) * 4 - digits * 4 + tracker->current_digit * 4 - 1, + y - 6, + 5, + 7); + } + } + + else { + canvas_draw_box(canvas, x - 1, y - 6, fmax(5, strlen(text) * 4 + 1), 7); + } + } + + if(tracker->focus == focus && tracker->selected_param == param && !(tracker->editing)) { + bool select_string = true; + + if(tracker->focus == EDIT_SONGINFO) { + if(param != SI_SONGNAME && param != SI_INSTRUMENTNAME) { + select_string = false; + } + } + + if(tracker->focus == EDIT_INSTRUMENT) { + if(param != INST_INSTRUMENTNAME) { + select_string = false; + } + } + + if(!(select_string)) { + if(tracker->focus == EDIT_INSTRUMENT && param == INST_CURRENTINSTRUMENT) { + canvas_draw_frame(canvas, x + strlen(text) * 4 - digits * 4 - 1, y - 6, 5, 7); + } + + else { + canvas_draw_frame( + canvas, + x + strlen(text) * 4 - digits * 4 + tracker->current_digit * 4 - 1, + y - 6, + 5, + 7); + } + } + + else { + canvas_draw_frame(canvas, x - 1, y - 6, fmax(5, strlen(text) * 4 + 1), 7); + } + } +} + +void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker) { + char buffer[30]; + + snprintf( + buffer, + sizeof(buffer), + "PAT.P.%02X/%02X", + tracker->tracker_engine.pattern_position, + tracker->song.pattern_length - 1); + draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_PATTERNPOS, buffer, 42, 5, 2); + snprintf( + buffer, + sizeof(buffer), + "SEQ.P.%02X/%02X", + tracker->tracker_engine.sequence_position, + tracker->song.num_sequence_steps - 1); + draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_SEQUENCEPOS, buffer, 42, 11, 2); + snprintf(buffer, sizeof(buffer), "SPD.%02X", tracker->song.speed); + draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_SONGSPEED, buffer, 42, 17, 2); + snprintf(buffer, sizeof(buffer), "RATE %02X", tracker->song.rate); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_SONGRATE, buffer, 42 + 4 * 7, 17, 2); + snprintf(buffer, sizeof(buffer), "VOL %02X", tracker->tracker_engine.master_volume); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_MASTERVOL, buffer, 42 + 4 * 7 + 4 * 8, 17, 2); + + snprintf(buffer, sizeof(buffer), "SONG:"); + canvas_draw_str(canvas, 42, 23, buffer); + snprintf(buffer, sizeof(buffer), "%s", tracker->song.song_name); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_SONGNAME, buffer, 42 + 4 * 5, 23, 1); + + snprintf(buffer, sizeof(buffer), "INST:%c", to_char(tracker->current_instrument)); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_CURRENTINSTRUMENT, buffer, 42, 29, 1); + snprintf( + buffer, sizeof(buffer), "%s", tracker->song.instrument[tracker->current_instrument]->name); + draw_generic_n_digit_field( + tracker, canvas, EDIT_SONGINFO, SI_INSTRUMENTNAME, buffer, 42 + 4 * 7, 29, 1); + + uint32_t song_size = calculate_song_size(&tracker->song); + uint32_t free_bytes = memmgr_get_free_heap(); + canvas_draw_line(canvas, 128 - 4 * 10 - 2, 0, 128 - 4 * 10 - 2, 10); + + char song_size_buffer[19]; + char free_bytes_buffer[19]; + + if(song_size > 9999) { + snprintf( + song_size_buffer, + sizeof(song_size_buffer), + "TUNE:%ld%c%01ldK", + song_size / 1024, + '.', + (song_size % 1024) / 103); + } + + else { + snprintf(song_size_buffer, sizeof(song_size_buffer), "TUNE:%ld", song_size); + } + + if(free_bytes > 9999) { + snprintf( + free_bytes_buffer, + sizeof(song_size_buffer), + "FREE:%ld%c%01ldK", + free_bytes / 1024, + '.', + (free_bytes % 1024) / 103); + } + + else { + snprintf(free_bytes_buffer, sizeof(song_size_buffer), "FREE:%ld", free_bytes); + } + + canvas_draw_str(canvas, 128 - 4 * 10, 5, song_size_buffer); + canvas_draw_str(canvas, 128 - 4 * 10, 11, free_bytes_buffer); +} \ No newline at end of file diff --git a/applications/external/flizzer_tracker/view/pattern_editor.h b/applications/external/flizzer_tracker/view/pattern_editor.h new file mode 100644 index 000000000..707b6abb8 --- /dev/null +++ b/applications/external/flizzer_tracker/view/pattern_editor.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../flizzer_tracker.h" +#include "../tracker_engine/tracker_engine_defs.h" + +#include +#include + +extern const char to_char_array[]; + +void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker); +void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker); +void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker); + +void draw_generic_n_digit_field( + FlizzerTrackerApp* tracker, + Canvas* canvas, + uint8_t focus, + uint8_t param, + const char* text, + uint8_t x, + uint8_t y, + uint8_t digits); +char to_char(uint8_t number); +char* notename(uint8_t note); \ No newline at end of file diff --git a/applications/external/nrf24_batch/LICENSE b/applications/external/nrf24_batch/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/nrf24_batch/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/applications/external/nrf24_batch/application.fam b/applications/external/nrf24_batch/application.fam new file mode 100644 index 000000000..3d09a2e7f --- /dev/null +++ b/applications/external/nrf24_batch/application.fam @@ -0,0 +1,20 @@ +App( + appid="nrf24_batch", + name="[NRF24] Batch", + apptype=FlipperAppType.EXTERNAL, + entry_point="nrf24batch_app", + cdefines=["APP_NRF24BATCH"], + requires=["gui"], + stack_size=2 * 1024, + order=60, + fap_icon="nrf24batch_10px.png", + fap_category="GPIO", + fap_private_libs=[ + Lib( + name="nrf24", + sources=[ + "nrf24.c", + ], + ), + ], +) diff --git a/applications/external/nrf24_batch/lib/nrf24/nrf24.c b/applications/external/nrf24_batch/lib/nrf24/nrf24.c new file mode 100644 index 000000000..789002f80 --- /dev/null +++ b/applications/external/nrf24_batch/lib/nrf24/nrf24.c @@ -0,0 +1,365 @@ +// Modified by vad7, 24.02.2023 +// +#include "nrf24.h" +#include +#include +#include +#include +#include + +void nrf24_init() { + furi_hal_spi_bus_handle_init(nrf24_HANDLE); + furi_hal_spi_acquire(nrf24_HANDLE); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_write(nrf24_CE_PIN, false); +} + +void nrf24_deinit() { + furi_hal_spi_release(nrf24_HANDLE); + furi_hal_spi_bus_handle_deinit(nrf24_HANDLE); + furi_hal_gpio_write(nrf24_CE_PIN, false); + furi_hal_gpio_init(nrf24_CE_PIN, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void nrf24_spi_trx( + FuriHalSpiBusHandle* handle, + uint8_t* tx, + uint8_t* rx, + uint8_t size) { + furi_hal_gpio_write(handle->cs, false); + furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT); + furi_hal_gpio_write(handle->cs, true); +} + +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) { + uint8_t buf[] = {W_REGISTER | (REGISTER_MASK & reg), data}; + nrf24_spi_trx(handle, buf, buf, 2); + //FURI_LOG_D("NRF_WR", " #%02X=%02X", reg, data); + return buf[0]; +} + +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t buf[size + 1]; + buf[0] = W_REGISTER | (REGISTER_MASK & reg); + memcpy(&buf[1], data, size); + nrf24_spi_trx(handle, buf, buf, size + 1); + //FURI_LOG_D("NRF_WR", " #%02X(%02X)=0x%02X%02X%02X%02X%02X", reg, size, data[0], data[1], data[2], data[3], data[4] ); + return buf[0]; +} + +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t buf[size + 1]; + memset(buf, 0, size + 1); + buf[0] = R_REGISTER | (REGISTER_MASK & reg); + nrf24_spi_trx(handle, buf, buf, size + 1); + memcpy(data, &buf[1], size); + return buf[0]; +} + +uint8_t nrf24_read_register(FuriHalSpiBusHandle* handle, uint8_t reg) { + uint8_t buf[] = { R_REGISTER | (REGISTER_MASK & reg), 0 }; + nrf24_spi_trx(handle, buf, buf, 2); + return buf[1]; +} + +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_RX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1); + return rx[0]; +} + +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_TX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1); + return rx[0]; +} + +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle) { + uint8_t maclen; + nrf24_read_reg(handle, REG_SETUP_AW, &maclen, 1); + maclen &= 3; + return maclen + 2; +} + +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) { + assert(maclen > 1 && maclen < 6); + uint8_t status = 0; + status = nrf24_write_reg(handle, REG_SETUP_AW, maclen - 2); + return status; +} + +uint8_t nrf24_status(FuriHalSpiBusHandle* handle) { + uint8_t tx = RF24_NOP; + nrf24_spi_trx(handle, &tx, &tx, 1); + return tx; +} + +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) { + uint8_t setup = 0; + uint32_t rate = 0; + nrf24_read_reg(handle, REG_RF_SETUP, &setup, 1); + setup &= 0x28; + if(setup == 0x20) + rate = 250000; // 250kbps + else if(setup == 0x08) + rate = 2000000; // 2Mbps + else if(setup == 0x00) + rate = 1000000; // 1Mbps + + return rate; +} + +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate) { + uint8_t r6 = 0; + uint8_t status = 0; + if(!rate) rate = 2000000; + + nrf24_read_reg(handle, REG_RF_SETUP, &r6, 1); // RF_SETUP register + r6 = r6 & (~0x28); // Clear rate fields. + if(rate == 2000000) + r6 = r6 | 0x08; + else if(rate == 1000000) + r6 = r6; + else if(rate == 250000) + r6 = r6 | 0x20; + + status = nrf24_write_reg(handle, REG_RF_SETUP, r6); // Write new rate. + return status; +} + +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle) { + uint8_t channel = 0; + nrf24_read_reg(handle, REG_RF_CH, &channel, 1); + return channel; +} + +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan) { + uint8_t status; + status = nrf24_write_reg(handle, REG_RF_CH, chan); + return status; +} + +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, mac, size); + return status; +} + +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) { + uint8_t size = 0; + uint8_t status = 0; + size = nrf24_get_maclen(handle); + status = nrf24_read_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) { + uint8_t status = 0; + uint8_t clearmac[] = {0, 0, 0, 0, 0}; + nrf24_set_maclen(handle, size); + nrf24_write_buf_reg(handle, REG_TX_ADDR, clearmac, 5); + status = nrf24_write_buf_reg(handle, REG_TX_ADDR, mac, size); + return status; +} + +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle, uint8_t pipe) { + uint8_t len = 0; + if(pipe > 5) pipe = 0; + nrf24_read_reg(handle, RX_PW_P0 + pipe, &len, 1); + return len; +} + +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { + uint8_t status = 0; + status = nrf24_write_reg(handle, RX_PW_P0, len); + return status; +} + +// packet_size: 0 - dyn payload (read from PL_WID), 1 - read from pipe size, >1 - override +// Return STATUS reg + additional: RX_DR - new data available, 0x80 - NRF24 hardware error +uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size) { + uint8_t status = 0; + uint8_t buf[33]; // 32 max payload size + 1 for command + + status = nrf24_status(handle); + if(!(status & RX_DR)) { + uint8_t st = nrf24_read_register(handle, REG_FIFO_STATUS); + if(st == 0xFF || st == 0) return 0x80; // hardware error + if((st & 1) == 0) { + FURI_LOG_D("NRF", "FIFO PKT"); + status |= RX_DR; // packet in FIFO buffer + } + } + if(status & RX_DR) { + if(status & 0x80) return 0x80; // hardware error + if(packet_size == 1) + packet_size = nrf24_get_packetlen(handle, (status >> 1) & 7); + else if(packet_size == 0){ + buf[0] = R_RX_PL_WID; buf[1] = 0xFF; + nrf24_spi_trx(handle, buf, buf, 2); + packet_size = buf[1]; + } + if(packet_size > 32 || packet_size == 0) packet_size = 32; + memset(buf, 0, packet_size + 1); + buf[0] = R_RX_PAYLOAD; + nrf24_spi_trx(handle, buf, buf, packet_size + 1); + memcpy(packet, &buf[1], packet_size); + nrf24_write_reg(handle, REG_STATUS, RX_DR); // clear RX_DR + } + if(status & (MAX_RT)) { // MAX_RT + nrf24_write_reg(handle, REG_STATUS, (MAX_RT)); // clear MAX_RT. + } + + *ret_packetsize = packet_size; + return status; +} + +// Return 0 when error +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) { + uint8_t status = 0; + uint8_t buf[size + 1]; + buf[0] = ack ? W_TX_PAYLOAD : W_TX_PAYLOAD_NOACK; + memcpy(&buf[1], payload, size); + nrf24_set_tx_mode(handle); + nrf24_spi_trx(handle, buf, buf, size + 1); + uint32_t start_time = furi_get_tick(); + do { + furi_delay_us(100); + status = nrf24_status(handle); + } while(!(status & (TX_DS | MAX_RT)) && furi_get_tick() - start_time < 100UL); + if(status & MAX_RT) { + if(furi_log_get_level() == FuriLogLevelDebug) FURI_LOG_D("NRF", "MAX RT: %X (%X)", nrf24_read_register(handle, REG_OBSERVE_TX), status); + nrf24_flush_tx(handle); + } + furi_hal_gpio_write(nrf24_CE_PIN, false); + //nrf24_set_idle(handle); + if(status & (TX_DS | MAX_RT)) nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); + return status & TX_DS; +} + +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg = cfg | 2; + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + //furi_delay_ms(1000); + return status; +} + +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg &= 0xfc; // clear bottom two bits to power down the radio + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, false); + return status; +} + +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) { + uint8_t cfg = 0; + cfg = nrf24_read_register(handle, REG_CONFIG); + cfg |= 0x03; // PWR_UP, and PRIM_RX + cfg = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + return cfg; +} + +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) { + uint8_t reg; + furi_hal_gpio_write(nrf24_CE_PIN, false); + //nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT); + reg = nrf24_read_register(handle, REG_CONFIG); + reg &= ~0x01; // disable PRIM_RX + reg |= 0x02; // PWR_UP + reg = nrf24_write_reg(handle, REG_CONFIG, reg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + return reg; +} + +void hexlify(uint8_t* in, uint8_t size, char* out) { + memset(out, 0, size * 2); + for(int i = 0; i < size; i++) + snprintf(out + strlen(out), sizeof(out + strlen(out)), "%02X", in[i]); +} + +uint64_t bytes_to_int64(uint8_t* bytes, uint8_t size, bool bigendian) { + uint64_t ret = 0; + for(int i = 0; i < size; i++) + if(bigendian) + ret |= bytes[i] << ((size - 1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 8; i++) { + if(bigendian) + out[i] = (val >> ((7 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian) { + uint32_t ret = 0; + for(int i = 0; i < 4; i++) + if(bigendian) + ret |= bytes[i] << ((3 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 4; i++) { + if(bigendian) + out[i] = (val >> ((3 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint64_t bytes_to_int16(uint8_t* bytes, bool bigendian) { + uint16_t ret = 0; + for(int i = 0; i < 2; i++) + if(bigendian) + ret |= bytes[i] << ((1 - i) * 8); + else + ret |= bytes[i] << (i * 8); + + return ret; +} + +void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) { + for(int i = 0; i < 2; i++) { + if(bigendian) + out[i] = (val >> ((1 - i) * 8)) & 0xff; + else + out[i] = (val >> (i * 8)) & 0xff; + } +} + +uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen) +{ + uint8_t addr[5]; + for(int i = 0; i < mlen; i++) addr[i] = mac[mlen - i - 1]; + return nrf24_write_buf_reg(nrf24_HANDLE, mac_addr, addr, mlen); +} \ No newline at end of file diff --git a/applications/external/nrf24_batch/lib/nrf24/nrf24.h b/applications/external/nrf24_batch/lib/nrf24/nrf24.h new file mode 100644 index 000000000..a05ddbebc --- /dev/null +++ b/applications/external/nrf24_batch/lib/nrf24/nrf24.h @@ -0,0 +1,386 @@ +#pragma once +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define R_REGISTER 0x00 +#define W_REGISTER 0x20 +#define REGISTER_MASK 0x1F +#define ACTIVATE 0x50 +#define R_RX_PL_WID 0x60 +#define R_RX_PAYLOAD 0x61 +#define W_TX_PAYLOAD 0xA0 +#define W_TX_PAYLOAD_NOACK 0xB0 +#define W_ACK_PAYLOAD 0xA8 +#define FLUSH_TX 0xE1 +#define FLUSH_RX 0xE2 +#define REUSE_TX_PL 0xE3 +#define RF24_NOP 0xFF + +#define REG_CONFIG 0x00 +#define REG_EN_AA 0x01 +#define REG_EN_RXADDR 0x02 +#define REG_SETUP_AW 0x03 +#define REG_SETUP_RETR 0x04 +#define REG_DYNPD 0x1C +#define REG_FEATURE 0x1D +#define REG_RF_SETUP 0x06 +#define REG_STATUS 0x07 +#define REG_RX_ADDR_P0 0x0A +#define REG_RX_ADDR_P1 0x0B +#define REG_RX_ADDR_P2 0x0C +#define REG_RX_ADDR_P3 0x0D +#define REG_RX_ADDR_P4 0x0E +#define REG_RX_ADDR_P5 0x0F +#define REG_RF_CH 0x05 +#define REG_TX_ADDR 0x10 +#define REG_FIFO_STATUS 0x17 +#define REG_OBSERVE_TX 0x08 + +#define RX_PW_P0 0x11 +#define RX_PW_P1 0x12 +#define RX_PW_P2 0x13 +#define RX_PW_P3 0x14 +#define RX_PW_P4 0x15 +#define RX_PW_P5 0x16 +#define RX_DR 0x40 +#define TX_DS 0x20 +#define MAX_RT 0x10 +#define NRF24_EN_DYN_ACK 0x01 + +#define nrf24_TIMEOUT 500 +#define nrf24_CE_PIN &gpio_ext_pb2 +#define nrf24_HANDLE &furi_hal_spi_bus_handle_external + +/* Low level API */ + +/** Write device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * + * @return device status + */ +uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data); + +/** Write buffer to device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param data - data to write + * @param size - size of data to write + * + * @return device status + */ +uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +/** Read device register + * + * @param handle - pointer to FuriHalSpiHandle + * @param reg - register + * @param[out] data - pointer to data + * + * @return device status + */ +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size); + +// Read single register (1 byte) +uint8_t nrf24_read_register(FuriHalSpiBusHandle* handle, uint8_t reg); + +/** Power up the radio for operation + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle); + +/** Power down the radio + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle); + +/** Sets the radio to RX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle); + +/** Sets the radio to TX mode + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle); + +/*=============================================================================================================*/ + +/* High level API */ + +/** Must call this before using any other nrf24 API + * + */ +void nrf24_init(); + +/** Must call this when we end using nrf24 device + * + */ +void nrf24_deinit(); + +/** Send flush rx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle); + +/** Send flush tx command + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return device status + */ +uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle); + +/** Gets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * pipe - pipe index (0..5) + * @return packet length in data pipe 0 + */ +uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle, uint8_t pipe); + +/** Sets the RX packet length in data pipe 0 + * + * @param handle - pointer to FuriHalSpiHandle + * @param len - length to set + * + * @return device status + */ +uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len); + +/** Gets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return MAC address length + */ +uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle); + +/** Sets configured length of MAC address + * + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length to set MAC address to, must be greater than 1 and less than 6 + * + * @return MAC address length + */ +uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen); + +/** Gets the current status flags from the STATUS register + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return status flags + */ +uint8_t nrf24_status(FuriHalSpiBusHandle* handle); + +/** Gets the current transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return transfer rate in bps + */ +uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle); + +/** Sets the transfer rate + * + * @param handle - pointer to FuriHalSpiHandle + * @param rate - the transfer rate in bps + * + * @return device status + */ +uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate); + +/** Gets the current channel + * In nrf24, the channel number is multiplied times 1MHz and added to 2400MHz to get the frequency + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return channel + */ +uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle); + +/** Sets the channel + * + * @param handle - pointer to FuriHalSpiHandle + * @param frequency - the frequency in hertz + * + * @return device status + */ +uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan); + +/** Gets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the source mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Gets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] mac - the source mac address + * + * @return device status + */ +uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac); + +/** Sets the dest mac address + * + * @param handle - pointer to FuriHalSpiHandle + * @param mac - the mac address to set + * @param size - the size of the mac address (2 to 5) + * + * @return device status + */ +uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size); + +/** Reads RX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param[out] packet - the packet contents + * @param[out] ret_packetsize - size of the received packet + * @param packet_size: >1 - size, 1 - packet length is determined by RX_PW_P0 register, 0 - it is determined by dynamic payload length command + * + * @return device status + */ +uint8_t + nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size_flag); + +/** Sends TX packet + * + * @param handle - pointer to FuriHalSpiHandle + * @param packet - the packet contents + * @param size - packet size + * @param ack - boolean to determine whether an ACK is required for the packet or not + * + * @return device status + */ +uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack); + +/** Configure the radio + * This is not comprehensive, but covers a lot of the common configuration options that may be changed + * @param handle - pointer to FuriHalSpiHandle + * @param rate - transfer rate in Mbps (1 or 2) + * @param srcmac - source mac address + * @param dstmac - destination mac address + * @param maclen - length of mac address + * @param channel - channel to tune to + * @param noack - if true, disable auto-acknowledge + * @param disable_aa - if true, disable ShockBurst + * + */ +void nrf24_configure( + FuriHalSpiBusHandle* handle, + uint8_t rate, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t channel, + bool noack, + bool disable_aa); + +// Set mac address (MSB first), Return: Status +uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen); + +/** Configures the radio for "promiscuous mode" and primes it for rx + * This is not an actual mode of the nrf24, but this function exploits a few bugs in the chip that allows it to act as if it were. + * See http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html for details. + * @param handle - pointer to FuriHalSpiHandle + * @param channel - channel to tune to + * @param rate - transfer rate in Mbps (1 or 2) + */ +void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate); + +/** Listens for a packet and returns first possible address sniffed + * Call this only after calling nrf24_init_promisc_mode + * @param handle - pointer to FuriHalSpiHandle + * @param maclen - length of target mac address + * @param[out] addresses - sniffed address + * + * @return success + */ +bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address); + +/** Sends ping packet on each channel for designated tx mac looking for ack + * + * @param handle - pointer to FuriHalSpiHandle + * @param srcmac - source address + * @param dstmac - destination address + * @param maclen - length of address + * @param rate - transfer rate in Mbps (1 or 2) + * @param min_channel - channel to start with + * @param max_channel - channel to end at + * @param autoinit - if true, automatically configure radio for this channel + * + * @return channel that the address is listening on, if this value is above the max_channel param, it failed + */ +uint8_t nrf24_find_channel( + FuriHalSpiBusHandle* handle, + uint8_t* srcmac, + uint8_t* dstmac, + uint8_t maclen, + uint8_t rate, + uint8_t min_channel, + uint8_t max_channel, + bool autoinit); + +/** Converts 64 bit value into uint8_t array + * @param val - 64-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian); + +/** Converts 32 bit value into uint8_t array + * @param val - 32-bit integer + * @param[out] out - bytes out + * @param bigendian - if true, convert as big endian, otherwise little endian + */ +void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian); + +/** Converts uint8_t array into 32 bit value + * @param bytes - uint8_t array + * @param bigendian - if true, convert as big endian, otherwise little endian + * + * @return 32-bit value + */ +uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/external/nrf24_batch/nrf24batch.c b/applications/external/nrf24_batch/nrf24batch.c new file mode 100644 index 000000000..047504105 --- /dev/null +++ b/applications/external/nrf24_batch/nrf24batch.c @@ -0,0 +1,1957 @@ +// +// Written by vad7, 10.01.2023. vad7@yahoo.com +// +#include "nrf24batch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "nrf24batch" +#define VERSION "1.9" + +#define SCAN_APP_PATH_FOLDER "/ext/nrf24batch" +#define LOG_FILEEXT ".txt" +#define NRF_READ_TIMEOUT 300UL // ms +#define WORK_PERIOD 2 // ms, Timer period +#define MAX_CHANNEL 125 +#define FONT_5x7_SCREEN_WIDTH 25 +#define NRF_EN_DYN_ACK 0 // does not work on some nrf24l01+ chips, (0/1) + +const char SettingsFld_Info[] = "Info:"; +const char SettingsFld_Ch[] = "Ch:"; +const char SettingsFld_Rate[] = "Rate:"; +const char SettingsFld_DPL[] = "DPL:"; +const char SettingsFld_CRC[] = "CRC:"; +const char SettingsFld_RETR[] = "RETR:"; +const char SettingsFld_Address[] = "Address:"; +const char SettingsFld_Resend[] = "Resend:"; +const char SettingsFld_Delay[] = "Delay_ms:"; +const char SettingsFld_WriteStart[] = "Write start:"; +const char SettingsFld_Payload[] = "Payload struct:"; +const char SettingsFld_ReadDefault[] = "R default:"; +const char SettingsFld_WriteDefault[] = "W default:"; +const char SettingsFld_Read[] = "R:"; // Read cmd +const char SettingsFld_Write[] = "W:"; // Write cmd +const char SettingsFld_Set[] = "S:"; // Set cmd (like Write but without "Write start" packet) +const char SettingsFld_ReadBatch[] = "RBatch:"; +const char SettingsFld_WriteBatch[] = "WBatch:"; +const char SettingsFld_SetBatch[] = "SBatch:"; +const char SettingsFld_Listen[] = "Listen:"; +const char SettingsFld_ReadCmdRepeatPeriod[] = "ReadCmd repeat:"; +const char AskQuestion_Save[] = "SAVE BATCH?"; +#define Settings_i 'i' +#define Settings_n 'n' +#define VAR_EMPTY ((int32_t)0x80000000) + +nRF24Batch* APP; +uint8_t what_doing = 0; // 0 - setup, 1 - cmd list, 2 - read/write/listen cmd +enum { + rwt_set_batch = 0, // fast send packet without question + rwt_read_batch, // Send read cmd and wait for answer in batch + rwt_read_cmd, // Send read cmd and wait for answer + rwt_write_batch, // Send write cmd (with Write start pkt if available) with a question before it + rwt_listen, // Listen mode (wait incoming pkts) + rwt_max +}; +uint8_t rw_type = rwt_read_batch; // What to do: rwt_* +enum { sst_none = 0, sst_sending, sst_receiving, sst_ok, sst_error, sst_timeout }; +uint8_t send_status = sst_none; // sst_* +bool cmd_array = false; +uint8_t cmd_array_idx; +uint8_t cmd_array_cnt = 0; +bool cmd_array_hex; +uint8_t save_settings = 0; +uint16_t view_cmd[rwt_max - 1] = {0}; // SetBatch, ReadBatch, Read, WriteBatch +uint8_t view_x = 0; +char screen_buf[64]; +char Info[35] = ""; +char file_name[FONT_5x7_SCREEN_WIDTH]; +char ERR_STR[FONT_5x7_SCREEN_WIDTH]; +uint8_t ERR = 0; +uint8_t NRF_rate; // 0 - 250Kbps, 1 - 1Mbps, 2 - 2Mbps +uint8_t NRF_channel; // 0..125 +uint8_t NRF_DPL; // 1 - Dynamic Payload Length +uint8_t NRF_CRC; // 1 - No, 1 - CRC 1byte, 2 - CRC 2byte +uint8_t NRF_RETR = ((0b0011 << 4) | 0b1111); // Automatic Retransmission, ARD, ARC +uint8_t NRF_Payload; // Payload len in bytes, 0..32 +bool NRF_ERROR = 0; +uint8_t NRF_INITED = 0; // 0 - not, 1 - rw, rwt_listen - listen +bool NRF_BOARD_POWER_5V = false; +uint8_t NRF_last_packet_send_st = 0; +uint8_t NRF_resend = 1; // number of transaction attempts +int8_t NRF_repeat = 0; // count number of repeated requests (until < NRF_resend) +uint32_t NRF_time; +uint16_t ReadCmdRepeatPeriod = 10; // s +bool ReadRepeat = false; +uint32_t delay_between_pkt = 10; // ms + +uint8_t addr[5]; // nRF24 address, MSB first +uint8_t addr_len = 0; // 2..5 +uint8_t payload[32]; +uint8_t payload_receive[32]; +uint8_t payload_struct[32]; // sizeof(1..4) in bytes of each field, example: 2,1,1 +uint8_t payload_fields = 0; +uint8_t payload_size = 0; // bytes +uint16_t view_Batch = 0; // view pos in Batch or inside WriteBatch (Log[view_Batch]) +uint16_t view_WriteBatch = 0; // view pos of WriteBatch list +uint8_t setup_cursor = 0; // cursor position on Setup scr +uint8_t Edit = 0; +char* Edit_pos; +char* Edit_start; +bool Edit_hex; // 0 - decimal, 1 - hex +bool Edited = false; // list of cmds edited + +Stream* file_stream = NULL; +FuriString* ReadDefault = NULL; +FuriString* WriteDefault = NULL; +FuriString* WriteStart = NULL; +FuriString* Constants = NULL; // text of STR=x +uint8_t listen_addr[5]; +uint8_t listen_addr_len = 0; +char* ListenFields = NULL; // ptr to string: field1,field2,... max 5 field now +bool ListenNew; +FuriHalRtcDateTime ListenLastTime = {0}; +uint32_t ListenPrev = 0; +uint32_t ListenLast = 0; +FuriString** Read_cmd = NULL; // Names of read cmd +uint16_t Read_cmd_Total = 0; +FuriString** Log = NULL; // Strings: var=n +uint16_t Log_Total = 0; +FuriString** ReadBatch_cmd = NULL; // Names of read batch cmd +uint16_t ReadBatch_cmd_Total = 0; +char* ReadBatch_cmd_curr = NULL; // =0xFFFFFFFF - finish +FuriString** WriteBatch_cmd = NULL; // Names of write batch cmd +uint16_t WriteBatch_cmd_Total = 0; +uint16_t WriteBatch_cmd_curr = 0; // == _Total - finish +FuriString** SetBatch_cmd = NULL; // Names of set batch cmd +uint16_t SetBatch_cmd_Total = 0; +uint16_t SetBatch_cmd_curr = 0; // == _Total - finish + +#define POWER_READ_PERIOD 501 // ms +uint16_t pwr_read_timer = 0; +int Current = 0; +int CurrentStart = 0; + +enum { ask_write_batch = 1, ask_save_batch, ask_skip_cmd, ask_return, ask_exit }; +uint8_t ask_question = 0; // 1 - Ask now - ask_* +uint8_t ask_question_answer = 0; // 0 - no, 1 - yes + +static bool ask_fill_screen_buf(void) { + if(ask_question == ask_write_batch) + strcpy(screen_buf, "RUN WRITE BATCH?"); + else if(ask_question == ask_save_batch) + strcpy(screen_buf, "SAVE AS WRITE BATCH?"); + else if(ask_question == ask_skip_cmd) + strcpy(screen_buf, "SKIP CMD?"); + else if(ask_question == ask_return) + strcpy(screen_buf, "RETURN?"); + else if(ask_question == ask_exit) + strcpy(screen_buf, "EXIT?"); + else + return false; + strcat(screen_buf, ask_question_answer ? " YES" : " NO"); + return true; +} + +//#define MIN(a, b) ((a= '0' && c <= '9') return true; + if(hex) { + c &= ~0x20; + if(c >= 'A' && c <= 'F') return true; + } else if(c == '-') + return true; + return false; +} + +// Return num bytes in array +static uint8_t ConvertHexToArray(char* hex, uint8_t* array, uint8_t maxlen) { + uint8_t len = 0; + while(maxlen) { + uint8_t ch = *hex++; + if(ch < ' ') break; + if(ch < '0') continue; + *array++ = (GetHexVal(ch) << 4) + GetHexVal(*hex++); + len++; + maxlen--; + } + return len; +} + +int32_t str_to_int(char* p) { + if(*(p + 1) == 'x') { // hex + return strtol(p + 2, NULL, 16); + } else + return strtol(p, NULL, 10); +} + +void str_rtrim(char* p) { + char* delim_col = strchr(p, '\r'); + if(delim_col) + *delim_col = '\0'; + else { + delim_col = strchr(p, '\n'); + if(delim_col) *delim_col = '\0'; + } +} + +static void add_to_str_hex_bytes(char* out, uint8_t* arr, int bytes) { + if(bytes <= 0) return; + out += strlen(out); + do { + snprintf(out, 3, "%02X", *arr++); + out += 2; + } while(--bytes); +} + +void Edit_insert_digit(char new) { + if(*Edit_pos == '-') return; + if(what_doing <= 1) { + if(strlen(Edit_start) < (what_doing == 0 && setup_cursor == 2 ? 3 : 5 * 2)) { + memmove(Edit_pos + 1, Edit_pos, strlen(Edit_pos) + 1); + *Edit_pos = new; + } + } else { + FuriString* fs = Log[view_Batch]; + FuriString* ns = furi_string_alloc(); + if(ns) { + uint16_t len = Edit_pos - (char*)furi_string_get_cstr(fs); + furi_string_set_n(ns, fs, 0, len); + furi_string_cat_printf(ns, "%c", new); + furi_string_cat_str(ns, Edit_pos); + Log[view_Batch] = ns; + Edit_pos = (char*)furi_string_get_cstr(ns); + Edit_start = Edit_pos + (Edit_start - (char*)furi_string_get_cstr(fs)); + Edit_pos += len; + furi_string_free(fs); + } + } +} + +int32_t get_payload_receive_field(uint8_t* var, uint8_t size) { + if(size <= 1) + return *var; + else if(size == 2) + return *(int16_t*)var; + else if(size == 3) + return (*(uint32_t*)var) & 0xFFFFFF; + else + return *(int32_t*)var; +} + +void free_Log() { + if(Log_Total) { + for(uint16_t i = 0; i < Log_Total; i++) + if(Log[i]) furi_string_free(Log[i]); + Log_Total = 0; + } + if(Log) { + free(Log); + Log = NULL; + } +} + +void free_store(void) { + if(Constants) { + furi_string_free(Constants); + Constants = NULL; + } + if(ReadDefault) { + furi_string_free(ReadDefault); + ReadDefault = NULL; + } + if(WriteDefault) { + furi_string_free(WriteDefault); + WriteDefault = NULL; + } + if(WriteStart) { + furi_string_free(WriteStart); + WriteDefault = NULL; + } + if(ListenFields) { + free(ListenFields); + ListenFields = NULL; + } + if(Read_cmd_Total) { + for(uint16_t i = 0; i < Read_cmd_Total; i++) furi_string_free(Read_cmd[i]); + Read_cmd_Total = 0; + } + if(Read_cmd) { + free(Read_cmd); + Read_cmd = NULL; + } + if(ReadBatch_cmd_Total) { + for(uint16_t i = 0; i < ReadBatch_cmd_Total; i++) furi_string_free(ReadBatch_cmd[i]); + ReadBatch_cmd_Total = 0; + } + if(ReadBatch_cmd) { + free(ReadBatch_cmd); + ReadBatch_cmd = NULL; + } + if(WriteBatch_cmd_Total) { + for(uint16_t i = 0; i < WriteBatch_cmd_Total; i++) furi_string_free(WriteBatch_cmd[i]); + WriteBatch_cmd_Total = 0; + } + if(WriteBatch_cmd) { + free(WriteBatch_cmd); + WriteBatch_cmd = NULL; + } + if(SetBatch_cmd_Total) { + for(uint16_t i = 0; i < SetBatch_cmd_Total; i++) furi_string_free(SetBatch_cmd[i]); + SetBatch_cmd_Total = 0; + } + if(SetBatch_cmd) { + free(SetBatch_cmd); + SetBatch_cmd = NULL; + } + free_Log(); +} + +void update_power(void) { + Current = furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000; +} + +void check_en_power_5V(void) { + if(!furi_hal_power_is_otg_enabled() && !furi_hal_power_is_charging()) { + FURI_LOG_D("PWR", "NO 5V, TURN ON"); + notification_message(APP->notification, &sequence_blink_yellow_100); + furi_delay_ms(10); + update_power(); + CurrentStart = Current; + furi_hal_power_enable_otg(); + NRF_BOARD_POWER_5V = true; + furi_delay_ms(100); + NRF_INITED = 0; + } +} + +static bool select_settings_file() { + DialogsApp* dialogs = furi_record_open("dialogs"); + bool result = false; + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, SCAN_APP_PATH_FOLDER); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".txt", NULL); + browser_options.hide_ext = false; + bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); + furi_record_close("dialogs"); + if(ret) { + if(!file_stream_open( + file_stream, furi_string_get_cstr(path), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { + FURI_LOG_D(TAG, "Cannot open file \"%s\"", furi_string_get_cstr(path)); + file_stream_close(file_stream); + } else { + FURI_LOG_D(TAG, "Open file \"%s\"", furi_string_get_cstr(path)); + strncpy( + file_name, + furi_string_get_cstr(path) + sizeof(SCAN_APP_PATH_FOLDER), + sizeof(file_name)); + result = true; + } + } + furi_string_free(path); + return result; +} + +static void prepare_nrf24(void) { + check_en_power_5V(); + if(!NRF_INITED || rw_type == rwt_listen || NRF_INITED == rwt_listen) { + FURI_LOG_D("NRF", "Prepare"); + uint8_t adrlen, *adr; + if(rw_type == rwt_listen) { + adrlen = listen_addr_len; + adr = listen_addr; + NRF_INITED = rwt_listen; + } else { + adrlen = addr_len; + adr = addr; + NRF_INITED = 1; + } + furi_hal_gpio_write(nrf24_CE_PIN, false); + nrf24_set_mac(REG_RX_ADDR_P0, adr, adrlen); + uint8_t tmp[5] = {0}; + nrf24_read_reg(nrf24_HANDLE, REG_RX_ADDR_P0, tmp, adrlen); + for(uint8_t i = 0; i < adrlen / 2; i++) { + uint8_t tb = tmp[i]; + tmp[i] = tmp[adrlen - i - 1]; + tmp[adrlen - i - 1] = tb; + } + NRF_ERROR = memcmp(adr, tmp, adrlen) != 0; + if(NRF_ERROR) { + NRF_INITED = 0; + return; + } + // EN_DYN_ACK(0x01) option for W_TX_PAYLOAD_NOACK cmd broke AA on some fake nRF24l01+, i.e. set it to 0 + nrf24_write_reg( + nrf24_HANDLE, + REG_FEATURE, + NRF_EN_DYN_ACK + + (NRF_DPL ? 4 : + 0)); // Dynamic Payload, Payload with ACK, W_TX_PAYLOAD_NOACK command + nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, NRF_channel); + nrf24_write_reg( + nrf24_HANDLE, + REG_RF_SETUP, + (NRF_rate == 0 ? 0b00100000 : + NRF_rate == 1 ? 0 : + 0b00001000) | + 0b111); // +TX high power + nrf24_write_reg( + nrf24_HANDLE, + REG_CONFIG, + 0x70 | ((NRF_CRC == 1 ? 0b1000 : + NRF_CRC == 2 ? 0b1100 : + 0))); // Mask all interrupts + nrf24_write_reg( + nrf24_HANDLE, REG_SETUP_RETR, NRF_RETR); // Automatic Retransmission, ARD<<4 + ARC + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x01); // Auto acknowledgement + nrf24_write_reg(nrf24_HANDLE, REG_DYNPD, NRF_DPL ? 0x3F : 0); // Enable dynamic payload reg + nrf24_write_reg(nrf24_HANDLE, RX_PW_P0, payload_size); + nrf24_set_maclen(nrf24_HANDLE, adrlen); + nrf24_set_mac(REG_TX_ADDR, adr, adrlen); + nrf24_write_reg(nrf24_HANDLE, REG_EN_RXADDR, 1); + //nrf24_set_idle(nrf24_HANDLE); + } + nrf24_flush_tx(nrf24_HANDLE); + nrf24_flush_rx(nrf24_HANDLE); + nrf24_write_reg(nrf24_HANDLE, REG_STATUS, MAX_RT | RX_DR | TX_DS); +} + +// true - ok +uint8_t nrf24_send_packet() { + if(furi_log_get_level() == FuriLogLevelDebug) { + char buf[65]; + buf[0] = 0; + add_to_str_hex_bytes(buf, payload, payload_size); + FURI_LOG_D(TAG, "SEND: %s", buf); + } + //nrf24_flush_tx(nrf24_HANDLE); + //nrf24_write_reg(nrf24_HANDLE, REG_STATUS, RX_DR | TX_DS | MAX_RT); + NRF_last_packet_send_st = nrf24_txpacket(nrf24_HANDLE, payload, payload_size, true); // ACK + if(NRF_last_packet_send_st) { + if((rw_type == rwt_read_cmd || rw_type == rwt_read_batch) && + send_status == sst_sending) { // Read + nrf24_set_rx_mode(nrf24_HANDLE); + send_status = sst_receiving; // receiving + } + } else + notification_message(APP->notification, &sequence_blink_red_100); + NRF_time = furi_get_tick(); + FURI_LOG_D( + TAG, + "Send packet: %d%s", + NRF_last_packet_send_st, + send_status == sst_receiving ? ", Receiving" : ""); + return NRF_last_packet_send_st; +} + +uint8_t nrf24_resend_read_packet() { + if(Log_Total && !cmd_array) { + FuriString* str = Log[Log_Total - 1]; + char* p = strstr(furi_string_get_cstr(str), ": "); + if(p) { + if(strncmp(p + 2, "0x", 2) == 0) p += 2; + furi_string_left(str, p - furi_string_get_cstr(str) + 2); + } + } + return nrf24_send_packet(); +} + +// true - new packet +bool nrf24_read_newpacket() { + bool found = false; + uint8_t packetsize; + uint8_t st = + nrf24_rxpacket(nrf24_HANDLE, payload_receive, &packetsize, NRF_DPL ? 0 : payload_size); + if(st & RX_DR) { + NRF_time = furi_get_tick(); + if(furi_log_get_level() == FuriLogLevelDebug) { + char buf[65]; + buf[0] = 0; + add_to_str_hex_bytes(buf, payload_receive, packetsize); + FURI_LOG_D(TAG, "READ(%X): %s", st, buf); + } + if(Log_Total) { + FuriString* str = Log[Log_Total - 1]; + uint8_t size = 1; + char* p = strchr((char*)furi_string_get_cstr(str), '*'); + if(p) { + p++; + if(*p == '=') + size = 0; // string + else { + size = *p - '0'; + if(size > 4) size = 0; + } + } + int32_t var = get_payload_receive_field(payload_receive, size); + //FURI_LOG_D(TAG, "VAR(%d): %ld", size, var); + if(size == 0) + furi_string_cat_printf(str, "%c", (char)var); + else { + char hex[9]; + hex[0] = '\0'; + add_to_str_hex_bytes(hex, (uint8_t*)&var, size); + if((cmd_array && cmd_array_hex) || furi_string_end_with_str(str, "0x")) + furi_string_cat_str(str, hex); + else { + if(var >= 0 && var <= 9) + furi_string_cat_printf(str, "%ld", var); + else + furi_string_cat_printf(str, "%ld (%s)", var, hex); + } + } + if(cmd_array) { + if(--cmd_array_cnt) { + furi_string_cat_str(str, ","); + if(cmd_array_hex) furi_string_cat_str(str, "0x"); + payload[cmd_array_idx] += size; // next array element + NRF_repeat = -1; + send_status = sst_sending; // Will be send after delay_between_pkt + } else + send_status = sst_ok; + } else { + if(size == 0) { // string, until '\0' + if(var == 0) send_status = sst_ok; + } else + send_status = sst_ok; + } + } + //notification_message(APP->notification, &sequence_blink_white_100); + found = true; + } else if(st & 0x80) { // NRF24 hardware error + NRF_ERROR = 1; + NRF_INITED = 0; + } + return found; +} + +// Search in constatnt pull (Const1=n; Const2=n;...) +// VAR_EMPTY - not found +int32_t subs_constant(char* p, uint8_t len) { + char* c = (char*)furi_string_get_cstr(Constants); + while((c = strchr(c, *p))) { + if(strncmp(c, p, len) != 0) { + c++; + continue; + } + if(c == (char*)furi_string_get_cstr(Constants) || *(c - 1) == ';' || *(c - 1) <= ' ') { + c += len; + if(*c == '=') { + c++; + return str_to_int(c); + } + } else + c += len; + } + return VAR_EMPTY; +} + +// fill payload with default = p +// if var_n = VAR_EMPTY - skip filling var_* +bool fill_payload(char* p, uint8_t* idx_i, int32_t var_n) { + if(idx_i) *idx_i = 255; + uint8_t fld = 0; // field # + uint8_t idx = 0; // byte index + do { + int32_t b = 0; + char* end = strchr(p, ','); + if(*p >= '0' && *p <= '9') { // Number found + b = str_to_int(p); + } else if(*p == 'i' && *(p + 1) == ':') { // 'i:' array index + b = str_to_int(p + 2); + if(idx_i) *idx_i = idx; + } else if(*p == 'n' && *(p + 1) < '0') { // var_n + if(var_n != VAR_EMPTY) b = var_n; + } else if(*p >= 'A') { // constant found + uint8_t len; + if(end) + len = end - p; + else { + len = strlen(p); + if(*(p + len - 1) == '#') len--; + } + b = subs_constant(p, len); + if(b == VAR_EMPTY) { + ERR = 1; + memset(ERR_STR, 0, sizeof(ERR_STR)); + strcpy(ERR_STR, "No "); + strncpy(ERR_STR + strlen(ERR_STR), p, sizeof(ERR_STR) - 4); + FURI_LOG_D(TAG, "Constant not found: %s", p); + return false; + } + } else if(end == p) { + idx += payload_struct[fld]; + } else if(*p == '#') { // value in Hexadecimal, end string + break; + } else { + ERR = 2; + strcpy(ERR_STR, "char: "); + uint8_t l = strlen(ERR_STR); + ERR_STR[l] = *p; + ERR_STR[l + 1] = '\0'; + FURI_LOG_D(TAG, "Wrong format char(%c)", *p); + return false; + } + if(end != p) { + payload[idx++] = b; + if(payload_struct[fld] > 1) payload[idx++] = b >> 8; + if(payload_struct[fld] > 2) payload[idx++] = b >> 16; + if(payload_struct[fld] > 3) payload[idx++] = b >> 24; + } + if(++fld == payload_fields || idx >= sizeof(payload) || end == NULL) break; + p = end + 1; + } while(1); + return true; +} + +// Cmd: "name=payload" +bool Run_Read_cmd(FuriString* cmd) { + char* p = (char*)furi_string_get_cstr(cmd); + p = strchr(p, '='); + if(p == NULL) return false; + if(Log == NULL) + Log = malloc(sizeof(Log)); + else + Log = realloc(Log, sizeof(Log) * (Log_Total + 1)); + if(Log == NULL) { + ERR = 3; + strcpy(ERR_STR, "Memory low"); + FURI_LOG_D(TAG, ERR_STR); + return false; + } + FuriString* fs = furi_string_alloc(); + furi_string_set_strn( + fs, (char*)furi_string_get_cstr(cmd), p - (char*)furi_string_get_cstr(cmd)); + furi_string_cat_str(fs, ": "); + bool hexval; + if((hexval = *(p + strlen(p) - 1) == '#')) + furi_string_cat_str(fs, "0x"); // value in Hex format + Log[Log_Total++] = fs; + p++; + memset(payload, 0, sizeof(payload)); + if(ReadDefault && !fill_payload((char*)furi_string_get_cstr(ReadDefault), NULL, VAR_EMPTY)) + return false; + if(!fill_payload(p, &cmd_array_idx, VAR_EMPTY)) return false; + memset(payload_receive, 0, sizeof(payload_receive)); + cmd_array = false; + if(*(p - 2) == ']' && cmd_array_idx != 255) { // array + p = strchr(furi_string_get_cstr(cmd), '['); + if(p) { + cmd_array_cnt = str_to_int(p + 1); + if(cmd_array_cnt > 1) { + cmd_array_hex = hexval; + cmd_array = true; // array + } + } + } + prepare_nrf24(); + if(NRF_ERROR) return false; + what_doing = 2; + NRF_repeat = 0; + send_status = sst_sending; // Read - sending + nrf24_send_packet(); + return true; +} + +// run commands one by one, true - command running +bool Run_ReadBatch_cmd(FuriString* cmd) { + char* p; + if(cmd) { + p = strchr((char*)furi_string_get_cstr(cmd), ':'); + if(p == NULL) { + ERR = 5; + strcpy(ERR_STR, "WRONG FORMAT"); + return false; + } + p += 2; + ReadBatch_cmd_curr = NULL; + free_Log(); + } else { + if(ReadBatch_cmd_curr) + p = ReadBatch_cmd_curr; + else + return false; + } + char* end = strchr(p, ';'); + uint8_t len; + if(end) + len = end - p; + else { + str_rtrim(p); + len = strlen(p); + } + for(uint16_t i = 0; i < Read_cmd_Total; i++) { + FuriString* fs = Read_cmd[i]; + if(strncmp((char*)furi_string_get_cstr(fs), p, len) == 0) { + char c = *((char*)furi_string_get_cstr(fs) + len); + if(c != '=' && c != '*' && c != '[') continue; + if(end) + ReadBatch_cmd_curr = end + 1; + else + ReadBatch_cmd_curr = (char*)0xFFFFFFFF; + if(!Run_Read_cmd(fs)) break; + return true; + } + } + if(NRF_ERROR) return false; + if(ERR == 0) { + ERR = 4; + strcpy(ERR_STR, "NOT FOUND"); + FuriString* fs = furi_string_alloc(); + furi_string_set_strn(fs, p, len); + Log[Log_Total++] = fs; + FURI_LOG_D(TAG, "CMD %s: %s", ERR_STR, p); + } + view_Batch = Log_Total ? Log_Total - 1 : 0; + return false; +} + +void Prepare_Write_cmd(FuriString* cmd) { + free_Log(); + if(cmd == NULL) return; + char *end, *p = strchr((char*)furi_string_get_cstr(cmd), ':'); + if(p == NULL) { + ERR = 8; + strcpy(ERR_STR, "Wrong batch"); + FURI_LOG_D(TAG, ERR_STR); + return; + } + p += 2; + Log = malloc(sizeof(Log)); + do { + end = strchr(p, ';'); + uint8_t len; + if(end) { + len = end - p; + end++; + } else { + str_rtrim(p); + len = strlen(p); + } + FuriString* fs = furi_string_alloc(); + if(Log_Total) Log = realloc(Log, sizeof(Log) * (Log_Total + 1)); + if(Log == NULL) { + ERR = 3; + strcpy(ERR_STR, "Memory low"); + FURI_LOG_D(TAG, ERR_STR); + return; + } + furi_string_set_strn(fs, p, len); + Log[Log_Total++] = fs; + } while((p = end)); +} + +// Write / Set batch +bool Run_WriteBatch_cmd() { + if(Log_Total == 0) return false; + uint16_t cmd_curr = rw_type == rwt_write_batch ? WriteBatch_cmd_curr : SetBatch_cmd_curr; + if(cmd_curr == 0) { // first + prepare_nrf24(); + if(NRF_ERROR) return false; + if(rw_type == rwt_write_batch && WriteStart) { + if(!fill_payload((char*)furi_string_get_cstr(WriteStart), NULL, VAR_EMPTY)) + return false; + send_status = sst_sending; + if(!nrf24_send_packet()) return false; + } + } + char* p = (char*)furi_string_get_cstr(Log[cmd_curr]); + uint16_t len = furi_string_size(Log[cmd_curr]); + char* arr = NULL; + cmd_array = false; + int32_t new = 0; + for(uint16_t i = 0; i < len; i++) { + if(p[i] == '=') { + len = i; + char* p2 = p + i + 1; + if(*p2 == '{') { + arr = ++p2; // array + cmd_array = true; + } + new = str_to_int(p2); + break; + } + } + FURI_LOG_D( + TAG, "%cBatch: =%d, (%d)%s", rw_type == rwt_write_batch ? 'W' : 'S', (int)new, len, p); + char *w, *delim_col, i, size; + FuriString* str = furi_string_alloc(); + stream_rewind(file_stream); + while(stream_read_line(file_stream, str)) { + w = (char*)furi_string_get_cstr(str); + if(strncmp(w, SettingsFld_Write, sizeof(SettingsFld_Write) - 1) == 0) + w += sizeof(SettingsFld_Write); + else if(strncmp(w, SettingsFld_Set, sizeof(SettingsFld_Set) - 1) == 0) + w += sizeof(SettingsFld_Set); + else + continue; + delim_col = strchr(w, '='); + if(delim_col == NULL) continue; + size = 1; + if(*(delim_col - 2) == '*') { + if(len != delim_col - w - 2) continue; + size = *(delim_col - 1) - '0'; + new = new&(size == 1 ? 0xFF : size == 2 ? 0xFFFF : size == 3 ? 0xFFFFFF : 0xFFFFFFFF); + } else if(len != delim_col - w) + continue; + if(strncmp(p, w, len) != 0) continue; + delim_col++; + str_rtrim(delim_col); + cmd_array_cnt = 255; + do { + memset(payload, 0, sizeof(payload)); + if(WriteDefault && + !fill_payload((char*)furi_string_get_cstr(WriteDefault), NULL, new)) { + view_Batch = cmd_curr; + return false; + } + if(!fill_payload(delim_col, &cmd_array_idx, VAR_EMPTY)) { + view_Batch = cmd_curr; + return false; + } + if(cmd_array && cmd_array_idx != 255) { + if(cmd_array_cnt != 255) payload[cmd_array_idx] = cmd_array_cnt; + } else + cmd_array = false; + send_status = sst_sending; + NRF_repeat = 0; + i = 0; + do { + if(nrf24_send_packet()) break; + furi_delay_ms(delay_between_pkt); + } while(i++ < NRF_resend); + if(i < NRF_resend || i == 0) { // ok + if(cmd_array) { // array + for(; arr != NULL;) { + if(*arr == ',') break; + if(*arr == '}' || *arr < ' ') + arr = NULL; + else + arr++; + } + if(arr == NULL) { + send_status = sst_ok; + break; + } + arr++; + new = str_to_int(arr); + cmd_array_cnt = payload[cmd_array_idx] + size; + //furi_delay_ms(delay_between_pkt); // do it fast + continue; // send next array element + } else + send_status = sst_ok; + } + break; + } while(1); + if(send_status != sst_ok) { + send_status = sst_error; + view_Batch = cmd_curr; + return false; + } + return true; + } + ERR = 7; + strcpy(ERR_STR, "NOT FOUND!"); + send_status = sst_error; + view_Batch = cmd_curr; + return false; +} + +// Return 0 - success, otherwise an error +static uint8_t load_settings_file() { + uint8_t err = 0; + FURI_LOG_D(TAG, "Loading settings file"); + FuriString* str = furi_string_alloc(); + free_store(); + Info[0] = '\0'; + NRF_INITED = false; + while(stream_read_line(file_stream, str)) { + char* p = (char*)furi_string_get_cstr(str); + if(*p <= ' ') continue; + //char* delim_eq = strchr(p, '='); + char* delim_col = strchr(p, ':'); + if(delim_col == NULL) { // Constant found - no ':' + if(Constants == NULL) { + Constants = furi_string_alloc_set(str); + } else + furi_string_cat(Constants, str); + } else { + str_rtrim(p); + if(strncmp(p, SettingsFld_Rate, sizeof(SettingsFld_Rate) - 1) == 0) { + NRF_rate = str_to_int(p + sizeof(SettingsFld_Rate)); + } else if(strncmp(p, SettingsFld_Info, sizeof(SettingsFld_Info) - 1) == 0) { + strncpy(Info, p + sizeof(SettingsFld_Info), sizeof(Info) - 1); + } else if(strncmp(p, SettingsFld_Ch, sizeof(SettingsFld_Ch) - 1) == 0) { + NRF_channel = str_to_int(p + sizeof(SettingsFld_Ch)); + } else if(strncmp(p, SettingsFld_Address, sizeof(SettingsFld_Address) - 1) == 0) { + p += sizeof(SettingsFld_Address); + addr_len = ConvertHexToArray(p, addr, 5); + } else if(strncmp(p, SettingsFld_CRC, sizeof(SettingsFld_CRC) - 1) == 0) { + NRF_CRC = str_to_int(p + sizeof(SettingsFld_CRC)); + } else if(strncmp(p, SettingsFld_DPL, sizeof(SettingsFld_DPL) - 1) == 0) { + NRF_DPL = str_to_int(p + sizeof(SettingsFld_DPL)); + } else if(strncmp(p, SettingsFld_RETR, sizeof(SettingsFld_RETR) - 1) == 0) { + NRF_RETR = str_to_int(p + sizeof(SettingsFld_RETR)); + } else if(strncmp(p, SettingsFld_Resend, sizeof(SettingsFld_Resend) - 1) == 0) { + NRF_resend = str_to_int(p + sizeof(SettingsFld_Resend)); + } else if(strncmp(p, SettingsFld_Delay, sizeof(SettingsFld_Delay) - 1) == 0) { + delay_between_pkt = str_to_int(p + sizeof(SettingsFld_Delay)); + } else if( + strncmp( + p, + SettingsFld_ReadCmdRepeatPeriod, + sizeof(SettingsFld_ReadCmdRepeatPeriod) - 1) == 0) { + ReadCmdRepeatPeriod = str_to_int(p + sizeof(SettingsFld_ReadCmdRepeatPeriod)); + } else if(strncmp(p, SettingsFld_Payload, sizeof(SettingsFld_Payload) - 1) == 0) { + p += sizeof(SettingsFld_Payload); + payload_fields = 0; + payload_size = 0; + do { + uint8_t b = str_to_int(p); + if(b < 1 || b > 4) { + FURI_LOG_D(TAG, "Wrong payload format (%d)", b); + err = 3; + break; + } + payload_struct[payload_fields++] = b; + payload_size += b; + if(payload_fields == sizeof(payload_struct) - 1) break; + if((p = strchr(p, ',')) == NULL) break; + p++; + } while(1); + FURI_LOG_D( + TAG, + "Payload fields %d: %d,%d,%d", + payload_fields, + payload_struct[0], + payload_struct[1], + payload_struct[2]); + } else if(strncmp(p, SettingsFld_ReadDefault, sizeof(SettingsFld_ReadDefault) - 1) == 0) { + ReadDefault = furi_string_alloc_set_str(p + sizeof(SettingsFld_ReadDefault)); + } else if(strncmp(p, SettingsFld_WriteStart, sizeof(SettingsFld_WriteStart) - 1) == 0) { + WriteStart = furi_string_alloc_set_str(p + sizeof(SettingsFld_WriteStart)); + } else if(strncmp(p, SettingsFld_WriteDefault, sizeof(SettingsFld_WriteDefault) - 1) == 0) { + WriteDefault = furi_string_alloc_set_str(p + sizeof(SettingsFld_WriteDefault)); + } else if(strncmp(p, SettingsFld_Read, sizeof(SettingsFld_Read) - 1) == 0) { + p += sizeof(SettingsFld_Read); + if(Read_cmd == NULL) + Read_cmd = malloc(sizeof(Read_cmd)); + else { + Read_cmd = realloc(Read_cmd, sizeof(Read_cmd) * (Read_cmd_Total + 1)); + } + if(Read_cmd == NULL) { + FURI_LOG_D(TAG, "Memory low, err 4"); + err = 4; + break; + } + Read_cmd[Read_cmd_Total++] = furi_string_alloc_set_str(p); + } else if(strncmp(p, SettingsFld_ReadBatch, sizeof(SettingsFld_ReadBatch) - 1) == 0) { + p += sizeof(SettingsFld_ReadBatch); + if(ReadBatch_cmd == NULL) + ReadBatch_cmd = malloc(sizeof(ReadBatch_cmd)); + else { + ReadBatch_cmd = + realloc(ReadBatch_cmd, sizeof(ReadBatch_cmd) * (ReadBatch_cmd_Total + 1)); + } + if(ReadBatch_cmd == NULL) { + FURI_LOG_D(TAG, "Memory low, err 5"); + err = 5; + break; + } + ReadBatch_cmd[ReadBatch_cmd_Total++] = furi_string_alloc_set_str(p); + } else if(strncmp(p, SettingsFld_WriteBatch, sizeof(SettingsFld_WriteBatch) - 1) == 0) { + p += sizeof(SettingsFld_WriteBatch); + if(WriteBatch_cmd == NULL) + WriteBatch_cmd = malloc(sizeof(WriteBatch_cmd)); + else { + WriteBatch_cmd = realloc( + WriteBatch_cmd, sizeof(WriteBatch_cmd) * (WriteBatch_cmd_Total + 1)); + } + if(WriteBatch_cmd == NULL) { + FURI_LOG_D(TAG, "Memory low, err 6"); + err = 6; + break; + } + WriteBatch_cmd[WriteBatch_cmd_Total++] = furi_string_alloc_set_str(p); + } else if(strncmp(p, SettingsFld_SetBatch, sizeof(SettingsFld_SetBatch) - 1) == 0) { + p += sizeof(SettingsFld_SetBatch); + if(SetBatch_cmd == NULL) + SetBatch_cmd = malloc(sizeof(SetBatch_cmd)); + else { + SetBatch_cmd = + realloc(SetBatch_cmd, sizeof(SetBatch_cmd) * (SetBatch_cmd_Total + 1)); + } + if(SetBatch_cmd == NULL) { + FURI_LOG_D(TAG, "Memory low, err 7"); + err = 7; + break; + } + SetBatch_cmd[SetBatch_cmd_Total++] = furi_string_alloc_set_str(p); + } else if(strncmp(p, SettingsFld_Listen, sizeof(SettingsFld_Listen) - 1) == 0) { + p += sizeof(SettingsFld_Listen); + char* p2 = strchr(p, '='); + if(p2) { + listen_addr_len = ConvertHexToArray(p, listen_addr, (p2 - p) / 2); + p2++; + uint8_t len = strlen(p2); + ListenFields = malloc(len + 1); + if(ListenFields) memcpy(ListenFields, p2, len); + } + } + } + } + furi_string_free(str); + return err; +} + +static void save_batch(void) { + FURI_LOG_D(TAG, "Save Batch"); + char *p, *p2; + stream_seek(file_stream, 0, StreamOffsetFromEnd); + FuriHalRtcDateTime dt; + furi_hal_rtc_get_datetime(&dt); + stream_write_format(file_stream, "\n%s ", SettingsFld_WriteBatch); + p = (char*)furi_string_get_cstr(ReadBatch_cmd[view_cmd[rwt_read_batch]]); + p2 = strchr(p, ':'); + if(p2 == NULL) p2 = p + strlen(p); + stream_write(file_stream, (uint8_t*)p, p2 - p); + stream_write_format( + file_stream, + " %02d.%02d.%02d %02d.%02d: ", + dt.day, + dt.month, + dt.year % 100, + dt.hour, + dt.minute); + for(uint16_t i = 0; i < Log_Total; i++) { + p = (char*)furi_string_get_cstr(Log[i]); + p2 = strchr(p, ':'); + if(p2 && *(p2 - 1) != '*') { // skip string + if(*(p2 - 1) == ']') { // array + char* p3 = strchr(p, '['); + if(p3) { + stream_write(file_stream, (uint8_t*)p, p3 - p - (*(p3 - 2) == '*' ? 2 : 0)); + stream_write_cstring(file_stream, "={"); + p = (p2 += 2); + do { + while(is_digit(p2, true) || *p2 == 'x') p2++; + stream_write(file_stream, (uint8_t*)p, p2 - p); + char c = *p2; + if(c == '\0') break; + if(c != ',') { + p2 = strchr(p2, ','); + if(p2 == NULL) break; + } + stream_write_char(file_stream, ','); + p = ++p2; + } while(1); + stream_write_char(file_stream, '}'); + } + } else { + stream_write(file_stream, (uint8_t*)p, p2 - p - (*(p2 - 2) == '*' ? 2 : 0)); + stream_write_char(file_stream, '='); + p2 += 2; + p = strchr(p2, ' '); + if(p == NULL) p = p2 + strlen(p2); + stream_write(file_stream, (uint8_t*)p2, p - p2); + } + if(i < Log_Total - 1) stream_write_char(file_stream, ';'); + } + } +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +void display_remove_asterisk(char* fsp, uint8_t vx) { + char* p2 = strchr(fsp, '*'); + if(p2) { // remove '*' or '*n' + int pos = p2 - fsp; + if((pos -= vx) < 0) pos = 0; + char c = *(p2 + 1); + if(*(screen_buf + pos)) + memmove( + screen_buf + pos, + screen_buf + pos + (c == ':' || c == '=' ? 1 : 2), + FONT_5x7_SCREEN_WIDTH + 1 + 2 - pos); + } +} + +void render_display_list( + Canvas* const canvas, + FuriString*** fsa, + char delim, + uint16_t view_pos, + uint16_t max_i) { + uint16_t page = view_pos & ~7; + char *p, *end; + uint16_t y, len; + for(uint8_t i = 0; i < 8 && page + i < max_i; i++) { + y = 14 + i * 7; + p = (char*)furi_string_get_cstr((*fsa)[page + i]); + end = strchr(p, delim); + if(end) { + len = MIN(end - p, view_x); + len = MIN(end - p - len, FONT_5x7_SCREEN_WIDTH); + strncpy(screen_buf, p + view_x, len); + screen_buf[len] = '\0'; + display_remove_asterisk(p, MIN(end - p, view_x)); + canvas_draw_str(canvas, 5, y, screen_buf); + } + if((view_pos & 7) == i) { + canvas_draw_str(canvas, 0, y, ">"); + canvas_draw_str(canvas, -1, y, ">"); + } + } +} + +void display_edit_ttf_font(Canvas* const canvas, uint8_t start_x, uint8_t start_y) { + screen_buf[0] = *Edit_pos; + screen_buf[1] = '\0'; + int n = canvas_string_width(canvas, screen_buf); + int len = Edit_pos - Edit_start; + memcpy(screen_buf, Edit_start, len); + screen_buf[len] = '\0'; + int x = start_x + canvas_string_width(canvas, screen_buf); + int len2 = strlen(Edit_pos); + memcpy(screen_buf + len, Edit_pos, len2); + screen_buf[len + len2] = '\0'; + canvas_draw_str(canvas, start_x, start_y, screen_buf); + start_y += 1; + canvas_draw_line( + canvas, x + (len ? 1 : 0), start_y, x + n + (*Edit_pos == '1' && len ? 1 : 0), start_y); +} + +void display_add_status(void) { + if(NRF_ERROR) + strcat(screen_buf, "nRF24 ERROR!"); + else if(ERR) + snprintf(screen_buf, sizeof(screen_buf), "ERROR %s", ERR_STR); + else if(send_status == sst_error) + strcat(screen_buf, "NO ACK!"); + else if(send_status == sst_timeout) + strcat(screen_buf, "TIMEOUT!"); + else if(send_status == sst_none) + ; + else if(send_status == sst_sending) + strcat(screen_buf, "sending"); + else if(send_status == sst_receiving) + strcat(screen_buf, "receiving"); + else if( + send_status == sst_ok && + (rw_type == rwt_read_cmd || + (rw_type == rwt_read_batch && (uint32_t)ReadBatch_cmd_curr == 0xFFFFFFFF) || + (rw_type == rwt_set_batch && SetBatch_cmd_curr == Log_Total) || + (rw_type == rwt_write_batch && WriteBatch_cmd_curr == Log_Total))) + strcat(screen_buf, "OK"); + else + strcat(screen_buf, "working"); +} + +static void render_callback(Canvas* const canvas, void* ctx) { + if(ctx == NULL) return; + const PluginState* plugin_state = ctx; + if(furi_mutex_acquire(plugin_state->mutex, 5) != FuriStatusOk) return; + + //canvas_draw_frame(canvas, 0, 0, 128, 64); // border around the edge of the screen + if(what_doing == 0) { + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + snprintf(screen_buf, sizeof(screen_buf), "%s: %s", addr_len ? "File" : "Open", file_name); + canvas_draw_str(canvas, 8, 10, screen_buf); + if(addr_len) { + if(Edit) { + if(setup_cursor == 1) + display_edit_ttf_font(canvas, 45, 20); + else if(setup_cursor == 2) + display_edit_ttf_font(canvas, 55, 30); + } + if(!Edit || setup_cursor != 1) { + screen_buf[0] = '\0'; + add_to_str_hex_bytes(screen_buf, addr, addr_len); + canvas_draw_str(canvas, 45, 20, screen_buf); + } + if(!Edit || setup_cursor != 2) { + snprintf(screen_buf, sizeof(screen_buf), "%d", NRF_channel); + canvas_draw_str(canvas, 55, 30, screen_buf); + } + canvas_draw_str(canvas, 8, 20, "Address:"); + snprintf(screen_buf, sizeof(screen_buf), "Rate: %d, Ch:", NRF_rate); + canvas_draw_str(canvas, 8, 30, screen_buf); + snprintf( + screen_buf, + sizeof(screen_buf), + "RB: %d, R: %d, WB: %d", + ReadBatch_cmd_Total, + Read_cmd_Total, + WriteBatch_cmd_Total); + canvas_draw_str(canvas, 8, 40, screen_buf); + canvas_draw_str(canvas, 0, 64, Info); + canvas_draw_str(canvas, 0, 10 + setup_cursor * 10, ">"); + } else { + snprintf(screen_buf, sizeof(screen_buf), "Ver. %s, vad7", VERSION); + canvas_draw_str(canvas, 10, 60, screen_buf); + canvas_draw_str(canvas, 0, 10, ">"); + } + } else if(what_doing == 1) { + if(rw_type == rwt_listen) { + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + canvas_draw_str(canvas, 0, 10, "Listen mode"); + canvas_draw_str(canvas, 0, 25, "Address:"); + if(Edit) + display_edit_ttf_font(canvas, 40, 25); + else if(listen_addr_len) { + screen_buf[0] = '\0'; + add_to_str_hex_bytes(screen_buf, listen_addr, listen_addr_len); + canvas_draw_str(canvas, 40, 25, screen_buf); + } + snprintf( + screen_buf, + sizeof(screen_buf), + "I: %d +(%d) mA", + Current - CurrentStart, + CurrentStart); + canvas_draw_str(canvas, 0, 60, screen_buf); + } else { + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines, 25 cols + if(rw_type == rwt_read_batch) { + canvas_draw_str(canvas, 0, 7, "Read Batch:"); + render_display_list( + canvas, &ReadBatch_cmd, ':', view_cmd[rw_type], ReadBatch_cmd_Total); + } else if(rw_type == rwt_read_cmd) { + canvas_draw_str(canvas, 0, 7, "Read Command:"); + render_display_list(canvas, &Read_cmd, '=', view_cmd[rw_type], Read_cmd_Total); + } else if(rw_type == rwt_write_batch) { + if(!ask_fill_screen_buf()) strcpy(screen_buf, "Write Batch:"); + canvas_draw_str(canvas, 0, 7, screen_buf); + render_display_list( + canvas, &WriteBatch_cmd, ':', view_cmd[rw_type], WriteBatch_cmd_Total); + } else if(rw_type == rwt_set_batch) { + strcpy(screen_buf, "Set: "); + display_add_status(); + canvas_draw_str(canvas, 0, 7, screen_buf); + render_display_list( + canvas, &SetBatch_cmd, ':', view_cmd[rw_type], SetBatch_cmd_Total); + } + } + } else { // what_doing == 2 + if(rw_type == rwt_listen) { + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + strcpy(screen_buf, "Listen: "); + if(NRF_ERROR) + strcat(screen_buf, "nRF24 ERROR!"); + else if(ListenNew) { + snprintf( + screen_buf + strlen(screen_buf), + 16, + "%02d:%02d:%02d", + ListenLastTime.hour, + ListenLastTime.minute, + ListenLastTime.second); + if(ListenPrev) + snprintf( + screen_buf + strlen(screen_buf), 16, " (%lu)", ListenLast - ListenPrev); + } else + strcat(screen_buf, "receiving"); + snprintf(screen_buf + strlen(screen_buf), 16, " %dmA", Current - CurrentStart); + canvas_draw_str(canvas, 0, 10, screen_buf); + if(ListenFields) { + char *p2, *p = ListenFields; + uint8_t hex, len, *pld = payload_receive; + for(uint8_t i = 0; i < 5 && *p; i++) { + hex = false; + p2 = strchr(p, ','); + if(p2 == NULL) p2 = p + strlen(p); + if(*(p2 - 1) == '#') hex = true; + memcpy(screen_buf, p, len = p2 - p); + strcpy(screen_buf + len, ": "); + if(ListenNew) { + len = payload_struct[i]; + int32_t n = get_payload_receive_field(pld, len); + if(hex) { + strcat(screen_buf, "0x"); + add_to_str_hex_bytes(screen_buf, pld, len); + } else { + snprintf(screen_buf + strlen(screen_buf), 20, "%ld", n); + if(n > 9) { + strcat(screen_buf, " ("); + add_to_str_hex_bytes(screen_buf, pld, len); + strcat(screen_buf, ")"); + } + } + pld += len; + } + canvas_draw_str(canvas, 0, 20 + i * 10, screen_buf); + if(*p2 == '\0') break; + p = p2 + 1; + } + } + } else if(rw_type == rwt_read_cmd) { // Read command + canvas_set_font(canvas, FontSecondary); // 8x10 font, 6 lines + if(!ask_fill_screen_buf()) { + strcpy(screen_buf, "Read "); + strcat(screen_buf, ReadRepeat ? "rep: " : "cmd: "); + } + display_add_status(); + canvas_draw_str(canvas, 0, 10, screen_buf); + if(Log_Total) { + char* p = (char*)furi_string_get_cstr(Log[Log_Total - 1]); + uint8_t vx = MIN(view_x, strlen(p)); + strncpy(screen_buf, p + vx, 30); + display_remove_asterisk(p, vx); + canvas_draw_str(canvas, 0, 15 + 10, screen_buf); + } + } else if(rw_type == rwt_set_batch) { + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines, 25 cols + strcpy(screen_buf, "Set: "); + display_add_status(); + canvas_draw_str(canvas, 0, 7, screen_buf); + render_display_list(canvas, &SetBatch_cmd, ':', view_cmd[rw_type], SetBatch_cmd_Total); + } else { // rwt_read_batch, rwt_write_batch + canvas_set_font(canvas, FontBatteryPercent); // 5x7 font, 9 lines, 25 cols + if(!ask_fill_screen_buf()) { + strcpy(screen_buf, rw_type == rwt_read_batch ? "Read Batch: " : "Write: "); + if(rw_type == rwt_read_batch || send_status != sst_none) { + display_add_status(); + } else if(rw_type == rwt_write_batch) { + char* p = + (char*)furi_string_get_cstr(WriteBatch_cmd[view_cmd[rwt_write_batch]]); + char* end = strchr(p, ':'); + if(end) { + uint8_t len = end - p; + uint8_t lenb = strlen(screen_buf); + end = screen_buf + lenb; + lenb = FONT_5x7_SCREEN_WIDTH - lenb; + if(len > lenb) { + if(view_x < len) { + strncpy(end, p + view_x, len = MIN(lenb, len - view_x)); + end[len] = '\0'; + } + } else { + strncpy(end, p, len); + end[len] = '\0'; + } + } + } + } + canvas_draw_str(canvas, 0, 7, screen_buf); + if(Log_Total) { + char* p; + uint16_t y, page = view_Batch & ~7; + uint8_t vx; + for(uint8_t i = 0; i < 8 && page + i < Log_Total; i++) { + p = (char*)furi_string_get_cstr(Log[page + i]); + y = 14 + i * 7; + vx = MIN(view_x, strlen(p)); + if((view_Batch & 7) == i) { + canvas_draw_str(canvas, 0, y, ">"); + canvas_draw_str(canvas, -1, y, ">"); + if(Edit) { + int n = Edit_pos - p - vx - (FONT_5x7_SCREEN_WIDTH - 4); + if(n > 0) vx += n; // fix out of screen + int x = 6 + (Edit_pos - p - vx) * 5; + canvas_draw_line(canvas, x - 1, y, x - 1, y - 1); + canvas_draw_line(canvas, x - 1, y, n = x + 1 * 5, y); + canvas_draw_line(canvas, n, y, n, y - 1); + } + } + strncpy(screen_buf, p + vx, FONT_5x7_SCREEN_WIDTH + 2); + screen_buf[FONT_5x7_SCREEN_WIDTH + 2] = '\0'; + display_remove_asterisk(p, vx); + canvas_draw_str(canvas, 6, y, screen_buf); + } + } + } + } + furi_mutex_release(plugin_state->mutex); +} + +void work_timer_callback(void* ctx) { + if(ctx == NULL) return; + if(rw_type == rwt_listen && (pwr_read_timer += WORK_PERIOD) >= POWER_READ_PERIOD) { + pwr_read_timer = 0; + update_power(); + } + if(what_doing == 2) { + const PluginState* plugin_state = ctx; + if(furi_mutex_acquire(plugin_state->mutex, 0) != FuriStatusOk) return; + if(rw_type == rwt_write_batch || rw_type == rwt_set_batch) { + uint16_t* cmd_curr = rw_type == rwt_set_batch ? &SetBatch_cmd_curr : + &WriteBatch_cmd_curr; + if(ERR == 0 && furi_get_tick() - NRF_time >= delay_between_pkt) { + if(send_status == sst_ok) { + if(ERR == 0 && *cmd_curr < Log_Total) { + if(++(*cmd_curr) < Log_Total) + Run_WriteBatch_cmd(); + else { // finished ok + if(rw_type == rwt_set_batch) what_doing = 1; + Edited = false; + } + } + } else if(send_status == sst_sending) { + if(NRF_repeat++ < NRF_resend) + nrf24_send_packet(); + else { + view_Batch = *cmd_curr; + send_status = sst_error; // error NO_ACK + } + } + } + if((ERR || send_status == sst_error) && rw_type == rwt_set_batch) { + what_doing = 1; + } + // ReadBatch or ReadCmd + } else if(send_status == sst_sending) { // sending + if(furi_get_tick() - NRF_time > delay_between_pkt) { + if(NRF_repeat++ < NRF_resend) { + nrf24_resend_read_packet(); + } else + send_status = sst_error; // error NO_ACK + } + } else if(send_status == sst_receiving) { // receiving + for(uint8_t i = 0; i < 3; i++) { + if(nrf24_read_newpacket()) { + if(rw_type == rwt_listen) { + ListenPrev = ListenLast; + furi_hal_rtc_get_datetime(&ListenLastTime); + ListenLast = furi_hal_rtc_datetime_to_timestamp(&ListenLastTime); + ListenNew = true; + } else if(send_status != sst_receiving) + break; + } else { + if(rw_type != rwt_listen && furi_get_tick() - NRF_time > NRF_READ_TIMEOUT) { + if(NRF_repeat++ < NRF_resend) { + send_status = sst_sending; + nrf24_resend_read_packet(); + } else { + FURI_LOG_D(TAG, "TIMEOUT: %lu", furi_get_tick() - NRF_time); + send_status = sst_timeout; + } + } + break; + } + } + } else if(send_status == sst_ok) { + if(rw_type == rwt_read_batch) { + if((uint32_t)ReadBatch_cmd_curr != 0xFFFFFFFF && ERR == 0 && + furi_get_tick() - NRF_time >= delay_between_pkt) { + Run_ReadBatch_cmd(NULL); + } + } + } + furi_mutex_release(plugin_state->mutex); + } +} + +void next_rw_type(int8_t add) { + do { + rw_type += add; + if(rw_type >= rwt_max) { + if(add > 0) + rw_type = 0; + else + rw_type = rwt_max - 1; + } + if(rw_type == rwt_set_batch && SetBatch_cmd_Total) break; + if(rw_type == rwt_read_batch && ReadBatch_cmd_Total) break; + if(rw_type == rwt_read_cmd && Read_cmd_Total) break; + if(rw_type == rwt_write_batch && WriteBatch_cmd_Total) break; + } while(rw_type != rwt_listen); + send_status = sst_none; +} + +void next_view_cmd(int8_t add) { + if(rw_type == rwt_listen) return; + uint16_t max = + (rw_type == rwt_read_batch ? ReadBatch_cmd_Total : + rw_type == rwt_read_cmd ? Read_cmd_Total : + rw_type == rwt_set_batch ? SetBatch_cmd_Total : + WriteBatch_cmd_Total); + if((view_cmd[rw_type] += add) >= max) view_cmd[rw_type] = add > 0 ? 0 : max - 1; +} + +int32_t nrf24batch_app(void* p) { + UNUSED(p); + APP = malloc(sizeof(nRF24Batch)); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + APP->plugin_state = malloc(sizeof(PluginState)); + APP->plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!APP->plugin_state->mutex) { + furi_message_queue_free(event_queue); + FURI_LOG_E(TAG, "cannot create mutex"); + free(APP->plugin_state); + return 255; + } + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, APP->plugin_state); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + APP->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(APP->gui, view_port, GuiLayerFullscreen); + APP->notification = furi_record_open(RECORD_NOTIFICATION); + APP->storage = furi_record_open(RECORD_STORAGE); + storage_common_mkdir(APP->storage, SCAN_APP_PATH_FOLDER); + file_stream = file_stream_alloc(APP->storage); + FuriTimer* work_timer = + furi_timer_alloc(work_timer_callback, FuriTimerTypePeriodic, APP->plugin_state); + furi_timer_start(work_timer, WORK_PERIOD); + nrf24_init(); + check_en_power_5V(); + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + furi_mutex_acquire(APP->plugin_state->mutex, FuriWaitForever); + + static FuriLogLevel FuriLogLevel = FuriLogLevelDefault; + if(furi_log_get_level() != FuriLogLevel) { + FuriLogLevel = furi_log_get_level(); + if(FuriLogLevel == FuriLogLevelDebug) + furi_hal_uart_set_br(FuriHalUartIdUSART1, 1843200); + } + if(what_doing == 2 && rw_type == rwt_read_cmd && ReadRepeat && + furi_get_tick() - NRF_time > (uint32_t)(ReadCmdRepeatPeriod * 1000)) { + ERR = 0; + free_Log(); + Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]); + notification_message(APP->notification, &sequence_blink_blue_100); + } + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + switch(event.input.key) { + case InputKeyUp: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(!ask_question) { + if(Edit) { + char c = *Edit_pos; + if(Edit_hex) { + if(c == '9') + *Edit_pos = 'A'; + else if(c < 'F' || c < '9') + *Edit_pos = c + 1; + } else { + if(c == '-') + *Edit_pos = '0'; + else if(c < '9') + *Edit_pos = c + 1; + else { + c = *(Edit_pos - 1); + if(Edit_pos > Edit_start && c < '9' && c >= '0') { + *Edit_pos = '0'; + (*(Edit_pos - 1)) = c + 1; + } + } + } + } else if(what_doing == 0) { + if(addr_len) { + if(setup_cursor > 0) + setup_cursor--; + else + setup_cursor = 2; + } + } else if(what_doing == 1) + next_view_cmd(-1); + else if(what_doing == 2) + if(view_Batch) view_Batch--; + } + } + break; + case InputKeyDown: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(!ask_question) { + if(Edit) { + if(*Edit_pos != '-') { + if(Edit_hex && *Edit_pos == 'A') + (*Edit_pos) = '9'; + else if(*Edit_pos > '0') + (*Edit_pos)--; + else if(!Edit_hex) { + if(Edit_pos > Edit_start) { + if(*(Edit_pos - 1) > '0' && *(Edit_pos - 1) <= '9') { + *Edit_pos = '9'; + (*(Edit_pos - 1))--; + } + } else + Edit_insert_digit('-'); // negative + } + } + } else if(what_doing == 0) { + if(addr_len) { + if(setup_cursor < 2) + setup_cursor++; + else + setup_cursor = 0; + } + } else if(what_doing == 1) + next_view_cmd(+1); + else if(what_doing == 2) + if(view_Batch < Log_Total - 1) view_Batch++; + } + } + break; + case InputKeyLeft: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(ask_question) { + if(event.input.type == InputTypeShort) ask_question_answer ^= 1; + } else if(Edit) { + if(Edit_pos > Edit_start) { + if(is_digit(Edit_pos - 1, Edit_hex)) + Edit_pos--; + else if(*(Edit_pos - 1) == 'x' && *(Edit_pos - 3) == ',') + Edit_pos -= 4; + else if(*(Edit_pos - 1) == ',') + Edit_pos -= 2; + } + } else if(what_doing == 0) { + if(addr_len) { + rw_type = rwt_listen; + what_doing = 1; + } + } else if(what_doing == 1) { + if(event.input.type == InputTypeShort) + next_rw_type(-1); + else if(view_x) + view_x--; + } else if(what_doing == 2) + if(view_x) view_x--; + } else if(event.input.type == InputTypeLong) { + if(!ask_question && view_x == 0 && what_doing == 2 && + (rw_type == rwt_write_batch || rw_type == rwt_read_batch) && + Log_Total && Log[view_Batch] != NULL) { + ask_question = ask_skip_cmd; + ask_question_answer = 1; + } + } + break; + case InputKeyRight: + if(event.input.type == InputTypeShort || event.input.type == InputTypeRepeat) { + if(ask_question) { + ask_question_answer ^= 1; + } else if(Edit) { + if(is_digit(Edit_pos + 1, Edit_hex)) + Edit_pos++; + else if(*(Edit_pos + 1) == ',') { + Edit_pos += 2; + if(*(Edit_pos + 1) == 'x') Edit_pos += 2; + } + } else if(what_doing == 0) { + if(addr_len) { + rw_type = rwt_set_batch; + what_doing = 1; + } + } else if(what_doing == 1) { + if(event.input.type == InputTypeShort) + next_rw_type(+1); + else + view_x++; + } else if(what_doing == 2) + view_x++; + } + break; + case InputKeyOk: + if(event.input.type == InputTypeShort) { + if(ask_question) { + if(ask_question_answer) { + if(ask_question == ask_save_batch) { + save_batch(); + } else if(ask_question == ask_write_batch) { + if(WriteBatch_cmd_Total) { + if(what_doing == 1) { + Prepare_Write_cmd( + WriteBatch_cmd[view_cmd[rwt_write_batch]]); + send_status = sst_none; + Edited = false; + view_x = 0; + view_Batch = 0; + } + if(Log_Total) { + ERR = 0; + WriteBatch_cmd_curr = 0; + Run_WriteBatch_cmd(); + what_doing = 2; + } + } + } else if(ask_question == ask_skip_cmd) { + if(rw_type == rwt_write_batch || rw_type == rwt_read_batch) { + furi_string_free(Log[view_Batch]); + if(view_Batch < Log_Total - 1) + memmove( + &Log[view_Batch], + &Log[view_Batch + 1], + sizeof(Log) * (Log_Total - view_Batch - 1)); + else + view_Batch--; + Log_Total--; + } + } else if(ask_question == ask_exit) { + processing = false; + } else if(ask_question == ask_return) { + if(what_doing == 2) { + ERR = 0; + send_status = sst_none; + Edited = false; + what_doing--; + } + } + } + ask_question = 0; + } else if(Edit) { // insert digit + Edit_insert_digit('0'); + } else if(what_doing == 0) { + if(setup_cursor == 0) { // open file + file_stream_close(file_stream); + if(select_settings_file()) { + uint8_t err = load_settings_file(); + if(err) + snprintf( + file_name, sizeof(file_name), "LOAD ERROR #%d", err); + } + } else if(setup_cursor == 1) { // change address + char* ebuf = (char*)payload; + ebuf[0] = '\0'; + add_to_str_hex_bytes(ebuf, addr, addr_len); + Edit_hex = true; + Edit_pos = ebuf + strlen(ebuf) - 1; + Edit_start = ebuf; + Edit = 1; + NRF_INITED = 0; + } else if(setup_cursor == 2) { // change channel + char* ebuf = (char*)payload; + snprintf(ebuf, sizeof(payload), "%d", NRF_channel); + Edit_hex = false; + Edit_pos = ebuf + strlen(ebuf) - 1; + Edit_start = ebuf; + Edit = 1; + NRF_INITED = 0; + } + } else if(what_doing == 1) { + if(rw_type == rwt_set_batch) { + if(SetBatch_cmd_Total) { + ERR = 0; + send_status = sst_none; + Prepare_Write_cmd(SetBatch_cmd[view_cmd[rwt_set_batch]]); + if(!ERR) { + SetBatch_cmd_curr = 0; + Run_WriteBatch_cmd(); + what_doing = 2; + } + Edited = false; + } + } else if(rw_type == rwt_read_batch) { + if(ReadBatch_cmd_Total) { + ERR = 0; + Run_ReadBatch_cmd(ReadBatch_cmd[view_cmd[rwt_read_batch]]); + view_x = 0; + view_Batch = 0; + what_doing = 2; + } + } else if(rw_type == rwt_read_cmd) { + if(Read_cmd_Total) { + ERR = 0; + free_Log(); + Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]); + view_x = 0; + what_doing = 2; + } + } else if(rw_type == rwt_write_batch) { + if(WriteBatch_cmd_Total) { + ERR = 0; + Prepare_Write_cmd(WriteBatch_cmd[view_cmd[rwt_write_batch]]); + send_status = sst_none; + Edited = false; + view_x = 0; + view_Batch = 0; + what_doing = 2; + } + } else if(rw_type == rwt_listen) { + if(listen_addr_len) { + free_Log(); + prepare_nrf24(); + if(!NRF_ERROR) { + nrf24_set_rx_mode(nrf24_HANDLE); + ListenNew = false; + send_status = sst_receiving; // receiving + } + what_doing = 2; + } + } + } else if(what_doing == 2) { + if(rw_type == rwt_read_cmd) { + ERR = 0; + free_Log(); + Run_Read_cmd(Read_cmd[view_cmd[rwt_read_cmd]]); + } else if(Log_Total) { + if(rw_type == rwt_read_batch) { + ask_question = ask_save_batch; + ask_question_answer = 0; + } else if(rw_type == rwt_write_batch) { + ask_question = ask_write_batch; + ask_question_answer = 0; + } + } + } + } else if(event.input.type == InputTypeLong) { + if(Edit) { // delete + if(what_doing <= 1) { + if(strlen(Edit_start) > 1) { + memmove(Edit_pos, Edit_pos + 1, strlen(Edit_pos)); + if(*Edit_pos == '\0') Edit_pos--; + } + } else { + FuriString* fs = Log[view_Batch]; + if(is_digit(Edit_pos + 1, Edit_hex) || + (Edit_pos > Edit_start && is_digit(Edit_pos - 1, Edit_hex))) { + memmove(Edit_pos, Edit_pos + 1, strlen(Edit_pos)); + if(*Edit_pos == '\0') Edit_pos--; + furi_string_left(fs, furi_string_size(fs) - 1); + } + } + } else if(what_doing == 1) { + if(rw_type == rwt_listen) { + if(listen_addr_len) { + char* ebuf = (char*)payload; + ebuf[0] = '\0'; + add_to_str_hex_bytes(ebuf, listen_addr, listen_addr_len); + Edit_hex = true; + Edit_pos = ebuf + strlen(ebuf) - 1; + Edit_start = ebuf; + Edit = 1; + NRF_INITED = 0; + } + } else if(rw_type == rwt_read_batch) { + if(ReadBatch_cmd_Total) { + ERR = 0; + Run_ReadBatch_cmd(ReadBatch_cmd[view_cmd[rwt_read_batch]]); + view_x = 0; + view_Batch = 0; + what_doing = 2; + } + } else if(rw_type == rwt_write_batch) { + ask_question = ask_write_batch; + ask_question_answer = 0; + } + } else if(what_doing == 2) { + if(rw_type == rwt_read_cmd) { + ReadRepeat = !ReadRepeat; + } else if(Log_Total) { + if(rw_type == rwt_write_batch) { + if(!Edit) { + Edit = 0; + Edit_hex = 0; + char* s = (char*)furi_string_get_cstr(Log[view_Batch]); + char* p = strchr(s, '='); + if(p) { + p++; + if(*p == '{') p++; // array + if(*(p + 1) == 'x') { + p += 2; + Edit_hex = 1; // hex + } + if(is_digit(p, Edit_hex)) { + Edit_start = p; + while(is_digit(p, Edit_hex)) p++; + Edit_pos = p - 1; + Edited = true; + Edit = 1; + } + } + } + } else if(rw_type == rwt_read_batch) { + ask_question = ask_save_batch; + ask_question_answer = 0; + } + } + } + } + break; + case InputKeyBack: + if(event.input.type == InputTypeLong) { + if(what_doing == 2 && Edited) { + if(!ask_question) ask_question_answer = 1; + ask_question = ask_exit; + } else + processing = false; + } else if(event.input.type == InputTypeShort) { + if(ask_question) + ask_question = 0; + else if(Edit) { + if(what_doing == 0) { + if(setup_cursor == 1) { + addr_len = ConvertHexToArray((char*)payload, addr, 5); + } else if(setup_cursor == 2) { + NRF_channel = str_to_int((char*)payload); + if(NRF_channel > MAX_CHANNEL) NRF_channel = MAX_CHANNEL; + } + } else if(what_doing == 1 && rw_type == rwt_listen) { + listen_addr_len = + ConvertHexToArray((char*)payload, listen_addr, 5); + } + Edit = 0; + } else { + if(what_doing == 2 && Edited) { + ask_question = ask_return; + ask_question_answer = 1; + } else if(what_doing != 0) { + if(what_doing) what_doing--; + if(what_doing == 0) rw_type = rwt_read_batch; + if(what_doing <= 1) view_x = 0; + ERR = 0; + send_status = sst_none; + } + } + } + break; + default: + break; + } + } + } + + view_port_update(view_port); + furi_mutex_release(APP->plugin_state->mutex); + } + nrf24_set_idle(nrf24_HANDLE); + nrf24_deinit(); + if(NRF_BOARD_POWER_5V) furi_hal_power_disable_otg(); + + view_port_enabled_set(view_port, false); + gui_remove_view_port(APP->gui, view_port); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_STORAGE); + if(file_stream) { + file_stream_close(file_stream); + stream_free(file_stream); + } + view_port_free(view_port); + furi_message_queue_free(event_queue); + free_store(); + furi_timer_stop(work_timer); + furi_timer_free(work_timer); + furi_mutex_free(APP->plugin_state->mutex); + free(APP->plugin_state); + free(APP); + return 0; +} diff --git a/applications/external/nrf24_batch/nrf24batch.h b/applications/external/nrf24_batch/nrf24batch.h new file mode 100644 index 000000000..02db0a402 --- /dev/null +++ b/applications/external/nrf24_batch/nrf24batch.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + FuriMutex* mutex; +} PluginState; + +typedef struct { + Gui* gui; + Storage* storage; + NotificationApp* notification; + PluginState* plugin_state; +} nRF24Batch; diff --git a/applications/external/nrf24_batch/nrf24batch_10px.png b/applications/external/nrf24_batch/nrf24batch_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..348b35eca7599d1c72a7b1efea9fd7f263e4606f GIT binary patch literal 1771 zcmcIlO>f*p7x-sRO- zS0zchJlO8D=?T@&96%@6B`l63J-Ivmxb=!1apOvRMP zOl`6P(2{h0y(uu+XSE!&ahiw9U%&jS$SDn#+fJlM#U`7i+s7rlb-Xhq$NR*m%K8oY zdJ_Oa#%e4#*)*?$CRFCU0gT0CO_Ar2dOuV)L_v8J?aG_HWU{B4i0Ha5d%kMKu7f@Q z9of`PQ`23|u#n*ewi_6>+%ZF;gS$|f)O8VP+H5vcXO_y#v1a(buj!^{ zng|f6I?8L@puAe^82YRtWm?oJ&t<`gV}4MF3P@e7AuE=&dDZ#^j%f`pG(*+JnmS+< zoej;hCAzA&4xvh`_p!v)@Tg$g9;^64N!ZpQ%j>nSSTW%>uO|En+LnL+MyFy@;}*Nuj;5(R4_$zX z$a<&{(PT!I_Hp_c_s*wtK|7l;AU&h`3s}XIdWK83F$Slf8ZPZg;wyZ3w)FEg{uxSd zZ6y<&j~VQonj#jXi7jT)6z%{0df4wSGaHa*(aoy0;U*q04Y|>U31f>wFfsDno`rl9 z!qiLj7!jJ7)btGECGMi`H0=~Dh_tgS&b2!ap z0w+mt(BBwd`-Gwau&+ngqv~uO^Yk#kNGO$cD{`nQ-o0Att`WFvA{_gdY i=ihk$!R+PXOVW$?Uc1=*arGZ@91XU1`aj;h``Nz$VJy}F literal 0 HcmV?d00001 From 849ae03d24e53f6e6599ee7bc7302c0ee71cfc41 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 17 Jun 2023 22:53:36 +0100 Subject: [PATCH 219/370] Format --- applications/external/nrf24scan/nrf24scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/nrf24scan/nrf24scan.c b/applications/external/nrf24scan/nrf24scan.c index 73d93e673..fbfbbafa7 100644 --- a/applications/external/nrf24scan/nrf24scan.c +++ b/applications/external/nrf24scan/nrf24scan.c @@ -1439,7 +1439,7 @@ int32_t nrf24scan_app(void* p) { if(log_arr_idx == 0) view_log_arr_idx = 0; else { - view_log_arr_idx += event.input.type == InputTypeRepeat ? 10 : 1; + view_log_arr_idx += event.input.type == InputTypeRepeat ? 10 : 1; if(view_log_arr_idx >= log_arr_idx) view_log_arr_idx = log_arr_idx - 1; } From f446e201939e5a34184748f2004a83e868223d84 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 18 Jun 2023 00:35:35 +0100 Subject: [PATCH 220/370] Reorganize external apps --- applications/external/bpmtapper/application.fam | 2 +- applications/external/dap_link/application.fam | 2 +- applications/external/etch_a_sketch/application.fam | 4 ++-- applications/external/flipbip/application.fam | 4 ++-- applications/external/flipper_i2ctools/application.fam | 2 +- applications/external/flizzer_tracker/application.fam | 2 +- applications/external/geiger/application.fam | 2 +- applications/external/gpioreader_a/application.fam | 2 +- applications/external/mandelbrot/application.fam | 2 +- applications/external/metronome/application.fam | 2 +- applications/external/music_beeper/application.fam | 2 +- applications/external/music_player/application.fam | 2 +- applications/external/music_tracker/application.fam | 2 +- applications/external/ocarina/application.fam | 2 +- applications/external/paint/application.fam | 2 +- applications/external/passgen/application.fam | 2 +- applications/external/pomodoro/application.fam | 2 +- applications/external/qrcode/application.fam | 2 +- applications/external/sam/application.fam | 8 ++++---- applications/external/text2sam/application.fam | 2 +- applications/external/tuning_fork/application.fam | 2 +- applications/external/uart_terminal/application.fam | 2 +- applications/external/wav_player/application.fam | 2 +- applications/external/wifi_scanner/application.fam | 2 +- applications/external/wiiec/application.fam | 2 +- 25 files changed, 30 insertions(+), 30 deletions(-) diff --git a/applications/external/bpmtapper/application.fam b/applications/external/bpmtapper/application.fam index ec8ca5baf..7f9cb79ce 100644 --- a/applications/external/bpmtapper/application.fam +++ b/applications/external/bpmtapper/application.fam @@ -7,7 +7,7 @@ App( requires=["gui"], stack_size=2 * 1024, fap_icon="bpm_10px.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="icons", order=15, ) diff --git a/applications/external/dap_link/application.fam b/applications/external/dap_link/application.fam index 321b9e102..e05a7e01f 100644 --- a/applications/external/dap_link/application.fam +++ b/applications/external/dap_link/application.fam @@ -1,6 +1,6 @@ App( appid="dap_link", - name="[GPIO] DAP Link", + name="[SWD-JTAG] DAP Link", apptype=FlipperAppType.EXTERNAL, entry_point="dap_link_app", requires=[ diff --git a/applications/external/etch_a_sketch/application.fam b/applications/external/etch_a_sketch/application.fam index 4dc6dd4b8..9d9b1c1f1 100644 --- a/applications/external/etch_a_sketch/application.fam +++ b/applications/external/etch_a_sketch/application.fam @@ -1,6 +1,6 @@ App( appid="etch", - name="Etch A Sketch", + name="Etch a Sketch", apptype=FlipperAppType.EXTERNAL, entry_point="etch_a_sketch_app", cdefines=["APP_ETCH_A_SKETCH"], @@ -8,6 +8,6 @@ App( stack_size=2 * 1024, order=175, fap_icon="etch-a-sketch-icon.png", - fap_category="Misc", + fap_category="Media", fap_libs=["assets"], ) diff --git a/applications/external/flipbip/application.fam b/applications/external/flipbip/application.fam index ca340b22b..e8c660a89 100644 --- a/applications/external/flipbip/application.fam +++ b/applications/external/flipbip/application.fam @@ -1,6 +1,6 @@ App( appid="flipbip", - name="FlipBIP Crypto Tool", + name="FlipBIP Crypto Wallet", apptype=FlipperAppType.EXTERNAL, entry_point="flipbip_app", requires=[ @@ -15,7 +15,7 @@ App( name="crypto", ), ], - fap_category="Tools", + fap_category="Misc", fap_description="Crypto toolkit for Flipper", fap_author="Struan Clark (xtruan)", fap_weburl="https://github.com/xtruan/FlipBIP", diff --git a/applications/external/flipper_i2ctools/application.fam b/applications/external/flipper_i2ctools/application.fam index 581de63bc..bd525dba9 100644 --- a/applications/external/flipper_i2ctools/application.fam +++ b/applications/external/flipper_i2ctools/application.fam @@ -1,6 +1,6 @@ App( appid="i2c_tools", - name="[GPIO] i2c Tools", + name="[I2C] Tools", apptype=FlipperAppType.EXTERNAL, entry_point="i2ctools_app", requires=["gui"], diff --git a/applications/external/flizzer_tracker/application.fam b/applications/external/flizzer_tracker/application.fam index d5215df29..c5b2d2cee 100644 --- a/applications/external/flizzer_tracker/application.fam +++ b/applications/external/flizzer_tracker/application.fam @@ -12,5 +12,5 @@ App( fap_weburl="https://github.com/LTVA1/flizzer_tracker", fap_icon="flizzer_tracker.png", fap_icon_assets="images", - fap_category="Music", + fap_category="Media", ) diff --git a/applications/external/geiger/application.fam b/applications/external/geiger/application.fam index ea5e1fa85..0f20bff78 100644 --- a/applications/external/geiger/application.fam +++ b/applications/external/geiger/application.fam @@ -1,6 +1,6 @@ App( appid="flipper_geiger", - name="[GPIO] Geiger Counter", + name="[J305] Geiger Counter", apptype=FlipperAppType.EXTERNAL, entry_point="flipper_geiger_app", cdefines=["APP_GEIGER"], diff --git a/applications/external/gpioreader_a/application.fam b/applications/external/gpioreader_a/application.fam index 4ec90df6a..2466ba6f8 100644 --- a/applications/external/gpioreader_a/application.fam +++ b/applications/external/gpioreader_a/application.fam @@ -1,6 +1,6 @@ App( appid="gpio_reader_a", - name="[GPIO] Reader (Aurelilc)", + name="[GPIO] Reader (aureli1c)", apptype=FlipperAppType.EXTERNAL, entry_point="GPIO_reader_app", requires=["gui"], diff --git a/applications/external/mandelbrot/application.fam b/applications/external/mandelbrot/application.fam index 82cc9871a..d51058be2 100644 --- a/applications/external/mandelbrot/application.fam +++ b/applications/external/mandelbrot/application.fam @@ -8,5 +8,5 @@ App( stack_size=1 * 1024, order=130, fap_icon="Mandelbrot.png", - fap_category="Games", + fap_category="Media", ) diff --git a/applications/external/metronome/application.fam b/applications/external/metronome/application.fam index 373f5f9b3..8acd4b3b0 100644 --- a/applications/external/metronome/application.fam +++ b/applications/external/metronome/application.fam @@ -7,7 +7,7 @@ App( "gui", ], fap_icon="metronome_icon.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="images", stack_size=2 * 1024, order=20, diff --git a/applications/external/music_beeper/application.fam b/applications/external/music_beeper/application.fam index c63a4f717..bdd2980bc 100644 --- a/applications/external/music_beeper/application.fam +++ b/applications/external/music_beeper/application.fam @@ -13,7 +13,7 @@ App( order=45, fap_icon="music_10px.png", fap_icon_assets="icons", - fap_category="Music", + fap_category="Media", ) App( diff --git a/applications/external/music_player/application.fam b/applications/external/music_player/application.fam index fb040c601..496d7dcc8 100644 --- a/applications/external/music_player/application.fam +++ b/applications/external/music_player/application.fam @@ -11,7 +11,7 @@ App( stack_size=2 * 1024, order=45, fap_icon="icons/music_10px.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="icons", ) diff --git a/applications/external/music_tracker/application.fam b/applications/external/music_tracker/application.fam index fe4355e86..bba3029db 100644 --- a/applications/external/music_tracker/application.fam +++ b/applications/external/music_tracker/application.fam @@ -9,5 +9,5 @@ App( stack_size=4 * 1024, order=20, fap_icon="zero_tracker.png", - fap_category="Music", + fap_category="Media", ) diff --git a/applications/external/ocarina/application.fam b/applications/external/ocarina/application.fam index 8a28d0927..e5d27c1bb 100644 --- a/applications/external/ocarina/application.fam +++ b/applications/external/ocarina/application.fam @@ -8,6 +8,6 @@ App( stack_size=1 * 1024, order=30, fap_icon="icons/music_10px.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="icons", ) diff --git a/applications/external/paint/application.fam b/applications/external/paint/application.fam index e4b7b4fa8..ceef47ab5 100644 --- a/applications/external/paint/application.fam +++ b/applications/external/paint/application.fam @@ -8,5 +8,5 @@ App( stack_size=2 * 1024, order=175, fap_icon="paintIcon.png", - fap_category="Misc", + fap_category="Media", ) diff --git a/applications/external/passgen/application.fam b/applications/external/passgen/application.fam index 6a9652dc1..ededadaec 100644 --- a/applications/external/passgen/application.fam +++ b/applications/external/passgen/application.fam @@ -6,7 +6,7 @@ App( requires=[ "gui", ], - fap_category="Tools", + fap_category="Misc", fap_icon="icons/passgen_icon.png", fap_icon_assets="icons", ) diff --git a/applications/external/pomodoro/application.fam b/applications/external/pomodoro/application.fam index 847d91606..b369db34f 100644 --- a/applications/external/pomodoro/application.fam +++ b/applications/external/pomodoro/application.fam @@ -5,7 +5,7 @@ App( entry_point="flipp_pomodoro_app", requires=["gui", "notification", "dolphin"], stack_size=1 * 1024, - fap_category="Tools", + fap_category="Misc", fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", ) diff --git a/applications/external/qrcode/application.fam b/applications/external/qrcode/application.fam index 3aac3f18d..abf06041b 100644 --- a/applications/external/qrcode/application.fam +++ b/applications/external/qrcode/application.fam @@ -13,7 +13,7 @@ App( "gui", "dialogs", ], - fap_category="Tools", + fap_category="Misc", fap_icon="icons/qrcode_10px.png", fap_icon_assets="icons", ) diff --git a/applications/external/sam/application.fam b/applications/external/sam/application.fam index 97b557db2..fa614304e 100644 --- a/applications/external/sam/application.fam +++ b/applications/external/sam/application.fam @@ -10,7 +10,7 @@ App( stack_size=4 * 1024, order=20, fap_icon="music_10px.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="icons", ) App( @@ -25,7 +25,7 @@ App( stack_size=4 * 1024, order=20, fap_icon="music_10px.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="icons", ) App( @@ -40,7 +40,7 @@ App( stack_size=4 * 1024, order=20, fap_icon="music_10px.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="icons", ) App( @@ -55,6 +55,6 @@ App( stack_size=4 * 1024, order=20, fap_icon="music_10px.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="icons", ) diff --git a/applications/external/text2sam/application.fam b/applications/external/text2sam/application.fam index 7239938a6..d278ef8c3 100644 --- a/applications/external/text2sam/application.fam +++ b/applications/external/text2sam/application.fam @@ -12,6 +12,6 @@ App( stack_size=4 * 1024, # stack_size=2 * 1024, fap_icon="icon.png", - fap_category="Music", + fap_category="Media", order=20, ) diff --git a/applications/external/tuning_fork/application.fam b/applications/external/tuning_fork/application.fam index 5979ddb2a..852e0ab71 100644 --- a/applications/external/tuning_fork/application.fam +++ b/applications/external/tuning_fork/application.fam @@ -8,7 +8,7 @@ App( "gui", ], fap_icon="tuning_fork_icon.png", - fap_category="Music", + fap_category="Media", stack_size=2 * 1024, order=20, ) diff --git a/applications/external/uart_terminal/application.fam b/applications/external/uart_terminal/application.fam index 530baf2fc..eb879458e 100644 --- a/applications/external/uart_terminal/application.fam +++ b/applications/external/uart_terminal/application.fam @@ -1,6 +1,6 @@ App( appid="uart_terminal", - name="[GPIO] UART Terminal", + name="[UART] Terminal", apptype=FlipperAppType.EXTERNAL, entry_point="uart_terminal_app", requires=["gui"], diff --git a/applications/external/wav_player/application.fam b/applications/external/wav_player/application.fam index 571e8b263..c71527c9d 100644 --- a/applications/external/wav_player/application.fam +++ b/applications/external/wav_player/application.fam @@ -6,6 +6,6 @@ App( stack_size=4 * 1024, order=46, fap_icon="wav_10px.png", - fap_category="Music", + fap_category="Media", fap_icon_assets="images", ) diff --git a/applications/external/wifi_scanner/application.fam b/applications/external/wifi_scanner/application.fam index 0f33384b9..2111a501d 100644 --- a/applications/external/wifi_scanner/application.fam +++ b/applications/external/wifi_scanner/application.fam @@ -1,6 +1,6 @@ App( appid="wifi_scanner", - name="[WiFi] Scanner", + name="[ESP] WiFi Scanner", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_scanner_app", requires=["gui"], diff --git a/applications/external/wiiec/application.fam b/applications/external/wiiec/application.fam index 0f0ee08e0..16be18a2f 100644 --- a/applications/external/wiiec/application.fam +++ b/applications/external/wiiec/application.fam @@ -3,7 +3,7 @@ App( # --- App Info appid="wii_ec_anal", - name="[GPIO] Wii EC Analyser", + name="[WII] EC Analyser", # --- Entry point apptype=FlipperAppType.EXTERNAL, entry_point="wii_ec_anal", From 64730bbfb24462926cc09aa0cecdcb03429d201f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 18 Jun 2023 00:53:11 +0100 Subject: [PATCH 221/370] Add hex editor --- .../external/hex_editor/application.fam | 16 + applications/external/hex_editor/hex_editor.c | 357 ++++++++++++++++++ .../hex_editor/icons/Pin_arrow_up_7x9.png | Bin 0 -> 3603 bytes .../external/hex_editor/icons/edit_10px.png | Bin 0 -> 150 bytes 4 files changed, 373 insertions(+) create mode 100644 applications/external/hex_editor/application.fam create mode 100644 applications/external/hex_editor/hex_editor.c create mode 100644 applications/external/hex_editor/icons/Pin_arrow_up_7x9.png create mode 100644 applications/external/hex_editor/icons/edit_10px.png diff --git a/applications/external/hex_editor/application.fam b/applications/external/hex_editor/application.fam new file mode 100644 index 000000000..cb7f878f1 --- /dev/null +++ b/applications/external/hex_editor/application.fam @@ -0,0 +1,16 @@ +App( + appid="hex_editor", + name="HEX Editor", + apptype=FlipperAppType.EXTERNAL, + entry_point="hex_editor_app", + cdefines=["APP_HEX_EDITOR"], + requires=[ + "gui", + "dialogs", + ], + stack_size=2 * 1024, + order=20, + fap_icon="icons/edit_10px.png", + fap_category="Tools", + fap_icon_assets="icons", +) diff --git a/applications/external/hex_editor/hex_editor.c b/applications/external/hex_editor/hex_editor.c new file mode 100644 index 000000000..5e5dc807c --- /dev/null +++ b/applications/external/hex_editor/hex_editor.c @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +// #include + +#define TAG "HexEditor" + +typedef struct { + // uint8_t file_bytes[HEX_editor_LINES_ON_SCREEN][HEX_editor_BYTES_PER_LINE]; + uint32_t file_offset; + uint32_t file_read_bytes; + uint32_t file_size; + uint8_t string_offset; + char editable_char; + Stream* stream; + bool mode; // Print address or content +} HexEditorModel; + +typedef struct { + HexEditorModel* model; + FuriMutex** mutex; + + FuriMessageQueue* input_queue; + + ViewPort* view_port; + Gui* gui; + Storage* storage; + + FuriString* buffer; +} HexEditor; + +static void draw_callback(Canvas* canvas, void* ctx) { + // UNUSED(ctx); + HexEditor* hex_editor = ctx; + + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 10, "Line and mode:"); + // elements_button_right(canvas, "Info"); + + // // elements_string_fit_width(canvas, buffer, 100); + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned( + canvas, + 0, + 20, + AlignLeft, + AlignBottom, + furi_string_get_cstr(hex_editor->buffer) + hex_editor->model->string_offset); + // elements_scrollable_text_line( + // canvas, 0, 20, 128, hex_editor->buffer, hex_editor->model->string_offset, false); + + // canvas_draw_line(canvas, 3, 20, 5, 30); + + canvas_draw_icon(canvas, 0, 20, &I_Pin_arrow_up_7x9); + + if(hex_editor->model->mode) { + elements_button_left(canvas, "ASCII -"); + elements_button_right(canvas, "ASCII +"); + } else { + elements_button_left(canvas, ""); + elements_button_right(canvas, ""); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_glyph(canvas, 0, 45, '0' + hex_editor->model->mode); + canvas_draw_glyph(canvas, 30, 45, hex_editor->model->editable_char); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + // Проверяем, что контекст не нулевой + furi_assert(ctx); + HexEditor* hex_editor = ctx; + + furi_message_queue_put(hex_editor->input_queue, input_event, 100); +} + +static HexEditor* hex_editor_alloc() { + HexEditor* instance = malloc(sizeof(HexEditor)); + + instance->model = malloc(sizeof(HexEditorModel)); + memset(instance->model, 0x0, sizeof(HexEditorModel)); + + instance->model->editable_char = ' '; + + instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + + instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, draw_callback, instance); + view_port_input_callback_set(instance->view_port, input_callback, instance); + + instance->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + instance->storage = furi_record_open(RECORD_STORAGE); + + instance->buffer = furi_string_alloc(); + + return instance; +} + +static void hex_editor_free(HexEditor* instance) { + furi_record_close(RECORD_STORAGE); + + gui_remove_view_port(instance->gui, instance->view_port); + furi_record_close(RECORD_GUI); + view_port_free(instance->view_port); + + furi_message_queue_free(instance->input_queue); + + furi_mutex_free(instance->mutex); + + if(instance->model->stream) buffered_file_stream_close(instance->model->stream); + + furi_string_free(instance->buffer); + + free(instance->model); + free(instance); +} + +static bool hex_editor_open_file(HexEditor* hex_editor, const char* file_path) { + furi_assert(hex_editor); + furi_assert(file_path); + + hex_editor->model->stream = buffered_file_stream_alloc(hex_editor->storage); + bool isOk = true; + + do { + if(!buffered_file_stream_open( + hex_editor->model->stream, file_path, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); + isOk = false; + break; + }; + + hex_editor->model->file_size = stream_size(hex_editor->model->stream); + } while(false); + + return isOk; +} + +// static bool hex_editor_read_file(HexEditor* hex_editor) { +// furi_assert(hex_editor); +// furi_assert(hex_editor->model->stream); +// // furi_assert(hex_editor->model->file_offset % hex_editor_BYTES_PER_LINE == 0); + +// memset(hex_editor->model->file_bytes, 0x0, hex_editor_BUF_SIZE); +// bool isOk = true; + +// do { +// uint32_t offset = hex_editor->model->file_offset; +// if(!stream_seek(hex_editor->model->stream, offset, true)) { +// FURI_LOG_E(TAG, "Unable to seek stream"); +// isOk = false; +// break; +// } + +// hex_editor->model->file_read_bytes = stream_read( +// hex_editor->model->stream, +// (uint8_t*)hex_editor->model->file_bytes, +// hex_editor_BUF_SIZE); +// } while(false); + +// return isOk; +// } + +int32_t hex_editor_app(void* p) { + UNUSED(p); + + HexEditor* hex_editor = hex_editor_alloc(); + + FuriString* file_path; + file_path = furi_string_alloc(); + + // furi_string_printf( + // hex_editor->buffer, + // "qqqqq1231231232343454565676urtfgsdfascesc\nasdqwe\new ra sssssssssssssssssssssssssqqqqqqqqqqq1231231232343454565676urtfgsdfascesc\nq2e"); + + do { + if(p && strlen(p)) { + furi_string_set(file_path, (const char*)p); + } else { + furi_string_set(file_path, "/any"); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, "*", &I_edit_10px); + browser_options.hide_ext = false; + + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); + + furi_record_close(RECORD_DIALOGS); + if(!res) { + FURI_LOG_I(TAG, "No file selected"); + break; + } + } + + FURI_LOG_I(TAG, "File selected: %s", furi_string_get_cstr(file_path)); + + if(!hex_editor_open_file(hex_editor, furi_string_get_cstr(file_path))) break; + + if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) { + FURI_LOG_T(TAG, "No keys left in dict"); + break; + } + + InputEvent event; + int8_t off; + while(1) { + // Выбираем событие из очереди в переменную event (ждем бесконечно долго, если очередь пуста) + // и проверяем, что у нас получилось это сделать + furi_check( + furi_message_queue_get(hex_editor->input_queue, &event, FuriWaitForever) == + FuriStatusOk); + + // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения + if(event.type == InputTypeShort || event.type == InputTypeRepeat) { + if(!hex_editor->model->mode) { + off = 1; + if(event.type == InputTypeRepeat) { + off = 2; + } + if(event.key == InputKeyRight) { + hex_editor->model->string_offset += off; + if(hex_editor->model->string_offset >= + furi_string_size(hex_editor->buffer)) { + // dengeros + hex_editor->model->string_offset -= + furi_string_size(hex_editor->buffer); + } + } + if(event.key == InputKeyLeft) { + if(hex_editor->model->string_offset - off < 0) { + // dengeros + hex_editor->model->string_offset += + furi_string_size(hex_editor->buffer); + } + hex_editor->model->string_offset -= off; + } + if(event.key == InputKeyDown) { + hex_editor->model->string_offset = 0; + if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) { + FURI_LOG_T(TAG, "No keys left in dict"); + } + } + if(event.key == InputKeyUp) { + hex_editor->model->string_offset = 0; + // TODO asert + if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + break; + } + // NOT work on first line + stream_seek_to_char( + hex_editor->model->stream, '\n', StreamDirectionBackward); + + // if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) { + // FURI_LOG_E(TAG, "Unable to seek stream"); + // break; + // } + if(!stream_seek_to_char( + hex_editor->model->stream, '\n', StreamDirectionBackward)) { + stream_rewind(hex_editor->model->stream); + } else { + if(!stream_seek( + hex_editor->model->stream, 1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + break; + } + } + + if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) { + FURI_LOG_T(TAG, "No keys left in dict"); + break; + } + } + + if(event.key == InputKeyOk) { + hex_editor->model->editable_char = furi_string_get_char( + hex_editor->buffer, hex_editor->model->string_offset); + + hex_editor->model->mode = 1; + } + } else { + off = 1; + if(event.type == InputTypeRepeat) { + off = 4; + } + if(event.key == InputKeyRight) { + hex_editor->model->editable_char += off; + } + if(event.key == InputKeyLeft) { + hex_editor->model->editable_char -= off; + } + + if(event.key == InputKeyOk) { + if(!stream_seek(hex_editor->model->stream, -1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + break; + } + stream_seek_to_char( + hex_editor->model->stream, '\n', StreamDirectionBackward); + stream_seek( + hex_editor->model->stream, + hex_editor->model->string_offset + 1, + StreamOffsetFromCurrent); + + stream_write_char( + hex_editor->model->stream, hex_editor->model->editable_char); + + hex_editor->model->editable_char = ' '; + + hex_editor->model->mode = 0; + + stream_seek_to_char( + hex_editor->model->stream, '\n', StreamDirectionBackward); + + if(!stream_seek(hex_editor->model->stream, 1, StreamOffsetFromCurrent)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + break; + } + + if(!stream_read_line(hex_editor->model->stream, hex_editor->buffer)) { + FURI_LOG_T(TAG, "No keys left in dict"); + break; + } + } + } + } + if(event.key == InputKeyBack) { + break; + } + // ? + view_port_update(hex_editor->view_port); + } + } while(false); + + furi_string_free(file_path); + hex_editor_free(hex_editor); + + return 0; +} \ No newline at end of file diff --git a/applications/external/hex_editor/icons/Pin_arrow_up_7x9.png b/applications/external/hex_editor/icons/Pin_arrow_up_7x9.png new file mode 100644 index 0000000000000000000000000000000000000000..a91a6fd5e99a72112e28865cd8a004c7d1933fff GIT binary patch literal 3603 zcmaJ@c|4Te+rMpvvSba(81X2}EGQ;p8_TE>jcrt7jKN@*#$ZMzB}>VcEo(xFhBigA zRfKF&B$OpfLSqS8d&l#8dVcR8Z}0is_kGT}&h`CX>-l`{D|W}MM1o0W+qqCz&a@8xmO|M3uh;cln|6OUI z@X7fQ&dki(hqbDStcmq@R)<*FE(x{7@jPF^02^V5=v9ihMb|f1hw)0IhxkF_<1H_} z1sVWgmXE~@Wjrum=ebV>cmZ0s_CATm;a}mEc52Q5C=nO}OHAzGNx%Y4+73-pK+|sE zf&F7oVIUa*{8{JBz(BDGF#W^YNC4<9N*a&_dh_-a2?DV^K)SlsK3W zOCXnR0@miQE9D7uc?!4U4XYLag5q!qVkYiDSh|^JD*)2x1yFk>+xS2jzFcTm?NE^$ zEusR=1Jt#ow51*G(vhl2c`F}0KRYy{Jo3{2p&4FwzqpssC^#!EQ$-Rz!G~$z2>|jd zoi8@^jT0uuM~BC~Cj2=+8uB*%W~pE!<+;Jls%yObfcUWvPM_P@SPvhqk>^2RtzXee zpw9{L8C-GI=@-g9A^bLEC5ENHZn8J$mR*yf;vV50J7!cpZdF6S#2Ee38Kw@!gf4MU zH~T|ofioE<=_Pgf;Tvc0l%P^<+(Zk%8H}<#p|aT+abY8Ff9Htq!&92lSLbk7D(t{E zjjU(bM04fllo5%^3-CFm)D5AeU=e^FXGmfr{&k_>d3a+)aa}=xN$7&sHTfNh zfVj6VoV5%9Nwq8SCK^0ITUx;v0I2%9`_$cJSLF_4$)r9^g5d7-;)ha7k^2JBT`QGyenmoI!B!BgFZa^nPSIjjmHP5e8zHBct z>}g(M=h3f$4B-6LI6_z_Ow{YzNBpU4Q5No3aPn%6GK4Xlo>ROYK@oQ-NLryT2hS1Q z#~TwSIW2hlviM8?O9=^9I1CPTS9MyYOrlcISt$H6?B!qJq`S6dsv#09^-K@M!vvfq zTkX5@UgaFs(|?Idx+S6ai8fy!JtnNIngF-nVeN7Z`Pkld>>sQwike&!d8m z!q}j+#PS5O1l#Lt&96qwr4S9#BN(B)eb|Czi6eSM<1zl*H{oXKxy8rZigMly7Dpp) zp0Fn82H8REqlzST12a_HGG$OL1zP#tZ!<{Vq-7t-B%@O3Q}|wsw6|$peqXmwPE3aX z2;M0YDH7g@_E4AelRGO{xVu~ql8(6}@GdRA$pQKSu8{71L+l3C5qDtez&Yu}Hxem` z6sMHXl!;;o#{fs;ZdUOQhkK4<_f9*Vzhmk6*zQY_(0iGC-9?Iy&x;P0wqt{_@pc`@ z-STVPHZH9aL>@&(Sms8e^BoA~ujOKuWnROHb2zgex)a}&rr!-4kCTs9rZGVRYYIV- zvlx3+K(QCwE72=^{7f5<=%`? zl>Nr(;dCk;g6aw$Opx=3=@VvK69`}ZZjdTEXD<)m-PPh#nON_W-)WuySB2X5DDN+N zOj#o@Hg%5&TlX_@z|RoxL4x-e)E6|2*6eRf_RH|9>@0i7Xl-rM9ANjdo2TOpy0iRp z@HHQ+`qyJ4Zd+tE9Emv?)0oNb81R+irnMuZ>Qj# zxib@y+4A&mNoGlXP$qd$YD6l2f7kv+drBW{dVN}WI%9gX}>;*m9J4X{*B+`P?WbMg?R|_dOLt0YC zJHiM_Ty3A^GkR^rdo$!_RLz|l@F22ACA23r zJ#_ne&f4MCmW}wIwZp7=nYm*E?mRDe#(1hP%3plU=f|hSpU!`KyPiO-!1Ha8okr4T zJB37Cl;}y+I@x)J6@t!yw`NAC^c%r!=@Sa8&{j3f-kx1?ksX4A;-S<#E11dFr-IQ# zR{qfyN+h{-*_HEB`wzg2wZ9!NvuB)PENk|#M_tyutK;V4i>^I8-0%C89^}pT^~d@X zrZX$TDvB#EGNXQ4%%w>%B=-r;Tp6wJtw&z@62Lp*pP`dAn&FVjAe4>`?UC_VILOQnvfFm7kYb}KIe$4b!q%cDFE;P^!}5wFhS$flol=(c zKOH`gTJ?#vwG4c%BV>!!U?s|3f2Oiv<7D3Rncea6%ttMQ=SEEn7*BSKM z{I;U9VyY&6%QWwRxn-WhQPHJ&t+6%>}7+sVXoLpPbO)$>wJq(%cIl{yAd4L zao(3TFdv5v@49^(rE$qwH>D`KxrI{ti`zebVW|0ofEcHjRC^^ydT1 zit!QWV{YB&7Fp!JzRyR>-^@&*rwXPh>}8kQ`$wvMO}pPl&We;M%*Bo=xRH;1X50$# zU5slhYkSkir-#>@IobM@-9LZpVE$4__664#r;U<(Fif+aek4~_5ISPczF+n%G&YJPZd_dwhcM)XK$a~zGT6f@?}u{2kzI_J`y5h z5613ABWPopVbs3NnT+5kv=awJUz(1+_-pXaxwBvFzTRqoHSnr!F#SULqTm#orO}0` z4PcuJ1W{iBF zKEPVWtf%|A9(S$wMs?&E%QC)W%H5Wm7d}tKyUte8et?%f`c=!1mLN-!R-v?wVf6iz z)G6X}%Z#&ODdUID)ZtFfy9=wnb=?6Uetyt)y~(QPyq;Dlr>K3}Q=wY9_%mo}MmAXZ zJ7&N&B%XPHy{2#D+xAtlZx_lo9}?@xLqFZ?+&f;mh;c-PqH;Eqf4z$u?y_pN>Q=E- ziH*-zQc@6+ub%g8PZ}Rf89BiysN>^Vu*|b~eTqQIXzO`L8nmD()4q3juuoh;Z zx{Lc)DaWwDG3=>cj9@&S2$*_OJ%}J{GTxhrCE`61Z>_G%gwd42_vIJi(910C^C-NfacQ^Sl-eB6%Xg&U!Xb8ybq}LqdnpiS{AK90(zP z1Ord7u@T6SiQp2Di3~i5N%p4%Aecz--@FL!dP@uegZ@@w_#wgnaSCT+2SQQlM9?8^ zm=*yFg@O(lXcIm0a1R|XJV6r#hr(eH8234(1v`X*>mXnTpnnFKYmn~gg}|Cy{$q~2 zLxO!63>pFg2@Vd{4%X48(!C)t0|NsH6b^yIwYVBu0W1mw&(xv>sQhLyCk7DcBpQQ6 zrGT~=@gCGb1`^D5_CHaOY5&qv0{+PqH)jwgo(6$wL${*(t!QKO|ErS8|7r&?u*CoR z`+pJ#IIw6$2$mQ?4WtvewewQhGDSn6=tMk&N_U`A{eLIY&WFmN2KZ2EAh?b;45V&@ zCy*#xlKp=}Y-|wLlmG^vLLge3Bf(q}Z4${7VPJ`Z>caJO59#RW!C)3BeO)*VWoc## zg<9yK4D<|sW6i0AKr)fS_>J}aFIMl5*sX>j)3}z+iF8sB(bJMnC4>Hs8bSKAFYrI| z{e$)VvoAV-#6q~vK(=c8ziRzk#BHFh<-g6#-Td4BL<+a(>D=bN76lY@FUB@IjDy9m z(5*YN-4s*8oj}&+rVh+L4|neH1o$j1E!71)pl~xe=$Un0lQ15DzW@Mf4u@pObhHwBu4M$1`knij1;us<^ zwYA@okHL_GseRf1|3=jYYI)94dTy>P47b0n$=t@VA~w|XMjE5XFTvgUla?rLJG9b5 uFn+!~!;=!Z4e?!TKl{{vol(GRtXm^`(&Se}nB@wf!3>_RelF{r5}E)}LoTiW literal 0 HcmV?d00001 From 42cb535739812733556ec2c31824735cf21a8261 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 18 Jun 2023 03:49:14 +0100 Subject: [PATCH 222/370] Why is strnlen not exported smh --- firmware/targets/f7/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ee0dd0c31..1c51c9d89 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2736,7 +2736,7 @@ Function,-,strncat,char*,"char*, const char*, size_t" Function,+,strncmp,int,"const char*, const char*, size_t" Function,+,strncpy,char*,"char*, const char*, size_t" Function,-,strndup,char*,"const char*, size_t" -Function,-,strnlen,size_t,"const char*, size_t" +Function,+,strnlen,size_t,"const char*, size_t" Function,-,strnstr,char*,"const char*, const char*, size_t" Function,-,strpbrk,char*,"const char*, const char*" Function,-,strptime,char*,"const char*, const char*, tm*" From a8a6aef712401561ba71d756bd3fc218fb7afbdd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 18 Jun 2023 22:36:05 +0100 Subject: [PATCH 223/370] BadKB fix MAC address byte order --- applications/main/bad_kb/helpers/ducky_script.c | 1 + .../main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c | 2 ++ firmware/targets/f7/api_symbols.csv | 1 + firmware/targets/f7/furi_hal/furi_hal_bt.c | 9 +++++++++ firmware/targets/furi_hal_include/furi_hal_bt.h | 5 +++++ 5 files changed, 18 insertions(+) diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 75040d98d..8f45b5e1f 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -346,6 +346,7 @@ static bool ducky_set_bt_id(BadKbScript* bad_kb, const char* line) { return false; } } + furi_hal_bt_reverse_mac_addr(cfg->bt_mac); strlcpy(cfg->bt_name, line + mac_len, BAD_KB_NAME_LEN); FURI_LOG_D(WORKER_TAG, "set bt id: %s", line); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c index 8005fdff3..e60d68622 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt_mac.c @@ -11,6 +11,7 @@ void bad_kb_scene_config_bt_mac_on_enter(void* context) { ByteInput* byte_input = bad_kb->byte_input; memmove(bad_kb->bt_mac_buf, bad_kb->config.bt_mac, BAD_KB_MAC_LEN); + furi_hal_bt_reverse_mac_addr(bad_kb->bt_mac_buf); byte_input_set_header_text(byte_input, "Set BT MAC address"); byte_input_set_result_callback( @@ -31,6 +32,7 @@ bool bad_kb_scene_config_bt_mac_on_event(void* context, SceneManagerEvent event) if(event.type == SceneManagerEventTypeCustom) { consumed = true; if(event.event == BadKbAppCustomEventByteInputDone) { + furi_hal_bt_reverse_mac_addr(bad_kb->bt_mac_buf); memmove(bad_kb->config.bt_mac, bad_kb->bt_mac_buf, BAD_KB_MAC_LEN); bad_kb_config_refresh(bad_kb); } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 1c51c9d89..41c2d612f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1117,6 +1117,7 @@ Function,+,furi_hal_bt_lock_core2,void, Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, Function,+,furi_hal_bt_nvm_sram_sem_release,void, Function,+,furi_hal_bt_reinit,void, +Function,+,furi_hal_bt_reverse_mac_addr,void,uint8_t[( 6 )] Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void, Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*" Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index f71cd3776..7ca91092f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -461,6 +461,15 @@ uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi) { return since; } +void furi_hal_bt_reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]) { + uint8_t tmp; + for(size_t i = 0; i < GAP_MAC_ADDR_SIZE / 2; i++) { + tmp = mac_addr[i]; + mac_addr[i] = mac_addr[GAP_MAC_ADDR_SIZE - 1 - i]; + mac_addr[GAP_MAC_ADDR_SIZE - 1 - i] = tmp; + } +} + void furi_hal_bt_set_profile_adv_name( FuriHalBtProfile profile, const char name[FURI_HAL_BT_ADV_NAME_LENGTH]) { diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index bfe4a67c3..132d0b95d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -218,6 +218,11 @@ float furi_hal_bt_get_rssi(); */ uint32_t furi_hal_bt_get_transmitted_packets(); +/** Reverse a MAC address byte order in-place + * @param[in] mac mac address to reverse +*/ +void furi_hal_bt_reverse_mac_addr(uint8_t mac_addr[GAP_MAC_ADDR_SIZE]); + /** Modify profile advertisement name and restart bluetooth * @param[in] profile profile type * @param[in] name new adv name From 0d15bebb695dba323ea6a0e0ab30fbe8f10a3d14 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 19 Jun 2023 16:29:19 +0100 Subject: [PATCH 224/370] Archive file menu improvements --- .../main/archive/helpers/archive_menu.h | 4 +- .../archive/scenes/archive_scene_browser.c | 78 ++++++++-------- .../main/archive/views/archive_browser_view.c | 92 ++++++------------- .../main/archive/views/archive_browser_view.h | 8 +- 4 files changed, 72 insertions(+), 110 deletions(-) diff --git a/applications/main/archive/helpers/archive_menu.h b/applications/main/archive/helpers/archive_menu.h index 4a85b851d..683c6e6fc 100644 --- a/applications/main/archive/helpers/archive_menu.h +++ b/applications/main/archive/helpers/archive_menu.h @@ -43,8 +43,8 @@ ARRAY_DEF( #pragma GCC diagnostic ignored "-Wunused-function" // Using in applications/archive/views/archive_browser_view.c static void - archive_menu_add_item(ArchiveContextMenuItem_t* obj, FuriString* text, uint32_t event) { + archive_menu_add_item(ArchiveContextMenuItem_t* obj, const char* text, uint32_t event) { obj->text = furi_string_alloc_set(text); obj->event = event; } -#pragma GCC diagnostic pop \ No newline at end of file +#pragma GCC diagnostic pop diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 0854c2d3f..458d5a442 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -114,9 +114,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventManageMenuOpen: - if(!favorites) { - archive_show_file_menu(browser, true, true); - } + archive_show_file_menu(browser, true, true); consumed = true; break; case ArchiveBrowserEventFileMenuClose: @@ -133,7 +131,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_show_file_menu(browser, false, false); consumed = true; break; - case ArchiveBrowserEventFileMenuPin: { + case ArchiveBrowserEventFileMenuFavorite: { const char* name = archive_get_name(browser); if(favorites) { archive_favorites_delete("%s", name); @@ -162,38 +160,6 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneShow); consumed = true; break; - case ArchiveBrowserEventFileMenuCut: - archive_show_file_menu(browser, false, false); - if(!favorites) { - with_view_model( - browser->view, - ArchiveBrowserViewModel * model, - { - if(model->clipboard == NULL) { - model->clipboard = strdup(furi_string_get_cstr(selected->path)); - model->clipboard_copy = false; - } - }, - false); - } - consumed = true; - break; - case ArchiveBrowserEventFileMenuCopy: - archive_show_file_menu(browser, false, false); - if(!favorites) { - with_view_model( - browser->view, - ArchiveBrowserViewModel * model, - { - if(model->clipboard == NULL) { - model->clipboard = strdup(furi_string_get_cstr(selected->path)); - model->clipboard_copy = true; - } - }, - false); - } - consumed = true; - break; case ArchiveBrowserEventFileMenuPaste: archive_show_file_menu(browser, false, false); if(!favorites) { @@ -249,6 +215,38 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { } consumed = true; break; + case ArchiveBrowserEventFileMenuCut: + archive_show_file_menu(browser, false, false); + if(!favorites) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + if(model->clipboard == NULL) { + model->clipboard = strdup(furi_string_get_cstr(selected->path)); + model->clipboard_copy = false; + } + }, + false); + } + consumed = true; + break; + case ArchiveBrowserEventFileMenuCopy: + archive_show_file_menu(browser, false, false); + if(!favorites) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { + if(model->clipboard == NULL) { + model->clipboard = strdup(furi_string_get_cstr(selected->path)); + model->clipboard_copy = true; + } + }, + false); + } + consumed = true; + break; case ArchiveBrowserEventFileMenuNewDir: archive_show_file_menu(browser, false, false); if(!favorites) { @@ -273,11 +271,9 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { break; case ArchiveBrowserEventFileMenuDelete: archive_show_file_menu(browser, false, false); - if(!favorites) { - scene_manager_set_scene_state( - archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); - scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); - } + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneDelete); consumed = true; break; case ArchiveBrowserEventEnterDir: diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index bd17f1925..af60e89b0 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -57,126 +57,92 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { NULL; bool favorites = model->tab_idx == ArchiveTabFavorites; - if(model->menu_manage && !favorites) { - FuriString* item_cut = furi_string_alloc_set("Cut"); - FuriString* item_copy = furi_string_alloc_set("Copy"); - FuriString* item_paste = furi_string_alloc_set("Paste"); - FuriString* item_new_dir = furi_string_alloc_set("New Dir"); - FuriString* item_rename = furi_string_alloc_set("Rename"); - FuriString* item_delete = furi_string_alloc_set("Delete"); - if(!model->is_app_tab) { + if(model->menu_manage) { + if(!model->is_app_tab && !favorites) { if(model->clipboard != NULL) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_paste, + "Paste", ArchiveBrowserEventFileMenuPaste); } else if(selected) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_cut, + "Cut", ArchiveBrowserEventFileMenuCut); archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_copy, + "Copy", ArchiveBrowserEventFileMenuCopy); } archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_new_dir, + "New Dir", ArchiveBrowserEventFileMenuNewDir); } if(selected) { - if(!selected->is_app) { + if(favorites) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_rename, + "Move", + ArchiveBrowserEventFileMenuRename); + } else if(!selected->is_app) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Rename", ArchiveBrowserEventFileMenuRename); } archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_delete, + "Delete", ArchiveBrowserEventFileMenuDelete); } - furi_string_free(item_cut); - furi_string_free(item_copy); - furi_string_free(item_paste); - furi_string_free(item_new_dir); - furi_string_free(item_rename); - furi_string_free(item_delete); - } else if(!model->menu_manage && selected) { - FuriString* item_run = furi_string_alloc_set("Run In App"); - FuriString* item_pin = furi_string_alloc_set("Pin"); - FuriString* item_info = furi_string_alloc_set("Info"); - FuriString* item_show = furi_string_alloc_set("Show"); - FuriString* item_move = furi_string_alloc_set("Move"); - if(selected->fav || favorites) { - furi_string_set(item_pin, "Unpin"); - } - + } else if(selected) { if(archive_is_known_app(selected->type)) { if(selected->type != ArchiveFileTypeFolder) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_run, + "Run In App", ArchiveBrowserEventFileMenuRun); } archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); + (selected->fav || favorites) ? "Unfavorite" : "Favorite", + ArchiveBrowserEventFileMenuFavorite); } if(!selected->is_app) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_info, + "Info", ArchiveBrowserEventFileMenuInfo); if(selected->type != ArchiveFileTypeFolder) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_show, + "Show", ArchiveBrowserEventFileMenuShow); } } - if(favorites) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_move, - ArchiveBrowserEventFileMenuRename); - } - - furi_string_free(item_run); - furi_string_free(item_pin); - furi_string_free(item_info); - furi_string_free(item_show); - furi_string_free(item_move); } - } /*else { - FURI_LOG_D(TAG, "menu_array_size already set: %d", menu_array_size(model->context_menu)); - }*/ + } size_t size_menu = menu_array_size(model->context_menu); const uint8_t menu_height = 48; const uint8_t line_height = 10; + const uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu - 1) * line_height); + const uint8_t off = (MENU_ITEMS - size_menu) * (line_height / 2); canvas_set_color(canvas, ColorWhite); - uint8_t calc_height = menu_height - ((MENU_ITEMS - size_menu - 1) * line_height); - canvas_draw_box(canvas, 71, 1, 57, calc_height + 4); + canvas_draw_box(canvas, 72, off + 2, 56, calc_height + 4); canvas_set_color(canvas, ColorBlack); - elements_slightly_rounded_frame(canvas, 70, 2, 58, calc_height + 4); + elements_slightly_rounded_frame(canvas, 71, off + 2, 57, calc_height + 4); - /*FURI_LOG_D( - TAG, - "size_menu: %d, calc_height: %d, menu_idx: %d", - size_menu, - calc_height, - model->menu_idx);*/ - canvas_draw_str(canvas, 74, 11, model->menu_manage ? "Manage:" : "Actions:"); + canvas_draw_str(canvas, 74, off + 11, model->menu_manage ? "Manage:" : "Actions:"); for(size_t i = 0; i < size_menu; i++) { ArchiveContextMenuItem_t* current = menu_array_get(model->context_menu, i); canvas_draw_str( - canvas, 82, 11 + (i + 1) * line_height, furi_string_get_cstr(current->text)); + canvas, 82, off + 11 + (i + 1) * line_height, furi_string_get_cstr(current->text)); } - canvas_draw_icon(canvas, 74, 4 + (model->menu_idx + 1) * line_height, &I_ButtonRight_4x7); + canvas_draw_icon( + canvas, 74, off + 4 + (model->menu_idx + 1) * line_height, &I_ButtonRight_4x7); } static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, bool moving) { diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 383da4769..885df4d09 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -40,15 +40,15 @@ typedef enum { ArchiveBrowserEventFileMenuOpen, ArchiveBrowserEventManageMenuOpen, ArchiveBrowserEventFileMenuRun, - ArchiveBrowserEventFileMenuPin, + ArchiveBrowserEventFileMenuFavorite, + ArchiveBrowserEventFileMenuInfo, + ArchiveBrowserEventFileMenuShow, + ArchiveBrowserEventFileMenuPaste, ArchiveBrowserEventFileMenuCut, ArchiveBrowserEventFileMenuCopy, - ArchiveBrowserEventFileMenuPaste, ArchiveBrowserEventFileMenuNewDir, ArchiveBrowserEventFileMenuRename, ArchiveBrowserEventFileMenuDelete, - ArchiveBrowserEventFileMenuInfo, - ArchiveBrowserEventFileMenuShow, ArchiveBrowserEventFileMenuClose, ArchiveBrowserEventEnterDir, From d839562c0afc3c8a43c120c74c1b90436621dbd9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 19 Jun 2023 18:35:59 +0100 Subject: [PATCH 225/370] Make widget_add_* return the widget element --- applications/services/gui/modules/widget.c | 21 ++++++++++++++------- applications/services/gui/modules/widget.h | 14 +++++++------- firmware/targets/f7/api_symbols.csv | 14 +++++++------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/applications/services/gui/modules/widget.c b/applications/services/gui/modules/widget.c index 21b8e5616..5174a556a 100644 --- a/applications/services/gui/modules/widget.c +++ b/applications/services/gui/modules/widget.c @@ -119,7 +119,7 @@ static void widget_add_element(Widget* widget, WidgetElement* element) { true); } -void widget_add_string_multiline_element( +WidgetElement* widget_add_string_multiline_element( Widget* widget, uint8_t x, uint8_t y, @@ -131,9 +131,10 @@ void widget_add_string_multiline_element( WidgetElement* string_multiline_element = widget_element_string_multiline_create(x, y, horizontal, vertical, font, text); widget_add_element(widget, string_multiline_element); + return string_multiline_element; } -void widget_add_string_element( +WidgetElement* widget_add_string_element( Widget* widget, uint8_t x, uint8_t y, @@ -145,9 +146,10 @@ void widget_add_string_element( WidgetElement* string_element = widget_element_string_create(x, y, horizontal, vertical, font, text); widget_add_element(widget, string_element); + return string_element; } -void widget_add_text_box_element( +WidgetElement* widget_add_text_box_element( Widget* widget, uint8_t x, uint8_t y, @@ -161,9 +163,10 @@ void widget_add_text_box_element( WidgetElement* text_box_element = widget_element_text_box_create( x, y, width, height, horizontal, vertical, text, strip_to_dots); widget_add_element(widget, text_box_element); + return text_box_element; } -void widget_add_text_scroll_element( +WidgetElement* widget_add_text_scroll_element( Widget* widget, uint8_t x, uint8_t y, @@ -174,9 +177,10 @@ void widget_add_text_scroll_element( WidgetElement* text_scroll_element = widget_element_text_scroll_create(x, y, width, height, text); widget_add_element(widget, text_scroll_element); + return text_scroll_element; } -void widget_add_button_element( +WidgetElement* widget_add_button_element( Widget* widget, GuiButtonType button_type, const char* text, @@ -186,16 +190,18 @@ void widget_add_button_element( WidgetElement* button_element = widget_element_button_create(button_type, text, callback, context); widget_add_element(widget, button_element); + return button_element; } -void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon) { +WidgetElement* widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon) { furi_assert(widget); furi_assert(icon); WidgetElement* icon_element = widget_element_icon_create(x, y, icon); widget_add_element(widget, icon_element); + return icon_element; } -void widget_add_frame_element( +WidgetElement* widget_add_frame_element( Widget* widget, uint8_t x, uint8_t y, @@ -205,4 +211,5 @@ void widget_add_frame_element( furi_assert(widget); WidgetElement* frame_element = widget_element_frame_create(x, y, width, height, radius); widget_add_element(widget, frame_element); + return frame_element; } diff --git a/applications/services/gui/modules/widget.h b/applications/services/gui/modules/widget.h index f86b14cc9..88d0dd667 100644 --- a/applications/services/gui/modules/widget.h +++ b/applications/services/gui/modules/widget.h @@ -51,7 +51,7 @@ View* widget_get_view(Widget* widget); * @param font Font instance * @param[in] text The text */ -void widget_add_string_multiline_element( +WidgetElement* widget_add_string_multiline_element( Widget* widget, uint8_t x, uint8_t y, @@ -70,7 +70,7 @@ void widget_add_string_multiline_element( * @param font Font instance * @param[in] text The text */ -void widget_add_string_element( +WidgetElement* widget_add_string_element( Widget* widget, uint8_t x, uint8_t y, @@ -94,7 +94,7 @@ void widget_add_string_element( * "\e!Inverted text\e!" - white text on black background * @param strip_to_dots Strip text to ... if does not fit to width */ -void widget_add_text_box_element( +WidgetElement* widget_add_text_box_element( Widget* widget, uint8_t x, uint8_t y, @@ -119,7 +119,7 @@ void widget_add_text_box_element( * "\ecCenter-aligned text" - sets center horizontal align until the next '\n' symbol * "\erRight-aligned text" - sets right horizontal align until the next '\n' symbol */ -void widget_add_text_scroll_element( +WidgetElement* widget_add_text_scroll_element( Widget* widget, uint8_t x, uint8_t y, @@ -135,7 +135,7 @@ void widget_add_text_scroll_element( * @param callback ButtonCallback instance * @param context pointer to context */ -void widget_add_button_element( +WidgetElement* widget_add_button_element( Widget* widget, GuiButtonType button_type, const char* text, @@ -149,7 +149,7 @@ void widget_add_button_element( * @param y top left y coordinate * @param icon Icon instance */ -void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon); +WidgetElement* widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* icon); /** Add Frame Element * @@ -160,7 +160,7 @@ void widget_add_icon_element(Widget* widget, uint8_t x, uint8_t y, const Icon* i * @param height frame height * @param radius frame radius */ -void widget_add_frame_element( +WidgetElement* widget_add_frame_element( Widget* widget, uint8_t x, uint8_t y, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 41c2d612f..01b6307ca 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -3189,13 +3189,13 @@ Function,-,vsprintf,int,"char*, const char*, __gnuc_va_list" Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list" Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t" Function,-,wctomb,int,"char*, wchar_t" -Function,+,widget_add_button_element,void,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" -Function,+,widget_add_frame_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" -Function,+,widget_add_icon_element,void,"Widget*, uint8_t, uint8_t, const Icon*" -Function,+,widget_add_string_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" -Function,+,widget_add_string_multiline_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" -Function,+,widget_add_text_box_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,widget_add_text_scroll_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*" +Function,+,widget_add_button_element,WidgetElement*,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" +Function,+,widget_add_frame_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,widget_add_icon_element,WidgetElement*,"Widget*, uint8_t, uint8_t, const Icon*" +Function,+,widget_add_string_element,WidgetElement*,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_string_multiline_element,WidgetElement*,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_text_box_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,widget_add_text_scroll_element,WidgetElement*,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*" Function,+,widget_alloc,Widget*, Function,+,widget_free,void,Widget* Function,+,widget_get_view,View*,Widget* From 1df4bb0696a04396c44c3f6e6ade1e51042968bd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 19 Jun 2023 18:37:02 +0100 Subject: [PATCH 226/370] Update widget text box element after creation --- .../services/gui/modules/widget_elements/widget_element_i.h | 5 ++++- .../gui/modules/widget_elements/widget_element_text_box.c | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/applications/services/gui/modules/widget_elements/widget_element_i.h b/applications/services/gui/modules/widget_elements/widget_element_i.h index 67dea4b1f..72f4b4ef1 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_i.h +++ b/applications/services/gui/modules/widget_elements/widget_element_i.h @@ -61,6 +61,9 @@ WidgetElement* widget_element_text_box_create( const char* text, bool strip_to_dots); +/** Update text box element */ +void widget_element_text_box_set_text(WidgetElement* gui_string, const char* text); + /** Create button element */ WidgetElement* widget_element_button_create( GuiButtonType button_type, @@ -88,4 +91,4 @@ WidgetElement* widget_element_text_scroll_create( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/services/gui/modules/widget_elements/widget_element_text_box.c b/applications/services/gui/modules/widget_elements/widget_element_text_box.c index 98f8e83d8..552025f96 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_text_box.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_box.c @@ -72,3 +72,8 @@ WidgetElement* widget_element_text_box_create( return gui_string; } //-V773 + +void widget_element_text_box_set_text(WidgetElement* gui_string, const char* text) { + GuiTextBoxModel* model = gui_string->model; + furi_string_set(model->text, text); +} From c70df6753675dac69fc952eb515df08fc6d73014 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 19 Jun 2023 20:58:51 +0100 Subject: [PATCH 227/370] File and folder info go brrrrrrrrr --- applications/main/archive/archive_i.h | 5 + .../main/archive/scenes/archive_scene_info.c | 146 +++++++++++++----- 2 files changed, 111 insertions(+), 40 deletions(-) diff --git a/applications/main/archive/archive_i.h b/applications/main/archive/archive_i.h index bd683c22d..8d60d63a2 100644 --- a/applications/main/archive/archive_i.h +++ b/applications/main/archive/archive_i.h @@ -8,9 +8,11 @@ #include #include #include +#include #include #include #include +#include #include #include "views/archive_browser_view.h" @@ -39,6 +41,9 @@ struct ArchiveApp { FuriString* fav_move_str; char text_store[MAX_NAME_LEN]; FuriString* file_extension; + + WidgetElement* element; + FuriThread* thread; }; void archive_show_loading_popup(ArchiveApp* context, bool show); diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 7af7d0db5..19bbc900e 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -3,6 +3,8 @@ #define TAG "Archive" +const char* units[] = {"Bytes", "KiB", "MiB", "GiB", "TiB"}; + void archive_scene_info_widget_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); ArchiveApp* app = (ArchiveApp*)context; @@ -11,6 +13,54 @@ void archive_scene_info_widget_callback(GuiButtonType result, InputType type, vo } } +uint32_t archive_scene_info_dirwalk(void* context) { + furi_assert(context); + ArchiveApp* instance = context; + + char buf[128]; + FileInfo fileinfo; + uint64_t total = 0; + DirWalk* dir_walk = dir_walk_alloc(furi_record_open(RECORD_STORAGE)); + ArchiveFile_t* current = archive_get_current_file(instance->browser); + if(dir_walk_open(dir_walk, furi_string_get_cstr(current->path))) { + while(scene_manager_get_scene_state(instance->scene_manager, ArchiveAppSceneInfo)) { + DirWalkResult result = dir_walk_read(dir_walk, NULL, &fileinfo); + if(result == DirWalkError) { + snprintf(buf, sizeof(buf), "Size: \e#Error\e#"); + widget_element_text_box_set_text(instance->element, buf); + break; + } + bool is_last = result == DirWalkLast; + if(!file_info_is_dir(&fileinfo) || is_last) { + if(!is_last) total += fileinfo.size; + double show = total; + size_t unit; + for(unit = 0; unit < COUNT_OF(units); unit++) { + if(show < 1024) break; + show /= 1024; + } + snprintf( + buf, + sizeof(buf), + unit ? "Size: %s\e#%.2f\e# %s" : "Size: %s\e#%.0f\e# %s", + is_last ? "" : "... ", + show, + units[unit]); + widget_element_text_box_set_text(instance->element, buf); + } + if(is_last) break; + } + } else { + snprintf(buf, sizeof(buf), "Size: \e#Error\e#"); + widget_element_text_box_set_text(instance->element, buf); + } + dir_walk_free(dir_walk); + furi_record_close(RECORD_STORAGE); + + view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget); + return 0; +} + void archive_scene_info_on_enter(void* context) { furi_assert(context); ArchiveApp* instance = context; @@ -18,55 +68,30 @@ void archive_scene_info_on_enter(void* context) { widget_add_button_element( instance->widget, GuiButtonTypeLeft, "Back", archive_scene_info_widget_callback, instance); - FuriString* filename; - FuriString* dirname; - FuriString* str_size; - filename = furi_string_alloc(); - dirname = furi_string_alloc(); - str_size = furi_string_alloc(); + FuriString* filename = furi_string_alloc(); + FuriString* dirname = furi_string_alloc(); ArchiveFile_t* current = archive_get_current_file(instance->browser); - char file_info_message[128]; - Storage* fs_api = furi_record_open(RECORD_STORAGE); + char buf[128]; // Filename path_extract_filename(current->path, filename, false); - snprintf( - file_info_message, sizeof(file_info_message), "\e#%s\e#", furi_string_get_cstr(filename)); + snprintf(buf, sizeof(buf), "\e#%s\e#", furi_string_get_cstr(filename)); widget_add_text_box_element( - instance->widget, 0, 0, 128, 25, AlignLeft, AlignCenter, file_info_message, false); + instance->widget, 0, 0, 128, 24, AlignLeft, AlignCenter, buf, false); // Directory path path_extract_dirname(furi_string_get_cstr(current->path), dirname); - - // File size - FileInfo fileinfo; - if(storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo) != FSE_OK || - file_info_is_dir(&fileinfo)) { - snprintf( - file_info_message, - sizeof(file_info_message), - "Size: \e#N/A\e#\n%s", - furi_string_get_cstr(dirname)); - } else if(fileinfo.size <= 1024) { - furi_string_printf(str_size, "%lld", fileinfo.size); - snprintf( - file_info_message, - sizeof(file_info_message), - "Size: \e#%s\e# bytes\n%s", - furi_string_get_cstr(str_size), - furi_string_get_cstr(dirname)); - } else { - furi_string_printf(str_size, "%lld", fileinfo.size / 1024); - snprintf( - file_info_message, - sizeof(file_info_message), - "Size: \e#%s\e# Kb.\n%s", - furi_string_get_cstr(str_size), - furi_string_get_cstr(dirname)); - } widget_add_text_box_element( - instance->widget, 0, 25, 128, 25, AlignLeft, AlignCenter, file_info_message, true); + instance->widget, + 0, + 42, + 128, + 12, + AlignLeft, + AlignCenter, + furi_string_get_cstr(dirname), + false); // This one to return and cursor select this file path_extract_filename_no_ext(furi_string_get_cstr(current->path), filename); @@ -74,9 +99,44 @@ void archive_scene_info_on_enter(void* context) { furi_string_free(filename); furi_string_free(dirname); - furi_string_free(str_size); + + // File size + FileInfo fileinfo; + bool is_dir = false; + if(storage_common_stat( + furi_record_open(RECORD_STORAGE), furi_string_get_cstr(current->path), &fileinfo) != + FSE_OK) { + snprintf(buf, sizeof(buf), "Size: \e#Error\e#"); + } else if(file_info_is_dir(&fileinfo)) { + is_dir = true; + snprintf(buf, sizeof(buf), "Size: ... \e#0\e# %s", units[0]); + + } else { + double show = fileinfo.size; + size_t unit; + for(unit = 0; unit < COUNT_OF(units); unit++) { + if(show < 1024) break; + show /= 1024; + } + snprintf( + buf, + sizeof(buf), + unit ? "Size: \e#%.2f\e# %s" : "Size: \e#%.0f\e# %s", + show, + units[unit]); + } + instance->element = widget_add_text_box_element( + instance->widget, 0, 24, 128, 24, AlignLeft, AlignCenter, buf, true); + furi_record_close(RECORD_STORAGE); view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget); + + if(is_dir) { + scene_manager_set_scene_state(instance->scene_manager, ArchiveAppSceneInfo, true); + instance->thread = furi_thread_alloc_ex( + "ArchiveDirWalk", 1024, (FuriThreadCallback)archive_scene_info_dirwalk, instance); + furi_thread_start(instance->thread); + } } bool archive_scene_info_on_event(void* context, SceneManagerEvent event) { @@ -94,5 +154,11 @@ void archive_scene_info_on_exit(void* context) { furi_assert(context); ArchiveApp* app = (ArchiveApp*)context; + scene_manager_set_scene_state(app->scene_manager, ArchiveAppSceneInfo, false); + if(app->thread) { + furi_thread_join(app->thread); + furi_thread_free(app->thread); + app->thread = NULL; + } widget_reset(app->widget); } From eb20b89f2544a1d8e5d63b5654daf7c21ac1d324 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 19 Jun 2023 20:59:00 +0100 Subject: [PATCH 228/370] Begone comment --- lib/toolbox/dir_walk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index adfeeaea5..003863588 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -58,7 +58,7 @@ static bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* filei static DirWalkResult dir_walk_iter(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) { DirWalkResult result = DirWalkError; - char* name = malloc(MAX_NAME_LEN); // FIXME: remove magic number + char* name = malloc(MAX_NAME_LEN); FileInfo info; bool end = false; From 1009fc40313411348b53ace98ffff35e31591706 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 20 Jun 2023 18:18:32 +0100 Subject: [PATCH 229/370] Orgasmotron fixes (#304) --- .../external/orgasmotron/orgasmotron.c | 88 +++++++++++-------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/applications/external/orgasmotron/orgasmotron.c b/applications/external/orgasmotron/orgasmotron.c index 80c7d9e42..fd66d7b04 100644 --- a/applications/external/orgasmotron/orgasmotron.c +++ b/applications/external/orgasmotron/orgasmotron.c @@ -16,12 +16,9 @@ void vibro_test_draw_callback(Canvas* canvas, void* ctx) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 2, 10, "Vibro Modes"); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 22, "LEFT: strong / RIGHT: Soft"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 34, "UP: Pulsed"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 46, "DOWN Pleasure combo"); - canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 22, "UP: Pulsed"); + canvas_draw_str(canvas, 2, 34, "LEFT: strong / RIGHT: Soft"); + canvas_draw_str(canvas, 2, 46, "DOWN: Pleasure combo"); canvas_draw_str(canvas, 2, 58, "OK: Pause"); } @@ -59,20 +56,16 @@ int32_t orgasmotron_app(void* p) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); InputEvent event; - //int mode = 0; bool processing = true; - //while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { + size_t i = 0; while(processing) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 50); furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); if(event_status == FuriStatusOk) { if(event.key == InputKeyBack && event.type == InputTypeShort) { //Exit Application - notification_message(notification, &sequence_reset_vibro); - notification_message(notification, &sequence_reset_green); plugin_state->mode = 0; processing = false; - //break; } if(event.key == InputKeyOk && (event.type == InputTypePress || event.type == InputTypeRelease)) { @@ -80,57 +73,80 @@ int32_t orgasmotron_app(void* p) { } if(event.key == InputKeyLeft && (event.type == InputTypePress || event.type == InputTypeRelease)) { + notification_message(notification, &sequence_set_green_255); plugin_state->mode = 1; } if(event.key == InputKeyRight && (event.type == InputTypePress || event.type == InputTypeRelease)) { + notification_message(notification, &sequence_set_green_255); plugin_state->mode = 3; } if(event.key == InputKeyUp && (event.type == InputTypePress || event.type == InputTypeRelease)) { + notification_message(notification, &sequence_set_green_255); plugin_state->mode = 2; } if(event.key == InputKeyDown && (event.type == InputTypePress || event.type == InputTypeRelease)) { + notification_message(notification, &sequence_set_green_255); plugin_state->mode = 4; } + i = 0; } if(plugin_state->mode == 0) { //Stop Vibration - notification_message(notification, &sequence_reset_vibro); - notification_message(notification, &sequence_reset_green); + if(i == 0) { + notification_message(notification, &sequence_reset_vibro); + notification_message(notification, &sequence_reset_green); + i++; + } } else if(plugin_state->mode == 1) { //Full power - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); + if(i == 0) { + notification_message(notification, &sequence_set_vibro_on); + i++; + } } else if(plugin_state->mode == 2) { //Pulsed Vibration - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); - delay(100); - notification_message(notification, &sequence_reset_vibro); + i++; + if(i == 1) { + notification_message(notification, &sequence_set_vibro_on); + } + if(i == 3) { + notification_message(notification, &sequence_reset_vibro); + } + if(i == 4) { + i = 0; + } } else if(plugin_state->mode == 3) { //Soft power - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); - delay(50); - notification_message(notification, &sequence_reset_vibro); + i++; + if(i == 1) { + notification_message(notification, &sequence_set_vibro_on); + } + if(i == 2) { + notification_message(notification, &sequence_reset_vibro); + i = 0; + } } else if(plugin_state->mode == 4) { //Special Sequence - for(int i = 0; i < 15; i++) { - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); - delay(50); + i++; + if(i < 23) { + if(i % 2) { + notification_message(notification, &sequence_set_vibro_on); + } else { + notification_message(notification, &sequence_reset_vibro); + } + } else if(i < 40) { + if(i == 24 || i == 33) { + notification_message(notification, &sequence_set_vibro_on); + } else if(i == 32) { + notification_message(notification, &sequence_reset_vibro); + } + } else if(i == 41) { notification_message(notification, &sequence_reset_vibro); - delay(50); - } - for(int i = 0; i < 2; i++) { - notification_message(notification, &sequence_set_vibro_on); - notification_message(notification, &sequence_set_green_255); - delay(400); - notification_message(notification, &sequence_reset_vibro); - delay(50); + i = 0; } } furi_mutex_release(plugin_state->mutex); From 2d7447d9793bfb31b0e99aa6f61267c398e4bb08 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:24:51 +0100 Subject: [PATCH 230/370] Use prev basename instead of index on folder exit Keeps folder history after closing external app --- .../main/archive/helpers/archive_browser.c | 16 ++++++++++++++++ .../services/gui/modules/file_browser_worker.c | 18 +++--------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 5c6f486d1..aca4e7523 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -75,11 +75,27 @@ static void archive_list_item_cb( ArchiveBrowserViewModel * model, { if(model->item_cnt <= BROWSER_SORT_THRESHOLD) { + FuriString* selected = NULL; + if(model->item_idx > 0) { + selected = furi_string_alloc_set( + files_array_get(model->files, model->item_idx)->path); + } + files_array_sort(model->files); + + if(selected != NULL) { + for(uint32_t i = 0; i < model->item_cnt; i++) { + if(!furi_string_cmp(files_array_get(model->files, i)->path, selected)) { + model->item_idx = i; + break; + } + } + } } model->list_loading = false; }, true); + archive_update_offset(browser); } } diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index bde6c02a7..1431041fd 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -32,8 +32,6 @@ typedef enum { (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit | \ WorkerEvtFolderRefresh | WorkerEvtConfigChange) -ARRAY_DEF(idx_last_array, int32_t) - struct BrowserWorker { FuriThread* thread; @@ -46,7 +44,6 @@ struct BrowserWorker { uint32_t load_count; bool skip_assets; bool hide_dot_files; - idx_last_array_t idx_last; void* cb_ctx; BrowserWorkerFolderOpenCallback folder_cb; @@ -345,7 +342,6 @@ static int32_t browser_worker(void* context) { if(browser_path_is_file(browser->path_next)) { path_extract_filename(browser->path_next, filename, false); } - idx_last_array_reset(browser->idx_last); furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); } @@ -354,9 +350,6 @@ static int32_t browser_worker(void* context) { furi_string_set(path, browser->path_next); bool is_root = browser_folder_check_and_switch(path); - // Push previous selected item index to history array - idx_last_array_push_back(browser->idx_last, browser->item_sel_idx); - int32_t file_idx = 0; browser_folder_init(browser, path, filename, &items_cnt, &file_idx); furi_string_set(browser->path_current, path); @@ -373,15 +366,13 @@ static int32_t browser_worker(void* context) { } if(flags & WorkerEvtFolderExit) { + path_extract_basename(furi_string_get_cstr(path), filename); + browser_path_trim(path); bool is_root = browser_folder_check_and_switch(path); int32_t file_idx = 0; browser_folder_init(browser, path, filename, &items_cnt, &file_idx); - if(idx_last_array_size(browser->idx_last) > 0) { - // Pop previous selected item index from history array - idx_last_array_pop_back(&file_idx, browser->idx_last); - } furi_string_set(browser->path_current, path); FURI_LOG_D( TAG, @@ -392,6 +383,7 @@ static int32_t browser_worker(void* context) { if(browser->folder_cb) { browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); } + furi_string_reset(filename); } if(flags & WorkerEvtFolderRefresh) { @@ -442,8 +434,6 @@ BrowserWorker* file_browser_worker_alloc( bool hide_dot_files) { BrowserWorker* browser = malloc(sizeof(BrowserWorker)); - idx_last_array_init(browser->idx_last); - browser->filter_extension = furi_string_alloc_set(filter_ext); browser->skip_assets = skip_assets; browser->hide_dot_files = hide_dot_files; @@ -474,8 +464,6 @@ void file_browser_worker_free(BrowserWorker* browser) { furi_string_free(browser->path_current); furi_string_free(browser->path_start); - idx_last_array_clear(browser->idx_last); - free(browser); } From 1c7d3eeb42febf5cf8cf1a07bb99967d35e76a2d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:16:19 +0100 Subject: [PATCH 231/370] Power off from locked (flipperdevices/flipperzero-firmware#1567) --- .../desktop/scenes/desktop_scene_locked.c | 10 ++++++++ .../services/desktop/views/desktop_events.h | 1 + .../desktop/views/desktop_view_locked.c | 6 +++++ .../desktop/views/desktop_view_main.c | 25 ++----------------- .../power_settings_app/power_settings_app.c | 14 ++++++++--- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index 11dd7f222..adf7a6a7c 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -15,6 +15,8 @@ #include "desktop_scene.h" #include "desktop_scene_i.h" +#define TAG "DesktopSrv" + #define WRONG_PIN_HEADER_TIMEOUT 3000 #define INPUT_PIN_VIEW_TIMEOUT 15000 @@ -83,6 +85,14 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { + case DesktopLockedEventOpenPowerOff: { + LoaderStatus status = loader_start(desktop->loader, "Power", "off"); + if(status != LoaderStatusOk) { + FURI_LOG_E(TAG, "loader_start failed: %d", status); + } + consumed = true; + break; + } case DesktopLockedEventUnlocked: desktop_unlock(desktop); consumed = true; diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index af7597012..11b45a8c7 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -23,6 +23,7 @@ typedef enum { DesktopMainEventOpenSubRemote, DesktopMainEventOpenClock, + DesktopLockedEventOpenPowerOff, DesktopLockedEventUnlocked, DesktopLockedEventUpdate, DesktopLockedEventShowPinInput, diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 21f24dde9..69ace11ed 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -231,6 +231,12 @@ static bool desktop_view_locked_input(InputEvent* event, void* context) { desktop_view_locked_update_hint_icon_timeout(locked_view); + if(event->key == InputKeyBack) { + if(event->type == InputTypeLong) { + locked_view->callback(DesktopLockedEventOpenPowerOff, locked_view->context); + } + } + if(pin_locked) { if(event->key == InputKeyUp) { locked_view->callback(DesktopLockedEventShowPinInput, locked_view->context); diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index c1009fde1..e7e4ab4fd 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -13,16 +13,8 @@ struct DesktopMainView { View* view; DesktopMainViewCallback callback; void* context; - TimerHandle_t poweroff_timer; }; -#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 1300 - -static void desktop_main_poweroff_timer_callback(TimerHandle_t timer) { - DesktopMainView* main_view = pvTimerGetTimerID(timer); - main_view->callback(DesktopMainEventOpenPowerOff, main_view->context); -} - void desktop_main_set_callback( DesktopMainView* main_view, DesktopMainViewCallback callback, @@ -68,13 +60,8 @@ bool desktop_main_input_callback(InputEvent* event, void* context) { } if(event->key == InputKeyBack) { - if(event->type == InputTypePress) { - xTimerChangePeriod( - main_view->poweroff_timer, - pdMS_TO_TICKS(DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT), - portMAX_DELAY); - } else if(event->type == InputTypeRelease) { - xTimerStop(main_view->poweroff_timer, portMAX_DELAY); + if(event->type == InputTypeLong) { + main_view->callback(DesktopMainEventOpenPowerOff, main_view->context); } } @@ -88,19 +75,11 @@ DesktopMainView* desktop_main_alloc() { view_set_context(main_view->view, main_view); view_set_input_callback(main_view->view, desktop_main_input_callback); - main_view->poweroff_timer = xTimerCreate( - NULL, - pdMS_TO_TICKS(DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT), - pdFALSE, - main_view, - desktop_main_poweroff_timer_callback); - return main_view; } void desktop_main_free(DesktopMainView* main_view) { furi_assert(main_view); view_free(main_view->view); - furi_timer_free(main_view->poweroff_timer); free(main_view); } diff --git a/applications/settings/power_settings_app/power_settings_app.c b/applications/settings/power_settings_app/power_settings_app.c index 1d5aafef1..3f969128f 100644 --- a/applications/settings/power_settings_app/power_settings_app.c +++ b/applications/settings/power_settings_app/power_settings_app.c @@ -19,7 +19,7 @@ static void power_settings_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { +PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene, ViewDispatcherType type) { PowerSettingsApp* app = malloc(sizeof(PowerSettingsApp)); app->about_battery = first_scene == PowerSettingsAppSceneBatteryInfo; @@ -42,7 +42,10 @@ PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) { app->view_dispatcher, power_settings_back_event_callback); view_dispatcher_set_tick_event_callback( app->view_dispatcher, power_settings_tick_event_callback, 2000); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, type); + if(type == ViewDispatcherTypeDesktop) { + gui_set_hide_statusbar(app->gui, true); + } // Views app->battery_info = battery_info_alloc(); @@ -95,14 +98,16 @@ void power_settings_app_free(PowerSettingsApp* app) { int32_t power_settings_app(void* p) { uint32_t first_scene = PowerSettingsAppSceneStart; + ViewDispatcherType type = ViewDispatcherTypeFullscreen; if(p && strlen(p)) { if(!strcmp(p, "off")) { first_scene = PowerSettingsAppScenePowerOff; + type = ViewDispatcherTypeDesktop; } else if(!strcmp(p, "about_battery")) { first_scene = PowerSettingsAppSceneBatteryInfo; } } - PowerSettingsApp* app = power_settings_app_alloc(first_scene); + PowerSettingsApp* app = power_settings_app_alloc(first_scene, type); while(true) { view_dispatcher_run(app->view_dispatcher); if(app->battery_info->exit_to_about) { @@ -114,6 +119,9 @@ int32_t power_settings_app(void* p) { } break; } + if(type == ViewDispatcherTypeDesktop) { + gui_set_hide_statusbar(app->gui, false); + } power_settings_app_free(app); return 0; } From 42056d17b196c35c25c8349d2b2fd634978a17c2 Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 21 Jun 2023 17:16:25 +0300 Subject: [PATCH 232/370] FBT: build and add FastFAP(tm) sections --- scripts/fastfap.py | 170 +++++++++++++++++++++++++++++++ scripts/fbt/sdk/collector.py | 8 +- scripts/fbt/sdk/hashes.py | 5 + scripts/fbt_tools/fbt_extapps.py | 15 ++- 4 files changed, 187 insertions(+), 11 deletions(-) create mode 100755 scripts/fastfap.py create mode 100644 scripts/fbt/sdk/hashes.py diff --git a/scripts/fastfap.py b/scripts/fastfap.py new file mode 100755 index 000000000..c84af95d1 --- /dev/null +++ b/scripts/fastfap.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +import hashlib +import os +import struct +import subprocess +import tempfile +from collections import defaultdict +from dataclasses import dataclass + +from elftools.elf.elffile import ELFFile +from elftools.elf.enums import ENUM_RELOC_TYPE_ARM +from elftools.elf.relocation import RelocationSection +from elftools.elf.sections import SymbolTableSection +from fbt.sdk.hashes import gnu_sym_hash +from flipper.app import App + +VERSION = 1 + + +@dataclass +class RelData: + section: int + section_value: int + type: int + offset: int + name: str + + +@dataclass(frozen=True) +class UniqueRelData: + section: int + section_value: int + type: int + name: str + + +@dataclass +class RelSection: + name: str + oringinal_name: str + data: dict[UniqueRelData, list[int]] + + +def serialize_relsection_data(data: dict[UniqueRelData, list[int]]) -> bytes: + result = struct.pack(" 0: + result += struct.pack("> 8) & 0xFF, (offset >> 16) & 0xFF + ) + + return result + + +class Main(App): + def init(self): + self.parser.add_argument("fap_src_path", help="App file to upload") + self.parser.add_argument("objcopy_path", help="Objcopy path") + self.parser.set_defaults(func=self.process) + + def process(self): + fap_path = self.args.fap_src_path + objcopy_path = self.args.objcopy_path + + sections: list[RelSection] = [] + + with open(fap_path, "rb") as f: + elf_file = ELFFile(f) + + relocation_sections: list[RelocationSection] = [] + symtab_section: SymbolTableSection | None = None + + for section in elf_file.iter_sections(): + if isinstance(section, RelocationSection): + relocation_sections.append(section) + + if isinstance(section, SymbolTableSection): + symtab_section = section + + if not symtab_section: + self.logger.error("No symbol table found") + return 1 + + if not relocation_sections: + self.logger.info("No relocation sections found") + return 0 + + for section in relocation_sections: + section_relocations: list[RelData] = [] + + for relocation in section.iter_relocations(): + symbol_id: int = relocation.entry["r_info_sym"] + offset: int = relocation.entry["r_offset"] + type: int = relocation.entry["r_info_type"] + symbol = symtab_section.get_symbol(symbol_id) + section_index: int = symbol["st_shndx"] + section_value: int = symbol["st_value"] + if section_index == "SHN_UNDEF": + section_index = 0 + + section_relocations.append( + RelData(section_index, section_value, type, offset, symbol.name) + ) + + unique_relocations: dict[UniqueRelData, list[int]] = defaultdict(list) + for relocation in section_relocations: + unique = UniqueRelData( + relocation.section, + relocation.section_value, + relocation.type, + relocation.name, + ) + + unique_relocations[unique].append(relocation.offset) + + section_name = section.name + if section_name.startswith(".rel"): + section_name = ".fast.rel" + section_name[4:] + else: + self.logger.error( + "Unknown relocation section name: %s", section_name + ) + return 1 + + sections.append( + RelSection(section_name, section.name, unique_relocations) + ) + + with tempfile.TemporaryDirectory() as temp_dir: + for section in sections: + data = serialize_relsection_data(section.data) + hash_name = hashlib.md5(section.name.encode()).hexdigest() + filename = f"{temp_dir}/{hash_name}.bin" + + if os.path.isfile(filename): + self.logger.error(f"File {filename} already exists") + return 1 + + with open(filename, "wb") as f: + f.write(data) + + exit_code = subprocess.run( + [ + objcopy_path, + "--add-section", + f"{section.name}={filename}", + fap_path, + ], + check=True, + ) + + if exit_code.returncode != 0: + self.logger.error("objcopy failed") + return 1 + + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py index 578a8c7a6..1dd3bc4eb 100644 --- a/scripts/fbt/sdk/collector.py +++ b/scripts/fbt/sdk/collector.py @@ -1,4 +1,5 @@ from typing import List +from .hashes import gnu_sym_hash from cxxheaderparser.parser import CxxParser from . import ( @@ -72,13 +73,6 @@ class SymbolManager: self.api.headers.add(ApiHeader(header)) -def gnu_sym_hash(name: str): - h = 0x1505 - for c in name: - h = (h << 5) + h + ord(c) - return str(hex(h))[-8:] - - class SdkCollector: def __init__(self): self.symbol_manager = SymbolManager() diff --git a/scripts/fbt/sdk/hashes.py b/scripts/fbt/sdk/hashes.py new file mode 100644 index 000000000..fef88ddb5 --- /dev/null +++ b/scripts/fbt/sdk/hashes.py @@ -0,0 +1,5 @@ +def gnu_sym_hash(name: str) -> int: + h = 0x1505 + for c in name: + h = ((h << 5) + h + ord(c)) & 0xFFFFFFFF + return h diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 16d5dcbab..65f265b54 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -384,10 +384,16 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature): "${SOURCES} ${TARGET}" ) - actions.append( - Action( - objcopy_str, - "$APPMETAEMBED_COMSTR", + actions.extend( + ( + Action( + objcopy_str, + "$APPMETAEMBED_COMSTR", + ), + Action( + "${FBT_SCRIPT_DIR}/fastfap.py ${TARGET} ${OBJCOPY}", + "$FASTFAP_COMSTR", + ), ) ) @@ -450,6 +456,7 @@ def generate(env, **kw): APPMETA_COMSTR="\tAPPMETA\t${TARGET}", APPFILE_COMSTR="\tAPPFILE\t${TARGET}", APPMETAEMBED_COMSTR="\tFAP\t${TARGET}", + FASTFAP_COMSTR="\tFASTFAP\t${TARGET}", APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", ) From 1bc9c03f359f1057f7011d4160692e89c86cdb1b Mon Sep 17 00:00:00 2001 From: SG Date: Wed, 21 Jun 2023 17:17:35 +0300 Subject: [PATCH 233/370] Elf file: fast loading fap files. Really fast, like x15 times faster. --- firmware/targets/f18/api_symbols.csv | 5 +- firmware/targets/f7/api_symbols.csv | 5 +- .../api_hashtable/api_hashtable.cpp | 19 ++- .../api_hashtable/api_hashtable.h | 12 +- .../elf/elf_api_interface.h | 2 +- lib/flipper_application/elf/elf_file.c | 127 +++++++++++++++++- lib/flipper_application/elf/elf_file_i.h | 10 +- .../plugins/composite_resolver.c | 4 +- 8 files changed, 153 insertions(+), 31 deletions(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index d429de655..fc62e9944 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.1,, +Version,+,31.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -679,7 +679,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 80b4eedbd..b1ec56d3b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.1,, +Version,+,31.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -808,7 +808,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* diff --git a/lib/flipper_application/api_hashtable/api_hashtable.cpp b/lib/flipper_application/api_hashtable/api_hashtable.cpp index 022792dce..6db5fb5fd 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.cpp +++ b/lib/flipper_application/api_hashtable/api_hashtable.cpp @@ -7,27 +7,22 @@ bool elf_resolve_from_hashtable( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address) { + bool result = false; const HashtableApiInterface* hashtable_interface = static_cast(interface); - bool result = false; - uint32_t gnu_sym_hash = elf_gnu_hash(name); sym_entry key = { - .hash = gnu_sym_hash, + .hash = hash, .address = 0, }; auto find_res = std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key); - if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) { + if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) { FURI_LOG_W( - TAG, - "Can't find symbol '%s' (hash %lx) @ %p!", - name, - gnu_sym_hash, - hashtable_interface->table_cbegin); + TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin); result = false; } else { result = true; @@ -36,3 +31,7 @@ bool elf_resolve_from_hashtable( return result; } + +uint32_t elf_symbolname_hash(const char* s) { + return elf_gnu_hash(s); +} \ No newline at end of file diff --git a/lib/flipper_application/api_hashtable/api_hashtable.h b/lib/flipper_application/api_hashtable/api_hashtable.h index 7e4b4aba1..7ba6aab97 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.h +++ b/lib/flipper_application/api_hashtable/api_hashtable.h @@ -19,15 +19,17 @@ struct sym_entry { /** * @brief Resolver for API entries using a pre-sorted table with hashes * @param interface pointer to HashtableApiInterface - * @param name function name + * @param hash gnu hash of function name * @param address output for function address * @return true if the table contains a function */ bool elf_resolve_from_hashtable( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address); +uint32_t elf_symbolname_hash(const char* s); + #ifdef __cplusplus } @@ -48,8 +50,10 @@ struct HashtableApiInterface : public ElfApiInterface { .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ } -#define API_VARIABLE(x, var_type) \ - sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), } +#define API_VARIABLE(x, var_type) \ + sym_entry { \ + .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \ + } constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { return k1.hash < k2.hash; diff --git a/lib/flipper_application/elf/elf_api_interface.h b/lib/flipper_application/elf/elf_api_interface.h index f07df4edb..facdc4447 100644 --- a/lib/flipper_application/elf/elf_api_interface.h +++ b/lib/flipper_application/elf/elf_api_interface.h @@ -11,6 +11,6 @@ typedef struct ElfApiInterface { uint16_t api_version_minor; bool (*resolver_callback)( const struct ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address); } ElfApiInterface; diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 0338144a9..fcb5c1d7d 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -2,6 +2,7 @@ #include "elf_file.h" #include "elf_file_i.h" #include "elf_api_interface.h" +#include "../api_hashtable/api_hashtable.h" #define TAG "elf" @@ -9,6 +10,7 @@ #define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr)) #define IS_FLAGS_SET(v, m) (((v) & (m)) == (m)) #define RESOLVER_THREAD_YIELD_STEP 30 +#define FAST_RELOCATION_VERSION 1 // #define ELF_DEBUG_LOG 1 @@ -71,6 +73,7 @@ static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) { .size = 0, .rel_count = 0, .rel_offset = 0, + .fast_rel = NULL, }); section_p = elf_file_get_section(elf, name); } @@ -168,7 +171,8 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) { static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) { if(sym->st_shndx == SHN_UNDEF) { Elf32_Addr addr = 0; - if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) { + uint32_t hash = elf_symbolname_hash(sName); + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { return addr; } } else { @@ -424,6 +428,7 @@ typedef enum { SectionTypeSymTab = 1 << 3, SectionTypeStrTab = 1 << 4, SectionTypeDebugLink = 1 << 5, + SectionTypeFastRelData = 1 << 6, SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab, } SectionType; @@ -505,7 +510,8 @@ static SectionType elf_preload_section( // TODO: how to do it not by name? // .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER // .rel.ARM: type 0x9, flags SHT_REL - if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.")) { + if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") || + str_prefix(name, ".fast.rel.ARM.")) { FURI_LOG_D(TAG, "Ignoring ARM section"); return SectionTypeUnused; } @@ -536,11 +542,32 @@ static SectionType elf_preload_section( // Load link info section if(section_header->sh_flags & SHF_INFO_LINK) { - name = name + strlen(".rel"); + if(str_prefix(name, ".rel")) { + name = name + strlen(".rel"); + ELFSection* section_p = elf_file_get_or_put_section(elf, name); + section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); + section_p->rel_offset = section_header->sh_offset; + return SectionTypeRelData; + } else { + FURI_LOG_E(TAG, "Unknown link info section '%s'", name); + return SectionTypeERROR; + } + } + + // Load fast rel section + if(str_prefix(name, ".fast.rel")) { + name = name + strlen(".fast.rel"); ELFSection* section_p = elf_file_get_or_put_section(elf, name); - section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); - section_p->rel_offset = section_header->sh_offset; - return SectionTypeRelData; + section_p->fast_rel = malloc(sizeof(ELFSection)); + FURI_LOG_I(TAG, "Fast rel section size: %ld", section_header->sh_size); + + if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) { + FURI_LOG_E(TAG, "Error loading section '%s'", name); + return SectionTypeERROR; + } + + FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name); + return SectionTypeFastRelData; } // Load symbol table @@ -571,8 +598,90 @@ static SectionType elf_preload_section( return SectionTypeUnused; } +static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) { + Elf32_Addr addr = 0; + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { + return addr; + } + return ELF_INVALID_ADDRESS; +} + +static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) { + UNUSED(elf); + const uint8_t* start = s->fast_rel->data; + const uint8_t version = *start; + + if(version != FAST_RELOCATION_VERSION) { + FURI_LOG_E(TAG, "Unsupported fast relocation version %d", version); + return false; + } + start += 1; + + const uint32_t records_count = *((uint32_t*)start); + start += 4; + FURI_LOG_D(TAG, "Fast relocation records count: %ld", records_count); + + for(uint32_t i = 0; i < records_count; i++) { + bool is_section = (*start & (0x1 << 7)) ? true : false; + uint8_t type = *start & 0x7F; + start += 1; + uint32_t hash_or_section_index = *((uint32_t*)start); + start += 4; + + uint32_t section_value = ELF_INVALID_ADDRESS; + if(is_section) { + section_value = *((uint32_t*)start); + start += 4; + } + + const uint32_t offsets_count = *((uint32_t*)start); + start += 4; + + FURI_LOG_D( + TAG, + "Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld", + i, + is_section, + type, + hash_or_section_index, + offsets_count); + + Elf32_Addr address = 0; + if(is_section) { + ELFSection* symSec = elf_section_of(elf, hash_or_section_index); + if(symSec) { + address = ((Elf32_Addr)symSec->data) + section_value; + } + } else { + address = elf_address_of_by_hash(elf, hash_or_section_index); + } + + if(address == ELF_INVALID_ADDRESS) { + FURI_LOG_E(TAG, "Failed to resolve address for hash %lX", hash_or_section_index); + return false; + } + + for(uint32_t j = 0; j < offsets_count; j++) { + uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF; + start += 3; + // FURI_LOG_I(TAG, " Fast relocation offset %ld: %ld", j, offset); + Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset; + elf_relocate_symbol(elf, relAddr, type, address); + } + } + + aligned_free(s->fast_rel->data); + free(s->fast_rel); + s->fast_rel = NULL; + + return true; +} + static bool elf_relocate_section(ELFFile* elf, ELFSection* section) { - if(section->rel_count) { + if(section->fast_rel) { + FURI_LOG_D(TAG, "Fast relocating section"); + return elf_relocate_fast(elf, section); + } else if(section->rel_count) { FURI_LOG_D(TAG, "Relocating section"); return elf_relocate(elf, section); } else { @@ -630,6 +739,10 @@ void elf_file_free(ELFFile* elf) { if(itref->value.data) { aligned_free(itref->value.data); } + if(itref->value.fast_rel) { + aligned_free(itref->value.fast_rel->data); + free(itref->value.fast_rel); + } free((void*)itref->key); } diff --git a/lib/flipper_application/elf/elf_file_i.h b/lib/flipper_application/elf/elf_file_i.h index af9a1d9b4..39cadfdc6 100644 --- a/lib/flipper_application/elf/elf_file_i.h +++ b/lib/flipper_application/elf/elf_file_i.h @@ -13,14 +13,18 @@ DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST) */ typedef int32_t(entry_t)(void*); -typedef struct { +typedef struct ELFSection ELFSection; + +struct ELFSection { void* data; - uint16_t sec_idx; Elf32_Word size; size_t rel_count; Elf32_Off rel_offset; -} ELFSection; + ELFSection* fast_rel; + + uint16_t sec_idx; +}; DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST) diff --git a/lib/flipper_application/plugins/composite_resolver.c b/lib/flipper_application/plugins/composite_resolver.c index 1402c3ad0..7cc2b340a 100644 --- a/lib/flipper_application/plugins/composite_resolver.c +++ b/lib/flipper_application/plugins/composite_resolver.c @@ -13,12 +13,12 @@ struct CompositeApiResolver { static bool composite_api_resolver_callback( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address) { CompositeApiResolver* resolver = (CompositeApiResolver*)interface; for M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) { - if((*interface)->resolver_callback(*interface, name, address)) { + if((*interface)->resolver_callback(*interface, hash, address)) { return true; } } From 5917c506aace5f093fe8e14c58d6f172651686fd Mon Sep 17 00:00:00 2001 From: SG Date: Thu, 22 Jun 2023 10:32:33 +0300 Subject: [PATCH 234/370] fastfap.py: cleanup unused imports --- scripts/fastfap.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/fastfap.py b/scripts/fastfap.py index c84af95d1..95e32c37b 100755 --- a/scripts/fastfap.py +++ b/scripts/fastfap.py @@ -8,7 +8,6 @@ from collections import defaultdict from dataclasses import dataclass from elftools.elf.elffile import ELFFile -from elftools.elf.enums import ENUM_RELOC_TYPE_ARM from elftools.elf.relocation import RelocationSection from elftools.elf.sections import SymbolTableSection from fbt.sdk.hashes import gnu_sym_hash From 62da715f754b95b6ff0e3cb596b5be4ff5d232ed Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Thu, 22 Jun 2023 22:33:01 +0300 Subject: [PATCH 235/370] Hid app: keynote vertical redraw and cleanup --- .../external/hid_app/assets/Space_60x18.png | Bin 0 -> 2871 bytes applications/external/hid_app/hid.c | 18 +- applications/external/hid_app/hid.h | 2 - applications/external/hid_app/views.h | 1 - .../external/hid_app/views/hid_keynote.c | 98 ++++++++ .../external/hid_app/views/hid_keynote.h | 2 + .../hid_app/views/hid_keynote_vertical.c | 228 ------------------ .../hid_app/views/hid_keynote_vertical.h | 16 -- 8 files changed, 104 insertions(+), 261 deletions(-) create mode 100644 applications/external/hid_app/assets/Space_60x18.png delete mode 100644 applications/external/hid_app/views/hid_keynote_vertical.c delete mode 100644 applications/external/hid_app/views/hid_keynote_vertical.h diff --git a/applications/external/hid_app/assets/Space_60x18.png b/applications/external/hid_app/assets/Space_60x18.png new file mode 100644 index 0000000000000000000000000000000000000000..e29f50ae9220d2f9a9753850dedcc6be0a211e76 GIT binary patch literal 2871 zcmV-73&`||P)004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000Uv 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 VN_%sSbR7Ty002ovPDHLkV1hy!X)pi) literal 0 HcmV?d00001 diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index d115f5bbf..7b136e63f 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -22,10 +22,12 @@ static void hid_submenu_callback(void* context, uint32_t index) { Hid* app = context; if(index == HidSubmenuIndexKeynote) { app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, false); view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); } else if(index == HidSubmenuIndexKeynoteVertical) { - app->view_id = HidViewKeynoteVertical; - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynoteVertical); + app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, true); + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); } else if(index == HidSubmenuIndexKeyboard) { app->view_id = HidViewKeyboard; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); @@ -62,7 +64,6 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con } } hid_keynote_set_connected_status(hid->hid_keynote, connected); - hid_keynote_vertical_set_connected_status(hid->hid_keynote_vertical, connected); hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_numpad_set_connected_status(hid->hid_numpad, connected); hid_media_set_connected_status(hid->hid_media, connected); @@ -177,15 +178,6 @@ Hid* hid_app_alloc_view(void* context) { view_dispatcher_add_view( app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); - // Keynote Vertical view - app->hid_keynote_vertical = hid_keynote_vertical_alloc(app); - view_set_previous_callback( - hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, - HidViewKeynoteVertical, - hid_keynote_vertical_get_view(app->hid_keynote_vertical)); - // Keyboard view app->hid_keyboard = hid_keyboard_alloc(app); view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); @@ -252,8 +244,6 @@ void hid_free(Hid* app) { dialog_ex_free(app->dialog); view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); hid_keynote_free(app->hid_keynote); - view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynoteVertical); - hid_keynote_vertical_free(app->hid_keynote_vertical); view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); hid_keyboard_free(app->hid_keyboard); view_dispatcher_remove_view(app->view_dispatcher, HidViewNumpad); diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index 4a8cd2a98..3899c860a 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -17,7 +17,6 @@ #include #include #include "views/hid_keynote.h" -#include "views/hid_keynote_vertical.h" #include "views/hid_keyboard.h" #include "views/hid_numpad.h" #include "views/hid_media.h" @@ -43,7 +42,6 @@ struct Hid { Submenu* device_type_submenu; DialogEx* dialog; HidKeynote* hid_keynote; - HidKeynoteVertical* hid_keynote_vertical; HidKeyboard* hid_keyboard; HidNumpad* hid_numpad; HidMedia* hid_media; diff --git a/applications/external/hid_app/views.h b/applications/external/hid_app/views.h index 5d02220cd..1f1536486 100644 --- a/applications/external/hid_app/views.h +++ b/applications/external/hid_app/views.h @@ -1,7 +1,6 @@ typedef enum { HidViewSubmenu, HidViewKeynote, - HidViewKeynoteVertical, HidViewKeyboard, HidViewNumpad, HidViewMedia, diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c index 5e5eeb790..543363bf6 100644 --- a/applications/external/hid_app/views/hid_keynote.c +++ b/applications/external/hid_app/views/hid_keynote.c @@ -111,6 +111,91 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); } +static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); + } else { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); + } + + canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + const uint8_t x_2 = 23; + const uint8_t x_1 = 2; + const uint8_t x_3 = 44; + + const uint8_t y_1 = 44; + const uint8_t y_2 = 65; + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom); + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + canvas_draw_icon(canvas, 2, 86, &I_Space_60x18); + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 5, 88, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9); + elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space"); + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 2, 107, &I_Space_60x18); + if(model->back_pressed) { + elements_slightly_rounded_box(canvas, 5, 109, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back"); +} + static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { with_view_model( hid_keynote->view, @@ -212,3 +297,16 @@ void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { with_view_model( hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); } + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) { + furi_assert(hid_keynote); + + if(vertical) { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback); + view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip); + + } else { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_orientation(hid_keynote->view, ViewOrientationHorizontal); + } +} diff --git a/applications/external/hid_app/views/hid_keynote.h b/applications/external/hid_app/views/hid_keynote.h index 4d4a0a9b1..3e84732aa 100644 --- a/applications/external/hid_app/views/hid_keynote.h +++ b/applications/external/hid_app/views/hid_keynote.h @@ -12,3 +12,5 @@ void hid_keynote_free(HidKeynote* hid_keynote); View* hid_keynote_get_view(HidKeynote* hid_keynote); void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical); \ No newline at end of file diff --git a/applications/external/hid_app/views/hid_keynote_vertical.c b/applications/external/hid_app/views/hid_keynote_vertical.c deleted file mode 100644 index 7d2303813..000000000 --- a/applications/external/hid_app/views/hid_keynote_vertical.c +++ /dev/null @@ -1,228 +0,0 @@ -#include "hid_keynote_vertical.h" -#include -#include "../hid.h" - -#include "hid_icons.h" - -#define TAG "HidKeynoteVertical" - -struct HidKeynoteVertical { - View* view; - Hid* hid; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool back_pressed; - bool connected; - HidTransport transport; -} HidKeynoteVerticalModel; - -static void - hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { - canvas_draw_triangle(canvas, x, y, 5, 3, dir); - if(dir == CanvasDirectionBottomToTop) { - canvas_draw_line(canvas, x, y + 6, x, y - 1); - } else if(dir == CanvasDirectionTopToBottom) { - canvas_draw_line(canvas, x, y - 6, x, y + 1); - } else if(dir == CanvasDirectionRightToLeft) { - canvas_draw_line(canvas, x + 6, y, x - 1, y); - } else if(dir == CanvasDirectionLeftToRight) { - canvas_draw_line(canvas, x - 6, y, x + 1, y); - } -} - -static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidKeynoteVerticalModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - } - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned( - canvas, 24, 14, AlignLeft, AlignTop, "Vertical Up --->"); - - canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit"); - - // Up - canvas_draw_icon(canvas, 21, 24, &I_Button_18x18); - if(model->up_pressed) { - elements_slightly_rounded_box(canvas, 24, 26, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_vertical_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); - canvas_set_color(canvas, ColorBlack); - - // Down - canvas_draw_icon(canvas, 21, 45, &I_Button_18x18); - if(model->down_pressed) { - elements_slightly_rounded_box(canvas, 24, 47, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_vertical_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); - canvas_set_color(canvas, ColorBlack); - - // Left - canvas_draw_icon(canvas, 0, 35, &I_Button_18x18); - if(model->left_pressed) { - elements_slightly_rounded_box(canvas, 3, 37, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_vertical_draw_arrow(canvas, 7, 43, CanvasDirectionRightToLeft); - canvas_set_color(canvas, ColorBlack); - - // Right - canvas_draw_icon(canvas, 42, 35, &I_Button_18x18); - if(model->right_pressed) { - elements_slightly_rounded_box(canvas, 45, 37, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_vertical_draw_arrow(canvas, 53, 43, CanvasDirectionLeftToRight); - canvas_set_color(canvas, ColorBlack); - - // Ok - canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); - if(model->ok_pressed) { - elements_slightly_rounded_box(canvas, 66, 27, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space"); - canvas_set_color(canvas, ColorBlack); - - // Back - canvas_draw_icon(canvas, 63, 45, &I_Space_65x18); - if(model->back_pressed) { - elements_slightly_rounded_box(canvas, 66, 47, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); -} - -static void - hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) { - with_view_model( - hid_keynote_vertical->view, - HidKeynoteVerticalModel * model, - { - if(event->type == InputTypePress) { - if(event->key == InputKeyUp) { - model->up_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR); - } else if(event->key == InputKeyBack) { - model->back_pressed = true; - } - } else if(event->type == InputTypeRelease) { - if(event->key == InputKeyUp) { - model->up_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR); - } else if(event->key == InputKeyBack) { - model->back_pressed = false; - } - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE); - hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE); - hid_hal_consumer_key_press(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK); - hid_hal_consumer_key_release(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK); - } - } - }, - true); -} - -static bool hid_keynote_vertical_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidKeynoteVertical* hid_keynote_vertical = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - hid_hal_keyboard_release_all(hid_keynote_vertical->hid); - } else { - hid_keynote_vertical_process(hid_keynote_vertical, event); - consumed = true; - } - - return consumed; -} - -HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* hid) { - HidKeynoteVertical* hid_keynote_vertical = malloc(sizeof(HidKeynoteVertical)); - hid_keynote_vertical->view = view_alloc(); - hid_keynote_vertical->hid = hid; - view_set_context(hid_keynote_vertical->view, hid_keynote_vertical); - view_allocate_model( - hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel)); - view_set_draw_callback(hid_keynote_vertical->view, hid_keynote_vertical_draw_callback); - view_set_input_callback(hid_keynote_vertical->view, hid_keynote_vertical_input_callback); - - with_view_model( - hid_keynote_vertical->view, - HidKeynoteVerticalModel * model, - { model->transport = hid->transport; }, - true); - - return hid_keynote_vertical; -} - -void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical) { - furi_assert(hid_keynote_vertical); - view_free(hid_keynote_vertical->view); - free(hid_keynote_vertical); -} - -View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical) { - furi_assert(hid_keynote_vertical); - return hid_keynote_vertical->view; -} - -void hid_keynote_vertical_set_connected_status( - HidKeynoteVertical* hid_keynote_vertical, - bool connected) { - furi_assert(hid_keynote_vertical); - with_view_model( - hid_keynote_vertical->view, - HidKeynoteVerticalModel * model, - { model->connected = connected; }, - true); -} diff --git a/applications/external/hid_app/views/hid_keynote_vertical.h b/applications/external/hid_app/views/hid_keynote_vertical.h deleted file mode 100644 index bb7134adb..000000000 --- a/applications/external/hid_app/views/hid_keynote_vertical.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -typedef struct Hid Hid; -typedef struct HidKeynoteVertical HidKeynoteVertical; - -HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* bt_hid); - -void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical); - -View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical); - -void hid_keynote_vertical_set_connected_status( - HidKeynoteVertical* hid_keynote_vertical, - bool connected); From 21498597194282f0e96b2226359244cc91e2109e Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 23 Jun 2023 00:06:20 +0300 Subject: [PATCH 236/370] Hid app: vertival numpad --- .../external/hid_app/views/hid_keynote.c | 4 +- .../external/hid_app/views/hid_numpad.c | 56 ++++++++++++------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c index 543363bf6..7d0e125d7 100644 --- a/applications/external/hid_app/views/hid_keynote.c +++ b/applications/external/hid_app/views/hid_keynote.c @@ -116,16 +116,16 @@ static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { HidKeynoteModel* model = context; // Header + canvas_set_font(canvas, FontPrimary); if(model->transport == HidTransportBle) { if(model->connected) { canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); } else { canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); } - canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); } else { - canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); } diff --git a/applications/external/hid_app/views/hid_numpad.c b/applications/external/hid_app/views/hid_numpad.c index d3b488801..cedb6d341 100644 --- a/applications/external/hid_app/views/hid_numpad.c +++ b/applications/external/hid_app/views/hid_numpad.c @@ -39,26 +39,26 @@ typedef struct { int8_t y; } HidNumpadPoint; -#define MARGIN_TOP 0 -#define MARGIN_LEFT 24 +#define MARGIN_TOP 32 +#define MARGIN_LEFT 1 #define KEY_WIDTH 20 #define KEY_HEIGHT 15 #define KEY_PADDING 1 -#define ROW_COUNT 5 -#define COLUMN_COUNT 4 +#define ROW_COUNT 6 +#define COLUMN_COUNT 3 const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = { { {.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK}, {.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH}, {.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK}, - {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS}, + // {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS}, }, { {.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7}, {.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8}, {.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9}, - {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS}, + // {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS}, }, { {.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4}, @@ -69,13 +69,18 @@ const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = { {.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1}, {.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2}, {.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3}, - {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER}, + // {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER}, }, { {.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0}, {.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0}, {.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT}, }, + { + {.width = 1, .height = 1, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER}, + {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS}, + {.width = 1, .height = 1, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS}, + }, }; static void hid_numpad_draw_key( @@ -128,26 +133,36 @@ static void hid_numpad_draw_callback(Canvas* canvas, void* context) { furi_assert(context); HidNumpadModel* model = context; - if((!model->connected) && (model->transport == HidTransportBle)) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Numpad"); - - canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit"); + // Header + canvas_set_font(canvas, FontPrimary); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad"); elements_multiline_text_aligned( - canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection..."); + canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection..."); + } else { + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad"); + } + + canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + if(!model->connected && (model->transport == HidTransportBle)) { return; } canvas_set_font(canvas, FontKeyboard); - uint8_t initY = model->y == 0 ? 0 : 1; + uint8_t initY = 0; // = model->y == 0 ? 0 : 1; - if(model->y > 5) { - initY = model->y - 4; - } + // if(model->y > ROW_COUNT) { + // initY = model->y - (ROW_COUNT - 1); + // } for(uint8_t y = initY; y < ROW_COUNT; y++) { const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y]; @@ -269,6 +284,7 @@ HidNumpad* hid_numpad_alloc(Hid* bt_hid) { hid_numpad->hid = bt_hid; view_set_context(hid_numpad->view, hid_numpad); view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel)); + view_set_orientation(hid_numpad->view, ViewOrientationVerticalFlip); view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback); view_set_input_callback(hid_numpad->view, hid_numpad_input_callback); From 4ff29e8910959b0320b565f3673161a516a2fda1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 23 Jun 2023 00:46:34 +0100 Subject: [PATCH 237/370] Revert "Preload extmainapps" This reverts commit fc56e390759238bccafee44815d32c9cb4ba5ea5. --- applications/services/applications.h | 13 +++-- applications/services/loader/loader.c | 12 ----- applications/services/loader/loader_preload.c | 54 ------------------- applications/services/loader/loader_preload.h | 6 --- firmware/targets/f7/api_symbols.csv | 1 - lib/flipper_application/flipper_application.c | 11 +--- lib/flipper_application/flipper_application.h | 9 +--- scripts/fbt/appmanifest.py | 2 +- 8 files changed, 10 insertions(+), 98 deletions(-) delete mode 100644 applications/services/loader/loader_preload.c delete mode 100644 applications/services/loader/loader_preload.h diff --git a/applications/services/applications.h b/applications/services/applications.h index 45e0be2e7..85f736742 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -15,7 +15,6 @@ typedef struct { const size_t stack_size; const Icon* icon; const FlipperApplicationFlag flags; - void* preload; } FlipperApplication; typedef void (*FlipperOnStartHook)(void); @@ -25,32 +24,32 @@ extern const char* FLIPPER_AUTORUN_APP_NAME; /* Services list * Spawned on startup */ -extern FlipperApplication FLIPPER_SERVICES[]; +extern const FlipperApplication FLIPPER_SERVICES[]; extern const size_t FLIPPER_SERVICES_COUNT; /* Apps list * Spawned by loader */ -extern FlipperApplication FLIPPER_APPS[]; +extern const FlipperApplication FLIPPER_APPS[]; extern const size_t FLIPPER_APPS_COUNT; /* On system start hooks * Called by loader, after OS initialization complete */ -extern FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; +extern const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; /* System apps * Can only be spawned by loader by name */ -extern FlipperApplication FLIPPER_SYSTEM_APPS[]; +extern const FlipperApplication FLIPPER_SYSTEM_APPS[]; extern const size_t FLIPPER_SYSTEM_APPS_COUNT; /* Separate scene app holder * Spawned by loader */ extern const FlipperApplication FLIPPER_SCENE; -extern FlipperApplication FLIPPER_SCENE_APPS[]; +extern const FlipperApplication FLIPPER_SCENE_APPS[]; extern const size_t FLIPPER_SCENE_APPS_COUNT; extern const FlipperApplication FLIPPER_ARCHIVE; @@ -58,5 +57,5 @@ extern const FlipperApplication FLIPPER_ARCHIVE; /* Settings list * Spawned by loader */ -extern FlipperApplication FLIPPER_SETTINGS_APPS[]; +extern const FlipperApplication FLIPPER_SETTINGS_APPS[]; extern const size_t FLIPPER_SETTINGS_APPS_COUNT; diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index ff187e99e..7652f9a89 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,7 +1,6 @@ #include "loader.h" #include "loader_i.h" #include "loader_menu.h" -#include "loader_preload.h" #include #include #include @@ -150,13 +149,6 @@ static Loader* loader_alloc() { if(furi_hal_is_normal_boot()) { Storage* storage = furi_record_open(RECORD_STORAGE); - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - if(FLIPPER_APPS[i].app != NULL || FLIPPER_APPS[i].stack_size != 1) continue; - if(storage_common_exists(storage, FLIPPER_APPS[i].appid)) { - void* preload = loader_preload(storage, FLIPPER_APPS[i].appid); - FLIPPER_APPS[i].preload = preload; - } - } FuriString* path = furi_string_alloc(); FuriString* name = furi_string_alloc(); Stream* stream = file_stream_alloc(storage); @@ -224,10 +216,6 @@ static void FURI_LOG_I(TAG, "Starting %s", app->name); if(app->app == NULL) { - if(app->preload != NULL) { - loader_preload_start(app->preload, app->appid); - return; - } args = app->appid; app = loader_find_application_by_name_in_list( FAP_LOADER_APP_NAME, FLIPPER_APPS, FLIPPER_APPS_COUNT); diff --git a/applications/services/loader/loader_preload.c b/applications/services/loader/loader_preload.c deleted file mode 100644 index 21e3770b7..000000000 --- a/applications/services/loader/loader_preload.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include - -#define TAG "Preload" - -void* loader_preload(Storage* storage, const char* path) { - FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); - size_t start = furi_get_tick(); - - FURI_LOG_I(TAG, "Loading %s", path); - - FlipperApplicationPreloadStatus preload_res = flipper_application_preload(app, path); - if(preload_res != FlipperApplicationPreloadStatusSuccess) { - return NULL; - } - - FURI_LOG_I(TAG, "Mapping"); - FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(app); - if(load_status != FlipperApplicationLoadStatusSuccess) { - const char* err_msg = flipper_application_load_status_to_string(load_status); - FURI_LOG_E(TAG, "Failed to map to memory %s: %s", path, err_msg); - return NULL; - } - - FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); - return app; -} - -void loader_preload_start(void* _app, const char* path) { - FlipperApplication* app = _app; - FURI_LOG_I(TAG, "Starting app"); - - FuriThread* thread = flipper_application_spawn(app, NULL); - - /* This flag is set by the debugger - to break on app start */ - if(furi_hal_debug_is_gdb_session_active()) { - FURI_LOG_W(TAG, "Triggering BP for debugger"); - /* After hitting this, you can set breakpoints in your .fap's code - * Note that you have to toggle breakpoints that were set before */ - __asm volatile("bkpt 0"); - } - - FuriString* app_name = furi_string_alloc(); - path_extract_filename_no_ext(path, app_name); - furi_thread_set_appid(thread, furi_string_get_cstr(app_name)); - furi_string_free(app_name); - - furi_thread_start(thread); - furi_thread_join(thread); - - flipper_application_despawn(app); -} diff --git a/applications/services/loader/loader_preload.h b/applications/services/loader/loader_preload.h deleted file mode 100644 index 1257c9890..000000000 --- a/applications/services/loader/loader_preload.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -void* loader_preload(Storage* storage, const char* path); -void loader_preload_start(void* app, const char* path); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b4b48703c..96f82dd31 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -953,7 +953,6 @@ Function,-,finitel,int,long double Function,-,fiprintf,int,"FILE*, const char*, ..." Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" -Function,+,flipper_application_despawn,void,FlipperApplication* Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 9272b9093..1b4f56814 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -236,15 +236,6 @@ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { return app->thread; } -void flipper_application_despawn(FlipperApplication* app) { - furi_check(app->thread != NULL); - furi_check(!flipper_application_is_plugin(app)); - - furi_thread_join(app->thread); - furi_thread_free(app->thread); - app->thread = NULL; -} - static const char* preload_status_strings[] = { [FlipperApplicationPreloadStatusSuccess] = "Success", [FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error", @@ -298,4 +289,4 @@ const FlipperAppPluginDescriptor* lib_descriptor->ep_api_version); return lib_descriptor; -} +} \ No newline at end of file diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h index 42665080b..519cc3971 100644 --- a/lib/flipper_application/flipper_application.h +++ b/lib/flipper_application/flipper_application.h @@ -107,19 +107,14 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio /** * @brief Create application thread at entry point address, using app name and - * stack size from metadata. Returned thread isn't started yet. + * stack size from metadata. Returned thread isn't started yet. + * Can be only called once for application instance. * @param app Applicaiton pointer * @param args Object to pass to app's entry point * @return Created thread */ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); -/** - * @brief Cleanup application in order to re-spawn later. - * @param app Applicaiton pointer - */ -void flipper_application_despawn(FlipperApplication* app); - /** * @brief Check if application is a plugin (not a runnable standalone app) * @param app Application pointer diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index bba748bfa..a09b4b2e9 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -403,7 +403,7 @@ class ApplicationsCGenerator: map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype)) ) entry_type, entry_block = self.APP_TYPE_MAP[apptype] - contents.append(f"{entry_type} {entry_block}[] = {{") + contents.append(f"const {entry_type} {entry_block}[] = {{") apps = self.buildset.get_apps_of_type(apptype) if apptype is FlipperAppType.APP: apps += self.buildset.get_apps_of_type(FlipperAppType.EXTMAINAPP) From c8ac5cc7b42beafc95d76c01366319a08425c245 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 23 Jun 2023 00:47:19 +0100 Subject: [PATCH 238/370] Revert "Option to preload extmainapps or not" This reverts commit 33b8519202ec3cf7932c423954bb62473cdb152b. --- applications/main/gpio/application.fam | 1 - applications/main/ibutton/application.fam | 1 - scripts/fbt/appmanifest.py | 3 +-- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index de77c7edd..222d1ca28 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -2,7 +2,6 @@ App( appid="gpio", name="GPIO", apptype=FlipperAppType.EXTMAINAPP, - preload=True, entry_point="gpio_app", cdefines=["APP_GPIO"], requires=["gui"], diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 511b145d5..f29fd7772 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -2,7 +2,6 @@ App( appid="ibutton", name="iButton", apptype=FlipperAppType.EXTMAINAPP, - preload=True, targets=["f7"], entry_point="ibutton_app", cdefines=["APP_IBUTTON"], diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index a09b4b2e9..531df8bc1 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -45,7 +45,6 @@ class FlipperApplication: appid: str apptype: FlipperAppType - preload: Optional[bool] = False name: Optional[str] = "" entry_point: Optional[str] = None flags: List[str] = field(default_factory=lambda: ["Default"]) @@ -381,7 +380,7 @@ class ApplicationsCGenerator: {{.app = NULL, .name = "{app.name}", .appid = "/ext/apps/.Main/{app.appid}.fap", - .stack_size = {1 if app.preload else 0}, + .stack_size = 0, .icon = {f"&{app.icon}" if app.icon else "NULL"}, .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)}}}""" return f""" From d3e73c26860ec69d3399086e1ee2b1cb261af3fc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Fri, 23 Jun 2023 01:17:18 +0100 Subject: [PATCH 239/370] fap fap fap --- applications/main/bad_kb/application.fam | 2 +- applications/main/infrared/application.fam | 2 +- applications/main/lfrfid/application.fam | 2 +- firmware/targets/f7/api_symbols.csv | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/applications/main/bad_kb/application.fam b/applications/main/bad_kb/application.fam index 09531da81..0c923ba5b 100644 --- a/applications/main/bad_kb/application.fam +++ b/applications/main/bad_kb/application.fam @@ -1,7 +1,7 @@ App( appid="bad_kb", name="Bad KB", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.EXTMAINAPP, entry_point="bad_kb_app", cdefines=["APP_BAD_KB"], requires=[ diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index e5483e9ff..8fc26d3d9 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -1,7 +1,7 @@ App( appid="infrared", name="Infrared", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.EXTMAINAPP, entry_point="infrared_app", targets=["f7"], cdefines=["APP_INFRARED"], diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index e84ed2a31..d75b5431f 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -1,7 +1,7 @@ App( appid="lfrfid", name="RFID", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.EXTMAINAPP, targets=["f7"], entry_point="lfrfid_app", cdefines=["APP_LF_RFID"], diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 96f82dd31..6179ab803 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1089,7 +1089,7 @@ Function,-,furi_hal_bt_get_hardfault_info,const FuriHalBtHardfaultInfo*, Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" Function,+,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile Function,+,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile -Function,-,furi_hal_bt_get_profile_pairing_method,GapPairing,FuriHalBtProfile +Function,+,furi_hal_bt_get_profile_pairing_method,GapPairing,FuriHalBtProfile Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, Function,+,furi_hal_bt_get_rssi,float, Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, @@ -2940,7 +2940,7 @@ Function,+,submenu_set_header,void,"Submenu*, const char*" Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" Function,-,system,int,const char* Function,+,t5577_write,void,LFRFIDT5577* -Function,-,t5577_write_with_pass,void,"LFRFIDT5577*, uint32_t" +Function,+,t5577_write_with_pass,void,"LFRFIDT5577*, uint32_t" Function,-,tan,double,double Function,-,tanf,float,float Function,-,tanh,double,double From 857fa406d8b3e377ef3450bf6c0cbb502a34c687 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Fri, 23 Jun 2023 03:18:33 +0200 Subject: [PATCH 240/370] Step into the light 1.15 stack. Your time has cometh --- fbt_options.py | 2 +- firmware/targets/f7/ble_glue/app_common.h | 1 + firmware/targets/f7/ble_glue/app_conf.h | 2 + firmware/targets/f7/ble_glue/app_debug.c | 3 +- firmware/targets/f7/ble_glue/ble_app.c | 10 +- firmware/targets/f7/ble_glue/ble_const.h | 4 + firmware/targets/f7/ble_glue/compiler.h | 10 +- firmware/targets/f7/ble_glue/gap.c | 49 ++-- lib/stm32wb_copro | 2 +- scripts/ob.data | 4 +- stack_1.15.0_upgrade.patch | 329 ---------------------- 11 files changed, 53 insertions(+), 363 deletions(-) delete mode 100644 stack_1.15.0_upgrade.patch diff --git a/fbt_options.py b/fbt_options.py index bb4ee592c..a1b6032b2 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -25,7 +25,7 @@ DIST_SUFFIX = f"XFW-0048_{datetime.datetime.today().strftime('%d%m%Y')}" COPRO_OB_DATA = "scripts/ob.data" # Must match lib/stm32wb_copro version -COPRO_CUBE_VERSION = "1.13.3" +COPRO_CUBE_VERSION = "1.15.0" COPRO_CUBE_DIR = "lib/stm32wb_copro" diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h index 214c85acd..8eaf23085 100644 --- a/firmware/targets/f7/ble_glue/app_common.h +++ b/firmware/targets/f7/ble_glue/app_common.h @@ -33,6 +33,7 @@ extern "C" { #include #include +#include #include "app_conf.h" diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h index aaa755a36..ee5115cfe 100644 --- a/firmware/targets/f7/ble_glue/app_conf.h +++ b/firmware/targets/f7/ble_glue/app_conf.h @@ -8,6 +8,8 @@ #define CFG_TX_POWER (0x19) /* +0dBm */ +#define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR + /** * Define Advertising parameters */ diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c index 78e789ac3..b443bee21 100644 --- a/firmware/targets/f7/ble_glue/app_debug.c +++ b/firmware/targets/f7/ble_glue/app_debug.c @@ -33,7 +33,8 @@ PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) -static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; +static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = + {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}, 0, 0, 0, 0, 0}; /** * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c index 4fc4d521b..37d8f7cd0 100644 --- a/firmware/targets/f7/ble_glue/ble_app.c +++ b/firmware/targets/f7/ble_glue/ble_app.c @@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; _Static_assert( - sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 49, - "Ble stack config structure size mismatch"); + sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, + "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); typedef struct { FuriMutex* hci_mtx; @@ -88,6 +88,12 @@ bool ble_app_init() { .min_tx_power = 0, .max_tx_power = 0, .rx_model_config = 1, + /* New stack (13.3->15.0) */ + .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set + .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB + .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB + .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) }}; status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); if(status) { diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h index 0e4c8b398..85f734b62 100644 --- a/firmware/targets/f7/ble_glue/ble_const.h +++ b/firmware/targets/f7/ble_glue/ble_const.h @@ -23,6 +23,7 @@ #include #include #include "osal.h" +#include "compiler.h" /* Default BLE variant */ #ifndef BASIC_FEATURES @@ -34,6 +35,9 @@ #ifndef LL_ONLY #define LL_ONLY 0 #endif +#ifndef LL_ONLY_BASIC +#define LL_ONLY_BASIC 0 +#endif #ifndef BEACON_ONLY #define BEACON_ONLY 0 #endif diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h index 1c3962819..98a93d712 100644 --- a/firmware/targets/f7/ble_glue/compiler.h +++ b/firmware/targets/f7/ble_glue/compiler.h @@ -5,7 +5,7 @@ ***************************************************************************** * @attention * - * Copyright (c) 2018-2022 STMicroelectronics. + * Copyright (c) 2018-2023 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file @@ -18,6 +18,14 @@ #ifndef COMPILER_H__ #define COMPILER_H__ +#ifndef __PACKED_STRUCT +#define __PACKED_STRUCT PACKED(struct) +#endif + +#ifndef __PACKED_UNION +#define __PACKED_UNION PACKED(union) +#endif + /** * @brief This is the section dedicated to IAR toolchain */ diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index afdbe3e55..94c556e54 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -1,5 +1,6 @@ #include "gap.h" +#include "app_common.h" #include #include @@ -100,7 +101,7 @@ static void gap_verify_connection_parameters(Gap* gap) { SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { hci_event_pckt* event_pckt; evt_le_meta_event* meta_evt; - evt_blue_aci* blue_evt; + evt_blecore_aci* blue_evt; hci_le_phy_update_complete_event_rp0* evt_le_phy_update_complete; uint8_t tx_phy; uint8_t rx_phy; @@ -112,7 +113,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { furi_mutex_acquire(gap->state_mutex, FuriWaitForever); } switch(event_pckt->evt) { - case EVT_DISCONN_COMPLETE: { + case HCI_DISCONNECTION_COMPLETE_EVT_CODE: { hci_disconnection_complete_event_rp0* disconnection_complete_event = (hci_disconnection_complete_event_rp0*)event_pckt->data; if(disconnection_complete_event->Connection_Handle == gap->service.connection_handle) { @@ -131,10 +132,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_LE_META_EVENT: + case HCI_LE_META_EVT_CODE: meta_evt = (evt_le_meta_event*)event_pckt->data; switch(meta_evt->subevent) { - case EVT_LE_CONN_UPDATE_COMPLETE: { + case HCI_LE_CONNECTION_UPDATE_COMPLETE_SUBEVT_CODE: { hci_le_connection_update_complete_event_rp0* event = (hci_le_connection_update_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -148,7 +149,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case EVT_LE_PHY_UPDATE_COMPLETE: + case HCI_LE_PHY_UPDATE_COMPLETE_SUBEVT_CODE: evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; if(evt_le_phy_update_complete->Status) { FURI_LOG_E( @@ -164,7 +165,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_LE_CONN_COMPLETE: { + case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: { hci_le_connection_complete_event_rp0* event = (hci_le_connection_complete_event_rp0*)meta_evt->data; gap->connection_params.conn_interval = event->Conn_Interval; @@ -191,16 +192,16 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_VENDOR: - blue_evt = (evt_blue_aci*)event_pckt->data; + case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: + blue_evt = (evt_blecore_aci*)event_pckt->data; switch(blue_evt->ecode) { aci_gap_pairing_complete_event_rp0* pairing_complete; - case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: + case ACI_GAP_LIMITED_DISCOVERABLE_VSEVT_CODE: FURI_LOG_I(TAG, "Limited discoverable event"); break; - case EVT_BLUE_GAP_PASS_KEY_REQUEST: { + case ACI_GAP_PASS_KEY_REQ_VSEVT_CODE: { // Generate random PIN code uint32_t pin = rand() % 999999; //-V1064 aci_gap_pass_key_resp(gap->service.connection_handle, pin); @@ -213,7 +214,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_BLUE_ATT_EXCHANGE_MTU_RESP: { + case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: { aci_att_exchange_mtu_resp_event_rp0* pr = (void*)blue_evt->data; FURI_LOG_I(TAG, "Rx MTU size: %d", pr->Server_RX_MTU); // Set maximum packet size given header size is 3 bytes @@ -222,32 +223,28 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->on_event_cb(event, gap->context); } break; - case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: + case ACI_GAP_AUTHORIZATION_REQ_VSEVT_CODE: FURI_LOG_D(TAG, "Authorization request event"); break; - case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: + case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE: FURI_LOG_D(TAG, "Slave security initiated"); break; - case EVT_BLUE_GAP_BOND_LOST: + case ACI_GAP_BOND_LOST_VSEVT_CODE: FURI_LOG_D(TAG, "Bond lost event. Start rebonding"); aci_gap_allow_rebond(gap->service.connection_handle); break; - case EVT_BLUE_GAP_DEVICE_FOUND: - FURI_LOG_D(TAG, "Device found event"); - break; - - case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: + case ACI_GAP_ADDR_NOT_RESOLVED_VSEVT_CODE: FURI_LOG_D(TAG, "Address not resolved event"); break; - case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: + case ACI_GAP_KEYPRESS_NOTIFICATION_VSEVT_CODE: FURI_LOG_D(TAG, "Key press notification event"); break; - case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: { + case ACI_GAP_NUMERIC_COMPARISON_VALUE_VSEVT_CODE: { uint32_t pin = ((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value; FURI_LOG_I(TAG, "Verify numeric comparison: %06lu", pin); @@ -257,7 +254,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { break; } - case EVT_BLUE_GAP_PAIRING_CMPLT: + case ACI_GAP_PAIRING_COMPLETE_VSEVT_CODE: pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; if(pairing_complete->Status) { FURI_LOG_E( @@ -275,11 +272,11 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { } break; - case EVT_BLUE_GAP_PROCEDURE_COMPLETE: + case ACI_L2CAP_CONNECTION_UPDATE_RESP_VSEVT_CODE: FURI_LOG_D(TAG, "Procedure complete event"); break; - case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: { + case ACI_L2CAP_CONNECTION_UPDATE_REQ_VSEVT_CODE: { uint16_t result = ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result; if(result == 0) { @@ -403,7 +400,7 @@ static void gap_init_svc(Gap* gap) { CFG_ENCRYPTION_KEY_SIZE_MAX, conf_used_fixed_pin, // 0x0 for no pin 0, - PUBLIC_ADDR); + CFG_IDENTITY_ADDRESS); // Configure whitelist aci_gap_configure_whitelist(); } @@ -438,7 +435,7 @@ static void gap_advertise_start(GapState new_state) { ADV_IND, min_interval, max_interval, - PUBLIC_ADDR, + CFG_IDENTITY_ADDRESS, 0, strlen(gap->service.adv_name), (uint8_t*)gap->service.adv_name, diff --git a/lib/stm32wb_copro b/lib/stm32wb_copro index d685979c2..6c9c54f05 160000 --- a/lib/stm32wb_copro +++ b/lib/stm32wb_copro @@ -1 +1 @@ -Subproject commit d685979c282c9f38d561dd1ea8e6fdbd735d7362 +Subproject commit 6c9c54f05669b2c4d436df58bb691d3b0d7c86df diff --git a/scripts/ob.data b/scripts/ob.data index 5276a5103..605faccbf 100644 --- a/scripts/ob.data +++ b/scripts/ob.data @@ -14,7 +14,7 @@ IWDGSTOP:0x1:rw IWDGSW:0x1:rw IPCCDBA:0x0:rw ESE:0x1:r -SFSA:0xD7:r +SFSA:0xD5:r FSD:0x0:r DDS:0x1:r C2OPT:0x1:r @@ -22,7 +22,7 @@ NBRSD:0x0:r SNBRSA:0xD:r BRSD:0x0:r SBRSA:0x12:r -SBRV:0x35C00:r +SBRV:0x35400:r PCROP1A_STRT:0x1FF:r PCROP1A_END:0x0:r PCROP_RDP:0x1:rw diff --git a/stack_1.15.0_upgrade.patch b/stack_1.15.0_upgrade.patch deleted file mode 100644 index 8416a9915..000000000 --- a/stack_1.15.0_upgrade.patch +++ /dev/null @@ -1,329 +0,0 @@ -diff --git a/fbt_options.py b/fbt_options.py -index bb4ee592c0..a1b6032b29 100644 ---- a/fbt_options.py -+++ b/fbt_options.py -@@ -25,7 +25,7 @@ DIST_SUFFIX = f"XFW-0048_{datetime.datetime.today().strftime('%d%m%Y')}" - COPRO_OB_DATA = "scripts/ob.data" - - # Must match lib/stm32wb_copro version --COPRO_CUBE_VERSION = "1.13.3" -+COPRO_CUBE_VERSION = "1.15.0" - - COPRO_CUBE_DIR = "lib/stm32wb_copro" - -diff --git a/firmware/targets/f7/ble_glue/app_common.h b/firmware/targets/f7/ble_glue/app_common.h -index 214c85acd2..8eaf230859 100644 ---- a/firmware/targets/f7/ble_glue/app_common.h -+++ b/firmware/targets/f7/ble_glue/app_common.h -@@ -33,6 +33,7 @@ extern "C" { - #include - - #include -+#include - - #include "app_conf.h" - -diff --git a/firmware/targets/f7/ble_glue/app_conf.h b/firmware/targets/f7/ble_glue/app_conf.h -index aaa755a362..ee5115cfed 100644 ---- a/firmware/targets/f7/ble_glue/app_conf.h -+++ b/firmware/targets/f7/ble_glue/app_conf.h -@@ -8,6 +8,8 @@ - - #define CFG_TX_POWER (0x19) /* +0dBm */ - -+#define CFG_IDENTITY_ADDRESS GAP_PUBLIC_ADDR -+ - /** - * Define Advertising parameters - */ -diff --git a/firmware/targets/f7/ble_glue/app_debug.c b/firmware/targets/f7/ble_glue/app_debug.c -index 78e789ac30..b443bee21f 100644 ---- a/firmware/targets/f7/ble_glue/app_debug.c -+++ b/firmware/targets/f7/ble_glue/app_debug.c -@@ -33,7 +33,8 @@ PLACE_IN_SECTION("MB_MEM2") - ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0}; - PLACE_IN_SECTION("MB_MEM2") - ALIGN(4) --static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}}; -+static SHCI_C2_DEBUG_GeneralConfig_t APPD_GeneralConfig = -+ {BLE_DTB_CFG, SYS_DBG_CFG1, {0, 0}, 0, 0, 0, 0, 0}; - - /** - * THE DEBUG ON GPIO FOR CPU2 IS INTENDED TO BE USED ONLY ON REQUEST FROM ST SUPPORT -diff --git a/firmware/targets/f7/ble_glue/ble_app.c b/firmware/targets/f7/ble_glue/ble_app.c -index 4fc4d521be..37d8f7cd04 100644 ---- a/firmware/targets/f7/ble_glue/ble_app.c -+++ b/firmware/targets/f7/ble_glue/ble_app.c -@@ -18,8 +18,8 @@ PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer; - PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE]; - - _Static_assert( -- sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 49, -- "Ble stack config structure size mismatch"); -+ sizeof(SHCI_C2_Ble_Init_Cmd_Packet_t) == 57, -+ "Ble stack config structure size mismatch (check new config options - last updated for v.1.15.0)"); - - typedef struct { - FuriMutex* hci_mtx; -@@ -88,6 +88,12 @@ bool ble_app_init() { - .min_tx_power = 0, - .max_tx_power = 0, - .rx_model_config = 1, -+ /* New stack (13.3->15.0) */ -+ .max_adv_set_nbr = 1, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set -+ .max_adv_data_len = 31, // Only used if SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV is set -+ .tx_path_compens = 0, // RF TX Path Compensation, * 0.1 dB -+ .rx_path_compens = 0, // RF RX Path Compensation, * 0.1 dB -+ .ble_core_version = 11, // BLE Core Version: 11(5.2), 12(5.3) - }}; - status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); - if(status) { -diff --git a/firmware/targets/f7/ble_glue/ble_const.h b/firmware/targets/f7/ble_glue/ble_const.h -index 0e4c8b398d..85f734b62c 100644 ---- a/firmware/targets/f7/ble_glue/ble_const.h -+++ b/firmware/targets/f7/ble_glue/ble_const.h -@@ -23,6 +23,7 @@ - #include - #include - #include "osal.h" -+#include "compiler.h" - - /* Default BLE variant */ - #ifndef BASIC_FEATURES -@@ -34,6 +35,9 @@ - #ifndef LL_ONLY - #define LL_ONLY 0 - #endif -+#ifndef LL_ONLY_BASIC -+#define LL_ONLY_BASIC 0 -+#endif - #ifndef BEACON_ONLY - #define BEACON_ONLY 0 - #endif -diff --git a/firmware/targets/f7/ble_glue/compiler.h b/firmware/targets/f7/ble_glue/compiler.h -index 1c39628197..98a93d7126 100644 ---- a/firmware/targets/f7/ble_glue/compiler.h -+++ b/firmware/targets/f7/ble_glue/compiler.h -@@ -5,7 +5,7 @@ - ***************************************************************************** - * @attention - * -- * Copyright (c) 2018-2022 STMicroelectronics. -+ * Copyright (c) 2018-2023 STMicroelectronics. - * All rights reserved. - * - * This software is licensed under terms that can be found in the LICENSE file -@@ -18,6 +18,14 @@ - #ifndef COMPILER_H__ - #define COMPILER_H__ - -+#ifndef __PACKED_STRUCT -+#define __PACKED_STRUCT PACKED(struct) -+#endif -+ -+#ifndef __PACKED_UNION -+#define __PACKED_UNION PACKED(union) -+#endif -+ - /** - * @brief This is the section dedicated to IAR toolchain - */ -diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c -index 9dfb5af89c..641eccc4ea 100644 ---- a/firmware/targets/f7/ble_glue/gap.c -+++ b/firmware/targets/f7/ble_glue/gap.c -@@ -1,5 +1,6 @@ - #include "gap.h" - -+#include "app_common.h" - #include - - #include -@@ -100,7 +101,7 @@ static void gap_verify_connection_parameters(Gap* gap) { - SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - hci_event_pckt* event_pckt; - evt_le_meta_event* meta_evt; -- evt_blue_aci* blue_evt; -+ evt_blecore_aci* blue_evt; - hci_le_phy_update_complete_event_rp0* evt_le_phy_update_complete; - uint8_t tx_phy; - uint8_t rx_phy; -@@ -112,7 +113,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - furi_mutex_acquire(gap->state_mutex, FuriWaitForever); - } - switch(event_pckt->evt) { -- case EVT_DISCONN_COMPLETE: { -+ case HCI_DISCONNECTION_COMPLETE_EVT_CODE: { - hci_disconnection_complete_event_rp0* disconnection_complete_event = - (hci_disconnection_complete_event_rp0*)event_pckt->data; - if(disconnection_complete_event->Connection_Handle == gap->service.connection_handle) { -@@ -131,10 +132,10 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - gap->on_event_cb(event, gap->context); - } break; - -- case EVT_LE_META_EVENT: -+ case HCI_LE_META_EVT_CODE: - meta_evt = (evt_le_meta_event*)event_pckt->data; - switch(meta_evt->subevent) { -- case EVT_LE_CONN_UPDATE_COMPLETE: { -+ case HCI_LE_CONNECTION_UPDATE_COMPLETE_SUBEVT_CODE: { - hci_le_connection_update_complete_event_rp0* event = - (hci_le_connection_update_complete_event_rp0*)meta_evt->data; - gap->connection_params.conn_interval = event->Conn_Interval; -@@ -148,7 +149,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - break; - } - -- case EVT_LE_PHY_UPDATE_COMPLETE: -+ case HCI_LE_PHY_UPDATE_COMPLETE_SUBEVT_CODE: - evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; - if(evt_le_phy_update_complete->Status) { - FURI_LOG_E( -@@ -164,7 +165,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - } - break; - -- case EVT_LE_CONN_COMPLETE: { -+ case HCI_LE_CONNECTION_COMPLETE_SUBEVT_CODE: { - hci_le_connection_complete_event_rp0* event = - (hci_le_connection_complete_event_rp0*)meta_evt->data; - gap->connection_params.conn_interval = event->Conn_Interval; -@@ -191,16 +192,16 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - } - break; - -- case EVT_VENDOR: -- blue_evt = (evt_blue_aci*)event_pckt->data; -+ case HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE: -+ blue_evt = (evt_blecore_aci*)event_pckt->data; - switch(blue_evt->ecode) { - aci_gap_pairing_complete_event_rp0* pairing_complete; - -- case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: -+ case ACI_GAP_LIMITED_DISCOVERABLE_VSEVT_CODE: - FURI_LOG_I(TAG, "Limited discoverable event"); - break; - -- case EVT_BLUE_GAP_PASS_KEY_REQUEST: { -+ case ACI_GAP_PASS_KEY_REQ_VSEVT_CODE: { - // Generate random PIN code - uint32_t pin = rand() % 999999; //-V1064 - aci_gap_pass_key_resp(gap->service.connection_handle, pin); -@@ -213,7 +214,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - gap->on_event_cb(event, gap->context); - } break; - -- case EVT_BLUE_ATT_EXCHANGE_MTU_RESP: { -+ case ACI_ATT_EXCHANGE_MTU_RESP_VSEVT_CODE: { - aci_att_exchange_mtu_resp_event_rp0* pr = (void*)blue_evt->data; - FURI_LOG_I(TAG, "Rx MTU size: %d", pr->Server_RX_MTU); - // Set maximum packet size given header size is 3 bytes -@@ -222,32 +223,28 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - gap->on_event_cb(event, gap->context); - } break; - -- case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: -+ case ACI_GAP_AUTHORIZATION_REQ_VSEVT_CODE: - FURI_LOG_D(TAG, "Authorization request event"); - break; - -- case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: -+ case ACI_GAP_SLAVE_SECURITY_INITIATED_VSEVT_CODE: - FURI_LOG_D(TAG, "Slave security initiated"); - break; - -- case EVT_BLUE_GAP_BOND_LOST: -+ case ACI_GAP_BOND_LOST_VSEVT_CODE: - FURI_LOG_D(TAG, "Bond lost event. Start rebonding"); - aci_gap_allow_rebond(gap->service.connection_handle); - break; - -- case EVT_BLUE_GAP_DEVICE_FOUND: -- FURI_LOG_D(TAG, "Device found event"); -- break; -- -- case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: -+ case ACI_GAP_ADDR_NOT_RESOLVED_VSEVT_CODE: - FURI_LOG_D(TAG, "Address not resolved event"); - break; - -- case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: -+ case ACI_GAP_KEYPRESS_NOTIFICATION_VSEVT_CODE: - FURI_LOG_D(TAG, "Key press notification event"); - break; - -- case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: { -+ case ACI_GAP_NUMERIC_COMPARISON_VALUE_VSEVT_CODE: { - uint32_t pin = - ((aci_gap_numeric_comparison_value_event_rp0*)(blue_evt->data))->Numeric_Value; - FURI_LOG_I(TAG, "Verify numeric comparison: %06lu", pin); -@@ -257,7 +254,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - break; - } - -- case EVT_BLUE_GAP_PAIRING_CMPLT: -+ case ACI_GAP_PAIRING_COMPLETE_VSEVT_CODE: - pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; - if(pairing_complete->Status) { - FURI_LOG_E( -@@ -275,11 +272,11 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { - } - break; - -- case EVT_BLUE_GAP_PROCEDURE_COMPLETE: -+ case ACI_L2CAP_CONNECTION_UPDATE_RESP_VSEVT_CODE: - FURI_LOG_D(TAG, "Procedure complete event"); - break; - -- case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: { -+ case ACI_L2CAP_CONNECTION_UPDATE_REQ_VSEVT_CODE: { - uint16_t result = - ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result; - if(result == 0) { -@@ -402,7 +399,7 @@ static void gap_init_svc(Gap* gap) { - CFG_ENCRYPTION_KEY_SIZE_MAX, - conf_used_fixed_pin, // 0x0 for no pin - 0, -- PUBLIC_ADDR); -+ CFG_IDENTITY_ADDRESS); - // Configure whitelist - aci_gap_configure_whitelist(); - } -@@ -437,7 +434,7 @@ static void gap_advertise_start(GapState new_state) { - ADV_IND, - min_interval, - max_interval, -- PUBLIC_ADDR, -+ CFG_IDENTITY_ADDRESS, - 0, - strlen(gap->service.adv_name), - (uint8_t*)gap->service.adv_name, -diff --git a/lib/stm32wb_copro b/lib/stm32wb_copro -index d685979c28..6c9c54f056 160000 ---- a/lib/stm32wb_copro -+++ b/lib/stm32wb_copro -@@ -1 +1 @@ --Subproject commit d685979c282c9f38d561dd1ea8e6fdbd735d7362 -+Subproject commit 6c9c54f05669b2c4d436df58bb691d3b0d7c86df -diff --git a/scripts/ob.data b/scripts/ob.data -index 5276a5103b..605faccbfc 100644 ---- a/scripts/ob.data -+++ b/scripts/ob.data -@@ -14,7 +14,7 @@ IWDGSTOP:0x1:rw - IWDGSW:0x1:rw - IPCCDBA:0x0:rw - ESE:0x1:r --SFSA:0xD7:r -+SFSA:0xD5:r - FSD:0x0:r - DDS:0x1:r - C2OPT:0x1:r -@@ -22,7 +22,7 @@ NBRSD:0x0:r - SNBRSA:0xD:r - BRSD:0x0:r - SBRSA:0x12:r --SBRV:0x35C00:r -+SBRV:0x35400:r - PCROP1A_STRT:0x1FF:r - PCROP1A_END:0x0:r - PCROP_RDP:0x1:rw From 243649f33f261aac49620985885adc8c37f1a6af Mon Sep 17 00:00:00 2001 From: SG Date: Fri, 23 Jun 2023 13:44:55 +0300 Subject: [PATCH 241/370] Toolchain: 23 version --- scripts/toolchain/fbtenv.cmd | 2 +- scripts/toolchain/fbtenv.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 9d45b7e9d..4ae04e2a2 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=21" +set "FLIPPER_TOOLCHAIN_VERSION=22" if ["%FBT_TOOLCHAIN_PATH%"] == [""] ( set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 143dce74b..e5548f488 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,7 +4,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"22"}"; if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then FBT_TOOLCHAIN_PATH_WAS_SET=0; From 355c3c2f9ce32f712fb268c87e869b3d4a8f5b79 Mon Sep 17 00:00:00 2001 From: SG Date: Fri, 23 Jun 2023 14:16:28 +0300 Subject: [PATCH 242/370] Elf File: remove log messsages --- lib/flipper_application/elf/elf_file.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index fcb5c1d7d..fc9dd06ba 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -559,7 +559,6 @@ static SectionType elf_preload_section( name = name + strlen(".fast.rel"); ELFSection* section_p = elf_file_get_or_put_section(elf, name); section_p->fast_rel = malloc(sizeof(ELFSection)); - FURI_LOG_I(TAG, "Fast rel section size: %ld", section_header->sh_size); if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) { FURI_LOG_E(TAG, "Error loading section '%s'", name); From 761a14e6e2ffd417a8afe2eb8dd6f24dd812f9b5 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Fri, 23 Jun 2023 15:01:40 +0300 Subject: [PATCH 243/370] [FL-2837][FL-3270] Loader refaptoring: second encounter (#2779) * Core: rename internal FlipperApplication to FlipperInternalApplication * FAP Loader: move load_name_and_icon to flipper_application library * Loader menu: rework api * View holder: move to gui service * Loader: simple "loading" worker * Loader: applications dialog * Loader: fapping * Update f18 api * Apps: remove fap_loader * Libs, flipper application: store args, rename thread allocation * Loader: error handling * Apps: use loader error handling * Loader: documentation * FBT: accomodate loader * Loader: do not raise gui error if loader is locked * Archive: accomodate loader * Loader: fix loading message * Flipper: drop some old dolphin legacy * Loader: generalize error construction Co-authored-by: Aleksandr Kutuzov --- applications/ReadMe.md | 1 - applications/main/application.fam | 1 - .../main/archive/helpers/archive_browser.c | 4 +- .../archive/scenes/archive_scene_browser.c | 59 ++-- applications/main/fap_loader/application.fam | 15 - applications/main/fap_loader/fap_loader_app.c | 216 ------------ applications/main/fap_loader/fap_loader_app.h | 27 -- applications/services/applications.h | 31 +- applications/services/desktop/desktop.c | 1 + .../desktop/scenes/desktop_scene_main.c | 46 +-- applications/services/dialogs/dialogs_i.h | 2 +- .../services/{dialogs => gui}/view_holder.c | 0 .../services/{dialogs => gui}/view_holder.h | 0 applications/services/loader/loader.c | 311 ++++++++++++++---- applications/services/loader/loader.h | 52 ++- .../services/loader/loader_applications.c | 146 ++++++++ .../services/loader/loader_applications.h | 16 + applications/services/loader/loader_cli.c | 18 +- applications/services/loader/loader_i.h | 7 +- applications/services/loader/loader_menu.c | 213 ++++++------ applications/services/loader/loader_menu.h | 16 +- applications/services/rpc/rpc_app.c | 2 +- .../scenes/desktop_settings_scene_favorite.c | 3 - .../storage_move_to_sd/storage_move_to_sd.c | 2 +- applications/system/updater/cli/updater_cli.c | 2 +- firmware/targets/f18/api_symbols.csv | 8 +- firmware/targets/f7/api_symbols.csv | 8 +- lib/flipper_application/flipper_application.c | 48 ++- lib/flipper_application/flipper_application.h | 21 +- scripts/distfap.py | 4 +- scripts/fbt/appmanifest.py | 22 +- scripts/runfap.py | 2 +- 32 files changed, 723 insertions(+), 581 deletions(-) delete mode 100644 applications/main/fap_loader/application.fam delete mode 100644 applications/main/fap_loader/fap_loader_app.c delete mode 100644 applications/main/fap_loader/fap_loader_app.h rename applications/services/{dialogs => gui}/view_holder.c (100%) rename applications/services/{dialogs => gui}/view_holder.h (100%) create mode 100644 applications/services/loader/loader_applications.c create mode 100644 applications/services/loader/loader_applications.h diff --git a/applications/ReadMe.md b/applications/ReadMe.md index e50d8e46a..10e54ce22 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -26,7 +26,6 @@ Applications for main Flipper menu. - `archive` - Archive and file manager - `bad_usb` - Bad USB application -- `fap_loader` - External applications loader - `gpio` - GPIO application: includes USART bridge and GPIO control - `ibutton` - iButton application, onewire keys and more - `infrared` - Infrared application, controls your IR devices diff --git a/applications/main/application.fam b/applications/main/application.fam index 5c2c21d37..75d55af93 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -12,7 +12,6 @@ App( "subghz", "bad_usb", "u2f", - "fap_loader", "archive", ], ) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 9a7973cb3..70137d694 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include static void @@ -367,7 +367,7 @@ void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { static bool archive_get_fap_meta(FuriString* file_path, FuriString* fap_name, uint8_t** icon_ptr) { Storage* storage = furi_record_open(RECORD_STORAGE); bool success = false; - if(fap_loader_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) { + if(flipper_application_load_name_and_icon(file_path, storage, icon_ptr, fap_name)) { success = true; } furi_record_close(RECORD_STORAGE); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index c28f91f52..e02f7622a 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -11,17 +11,28 @@ #define SCENE_STATE_DEFAULT (0) #define SCENE_STATE_NEED_REFRESH (1) -static const char* flipper_app_name[] = { - [ArchiveFileTypeIButton] = "iButton", - [ArchiveFileTypeNFC] = "NFC", - [ArchiveFileTypeSubGhz] = "Sub-GHz", - [ArchiveFileTypeLFRFID] = "125 kHz RFID", - [ArchiveFileTypeInfrared] = "Infrared", - [ArchiveFileTypeBadUsb] = "Bad USB", - [ArchiveFileTypeU2f] = "U2F", - [ArchiveFileTypeUpdateManifest] = "UpdaterApp", - [ArchiveFileTypeApplication] = "Applications", -}; +const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) { + switch(file_type) { + case ArchiveFileTypeIButton: + return "iButton"; + case ArchiveFileTypeNFC: + return "NFC"; + case ArchiveFileTypeSubGhz: + return "Sub-GHz"; + case ArchiveFileTypeLFRFID: + return "125 kHz RFID"; + case ArchiveFileTypeInfrared: + return "Infrared"; + case ArchiveFileTypeBadUsb: + return "Bad USB"; + case ArchiveFileTypeU2f: + return "U2F"; + case ArchiveFileTypeUpdateManifest: + return "UpdaterApp"; + default: + return NULL; + } +} static void archive_loader_callback(const void* message, void* context) { furi_assert(message); @@ -39,20 +50,20 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); - LoaderStatus status; - if(selected->is_app) { - char* param = strrchr(furi_string_get_cstr(selected->path), '/'); - if(param != NULL) { - param++; - } - status = loader_start(loader, flipper_app_name[selected->type], param); - } else { - status = loader_start( - loader, flipper_app_name[selected->type], furi_string_get_cstr(selected->path)); - } + const char* app_name = archive_get_flipper_app_name(selected->type); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); + if(app_name) { + if(selected->is_app) { + char* param = strrchr(furi_string_get_cstr(selected->path), '/'); + if(param != NULL) { + param++; + } + loader_start_with_gui_error(loader, app_name, param); + } else { + loader_start_with_gui_error(loader, app_name, furi_string_get_cstr(selected->path)); + } + } else { + loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); } furi_record_close(RECORD_LOADER); diff --git a/applications/main/fap_loader/application.fam b/applications/main/fap_loader/application.fam deleted file mode 100644 index b0e67cd42..000000000 --- a/applications/main/fap_loader/application.fam +++ /dev/null @@ -1,15 +0,0 @@ -App( - appid="fap_loader", - name="Applications", - apptype=FlipperAppType.APP, - entry_point="fap_loader_app", - cdefines=["APP_FAP_LOADER"], - requires=[ - "gui", - "storage", - "loader", - ], - stack_size=int(1.5 * 1024), - icon="A_Plugins_14", - order=90, -) diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c deleted file mode 100644 index 7af5244ae..000000000 --- a/applications/main/fap_loader/fap_loader_app.c +++ /dev/null @@ -1,216 +0,0 @@ -#include "fap_loader_app.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "FapLoader" - -struct FapLoader { - FlipperApplication* app; - Storage* storage; - DialogsApp* dialogs; - Gui* gui; - FuriString* fap_path; - ViewDispatcher* view_dispatcher; - Loading* loading; -}; - -bool fap_loader_load_name_and_icon( - FuriString* path, - Storage* storage, - uint8_t** icon_ptr, - FuriString* item_name) { - FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); - - FlipperApplicationPreloadStatus preload_res = - flipper_application_preload_manifest(app, furi_string_get_cstr(path)); - - bool load_success = false; - - if(preload_res == FlipperApplicationPreloadStatusSuccess) { - const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); - if(manifest->has_icon) { - memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); - } - furi_string_set(item_name, manifest->name); - load_success = true; - } else { - FURI_LOG_E(TAG, "FAP Loader failed to preload %s", furi_string_get_cstr(path)); - load_success = false; - } - - flipper_application_free(app); - return load_success; -} - -static bool fap_loader_item_callback( - FuriString* path, - void* context, - uint8_t** icon_ptr, - FuriString* item_name) { - FapLoader* fap_loader = context; - furi_assert(fap_loader); - return fap_loader_load_name_and_icon(path, fap_loader->storage, icon_ptr, item_name); -} - -static bool fap_loader_run_selected_app(FapLoader* loader) { - furi_assert(loader); - - FuriString* error_message; - - error_message = furi_string_alloc_set("unknown error"); - - bool file_selected = false; - bool show_error = true; - do { - file_selected = true; - loader->app = flipper_application_alloc(loader->storage, firmware_api_interface); - size_t start = furi_get_tick(); - - FURI_LOG_I(TAG, "FAP Loader is loading %s", furi_string_get_cstr(loader->fap_path)); - - FlipperApplicationPreloadStatus preload_res = - flipper_application_preload(loader->app, furi_string_get_cstr(loader->fap_path)); - if(preload_res != FlipperApplicationPreloadStatusSuccess) { - const char* err_msg = flipper_application_preload_status_to_string(preload_res); - furi_string_printf(error_message, "Preload failed: %s", err_msg); - FURI_LOG_E( - TAG, - "FAP Loader failed to preload %s: %s", - furi_string_get_cstr(loader->fap_path), - err_msg); - break; - } - - FURI_LOG_I(TAG, "FAP Loader is mapping"); - FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(loader->app); - if(load_status != FlipperApplicationLoadStatusSuccess) { - const char* err_msg = flipper_application_load_status_to_string(load_status); - furi_string_printf(error_message, "Load failed: %s", err_msg); - FURI_LOG_E( - TAG, - "FAP Loader failed to map to memory %s: %s", - furi_string_get_cstr(loader->fap_path), - err_msg); - break; - } - - FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start)); - FURI_LOG_I(TAG, "FAP Loader is starting app"); - - FuriThread* thread = flipper_application_spawn(loader->app, NULL); - - /* This flag is set by the debugger - to break on app start */ - if(furi_hal_debug_is_gdb_session_active()) { - FURI_LOG_W(TAG, "Triggering BP for debugger"); - /* After hitting this, you can set breakpoints in your .fap's code - * Note that you have to toggle breakpoints that were set before */ - __asm volatile("bkpt 0"); - } - - FuriString* app_name = furi_string_alloc(); - path_extract_filename_no_ext(furi_string_get_cstr(loader->fap_path), app_name); - furi_thread_set_appid(thread, furi_string_get_cstr(app_name)); - furi_string_free(app_name); - - furi_thread_start(thread); - furi_thread_join(thread); - - show_error = false; - int ret = furi_thread_get_return_code(thread); - - FURI_LOG_I(TAG, "FAP app returned: %i", ret); - } while(0); - - if(show_error) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(message, NULL, NULL, NULL); - - FuriString* buffer; - buffer = furi_string_alloc(); - furi_string_printf(buffer, "%s", furi_string_get_cstr(error_message)); - furi_string_replace(buffer, ":", "\n"); - dialog_message_set_text( - message, furi_string_get_cstr(buffer), 64, 32, AlignCenter, AlignCenter); - - dialog_message_show(loader->dialogs, message); - dialog_message_free(message); - furi_string_free(buffer); - } - - furi_string_free(error_message); - - if(file_selected) { - flipper_application_free(loader->app); - } - - return file_selected; -} - -static bool fap_loader_select_app(FapLoader* loader) { - const DialogsFileBrowserOptions browser_options = { - .extension = ".fap", - .skip_assets = true, - .icon = &I_unknown_10px, - .hide_ext = true, - .item_loader_callback = fap_loader_item_callback, - .item_loader_context = loader, - .base_path = EXT_PATH("apps"), - }; - - return dialog_file_browser_show( - loader->dialogs, loader->fap_path, loader->fap_path, &browser_options); -} - -static FapLoader* fap_loader_alloc(const char* path) { - FapLoader* loader = malloc(sizeof(FapLoader)); //-V799 - loader->fap_path = furi_string_alloc_set(path); - loader->storage = furi_record_open(RECORD_STORAGE); - loader->dialogs = furi_record_open(RECORD_DIALOGS); - loader->gui = furi_record_open(RECORD_GUI); - loader->view_dispatcher = view_dispatcher_alloc(); - loader->loading = loading_alloc(); - view_dispatcher_attach_to_gui( - loader->view_dispatcher, loader->gui, ViewDispatcherTypeFullscreen); - view_dispatcher_add_view(loader->view_dispatcher, 0, loading_get_view(loader->loading)); - return loader; -} //-V773 - -static void fap_loader_free(FapLoader* loader) { - view_dispatcher_remove_view(loader->view_dispatcher, 0); - loading_free(loader->loading); - view_dispatcher_free(loader->view_dispatcher); - furi_string_free(loader->fap_path); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_DIALOGS); - furi_record_close(RECORD_STORAGE); - free(loader); -} - -int32_t fap_loader_app(void* p) { - FapLoader* loader; - if(p) { - loader = fap_loader_alloc((const char*)p); - view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - fap_loader_run_selected_app(loader); - } else { - loader = fap_loader_alloc(EXT_PATH("apps")); - while(fap_loader_select_app(loader)) { - view_dispatcher_switch_to_view(loader->view_dispatcher, 0); - fap_loader_run_selected_app(loader); - }; - } - - fap_loader_free(loader); - return 0; -} diff --git a/applications/main/fap_loader/fap_loader_app.h b/applications/main/fap_loader/fap_loader_app.h deleted file mode 100644 index 9ed725efe..000000000 --- a/applications/main/fap_loader/fap_loader_app.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct FapLoader FapLoader; - -/** - * @brief Load name and icon from FAP file. - * - * @param path Path to FAP file. - * @param storage Storage instance. - * @param icon_ptr Icon pointer. - * @param item_name Application name. - * @return true if icon and name were loaded successfully. - */ -bool fap_loader_load_name_and_icon( - FuriString* path, - Storage* storage, - uint8_t** icon_ptr, - FuriString* item_name); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/services/applications.h b/applications/services/applications.h index 85f736742..45b050a06 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -4,9 +4,9 @@ #include typedef enum { - FlipperApplicationFlagDefault = 0, - FlipperApplicationFlagInsomniaSafe = (1 << 0), -} FlipperApplicationFlag; + FlipperInternalApplicationFlagDefault = 0, + FlipperInternalApplicationFlagInsomniaSafe = (1 << 0), +} FlipperInternalApplicationFlag; typedef struct { const FuriThreadCallback app; @@ -14,48 +14,41 @@ typedef struct { const char* appid; const size_t stack_size; const Icon* icon; - const FlipperApplicationFlag flags; -} FlipperApplication; + const FlipperInternalApplicationFlag flags; +} FlipperInternalApplication; -typedef void (*FlipperOnStartHook)(void); +typedef void (*FlipperInternalOnStartHook)(void); extern const char* FLIPPER_AUTORUN_APP_NAME; /* Services list * Spawned on startup */ -extern const FlipperApplication FLIPPER_SERVICES[]; +extern const FlipperInternalApplication FLIPPER_SERVICES[]; extern const size_t FLIPPER_SERVICES_COUNT; /* Apps list * Spawned by loader */ -extern const FlipperApplication FLIPPER_APPS[]; +extern const FlipperInternalApplication FLIPPER_APPS[]; extern const size_t FLIPPER_APPS_COUNT; /* On system start hooks * Called by loader, after OS initialization complete */ -extern const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; +extern const FlipperInternalOnStartHook FLIPPER_ON_SYSTEM_START[]; extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; /* System apps * Can only be spawned by loader by name */ -extern const FlipperApplication FLIPPER_SYSTEM_APPS[]; +extern const FlipperInternalApplication FLIPPER_SYSTEM_APPS[]; extern const size_t FLIPPER_SYSTEM_APPS_COUNT; -/* Separate scene app holder - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_SCENE; -extern const FlipperApplication FLIPPER_SCENE_APPS[]; -extern const size_t FLIPPER_SCENE_APPS_COUNT; - -extern const FlipperApplication FLIPPER_ARCHIVE; +extern const FlipperInternalApplication FLIPPER_ARCHIVE; /* Settings list * Spawned by loader */ -extern const FlipperApplication FLIPPER_SETTINGS_APPS[]; +extern const FlipperInternalApplication FLIPPER_SETTINGS_APPS[]; extern const size_t FLIPPER_SETTINGS_APPS_COUNT; diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index e1da64940..1233af893 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -36,6 +36,7 @@ static void desktop_loader_callback(const void* message, void* context) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); } } + static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { UNUSED(context); furi_assert(canvas); diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index d19b5560f..ae39ec223 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -16,8 +16,6 @@ #define SNAKE_GAME_APP EXT_PATH("/apps/Games/snake_game.fap") #define CLOCK_APP EXT_PATH("/apps/Tools/clock.fap") -#define FAP_LOADER_APP_NAME "Applications" - static void desktop_scene_main_new_idle_animation_callback(void* context) { furi_assert(context); Desktop* desktop = context; @@ -40,7 +38,8 @@ static void desktop_scene_main_interact_animation_callback(void* context) { } #ifdef APP_ARCHIVE -static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* flipper_app) { +static void + desktop_switch_to_app(Desktop* desktop, const FlipperInternalApplication* flipper_app) { furi_assert(desktop); furi_assert(flipper_app); furi_assert(flipper_app->app); @@ -67,30 +66,16 @@ static void desktop_switch_to_app(Desktop* desktop, const FlipperApplication* fl #endif static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* path) { - do { - LoaderStatus status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, path); - if(status == LoaderStatusOk) break; - FURI_LOG_E(TAG, "loader_start failed: %d", status); - - status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - } while(false); + if(loader_start_with_gui_error(desktop->loader, path, NULL) != LoaderStatusOk) { + loader_start(desktop->loader, "Passport", NULL, NULL); + } } static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) { - LoaderStatus status = LoaderStatusErrorInternal; - if(application->is_external) { - status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, application->name_or_path); - } else if(strlen(application->name_or_path) > 0) { - status = loader_start(desktop->loader, application->name_or_path, NULL); + if(strlen(application->name_or_path) > 0) { + loader_start_with_gui_error(desktop->loader, application->name_or_path, NULL); } else { - status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, NULL); - } - - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); + loader_start(desktop->loader, LOADER_APPLICATIONS_NAME, NULL, NULL); } } @@ -148,10 +133,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; case DesktopMainEventOpenPowerOff: { - LoaderStatus status = loader_start(desktop->loader, "Power", "off"); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start(desktop->loader, "Power", "off", NULL); consumed = true; break; } @@ -176,18 +158,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; case DesktopAnimationEventInteractAnimation: if(!animation_manager_interact_process(desktop->animation_manager)) { - LoaderStatus status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start(desktop->loader, "Passport", NULL, NULL); } consumed = true; break; case DesktopMainEventOpenPassport: { - LoaderStatus status = loader_start(desktop->loader, "Passport", NULL); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start(desktop->loader, "Passport", NULL, NULL); break; } case DesktopMainEventOpenGame: { diff --git a/applications/services/dialogs/dialogs_i.h b/applications/services/dialogs/dialogs_i.h index 76495d31b..29417b41b 100644 --- a/applications/services/dialogs/dialogs_i.h +++ b/applications/services/dialogs/dialogs_i.h @@ -1,7 +1,7 @@ #pragma once #include "dialogs.h" #include "dialogs_message.h" -#include "view_holder.h" +#include #include #ifdef __cplusplus diff --git a/applications/services/dialogs/view_holder.c b/applications/services/gui/view_holder.c similarity index 100% rename from applications/services/dialogs/view_holder.c rename to applications/services/gui/view_holder.c diff --git a/applications/services/dialogs/view_holder.h b/applications/services/gui/view_holder.h similarity index 100% rename from applications/services/dialogs/view_holder.h rename to applications/services/gui/view_holder.h diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index f385efdf9..ab7876a03 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,20 +1,27 @@ #include "loader.h" #include "loader_i.h" -#include "loader_menu.h" #include +#include #include +#include +#include +#include +#include + #define TAG "Loader" #define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF // api -LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { +LoaderStatus + loader_start(Loader* loader, const char* name, const char* args, FuriString* error_message) { LoaderMessage message; LoaderMessageLoaderStatusResult result; message.type = LoaderMessageTypeStartByName; message.start.name = name; message.start.args = args; + message.start.error_message = error_message; message.api_lock = api_lock_alloc_locked(); message.status_value = &result; furi_message_queue_put(loader->queue, &message, FuriWaitForever); @@ -22,6 +29,31 @@ LoaderStatus loader_start(Loader* loader, const char* name, const char* args) { return result.value; } +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) { + FuriString* error_message = furi_string_alloc(); + LoaderStatus status = loader_start(loader, name, args, error_message); + + // TODO: we have many places where we can emit a double start, ex: desktop, menu + // so i prefer to not show LoaderStatusErrorAppStarted error message for now + if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, NULL); + + furi_string_replace(error_message, ":", "\n"); + dialog_message_set_text( + message, furi_string_get_cstr(error_message), 64, 32, AlignCenter, AlignCenter); + + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } + + furi_string_free(error_message); + return status; +} + bool loader_lock(Loader* loader) { LoaderMessage message; LoaderMessageBoolResult result; @@ -73,27 +105,26 @@ static void loader_menu_closed_callback(void* context) { furi_message_queue_put(loader->queue, &message, FuriWaitForever); } -static void loader_menu_click_callback(const char* name, void* context) { +static void loader_applications_closed_callback(void* context) { Loader* loader = context; - loader_start(loader, name, NULL); + LoaderMessage message; + message.type = LoaderMessageTypeApplicationsClosed; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); } static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { furi_assert(context); Loader* loader = context; - LoaderEvent event; if(thread_state == FuriThreadStateRunning) { + LoaderEvent event; event.type = LoaderEventTypeApplicationStarted; furi_pubsub_publish(loader->pubsub, &event); } else if(thread_state == FuriThreadStateStopped) { LoaderMessage message; message.type = LoaderMessageTypeAppClosed; furi_message_queue_put(loader->queue, &message, FuriWaitForever); - - event.type = LoaderEventTypeApplicationStopped; - furi_pubsub_publish(loader->pubsub, &event); } } @@ -104,16 +135,17 @@ static Loader* loader_alloc() { loader->pubsub = furi_pubsub_alloc(); loader->queue = furi_message_queue_alloc(1, sizeof(LoaderMessage)); loader->loader_menu = NULL; + loader->loader_applications = NULL; loader->app.args = NULL; - loader->app.name = NULL; loader->app.thread = NULL; loader->app.insomniac = false; + loader->app.fap = NULL; return loader; } -static FlipperApplication const* loader_find_application_by_name_in_list( +static FlipperInternalApplication const* loader_find_application_by_name_in_list( const char* name, - const FlipperApplication* list, + const FlipperInternalApplication* list, const uint32_t n_apps) { for(size_t i = 0; i < n_apps; i++) { if(strcmp(name, list[i].name) == 0) { @@ -123,8 +155,8 @@ static FlipperApplication const* loader_find_application_by_name_in_list( return NULL; } -static const FlipperApplication* loader_find_application_by_name(const char* name) { - const FlipperApplication* application = NULL; +static const FlipperInternalApplication* loader_find_application_by_name(const char* name) { + const FlipperInternalApplication* application = NULL; application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); if(!application) { application = loader_find_application_by_name_in_list( @@ -138,25 +170,7 @@ static const FlipperApplication* loader_find_application_by_name(const char* nam return application; } -static void - loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) { - FURI_LOG_I(TAG, "Starting %s", app->name); - - // store args - furi_assert(loader->app.args == NULL); - if(args && strlen(args) > 0) { - loader->app.args = strdup(args); - } - - // store name - furi_assert(loader->app.name == NULL); - loader->app.name = strdup(app->name); - - // setup app thread - loader->app.thread = - furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); - furi_thread_set_appid(loader->app.thread, app->appid); - +static void loader_start_app_thread(Loader* loader, FlipperInternalApplicationFlag flags) { // setup heap trace FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode(); if(mode > FuriHalRtcHeapTrackModeNone) { @@ -166,14 +180,14 @@ static void } // setup insomnia - if(!(app->flags & FlipperApplicationFlagInsomniaSafe)) { + if(!(flags & FlipperInternalApplicationFlagInsomniaSafe)) { furi_hal_power_insomnia_enter(); loader->app.insomniac = true; } else { loader->app.insomniac = false; } - // setup app thread callbacks + // setup thread state callbacks furi_thread_set_state_context(loader->app.thread, loader); furi_thread_set_state_callback(loader->app.thread, loader_thread_state_callback); @@ -181,42 +195,206 @@ static void furi_thread_start(loader->app.thread); } +static void loader_start_internal_app( + Loader* loader, + const FlipperInternalApplication* app, + const char* args) { + FURI_LOG_I(TAG, "Starting %s", app->name); + + // store args + furi_assert(loader->app.args == NULL); + if(args && strlen(args) > 0) { + loader->app.args = strdup(args); + } + + loader->app.thread = + furi_thread_alloc_ex(app->name, app->stack_size, app->app, loader->app.args); + furi_thread_set_appid(loader->app.thread, app->appid); + + loader_start_app_thread(loader, app->flags); +} + +static void loader_log_status_error( + LoaderStatus status, + FuriString* error_message, + const char* format, + va_list args) { + if(error_message) { + furi_string_vprintf(error_message, format, args); + FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(error_message)); + } else { + FuriString* tmp = furi_string_alloc(); + FURI_LOG_E(TAG, "Status [%d]: %s", status, furi_string_get_cstr(tmp)); + furi_string_free(tmp); + } +} + +static LoaderStatus loader_make_status_error( + LoaderStatus status, + FuriString* error_message, + const char* format, + ...) { + va_list args; + va_start(args, format); + loader_log_status_error(status, error_message, format, args); + va_end(args); + return status; +} + +static LoaderStatus loader_make_success_status(FuriString* error_message) { + if(error_message) { + furi_string_set(error_message, "App started"); + } + + return LoaderStatusOk; +} + +static LoaderStatus loader_start_external_app( + Loader* loader, + Storage* storage, + const char* path, + const char* args, + FuriString* error_message) { + LoaderStatus status = loader_make_success_status(error_message); + + do { + loader->app.fap = flipper_application_alloc(storage, firmware_api_interface); + size_t start = furi_get_tick(); + + FURI_LOG_I(TAG, "Loading %s", path); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload(loader->app.fap, path); + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + const char* err_msg = flipper_application_preload_status_to_string(preload_res); + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Preload failed %s: %s", path, err_msg); + break; + } + + FURI_LOG_I(TAG, "Mapping"); + FlipperApplicationLoadStatus load_status = + flipper_application_map_to_memory(loader->app.fap); + if(load_status != FlipperApplicationLoadStatusSuccess) { + const char* err_msg = flipper_application_load_status_to_string(load_status); + status = loader_make_status_error( + LoaderStatusErrorInternal, error_message, "Load failed %s: %s", path, err_msg); + break; + } + + FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start)); + FURI_LOG_I(TAG, "Starting app"); + + loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args); + FuriString* app_name = furi_string_alloc(); + path_extract_filename_no_ext(path, app_name); + furi_thread_set_appid(loader->app.thread, furi_string_get_cstr(app_name)); + furi_string_free(app_name); + + /* This flag is set by the debugger - to break on app start */ + if(furi_hal_debug_is_gdb_session_active()) { + FURI_LOG_W(TAG, "Triggering BP for debugger"); + /* After hitting this, you can set breakpoints in your .fap's code + * Note that you have to toggle breakpoints that were set before */ + __asm volatile("bkpt 0"); + } + + loader_start_app_thread(loader, FlipperInternalApplicationFlagDefault); + } while(0); + + if(status != LoaderStatusOk) { + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; + } + + return status; +} + // process messages static void loader_do_menu_show(Loader* loader) { if(!loader->loader_menu) { - loader->loader_menu = loader_menu_alloc(); - loader_menu_set_closed_callback(loader->loader_menu, loader_menu_closed_callback, loader); - loader_menu_set_click_callback(loader->loader_menu, loader_menu_click_callback, loader); - loader_menu_start(loader->loader_menu); + loader->loader_menu = loader_menu_alloc(loader_menu_closed_callback, loader); } } static void loader_do_menu_closed(Loader* loader) { if(loader->loader_menu) { - loader_menu_stop(loader->loader_menu); loader_menu_free(loader->loader_menu); loader->loader_menu = NULL; } } +static void loader_do_applications_show(Loader* loader) { + if(!loader->loader_applications) { + loader->loader_applications = + loader_applications_alloc(loader_applications_closed_callback, loader); + } +} + +static void loader_do_applications_closed(Loader* loader) { + if(loader->loader_applications) { + loader_applications_free(loader->loader_applications); + loader->loader_applications = NULL; + } +} + static bool loader_do_is_locked(Loader* loader) { return loader->app.thread != NULL; } -static LoaderStatus loader_do_start_by_name(Loader* loader, const char* name, const char* args) { - if(loader_do_is_locked(loader)) { - return LoaderStatusErrorAppStarted; - } +static LoaderStatus loader_do_start_by_name( + Loader* loader, + const char* name, + const char* args, + FuriString* error_message) { + LoaderStatus status; + do { + // check lock + if(loader_do_is_locked(loader)) { + const char* current_thread_name = + furi_thread_get_name(furi_thread_get_id(loader->app.thread)); + status = loader_make_status_error( + LoaderStatusErrorAppStarted, + error_message, + "Loader is locked, please close the \"%s\" first", + current_thread_name); + break; + } - const FlipperApplication* app = loader_find_application_by_name(name); + // check internal apps + { + const FlipperInternalApplication* app = loader_find_application_by_name(name); + if(app) { + loader_start_internal_app(loader, app, args); + status = loader_make_success_status(error_message); + break; + } + } - if(!app) { - return LoaderStatusErrorUnknownApp; - } + // check Applications + if(strcmp(name, LOADER_APPLICATIONS_NAME) == 0) { + loader_do_applications_show(loader); + status = loader_make_success_status(error_message); + break; + } - loader_start_internal_app(loader, app, args); - return LoaderStatusOk; + // check external apps + { + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_file_exists(storage, name)) { + status = loader_start_external_app(loader, storage, name, args, error_message); + furi_record_close(RECORD_STORAGE); + break; + } + furi_record_close(RECORD_STORAGE); + } + + status = loader_make_status_error( + LoaderStatusErrorUnknownApp, error_message, "Application \"%s\" not found", name); + } while(false); + + return status; } static bool loader_do_lock(Loader* loader) { @@ -229,13 +407,16 @@ static bool loader_do_lock(Loader* loader) { } static void loader_do_unlock(Loader* loader) { - furi_assert(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); + furi_check(loader->app.thread == (FuriThread*)LOADER_MAGIC_THREAD_VALUE); loader->app.thread = NULL; } static void loader_do_app_closed(Loader* loader) { furi_assert(loader->app.thread); - FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); + + furi_thread_join(loader->app.thread); + FURI_LOG_I(TAG, "App returned: %li", furi_thread_get_return_code(loader->app.thread)); + if(loader->app.args) { free(loader->app.args); loader->app.args = NULL; @@ -245,12 +426,20 @@ static void loader_do_app_closed(Loader* loader) { furi_hal_power_insomnia_exit(); } - free(loader->app.name); - loader->app.name = NULL; + if(loader->app.fap) { + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; + loader->app.thread = NULL; + } else { + furi_thread_free(loader->app.thread); + loader->app.thread = NULL; + } - furi_thread_join(loader->app.thread); - furi_thread_free(loader->app.thread); - loader->app.thread = NULL; + FURI_LOG_I(TAG, "Application stopped. Free heap: %zu", memmgr_get_free_heap()); + + LoaderEvent event; + event.type = LoaderEventTypeApplicationStopped; + furi_pubsub_publish(loader->pubsub, &event); } // app @@ -266,7 +455,7 @@ int32_t loader_srv(void* p) { } if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { - loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL); + loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL, NULL); } LoaderMessage message; @@ -274,8 +463,8 @@ int32_t loader_srv(void* p) { if(furi_message_queue_get(loader->queue, &message, FuriWaitForever) == FuriStatusOk) { switch(message.type) { case LoaderMessageTypeStartByName: - message.status_value->value = - loader_do_start_by_name(loader, message.start.name, message.start.args); + message.status_value->value = loader_do_start_by_name( + loader, message.start.name, message.start.args, message.start.error_message); api_lock_unlock(message.api_lock); break; case LoaderMessageTypeShowMenu: @@ -297,6 +486,10 @@ int32_t loader_srv(void* p) { break; case LoaderMessageTypeUnlock: loader_do_unlock(loader); + break; + case LoaderMessageTypeApplicationsClosed: + loader_do_applications_closed(loader); + break; } } } diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index e3a691b76..9fc4059f2 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -6,6 +6,7 @@ extern "C" { #endif #define RECORD_LOADER "loader" +#define LOADER_APPLICATIONS_NAME "Applications" typedef struct Loader Loader; @@ -25,28 +26,57 @@ typedef struct { LoaderEventType type; } LoaderEvent; -/** Start application - * @param name - application name - * @param args - application arguments - * @retval true on success +/** + * @brief Start application + * @param[in] instance loader instance + * @param[in] name application name + * @param[in] args application arguments + * @param[out] error_message detailed error message, can be NULL + * @return LoaderStatus */ -LoaderStatus loader_start(Loader* instance, const char* name, const char* args); +LoaderStatus + loader_start(Loader* instance, const char* name, const char* args, FuriString* error_message); -/** Lock application start - * @retval true on success +/** + * @brief Start application with GUI error message + * @param[in] instance loader instance + * @param[in] name application name + * @param[in] args application arguments + * @return LoaderStatus + */ +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args); + +/** + * @brief Lock application start + * @param[in] instance loader instance + * @return true on success */ bool loader_lock(Loader* instance); -/** Unlock application start */ +/** + * @brief Unlock application start + * @param[in] instance loader instance + */ void loader_unlock(Loader* instance); -/** Get loader lock status */ +/** + * @brief Check if loader is locked + * @param[in] instance loader instance + * @return true if locked + */ bool loader_is_locked(Loader* instance); -/** Show primary loader */ +/** + * @brief Show loader menu + * @param[in] instance loader instance + */ void loader_show_menu(Loader* instance); -/** Show primary loader */ +/** + * @brief Get loader pubsub + * @param[in] instance loader instance + * @return FuriPubSub* + */ FuriPubSub* loader_get_pubsub(Loader* instance); #ifdef __cplusplus diff --git a/applications/services/loader/loader_applications.c b/applications/services/loader/loader_applications.c new file mode 100644 index 000000000..1801edef9 --- /dev/null +++ b/applications/services/loader/loader_applications.c @@ -0,0 +1,146 @@ +#include "loader.h" +#include "loader_applications.h" +#include +#include +#include +#include +#include +#include + +#define TAG "LoaderApplications" + +struct LoaderApplications { + FuriThread* thread; + void (*closed_cb)(void*); + void* context; +}; + +static int32_t loader_applications_thread(void* p); + +LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context) { + LoaderApplications* loader_applications = malloc(sizeof(LoaderApplications)); + loader_applications->thread = + furi_thread_alloc_ex(TAG, 512, loader_applications_thread, (void*)loader_applications); + loader_applications->closed_cb = closed_cb; + loader_applications->context = context; + furi_thread_start(loader_applications->thread); + return loader_applications; +} + +void loader_applications_free(LoaderApplications* loader_applications) { + furi_assert(loader_applications); + furi_thread_join(loader_applications->thread); + furi_thread_free(loader_applications->thread); + free(loader_applications); +} + +typedef struct { + FuriString* fap_path; + DialogsApp* dialogs; + Storage* storage; +} LoaderApplicationsApp; + +static LoaderApplicationsApp* loader_applications_app_alloc() { + LoaderApplicationsApp* app = malloc(sizeof(LoaderApplicationsApp)); //-V799 + app->fap_path = furi_string_alloc_set(EXT_PATH("apps")); + app->dialogs = furi_record_open(RECORD_DIALOGS); + app->storage = furi_record_open(RECORD_STORAGE); + return app; +} //-V773 + +static void loader_applications_app_free(LoaderApplicationsApp* loader_applications_app) { + furi_assert(loader_applications_app); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_STORAGE); + furi_string_free(loader_applications_app->fap_path); + free(loader_applications_app); +} + +static bool loader_applications_item_callback( + FuriString* path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + LoaderApplicationsApp* loader_applications_app = context; + furi_assert(loader_applications_app); + return flipper_application_load_name_and_icon( + path, loader_applications_app->storage, icon_ptr, item_name); +} + +static bool loader_applications_select_app(LoaderApplicationsApp* loader_applications_app) { + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .skip_assets = true, + .icon = &I_unknown_10px, + .hide_ext = true, + .item_loader_callback = loader_applications_item_callback, + .item_loader_context = loader_applications_app, + .base_path = EXT_PATH("apps"), + }; + + return dialog_file_browser_show( + loader_applications_app->dialogs, + loader_applications_app->fap_path, + loader_applications_app->fap_path, + &browser_options); +} + +#define APPLICATION_STOP_EVENT 1 + +static void loader_pubsub_callback(const void* message, void* context) { + const LoaderEvent* event = message; + const FuriThreadId thread_id = (FuriThreadId)context; + + if(event->type == LoaderEventTypeApplicationStopped) { + furi_thread_flags_set(thread_id, APPLICATION_STOP_EVENT); + } +} + +static void loader_applications_start_app(const char* name) { + // start loading animation + Gui* gui = furi_record_open(RECORD_GUI); + ViewHolder* view_holder = view_holder_alloc(); + Loading* loading = loading_alloc(); + + view_holder_attach_to_gui(view_holder, gui); + view_holder_set_view(view_holder, loading_get_view(loading)); + view_holder_start(view_holder); + + // load app + FuriThreadId thread_id = furi_thread_get_current_id(); + Loader* loader = furi_record_open(RECORD_LOADER); + FuriPubSubSubscription* subscription = + furi_pubsub_subscribe(loader_get_pubsub(loader), loader_pubsub_callback, thread_id); + + LoaderStatus status = loader_start_with_gui_error(loader, name, NULL); + + if(status == LoaderStatusOk) { + furi_thread_flags_wait(APPLICATION_STOP_EVENT, FuriFlagWaitAny, FuriWaitForever); + } + + furi_pubsub_unsubscribe(loader_get_pubsub(loader), subscription); + furi_record_close(RECORD_LOADER); + + // stop loading animation + view_holder_stop(view_holder); + view_holder_free(view_holder); + loading_free(loading); + furi_record_close(RECORD_GUI); +} + +static int32_t loader_applications_thread(void* p) { + LoaderApplications* loader_applications = p; + LoaderApplicationsApp* loader_applications_app = loader_applications_app_alloc(); + + while(loader_applications_select_app(loader_applications_app)) { + loader_applications_start_app(furi_string_get_cstr(loader_applications_app->fap_path)); + } + + loader_applications_app_free(loader_applications_app); + + if(loader_applications->closed_cb) { + loader_applications->closed_cb(loader_applications->context); + } + + return 0; +} \ No newline at end of file diff --git a/applications/services/loader/loader_applications.h b/applications/services/loader/loader_applications.h new file mode 100644 index 000000000..6b132af05 --- /dev/null +++ b/applications/services/loader/loader_applications.h @@ -0,0 +1,16 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct LoaderApplications LoaderApplications; + +LoaderApplications* loader_applications_alloc(void (*closed_cb)(void*), void* context); + +void loader_applications_free(LoaderApplications* loader_applications); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index 2d4602215..af3ebf9e0 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -50,21 +50,11 @@ static void loader_cli_open(FuriString* args, Loader* loader) { const char* app_name_str = furi_string_get_cstr(app_name); - LoaderStatus status = loader_start(loader, app_name_str, args_str); - - switch(status) { - case LoaderStatusOk: - break; - case LoaderStatusErrorAppStarted: - printf("Can't start, application is running"); - break; - case LoaderStatusErrorUnknownApp: - printf("%s doesn't exists\r\n", app_name_str); - break; - case LoaderStatusErrorInternal: - printf("Internal error\r\n"); - break; + FuriString* error_message = furi_string_alloc(); + if(loader_start(loader, app_name_str, args_str, error_message) != LoaderStatusOk) { + printf("%s\r\n", furi_string_get_cstr(error_message)); } + furi_string_free(error_message); } while(false); furi_string_free(app_name); diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index 2e3f10dad..688b8fb66 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -1,20 +1,23 @@ #pragma once #include #include +#include #include "loader.h" #include "loader_menu.h" +#include "loader_applications.h" typedef struct { char* args; - char* name; FuriThread* thread; bool insomniac; + FlipperApplication* fap; } LoaderAppData; struct Loader { FuriPubSub* pubsub; FuriMessageQueue* queue; LoaderMenu* loader_menu; + LoaderApplications* loader_applications; LoaderAppData app; }; @@ -23,6 +26,7 @@ typedef enum { LoaderMessageTypeAppClosed, LoaderMessageTypeShowMenu, LoaderMessageTypeMenuClosed, + LoaderMessageTypeApplicationsClosed, LoaderMessageTypeLock, LoaderMessageTypeUnlock, LoaderMessageTypeIsLocked, @@ -31,6 +35,7 @@ typedef enum { typedef struct { const char* name; const char* args; + FuriString* error_message; } LoaderMessageStartByName; typedef struct { diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index ec853661f..28283f85c 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -5,106 +5,76 @@ #include #include +#include "loader.h" #include "loader_menu.h" #define TAG "LoaderMenu" struct LoaderMenu { - Gui* gui; - ViewDispatcher* view_dispatcher; - Menu* primary_menu; - Submenu* settings_menu; - - void (*closed_callback)(void*); - void* closed_callback_context; - - void (*click_callback)(const char*, void*); - void* click_callback_context; - FuriThread* thread; + void (*closed_cb)(void*); + void* context; }; +static int32_t loader_menu_thread(void* p); + +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context) { + LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); + loader_menu->closed_cb = closed_cb; + loader_menu->context = context; + loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); + furi_thread_start(loader_menu->thread); + return loader_menu; +} + +void loader_menu_free(LoaderMenu* loader_menu) { + furi_assert(loader_menu); + furi_thread_join(loader_menu->thread); + furi_thread_free(loader_menu->thread); + free(loader_menu); +} + typedef enum { LoaderMenuViewPrimary, LoaderMenuViewSettings, } LoaderMenuView; -static int32_t loader_menu_thread(void* p); +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Menu* primary_menu; + Submenu* settings_menu; +} LoaderMenuApp; -LoaderMenu* loader_menu_alloc() { - LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); - loader_menu->gui = furi_record_open(RECORD_GUI); - loader_menu->view_dispatcher = view_dispatcher_alloc(); - loader_menu->primary_menu = menu_alloc(); - loader_menu->settings_menu = submenu_alloc(); - loader_menu->thread = NULL; - return loader_menu; -} - -void loader_menu_free(LoaderMenu* loader_menu) { - furi_assert(loader_menu); - // check if thread is running - furi_assert(!loader_menu->thread); - - submenu_free(loader_menu->settings_menu); - menu_free(loader_menu->primary_menu); - view_dispatcher_free(loader_menu->view_dispatcher); - furi_record_close(RECORD_GUI); - free(loader_menu); -} - -void loader_menu_start(LoaderMenu* loader_menu) { - furi_assert(loader_menu); - furi_assert(!loader_menu->thread); - loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); - furi_thread_start(loader_menu->thread); -} - -void loader_menu_stop(LoaderMenu* loader_menu) { - furi_assert(loader_menu); - furi_assert(loader_menu->thread); - view_dispatcher_stop(loader_menu->view_dispatcher); - furi_thread_join(loader_menu->thread); - furi_thread_free(loader_menu->thread); - loader_menu->thread = NULL; -} - -void loader_menu_set_closed_callback( - LoaderMenu* loader_menu, - void (*callback)(void*), - void* context) { - loader_menu->closed_callback = callback; - loader_menu->closed_callback_context = context; -} - -void loader_menu_set_click_callback( - LoaderMenu* loader_menu, - void (*callback)(const char*, void*), - void* context) { - loader_menu->click_callback = callback; - loader_menu->click_callback_context = context; +static void loader_menu_start(const char* name) { + Loader* loader = furi_record_open(RECORD_LOADER); + loader_start_with_gui_error(loader, name, NULL); + furi_record_close(RECORD_LOADER); } static void loader_menu_callback(void* context, uint32_t index) { - LoaderMenu* loader_menu = context; + UNUSED(context); const char* name = FLIPPER_APPS[index].name; - if(loader_menu->click_callback) { - loader_menu->click_callback(name, loader_menu->click_callback_context); - } + loader_menu_start(name); +} + +static void loader_menu_applications_callback(void* context, uint32_t index) { + UNUSED(index); + UNUSED(context); + const char* name = LOADER_APPLICATIONS_NAME; + loader_menu_start(name); } static void loader_menu_settings_menu_callback(void* context, uint32_t index) { - LoaderMenu* loader_menu = context; + UNUSED(context); const char* name = FLIPPER_SETTINGS_APPS[index].name; - if(loader_menu->click_callback) { - loader_menu->click_callback(name, loader_menu->click_callback_context); - } + loader_menu_start(name); } static void loader_menu_switch_to_settings(void* context, uint32_t index) { UNUSED(index); - LoaderMenu* loader_menu = context; - view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); + LoaderMenuApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewSettings); } static uint32_t loader_menu_switch_to_primary(void* context) { @@ -117,30 +87,32 @@ static uint32_t loader_menu_exit(void* context) { return VIEW_NONE; } -static void loader_menu_build_menu(LoaderMenu* loader_menu) { +static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { size_t i; for(i = 0; i < FLIPPER_APPS_COUNT; i++) { menu_add_item( - loader_menu->primary_menu, + app->primary_menu, FLIPPER_APPS[i].name, FLIPPER_APPS[i].icon, i, loader_menu_callback, - (void*)loader_menu); + (void*)menu); } menu_add_item( - loader_menu->primary_menu, - "Settings", - &A_Settings_14, + app->primary_menu, "Settings", &A_Settings_14, i++, loader_menu_switch_to_settings, app); + menu_add_item( + app->primary_menu, + LOADER_APPLICATIONS_NAME, + &A_Plugins_14, i++, - loader_menu_switch_to_settings, - loader_menu); + loader_menu_applications_callback, + (void*)menu); }; -static void loader_menu_build_submenu(LoaderMenu* loader_menu) { +static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) { for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { submenu_add_item( - loader_menu->settings_menu, + app->settings_menu, FLIPPER_SETTINGS_APPS[i].name, i, loader_menu_settings_menu_callback, @@ -148,40 +120,59 @@ static void loader_menu_build_submenu(LoaderMenu* loader_menu) { } } +static LoaderMenuApp* loader_menu_app_alloc(LoaderMenu* loader_menu) { + LoaderMenuApp* app = malloc(sizeof(LoaderMenuApp)); + app->gui = furi_record_open(RECORD_GUI); + app->view_dispatcher = view_dispatcher_alloc(); + app->primary_menu = menu_alloc(); + app->settings_menu = submenu_alloc(); + + loader_menu_build_menu(app, loader_menu); + loader_menu_build_submenu(app, loader_menu); + + // Primary menu + View* primary_view = menu_get_view(app->primary_menu); + view_set_context(primary_view, app->primary_menu); + view_set_previous_callback(primary_view, loader_menu_exit); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewPrimary, primary_view); + + // Settings menu + View* settings_view = submenu_get_view(app->settings_menu); + view_set_context(settings_view, app->settings_menu); + view_set_previous_callback(settings_view, loader_menu_switch_to_primary); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewSettings, settings_view); + + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewPrimary); + + return app; +} + +static void loader_menu_app_free(LoaderMenuApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewPrimary); + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewSettings); + view_dispatcher_free(app->view_dispatcher); + + menu_free(app->primary_menu); + submenu_free(app->settings_menu); + furi_record_close(RECORD_GUI); + free(app); +} + static int32_t loader_menu_thread(void* p) { LoaderMenu* loader_menu = p; furi_assert(loader_menu); - loader_menu_build_menu(loader_menu); - loader_menu_build_submenu(loader_menu); + LoaderMenuApp* app = loader_menu_app_alloc(loader_menu); - view_dispatcher_attach_to_gui( - loader_menu->view_dispatcher, loader_menu->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_run(app->view_dispatcher); - // Primary menu - View* primary_view = menu_get_view(loader_menu->primary_menu); - view_set_context(primary_view, loader_menu->primary_menu); - view_set_previous_callback(primary_view, loader_menu_exit); - view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary, primary_view); - - // Settings menu - View* settings_view = submenu_get_view(loader_menu->settings_menu); - view_set_context(settings_view, loader_menu->settings_menu); - view_set_previous_callback(settings_view, loader_menu_switch_to_primary); - view_dispatcher_add_view(loader_menu->view_dispatcher, LoaderMenuViewSettings, settings_view); - - view_dispatcher_enable_queue(loader_menu->view_dispatcher); - view_dispatcher_switch_to_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); - - // run view dispatcher - view_dispatcher_run(loader_menu->view_dispatcher); - - view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewPrimary); - view_dispatcher_remove_view(loader_menu->view_dispatcher, LoaderMenuViewSettings); - - if(loader_menu->closed_callback) { - loader_menu->closed_callback(loader_menu->closed_callback_context); + if(loader_menu->closed_cb) { + loader_menu->closed_cb(loader_menu->context); } + loader_menu_app_free(app); + return 0; } \ No newline at end of file diff --git a/applications/services/loader/loader_menu.h b/applications/services/loader/loader_menu.h index 7405b87be..528fe7d29 100644 --- a/applications/services/loader/loader_menu.h +++ b/applications/services/loader/loader_menu.h @@ -7,24 +7,10 @@ extern "C" { typedef struct LoaderMenu LoaderMenu; -LoaderMenu* loader_menu_alloc(); +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context); void loader_menu_free(LoaderMenu* loader_menu); -void loader_menu_start(LoaderMenu* loader_menu); - -void loader_menu_stop(LoaderMenu* loader_menu); - -void loader_menu_set_closed_callback( - LoaderMenu* loader_menu, - void (*callback)(void*), - void* context); - -void loader_menu_set_click_callback( - LoaderMenu* loader_menu, - void (*callback)(const char*, void*), - void* context); - #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/applications/services/rpc/rpc_app.c b/applications/services/rpc/rpc_app.c index cc18b6cec..bf44ed2de 100644 --- a/applications/services/rpc/rpc_app.c +++ b/applications/services/rpc/rpc_app.c @@ -52,7 +52,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); app_args = args_temp; } - LoaderStatus status = loader_start(loader, app_name, app_args); + LoaderStatus status = loader_start(loader, app_name, app_args, NULL); if(status == LoaderStatusErrorAppStarted) { result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; } else if(status == LoaderStatusErrorInternal) { diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index 4b5c47921..698cfae1b 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -3,7 +3,6 @@ #include "desktop_settings_scene.h" #include #include -#include #define EXTERNAL_APPLICATION_NAME ("[External Application]") #define EXTERNAL_APPLICATION_INDEX (FLIPPER_APPS_COUNT + 1) @@ -65,7 +64,6 @@ void desktop_settings_scene_favorite_on_enter(void* context) { } } -#ifdef APP_FAP_LOADER submenu_add_item( submenu, EXTERNAL_APPLICATION_NAME, @@ -75,7 +73,6 @@ void desktop_settings_scene_favorite_on_enter(void* context) { if(curr_favorite_app->is_external) { pre_select_item = EXTERNAL_APPLICATION_INDEX; } -#endif submenu_set_header( submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:"); diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.c b/applications/system/storage_move_to_sd/storage_move_to_sd.c index 9c91b9266..25893f011 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.c +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.c @@ -172,7 +172,7 @@ static void storage_move_to_sd_mount_callback(const void* message, void* context if(storage_event->type == StorageEventTypeCardMount) { Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "StorageMoveToSd", NULL); + loader_start(loader, "StorageMoveToSd", NULL, NULL); furi_record_close(RECORD_LOADER); } } diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index 659c431f7..cebdc4d7c 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -99,7 +99,7 @@ static void updater_start_app(void* context, uint32_t arg) { * So, accessing its record would cause a deadlock */ Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "UpdaterApp", NULL); + loader_start(loader, "UpdaterApp", NULL, NULL); furi_record_close(RECORD_LOADER); } diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index d429de655..101ea92a6 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.1,, +Version,+,31.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -735,9 +735,11 @@ Function,+,filesystem_api_error_get_desc,const char*,FS_Error Function,-,fiprintf,int,"FILE*, const char*, ..." Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_alloc_thread,FuriThread*,"FlipperApplication*, const char*" Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* +Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* @@ -747,7 +749,6 @@ Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescr Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus -Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" @@ -1419,7 +1420,8 @@ Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* -Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 80b4eedbd..5ed26f296 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.1,, +Version,+,31.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -892,9 +892,11 @@ Function,-,finitel,int,long double Function,-,fiprintf,int,"FILE*, const char*, ..." Function,-,fiscanf,int,"FILE*, const char*, ..." Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_alloc_thread,FuriThread*,"FlipperApplication*, const char*" Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_is_plugin,_Bool,FlipperApplication* +Function,+,flipper_application_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* @@ -904,7 +906,6 @@ Function,+,flipper_application_plugin_get_descriptor,const FlipperAppPluginDescr Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" Function,+,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus -Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* Function,+,flipper_format_buffered_file_open_always,_Bool,"FlipperFormat*, const char*" @@ -1825,7 +1826,8 @@ Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* -Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* Function,+,loading_alloc,Loading*, Function,+,loading_free,void,Loading* diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 1b4f56814..fbcf2973d 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -2,6 +2,7 @@ #include "elf/elf_file.h" #include #include "application_assets.h" +#include #include @@ -81,6 +82,12 @@ void flipper_application_free(FlipperApplication* app) { } elf_file_free(app->elf); + + if(app->ep_thread_args) { + free(app->ep_thread_args); + app->ep_thread_args = NULL; + } + free(app); } @@ -224,10 +231,19 @@ static int32_t flipper_application_thread(void* context) { return ret_code; } -FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { +FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args) { furi_check(app->thread == NULL); furi_check(!flipper_application_is_plugin(app)); - app->ep_thread_args = args; + + if(app->ep_thread_args) { + free(app->ep_thread_args); + } + + if(args) { + app->ep_thread_args = strdup(args); + } else { + app->ep_thread_args = NULL; + } const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); app->thread = furi_thread_alloc_ex( @@ -289,4 +305,32 @@ const FlipperAppPluginDescriptor* lib_descriptor->ep_api_version); return lib_descriptor; +} + +bool flipper_application_load_name_and_icon( + FuriString* path, + Storage* storage, + uint8_t** icon_ptr, + FuriString* item_name) { + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload_manifest(app, furi_string_get_cstr(path)); + + bool load_success = false; + + if(preload_res == FlipperApplicationPreloadStatusSuccess) { + const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); + if(manifest->has_icon) { + memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); + } + furi_string_set(item_name, manifest->name); + load_success = true; + } else { + FURI_LOG_E(TAG, "Failed to preload %s", furi_string_get_cstr(path)); + load_success = false; + } + + flipper_application_free(app); + return load_success; } \ No newline at end of file diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h index 519cc3971..20baae826 100644 --- a/lib/flipper_application/flipper_application.h +++ b/lib/flipper_application/flipper_application.h @@ -106,14 +106,14 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app); /** - * @brief Create application thread at entry point address, using app name and + * @brief Allocate application thread at entry point address, using app name and * stack size from metadata. Returned thread isn't started yet. * Can be only called once for application instance. * @param app Applicaiton pointer - * @param args Object to pass to app's entry point + * @param args Args to pass to app's entry point * @return Created thread */ -FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); +FuriThread* flipper_application_alloc_thread(FlipperApplication* app, const char* args); /** * @brief Check if application is a plugin (not a runnable standalone app) @@ -149,6 +149,21 @@ typedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)( const FlipperAppPluginDescriptor* flipper_application_plugin_get_descriptor(FlipperApplication* app); +/** + * @brief Load name and icon from FAP file. + * + * @param path Path to FAP file. + * @param storage Storage instance. + * @param icon_ptr Icon pointer. + * @param item_name Application name. + * @return true if icon and name were loaded successfully. + */ +bool flipper_application_load_name_and_icon( + FuriString* path, + Storage* storage, + uint8_t** icon_ptr, + FuriString* item_name); + #ifdef __cplusplus } #endif diff --git a/scripts/distfap.py b/scripts/distfap.py index d330988b5..b1c558790 100644 --- a/scripts/distfap.py +++ b/scripts/distfap.py @@ -52,9 +52,7 @@ class Main(App): if not self.args.launch_app: return 0 - storage.send_and_wait_eol( - f'loader open "Applications" {fap_dst_path}\r' - ) + storage.send_and_wait_eol(f"loader open {fap_dst_path}\r") if len(result := storage.read.until(storage.CLI_EOL)): self.logger.error(f"Unexpected response: {result.decode('ascii')}") diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 820f5a8c5..73e5c7770 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -353,12 +353,18 @@ class AppBuildset: class ApplicationsCGenerator: APP_TYPE_MAP = { - FlipperAppType.SERVICE: ("FlipperApplication", "FLIPPER_SERVICES"), - FlipperAppType.SYSTEM: ("FlipperApplication", "FLIPPER_SYSTEM_APPS"), - FlipperAppType.APP: ("FlipperApplication", "FLIPPER_APPS"), - FlipperAppType.DEBUG: ("FlipperApplication", "FLIPPER_DEBUG_APPS"), - FlipperAppType.SETTINGS: ("FlipperApplication", "FLIPPER_SETTINGS_APPS"), - FlipperAppType.STARTUP: ("FlipperOnStartHook", "FLIPPER_ON_SYSTEM_START"), + FlipperAppType.SERVICE: ("FlipperInternalApplication", "FLIPPER_SERVICES"), + FlipperAppType.SYSTEM: ("FlipperInternalApplication", "FLIPPER_SYSTEM_APPS"), + FlipperAppType.APP: ("FlipperInternalApplication", "FLIPPER_APPS"), + FlipperAppType.DEBUG: ("FlipperInternalApplication", "FLIPPER_DEBUG_APPS"), + FlipperAppType.SETTINGS: ( + "FlipperInternalApplication", + "FLIPPER_SETTINGS_APPS", + ), + FlipperAppType.STARTUP: ( + "FlipperInternalOnStartHook", + "FLIPPER_ON_SYSTEM_START", + ), } def __init__(self, buildset: AppBuildset, autorun_app: str = ""): @@ -379,7 +385,7 @@ class ApplicationsCGenerator: .appid = "{app.appid}", .stack_size = {app.stack_size}, .icon = {f"&{app.icon}" if app.icon else "NULL"}, - .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)} }}""" + .flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)} }}""" def generate(self): contents = [ @@ -408,7 +414,7 @@ class ApplicationsCGenerator: contents.extend( [ self.get_app_ep_forward(archive_app[0]), - f"const FlipperApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};", + f"const FlipperInternalApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};", ] ) diff --git a/scripts/runfap.py b/scripts/runfap.py index a240acf12..42141acff 100644 --- a/scripts/runfap.py +++ b/scripts/runfap.py @@ -63,7 +63,7 @@ class Main(App): storage_ops.recursive_send(fap_dst_path, fap_local_path, False) fap_host_app = self.args.targets[0] - startup_command = f'"Applications" {fap_host_app}' + startup_command = f"{fap_host_app}" if self.args.host_app: startup_command = self.args.host_app From 3ee2223cbd05bf8b77c3528a0eebec5ec161438a Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 23 Jun 2023 21:54:19 +0300 Subject: [PATCH 244/370] Hid app: Flip Numpad --- applications/external/hid_app/views/hid_numpad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/hid_app/views/hid_numpad.c b/applications/external/hid_app/views/hid_numpad.c index cedb6d341..c27576862 100644 --- a/applications/external/hid_app/views/hid_numpad.c +++ b/applications/external/hid_app/views/hid_numpad.c @@ -284,7 +284,7 @@ HidNumpad* hid_numpad_alloc(Hid* bt_hid) { hid_numpad->hid = bt_hid; view_set_context(hid_numpad->view, hid_numpad); view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel)); - view_set_orientation(hid_numpad->view, ViewOrientationVerticalFlip); + view_set_orientation(hid_numpad->view, ViewOrientationVertical); view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback); view_set_input_callback(hid_numpad->view, hid_numpad_input_callback); From b709ade194f743fec2e746add6f24abb26a30c83 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sat, 24 Jun 2023 05:35:08 +0200 Subject: [PATCH 245/370] Toolchain bump + readme update --- ReadMe.md | 3 +++ lib/flipper_application/elf/elf_file.c | 1 - scripts/toolchain/fbtenv.cmd | 2 +- scripts/toolchain/fbtenv.sh | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index e99e1b9dd..fe0cfc12f 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -190,6 +190,9 @@ There are 3 methods to install Xtreme, we recommend you use the **Web Updater**,

Build it yourself:

+> **Warning** +> We will not give basic support for compiling in our server. This is intended for people that already *know* what they are doing! + ```bash To download the needed tools: $ git clone --recursive https://github.com/ClaraCrazy/Flipper-Xtreme.git diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 605b62186..9f37fc4e0 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -559,7 +559,6 @@ static SectionType elf_preload_section( name = name + strlen(".fast.rel"); ELFSection* section_p = elf_file_get_or_put_section(elf, name); section_p->fast_rel = malloc(sizeof(ELFSection)); - FURI_LOG_I(TAG, "Fast rel section size: %ld", section_header->sh_size); if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) { FURI_LOG_E(TAG, "Error loading section '%s'", name); diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 9d45b7e9d..4ae04e2a2 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=21" +set "FLIPPER_TOOLCHAIN_VERSION=22" if ["%FBT_TOOLCHAIN_PATH%"] == [""] ( set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index d911c2334..81df6c0af 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,7 +4,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"22"}"; if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then FBT_TOOLCHAIN_PATH_WAS_SET=0; From 21d23f5430ce6bcb80e8110347917bae38708b74 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sat, 24 Jun 2023 05:41:52 +0200 Subject: [PATCH 246/370] remove legacy information --- ReadMe.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index fe0cfc12f..2da02b530 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -183,9 +183,6 @@ There are 3 methods to install Xtreme, we recommend you use the **Web Updater**,
-**If you have issues or crashes with the install process, you can try to use `Settings > Storage > Factory Reset` then retry the install.** -**Doing that will NOT remove your saved files, it will only forget some settings and paired devices.** - ----

Build it yourself:

From 8ae952f499587dbfb0368881bd9a43662028a207 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 Jun 2023 17:50:27 +0300 Subject: [PATCH 247/370] Fix furi_hal_bus issues in AVR Programmer and Signal Generator --- .../helpers/avr_isp_worker_rw.c | 19 ++++++-- .../scenes/signal_gen_scene_pwm.c | 47 +++++++++++++++++-- .../signal_generator/signal_gen_app_i.h | 1 + 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index 209551a47..c4323a56d 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -60,7 +60,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) { AvrIspWorkerRW* instance = context; /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + if(!furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + } FURI_LOG_D(TAG, "Start"); @@ -122,7 +124,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) { } FURI_LOG_D(TAG, "Stop"); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } return 0; } @@ -136,7 +140,12 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { instance->chip_arr_ind = avr_isp_chip_arr_size + 1; /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + bool was_pwm_enabled = false; + if(!furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + } else { + was_pwm_enabled = true; + } do { if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { @@ -200,7 +209,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } while(0); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2) && !was_pwm_enabled) { + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c index 7ac3fadda..9e09a472d 100644 --- a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c +++ b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c @@ -33,7 +33,13 @@ void signal_gen_scene_pwm_on_enter(void* context) { signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app); signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY); - furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + + if(!furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + } else { + furi_hal_pwm_stop(pwm_ch_id[0]); + furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + } } bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { @@ -46,8 +52,32 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty); } else if(event.event == SignalGenPwmEventChannelChange) { consumed = true; - furi_hal_pwm_stop(app->pwm_ch_prev); - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + // Stop previous channel PWM + if(app->pwm_ch_prev == FuriHalPwmOutputIdTim1PA7) { + if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + furi_hal_pwm_stop(app->pwm_ch_prev); + } + } else if(app->pwm_ch_prev == FuriHalPwmOutputIdLptim2PA4) { + if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + furi_hal_pwm_stop(app->pwm_ch_prev); + } + } + // Start PWM and restart if it was starter already + if(app->pwm_ch == FuriHalPwmOutputIdTim1PA7) { + if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + furi_hal_pwm_stop(app->pwm_ch); + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } else { + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } + } else if(app->pwm_ch == FuriHalPwmOutputIdLptim2PA4) { + if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + furi_hal_pwm_stop(app->pwm_ch); + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } else { + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } + } } } return consumed; @@ -56,5 +86,14 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { void signal_gen_scene_pwm_on_exit(void* context) { SignalGenApp* app = context; variable_item_list_reset(app->var_item_list); - furi_hal_pwm_stop(app->pwm_ch); + + if(app->pwm_ch == FuriHalPwmOutputIdTim1PA7) { + if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + furi_hal_pwm_stop(app->pwm_ch); + } + } else if(app->pwm_ch == FuriHalPwmOutputIdLptim2PA4) { + if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + furi_hal_pwm_stop(app->pwm_ch); + } + } } diff --git a/applications/external/signal_generator/signal_gen_app_i.h b/applications/external/signal_generator/signal_gen_app_i.h index 60e4d7ed9..2d31dda60 100644 --- a/applications/external/signal_generator/signal_gen_app_i.h +++ b/applications/external/signal_generator/signal_gen_app_i.h @@ -4,6 +4,7 @@ #include #include +#include #include #include From 0540c2743f7fd9670c0ca39eb95cd5ceb8ba3476 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 Jun 2023 18:34:33 +0300 Subject: [PATCH 248/370] Update totp --- applications/external/totp/workers/type_code_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/totp/workers/type_code_common.c b/applications/external/totp/workers/type_code_common.c index bc42fadaa..82a5a028e 100644 --- a/applications/external/totp/workers/type_code_common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -52,10 +52,10 @@ void totp_type_code_worker_execute_automation( while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) { uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char); if(char_index > 9) { - char_index = cb_char - 0x41 + 10; + char_index = cb_char - 'A' + 10; } - if(char_index > 35) break; + if(char_index >= sizeof(hid_number_keys)) break; uint16_t hid_kb_key = hid_number_keys[char_index]; if(char_index > 9) { From ae47b9888f0c615175d33cd2921fd0c47c828d60 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 Jun 2023 20:23:46 +0300 Subject: [PATCH 249/370] Rework pwm is running check --- .../helpers/avr_isp_worker_rw.c | 8 ++-- .../scenes/signal_gen_scene_pwm.c | 42 +++++-------------- .../signal_generator/signal_gen_app_i.h | 1 - firmware/targets/f7/api_symbols.csv | 3 +- firmware/targets/f7/furi_hal/furi_hal_pwm.c | 9 ++++ firmware/targets/f7/furi_hal/furi_hal_pwm.h | 8 ++++ 6 files changed, 34 insertions(+), 37 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index c4323a56d..121f08565 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -60,7 +60,7 @@ static int32_t avr_isp_worker_rw_thread(void* context) { AvrIspWorkerRW* instance = context; /* start PWM on &gpio_ext_pa4 */ - if(!furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); } @@ -124,7 +124,7 @@ static int32_t avr_isp_worker_rw_thread(void* context) { } FURI_LOG_D(TAG, "Stop"); - if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); } @@ -141,7 +141,7 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { /* start PWM on &gpio_ext_pa4 */ bool was_pwm_enabled = false; - if(!furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { + if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); } else { was_pwm_enabled = true; @@ -209,7 +209,7 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } while(0); - if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2) && !was_pwm_enabled) { + if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) { furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); } diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c index 9e09a472d..1cadb3a1a 100644 --- a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c +++ b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c @@ -34,7 +34,7 @@ void signal_gen_scene_pwm_on_enter(void* context) { signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY); - if(!furi_hal_bus_is_enabled(FuriHalBusTIM1)) { + if(!furi_hal_pwm_is_running(pwm_ch_id[0])) { furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); } else { furi_hal_pwm_stop(pwm_ch_id[0]); @@ -53,30 +53,16 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SignalGenPwmEventChannelChange) { consumed = true; // Stop previous channel PWM - if(app->pwm_ch_prev == FuriHalPwmOutputIdTim1PA7) { - if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) { - furi_hal_pwm_stop(app->pwm_ch_prev); - } - } else if(app->pwm_ch_prev == FuriHalPwmOutputIdLptim2PA4) { - if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { - furi_hal_pwm_stop(app->pwm_ch_prev); - } + if(furi_hal_pwm_is_running(app->pwm_ch_prev)) { + furi_hal_pwm_stop(app->pwm_ch_prev); } + // Start PWM and restart if it was starter already - if(app->pwm_ch == FuriHalPwmOutputIdTim1PA7) { - if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) { - furi_hal_pwm_stop(app->pwm_ch); - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); - } else { - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); - } - } else if(app->pwm_ch == FuriHalPwmOutputIdLptim2PA4) { - if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { - furi_hal_pwm_stop(app->pwm_ch); - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); - } else { - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); - } + if(furi_hal_pwm_is_running(app->pwm_ch)) { + furi_hal_pwm_stop(app->pwm_ch); + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } else { + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); } } } @@ -87,13 +73,7 @@ void signal_gen_scene_pwm_on_exit(void* context) { SignalGenApp* app = context; variable_item_list_reset(app->var_item_list); - if(app->pwm_ch == FuriHalPwmOutputIdTim1PA7) { - if(furi_hal_bus_is_enabled(FuriHalBusTIM1)) { - furi_hal_pwm_stop(app->pwm_ch); - } - } else if(app->pwm_ch == FuriHalPwmOutputIdLptim2PA4) { - if(furi_hal_bus_is_enabled(FuriHalBusLPTIM2)) { - furi_hal_pwm_stop(app->pwm_ch); - } + if(furi_hal_pwm_is_running(app->pwm_ch)) { + furi_hal_pwm_stop(app->pwm_ch); } } diff --git a/applications/external/signal_generator/signal_gen_app_i.h b/applications/external/signal_generator/signal_gen_app_i.h index 2d31dda60..60e4d7ed9 100644 --- a/applications/external/signal_generator/signal_gen_app_i.h +++ b/applications/external/signal_generator/signal_gen_app_i.h @@ -4,7 +4,6 @@ #include #include -#include #include #include diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 26e4c0b3e..41d38cb90 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,30.1,, +Version,+,30.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1306,6 +1306,7 @@ Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, Function,+,furi_hal_power_suppress_charge_enter,void, Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.c b/firmware/targets/f7/furi_hal/furi_hal_pwm.c index 7e985cbb1..879460e6b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.c +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.c @@ -82,6 +82,15 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel) { } } +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel) { + if(channel == FuriHalPwmOutputIdTim1PA7) { + return furi_hal_bus_is_enabled(FuriHalBusTIM1); + } else if(channel == FuriHalPwmOutputIdLptim2PA4) { + return furi_hal_bus_is_enabled(FuriHalBusLPTIM2); + } + return false; +} + void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) { furi_assert(freq > 0); uint32_t freq_div = 64000000LU / freq; diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.h b/firmware/targets/f7/furi_hal/furi_hal_pwm.h index a8682c5fb..16acca05e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.h +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.h @@ -9,6 +9,7 @@ extern "C" { #endif #include +#include typedef enum { FuriHalPwmOutputIdTim1PA7, @@ -37,6 +38,13 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel); */ void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty); +/** Is PWM channel running? + * + * @param[in] channel PWM channel (FuriHalPwmOutputId) + * @return bool - true if running +*/ +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel); + #ifdef __cplusplus } #endif From 8e126112f0c2e343a79b9e9427bc2fb0633d7399 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 Jun 2023 21:11:27 +0300 Subject: [PATCH 250/370] OFW PR 2782: NFC: Fix key invalidation logic by AloneLiberty --- applications/main/nfc/nfc_cli.c | 4 ++++ lib/nfc/nfc_worker.c | 12 ++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 6e6e04ca9..0b7e75475 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -144,6 +144,10 @@ static void nfc_cli_apdu(Cli* cli, FuriString* args) { break; } resp_size = (tx_rx.rx_bits / 8) * 2; + if(!resp_size) { + printf("No response\r\n"); + break; + } resp_buffer = malloc(resp_size); uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size); resp_buffer[resp_size] = 0; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 7425573fb..d6618b5b7 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -1025,14 +1025,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key A is marked as found and matches the searching key, invalidate it - uint8_t found_key[6]; - memcpy(found_key, data->block[i].value, 6); + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, i); uint8_t current_key[6]; memcpy(current_key, &key, 6); if(mf_classic_is_key_found(data, i, MfClassicKeyA) && - memcmp(found_key, current_key, 6) == 0) { + memcmp(sec_trailer->key_a, current_key, 6) == 0) { mf_classic_set_key_not_found(data, i, MfClassicKeyA); is_key_a_found = false; FURI_LOG_D(TAG, "Key %dA not found in attack", i); @@ -1051,14 +1051,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key B is marked as found and matches the searching key, invalidate it - uint8_t found_key[6]; - memcpy(found_key, data->block[i].value + 10, 6); + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, i); uint8_t current_key[6]; memcpy(current_key, &key, 6); if(mf_classic_is_key_found(data, i, MfClassicKeyB) && - memcmp(found_key, current_key, 6) == 0) { + memcmp(sec_trailer->key_b, current_key, 6) == 0) { mf_classic_set_key_not_found(data, i, MfClassicKeyB); is_key_b_found = false; FURI_LOG_D(TAG, "Key %dB not found in attack", i); From 6f3f2fa1e7fa18d1f8682e1461b05657ebfb93e7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 Jun 2023 21:14:00 +0300 Subject: [PATCH 251/370] OFW PR 2783: SLIX2 emulation support by g3gg0 --- .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 161 ++++--- .../nfc/scenes/nfc_scene_nfcv_read_success.c | 6 +- lib/digital_signal/digital_signal.c | 199 ++++---- lib/nfc/nfc_device.c | 360 +++++++------- lib/nfc/protocols/nfcv.c | 56 ++- lib/nfc/protocols/nfcv.h | 49 +- lib/nfc/protocols/slix.c | 451 ++++++++++++++++-- lib/nfc/protocols/slix.h | 46 +- 8 files changed, 910 insertions(+), 418 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index eb2f939c6..66a9174df 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -7,6 +7,83 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ } } +void nfc_scene_slix_build_string( + FuriString* temp_str, + NfcVData* nfcv_data, + SlixTypeFeatures features, + const char* type) { + furi_string_cat_printf(temp_str, "Type: %s\n", type); + furi_string_cat_printf(temp_str, "Keys:\n"); + if(features & SlixFeatureRead) { + furi_string_cat_printf( + temp_str, + " Read %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyRead) ? "" : " (unset)"); + } + if(features & SlixFeatureWrite) { + furi_string_cat_printf( + temp_str, + " Write %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyWrite) ? "" : " (unset)"); + } + if(features & SlixFeaturePrivacy) { + furi_string_cat_printf( + temp_str, + " Privacy %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyPrivacy) ? "" : " (unset)"); + furi_string_cat_printf( + temp_str, + " Privacy mode %s\n", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ENABLED" : "DISABLED"); + } + if(features & SlixFeatureDestroy) { + furi_string_cat_printf( + temp_str, + " Destroy %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyDestroy) ? "" : " (unset)"); + } + if(features & SlixFeatureEas) { + furi_string_cat_printf( + temp_str, + " EAS %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyEas) ? "" : " (unset)"); + } + if(features & SlixFeatureSignature) { + furi_string_cat_printf( + temp_str, + "Signature %08llX...\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.signature, 4)); + } + furi_string_cat_printf( + temp_str, + "DSFID: %02X %s\n", + nfcv_data->dsfid, + (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "AFI: %02X %s\n", + nfcv_data->afi, + (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "EAS: %s\n", + (nfcv_data->security_status[0] & NfcVLockBitEas) ? "locked" : "not locked"); + + if(features & SlixFeatureProtection) { + furi_string_cat_printf( + temp_str, + "PPL: %s\n", + (nfcv_data->security_status[0] & NfcVLockBitPpl) ? "locked" : "not locked"); + furi_string_cat_printf(temp_str, "Prot.ptr %02X\n", nfcv_data->sub_data.slix.pp_pointer); + furi_string_cat_printf(temp_str, "Prot.con %02X\n", nfcv_data->sub_data.slix.pp_condition); + } +} + void nfc_scene_nfc_data_info_on_enter(void* context) { Nfc* nfc = context; Widget* widget = nfc->widget; @@ -76,95 +153,25 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { } furi_string_cat_printf(temp_str, "\n"); - furi_string_cat_printf( - temp_str, - "DSFID: %02X %s\n", - nfcv_data->dsfid, - (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); - furi_string_cat_printf( - temp_str, - "AFI: %02X %s\n", - nfcv_data->afi, - (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); - furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); - furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); - furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + furi_string_cat_printf(temp_str, "IC Ref: %d\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %d\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %d\n", nfcv_data->block_size); switch(dev_data->nfcv_data.sub_type) { case NfcVTypePlain: furi_string_cat_printf(temp_str, "Type: Plain\n"); break; case NfcVTypeSlix: - furi_string_cat_printf(temp_str, "Type: SLIX\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix, "SLIX"); break; case NfcVTypeSlixS: - furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Read %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); - furi_string_cat_printf( - temp_str, - " Write %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixS, "SLIX-S"); break; case NfcVTypeSlixL: - furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixL, "SLIX-L"); break; case NfcVTypeSlix2: - furi_string_cat_printf(temp_str, "Type: SLIX2\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Read %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); - furi_string_cat_printf( - temp_str, - " Write %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix2, "SLIX2"); break; default: furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c index bdf7692cc..04e60611d 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c @@ -16,7 +16,6 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) { Nfc* nfc = context; NfcDeviceData* dev_data = &nfc->dev->dev_data; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; // Setup view Widget* widget = nfc->widget; widget_add_button_element( @@ -46,13 +45,12 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); break; } - furi_string_cat_printf(temp_str, "UID:"); + furi_string_cat_printf(temp_str, "UID:\n"); for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); } furi_string_cat_printf(temp_str, "\n"); - furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); - furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + furi_string_cat_printf(temp_str, "(see More->Info for details)\n"); widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 749c12c7d..25adb878b 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -51,8 +51,16 @@ struct DigitalSignalInternals { #define T_TIM 1562 /* 15.625 ns *100 */ #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ +/* end marker in DMA ringbuffer, will get written into timer register at the end */ +#define SEQ_TIMER_MAX 0xFFFFFFFF + +/* time to wait in loops before returning */ +#define SEQ_LOCK_WAIT_MS 10UL +#define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64) + /* maximum entry count of the sequence dma ring buffer */ -#define SEQUENCE_DMA_RINGBUFFER_SIZE 32 +#define RINGBUFFER_SIZE 128 + /* maximum number of DigitalSignals in a sequence */ #define SEQUENCE_SIGNALS_SIZE 32 /* @@ -214,12 +222,12 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { for(size_t pos = 0; pos < signal->edge_cnt; pos++) { uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder; if(pulse_duration < 10 || pulse_duration > 10000000) { - /*FURI_LOG_D( + FURI_LOG_D( TAG, "[prepare] pulse_duration out of range: %lu = %lu * %llu", pulse_duration, signal->edge_timings[pos], - internals->factor);*/ + internals->factor); pulse_duration = 100; } uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM; @@ -243,20 +251,16 @@ static void digital_signal_stop_timer() { LL_TIM_DisableUpdateEvent(TIM2); LL_TIM_DisableDMAReq_UPDATE(TIM2); - if(furi_hal_bus_is_enabled(FuriHalBusTIM2)) { - furi_hal_bus_disable(FuriHalBusTIM2); - } + furi_hal_bus_disable(FuriHalBusTIM2); } static void digital_signal_setup_timer() { - if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) { - furi_hal_bus_enable(FuriHalBusTIM2); - } + furi_hal_bus_enable(FuriHalBusTIM2); LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); - LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); + LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX); LL_TIM_SetCounter(TIM2, 0); } @@ -339,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { sequence->bake = false; sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer)); - sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE; + sequence->dma_buffer->size = RINGBUFFER_SIZE; sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t)); sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -458,42 +462,26 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { return ret; } -static void digital_sequence_update_pos(DigitalSequence* sequence) { - struct ReloadBuffer* dma_buffer = sequence->dma_buffer; - - dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); -} - -static const uint32_t wait_ms = 10; -static const uint32_t wait_ticks = wait_ms * 1000 * 64; - static void digital_sequence_finish(DigitalSequence* sequence) { struct ReloadBuffer* dma_buffer = sequence->dma_buffer; if(dma_buffer->dma_active) { uint32_t prev_timer = DWT->CYCCNT; - uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; do { - uint32_t last_pos = dma_buffer->read_pos; - - digital_sequence_update_pos(sequence); - - /* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */ - if(dma_buffer->read_pos == end_pos) { + /* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */ + if(TIM2->ARR == SEQ_TIMER_MAX) { break; } - - if(last_pos != dma_buffer->read_pos) { //-V547 - prev_timer = DWT->CYCCNT; - } - if(DWT->CYCCNT - prev_timer > wait_ticks) { - /*FURI_LOG_D( + if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { + dma_buffer->read_pos = + RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); + FURI_LOG_D( TAG, "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", - wait_ms, + SEQ_LOCK_WAIT_MS, TIM2->ARR, dma_buffer->read_pos, - dma_buffer->write_pos);*/ + dma_buffer->write_pos); break; } } while(1); @@ -508,34 +496,42 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len if(dma_buffer->dma_active) { uint32_t prev_timer = DWT->CYCCNT; - uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; do { - uint32_t last_pos = dma_buffer->read_pos; - digital_sequence_update_pos(sequence); + dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); - if(dma_buffer->read_pos != end_pos) { + uint32_t free = + (RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE; + + if(free > 2) { break; } - if(last_pos != dma_buffer->read_pos) { //-V547 - prev_timer = DWT->CYCCNT; - } - if(DWT->CYCCNT - prev_timer > wait_ticks) { - /*FURI_LOG_D( + if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { + FURI_LOG_D( TAG, "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", - wait_ms, + SEQ_LOCK_WAIT_MS, TIM2->ARR, dma_buffer->read_pos, - dma_buffer->write_pos);*/ + dma_buffer->write_pos); + break; + } + if(TIM2->ARR == SEQ_TIMER_MAX) { + FURI_LOG_D( + TAG, + "[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)", + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); break; } } while(1); } dma_buffer->buffer[dma_buffer->write_pos] = length; - dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; - dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF; + dma_buffer->write_pos++; + dma_buffer->write_pos %= RINGBUFFER_SIZE; + dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX; } bool digital_sequence_send(DigitalSequence* sequence) { @@ -557,90 +553,97 @@ bool digital_sequence_send(DigitalSequence* sequence) { return true; } - int32_t remainder = 0; - bool traded_first = false; + if(!sequence->sequence_used) { + return false; + } - FURI_CRITICAL_ENTER(); + int32_t remainder = 0; + uint32_t trade_for_next = 0; + uint32_t seq_pos_next = 1; dma_buffer->dma_active = false; - dma_buffer->buffer[0] = 0xFFFFFFFF; + dma_buffer->buffer[0] = SEQ_TIMER_MAX; dma_buffer->read_pos = 0; dma_buffer->write_pos = 0; - for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) { - uint8_t signal_index = sequence->sequence[seq_pos]; - DigitalSignal* sig = sequence->signals[signal_index]; - bool last_signal = ((seq_pos + 1) == sequence->sequence_used); + /* already prepare the current signal pointer */ + DigitalSignal* sig = sequence->signals[sequence->sequence[0]]; + DigitalSignal* sig_next = NULL; + /* re-use the GPIO buffer from the first signal */ + sequence->gpio_buff = sig->internals->gpio_buff; - /* all signals are prepared and we can re-use the GPIO buffer from the fist signal */ - if(seq_pos == 0) { - sequence->gpio_buff = sig->internals->gpio_buff; + FURI_CRITICAL_ENTER(); + + while(sig) { + bool last_signal = (seq_pos_next >= sequence->sequence_used); + + if(!last_signal) { + sig_next = sequence->signals[sequence->sequence[seq_pos_next++]]; } for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) { - if(traded_first) { - traded_first = false; - continue; - } - uint32_t pulse_length = 0; - bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries); + bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries); + uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next; - pulse_length = sig->reload_reg_buff[pulse_pos]; + trade_for_next = 0; /* when we are too late more than half a tick, make the first edge temporarily longer */ if(remainder >= T_TIM_DIV2) { remainder -= T_TIM; pulse_length += 1; } - remainder += sig->internals->reload_reg_remainder; - /* last pulse in that signal and have a next signal? */ - if(last_pulse) { - if((seq_pos + 1) < sequence->sequence_used) { - DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]]; + /* last pulse in current signal and have a next signal? */ + if(last_pulse && sig_next) { + /* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse. + beware, we do not want the level after the last edge, but the last level before that edge */ + bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); - /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ - /* beware, we do not want the level after the last edge, but the last level before that edge */ - bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); - - /* take from the next, add it to the current if they have the same level */ - if(end_level == sig_next->start_level) { - pulse_length += sig_next->reload_reg_buff[0]; - traded_first = true; - } + /* if they have the same level, pass the duration to the next pulse(s) */ + if(end_level == sig_next->start_level) { + trade_for_next = pulse_length; } } - digital_sequence_queue_pulse(sequence, pulse_length); + /* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */ + if(!trade_for_next) { + digital_sequence_queue_pulse(sequence, pulse_length); - /* start transmission when buffer was filled enough */ - bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4); + if(!dma_buffer->dma_active) { + /* start transmission when buffer was filled enough */ + bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2); - /* or it was the last pulse */ - if(last_pulse && last_signal) { - start_send = true; - } + /* or it was the last pulse */ + if(last_pulse && last_signal) { + start_send = true; + } - /* start transmission */ - if(start_send && !dma_buffer->dma_active) { - digital_sequence_setup_dma(sequence); - digital_signal_setup_timer(); + /* start transmission */ + if(start_send) { + digital_sequence_setup_dma(sequence); + digital_signal_setup_timer(); - /* if the send time is specified, wait till the core timer passed beyond that time */ - if(sequence->send_time_active) { - sequence->send_time_active = false; - while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + /* if the send time is specified, wait till the core timer passed beyond that time */ + if(sequence->send_time_active) { + sequence->send_time_active = false; + while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + } + } + digital_signal_start_timer(); + dma_buffer->dma_active = true; } } - digital_signal_start_timer(); - dma_buffer->dma_active = true; } } + + remainder += sig->internals->reload_reg_remainder; + sig = sig_next; + sig_next = NULL; } /* wait until last dma transaction was finished */ - digital_sequence_finish(sequence); FURI_CRITICAL_EXIT(); + digital_sequence_finish(sequence); return true; } diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 6db695768..cd9eb336c 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -657,178 +657,167 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } -static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { +static bool nfc_device_save_slix_data( + FlipperFormat* file, + NfcDevice* dev, + SlixTypeFeatures features, + const char* type) { bool saved = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; do { - if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + char msg[64]; + snprintf(msg, sizeof(msg), "%s specific data", type); + if(!flipper_format_write_comment_cstr(file, msg)) break; + if(!flipper_format_write_comment_cstr( + file, "Passwords are optional. If password is omitted, any password is accepted")) break; + + if(features & SlixFeatureRead) { + if(data->flags & NfcVSlixDataFlagsHasKeyRead) { + if(!flipper_format_write_hex( + file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + } + } + if(features & SlixFeatureWrite) { + if(data->flags & NfcVSlixDataFlagsHasKeyWrite) { + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + } + } + if(features & SlixFeaturePrivacy) { + if(data->flags & NfcVSlixDataFlagsHasKeyPrivacy) { + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + } + } + if(features & SlixFeatureDestroy) { + if(data->flags & NfcVSlixDataFlagsHasKeyDestroy) { + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + } + } + if(features & SlixFeatureEas) { + if(data->flags & NfcVSlixDataFlagsHasKeyEas) { + if(!flipper_format_write_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + } + } + if(features & SlixFeatureSignature) { + if(!flipper_format_write_comment_cstr( + file, + "This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.")) + break; + if(!flipper_format_write_hex( + file, "Signature", data->signature, sizeof(data->signature))) + break; + } + if(features & SlixFeaturePrivacy) { + bool privacy = (data->flags & NfcVSlixDataFlagsPrivacy) ? true : false; + if(!flipper_format_write_bool(file, "Privacy Mode", &privacy, 1)) break; + } + if(features & SlixFeatureProtection) { + if(!flipper_format_write_comment_cstr(file, "Protection pointer configuration")) break; + if(!flipper_format_write_hex(file, "Protection pointer", &data->pp_pointer, 1)) break; + if(!flipper_format_write_hex(file, "Protection condition", &data->pp_condition, 1)) + break; + } saved = true; } while(false); return saved; } -bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { +bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev, SlixTypeFeatures features) { bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; memset(data, 0, sizeof(NfcVSlixData)); do { - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; - if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_write_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_read_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; - - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; - - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; - if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_write_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524 - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_read_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + data->flags = 0; + if(features & SlixFeatureRead) { + if(flipper_format_key_exist(file, "Password Read")) { + if(!flipper_format_read_hex( + file, "Password Read", data->key_read, sizeof(data->key_read))) { + FURI_LOG_D(TAG, "Failed reading Password Read"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyRead; + } + } + if(features & SlixFeatureWrite) { + if(flipper_format_key_exist(file, "Password Write")) { + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) { + FURI_LOG_D(TAG, "Failed reading Password Write"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyWrite; + } + } + if(features & SlixFeaturePrivacy) { + if(flipper_format_key_exist(file, "Password Privacy")) { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) { + FURI_LOG_D(TAG, "Failed reading Password Privacy"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyPrivacy; + } + } + if(features & SlixFeatureDestroy) { + if(flipper_format_key_exist(file, "Password Destroy")) { + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) { + FURI_LOG_D(TAG, "Failed reading Password Destroy"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyDestroy; + } + } + if(features & SlixFeatureEas) { + if(flipper_format_key_exist(file, "Password EAS")) { + if(!flipper_format_read_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) { + FURI_LOG_D(TAG, "Failed reading Password EAS"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyEas; + } + } + if(features & SlixFeatureSignature) { + if(!flipper_format_read_hex( + file, "Signature", data->signature, sizeof(data->signature))) { + FURI_LOG_D(TAG, "Failed reading Signature"); + break; + } + } + if(features & SlixFeaturePrivacy) { + bool privacy; + if(!flipper_format_read_bool(file, "Privacy Mode", &privacy, 1)) { + FURI_LOG_D(TAG, "Failed reading Privacy Mode"); + break; + } + if(privacy) { + data->flags |= NfcVSlixDataFlagsPrivacy; + } + } + if(features & SlixFeatureProtection) { + if(!flipper_format_read_hex(file, "Protection pointer", &(data->pp_pointer), 1)) { + FURI_LOG_D(TAG, "Failed reading Protection pointer"); + break; + } + if(!flipper_format_read_hex(file, "Protection condition", &(data->pp_condition), 1)) { + FURI_LOG_D(TAG, "Failed reading Protection condition"); + break; + } + } parsed = true; } while(false); @@ -859,7 +848,8 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { file, "Data Content", data->data, data->block_num * data->block_size)) break; if(!flipper_format_write_comment_cstr( - file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) + file, + "First byte: DSFID (0x01) / AFI (0x02) / EAS (0x04) / PPL (0x08) lock info, others: block lock info")) break; if(!flipper_format_write_hex( file, "Security Status", data->security_status, 1 + data->block_num)) @@ -877,16 +867,16 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { saved = true; break; case NfcVTypeSlix: - saved = nfc_device_save_slix_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix, "SLIX"); break; case NfcVTypeSlixS: - saved = nfc_device_save_slix_s_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixS, "SLIX-S"); break; case NfcVTypeSlixL: - saved = nfc_device_save_slix_l_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixL, "SLIX-L"); break; case NfcVTypeSlix2: - saved = nfc_device_save_slix2_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix2, "SLIX2"); break; default: break; @@ -906,23 +896,45 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { uint32_t temp_uint32 = 0; uint8_t temp_value = 0; - if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; - if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; - if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; - if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; - data->block_num = temp_uint32; - if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; - if(!flipper_format_read_hex( - file, "Data Content", data->data, data->block_num * data->block_size)) + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) { + FURI_LOG_D(TAG, "Failed reading DSFID"); break; + } + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) { + FURI_LOG_D(TAG, "Failed reading AFI"); + break; + } + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) { + FURI_LOG_D(TAG, "Failed reading IC Reference"); + break; + } + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) { + FURI_LOG_D(TAG, "Failed reading Block Count"); + break; + } + data->block_num = temp_uint32; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) { + FURI_LOG_D(TAG, "Failed reading Block Size"); + break; + } + if(!flipper_format_read_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) { + FURI_LOG_D(TAG, "Failed reading Data Content"); + break; + } /* optional, as added later */ if(flipper_format_key_exist(file, "Security Status")) { if(!flipper_format_read_hex( - file, "Security Status", data->security_status, 1 + data->block_num)) + file, "Security Status", data->security_status, 1 + data->block_num)) { + FURI_LOG_D(TAG, "Failed reading Security Status"); break; + } + } + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) { + FURI_LOG_D(TAG, "Failed reading Subtype"); + break; } - if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; data->sub_type = temp_value; switch(data->sub_type) { @@ -930,16 +942,16 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { parsed = true; break; case NfcVTypeSlix: - parsed = nfc_device_load_slix_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix); break; case NfcVTypeSlixS: - parsed = nfc_device_load_slix_s_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixS); break; case NfcVTypeSlixL: - parsed = nfc_device_load_slix_l_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixL); break; case NfcVTypeSlix2: - parsed = nfc_device_load_slix2_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix2); break; default: break; diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 3c37153d8..017b06cae 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -149,12 +149,18 @@ bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* n return false; } + /* clear all know sub type data before reading them */ + memset(&nfcv_data->sub_data, 0x00, sizeof(nfcv_data->sub_data)); + if(slix_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX detected"); nfcv_data->sub_type = NfcVTypeSlix; } else if(slix2_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX2 detected"); nfcv_data->sub_type = NfcVTypeSlix2; + if(slix2_read_custom(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } } else if(slix_s_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX-S detected"); nfcv_data->sub_type = NfcVTypeSlixS; @@ -612,9 +618,34 @@ void nfcv_emu_handle_packet( if(ctx->flags & NFCV_REQ_FLAG_AFI) { uint8_t afi = nfcv_data->frame[ctx->payload_offset]; - if(afi == nfcv_data->afi) { - respond = true; + + uint8_t family = (afi & 0xF0); + uint8_t subfamily = (afi & 0x0F); + + if(family) { + if(subfamily) { + /* selected family and subfamily only */ + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + /* selected family, any subfamily */ + if(family == (nfcv_data->afi & 0xf0)) { + respond = true; + } + } + } else { + if(subfamily) { + /* proprietary subfamily only */ + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + /* all families and subfamilies */ + respond = true; + } } + } else { respond = true; } @@ -740,13 +771,19 @@ void nfcv_emu_handle_packet( case NFCV_CMD_READ_MULTI_BLOCK: case NFCV_CMD_READ_BLOCK: { uint8_t block = nfcv_data->frame[ctx->payload_offset]; - uint8_t blocks = 1; + int blocks = 1; if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; } - if(block + blocks <= nfcv_data->block_num) { + /* limit the maximum block count, underflow accepted */ + if(block + blocks > nfcv_data->block_num) { + blocks = nfcv_data->block_num - block; + } + + /* only respond with the valid blocks, if there are any */ + if(blocks > 0) { uint8_t buffer_pos = 0; ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; @@ -773,10 +810,13 @@ void nfcv_emu_handle_packet( ctx->response_flags, ctx->send_time); } else { - ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; - ctx->response_buffer[1] = NFCV_ERROR_GENERIC; - nfcv_emu_send( - tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + /* reply with an error only in addressed or selected mode */ + if(ctx->addressed || ctx->selected) { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } } snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 87a696737..e4139de99 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -139,8 +139,10 @@ typedef enum { } NfcVErrorcodes; typedef enum { - NfcVLockBitDsfid = 1, - NfcVLockBitAfi = 2, + NfcVLockBitDsfid = 1 << 0, + NfcVLockBitAfi = 1 << 1, + NfcVLockBitEas = 1 << 2, + NfcVLockBitPpl = 1 << 3, } NfcVLockBits; typedef enum { @@ -168,14 +170,55 @@ typedef enum { NfcVSendFlagsHighRate = 1 << 4 } NfcVSendFlags; +/* SLIX specific config flags */ +typedef enum { + NfcVSlixDataFlagsNone = 0, + NfcVSlixDataFlagsHasKeyRead = 1 << 0, + NfcVSlixDataFlagsHasKeyWrite = 1 << 1, + NfcVSlixDataFlagsHasKeyPrivacy = 1 << 2, + NfcVSlixDataFlagsHasKeyDestroy = 1 << 3, + NfcVSlixDataFlagsHasKeyEas = 1 << 4, + NfcVSlixDataFlagsValidKeyRead = 1 << 8, + NfcVSlixDataFlagsValidKeyWrite = 1 << 9, + NfcVSlixDataFlagsValidKeyPrivacy = 1 << 10, + NfcVSlixDataFlagsValidKeyDestroy = 1 << 11, + NfcVSlixDataFlagsValidKeyEas = 1 << 12, + NfcVSlixDataFlagsPrivacy = 1 << 16, + NfcVSlixDataFlagsDestroyed = 1 << 17 +} NfcVSlixDataFlags; + +/* abstract the file read/write operations for all SLIX types to reduce duplicated code */ +typedef enum { + SlixFeatureRead = 1 << 0, + SlixFeatureWrite = 1 << 1, + SlixFeaturePrivacy = 1 << 2, + SlixFeatureDestroy = 1 << 3, + SlixFeatureEas = 1 << 4, + SlixFeatureSignature = 1 << 5, + SlixFeatureProtection = 1 << 6, + + SlixFeatureSlix = SlixFeatureEas, + SlixFeatureSlixS = + (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy | + SlixFeatureEas), + SlixFeatureSlixL = (SlixFeaturePrivacy | SlixFeatureDestroy | SlixFeatureEas), + SlixFeatureSlix2 = + (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy | + SlixFeatureEas | SlixFeatureSignature | SlixFeatureProtection), +} SlixTypeFeatures; + typedef struct { + uint32_t flags; uint8_t key_read[4]; uint8_t key_write[4]; uint8_t key_privacy[4]; uint8_t key_destroy[4]; uint8_t key_eas[4]; uint8_t rand[2]; - bool privacy; + uint8_t signature[32]; + /* SLIX2 options */ + uint8_t pp_pointer; + uint8_t pp_condition; } NfcVSlixData; typedef union { diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index 1c14c0bf9..9f8ffa143 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -9,6 +9,120 @@ #define TAG "SLIX" +ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + uint8_t rxBuf[32]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read NXP SYSTEM INFORMATION..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + uint8_t cmd[] = {}; + uint8_t uid[NFCV_UID_LENGTH]; + + /* UID is stored reversed in requests */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + uid, + cmd, + sizeof(cmd), + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret != ERR_NONE || received != 8) { + FURI_LOG_D(TAG, "Failed: %d, %d", ret, received); + return ret; + } + FURI_LOG_D(TAG, "Success..."); + + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + slix->pp_pointer = rxBuf[1]; + slix->pp_condition = rxBuf[2]; + + /* convert NXP's to our internal lock bits format */ + nfcv_data->security_status[0] = 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitDsfid) ? NfcVLockBitDsfid : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitAfi) ? NfcVLockBitAfi : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitEas) ? NfcVLockBitEas : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitPpl) ? NfcVLockBitPpl : 0; + + return ERR_NONE; +} + +ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + uint8_t rxBuf[64]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read SIGNATURE..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + uint8_t cmd[] = {}; + uint8_t uid[NFCV_UID_LENGTH]; + + /* UID is stored reversed in requests */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_READ_SIGNATURE, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + uid, + cmd, + sizeof(cmd), + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret != ERR_NONE || received != 33) { + FURI_LOG_D(TAG, "Failed: %d, %d", ret, received); + return ret; + } + FURI_LOG_D(TAG, "Success..."); + + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + memcpy(slix->signature, &rxBuf[1], 32); + + return ERR_NONE; +} + +ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + ReturnCode ret = ERR_NONE; + + ret = slix2_read_nxp_sysinfo(nfc_data, nfcv_data); + if(ret != ERR_NONE) { + return ret; + } + ret = slix2_read_signature(nfc_data, nfcv_data); + + return ret; +} + static uint32_t slix_read_be(uint8_t* data, uint32_t length) { uint32_t value = 0; @@ -137,6 +251,43 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { return ret; } +static void slix_generic_pass_infos( + uint8_t password_id, + NfcVSlixData* slix, + uint8_t** password, + uint32_t* flag_valid, + uint32_t* flag_set) { + switch(password_id) { + case SLIX_PASS_READ: + *password = slix->key_read; + *flag_valid = NfcVSlixDataFlagsValidKeyRead; + *flag_set = NfcVSlixDataFlagsHasKeyRead; + break; + case SLIX_PASS_WRITE: + *password = slix->key_write; + *flag_valid = NfcVSlixDataFlagsValidKeyWrite; + *flag_set = NfcVSlixDataFlagsHasKeyWrite; + break; + case SLIX_PASS_PRIVACY: + *password = slix->key_privacy; + *flag_valid = NfcVSlixDataFlagsValidKeyPrivacy; + *flag_set = NfcVSlixDataFlagsHasKeyPrivacy; + break; + case SLIX_PASS_DESTROY: + *password = slix->key_destroy; + *flag_valid = NfcVSlixDataFlagsValidKeyDestroy; + *flag_set = NfcVSlixDataFlagsHasKeyDestroy; + break; + case SLIX_PASS_EASAFI: + *password = slix->key_eas; + *flag_valid = NfcVSlixDataFlagsValidKeyEas; + *flag_set = NfcVSlixDataFlagsHasKeyEas; + break; + default: + break; + } +} + bool slix_generic_protocol_filter( FuriHalNfcTxRxContext* tx_rx, FuriHalNfcDevData* nfc_data, @@ -150,7 +301,8 @@ bool slix_generic_protocol_filter( NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; NfcVSlixData* slix = &nfcv_data->sub_data.slix; - if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && + if((slix->flags & NfcVSlixDataFlagsPrivacy) && + ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { snprintf( nfcv_data->last_command, @@ -186,66 +338,73 @@ bool slix_generic_protocol_filter( } case NFCV_CMD_NXP_SET_PASSWORD: { + /* the password to be set is the first parameter */ uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + /* right after that is the XORed password */ + uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + /* only handle if the password type is supported */ if(!(password_id & password_supported)) { break; } - uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + /* fetch the last RAND value */ uint8_t* rand = slix->rand; - uint8_t* password = NULL; + + /* first calc the password that has been sent */ uint8_t password_rcv[4]; - - switch(password_id) { - case SLIX_PASS_READ: - password = slix->key_read; - break; - case SLIX_PASS_WRITE: - password = slix->key_write; - break; - case SLIX_PASS_PRIVACY: - password = slix->key_privacy; - break; - case SLIX_PASS_DESTROY: - password = slix->key_destroy; - break; - case SLIX_PASS_EASAFI: - password = slix->key_eas; - break; - default: - break; + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; } + uint32_t pass_received = slix_read_be(password_rcv, 4); + /* then determine the password type (or even update if not set yet) */ + uint8_t* password = NULL; + uint32_t flag_valid = 0; + uint32_t flag_set = 0; + + slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set); + + /* when the password is not supported, return silently */ if(!password) { break; } - for(int pos = 0; pos < 4; pos++) { - password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; - } - uint32_t pass_expect = slix_read_be(password, 4); - uint32_t pass_received = slix_read_be(password_rcv, 4); + /* check if the password is known */ + bool pass_valid = false; + uint32_t pass_expect = 0; - /* if the password is all-zeroes, just accept any password*/ - if(!pass_expect || pass_expect == pass_received) { + if(slix->flags & flag_set) { + /* if so, fetch the stored password and compare */ + pass_expect = slix_read_be(password, 4); + pass_valid = (pass_expect == pass_received); + } else { + /* if not known, just accept it and store that password */ + memcpy(password, password_rcv, 4); + nfcv_data->modified = true; + slix->flags |= flag_set; + + pass_valid = true; + } + + /* if the pass was valid or accepted for other reasons, continue */ + if(pass_valid) { + slix->flags |= flag_valid; + + /* handle actions when a correct password was given, aside of setting the flag */ switch(password_id) { - case SLIX_PASS_READ: - break; - case SLIX_PASS_WRITE: - break; case SLIX_PASS_PRIVACY: - slix->privacy = false; + slix->flags &= ~NfcVSlixDataFlagsPrivacy; nfcv_data->modified = true; break; case SLIX_PASS_DESTROY: + slix->flags |= NfcVSlixDataFlagsDestroyed; FURI_LOG_D(TAG, "Pooof! Got destroyed"); break; - case SLIX_PASS_EASAFI: - break; default: break; } + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); @@ -268,6 +427,49 @@ bool slix_generic_protocol_filter( break; } + case NFCV_CMD_NXP_WRITE_PASSWORD: { + uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + + if(!(password_id & password_supported)) { + break; + } + + uint8_t* new_password = &nfcv_data->frame[ctx->payload_offset + 1]; + uint8_t* password = NULL; + uint32_t flag_valid = 0; + uint32_t flag_set = 0; + + slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set); + + /* when the password is not supported, return silently */ + if(!password) { + break; + } + + bool pass_valid = (slix->flags & flag_valid); + if(!(slix->flags & flag_set)) { + pass_valid = true; + } + + if(pass_valid) { + slix->flags |= flag_valid; + slix->flags |= flag_set; + + memcpy(password, new_password, 4); + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD OK"); + } else { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD FAIL"); + } + handled = true; + break; + } + case NFCV_CMD_NXP_ENABLE_PRIVACY: { ctx->response_buffer[0] = NFCV_NOERROR; @@ -278,7 +480,7 @@ bool slix_generic_protocol_filter( sizeof(nfcv_data->last_command), "NFCV_CMD_NXP_ENABLE_PRIVACY"); - slix->privacy = true; + slix->flags |= NfcVSlixDataFlagsPrivacy; handled = true; break; } @@ -315,7 +517,10 @@ void slix_l_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_l_protocol_filter; @@ -345,7 +550,10 @@ void slix_s_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_s_protocol_filter; @@ -375,7 +583,10 @@ void slix_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_protocol_filter; @@ -389,6 +600,10 @@ bool slix2_protocol_filter( // -V524 furi_assert(nfc_data); furi_assert(nfcv_data_in); + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + bool handled = false; /* many SLIX share some of the functions, place that in a generic handler */ @@ -396,6 +611,157 @@ bool slix2_protocol_filter( // -V524 return true; } + switch(ctx->command) { + /* override WRITE BLOCK for block 79 (16 bit counter) */ + case NFCV_CMD_WRITE_BLOCK: + case NFCV_CMD_WRITE_MULTI_BLOCK: { + uint8_t resp_len = 1; + uint8_t blocks = 1; + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t data_pos = ctx->payload_offset + 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[data_pos] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[data_pos]; + uint32_t data_len = nfcv_data->block_size * blocks; + + if((block + blocks) <= nfcv_data->block_num && + (data_pos + data_len + 2) == nfcv_data->frame_length) { + ctx->response_buffer[0] = NFCV_NOERROR; + + for(int block_num = block; block_num < block + blocks; block_num++) { + /* special case, 16-bit counter */ + if(block_num == 79) { + uint32_t dest; + uint32_t ctr_old; + + memcpy(&dest, &nfcv_data->frame[data_pos], 4); + memcpy(&ctr_old, &nfcv_data->data[nfcv_data->block_size * block_num], 4); + + uint32_t ctr_new = ctr_old; + bool allowed = true; + + /* increment counter */ + if(dest == 1) { + ctr_new = (ctr_old & 0xFFFF0000) | ((ctr_old + 1) & 0xFFFF); + + /* protection flag set? */ + if(ctr_old & 0x01000000) { + allowed = nfcv_data->sub_data.slix.flags & + NfcVSlixDataFlagsValidKeyRead; + } + } else { + ctr_new = dest; + allowed = nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsValidKeyWrite; + } + + if(allowed) { + memcpy(&nfcv_data->data[nfcv_data->block_size * block_num], &ctr_new, 4); + } else { + /* incorrect read or write password */ + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + resp_len = 2; + } + } else { + memcpy( + &nfcv_data->data[nfcv_data->block_size * block_num], + &nfcv_data->frame[data_pos], + nfcv_data->block_size); + } + data_pos += nfcv_data->block_size; + } + nfcv_data->modified = true; + + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + resp_len = 2; + } + + bool respond = (ctx->response_buffer[0] == NFCV_NOERROR) || + (ctx->addressed || ctx->selected); + + if(respond) { + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + resp_len, + ctx->response_flags, + ctx->send_time); + } + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE MULTI BLOCK %d, %d blocks", + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE BLOCK %d <- %02X %02X %02X %02X", + block, + data[0], + data[1], + data[2], + data[3]); + } + handled = true; + break; + } + + case NFCV_CMD_NXP_READ_SIGNATURE: { + uint32_t len = 0; + ctx->response_buffer[len++] = NFCV_NOERROR; + memcpy(&ctx->response_buffer[len], slix->signature, sizeof(slix->signature)); + len += sizeof(slix->signature); + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_SIGNATURE"); + + handled = true; + break; + } + + case NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION: { + uint32_t len = 0; + uint8_t lock_bits = 0; + + /* convert our internal lock bits format into NXP's */ + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? SlixLockBitDsfid : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitAfi) ? SlixLockBitAfi : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitEas) ? SlixLockBitEas : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitPpl) ? SlixLockBitPpl : 0; + + ctx->response_buffer[len++] = NFCV_NOERROR; + ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_pointer; + ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_condition; + ctx->response_buffer[len++] = lock_bits; + ctx->response_buffer[len++] = 0x7F; /* features LSB */ + ctx->response_buffer[len++] = 0x35; /* features */ + ctx->response_buffer[len++] = 0; /* features */ + ctx->response_buffer[len++] = 0; /* features MSB */ + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "GET_NXP_SYSTEM_INFORMATION"); + + handled = true; + break; + } + } + return handled; } @@ -405,7 +771,10 @@ void slix2_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix2_protocol_filter; diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h index 701fa2f82..67f09e46d 100644 --- a/lib/nfc/protocols/slix.h +++ b/lib/nfc/protocols/slix.h @@ -8,19 +8,35 @@ #define NFCV_MANUFACTURER_NXP 0x04 /* ISO15693-3 CUSTOM NXP COMMANDS */ -#define NFCV_CMD_NXP_SET_EAS 0xA2 -#define NFCV_CMD_NXP_RESET_EAS 0xA3 -#define NFCV_CMD_NXP_LOCK_EAS 0xA4 -#define NFCV_CMD_NXP_EAS_ALARM 0xA5 -#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 -#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 -#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 -#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 -#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 -#define NFCV_CMD_NXP_SET_PASSWORD 0xB3 -#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 -#define NFCV_CMD_NXP_DESTROY 0xB9 -#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA +typedef enum { + NFCV_CMD_NXP_SET_EAS = 0xA2, + NFCV_CMD_NXP_RESET_EAS = 0xA3, + NFCV_CMD_NXP_LOCK_EAS = 0xA4, + NFCV_CMD_NXP_EAS_ALARM = 0xA5, + NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI = 0xA6, + NFCV_CMD_NXP_WRITE_EAS_ID = 0xA7, + NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION = 0xAB, + NFCV_CMD_NXP_INVENTORY_PAGE_READ = 0xB0, + NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST = 0xB1, + NFCV_CMD_NXP_GET_RANDOM_NUMBER = 0xB2, + NFCV_CMD_NXP_SET_PASSWORD = 0xB3, + NFCV_CMD_NXP_WRITE_PASSWORD = 0xB4, + NFCV_CMD_NXP_64_BIT_PASSWORD_PROTECTION = 0xB5, + NFCV_CMD_NXP_PROTECT_PAGE = 0xB6, + NFCV_CMD_NXP_LOCK_PAGE_PROTECTION_CONDITION = 0xB7, + NFCV_CMD_NXP_DESTROY = 0xB9, + NFCV_CMD_NXP_ENABLE_PRIVACY = 0xBA, + NFCV_CMD_NXP_STAY_QUIET_PERSISTENT = 0xBC, + NFCV_CMD_NXP_READ_SIGNATURE = 0xBD +} SlixCommands; + +/* lock bit bits used in SLIX's NXP SYSTEM INFORMATION response */ +typedef enum { + SlixLockBitAfi = 1 << 0, + SlixLockBitEas = 1 << 1, + SlixLockBitDsfid = 1 << 2, + SlixLockBitPpl = 1 << 3, +} SlixLockBits; /* available passwords */ #define SLIX_PASS_READ 0x01 @@ -37,6 +53,10 @@ bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); +ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); + ReturnCode slix_get_random(NfcVData* data); ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); From b92f7c669b7a166a62b587e62b90f5a4ec9e0378 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 24 Jun 2023 22:11:25 +0300 Subject: [PATCH 252/370] Update changelog --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4249c94f..04f95fbee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ ## New changes -* Infrared: Updated universal remote asstes (by @amec0e | PR #522) +* Plugins: Fix furi_hal_bus issues in AVR Programmer and Signal Generator (fixes issue #525) +* Plugins: USB / BLE Remote - Updated UI in keynote vertical and numpad (by @gid9798 | PR #524) +* Plugins: Update TOTP [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) * Plugins: Fixed ESP32 WiFi Marauder crashes when reopening app +* Infrared: Updated universal remote asstes (by @amec0e | PR #522) +* OFW PR 2783: SLIX2 emulation support / practical use for Dymo printers (by @g3gg0) +* OFW PR 2782: NFC: Fix key invalidation logic (by @AloneLiberty) * OFW: Debug: sync apps on attach, makes it possible to debug already started app that has crashed ---- From fef90f1ec545dddd43e8a0731deb81aad4b77d7d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 25 Jun 2023 00:31:08 +0300 Subject: [PATCH 253/370] Unitemp SCD40 support by divinebird --- applications/external/unitemp/Sensors.c | 2 +- .../external/unitemp/interfaces/endianness.h | 60 ++++ applications/external/unitemp/sensors/SCD30.c | 53 +--- applications/external/unitemp/sensors/SCD40.c | 291 ++++++++++++++++++ applications/external/unitemp/sensors/SCD40.h | 59 ++++ 5 files changed, 412 insertions(+), 53 deletions(-) create mode 100644 applications/external/unitemp/interfaces/endianness.h create mode 100644 applications/external/unitemp/sensors/SCD40.c create mode 100644 applications/external/unitemp/sensors/SCD40.h diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c index ae90ce4d5..f96401ea2 100644 --- a/applications/external/unitemp/Sensors.c +++ b/applications/external/unitemp/Sensors.c @@ -79,7 +79,7 @@ static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT &Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10, &SHT30, &GXHT30, &LM75, &HDC1080, &BMP180, &BMP280, &BME280, &BME680, &MAX31855, &MAX6675, - &SCD30}; + &SCD30, &SCD40}; const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) { if(index > SENSOR_TYPES_COUNT) return NULL; diff --git a/applications/external/unitemp/interfaces/endianness.h b/applications/external/unitemp/interfaces/endianness.h new file mode 100644 index 000000000..c4a3f4b87 --- /dev/null +++ b/applications/external/unitemp/interfaces/endianness.h @@ -0,0 +1,60 @@ +// +// Created by Avilov Vasily on 10.06.2023. +// + +#ifndef FLIPPERZERO_FIRMWARE_ENDIANNESS_H +#define FLIPPERZERO_FIRMWARE_ENDIANNESS_H + +inline static void store16(uint8_t* b, uint16_t i) { + memcpy(b, &i, 2); +} + +inline static void store32(uint8_t* b, uint32_t i) { + memcpy(b, &i, 4); +} + +inline static uint16_t load16(uint8_t* b) { + uint16_t x; + memcpy(&x, b, 2); + return x; +} + +inline static uint32_t load32(uint8_t* b) { + uint32_t x; + memcpy(&x, b, 4); + return x; +} + +#if BYTE_ORDER == BIG_ENDIAN +#define htobe16(x) (x) +#define htobe32(x) (x) +#define htole16(x) __builtin_bswap16(x) +#define htole32(x) __builtin_bswap32(x) +#define be16toh(x) (x) +#define be32toh(x) (x) +#define le16toh(x) __builtin_bswap16(x) +#define le32toh(x) __builtin_bswap32(x) +#elif BYTE_ORDER == LITTLE_ENDIAN +#define htobe16(x) __builtin_bswap16(x) +#define htobe32(x) __builtin_bswap32(x) +#define htole16(x) (x) +#define htole32(x) (x) +#define be16toh(x) __builtin_bswap16(x) +#define be32toh(x) __builtin_bswap32(x) +#define le16toh(x) (x) +#define le32toh(x) (x) +#else +#error "What kind of system is this?" +#endif + +#define load16_le(b) (le16toh(load16(b))) +#define load32_le(b) (le32toh(load32(b))) +#define store16_le(b, i) (store16(b, htole16(i))) +#define store32_le(b, i) (store32(b, htole32(i))) + +#define load16_be(b) (be16toh(load16(b))) +#define load32_be(b) (be32toh(load32(b))) +#define store16_be(b, i) (store16(b, htobe16(i))) +#define store32_be(b, i) (store32(b, htobe32(i))) + +#endif //FLIPPERZERO_FIRMWARE_ENDIANNESS_H diff --git a/applications/external/unitemp/sensors/SCD30.c b/applications/external/unitemp/sensors/SCD30.c index 627130da7..d7a10149c 100644 --- a/applications/external/unitemp/sensors/SCD30.c +++ b/applications/external/unitemp/sensors/SCD30.c @@ -21,60 +21,9 @@ #include "SCD30.h" #include "../interfaces/I2CSensor.h" +#include "../interfaces/endianness.h" //#include <3rdparty/everest/include/everest/kremlin/c_endianness.h> -inline static uint16_t load16(uint8_t* b) { - uint16_t x; - memcpy(&x, b, 2); - return x; -} - -inline static uint32_t load32(uint8_t* b) { - uint32_t x; - memcpy(&x, b, 4); - return x; -} - -inline static void store16(uint8_t* b, uint16_t i) { - memcpy(b, &i, 2); -} - -inline static void store32(uint8_t* b, uint32_t i) { - memcpy(b, &i, 4); -} - -#if BYTE_ORDER == BIG_ENDIAN -#define htobe16(x) (x) -#define htobe32(x) (x) -#define htole16(x) __builtin_bswap16(x) -#define htole32(x) __builtin_bswap32(x) -#define be16toh(x) (x) -#define be32toh(x) (x) -#define le16toh(x) __builtin_bswap16(x) -#define le32toh(x) __builtin_bswap32(x) -#elif BYTE_ORDER == LITTLE_ENDIAN -#define htobe16(x) __builtin_bswap16(x) -#define htobe32(x) __builtin_bswap32(x) -#define htole16(x) (x) -#define htole32(x) (x) -#define be16toh(x) __builtin_bswap16(x) -#define be32toh(x) __builtin_bswap32(x) -#define le16toh(x) (x) -#define le32toh(x) (x) -#else -#error "What kind of system is this?" -#endif - -#define load16_le(b) (le16toh(load16(b))) -#define load32_le(b) (le32toh(load32(b))) -#define store16_le(b, i) (store16(b, htole16(i))) -#define store32_le(b, i) (store32(b, htole32(i))) - -#define load16_be(b) (be16toh(load16(b))) -#define load32_be(b) (be32toh(load32(b))) -#define store16_be(b, i) (store16(b, htobe16(i))) -#define store32_be(b, i) (store32(b, htobe32(i))) - typedef union { uint16_t array16[2]; uint8_t array8[4]; diff --git a/applications/external/unitemp/sensors/SCD40.c b/applications/external/unitemp/sensors/SCD40.c new file mode 100644 index 000000000..c88943a00 --- /dev/null +++ b/applications/external/unitemp/sensors/SCD40.c @@ -0,0 +1,291 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library + +#include "SCD30.h" +#include "../interfaces/I2CSensor.h" +#include "../interfaces/endianness.h" +//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h> + +bool unitemp_SCD40_alloc(Sensor* sensor, char* args); +bool unitemp_SCD40_init(Sensor* sensor); +bool unitemp_SCD40_deinit(Sensor* sensor); +UnitempStatus unitemp_SCD40_update(Sensor* sensor); +bool unitemp_SCD40_free(Sensor* sensor); + +const SensorType SCD40 = { + .typename = "SCD40", + .interface = &I2C, + .datatype = UT_DATA_TYPE_TEMP_HUM_CO2, + .pollingInterval = 5000, + .allocator = unitemp_SCD40_alloc, + .mem_releaser = unitemp_SCD40_free, + .initializer = unitemp_SCD40_init, + .deinitializer = unitemp_SCD40_deinit, + .updater = unitemp_SCD40_update}; + +#define SCD40_ID 0x62 + +#define COMMAND_START_PERIODIC_MEASUREMENT 0X21B1 +#define COMMAND_READ_MEASUREMENT 0XEC05 +#define COMMAND_STOP_PERIODIC_MEASUREMENT 0X3F86 + +#define COMMAND_PERSIST_SETTINGS 0X3615 +#define COMMAND_GET_SERIAL_NUMBER 0X3682 +#define COMMAND_PERFORM_SELF_TEST 0X3639 +#define COMMAND_PERFORM_FACTORY_RESET 0X3632 +#define COMMAND_REINIT 0X3646 + +#define COMMAND_SET_TEMPERATURE_OFFSET 0X241D +#define COMMAND_GET_TEMPERATURE_OFFSET 0X2318 +#define COMMAND_SET_SENSOR_ALTITUDE 0X2427 +#define COMMAND_GET_SENSOR_ALTITUDE 0X2322 +#define COMMAND_SET_AMBIENT_PRESSURE 0XE000 +#define COMMAND_PERFORM_FORCED_RECALIBRATION 0X362F +#define COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2416 +#define COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2313 + +static bool readMeasurement(Sensor* sensor) __attribute__((unused)); +static void reset(Sensor* sensor) __attribute__((unused)); + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused)); +static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused)); + +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused)); + +static float getTemperatureOffset(Sensor* sensor) __attribute__((unused)); +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused)); + +static bool beginMeasuring(Sensor* sensor) __attribute__((unused)); +static bool stopMeasurement(Sensor* sensor) __attribute__((unused)); + +bool unitemp_SCD40_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + i2c_sensor->minI2CAdr = SCD40_ID << 1; + i2c_sensor->maxI2CAdr = SCD40_ID << 1; + return true; +} + +bool unitemp_SCD40_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_SCD40_init(Sensor* sensor) { + return beginMeasuring(sensor); +} + +bool unitemp_SCD40_deinit(Sensor* sensor) { + return stopMeasurement(sensor); +} + +UnitempStatus unitemp_SCD40_update(Sensor* sensor) { + readMeasurement(sensor); + return UT_SENSORSTATUS_OK; +} + +#define CRC8_POLYNOMIAL 0x31 +#define CRC8_INIT 0xFF + +static uint8_t computeCRC8(uint8_t* message, uint8_t len) { + uint8_t crc = CRC8_INIT; // Init with 0xFF + for(uint8_t x = 0; x < len; x++) { + crc ^= message[x]; // XOR-in the next input byte + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ CRC8_POLYNOMIAL); + else + crc <<= 1; + } + } + return crc; // No output reflection +} + +// Sends a command along with arguments and CRC +static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) { + static const uint8_t cmdSize = 5; + + uint8_t bytes[cmdSize]; + uint8_t* pointer = bytes; + store16_be(pointer, command); + pointer += 2; + uint8_t* argPos = pointer; + store16_be(pointer, arguments); + pointer += 2; + *pointer = computeCRC8(argPos, pointer - argPos); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +// Sends just a command, no arguments, no CRC +static bool sendCommand(Sensor* sensor, uint16_t command) { + static const uint8_t cmdSize = 2; + + uint8_t bytes[cmdSize]; + store16_be(bytes, command); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) { + static const uint8_t regSize = 2; + + if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[regSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0; + + return load16_be(bytes); +} + +static bool loadWord(uint8_t* buff, uint16_t* val) { + uint16_t tmp = load16_be(buff); + uint8_t expectedCRC = computeCRC8(buff, 2); + if(buff[2] != expectedCRC) return false; + *val = tmp; + return true; +} + +static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) { + static const uint8_t respSize = 3; + + if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[respSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false; + + return loadWord(bytes, val); +} + +// Get 18 bytes from SCD30 +// Updates global variables with floats +// Returns true if success +static bool readMeasurement(Sensor* sensor) { + if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) { + FURI_LOG_E(APP_NAME, "Sensor did not ACK"); + return false; // Sensor did not ACK + } + + furi_delay_ms(3); + + static const uint8_t respSize = 9; + uint8_t buff[respSize]; + uint8_t* bytes = buff; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) { + FURI_LOG_E(APP_NAME, "Error while read measures"); + return false; + } + + uint16_t tmpValue; + + bool error = false; + if(loadWord(bytes, &tmpValue)) { + sensor->co2 = tmpValue; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing CO2"); + error = true; + } + + bytes += 3; + if(loadWord(bytes, &tmpValue)) { + sensor->temp = (float)tmpValue * 175.0f / 65535.0f - 45.0f; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing temp"); + error = true; + } + + bytes += 3; + if(loadWord(bytes, &tmpValue)) { + sensor->hum = (float)tmpValue * 100.0f / 65535.0f; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing humidity"); + error = true; + } + + return !error; +} + +static void reset(Sensor* sensor) { + sendCommand(sensor, COMMAND_REINIT); +} + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) { + return sendCommandWithCRC( + sensor, COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED, enable); // Activate continuous ASC +} + +// Get the current ASC setting +static bool getAutoSelfCalibration(Sensor* sensor) { + return 1 == readRegister(sensor, COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED); +} + +// Unfinished +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) { + if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) { + FURI_LOG_E(APP_NAME, "Sensor did not ACK"); + return false; // Sensor did not ACK + } + + static const uint8_t respSize = 9; + uint8_t buff[respSize]; + uint8_t* bytes = buff; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) { + FURI_LOG_E(APP_NAME, "Error while read measures"); + return false; + } + + *val = 0; + + return true; +} + +static bool beginMeasuring(Sensor* sensor) { + return sendCommand(sensor, COMMAND_START_PERIODIC_MEASUREMENT); +} + +// Stop continuous measurement +static bool stopMeasurement(Sensor* sensor) { + return sendCommand(sensor, COMMAND_READ_MEASUREMENT); +} + +static float getTemperatureOffset(Sensor* sensor) { + uint16_t curOffset; + if(!getSettingValue(sensor, COMMAND_GET_TEMPERATURE_OFFSET, &curOffset)) return 0.0; + return (float)curOffset * 175.0f / 65536.0f; +} + +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) { + uint16_t newOffset = tempOffset * 65536.0 / 175.0 + 0.5f; + return sendCommandWithCRC( + sensor, COMMAND_SET_TEMPERATURE_OFFSET, newOffset); // Activate continuous ASC +} diff --git a/applications/external/unitemp/sensors/SCD40.h b/applications/external/unitemp/sensors/SCD40.h new file mode 100644 index 000000000..5cf7a4324 --- /dev/null +++ b/applications/external/unitemp/sensors/SCD40.h @@ -0,0 +1,59 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_SCD40 +#define UNITEMP_SCD40 + +#include "../unitemp.h" +#include "../Sensors.h" + +extern const SensorType SCD40; +/** + * @brief Выделение памяти и установка начальных значений датчика SCD40 + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_SCD40_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика SCD40 + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_SCD40_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD40_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * @param sensor Указатель на датчик + * @return Статус опроса датчика + */ +UnitempStatus unitemp_SCD40_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD40_free(Sensor* sensor); + +#endif \ No newline at end of file From 34ff4c7dfa3354c7eff9004ae11256ec4967b8c0 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 25 Jun 2023 00:31:23 +0300 Subject: [PATCH 254/370] . --- applications/external/unitemp/Sensors.h | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/external/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h index 25b9cb49e..aec220c0e 100644 --- a/applications/external/unitemp/Sensors.h +++ b/applications/external/unitemp/Sensors.h @@ -334,4 +334,5 @@ const GPIO* #include "./sensors/MAX31855.h" #include "./sensors/MAX6675.h" #include "./sensors/SCD30.h" +#include "./sensors/SCD40.h" #endif From 35f95336ed24142de1fc5b8068e1e4eda3d91832 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 25 Jun 2023 00:38:26 +0300 Subject: [PATCH 255/370] Heat index by ClementGre --- applications/external/unitemp/Sensors.c | 13 +++-- applications/external/unitemp/Sensors.h | 1 + .../unitemp/assets/heat_index_11x14.png | Bin 0 -> 1239 bytes applications/external/unitemp/unitemp.c | 30 +++++++++++ applications/external/unitemp/unitemp.h | 11 +++- .../external/unitemp/views/General_view.c | 50 +++++++++++++++--- .../external/unitemp/views/Settings_view.c | 15 ++++++ 7 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 applications/external/unitemp/assets/heat_index_11x14.png diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c index f96401ea2..33dd3fa88 100644 --- a/applications/external/unitemp/Sensors.c +++ b/applications/external/unitemp/Sensors.c @@ -624,11 +624,16 @@ UnitempStatus unitemp_sensor_updateData(Sensor* sensor) { UNITEMP_DEBUG("Sensor %s update status %d", sensor->name, sensor->status); } - if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT && sensor->status == UT_SENSORSTATUS_OK) { - uintemp_celsiumToFarengate(sensor); - } - if(sensor->status == UT_SENSORSTATUS_OK) { + if(app->settings.heat_index && + ((sensor->type->datatype & (UT_TEMPERATURE | UT_HUMIDITY)) == + (UT_TEMPERATURE | UT_HUMIDITY))) { + unitemp_calculate_heat_index(sensor); + } + if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT) { + uintemp_celsiumToFarengate(sensor); + } + sensor->temp += sensor->temp_offset / 10.f; if(app->settings.pressure_unit == UT_PRESSURE_MM_HG) { unitemp_pascalToMmHg(sensor); diff --git a/applications/external/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h index aec220c0e..339a4deff 100644 --- a/applications/external/unitemp/Sensors.h +++ b/applications/external/unitemp/Sensors.h @@ -119,6 +119,7 @@ typedef struct Sensor { char* name; //Температура float temp; + float heat_index; //Относительная влажность float hum; //Атмосферное давление diff --git a/applications/external/unitemp/assets/heat_index_11x14.png b/applications/external/unitemp/assets/heat_index_11x14.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f0d4ce5ffe1ca23daf24e02f7d250072560913 GIT binary patch literal 1239 zcmeAS@N?(olHy`uVBq!ia0vp^+(697!2~4Bm=@peSF#SkG9`3@FF6fC;XTdjT_?%?FfY&|GmkA4qW)ctjQhU48_F8K-LV zNi#4o=VXRNltlRYSS9D@>LsS+C#C9DzC zHb_`sNdc^+B->Ug!Z$#{Ilm}X!A#FU&p^qJOF==wrYI%ND#*nRsvXF)RmvzSDX`Ml zFE20GD>v55FG|-pw6wI;H!#vSGSUUA&@HaaD@m--%_~-h7y>iLCAB!YD6^m>Ge1uO zWNuzUS^4%5mXDB zFuJZtggiD2k)_eK`WI!U0uv+Ht-yc=I}lk6s@_H)lpc}NCnWKMMS-c`jtdwpu$*Ma zRnxte2bj6CJzX3_B&L=IT;yX=temp = sensor->temp * (9.0 / 5.0) + 32; + sensor->heat_index = sensor->heat_index * (9.0 / 5.0) + 32; } +static float heat_index_consts[9] = { + -42.379f, + 2.04901523f, + 10.14333127f, + -0.22475541f, + -0.00683783f, + -0.05481717f, + 0.00122874f, + 0.00085282f, + -0.00000199f}; +void unitemp_calculate_heat_index(Sensor* sensor) { + // temp should be in Celsius, heat index will be in Celsius + float temp = sensor->temp * (9.0 / 5.0) + 32.0f; + float hum = sensor->hum; + sensor->heat_index = + (heat_index_consts[0] + heat_index_consts[1] * temp + heat_index_consts[2] * hum + + heat_index_consts[3] * temp * hum + heat_index_consts[4] * temp * temp + + heat_index_consts[5] * hum * hum + heat_index_consts[6] * temp * temp * hum + + heat_index_consts[7] * temp * hum * hum + heat_index_consts[8] * temp * temp * hum * hum - + 32.0f) * + (5.0 / 9.0); +} void unitemp_pascalToMmHg(Sensor* sensor) { sensor->pressure = sensor->pressure * 0.007500638; } @@ -71,6 +94,7 @@ bool unitemp_saveSettings(void) { app->file_stream, "INFINITY_BACKLIGHT %d\n", app->settings.infinityBacklight); stream_write_format(app->file_stream, "TEMP_UNIT %d\n", app->settings.temp_unit); stream_write_format(app->file_stream, "PRESSURE_UNIT %d\n", app->settings.pressure_unit); + stream_write_format(app->file_stream, "HEAT_INDEX %d\n", app->settings.heat_index); //Закрытие потока и освобождение памяти file_stream_close(app->file_stream); @@ -166,6 +190,11 @@ bool unitemp_loadSettings(void) { int p = 0; sscanf(((char*)(file_buf + line_end)), "\nPRESSURE_UNIT %d", &p); app->settings.pressure_unit = p; + } else if(!strcmp(buff, "HEAT_INDEX")) { + //Чтение значения параметра + int p = 0; + sscanf(((char*)(file_buf + line_end)), "\nHEAT_INDEX %d", &p); + app->settings.heat_index = p; } else { FURI_LOG_W(APP_NAME, "Unknown settings parameter: %s", buff); } @@ -203,6 +232,7 @@ static bool unitemp_alloc(void) { app->settings.infinityBacklight = true; //Подсветка горит всегда app->settings.temp_unit = UT_TEMP_CELSIUS; //Единица измерения температуры - градусы Цельсия app->settings.pressure_unit = UT_PRESSURE_MM_HG; //Единица измерения давления - мм рт. ст. + app->settings.heat_index = false; app->gui = furi_record_open(RECORD_GUI); //Диспетчер окон diff --git a/applications/external/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h index 69cd8cf4f..c2b61b899 100644 --- a/applications/external/unitemp/unitemp.h +++ b/applications/external/unitemp/unitemp.h @@ -40,7 +40,7 @@ //Имя приложения #define APP_NAME "Unitemp" //Версия приложения -#define UNITEMP_APP_VER "1.3" +#define UNITEMP_APP_VER "1.4" //Путь хранения файлов плагина #define APP_PATH_FOLDER "/ext/unitemp" //Имя файла с настройками @@ -80,6 +80,8 @@ typedef struct { tempMeasureUnit temp_unit; //Единица измерения давления pressureMeasureUnit pressure_unit; + // Do calculate and show heat index + bool heat_index; //Последнее состояние OTG bool lastOTGState; } UnitempSettings; @@ -111,6 +113,13 @@ typedef struct { /* Объявление прототипов функций */ +/** + * @brief Calculates the heat index in Celsius from the temperature and humidity and stores it in the sensor heat_index field + * + * @param sensor The sensor struct, with temperature in Celcius and humidity in percent + */ +void unitemp_calculate_heat_index(Sensor* sensor); + /** * @brief Перевод значения температуры датчика из Цельсия в Фаренгейты * diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c index 22d724935..13e98715a 100644 --- a/applications/external/unitemp/views/General_view.c +++ b/applications/external/unitemp/views/General_view.c @@ -113,6 +113,33 @@ static void _draw_humidity(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 4, pos[1] + 10 + 7, "%"); } +static void _draw_heat_index(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) { + canvas_draw_rframe(canvas, pos[0], pos[1], 54, 20, 3); + canvas_draw_rframe(canvas, pos[0], pos[1], 54, 19, 3); + + canvas_draw_icon(canvas, pos[0] + 3, pos[1] + 3, &I_heat_index_11x14); + + int16_t heat_index_int = sensor->heat_index; + int8_t heat_index_dec = abs((int16_t)(sensor->heat_index * 10) % 10); + + snprintf(app->buff, BUFF_SIZE, "%d", heat_index_int); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned( + canvas, + pos[0] + 27 + ((sensor->heat_index <= -10 || sensor->heat_index > 99) ? 5 : 0), + pos[1] + 10, + AlignCenter, + AlignCenter, + app->buff); + + if(heat_index_int <= 99) { + uint8_t int_len = canvas_string_width(canvas, app->buff); + snprintf(app->buff, BUFF_SIZE, ".%d", heat_index_dec); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 2, pos[1] + 10 + 7, app->buff); + } +} + static void _draw_pressure(Canvas* canvas, Sensor* sensor) { const uint8_t x = 29, y = 39; //Рисование рамки @@ -320,12 +347,23 @@ static void _draw_carousel_values(Canvas* canvas) { ColorWhite); break; case UT_DATA_TYPE_TEMP_HUM: - _draw_temperature( - canvas, - unitemp_sensor_getActive(generalview_sensor_index), - temp_positions[1][0], - temp_positions[1][1], - ColorWhite); + if(!app->settings.heat_index) { + _draw_temperature( + canvas, + unitemp_sensor_getActive(generalview_sensor_index), + temp_positions[1][0], + temp_positions[1][1], + ColorWhite); + } else { + _draw_temperature( + canvas, + unitemp_sensor_getActive(generalview_sensor_index), + temp_positions[2][0], + temp_positions[2][1], + ColorWhite); + _draw_heat_index( + canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]); + } _draw_humidity( canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[0]); break; diff --git a/applications/external/unitemp/views/Settings_view.c b/applications/external/unitemp/views/Settings_view.c index e61c6cad6..3d1eca906 100644 --- a/applications/external/unitemp/views/Settings_view.c +++ b/applications/external/unitemp/views/Settings_view.c @@ -26,6 +26,7 @@ static VariableItemList* variable_item_list; static const char states[2][9] = {"Auto", "Infinity"}; static const char temp_units[UT_TEMP_COUNT][3] = {"*C", "*F"}; static const char pressure_units[UT_PRESSURE_COUNT][6] = {"mm Hg", "in Hg", "kPa", "hPA"}; +static const char heat_index_bool[2][4] = {"OFF", "ON"}; //Элемент списка - бесконечная подсветка VariableItem* infinity_backlight_item; @@ -33,6 +34,8 @@ VariableItem* infinity_backlight_item; VariableItem* temperature_unit_item; //Единица измерения давления VariableItem* pressure_unit_item; + +VariableItem* heat_index_item; #define VIEW_ID UnitempViewSettings /** @@ -57,6 +60,7 @@ static uint32_t _exit_callback(void* context) { (bool)variable_item_get_current_value_index(infinity_backlight_item); app->settings.temp_unit = variable_item_get_current_value_index(temperature_unit_item); app->settings.pressure_unit = variable_item_get_current_value_index(pressure_unit_item); + app->settings.heat_index = variable_item_get_current_value_index(heat_index_item); unitemp_saveSettings(); unitemp_loadSettings(); @@ -90,6 +94,11 @@ static void _setting_change_callback(VariableItem* item) { pressure_unit_item, pressure_units[variable_item_get_current_value_index(pressure_unit_item)]); } + if(item == heat_index_item) { + variable_item_set_current_value_text( + heat_index_item, + heat_index_bool[variable_item_get_current_value_index(heat_index_item)]); + } } /** @@ -106,6 +115,8 @@ void unitemp_Settings_alloc(void) { variable_item_list_add(variable_item_list, "Temp. unit", 2, _setting_change_callback, app); pressure_unit_item = variable_item_list_add( variable_item_list, "Press. unit", UT_PRESSURE_COUNT, _setting_change_callback, app); + heat_index_item = variable_item_list_add( + variable_item_list, "Calc. heat index", 2, _setting_change_callback, app); //Добавление колбека на нажатие средней кнопки variable_item_list_set_enter_callback(variable_item_list, _enter_callback, app); @@ -139,6 +150,10 @@ void unitemp_Settings_switch(void) { pressure_unit_item, pressure_units[variable_item_get_current_value_index(pressure_unit_item)]); + variable_item_set_current_value_index(heat_index_item, (uint8_t)app->settings.heat_index); + variable_item_set_current_value_text( + heat_index_item, heat_index_bool[variable_item_get_current_value_index(heat_index_item)]); + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_ID); } From 300bd2c16bba670377a7d314a55a200726bc8f4c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 25 Jun 2023 00:44:15 +0300 Subject: [PATCH 256/370] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04f95fbee..846ead301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## New changes +* Plugins: UniTemp update merged PRs -> Heat Index Feature (by @ClementGre) + Append carbon dioxide sensor (SCD40) (by @divinebird) * Plugins: Fix furi_hal_bus issues in AVR Programmer and Signal Generator (fixes issue #525) * Plugins: USB / BLE Remote - Updated UI in keynote vertical and numpad (by @gid9798 | PR #524) * Plugins: Update TOTP [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) From eb282d20b771b7426140c8f16caec74fc463cf91 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 25 Jun 2023 00:53:36 +0300 Subject: [PATCH 257/370] Fix numpad ui wrong placed message --- applications/external/hid_app/views/hid_numpad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/hid_app/views/hid_numpad.c b/applications/external/hid_app/views/hid_numpad.c index c27576862..bd4788b83 100644 --- a/applications/external/hid_app/views/hid_numpad.c +++ b/applications/external/hid_app/views/hid_numpad.c @@ -140,11 +140,11 @@ static void hid_numpad_draw_callback(Canvas* canvas, void* context) { canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); } else { canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + elements_multiline_text_aligned( + canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection..."); } elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad"); - elements_multiline_text_aligned( - canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection..."); } else { elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad"); } From 190d47e5280823b327bcbea19ad181a85fa4a921 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 25 Jun 2023 01:39:46 +0300 Subject: [PATCH 258/370] Unitemp Fix SDA SCL pin numbers text --- applications/external/unitemp/views/General_view.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c index 13e98715a..0a8e8ad17 100644 --- a/applications/external/unitemp/views/General_view.c +++ b/applications/external/unitemp/views/General_view.c @@ -484,8 +484,8 @@ static void _draw_carousel_info(Canvas* canvas) { ->currentI2CAdr >> 1); canvas_draw_str(canvas, 57, 35, app->buff); - canvas_draw_str(canvas, 54, 46, "15 (C0)"); - canvas_draw_str(canvas, 54, 58, "16 (C1)"); + canvas_draw_str(canvas, 54, 46, "15 (C1)"); + canvas_draw_str(canvas, 54, 58, "16 (C0)"); } } static void _draw_view_sensorsCarousel(Canvas* canvas) { From 7aa15ada3002893d2a73a2c046bbdd44aa97f7fe Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 25 Jun 2023 03:10:28 +0300 Subject: [PATCH 259/370] merge fix --- applications/main/application.fam | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/main/application.fam b/applications/main/application.fam index 6fa239123..c8884f934 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -31,7 +31,6 @@ App( "subghz", #"bad_usb", #"u2f", - "fap_loader", "archive", ], ) From 99cd94f39f3626752fa69103eeb04bad90a78581 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 20:35:40 +0100 Subject: [PATCH 260/370] Revert extmainapp stuff --- applications/services/loader/loader.c | 6 ------ applications/services/loader/loader.h | 2 -- scripts/fbt/appmanifest.py | 19 +++++-------------- scripts/fbt_tools/fbt_extapps.py | 2 +- site_scons/extapps.scons | 1 - 5 files changed, 6 insertions(+), 24 deletions(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 7652f9a89..9c59e9ca9 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -215,12 +215,6 @@ static void loader_start_internal_app(Loader* loader, const FlipperApplication* app, const char* args) { FURI_LOG_I(TAG, "Starting %s", app->name); - if(app->app == NULL) { - args = app->appid; - app = loader_find_application_by_name_in_list( - FAP_LOADER_APP_NAME, FLIPPER_APPS, FLIPPER_APPS_COUNT); - } - // store args furi_assert(loader->app.args == NULL); if(args && strlen(args) > 0) { diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index b31bb1bdd..7f4e2ec89 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -8,8 +8,6 @@ extern "C" { #define RECORD_LOADER "loader" -#define FAP_LOADER_APP_NAME "Apps" - typedef struct Loader Loader; typedef enum { diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 531df8bc1..e4967a2ad 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -20,7 +20,6 @@ class FlipperAppType(Enum): EXTERNAL = "External" METAPACKAGE = "Package" PLUGIN = "Plugin" - EXTMAINAPP = "ExtMainApp" @dataclass @@ -375,14 +374,6 @@ class ApplicationsCGenerator: def get_app_descr(self, app: FlipperApplication): if app.apptype == FlipperAppType.STARTUP: return app.entry_point - if app.apptype == FlipperAppType.EXTMAINAPP: - return f""" - {{.app = NULL, - .name = "{app.name}", - .appid = "/ext/apps/.Main/{app.appid}.fap", - .stack_size = 0, - .icon = {f"&{app.icon}" if app.icon else "NULL"}, - .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)}}}""" return f""" {{.app = {app.entry_point}, .name = "{app.name}", @@ -403,11 +394,11 @@ class ApplicationsCGenerator: ) entry_type, entry_block = self.APP_TYPE_MAP[apptype] contents.append(f"const {entry_type} {entry_block}[] = {{") - apps = self.buildset.get_apps_of_type(apptype) - if apptype is FlipperAppType.APP: - apps += self.buildset.get_apps_of_type(FlipperAppType.EXTMAINAPP) - apps.sort(key=lambda app: app.order) - contents.append(",\n".join(map(self.get_app_descr, apps))) + contents.append( + ",\n".join( + map(self.get_app_descr, self.buildset.get_apps_of_type(apptype)) + ) + ) contents.append("};") contents.append( f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});" diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 2a16558fc..3ad3fbda8 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -179,7 +179,7 @@ class AppBuilder: deployable = False app_artifacts.dist_entries.append((deployable, fal_path)) else: - fap_path = f"apps/{'.Main' if self.app.apptype == FlipperAppType.EXTMAINAPP else self.app.fap_category}/{app_artifacts.compact.name}" + fap_path = f"apps/{self.app.fap_category}/{app_artifacts.compact.name}" app_artifacts.dist_entries.append( (self.app.is_default_deployable, fap_path) ) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 946844870..6db0e538d 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -67,7 +67,6 @@ class FlipperExtAppBuildArtifacts: apps_to_build_as_faps = [ FlipperAppType.PLUGIN, FlipperAppType.EXTERNAL, - FlipperAppType.EXTMAINAPP, FlipperAppType.DEBUG, ] From ab8295848ea548557464c1867e3cb2687b1a81b5 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 21:45:21 +0100 Subject: [PATCH 261/370] Fixes --- applications/main/application.fam | 1 - .../main/archive/scenes/archive_scene_browser.c | 7 ++----- applications/main/bad_kb/application.fam | 2 +- applications/main/gpio/application.fam | 2 +- applications/main/ibutton/application.fam | 2 +- applications/main/infrared/application.fam | 2 +- applications/main/lfrfid/application.fam | 2 +- applications/main/xtreme_app/application.fam | 2 +- .../xtreme_app_scene_interface_mainmenu_add.c | 4 ++-- applications/main/xtreme_app/xtreme_app.c | 2 +- applications/main/xtreme_app/xtreme_app.h | 2 +- .../animations/views/bubble_animation_view.c | 2 +- .../animations/views/one_shot_animation_view.c | 2 +- .../desktop/scenes/desktop_scene_lock_menu.c | 10 +++++----- .../desktop/scenes/desktop_scene_locked.c | 5 +---- .../services/desktop/scenes/desktop_scene_main.c | 15 ++------------- applications/services/loader/loader.h | 4 ++-- .../scenes/desktop_settings_scene_favorite.c | 16 +++++----------- firmware/targets/f7/api_symbols.csv | 3 --- furi/flipper.c | 2 +- 20 files changed, 30 insertions(+), 57 deletions(-) diff --git a/applications/main/application.fam b/applications/main/application.fam index 006670947..f3409d5c9 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -3,7 +3,6 @@ App( name="Basic applications for main menu", apptype=FlipperAppType.METAPACKAGE, provides=[ - "fap_loader", "subghz", "lfrfid", "nfc", diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index ac02b01aa..82aa85912 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -23,7 +23,7 @@ const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) { return "RFID"; case ArchiveFileTypeInfrared: return "Infrared"; - case ArchiveFileTypeBadUsb: + case ArchiveFileTypeBadKb: return "Bad KB"; case ArchiveFileTypeU2f: return "U2F"; @@ -65,6 +65,7 @@ static void } } else { loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); + UNUSED(favorites); // const char* str = furi_string_get_cstr(selected->path); // if(favorites) { // char arg[strlen(str) + 4]; @@ -75,10 +76,6 @@ static void // } } - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } - furi_record_close(RECORD_LOADER); } diff --git a/applications/main/bad_kb/application.fam b/applications/main/bad_kb/application.fam index 0c923ba5b..09531da81 100644 --- a/applications/main/bad_kb/application.fam +++ b/applications/main/bad_kb/application.fam @@ -1,7 +1,7 @@ App( appid="bad_kb", name="Bad KB", - apptype=FlipperAppType.EXTMAINAPP, + apptype=FlipperAppType.APP, entry_point="bad_kb_app", cdefines=["APP_BAD_KB"], requires=[ diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index 222d1ca28..efeb8b6fe 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -1,7 +1,7 @@ App( appid="gpio", name="GPIO", - apptype=FlipperAppType.EXTMAINAPP, + apptype=FlipperAppType.APP, entry_point="gpio_app", cdefines=["APP_GPIO"], requires=["gui"], diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index f29fd7772..06968bba4 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -1,7 +1,7 @@ App( appid="ibutton", name="iButton", - apptype=FlipperAppType.EXTMAINAPP, + apptype=FlipperAppType.APP, targets=["f7"], entry_point="ibutton_app", cdefines=["APP_IBUTTON"], diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 8fc26d3d9..e5483e9ff 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -1,7 +1,7 @@ App( appid="infrared", name="Infrared", - apptype=FlipperAppType.EXTMAINAPP, + apptype=FlipperAppType.APP, entry_point="infrared_app", targets=["f7"], cdefines=["APP_INFRARED"], diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index d75b5431f..e84ed2a31 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -1,7 +1,7 @@ App( appid="lfrfid", name="RFID", - apptype=FlipperAppType.EXTMAINAPP, + apptype=FlipperAppType.APP, targets=["f7"], entry_point="lfrfid_app", cdefines=["APP_LF_RFID"], diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam index ca9cc62dc..7f30db104 100644 --- a/applications/main/xtreme_app/application.fam +++ b/applications/main/xtreme_app/application.fam @@ -1,7 +1,7 @@ App( appid="xtreme_app", name="Xtreme", - apptype=FlipperAppType.EXTMAINAPP, + apptype=FlipperAppType.APP, entry_point="xtreme_app", cdefines=["APP_XTREME"], requires=[ diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c index 57fa78090..dd9336022 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu_add.c @@ -11,7 +11,7 @@ static bool xtreme_app_scene_interface_mainmenu_add_file_browser_callback( FuriString* item_name) { UNUSED(context); Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); furi_record_close(RECORD_STORAGE); return success; } @@ -32,7 +32,7 @@ void xtreme_app_scene_interface_mainmenu_add_on_enter(void* context) { if(dialog_file_browser_show(app->dialogs, string, string, &browser_options)) { CharList_push_back(app->mainmenu_app_paths, strdup(furi_string_get_cstr(string))); Storage* storage = furi_record_open(RECORD_STORAGE); - fap_loader_load_name_and_icon(string, storage, NULL, string); + flipper_application_load_name_and_icon(string, storage, NULL, string); furi_record_close(RECORD_STORAGE); CharList_push_back(app->mainmenu_app_names, strdup(furi_string_get_cstr(string))); app->save_mainmenu_apps = true; diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index 3d5760923..01822084f 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -229,7 +229,7 @@ XtremeApp* xtreme_app_alloc() { furi_string_replace_all(line, "\r", ""); furi_string_replace_all(line, "\n", ""); CharList_push_back(app->mainmenu_app_paths, strdup(furi_string_get_cstr(line))); - fap_loader_load_name_and_icon(line, storage, NULL, line); + flipper_application_load_name_and_icon(line, storage, NULL, line); CharList_push_back(app->mainmenu_app_names, strdup(furi_string_get_cstr(line))); } } diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index 321ad167c..da6029ed1 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -18,7 +18,7 @@ #include "dolphin/dolphin_i.h" #include #include -#include +#include #include #include #include diff --git a/applications/services/desktop/animations/views/bubble_animation_view.c b/applications/services/desktop/animations/views/bubble_animation_view.c index e59ca0c98..8fcfba0e3 100644 --- a/applications/services/desktop/animations/views/bubble_animation_view.c +++ b/applications/services/desktop/animations/views/bubble_animation_view.c @@ -136,7 +136,7 @@ static bool bubble_animation_input_callback(InputEvent* event, void* context) { } } else if(event->type == InputTypeLong) { Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "Power", "about_battery"); + loader_start(loader, "Power", "about_battery", NULL); furi_record_close(RECORD_LOADER); } } diff --git a/applications/services/desktop/animations/views/one_shot_animation_view.c b/applications/services/desktop/animations/views/one_shot_animation_view.c index d11d35b17..52b183566 100644 --- a/applications/services/desktop/animations/views/one_shot_animation_view.c +++ b/applications/services/desktop/animations/views/one_shot_animation_view.c @@ -73,7 +73,7 @@ static bool one_shot_view_input(InputEvent* event, void* context) { } } else if(event->type == InputTypeLong) { Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "Power", "about_battery"); + loader_start(loader, "Power", "about_battery", NULL); furi_record_close(RECORD_LOADER); } } diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index d1c7e48bc..2eb452192 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -80,8 +80,8 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case DesktopLockMenuEventSettings: desktop_scene_lock_menu_save_settings(desktop); - loader_show_settings(furi_record_open(RECORD_LOADER)); - furi_record_close(RECORD_LOADER); + // loader_show_settings(furi_record_open(RECORD_LOADER)); + // furi_record_close(RECORD_LOADER); consumed = true; break; case DesktopLockMenuEventLock: @@ -95,7 +95,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { desktop_lock(desktop, true); } else { LoaderStatus status = - loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); + loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG, NULL); if(status == LoaderStatusOk) { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); } else { @@ -114,7 +114,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { furi_record_close(RECORD_POWER); } else { LoaderStatus status = - loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); + loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG, NULL); if(status == LoaderStatusOk) { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 2); } else { @@ -125,7 +125,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventXtreme: desktop_scene_lock_menu_save_settings(desktop); - loader_start(desktop->loader, "Xtreme", NULL); + loader_start_with_gui_error(desktop->loader, "Xtreme", NULL); consumed = true; break; case DesktopLockMenuEventStealthModeOn: diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index adf7a6a7c..3121d1d38 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -86,10 +86,7 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case DesktopLockedEventOpenPowerOff: { - LoaderStatus status = loader_start(desktop->loader, "Power", "off"); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start(desktop->loader, "Power", "off", NULL); consumed = true; break; } diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 5be8a9197..f4e8fd309 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -64,14 +64,6 @@ static void static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) { if(strlen(application->name_or_path) > 0) { loader_start_with_gui_error(desktop->loader, application->name_or_path, NULL); - } else { - // No favourite app is set! So we skipping this part - return; - //status = loader_start(desktop->loader, FAP_LOADER_APP_NAME, NULL); - } - - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); } } @@ -167,11 +159,8 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; } case DesktopMainEventOpenClock: { - LoaderStatus status = loader_start( - desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("apps/Misc/Nightstand.fap")); - if(status != LoaderStatusOk) { - FURI_LOG_E(TAG, "loader_start failed: %d", status); - } + loader_start_with_gui_error( + desktop->loader, EXT_PATH("apps/Misc/Nightstand.fap"), NULL); break; } case DesktopLockedEventUpdate: diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index 9fc4059f2..d49bec0a4 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -6,7 +6,7 @@ extern "C" { #endif #define RECORD_LOADER "loader" -#define LOADER_APPLICATIONS_NAME "Applications" +#define LOADER_APPLICATIONS_NAME "Apps" typedef struct Loader Loader; @@ -81,4 +81,4 @@ FuriPubSub* loader_get_pubsub(Loader* instance); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index f18b0c0a7..0251f5710 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -3,6 +3,7 @@ #include "desktop_settings_scene.h" #include #include +#include static bool favorite_fap_selector_item_callback( FuriString* file_path, @@ -10,16 +11,9 @@ static bool favorite_fap_selector_item_callback( uint8_t** icon_ptr, FuriString* item_name) { UNUSED(context); -#ifdef APP_FAP_LOADER Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); furi_record_close(RECORD_STORAGE); -#else - UNUSED(file_path); - UNUSED(icon_ptr); - UNUSED(item_name); - bool success = false; -#endif return success; } @@ -54,13 +48,13 @@ void desktop_settings_scene_favorite_on_enter(void* context) { if(primary_favorite) { // Select favorite item in submenu if((app->settings.favorite_primary.is_external && - !strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) || + !strcmp(FLIPPER_APPS[i].name, LOADER_APPLICATIONS_NAME)) || (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_primary.name_or_path))) { pre_select_item = i; } } else { if((app->settings.favorite_secondary.is_external && - !strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) || + !strcmp(FLIPPER_APPS[i].name, LOADER_APPLICATIONS_NAME)) || (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_secondary.name_or_path))) { pre_select_item = i; } @@ -83,7 +77,7 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); if(event.type == SceneManagerEventTypeCustom) { - if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME) != 0) { + if(strcmp(FLIPPER_APPS[event.event].name, LOADER_APPLICATIONS_NAME) != 0) { if(primary_favorite) { app->settings.favorite_primary.is_external = false; strncpy( diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b737146e0..7535942dc 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,7 +1,6 @@ entry,status,name,type,params Version,+,31.0,, Header,+,applications/main/archive/helpers/favorite_timeout.h,, -Header,+,applications/main/fap_loader/fap_loader_app.h,, Header,+,applications/main/subghz/helpers/subghz_txrx.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -878,7 +877,6 @@ Function,-,expm1l,long double,long double Function,-,fabs,double,double Function,-,fabsf,float,float Function,-,fabsl,long double,long double -Function,+,fap_loader_load_name_and_icon,_Bool,"FuriString*, Storage*, uint8_t**, FuriString*" Function,+,favorite_timeout_callback,void,void* Function,+,favorite_timeout_run,void,"ViewDispatcher*, SceneManager*" Function,-,fclose,int,FILE* @@ -1920,7 +1918,6 @@ Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* -Function,+,loader_show_settings,void,Loader* Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* diff --git a/furi/flipper.c b/furi/flipper.c index c42ba0fdd..bdb80c851 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -74,7 +74,7 @@ void flipper_migrate_files() { furi_record_close(RECORD_STORAGE); } -void flipper_start_service(const FlipperApplication* service) { +void flipper_start_service(const FlipperInternalApplication* service) { FURI_LOG_D(TAG, "Starting service %s", service->name); FuriThread* thread = From 8969aa10a988de87f3d40a9ed620a9d1376fc41b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 21:45:49 +0100 Subject: [PATCH 262/370] Add back the improved fap meta loader --- lib/flipper_application/flipper_application.c | 55 +++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index fbcf2973d..620bd5994 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -3,6 +3,7 @@ #include #include "application_assets.h" #include +#include #include @@ -312,25 +313,45 @@ bool flipper_application_load_name_and_icon( Storage* storage, uint8_t** icon_ptr, FuriString* item_name) { - FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + bool load_success = true; - FlipperApplicationPreloadStatus preload_res = - flipper_application_preload_manifest(app, furi_string_get_cstr(path)); - - bool load_success = false; - - if(preload_res == FlipperApplicationPreloadStatusSuccess) { - const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); - if(manifest->has_icon) { - memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); - } - furi_string_set(item_name, manifest->name); - load_success = true; - } else { - FURI_LOG_E(TAG, "Failed to preload %s", furi_string_get_cstr(path)); + StorageData* storage_data; + if(storage_get_data(storage, path, &storage_data) == FSE_OK && + storage_path_already_open(path, storage_data)) { load_success = false; } - flipper_application_free(app); + if(load_success) { + load_success = false; + + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload_manifest(app, furi_string_get_cstr(path)); + + if(preload_res == FlipperApplicationPreloadStatusSuccess || + preload_res == FlipperApplicationPreloadStatusApiMismatch) { + const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); + if(manifest->has_icon && icon_ptr != NULL && *icon_ptr != NULL) { + memcpy(*icon_ptr, manifest->icon, FAP_MANIFEST_MAX_ICON_SIZE); + } + furi_string_set(item_name, manifest->name); + load_success = true; + } else { + FURI_LOG_E(TAG, "Failed to preload %s", furi_string_get_cstr(path)); + } + + flipper_application_free(app); + } + + if(!load_success) { + size_t offset = furi_string_search_rchar(path, '/'); + if(offset != FURI_STRING_FAILURE) { + furi_string_set_n(item_name, path, offset + 1, furi_string_size(path) - offset - 1); + } else { + furi_string_set(item_name, path); + } + } + return load_success; -} \ No newline at end of file +} From d7f9a5ef60fe5e3c99cc317a132f694dc88ef6bb Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:14:06 +0100 Subject: [PATCH 263/370] Format --- .../services/desktop/scenes/desktop_scene_lock_menu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index 2eb452192..be0290930 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -94,8 +94,8 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock(desktop, true); } else { - LoaderStatus status = - loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG, NULL); + LoaderStatus status = loader_start( + desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG, NULL); if(status == LoaderStatusOk) { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); } else { @@ -113,8 +113,8 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { power_off(power); furi_record_close(RECORD_POWER); } else { - LoaderStatus status = - loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG, NULL); + LoaderStatus status = loader_start( + desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG, NULL); if(status == LoaderStatusOk) { scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 2); } else { From 888e325fa755d431e6c616c0223228f333e4d7a7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:14:33 +0100 Subject: [PATCH 264/370] Begone retarded comment --- scripts/fbt/appmanifest.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 53bb104f7..c917c9a7a 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -129,11 +129,6 @@ class AppManager: raise FlipperManifestException( f"Plugin {kw.get('appid')} must have 'requires' in manifest" ) - # Harmless - cdefines for external apps are meaningless - # if apptype == FlipperAppType.EXTERNAL and kw.get("cdefines"): - # raise FlipperManifestException( - # f"External app {kw.get('appid')} must not have 'cdefines' in manifest" - # ) def load_manifest(self, app_manifest_path: str, app_dir_node: object): if not os.path.exists(app_manifest_path): From 71d1c1d0f1456c2a901559cdbde03eefe132340f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:39:15 +0100 Subject: [PATCH 265/370] FAPP(TM) support --- applications/services/loader/loader.c | 13 ++++++++++--- scripts/fbt/appmanifest.py | 19 ++++++++++++++----- scripts/fbt_tools/fbt_extapps.py | 2 +- site_scons/extapps.scons | 1 + 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index ab7876a03..91b6d1a2d 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -366,8 +366,15 @@ static LoaderStatus loader_do_start_by_name( { const FlipperInternalApplication* app = loader_find_application_by_name(name); if(app) { - loader_start_internal_app(loader, app, args); - status = loader_make_success_status(error_message); + if(app->app == NULL) { + // FAPP support + status = loader_start_external_app( + loader, furi_record_open(RECORD_STORAGE), app->appid, args, error_message); + furi_record_close(RECORD_STORAGE); + } else { + loader_start_internal_app(loader, app, args); + status = loader_make_success_status(error_message); + } break; } } @@ -495,4 +502,4 @@ int32_t loader_srv(void* p) { } return 0; -} \ No newline at end of file +} diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index c917c9a7a..86ed066ed 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -13,6 +13,7 @@ class FlipperAppType(Enum): SERVICE = "Service" SYSTEM = "System" APP = "App" + FAPP = "Fapp" DEBUG = "Debug" ARCHIVE = "Archive" SETTINGS = "Settings" @@ -375,6 +376,14 @@ class ApplicationsCGenerator: def get_app_descr(self, app: FlipperApplication): if app.apptype == FlipperAppType.STARTUP: return app.entry_point + if app.apptype == FlipperAppType.FAPP: + return f""" + {{.app = NULL, + .name = "{app.name}", + .appid = "/ext/apps/.Main/{app.appid}.fap", + .stack_size = 0, + .icon = {f"&{app.icon}" if app.icon else "NULL"}, + .flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)}}}""" return f""" {{.app = {app.entry_point}, .name = "{app.name}", @@ -395,11 +404,11 @@ class ApplicationsCGenerator: ) entry_type, entry_block = self.APP_TYPE_MAP[apptype] contents.append(f"const {entry_type} {entry_block}[] = {{") - contents.append( - ",\n".join( - map(self.get_app_descr, self.buildset.get_apps_of_type(apptype)) - ) - ) + apps = self.buildset.get_apps_of_type(apptype) + if apptype is FlipperAppType.APP: + apps += self.buildset.get_apps_of_type(FlipperAppType.FAPP) + apps.sort(key=lambda app: app.order) + contents.append(",\n".join(map(self.get_app_descr, apps))) contents.append("};") contents.append( f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});" diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 3ad3fbda8..2441ed883 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -179,7 +179,7 @@ class AppBuilder: deployable = False app_artifacts.dist_entries.append((deployable, fal_path)) else: - fap_path = f"apps/{self.app.fap_category}/{app_artifacts.compact.name}" + fap_path = f"apps/{'.Main' if self.app.apptype == FlipperAppType.FAPP else self.app.fap_category}/{app_artifacts.compact.name}" app_artifacts.dist_entries.append( (self.app.is_default_deployable, fap_path) ) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 6db0e538d..1005ab1a6 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -65,6 +65,7 @@ class FlipperExtAppBuildArtifacts: apps_to_build_as_faps = [ + FlipperAppType.FAPP, FlipperAppType.PLUGIN, FlipperAppType.EXTERNAL, FlipperAppType.DEBUG, From 407e0fa6c2bca7d76d05d6678a751a5894ceb8de Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:39:30 +0100 Subject: [PATCH 266/370] The FAPPening(TM) --- applications/main/bad_kb/application.fam | 2 +- applications/main/gpio/application.fam | 2 +- applications/main/ibutton/application.fam | 2 +- applications/main/infrared/application.fam | 2 +- applications/main/lfrfid/application.fam | 2 +- applications/main/xtreme_app/application.fam | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/applications/main/bad_kb/application.fam b/applications/main/bad_kb/application.fam index 09531da81..74764f8b8 100644 --- a/applications/main/bad_kb/application.fam +++ b/applications/main/bad_kb/application.fam @@ -1,7 +1,7 @@ App( appid="bad_kb", name="Bad KB", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, entry_point="bad_kb_app", cdefines=["APP_BAD_KB"], requires=[ diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index efeb8b6fe..f18b8318e 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -1,7 +1,7 @@ App( appid="gpio", name="GPIO", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, entry_point="gpio_app", cdefines=["APP_GPIO"], requires=["gui"], diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 06968bba4..e5f2907af 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -1,7 +1,7 @@ App( appid="ibutton", name="iButton", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, targets=["f7"], entry_point="ibutton_app", cdefines=["APP_IBUTTON"], diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index e5483e9ff..c08cb80bd 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -1,7 +1,7 @@ App( appid="infrared", name="Infrared", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, entry_point="infrared_app", targets=["f7"], cdefines=["APP_INFRARED"], diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index e84ed2a31..60d5f8ac2 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -1,7 +1,7 @@ App( appid="lfrfid", name="RFID", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, targets=["f7"], entry_point="lfrfid_app", cdefines=["APP_LF_RFID"], diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam index 7f30db104..fe8d59bd9 100644 --- a/applications/main/xtreme_app/application.fam +++ b/applications/main/xtreme_app/application.fam @@ -1,7 +1,7 @@ App( appid="xtreme_app", name="Xtreme", - apptype=FlipperAppType.APP, + apptype=FlipperAppType.FAPP, entry_point="xtreme_app", cdefines=["APP_XTREME"], requires=[ From 560233c7003ef74b91ccb743a930479aedba8159 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 22:51:00 +0100 Subject: [PATCH 267/370] Move apps first in main menu --- applications/services/loader/loader_menu.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index 28283f85c..604ae7b7a 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -88,8 +88,14 @@ static uint32_t loader_menu_exit(void* context) { } static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { - size_t i; - for(i = 0; i < FLIPPER_APPS_COUNT; i++) { + menu_add_item( + app->primary_menu, + LOADER_APPLICATIONS_NAME, + &A_Plugins_14, + 0, + loader_menu_applications_callback, + (void*)menu); + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { menu_add_item( app->primary_menu, FLIPPER_APPS[i].name, @@ -99,14 +105,7 @@ static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { (void*)menu); } menu_add_item( - app->primary_menu, "Settings", &A_Settings_14, i++, loader_menu_switch_to_settings, app); - menu_add_item( - app->primary_menu, - LOADER_APPLICATIONS_NAME, - &A_Plugins_14, - i++, - loader_menu_applications_callback, - (void*)menu); + app->primary_menu, "Settings", &A_Settings_14, 0, loader_menu_switch_to_settings, app); }; static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) { @@ -175,4 +174,4 @@ static int32_t loader_menu_thread(void* p) { loader_menu_app_free(app); return 0; -} \ No newline at end of file +} From 81f11358396e9dce3d085a1c4310700c5db234e3 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:06:01 +0100 Subject: [PATCH 268/370] Use standard "assets" dir for FAPPs --- scripts/fbt/appmanifest.py | 2 +- scripts/fbt_tools/fbt_extapps.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 86ed066ed..f570491ea 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -380,7 +380,7 @@ class ApplicationsCGenerator: return f""" {{.app = NULL, .name = "{app.name}", - .appid = "/ext/apps/.Main/{app.appid}.fap", + .appid = "/ext/apps/assets/{app.appid}.fap", .stack_size = 0, .icon = {f"&{app.icon}" if app.icon else "NULL"}, .flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)}}}""" diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 2441ed883..455f841b4 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -179,7 +179,7 @@ class AppBuilder: deployable = False app_artifacts.dist_entries.append((deployable, fal_path)) else: - fap_path = f"apps/{'.Main' if self.app.apptype == FlipperAppType.FAPP else self.app.fap_category}/{app_artifacts.compact.name}" + fap_path = f"apps/{'assets' if self.app.apptype == FlipperAppType.FAPP else self.app.fap_category}/{app_artifacts.compact.name}" app_artifacts.dist_entries.append( (self.app.is_default_deployable, fap_path) ) From ef0203f778ef2be4294d84e3155377bbe4dbfced Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:53:11 +0100 Subject: [PATCH 269/370] Add back loader_show_settings --- .../desktop/scenes/desktop_scene_lock_menu.c | 4 +- applications/services/loader/loader.c | 15 ++++++-- applications/services/loader/loader.h | 6 +++ applications/services/loader/loader_i.h | 1 + applications/services/loader/loader_menu.c | 38 +++++++++++-------- applications/services/loader/loader_menu.h | 4 +- firmware/targets/f7/api_symbols.csv | 1 + 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index be0290930..66d732781 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -80,8 +80,8 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case DesktopLockMenuEventSettings: desktop_scene_lock_menu_save_settings(desktop); - // loader_show_settings(furi_record_open(RECORD_LOADER)); - // furi_record_close(RECORD_LOADER); + loader_show_settings(furi_record_open(RECORD_LOADER)); + furi_record_close(RECORD_LOADER); consumed = true; break; case DesktopLockMenuEventLock: diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 91b6d1a2d..07102eb0e 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -88,6 +88,12 @@ void loader_show_menu(Loader* loader) { furi_message_queue_put(loader->queue, &message, FuriWaitForever); } +void loader_show_settings(Loader* loader) { + LoaderMessage message; + message.type = LoaderMessageTypeShowSettings; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + FuriPubSub* loader_get_pubsub(Loader* loader) { furi_assert(loader); // it's safe to return pubsub without locking @@ -312,9 +318,9 @@ static LoaderStatus loader_start_external_app( // process messages -static void loader_do_menu_show(Loader* loader) { +static void loader_do_menu_show(Loader* loader, bool settings) { if(!loader->loader_menu) { - loader->loader_menu = loader_menu_alloc(loader_menu_closed_callback, loader); + loader->loader_menu = loader_menu_alloc(loader_menu_closed_callback, loader, settings); } } @@ -475,7 +481,10 @@ int32_t loader_srv(void* p) { api_lock_unlock(message.api_lock); break; case LoaderMessageTypeShowMenu: - loader_do_menu_show(loader); + loader_do_menu_show(loader, false); + break; + case LoaderMessageTypeShowSettings: + loader_do_menu_show(loader, true); break; case LoaderMessageTypeMenuClosed: loader_do_menu_closed(loader); diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index d49bec0a4..f88439593 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -72,6 +72,12 @@ bool loader_is_locked(Loader* instance); */ void loader_show_menu(Loader* instance); +/** + * @brief Show settings menu + * @param[in] instance loader instance + */ +void loader_show_settings(Loader* instance); + /** * @brief Get loader pubsub * @param[in] instance loader instance diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index 688b8fb66..cdf01c33c 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -25,6 +25,7 @@ typedef enum { LoaderMessageTypeStartByName, LoaderMessageTypeAppClosed, LoaderMessageTypeShowMenu, + LoaderMessageTypeShowSettings, LoaderMessageTypeMenuClosed, LoaderMessageTypeApplicationsClosed, LoaderMessageTypeLock, diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index 604ae7b7a..c0e3344c3 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -12,16 +12,18 @@ struct LoaderMenu { FuriThread* thread; + bool settings; void (*closed_cb)(void*); void* context; }; static int32_t loader_menu_thread(void* p); -LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context) { +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context, bool settings) { LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); loader_menu->closed_cb = closed_cb; loader_menu->context = context; + loader_menu->settings = settings; loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); furi_thread_start(loader_menu->thread); return loader_menu; @@ -44,6 +46,7 @@ typedef struct { ViewDispatcher* view_dispatcher; Menu* primary_menu; Submenu* settings_menu; + bool settings; } LoaderMenuApp; static void loader_menu_start(const char* name) { @@ -123,37 +126,42 @@ static LoaderMenuApp* loader_menu_app_alloc(LoaderMenu* loader_menu) { LoaderMenuApp* app = malloc(sizeof(LoaderMenuApp)); app->gui = furi_record_open(RECORD_GUI); app->view_dispatcher = view_dispatcher_alloc(); - app->primary_menu = menu_alloc(); - app->settings_menu = submenu_alloc(); - - loader_menu_build_menu(app, loader_menu); - loader_menu_build_submenu(app, loader_menu); + app->settings = loader_menu->settings; // Primary menu - View* primary_view = menu_get_view(app->primary_menu); - view_set_context(primary_view, app->primary_menu); - view_set_previous_callback(primary_view, loader_menu_exit); - view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewPrimary, primary_view); + if(!app->settings) { + app->primary_menu = menu_alloc(); + loader_menu_build_menu(app, loader_menu); + View* primary_view = menu_get_view(app->primary_menu); + view_set_context(primary_view, app->primary_menu); + view_set_previous_callback(primary_view, loader_menu_exit); + view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewPrimary, primary_view); + } // Settings menu + app->settings_menu = submenu_alloc(); + loader_menu_build_submenu(app, loader_menu); View* settings_view = submenu_get_view(app->settings_menu); view_set_context(settings_view, app->settings_menu); - view_set_previous_callback(settings_view, loader_menu_switch_to_primary); + view_set_previous_callback(settings_view, app->settings ? loader_menu_exit : loader_menu_switch_to_primary); view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewSettings, settings_view); view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewPrimary); + view_dispatcher_switch_to_view(app->view_dispatcher, app->settings ? LoaderMenuViewSettings : LoaderMenuViewPrimary); return app; } static void loader_menu_app_free(LoaderMenuApp* app) { - view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewPrimary); + if(!app->settings) { + view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewPrimary); + menu_free(app->primary_menu); + } view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewSettings); + submenu_free(app->settings_menu); + view_dispatcher_free(app->view_dispatcher); - menu_free(app->primary_menu); - submenu_free(app->settings_menu); furi_record_close(RECORD_GUI); free(app); } diff --git a/applications/services/loader/loader_menu.h b/applications/services/loader/loader_menu.h index 528fe7d29..bfb45285a 100644 --- a/applications/services/loader/loader_menu.h +++ b/applications/services/loader/loader_menu.h @@ -7,10 +7,10 @@ extern "C" { typedef struct LoaderMenu LoaderMenu; -LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context); +LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context, bool settings); void loader_menu_free(LoaderMenu* loader_menu); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7535942dc..7d356bef5 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1918,6 +1918,7 @@ Function,+,loader_get_pubsub,FuriPubSub*,Loader* Function,+,loader_is_locked,_Bool,Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* +Function,+,loader_show_settings,void,Loader* Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* From 64ecdc0c45802b094b6a5a7655733ef3de6ffa5c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 25 Jun 2023 23:53:43 +0100 Subject: [PATCH 270/370] Format --- applications/services/loader/loader_menu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index c0e3344c3..45d3d4c92 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -143,11 +143,13 @@ static LoaderMenuApp* loader_menu_app_alloc(LoaderMenu* loader_menu) { loader_menu_build_submenu(app, loader_menu); View* settings_view = submenu_get_view(app->settings_menu); view_set_context(settings_view, app->settings_menu); - view_set_previous_callback(settings_view, app->settings ? loader_menu_exit : loader_menu_switch_to_primary); + view_set_previous_callback( + settings_view, app->settings ? loader_menu_exit : loader_menu_switch_to_primary); view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewSettings, settings_view); view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_switch_to_view(app->view_dispatcher, app->settings ? LoaderMenuViewSettings : LoaderMenuViewPrimary); + view_dispatcher_switch_to_view( + app->view_dispatcher, app->settings ? LoaderMenuViewSettings : LoaderMenuViewPrimary); return app; } From cc5bb03b8698a5605ff36f167f1236d88a1e5653 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 26 Jun 2023 00:17:26 +0100 Subject: [PATCH 271/370] Add back extmainapps --- applications/services/loader/loader.c | 57 +++++++++++++++++++ .../services/loader/loader_extmainapp.h | 14 +++++ applications/services/loader/loader_i.h | 2 + applications/services/loader/loader_menu.c | 20 ++++++- 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 applications/services/loader/loader_extmainapp.h diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 07102eb0e..2e4649002 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -3,11 +3,14 @@ #include #include #include +#include #include #include #include #include +#include +#include #define TAG "Loader" #define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF @@ -102,6 +105,11 @@ FuriPubSub* loader_get_pubsub(Loader* loader) { return loader->pubsub; } +ExtMainAppList_t* loader_get_ext_main_apps(Loader* loader) { + furi_assert(loader); + return &loader->ext_main_apps; +} + // callbacks static void loader_menu_closed_callback(void* context) { @@ -136,6 +144,28 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con // implementation +bool loader_menu_load_fap_meta( + Storage* storage, + FuriString* path, + FuriString* name, + const Icon** icon) { + *icon = NULL; + uint8_t* icon_buf = malloc(CUSTOM_ICON_MAX_SIZE); + if(!flipper_application_load_name_and_icon(path, storage, &icon_buf, name)) { + free(icon_buf); + icon_buf = NULL; + return false; + } + *icon = malloc(sizeof(Icon)); + FURI_CONST_ASSIGN((*icon)->frame_count, 1); + FURI_CONST_ASSIGN((*icon)->frame_rate, 0); + FURI_CONST_ASSIGN((*icon)->width, 10); + FURI_CONST_ASSIGN((*icon)->height, 10); + FURI_CONST_ASSIGN_PTR((*icon)->frames, malloc(sizeof(const uint8_t*))); + FURI_CONST_ASSIGN_PTR((*icon)->frames[0], icon_buf); + return true; +} + static Loader* loader_alloc() { Loader* loader = malloc(sizeof(Loader)); loader->pubsub = furi_pubsub_alloc(); @@ -146,6 +176,33 @@ static Loader* loader_alloc() { loader->app.thread = NULL; loader->app.insomniac = false; loader->app.fap = NULL; + ExtMainAppList_init(loader->ext_main_apps); + + if(furi_hal_is_normal_boot()) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* path = furi_string_alloc(); + FuriString* name = furi_string_alloc(); + Stream* stream = file_stream_alloc(storage); + if(file_stream_open(stream, XTREME_APPS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + while(stream_read_line(stream, path)) { + furi_string_replace_all(path, "\r", ""); + furi_string_replace_all(path, "\n", ""); + const Icon* icon; + if(!loader_menu_load_fap_meta(storage, path, name, &icon)) continue; + ExtMainAppList_push_back( + loader->ext_main_apps, + (ExtMainApp){ + .name = strdup(furi_string_get_cstr(name)), + .path = strdup(furi_string_get_cstr(path)), + .icon = icon}); + } + } + file_stream_close(stream); + stream_free(stream); + furi_string_free(name); + furi_string_free(path); + furi_record_close(RECORD_STORAGE); + } return loader; } diff --git a/applications/services/loader/loader_extmainapp.h b/applications/services/loader/loader_extmainapp.h new file mode 100644 index 000000000..4eeb263c7 --- /dev/null +++ b/applications/services/loader/loader_extmainapp.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +typedef struct { + const char* name; + const char* path; + const Icon* icon; +} ExtMainApp; + +LIST_DEF(ExtMainAppList, ExtMainApp, M_POD_OPLIST) + +ExtMainAppList_t* loader_get_ext_main_apps(Loader* loader); diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index cdf01c33c..cbe01c7f3 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -4,6 +4,7 @@ #include #include "loader.h" #include "loader_menu.h" +#include "loader_extmainapp.h" #include "loader_applications.h" typedef struct { @@ -19,6 +20,7 @@ struct Loader { LoaderMenu* loader_menu; LoaderApplications* loader_applications; LoaderAppData app; + ExtMainAppList_t ext_main_apps; }; typedef enum { diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index 45d3d4c92..de063083c 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -7,6 +7,7 @@ #include "loader.h" #include "loader_menu.h" +#include "loader_extmainapp.h" #define TAG "LoaderMenu" @@ -57,8 +58,8 @@ static void loader_menu_start(const char* name) { static void loader_menu_callback(void* context, uint32_t index) { UNUSED(context); - const char* name = FLIPPER_APPS[index].name; - loader_menu_start(name); + const char* name_or_path = (const char*)index; + loader_menu_start(name_or_path); } static void loader_menu_applications_callback(void* context, uint32_t index) { @@ -103,12 +104,25 @@ static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { app->primary_menu, FLIPPER_APPS[i].name, FLIPPER_APPS[i].icon, - i, + (uint32_t)FLIPPER_APPS[i].name, loader_menu_callback, (void*)menu); } menu_add_item( app->primary_menu, "Settings", &A_Settings_14, 0, loader_menu_switch_to_settings, app); + Loader* loader = furi_record_open(RECORD_LOADER); + ExtMainAppList_t* ext_main_apps = loader_get_ext_main_apps(loader); + for(size_t i = 0; i < ExtMainAppList_size(*ext_main_apps); i++) { + const ExtMainApp* ext_main_app = ExtMainAppList_get(*ext_main_apps, i); + menu_add_item( + app->primary_menu, + ext_main_app->name, + ext_main_app->icon, + (uint32_t)ext_main_app->path, + loader_menu_callback, + (void*)menu); + } + furi_record_close(RECORD_LOADER); }; static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) { From 5a38c821ef043906baf6d819cd4cbd48eb5ee75d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 26 Jun 2023 00:21:14 +0100 Subject: [PATCH 272/370] Add back app names translation for RPC --- applications/services/loader/loader.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 2e4649002..703405a45 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -425,6 +425,16 @@ static LoaderStatus loader_do_start_by_name( break; } + // Translate app names (mainly for RPC, thanks OFW for not using a smart system like appid's :/) + if(!strncmp(name, "Bad USB", strlen("Bad USB"))) + name = "Bad KB"; + else if(!strncmp(name, "Applications", strlen("Applications"))) + name = "Apps"; + else if(!strncmp(name, "125 kHz RFID", strlen("125 kHz RFID"))) + name = "RFID"; + else if(!strncmp(name, "Sub-GHz", strlen("Sub-GHz"))) + name = "SubGHz"; + // check internal apps { const FlipperInternalApplication* app = loader_find_application_by_name(name); From 24c73c9460b85cdb4ffa9c22ccb7a3abfa7cfc83 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 26 Jun 2023 00:54:53 +0100 Subject: [PATCH 273/370] Add back API mismatch ignore dialog --- applications/services/loader/loader.c | 33 ++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 703405a45..e7646bec9 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -328,7 +328,10 @@ static LoaderStatus loader_start_external_app( FlipperApplicationPreloadStatus preload_res = flipper_application_preload(loader->app.fap, path); - if(preload_res != FlipperApplicationPreloadStatusSuccess) { + bool api_mismatch = false; + if(preload_res == FlipperApplicationPreloadStatusApiMismatch) { + api_mismatch = true; + } else if(preload_res != FlipperApplicationPreloadStatusSuccess) { const char* err_msg = flipper_application_preload_status_to_string(preload_res); status = loader_make_status_error( LoaderStatusErrorInternal, error_message, "Preload failed %s: %s", path, err_msg); @@ -338,14 +341,32 @@ static LoaderStatus loader_start_external_app( FURI_LOG_I(TAG, "Mapping"); FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(loader->app.fap); + FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start)); if(load_status != FlipperApplicationLoadStatusSuccess) { const char* err_msg = flipper_application_load_status_to_string(load_status); status = loader_make_status_error( LoaderStatusErrorInternal, error_message, "Load failed %s: %s", path, err_msg); break; + } else if(api_mismatch) { + // Successful map, but found api mismatch -> warn user + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "API Mismatch", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, "Cancel", NULL, "Continue"); + dialog_message_set_text( + message, + "This app might not\nwork correctly\nContinue anyways?", + 64, + 32, + AlignCenter, + AlignCenter); + DialogMessageButton res = dialog_message_show(furi_record_open(RECORD_DIALOGS), message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + if(res != DialogMessageButtonRight) { + break; + } } - FURI_LOG_I(TAG, "Loaded in %zums", (size_t)(furi_get_tick() - start)); FURI_LOG_I(TAG, "Starting app"); loader->app.thread = flipper_application_alloc_thread(loader->app.fap, args); @@ -363,12 +384,12 @@ static LoaderStatus loader_start_external_app( } loader_start_app_thread(loader, FlipperInternalApplicationFlagDefault); + + return status; } while(0); - if(status != LoaderStatusOk) { - flipper_application_free(loader->app.fap); - loader->app.fap = NULL; - } + flipper_application_free(loader->app.fap); + loader->app.fap = NULL; return status; } From b57f912102450a1a74525858d0671d472cab7ebe Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 26 Jun 2023 01:05:02 +0100 Subject: [PATCH 274/370] Add back favorite timeouts --- .../archive/scenes/archive_scene_browser.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 82aa85912..b92cdf386 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -61,19 +61,17 @@ static void } loader_start_with_gui_error(loader, app_name, param); } else { - loader_start_with_gui_error(loader, app_name, furi_string_get_cstr(selected->path)); + const char* str = furi_string_get_cstr(selected->path); + if(favorites) { + char arg[strlen(str) + 4]; + snprintf(arg, sizeof(arg), "fav%s", str); + loader_start_with_gui_error(loader, app_name, arg); + } else { + loader_start_with_gui_error(loader, app_name, str); + } } } else { loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); - UNUSED(favorites); - // const char* str = furi_string_get_cstr(selected->path); - // if(favorites) { - // char arg[strlen(str) + 4]; - // snprintf(arg, sizeof(arg), "fav%s", str); - // status = loader_start(loader, flipper_app_name[selected->type], arg); - // } else { - // status = loader_start(loader, flipper_app_name[selected->type], str); - // } } furi_record_close(RECORD_LOADER); From afb6770f9282d2a7dd0242bb182de3fc10e86ea7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 26 Jun 2023 01:05:31 +0100 Subject: [PATCH 275/370] Format --- applications/services/loader/loader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index e7646bec9..7733c3237 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -359,7 +359,8 @@ static LoaderStatus loader_start_external_app( 32, AlignCenter, AlignCenter); - DialogMessageButton res = dialog_message_show(furi_record_open(RECORD_DIALOGS), message); + DialogMessageButton res = + dialog_message_show(furi_record_open(RECORD_DIALOGS), message); dialog_message_free(message); furi_record_close(RECORD_DIALOGS); if(res != DialogMessageButtonRight) { From 15bac5e02a119f8c125b9873d111176dbd96c7c4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 26 Jun 2023 03:05:46 +0100 Subject: [PATCH 276/370] Fix archive selection index issues after refresh --- .../main/archive/helpers/archive_browser.c | 12 +++--- .../main/archive/helpers/archive_files.c | 39 ++++++++----------- .../main/archive/helpers/archive_files.h | 2 +- .../archive/scenes/archive_scene_browser.c | 15 +++---- .../archive/scenes/archive_scene_new_dir.c | 8 ++-- .../archive/scenes/archive_scene_rename.c | 9 +++-- .../gui/modules/file_browser_worker.c | 20 ++++++---- .../gui/modules/file_browser_worker.h | 2 +- firmware/targets/f7/api_symbols.csv | 2 +- 9 files changed, 58 insertions(+), 51 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 2167eeb34..a87d23c8f 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -573,9 +573,11 @@ void archive_leave_dir(ArchiveBrowserView* browser) { void archive_refresh_dir(ArchiveBrowserView* browser) { furi_assert(browser); - int32_t idx_temp = 0; - - with_view_model( - browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false); - file_browser_worker_folder_refresh(browser->worker, idx_temp); + ArchiveFile_t* current = archive_get_current_file(browser); + FuriString* str = furi_string_alloc(); + if(current != NULL) { + path_extract_basename(furi_string_get_cstr(current->path), str); + } + file_browser_worker_folder_refresh(browser->worker, furi_string_get_cstr(str)); + furi_string_free(str); } diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index e1083ecd4..f4c97fe47 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -113,47 +113,45 @@ void archive_delete_file(void* context, const char* format, ...) { FS_Error archive_copy_rename_file_or_dir( void* context, const char* src_path, - const char* dst_path, + FuriString* dst_path, bool copy, bool find_name) { furi_assert(context); + const char* dst_cstr = furi_string_get_cstr(dst_path); - FURI_LOG_I(TAG, "%s from %s to %s", copy ? "Copy" : "Move", src_path, dst_path); + FURI_LOG_I(TAG, "%s from %s to %s", copy ? "Copy" : "Move", src_path, dst_cstr); - ArchiveBrowserView* browser = context; + UNUSED(context); Storage* fs_api = furi_record_open(RECORD_STORAGE); - FuriString* dst_str = furi_string_alloc_set(dst_path); - dst_path = furi_string_get_cstr(dst_str); FileInfo fileinfo; storage_common_stat(fs_api, src_path, &fileinfo); FS_Error error = FSE_OK; - if(!path_contains_only_ascii(dst_path)) { + if(!path_contains_only_ascii(dst_cstr)) { error = FSE_INVALID_NAME; - } else if(!copy && !strcmp(src_path, dst_path)) { + } else if(!copy && !strcmp(src_path, dst_cstr)) { error = FSE_EXIST; } else { - if(find_name && storage_common_exists(fs_api, dst_path)) { + if(find_name && storage_common_exists(fs_api, dst_cstr)) { FuriString* dir_path = furi_string_alloc(); FuriString* filename = furi_string_alloc(); FuriString* file_ext = furi_string_alloc(); - path_extract_dirname(furi_string_get_cstr(dst_str), dir_path); - path_extract_filename(dst_str, filename, true); - path_extract_ext_str(dst_str, file_ext); + path_extract_dirname(dst_cstr, dir_path); + path_extract_filename(dst_path, filename, true); + path_extract_ext_str(dst_path, file_ext); storage_get_next_filename( fs_api, furi_string_get_cstr(dir_path), furi_string_get_cstr(filename), furi_string_get_cstr(file_ext), - dst_str, + dst_path, 255); - furi_string_cat_printf( - dir_path, "/%s%s", furi_string_get_cstr(dst_str), furi_string_get_cstr(file_ext)); - furi_string_set(dst_str, dir_path); + furi_string_cat_printf(dir_path, "/%s%s", dst_cstr, furi_string_get_cstr(file_ext)); + furi_string_set(dst_path, dir_path); furi_string_free(dir_path); furi_string_free(filename); @@ -161,20 +159,19 @@ FS_Error archive_copy_rename_file_or_dir( } if(copy) { - error = storage_common_copy(fs_api, src_path, dst_path); + error = storage_common_copy(fs_api, src_path, dst_cstr); } else { - error = storage_common_rename(fs_api, src_path, dst_path); + error = storage_common_rename(fs_api, src_path, dst_cstr); } } furi_record_close(RECORD_STORAGE); if(!copy && archive_is_favorite("%s", src_path)) { - archive_favorites_rename(src_path, dst_path); + archive_favorites_rename(src_path, dst_cstr); } if(error == FSE_OK) { - FURI_LOG_I(TAG, "%s from %s to %s is DONE", copy ? "Copy" : "Move", src_path, dst_path); - archive_refresh_dir(browser); + FURI_LOG_I(TAG, "%s from %s to %s is DONE", copy ? "Copy" : "Move", src_path, dst_cstr); } else { FURI_LOG_E( TAG, @@ -184,7 +181,5 @@ FS_Error archive_copy_rename_file_or_dir( error); } - furi_string_free(dst_str); - return error; } diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 0d2569b09..ff74121d7 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -116,6 +116,6 @@ void archive_delete_file(void* context, const char* format, ...) FS_Error archive_copy_rename_file_or_dir( void* context, const char* src_path, - const char* dst_path, + FuriString* dst_path, bool copy, bool find_name); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index b92cdf386..551f68924 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -201,14 +201,8 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewStack); archive_show_loading_popup(archive, true); FS_Error error = archive_copy_rename_file_or_dir( - archive->browser, - furi_string_get_cstr(path_src), - furi_string_get_cstr(path_dst), - copy, - true); + archive->browser, furi_string_get_cstr(path_src), path_dst, copy, true); archive_show_loading_popup(archive, false); - furi_string_free(path_src); - furi_string_free(path_dst); if(error != FSE_OK) { FuriString* dialog_msg; dialog_msg = furi_string_alloc(); @@ -220,7 +214,14 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { dialog_message_show_storage_error( archive->dialogs, furi_string_get_cstr(dialog_msg)); furi_string_free(dialog_msg); + } else { + ArchiveFile_t* current = archive_get_current_file(archive->browser); + if(current != NULL) furi_string_set(current->path, path_dst); + view_dispatcher_send_custom_event( + archive->view_dispatcher, ArchiveBrowserEventListRefresh); } + furi_string_free(path_src); + furi_string_free(path_dst); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); } } diff --git a/applications/main/archive/scenes/archive_scene_new_dir.c b/applications/main/archive/scenes/archive_scene_new_dir.c index 235b349d2..ba8226d5b 100644 --- a/applications/main/archive/scenes/archive_scene_new_dir.c +++ b/applications/main/archive/scenes/archive_scene_new_dir.c @@ -55,11 +55,8 @@ bool archive_scene_new_dir_on_event(void* context, SceneManagerEvent event) { error = storage_common_mkdir(fs_api, furi_string_get_cstr(path_dst)); furi_record_close(RECORD_STORAGE); } - archive_refresh_dir(archive->browser); archive_show_loading_popup(archive, false); - furi_string_free(path_dst); - if(error != FSE_OK) { FuriString* dialog_msg; dialog_msg = furi_string_alloc(); @@ -68,7 +65,12 @@ bool archive_scene_new_dir_on_event(void* context, SceneManagerEvent event) { dialog_message_show_storage_error( archive->dialogs, furi_string_get_cstr(dialog_msg)); furi_string_free(dialog_msg); + } else { + ArchiveFile_t* current = archive_get_current_file(archive->browser); + if(current != NULL) furi_string_set(current->path, path_dst); } + + furi_string_free(path_dst); scene_manager_previous_scene(archive->scene_manager); consumed = true; } diff --git a/applications/main/archive/scenes/archive_scene_rename.c b/applications/main/archive/scenes/archive_scene_rename.c index eb2f95489..bf70c80c2 100644 --- a/applications/main/archive/scenes/archive_scene_rename.c +++ b/applications/main/archive/scenes/archive_scene_rename.c @@ -85,11 +85,9 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewStack); archive_show_loading_popup(archive, true); FS_Error error = archive_copy_rename_file_or_dir( - archive->browser, path_src, furi_string_get_cstr(path_dst), false, false); + archive->browser, path_src, path_dst, false, false); archive_show_loading_popup(archive, false); - furi_string_free(path_dst); - if(error != FSE_OK) { FuriString* dialog_msg; dialog_msg = furi_string_alloc(); @@ -98,7 +96,12 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { dialog_message_show_storage_error( archive->dialogs, furi_string_get_cstr(dialog_msg)); furi_string_free(dialog_msg); + } else { + ArchiveFile_t* current = archive_get_current_file(archive->browser); + if(current != NULL) furi_string_set(current->path, path_dst); } + + furi_string_free(path_dst); scene_manager_previous_scene(archive->scene_manager); consumed = true; } diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 1431041fd..5f2b14053 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -39,7 +39,6 @@ struct BrowserWorker { FuriString* path_start; FuriString* path_current; FuriString* path_next; - int32_t item_sel_idx; uint32_t load_offset; uint32_t load_count; bool skip_assets; @@ -325,7 +324,6 @@ static int32_t browser_worker(void* context) { uint32_t items_cnt = 0; FuriString* path; path = furi_string_alloc_set(BROWSER_ROOT); - browser->item_sel_idx = -1; FuriString* filename; filename = furi_string_alloc(); @@ -387,20 +385,22 @@ static int32_t browser_worker(void* context) { } if(flags & WorkerEvtFolderRefresh) { + furi_string_set(filename, browser->path_next); + bool is_root = browser_folder_check_and_switch(path); int32_t file_idx = 0; - furi_string_reset(filename); browser_folder_init(browser, path, filename, &items_cnt, &file_idx); FURI_LOG_D( TAG, "Refresh folder: %s items: %lu idx: %ld", furi_string_get_cstr(path), items_cnt, - browser->item_sel_idx); + file_idx); if(browser->folder_cb) { - browser->folder_cb(browser->cb_ctx, items_cnt, browser->item_sel_idx, is_root); + browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); } + furi_string_reset(filename); } if(flags & WorkerEvtLoad) { @@ -517,7 +517,7 @@ void file_browser_worker_set_config( void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx) { furi_assert(browser); furi_string_set(browser->path_next, path); - browser->item_sel_idx = item_idx; + UNUSED(item_idx); furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); } @@ -531,9 +531,13 @@ void file_browser_worker_folder_exit(BrowserWorker* browser) { furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderExit); } -void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) { +void file_browser_worker_folder_refresh(BrowserWorker* browser, const char* item_name) { furi_assert(browser); - browser->item_sel_idx = item_idx; + if(item_name != NULL) { + furi_string_set(browser->path_next, item_name); + } else { + furi_string_reset(browser->path_next); + } furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderRefresh); } diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index 859b11be4..02d7266e3 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -64,7 +64,7 @@ bool file_browser_worker_is_in_start_folder(BrowserWorker* browser); void file_browser_worker_folder_exit(BrowserWorker* browser); -void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx); +void file_browser_worker_folder_refresh(BrowserWorker* browser, const char* item_name); void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b20a58a33..734862ceb 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -927,7 +927,7 @@ Function,+,file_browser_stop,void,FileBrowser* Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool" Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t" Function,+,file_browser_worker_folder_exit,void,BrowserWorker* -Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" +Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, const char*" Function,+,file_browser_worker_free,void,BrowserWorker* Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" From 3d1ef1aae37619ed901fd9e3fe68a25aba663a02 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov Date: Mon, 26 Jun 2023 14:48:57 +0300 Subject: [PATCH 277/370] Disable subghz item scrolling * Show time on receive new signal also --- applications/main/subghz/views/receiver.c | 72 +++++++++++------------ 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 53921f866..279d0d350 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -14,8 +14,7 @@ #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f -#define SCROLL_INTERVAL (606) -#define SCROLL_DELAY (2) +#define FLIP_TIMEOUT (500) typedef struct { FuriString* item_str; @@ -55,7 +54,7 @@ struct SubGhzViewReceiver { View* view; SubGhzViewReceiverCallback callback; void* context; - FuriTimer* scroll_timer; + FuriTimer* flip_time_timer; }; typedef struct { @@ -70,7 +69,7 @@ typedef struct { SubGhzViewReceiverBarShow bar_show; SubGhzViewReceiverMode mode; uint8_t u_rssi; - size_t scroll_counter; + bool show_time; bool nodraw; } SubGhzViewReceiverModel; @@ -148,6 +147,24 @@ static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiv true); } +static void subghz_view_receiver_show_time_moment(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, SubGhzViewReceiverModel * model, { model->show_time = true; }, true); + furi_timer_start(subghz_receiver->flip_time_timer, FLIP_TIMEOUT); +} + +static void subghz_view_receiver_flip_string_callback(void* context) { + furi_assert(context); + SubGhzViewReceiver* subghz_receiver = context; + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->show_time = false; }, + true); +} + void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, @@ -166,6 +183,7 @@ void subghz_view_receiver_add_item_to_menu( if((model->idx == model->history_item - 1)) { model->history_item++; model->idx++; + subghz_view_receiver_show_time_moment(subghz_receiver); } else { model->history_item++; } @@ -255,29 +273,17 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { break; } furi_string_set(str_buff, item_menu->item_str); - size_t scroll_counter = model->scroll_counter; if(model->idx == idx) { subghz_view_receiver_draw_frame(canvas, i, scrollbar); - if(scroll_counter < SCROLL_DELAY) { + if(model->show_time) { // Show time of signal one moment furi_string_set(str_buff, item_menu->time); - scroll_counter = 0; - } else { - scroll_counter -= SCROLL_DELAY; } } else { canvas_set_color(canvas, ColorBlack); - scroll_counter = 0; } canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - elements_scrollable_text_line( - canvas, - 15, - 9 + i * FRAME_HEIGHT, - (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX), - str_buff, - scroll_counter, - (model->idx != idx)); + canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); furi_string_reset(str_buff); } if(scrollbar) { @@ -384,13 +390,6 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } } -static void subghz_view_receiver_scroll_timer_callback(void* context) { - furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - with_view_model( - subghz_receiver->view, SubGhzViewReceiverModel * model, { model->scroll_counter++; }, true); -} - static void subghz_view_receiver_timer_callback(void* context) { furi_assert(context); SubGhzViewReceiver* subghz_receiver = context; @@ -450,7 +449,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { SubGhzViewReceiverModel * model, { if(model->idx != 0) model->idx--; - model->scroll_counter = 0; + subghz_view_receiver_show_time_moment(context); }, true); } else if( @@ -460,9 +459,10 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { subghz_receiver->view, SubGhzViewReceiverModel * model, { - if((model->history_item != 0) && (model->idx != model->history_item - 1)) + if((model->history_item != 0) && (model->idx != model->history_item - 1)) { model->idx++; - model->scroll_counter = 0; + subghz_view_receiver_show_time_moment(context); + } }, true); } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { @@ -499,13 +499,7 @@ bool subghz_view_receiver_input(InputEvent* event, void* context) { void subghz_view_receiver_enter(void* context) { furi_assert(context); - SubGhzViewReceiver* subghz_receiver = context; - with_view_model( - subghz_receiver->view, - SubGhzViewReceiverModel * model, - { model->scroll_counter = 0; }, - true); - furi_timer_start(subghz_receiver->scroll_timer, SCROLL_INTERVAL); + subghz_view_receiver_show_time_moment(context); } void subghz_view_receiver_exit(void* context) { @@ -533,7 +527,7 @@ void subghz_view_receiver_exit(void* context) { }, false); furi_timer_stop(subghz_receiver->timer); - furi_timer_stop(subghz_receiver->scroll_timer); + furi_timer_stop(subghz_receiver->flip_time_timer); } SubGhzViewReceiver* subghz_view_receiver_alloc() { @@ -552,8 +546,8 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { view_set_enter_callback(subghz_receiver->view, subghz_view_receiver_enter); view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit); - subghz_receiver->scroll_timer = furi_timer_alloc( - subghz_view_receiver_scroll_timer_callback, FuriTimerTypePeriodic, subghz_receiver); + subghz_receiver->flip_time_timer = furi_timer_alloc( + subghz_view_receiver_flip_string_callback, FuriTimerTypeOnce, subghz_receiver); with_view_model( subghz_receiver->view, @@ -577,7 +571,7 @@ SubGhzViewReceiver* subghz_view_receiver_alloc() { void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) { furi_assert(subghz_receiver); - furi_timer_free(subghz_receiver->scroll_timer); + furi_timer_free(subghz_receiver->flip_time_timer); with_view_model( subghz_receiver->view, From 0b6848751992ac17e2c19ed075e050bdbf9885d3 Mon Sep 17 00:00:00 2001 From: Nikita Vostokov Date: Mon, 26 Jun 2023 15:01:38 +0300 Subject: [PATCH 278/370] Fix cursor hiding after delete and exit from exit dialog --- applications/main/subghz/scenes/subghz_scene_receiver.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index d9fd38836..3076904c5 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -204,6 +204,9 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { SubGhz* subghz = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { + // Save cursor position before going to any other dialog + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); + switch(event.event) { case SubGhzCustomEventViewReceiverBack: // Stop CC1101 Rx @@ -226,23 +229,21 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReceiverOK: // Show file info, scene: receiver_info - subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo); dolphin_deed(DolphinDeedSubGhzReceiverInfo); consumed = true; break; case SubGhzCustomEventViewReceiverDeleteItem: subghz->state_notifications = SubGhzNotificationStateRx; - subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver); subghz_history_delete_item(subghz->history, subghz->idx_menu_chosen); subghz_view_receiver_delete_element_callback(subghz->subghz_receiver); - subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver); subghz_scene_receiver_update_statusbar(subghz); + subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); if(subghz_history_get_last_index(subghz->history) == 0) { subghz_rx_key_state_set(subghz, SubGhzRxKeyStateStart); } @@ -250,7 +251,6 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { break; case SubGhzCustomEventViewReceiverConfig: // Actually signals are received but SubGhzNotificationStateRx is not working inside Config Scene - subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver); scene_manager_set_scene_state( subghz->scene_manager, SubGhzViewIdReceiver, SubGhzCustomEventManagerSet); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig); From c5ec72692cfb125ef0fab59a9f951b4b389f0cc4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 26 Jun 2023 17:21:44 +0100 Subject: [PATCH 279/370] Better ios demo badkb script --- assets/resources/badkb/demo_ios.txt | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/assets/resources/badkb/demo_ios.txt b/assets/resources/badkb/demo_ios.txt index ed7b4d8cc..eca294e47 100644 --- a/assets/resources/badkb/demo_ios.txt +++ b/assets/resources/badkb/demo_ios.txt @@ -1,16 +1,13 @@ REM Version 1.0 REM OS: iOS -REM Author: Peaakss +REM Author: Peaakss | Optimized for older versions by WillyJL REM Description: A simple payload that opens safari and inserts a link REM NOTICE CHANGE "STRING" to your desired link | EXAMPLE: STRING https://github.com/ClaraCrazy/Flipper-Xtreme | -REM NOITCE Payload was made on iOS 16.1 - iPhone | Timing may have have to be changed based on version/model GUI h -DELAY 100 +DELAY 420 GUI SPACE -DELAY 150 -BACKSPACE -DELAY 250 +DELAY 420 STRING https://github.com/ClaraCrazy/Flipper-Xtreme -DELAY 250 +DELAY 69 ENTER From 168fa72d53bd83a77ead8cc4db04ba403f0299c2 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Tue, 27 Jun 2023 12:22:35 +0300 Subject: [PATCH 280/370] [FL-3373] Scroll acceleration (#2784) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support for scroll acceleration * Revert keyboard acceleration * Add scroll acceleration to the text box * Remove redundant code from the file manager input handler * Archive: slightly better scrolling * Gui,Archive: final version of accelerated scrolling Co-authored-by: あく --- .../main/archive/views/archive_browser_view.c | 32 ++++++++- .../main/archive/views/archive_browser_view.h | 2 + .../services/gui/modules/file_browser.c | 34 ++++++++- applications/services/gui/modules/text_box.c | 70 ++++++++++++++----- 4 files changed, 115 insertions(+), 23 deletions(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 2aca3c02b..7e2f84fc2 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -334,9 +334,22 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->view, ArchiveBrowserViewModel * model, { + int32_t scroll_speed = 1; + if(model->button_held_for_ticks > 5) { + if(model->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = model->button_held_for_ticks > 9 ? 4 : 2; + } + } + if(event->key == InputKeyUp) { + if(model->item_idx < scroll_speed) { + scroll_speed = model->item_idx; + } + model->item_idx = - ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; + ((model->item_idx - scroll_speed) + model->item_cnt) % model->item_cnt; if(is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context); @@ -345,8 +358,14 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); } model->scroll_counter = 0; + model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { - model->item_idx = (model->item_idx + 1) % model->item_cnt; + int32_t count = model->item_cnt; + if(model->item_idx >= (count - scroll_speed)) { + scroll_speed = model->item_cnt - model->item_idx - 1; + } + + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; if(is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadNextItems, browser->context); @@ -355,6 +374,7 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); } model->scroll_counter = 0; + model->button_held_for_ticks += 1; } }, true); @@ -391,6 +411,14 @@ static bool archive_view_input(InputEvent* event, void* context) { } } + if(event->type == InputTypeRelease) { + with_view_model( + browser->view, + ArchiveBrowserViewModel * model, + { model->button_held_for_ticks = 0; }, + true); + } + return true; } diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 0a000e5ac..25490aedd 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -100,6 +100,8 @@ typedef struct { int32_t array_offset; int32_t list_offset; size_t scroll_counter; + + uint32_t button_held_for_ticks; } ArchiveBrowserViewModel; void archive_browser_set_callback( diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index d12a00ba5..b2a2c3c23 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -118,6 +118,8 @@ typedef struct { const Icon* file_icon; bool hide_ext; size_t scroll_counter; + + uint32_t button_held_for_ticks; } FileBrowserModel; static const Icon* BrowserItemIcons[] = { @@ -589,9 +591,22 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->view, FileBrowserModel * model, { + int32_t scroll_speed = 1; + if(model->button_held_for_ticks > 5) { + if(model->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = model->button_held_for_ticks > 9 ? 5 : 3; + } + } + if(event->key == InputKeyUp) { + if(model->item_idx < scroll_speed) { + scroll_speed = model->item_idx; + } + model->item_idx = - ((model->item_idx - 1) + model->item_cnt) % model->item_cnt; + ((model->item_idx - scroll_speed) + model->item_cnt) % model->item_cnt; if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -602,8 +617,15 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->worker, load_offset, ITEM_LIST_LEN_MAX); } model->scroll_counter = 0; + + model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { - model->item_idx = (model->item_idx + 1) % model->item_cnt; + int32_t count = model->item_cnt; + if(model->item_idx + scroll_speed >= count) { + scroll_speed = count - model->item_idx - 1; + } + + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -614,11 +636,19 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { browser->worker, load_offset, ITEM_LIST_LEN_MAX); } model->scroll_counter = 0; + + model->button_held_for_ticks += 1; } }, true); browser_update_offset(browser); consumed = true; + } else if(event->type == InputTypeRelease) { + with_view_model( + browser->view, + FileBrowserModel * model, + { model->button_held_for_ticks = 0; }, + true); } } else if(event->key == InputKeyOk) { if(event->type == InputTypeShort) { diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 01ccdbf52..0e4aae944 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -6,6 +6,8 @@ struct TextBox { View* view; + + uint16_t button_held_for_ticks; }; typedef struct { @@ -19,36 +21,52 @@ typedef struct { bool formatted; } TextBoxModel; -static void text_box_process_down(TextBox* text_box) { +static void text_box_process_down(TextBox* text_box, uint8_t lines) { with_view_model( text_box->view, TextBoxModel * model, { - if(model->scroll_pos < model->scroll_num - 1) { - model->scroll_pos++; - // Search next line start - while(*model->text_pos++ != '\n') - ; + if(model->scroll_pos < model->scroll_num - lines) { + model->scroll_pos += lines; + for(uint8_t i = 0; i < lines; i++) { + // Search next line start + while(*model->text_pos++ != '\n') + ; + } + } else if(lines > 1) { + lines = model->scroll_num - model->scroll_pos - 1; + model->scroll_pos = model->scroll_num - 1; + for(uint8_t i = 0; i < lines; i++) { + // Search next line start + while(*model->text_pos++ != '\n') + ; + } } }, true); } -static void text_box_process_up(TextBox* text_box) { +static void text_box_process_up(TextBox* text_box, uint8_t lines) { with_view_model( text_box->view, TextBoxModel * model, { - if(model->scroll_pos > 0) { - model->scroll_pos--; - // Reach last symbol of previous line - model->text_pos--; - // Search previous line start - while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) - ; - if(*model->text_pos == '\n') { - model->text_pos++; + if(model->scroll_pos > lines - 1) { + model->scroll_pos -= lines; + for(uint8_t i = 0; i < lines; i++) { + // Reach last symbol of previous line + model->text_pos--; + // Search previous line start + while((model->text_pos != model->text) && (*(--model->text_pos) != '\n')) + ; + if(*model->text_pos == '\n') { + model->text_pos++; + } } + } else if(lines > 1) { + lines = model->scroll_pos; + model->scroll_pos = 0; + model->text_pos = (char*)model->text; } }, true); @@ -120,14 +138,28 @@ static bool text_box_view_input_callback(InputEvent* event, void* context) { TextBox* text_box = context; bool consumed = false; - if(event->type == InputTypeShort) { + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + int32_t scroll_speed = 1; + if(text_box->button_held_for_ticks > 5) { + if(text_box->button_held_for_ticks % 2) { + scroll_speed = 0; + } else { + scroll_speed = text_box->button_held_for_ticks > 9 ? 5 : 3; + } + } + if(event->key == InputKeyDown) { - text_box_process_down(text_box); + text_box_process_down(text_box, scroll_speed); consumed = true; } else if(event->key == InputKeyUp) { - text_box_process_up(text_box); + text_box_process_up(text_box, scroll_speed); consumed = true; } + + text_box->button_held_for_ticks++; + } else if(event->type == InputTypeRelease) { + text_box->button_held_for_ticks = 0; + consumed = true; } return consumed; } From 75354ec5bac8be5f631213c8e236123c2bcea075 Mon Sep 17 00:00:00 2001 From: Petr Portnov | PROgrm_JARvis Date: Tue, 27 Jun 2023 12:46:04 +0300 Subject: [PATCH 281/370] fix: make `dialog_file_browser_set_basic_options` initialize all fields (#2756) * fix: make `dialog_file_browser_set_basic_options` initialize all fields * fix(GH-2756): use alternative test for `test_dialog_file_browser_set_basic_options_should_init_all_fields` Co-authored-by: Aleksandr Kutuzov --- CODING_STYLE.md | 2 +- .../dialogs/dialogs_file_browser_options.c | 32 +++++++++++++++++++ applications/debug/unit_tests/test_index.c | 3 ++ applications/services/dialogs/dialogs.c | 3 +- applications/services/dialogs/dialogs.h | 9 ++++-- 5 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c diff --git a/CODING_STYLE.md b/CODING_STYLE.md index c62009eff..002c67f24 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -48,7 +48,7 @@ Almost everything in flipper firmware is built around this concept. # C coding style - Tab is 4 spaces -- Use `fbt format` to reformat source code and check style guide before commit +- Use `./fbt format` to reformat source code and check style guide before commit ## Naming diff --git a/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c b/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c new file mode 100644 index 000000000..2d5bad4c8 --- /dev/null +++ b/applications/debug/unit_tests/dialogs/dialogs_file_browser_options.c @@ -0,0 +1,32 @@ +#include + +#include "../minunit.h" + +MU_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields) { + mu_assert( + sizeof(DialogsFileBrowserOptions) == 28, + "Changes to `DialogsFileBrowserOptions` should also be reflected in `dialog_file_browser_set_basic_options`"); + + DialogsFileBrowserOptions options; + dialog_file_browser_set_basic_options(&options, ".fap", NULL); + // note: this assertions can safely be changed, their primary purpose is to remind the maintainer + // to update `dialog_file_browser_set_basic_options` by including all structure fields in it + mu_assert_string_eq(".fap", options.extension); + mu_assert_null(options.base_path); + mu_assert(options.skip_assets, "`skip_assets` should default to `true"); + mu_assert(options.hide_dot_files, "`hide_dot_files` should default to `true"); + mu_assert_null(options.icon); + mu_assert(options.hide_ext, "`hide_ext` should default to `true"); + mu_assert_null(options.item_loader_callback); + mu_assert_null(options.item_loader_context); +} + +MU_TEST_SUITE(dialogs_file_browser_options) { + MU_RUN_TEST(test_dialog_file_browser_set_basic_options_should_init_all_fields); +} + +int run_minunit_test_dialogs_file_browser_options() { + MU_RUN_SUITE(dialogs_file_browser_options); + + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index ac71ca397..9d7631bfe 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -27,6 +27,7 @@ int run_minunit_test_nfc(); int run_minunit_test_bit_lib(); int run_minunit_test_float_tools(); int run_minunit_test_bt(); +int run_minunit_test_dialogs_file_browser_options(); typedef int (*UnitTestEntry)(); @@ -55,6 +56,8 @@ const UnitTest unit_tests[] = { {.name = "bit_lib", .entry = run_minunit_test_bit_lib}, {.name = "float_tools", .entry = run_minunit_test_float_tools}, {.name = "bt", .entry = run_minunit_test_bt}, + {.name = "dialogs_file_browser_options", + .entry = run_minunit_test_dialogs_file_browser_options}, }; void minunit_print_progress() { diff --git a/applications/services/dialogs/dialogs.c b/applications/services/dialogs/dialogs.c index 3908ca31b..10c08a991 100644 --- a/applications/services/dialogs/dialogs.c +++ b/applications/services/dialogs/dialogs.c @@ -9,12 +9,13 @@ void dialog_file_browser_set_basic_options( const char* extension, const Icon* icon) { options->extension = extension; + options->base_path = NULL; options->skip_assets = true; + options->hide_dot_files = true; options->icon = icon; options->hide_ext = true; options->item_loader_callback = NULL; options->item_loader_context = NULL; - options->base_path = NULL; } static DialogsApp* dialogs_app_alloc() { diff --git a/applications/services/dialogs/dialogs.h b/applications/services/dialogs/dialogs.h index 4c1b675a6..39b15c67c 100644 --- a/applications/services/dialogs/dialogs.h +++ b/applications/services/dialogs/dialogs.h @@ -16,7 +16,8 @@ typedef struct DialogsApp DialogsApp; /****************** FILE BROWSER ******************/ /** - * File browser dialog extra options + * File browser dialog extra options. + * This can be default-initialized using {@link dialog_file_browser_set_basic_options}. * @param extension file extension to be offered for selection * @param base_path root folder path for navigation with back key * @param skip_assets true - do not show assets folders @@ -38,8 +39,10 @@ typedef struct { } DialogsFileBrowserOptions; /** - * Initialize file browser dialog options - * and set default values + * Initialize file browser dialog options and set default values. + * This is guaranteed to initialize all fields + * so it is safe to pass pointer to uninitialized {@code options} + * and assume that the data behind it becomes fully initialized after the call. * @param options pointer to options structure * @param extension file extension to filter * @param icon file icon pointer, NULL for default icon From 0a5508a8a1ab202131262702901eaf0e388b212a Mon Sep 17 00:00:00 2001 From: PpHd Date: Tue, 27 Jun 2023 12:50:09 +0200 Subject: [PATCH 282/370] Fix M*LIB usage (#2762) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix M*LIB usage * Fix oplist definition of SubGhzFrequencyAnalyzerLogItem * Fix oplist definition of M_CSTR_DUP_OPLIST * Remove dependency of furi_string_utf8_decode to the internal definition of string_unicode_t * Replace obsolete macro M_IF_DEFAULT1 to M_DEFAULT_ARGS Co-authored-by: hedger Co-authored-by: あく --- ...subghz_frequency_analyzer_log_item_array.h | 7 ++- furi/core/string.c | 4 +- furi/core/string.h | 44 ++++++++----------- lib/toolbox/m_cstr_dup.h | 19 ++++---- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h index b94ebe380..12c6bef6a 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -27,7 +27,12 @@ TUPLE_DEF2( (rssi_max, uint8_t)) /* Register globally the oplist */ #define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ - TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) + TUPLE_OPLIST( \ + SubGhzFrequencyAnalyzerLogItem, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST, \ + M_DEFAULT_OPLIST) /* Define the array, register the oplist and define further algorithms on it */ ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) diff --git a/furi/core/string.c b/furi/core/string.c index 4384fe06a..682c8d409 100644 --- a/furi/core/string.c +++ b/furi/core/string.c @@ -296,7 +296,9 @@ static FuriStringUTF8State state_to_furi_state(m_str1ng_utf8_state_e state) { } void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode) { + string_unicode_t m_u = *unicode; m_str1ng_utf8_state_e m_state = furi_state_to_state(*state); - m_str1ng_utf8_decode(c, &m_state, unicode); + m_str1ng_utf8_decode(c, &m_state, &m_u); *state = state_to_furi_state(m_state); + *unicode = m_u; } diff --git a/furi/core/string.h b/furi/core/string.h index 0523d3ba0..7529deacd 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -633,20 +633,17 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Search for a string (or C string) in a string * (string, [c]string[, start=0]) */ -#define furi_string_search(v, ...) \ - M_APPLY( \ - FURI_STRING_SELECT3, \ - furi_string_search, \ - furi_string_search_str, \ - v, \ - M_IF_DEFAULT1(0, __VA_ARGS__)) - +#define furi_string_search(...) \ + M_APPLY( \ + FURI_STRING_SELECT3, \ + furi_string_search, \ + furi_string_search_str, \ + M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Search for a C string in a string * (string, cstring[, start=0]) */ -#define furi_string_search_str(v, ...) \ - M_APPLY(furi_string_search_str, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_str(...) furi_string_search_str(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Test if the string starts with the given string (or C string). @@ -672,41 +669,36 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico * @brief Trim a string from the given set of characters (default is " \n\r\t"). * (string[, set=" \n\r\t"]) */ -#define furi_string_trim(...) M_APPLY(furi_string_trim, M_IF_DEFAULT1(" \n\r\t", __VA_ARGS__)) +#define furi_string_trim(...) furi_string_trim(M_DEFAULT_ARGS(2, (" \n\r\t"), __VA_ARGS__)) /** * @brief Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_char(v, ...) \ - M_APPLY(furi_string_search_char, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_char(...) furi_string_search_char(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Reverse Search for a character in a string. * (string, character[, start=0]) */ -#define furi_string_search_rchar(v, ...) \ - M_APPLY(furi_string_search_rchar, v, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_search_rchar(...) furi_string_search_rchar(M_DEFAULT_ARGS(3, (0), __VA_ARGS__)) /** * @brief Replace a string to another string (or C string to another C string) in a string. * (string, [c]string, [c]string[, start=0]) */ -#define furi_string_replace(a, b, ...) \ - M_APPLY( \ - FURI_STRING_SELECT4, \ - furi_string_replace, \ - furi_string_replace_str, \ - a, \ - b, \ - M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_replace(...) \ + M_APPLY( \ + FURI_STRING_SELECT4, \ + furi_string_replace, \ + furi_string_replace_str, \ + M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief Replace a C string to another C string in a string. * (string, cstring, cstring[, start=0]) */ -#define furi_string_replace_str(a, b, ...) \ - M_APPLY(furi_string_replace_str, a, b, M_IF_DEFAULT1(0, __VA_ARGS__)) +#define furi_string_replace_str(...) furi_string_replace_str(M_DEFAULT_ARGS(4, (0), __VA_ARGS__)) /** * @brief INIT OPLIST for FuriString. @@ -743,4 +735,4 @@ void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnico #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/toolbox/m_cstr_dup.h b/lib/toolbox/m_cstr_dup.h index 0555f72c6..11b7fe35a 100644 --- a/lib/toolbox/m_cstr_dup.h +++ b/lib/toolbox/m_cstr_dup.h @@ -2,15 +2,16 @@ #include #define M_INIT_DUP(a) ((a) = strdup("")) -#define M_SET_DUP(a, b) (M_CHECK_DEFAULT_TYPE(a), free((void*)a), (a) = strdup(b)) +#define M_INIT_SET_DUP(a, b) ((a) = strdup(b)) +#define M_SET_DUP(a, b) (free((void*)a), (a) = strdup(b)) #define M_CLEAR_DUP(a) (free((void*)a)) -#define M_CSTR_DUP_OPLIST \ - (INIT(M_INIT_DUP), \ - INIT_SET(M_SET_DUP), \ - SET(M_SET_DUP), \ - CLEAR(M_CLEAR_DUP), \ - HASH(m_core_cstr_hash), \ - EQUAL(M_CSTR_EQUAL), \ - CMP(strcmp), \ +#define M_CSTR_DUP_OPLIST \ + (INIT(M_INIT_DUP), \ + INIT_SET(M_INIT_SET_DUP), \ + SET(M_SET_DUP), \ + CLEAR(M_CLEAR_DUP), \ + HASH(m_core_cstr_hash), \ + EQUAL(M_CSTR_EQUAL), \ + CMP(strcmp), \ TYPE(const char*)) From 4e6e8ece0723abefab1a48a311384803308b8a46 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 27 Jun 2023 17:54:40 +0200 Subject: [PATCH 283/370] Remove usb mass storage for now --- .../external/{mass_storage => .mass_storage}/application.fam | 0 .../{mass_storage => .mass_storage}/helpers/mass_storage_scsi.c | 0 .../{mass_storage => .mass_storage}/helpers/mass_storage_scsi.h | 0 .../{mass_storage => .mass_storage}/helpers/mass_storage_usb.c | 0 .../{mass_storage => .mass_storage}/helpers/mass_storage_usb.h | 0 .../external/{mass_storage => .mass_storage}/mass_storage_app.c | 0 .../external/{mass_storage => .mass_storage}/mass_storage_app.h | 0 .../external/{mass_storage => .mass_storage}/mass_storage_app_i.h | 0 .../{mass_storage => .mass_storage}/scenes/mass_storage_scene.c | 0 .../{mass_storage => .mass_storage}/scenes/mass_storage_scene.h | 0 .../scenes/mass_storage_scene_config.h | 0 .../scenes/mass_storage_scene_error.c | 0 .../scenes/mass_storage_scene_file_select.c | 0 .../scenes/mass_storage_scene_work.c | 0 .../{mass_storage => .mass_storage}/views/mass_storage_view.c | 0 .../{mass_storage => .mass_storage}/views/mass_storage_view.h | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename applications/external/{mass_storage => .mass_storage}/application.fam (100%) rename applications/external/{mass_storage => .mass_storage}/helpers/mass_storage_scsi.c (100%) rename applications/external/{mass_storage => .mass_storage}/helpers/mass_storage_scsi.h (100%) rename applications/external/{mass_storage => .mass_storage}/helpers/mass_storage_usb.c (100%) rename applications/external/{mass_storage => .mass_storage}/helpers/mass_storage_usb.h (100%) rename applications/external/{mass_storage => .mass_storage}/mass_storage_app.c (100%) rename applications/external/{mass_storage => .mass_storage}/mass_storage_app.h (100%) rename applications/external/{mass_storage => .mass_storage}/mass_storage_app_i.h (100%) rename applications/external/{mass_storage => .mass_storage}/scenes/mass_storage_scene.c (100%) rename applications/external/{mass_storage => .mass_storage}/scenes/mass_storage_scene.h (100%) rename applications/external/{mass_storage => .mass_storage}/scenes/mass_storage_scene_config.h (100%) rename applications/external/{mass_storage => .mass_storage}/scenes/mass_storage_scene_error.c (100%) rename applications/external/{mass_storage => .mass_storage}/scenes/mass_storage_scene_file_select.c (100%) rename applications/external/{mass_storage => .mass_storage}/scenes/mass_storage_scene_work.c (100%) rename applications/external/{mass_storage => .mass_storage}/views/mass_storage_view.c (100%) rename applications/external/{mass_storage => .mass_storage}/views/mass_storage_view.h (100%) diff --git a/applications/external/mass_storage/application.fam b/applications/external/.mass_storage/application.fam similarity index 100% rename from applications/external/mass_storage/application.fam rename to applications/external/.mass_storage/application.fam diff --git a/applications/external/mass_storage/helpers/mass_storage_scsi.c b/applications/external/.mass_storage/helpers/mass_storage_scsi.c similarity index 100% rename from applications/external/mass_storage/helpers/mass_storage_scsi.c rename to applications/external/.mass_storage/helpers/mass_storage_scsi.c diff --git a/applications/external/mass_storage/helpers/mass_storage_scsi.h b/applications/external/.mass_storage/helpers/mass_storage_scsi.h similarity index 100% rename from applications/external/mass_storage/helpers/mass_storage_scsi.h rename to applications/external/.mass_storage/helpers/mass_storage_scsi.h diff --git a/applications/external/mass_storage/helpers/mass_storage_usb.c b/applications/external/.mass_storage/helpers/mass_storage_usb.c similarity index 100% rename from applications/external/mass_storage/helpers/mass_storage_usb.c rename to applications/external/.mass_storage/helpers/mass_storage_usb.c diff --git a/applications/external/mass_storage/helpers/mass_storage_usb.h b/applications/external/.mass_storage/helpers/mass_storage_usb.h similarity index 100% rename from applications/external/mass_storage/helpers/mass_storage_usb.h rename to applications/external/.mass_storage/helpers/mass_storage_usb.h diff --git a/applications/external/mass_storage/mass_storage_app.c b/applications/external/.mass_storage/mass_storage_app.c similarity index 100% rename from applications/external/mass_storage/mass_storage_app.c rename to applications/external/.mass_storage/mass_storage_app.c diff --git a/applications/external/mass_storage/mass_storage_app.h b/applications/external/.mass_storage/mass_storage_app.h similarity index 100% rename from applications/external/mass_storage/mass_storage_app.h rename to applications/external/.mass_storage/mass_storage_app.h diff --git a/applications/external/mass_storage/mass_storage_app_i.h b/applications/external/.mass_storage/mass_storage_app_i.h similarity index 100% rename from applications/external/mass_storage/mass_storage_app_i.h rename to applications/external/.mass_storage/mass_storage_app_i.h diff --git a/applications/external/mass_storage/scenes/mass_storage_scene.c b/applications/external/.mass_storage/scenes/mass_storage_scene.c similarity index 100% rename from applications/external/mass_storage/scenes/mass_storage_scene.c rename to applications/external/.mass_storage/scenes/mass_storage_scene.c diff --git a/applications/external/mass_storage/scenes/mass_storage_scene.h b/applications/external/.mass_storage/scenes/mass_storage_scene.h similarity index 100% rename from applications/external/mass_storage/scenes/mass_storage_scene.h rename to applications/external/.mass_storage/scenes/mass_storage_scene.h diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_config.h b/applications/external/.mass_storage/scenes/mass_storage_scene_config.h similarity index 100% rename from applications/external/mass_storage/scenes/mass_storage_scene_config.h rename to applications/external/.mass_storage/scenes/mass_storage_scene_config.h diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_error.c b/applications/external/.mass_storage/scenes/mass_storage_scene_error.c similarity index 100% rename from applications/external/mass_storage/scenes/mass_storage_scene_error.c rename to applications/external/.mass_storage/scenes/mass_storage_scene_error.c diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_file_select.c b/applications/external/.mass_storage/scenes/mass_storage_scene_file_select.c similarity index 100% rename from applications/external/mass_storage/scenes/mass_storage_scene_file_select.c rename to applications/external/.mass_storage/scenes/mass_storage_scene_file_select.c diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_work.c b/applications/external/.mass_storage/scenes/mass_storage_scene_work.c similarity index 100% rename from applications/external/mass_storage/scenes/mass_storage_scene_work.c rename to applications/external/.mass_storage/scenes/mass_storage_scene_work.c diff --git a/applications/external/mass_storage/views/mass_storage_view.c b/applications/external/.mass_storage/views/mass_storage_view.c similarity index 100% rename from applications/external/mass_storage/views/mass_storage_view.c rename to applications/external/.mass_storage/views/mass_storage_view.c diff --git a/applications/external/mass_storage/views/mass_storage_view.h b/applications/external/.mass_storage/views/mass_storage_view.h similarity index 100% rename from applications/external/mass_storage/views/mass_storage_view.h rename to applications/external/.mass_storage/views/mass_storage_view.h From 1c0daf5d5d7c30bcc0ca9fd418edba1f7bf510bc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 27 Jun 2023 18:03:06 +0200 Subject: [PATCH 284/370] Desktop settings menu code cleanup --- .../scenes/desktop_settings_scene_start.c | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 48031904b..5765963c4 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -7,7 +7,6 @@ #define SCENE_EVENT_SELECT_FAVORITE_PRIMARY 0 #define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1 -// #define SCENE_EVENT_SELECT_FAVORITE_GAME 2 #define SCENE_EVENT_SELECT_PIN_SETUP 2 #define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3 #define SCENE_EVENT_SELECT_AUTO_LOCK_PIN 4 @@ -76,8 +75,6 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_list_add(variable_item_list, "Secondary Fav App (Down)", 1, NULL, NULL); - // variable_item_list_add(variable_item_list, "Favorite Game", 1, NULL, NULL); - variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL); item = variable_item_list_add( @@ -120,29 +117,22 @@ void desktop_settings_scene_start_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); } -bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent sme) { +bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) { DesktopSettingsApp* app = context; bool consumed = false; - if(sme.type == SceneManagerEventTypeCustom) { - switch(sme.event) { + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { case SCENE_EVENT_SELECT_FAVORITE_PRIMARY: - scene_manager_set_scene_state( - app->scene_manager, DesktopSettingsAppSceneFavorite, true); + scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 1); scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); consumed = true; break; case SCENE_EVENT_SELECT_FAVORITE_SECONDARY: - scene_manager_set_scene_state( - app->scene_manager, DesktopSettingsAppSceneFavorite, false); + scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 0); scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); consumed = true; break; - // case SCENE_EVENT_SELECT_FAVORITE_GAME: - // scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 2); - // scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - // consumed = true; - // break; case SCENE_EVENT_SELECT_PIN_SETUP: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); consumed = true; @@ -162,9 +152,4 @@ void desktop_settings_scene_start_on_exit(void* context) { DesktopSettingsApp* app = context; variable_item_list_reset(app->variable_item_list); DESKTOP_SETTINGS_SAVE(&app->settings); - - // Trigger UI update in case we changed battery layout - Power* power = furi_record_open(RECORD_POWER); - power_trigger_ui_update(power); - furi_record_close(RECORD_POWER); } From 3ff34c1de737f332c924676f49435200cbab0e9d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 27 Jun 2023 18:13:18 +0200 Subject: [PATCH 285/370] Update favorite apps logic --- .../scenes/desktop_settings_scene_favorite.c | 92 ++++++++----------- 1 file changed, 40 insertions(+), 52 deletions(-) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index 0251f5710..77c10a05f 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -5,6 +5,9 @@ #include #include +#define EXTERNAL_APPLICATION_NAME ("[External Application]") +#define EXTERNAL_APPLICATION_INDEX (FLIPPER_APPS_COUNT + 1) + static bool favorite_fap_selector_item_callback( FuriString* file_path, void* context, @@ -37,6 +40,8 @@ void desktop_settings_scene_favorite_on_enter(void* context) { uint32_t primary_favorite = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); uint32_t pre_select_item = 0; + FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary : + &app->settings.favorite_secondary; for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { submenu_add_item( @@ -46,21 +51,23 @@ void desktop_settings_scene_favorite_on_enter(void* context) { desktop_settings_scene_favorite_submenu_callback, app); - if(primary_favorite) { // Select favorite item in submenu - if((app->settings.favorite_primary.is_external && - !strcmp(FLIPPER_APPS[i].name, LOADER_APPLICATIONS_NAME)) || - (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_primary.name_or_path))) { - pre_select_item = i; - } - } else { - if((app->settings.favorite_secondary.is_external && - !strcmp(FLIPPER_APPS[i].name, LOADER_APPLICATIONS_NAME)) || - (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_secondary.name_or_path))) { - pre_select_item = i; - } + // Select favorite item in submenu + if(!curr_favorite_app->is_external && + !strcmp(FLIPPER_APPS[i].name, curr_favorite_app->name_or_path)) { + pre_select_item = i; } } + submenu_add_item( + submenu, + EXTERNAL_APPLICATION_NAME, + EXTERNAL_APPLICATION_INDEX, + desktop_settings_scene_favorite_submenu_callback, + app); + if(curr_favorite_app->is_external) { + pre_select_item = EXTERNAL_APPLICATION_INDEX; + } + submenu_set_header( submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:"); submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. @@ -75,23 +82,11 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e uint32_t primary_favorite = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); + FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary : + &app->settings.favorite_secondary; if(event.type == SceneManagerEventTypeCustom) { - if(strcmp(FLIPPER_APPS[event.event].name, LOADER_APPLICATIONS_NAME) != 0) { - if(primary_favorite) { - app->settings.favorite_primary.is_external = false; - strncpy( - app->settings.favorite_primary.name_or_path, - FLIPPER_APPS[event.event].name, - MAX_APP_LENGTH); - } else { - app->settings.favorite_secondary.is_external = false; - strncpy( - app->settings.favorite_secondary.name_or_path, - FLIPPER_APPS[event.event].name, - MAX_APP_LENGTH); - } - } else { + if(event.event == EXTERNAL_APPLICATION_INDEX) { const DialogsFileBrowserOptions browser_options = { .extension = ".fap", .icon = &I_unknown_10px, @@ -102,36 +97,29 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e .base_path = EXT_PATH("apps"), }; - if(primary_favorite) { // Select favorite fap in file browser - if(favorite_fap_selector_file_exists( - app->settings.favorite_primary.name_or_path)) { - furi_string_set_str(temp_path, app->settings.favorite_primary.name_or_path); - } - } else { - if(favorite_fap_selector_file_exists( - app->settings.favorite_secondary.name_or_path)) { - furi_string_set_str(temp_path, app->settings.favorite_secondary.name_or_path); - } + // Select favorite fap in file browser + if(favorite_fap_selector_file_exists(curr_favorite_app->name_or_path)) { + furi_string_set_str(temp_path, curr_favorite_app->name_or_path); } - submenu_reset(app->submenu); if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { - if(primary_favorite) { - app->settings.favorite_primary.is_external = true; - strncpy( - app->settings.favorite_primary.name_or_path, - furi_string_get_cstr(temp_path), - MAX_APP_LENGTH); - } else { - app->settings.favorite_secondary.is_external = true; - strncpy( - app->settings.favorite_secondary.name_or_path, - furi_string_get_cstr(temp_path), - MAX_APP_LENGTH); - } + submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene + curr_favorite_app->is_external = true; + strncpy( + curr_favorite_app->name_or_path, + furi_string_get_cstr(temp_path), + MAX_APP_LENGTH); + consumed = true; } + } else { + curr_favorite_app->is_external = false; + strncpy( + curr_favorite_app->name_or_path, FLIPPER_APPS[event.event].name, MAX_APP_LENGTH); + consumed = true; } - scene_manager_previous_scene(app->scene_manager); + if(consumed) { + scene_manager_previous_scene(app->scene_manager); + }; consumed = true; } From f780b832ada618579f39270b26f1c123261f1b87 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 27 Jun 2023 19:57:31 +0200 Subject: [PATCH 286/370] Ddesktop service and events cleanup --- .../services/desktop/animations/animation_storage.c | 12 +++--------- .../desktop/animations/views/bubble_animation_view.c | 7 +------ .../animations/views/one_shot_animation_view.c | 7 +------ .../desktop/scenes/desktop_scene_lock_menu.c | 1 - .../services/desktop/scenes/desktop_scene_main.c | 4 ---- applications/services/desktop/views/desktop_events.h | 11 ----------- .../services/desktop/views/desktop_view_main.c | 8 ++++++++ 7 files changed, 13 insertions(+), 37 deletions(-) diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index a173de2a3..52927225a 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -15,15 +15,8 @@ #define ANIMATION_META_FILE "meta.txt" #define BASE_ANIMATION_DIR EXT_PATH("dolphin") #define TAG "AnimationStorage" -/* Unused old code, for safe-keeping - -#define ANIMATION_MANIFEST_FILE ANIMATION_DIR "/manifest.txt" - -*/ -// 59 Max length = strlen("/ext/dolphin_custom//Anims") + XTREME_ASSETS_PACK_NAME_LEN + 1 (Null terminator) -char ANIMATION_DIR[59]; -// 72 Max length = ANIMATION_DIR + strlen("/manifest.txt") -char ANIMATION_MANIFEST_FILE[72]; +char ANIMATION_DIR[26 /*"/ext/dolphin_custom//Anims"*/ + XTREME_ASSETS_PACK_NAME_LEN + 1]; +char ANIMATION_MANIFEST_FILE[sizeof(ANIMATION_DIR) + 13 /*"/manifest.txt"*/]; static void animation_storage_free_bubbles(BubbleAnimation* animation); static void animation_storage_free_frames(BubbleAnimation* animation); @@ -43,6 +36,7 @@ void animation_handler_select_manifest() { if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { FURI_LOG_I(TAG, "Custom manifest selected"); } else { + FURI_LOG_E(TAG, "Custom manifest does not exist!"); use_asset_pack = false; } furi_record_close(RECORD_STORAGE); diff --git a/applications/services/desktop/animations/views/bubble_animation_view.c b/applications/services/desktop/animations/views/bubble_animation_view.c index 8fcfba0e3..199f7cd71 100644 --- a/applications/services/desktop/animations/views/bubble_animation_view.c +++ b/applications/services/desktop/animations/views/bubble_animation_view.c @@ -12,7 +12,6 @@ #include #include #include -#include #define ACTIVE_SHIFT 2 @@ -129,15 +128,11 @@ static bool bubble_animation_input_callback(InputEvent* event, void* context) { if(event->key == InputKeyRight) { /* Right button reserved for animation activation, so consume */ - consumed = true; if(event->type == InputTypeShort) { if(animation_view->interact_callback) { + consumed = true; animation_view->interact_callback(animation_view->interact_callback_context); } - } else if(event->type == InputTypeLong) { - Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "Power", "about_battery", NULL); - furi_record_close(RECORD_LOADER); } } diff --git a/applications/services/desktop/animations/views/one_shot_animation_view.c b/applications/services/desktop/animations/views/one_shot_animation_view.c index 52b183566..e76856288 100644 --- a/applications/services/desktop/animations/views/one_shot_animation_view.c +++ b/applications/services/desktop/animations/views/one_shot_animation_view.c @@ -6,7 +6,6 @@ #include #include #include -#include typedef void (*OneShotInteractCallback)(void*); @@ -66,15 +65,11 @@ static bool one_shot_view_input(InputEvent* event, void* context) { if(!consumed) { if(event->key == InputKeyRight) { /* Right button reserved for animation activation, so consume */ - consumed = true; if(event->type == InputTypeShort) { if(view->interact_callback) { + consumed = true; view->interact_callback(view->interact_callback_context); } - } else if(event->type == InputTypeLong) { - Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "Power", "about_battery", NULL); - furi_record_close(RECORD_LOADER); } } } diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index 66d732781..480bef951 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -4,7 +4,6 @@ #include #include #include -// #include #include #include "../desktop_i.h" diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index f4e8fd309..1d425d73a 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -154,10 +154,6 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { } consumed = true; break; - case DesktopMainEventOpenPassport: { - loader_start(desktop->loader, "Passport", NULL, NULL); - break; - } case DesktopMainEventOpenClock: { loader_start_with_gui_error( desktop->loader, EXT_PATH("apps/Misc/Nightstand.fap"), NULL); diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index 11b45a8c7..c0da403da 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -6,21 +6,10 @@ typedef enum { DesktopMainEventOpenFavoritePrimary, DesktopMainEventOpenFavoriteSecondary, DesktopMainEventOpenMenu, - DesktopMainEventOpenGames, DesktopMainEventOpenDebug, - DesktopMainEventOpenPassport, DesktopMainEventOpenPowerOff, DesktopMainEventLock, - DesktopMainEventOpenSnake, - DesktopMainEventOpen2048, - DesktopMainEventOpenZombiez, - DesktopMainEventOpenTetris, - DesktopMainEventOpenDOOM, - DesktopMainEventOpenDice, - DesktopMainEventOpenArkanoid, - DesktopMainEventOpenHeap, - DesktopMainEventOpenSubRemote, DesktopMainEventOpenClock, DesktopLockedEventOpenPowerOff, diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index e7e4ab4fd..bd2625f97 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -43,6 +43,10 @@ bool desktop_main_input_callback(InputEvent* event, void* context) { main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); } else if(event->key == InputKeyDown) { main_view->callback(DesktopMainEventOpenArchive, main_view->context); + } else if(event->key == InputKeyRight) { + Loader* loader = furi_record_open(RECORD_LOADER); + loader_start(loader, "Passport", NULL, NULL); + furi_record_close(RECORD_LOADER); } else if(event->key == InputKeyLeft) { main_view->callback(DesktopMainEventOpenClock, main_view->context); } @@ -54,6 +58,10 @@ bool desktop_main_input_callback(InputEvent* event, void* context) { main_view->callback(DesktopMainEventOpenFavoritePrimary, main_view->context); } else if(event->key == InputKeyDown) { main_view->callback(DesktopMainEventOpenFavoriteSecondary, main_view->context); + } else if(event->key == InputKeyRight) { + Loader* loader = furi_record_open(RECORD_LOADER); + loader_start(loader, "Power", "about_battery", NULL); + furi_record_close(RECORD_LOADER); } else if(event->key == InputKeyLeft) { main_view->callback(DesktopMainEventLock, main_view->context); } From 3cbf932bd13034b248b3fdafb92bdc245781110c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:35:50 +0200 Subject: [PATCH 287/370] Move clock setting to statusbar section of xfw app --- .../xtreme_app_scene_interface_statusbar.c | 18 +++++++++++ applications/services/desktop/desktop.c | 4 +-- .../services/desktop/desktop_settings.h | 1 - .../scenes/desktop_settings_scene_start.c | 32 ------------------- lib/xtreme/settings.c | 6 ++++ lib/xtreme/xtreme.h | 1 + 6 files changed, 27 insertions(+), 35 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c index 5d58472d8..50b3c14ec 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c @@ -2,6 +2,7 @@ enum VarItemListIndex { VarItemListIndexBatteryIcon, + VarItemListIndexShowClock, VarItemListIndexStatusIcons, VarItemListIndexBarBorders, VarItemListIndexBarBackground, @@ -24,6 +25,14 @@ static void xtreme_app_scene_interface_statusbar_battery_icon_changed(VariableIt furi_record_close(RECORD_POWER); } +static void xtreme_app_scene_interface_statusbar_statusbar_clock_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->statusbar_clock = value; + app->save_settings = true; +} + static void xtreme_app_scene_interface_statusbar_status_icons_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); @@ -63,6 +72,15 @@ void xtreme_app_scene_interface_statusbar_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->battery_icon); variable_item_set_current_value_text(item, battery_icon_names[xtreme_settings->battery_icon]); + item = variable_item_list_add( + var_item_list, + "Show Clock", + 2, + xtreme_app_scene_interface_statusbar_statusbar_clock_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->statusbar_clock); + variable_item_set_current_value_text(item, xtreme_settings->statusbar_clock ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Status Icons", diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index e2d7354d7..f78150102 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -157,7 +157,7 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { // locking and unlocking DESKTOP_SETTINGS_LOAD(&desktop->settings); - desktop_clock_toggle_view(desktop, desktop->settings.display_clock); + desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); desktop_auto_lock_arm(desktop); return true; @@ -470,7 +470,7 @@ int32_t desktop_srv(void* p) { furi_hal_rtc_set_pin_fails(0); } - desktop_clock_toggle_view(desktop, desktop->settings.display_clock); + desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 4cc07e735..fb269eaff 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -36,7 +36,6 @@ typedef struct { PinCode pin_code; uint32_t auto_lock_delay_ms; bool auto_lock_with_pin; - uint8_t display_clock; } DesktopSettings; bool DESKTOP_SETTINGS_SAVE(DesktopSettings* x); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 5765963c4..baa3a9af4 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -10,7 +10,6 @@ #define SCENE_EVENT_SELECT_PIN_SETUP 2 #define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3 #define SCENE_EVENT_SELECT_AUTO_LOCK_PIN 4 -#define SCENE_EVENT_SELECT_CLOCK_DISPLAY 5 #define AUTO_LOCK_DELAY_COUNT 9 const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { @@ -27,27 +26,11 @@ const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] = {0, 10000, 15000, 30000, 60000, 90000, 120000, 300000, 600000}; -#define CLOCK_ENABLE_COUNT 2 -const char* const clock_enable_text[CLOCK_ENABLE_COUNT] = { - "OFF", - "ON", -}; - -const uint32_t clock_enable_value[CLOCK_ENABLE_COUNT] = {0, 1}; - static void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) { DesktopSettingsApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void desktop_settings_scene_start_clock_enable_changed(VariableItem* item) { - DesktopSettingsApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, clock_enable_text[index]); - app->settings.display_clock = index; -} - static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) { DesktopSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -99,18 +82,6 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, app->settings.auto_lock_with_pin); variable_item_set_current_value_text(item, app->settings.auto_lock_with_pin ? "ON" : "OFF"); - item = variable_item_list_add( - variable_item_list, - "Show Clock", - CLOCK_ENABLE_COUNT, - desktop_settings_scene_start_clock_enable_changed, // - app); - - value_index = - value_index_uint32(app->settings.display_clock, clock_enable_value, CLOCK_ENABLE_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, clock_enable_text[value_index]); - variable_item_list_set_enter_callback( variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); @@ -140,9 +111,6 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: consumed = true; break; - case SCENE_EVENT_SELECT_CLOCK_DISPLAY: - consumed = true; - break; } } return consumed; diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index bdceb6900..57e5670e0 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -21,6 +21,7 @@ XtremeSettings xtreme_settings = { .lockscreen_statusbar = true, // ON .lockscreen_prompt = true, // ON .battery_icon = BatteryIconBarPercent, // Bar % + .statusbar_clock = false, // OFF .status_icons = true, // ON .bar_borders = true, // ON .bar_background = false, // OFF @@ -108,6 +109,10 @@ void XTREME_SETTINGS_LOAD() { x->battery_icon = CLAMP(u, BatteryIconCount - 1U, 0U); } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "statusbar_clock", &b, 1)) { + x->statusbar_clock = b; + } + flipper_format_rewind(file); if(flipper_format_read_bool(file, "status_icons", &b, 1)) { x->status_icons = b; } @@ -186,6 +191,7 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); flipper_format_write_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); flipper_format_write_uint32(file, "battery_icon", &x->battery_icon, 1); + flipper_format_write_bool(file, "statusbar_clock", &x->statusbar_clock, 1); flipper_format_write_bool(file, "status_icons", &x->status_icons, 1); flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index b2a86e9c0..134f737e1 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -30,6 +30,7 @@ typedef struct { bool lockscreen_statusbar; bool lockscreen_prompt; uint32_t battery_icon; + bool statusbar_clock; bool status_icons; bool bar_borders; bool bar_background; From e680cf59b60fd80065172bd6902f467f02e08e57 Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Tue, 27 Jun 2023 23:47:13 +0300 Subject: [PATCH 288/370] Actions: unit_test and updater timeouts (#2807) * added some extra timeouts, fixed duration of units run command and minor logging changes. No list_ports yet needed * increased timeouts * make pvs happy --------- Co-authored-by: doomwastaken Co-authored-by: SG --- .github/workflows/unit_tests.yml | 5 ++++- .github/workflows/updater_test.yml | 2 ++ .../helpers/subghz_frequency_analyzer_log_item_array.h | 2 +- scripts/testing/await_flipper.py | 5 ++++- scripts/testing/units.py | 4 ++-- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 81f0e0d05..4cb112c77 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -29,12 +29,14 @@ jobs: - name: 'Flash unit tests firmware' id: flashing if: success() + timeout-minutes: 5 run: | ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - name: 'Wait for flipper and format ext' id: format_ext if: steps.flashing.outcome == 'success' + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} @@ -43,6 +45,7 @@ jobs: - name: 'Copy assets and unit data, reboot and wait for flipper' id: copy if: steps.format_ext.outcome == 'success' + timeout-minutes: 3 run: | source scripts/toolchain/fbtenv.sh python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/resources /ext @@ -53,7 +56,7 @@ jobs: - name: 'Run units and validate results' id: run_units if: steps.copy.outcome == 'success' - timeout-minutes: 2.5 + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index bd8372979..1d383d9eb 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -30,6 +30,7 @@ jobs: - name: 'Flashing target firmware' id: first_full_flash + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 @@ -37,6 +38,7 @@ jobs: - name: 'Validating updater' id: second_full_flash + timeout-minutes: 5 if: success() run: | source scripts/toolchain/fbtenv.sh diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h index 12c6bef6a..df53143d2 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -35,7 +35,7 @@ TUPLE_DEF2( M_DEFAULT_OPLIST) /* Define the array, register the oplist and define further algorithms on it */ -ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) +ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) //-V779 #define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_t() \ ARRAY_OPLIST(SubGhzFrequencyAnalyzerLogItemArray, M_OPL_SubGhzFrequencyAnalyzerLogItem_t()) ALGO_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItemArray_t) diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py index 2b4c8b4c3..ea07d6be7 100755 --- a/scripts/testing/await_flipper.py +++ b/scripts/testing/await_flipper.py @@ -8,6 +8,7 @@ import time def flp_serial_by_name(flp_name): if sys.platform == "darwin": # MacOS flp_serial = "/dev/cu.usbmodemflip_" + flp_name + "1" + logging.info(f"Darwin, looking for {flp_serial}") elif sys.platform == "linux": # Linux flp_serial = ( "/dev/serial/by-id/usb-Flipper_Devices_Inc._Flipper_" @@ -16,10 +17,12 @@ def flp_serial_by_name(flp_name): + flp_name + "-if00" ) + logging.info(f"linux, looking for {flp_serial}") if os.path.exists(flp_serial): return flp_serial else: + logging.info(f"Couldn't find {logging.info} on this attempt.") if os.path.exists(flp_name): return flp_name else: @@ -38,7 +41,7 @@ def main(): level=logging.INFO, datefmt="%Y-%m-%d %H:%M:%S", ) - logging.info("Waiting for Flipper to be ready...") + logging.info(f"Waiting for Flipper {flipper_name} to be ready...") while flipper == "" and elapsed < UPDATE_TIMEOUT: elapsed += 1 diff --git a/scripts/testing/units.py b/scripts/testing/units.py index 5083bcd43..fd8e29a73 100755 --- a/scripts/testing/units.py +++ b/scripts/testing/units.py @@ -20,13 +20,13 @@ def main(): logging.error("Flipper not found!") sys.exit(1) - with serial.Serial(flp_serial, timeout=1) as flipper: + with serial.Serial(flp_serial, timeout=10) as flipper: logging.info(f"Found Flipper at {flp_serial}") flipper.baudrate = 230400 flipper.flushOutput() flipper.flushInput() - flipper.timeout = 180 + flipper.timeout = 300 flipper.read_until(b">: ").decode("utf-8") flipper.write(b"unit_tests\r") From 92c1bb83bf37c7f1d622c28e20870402830cf4a4 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Wed, 28 Jun 2023 10:30:59 +0300 Subject: [PATCH 289/370] LF-RFID debug: make it work (#2793) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../scenes/lfrfid_debug_app_scene_tune.c | 7 +++ .../views/lfrfid_debug_view_tune.c | 44 +++++++++++++------ .../views/lfrfid_debug_view_tune.h | 5 +++ 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c index 74c53ae6d..ac2e2b806 100644 --- a/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c +++ b/applications/debug/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c @@ -6,6 +6,11 @@ static void comparator_trigger_callback(bool level, void* comp_ctx) { furi_hal_gpio_write(&gpio_ext_pa7, !level); } +void lfrfid_debug_view_tune_callback(void* context) { + LfRfidDebug* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, 0xBA); +} + void lfrfid_debug_scene_tune_on_enter(void* context) { LfRfidDebug* app = context; @@ -16,6 +21,8 @@ void lfrfid_debug_scene_tune_on_enter(void* context) { furi_hal_rfid_tim_read_start(125000, 0.5); + lfrfid_debug_view_tune_set_callback(app->tune_view, lfrfid_debug_view_tune_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune); } diff --git a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c index fd221c4e9..9e48a7e27 100644 --- a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.c @@ -13,6 +13,8 @@ typedef struct { uint32_t ARR; uint32_t CCR; int pos; + void (*update_callback)(void* context); + void* update_context; } LfRfidTuneViewModel; static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) { @@ -151,6 +153,18 @@ static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* conte consumed = false; break; } + + if(event->key == InputKeyLeft || event->key == InputKeyRight) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + if(model->update_callback) { + model->update_callback(model->update_context); + } + }, + false); + } } return consumed; @@ -161,19 +175,7 @@ LfRfidTuneView* lfrfid_debug_view_tune_alloc() { tune_view->view = view_alloc(); view_set_context(tune_view->view, tune_view); view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel)); - - with_view_model( - tune_view->view, - LfRfidTuneViewModel * model, - { - model->dirty = true; - model->fine = false; - model->ARR = 511; - model->CCR = 255; - model->pos = 0; - }, - true); - + lfrfid_debug_view_tune_clean(tune_view); view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback); view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback); @@ -199,6 +201,8 @@ void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) { model->ARR = 511; model->CCR = 255; model->pos = 0; + model->update_callback = NULL; + model->update_context = NULL; }, true); } @@ -232,3 +236,17 @@ uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) { return result; } + +void lfrfid_debug_view_tune_set_callback( + LfRfidTuneView* tune_view, + void (*callback)(void* context), + void* context) { + with_view_model( + tune_view->view, + LfRfidTuneViewModel * model, + { + model->update_callback = callback; + model->update_context = context; + }, + false); +} diff --git a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h index fd6d0b1fe..be54b63f9 100644 --- a/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h +++ b/applications/debug/lfrfid_debug/views/lfrfid_debug_view_tune.h @@ -16,3 +16,8 @@ bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view); uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view); uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view); + +void lfrfid_debug_view_tune_set_callback( + LfRfidTuneView* tune_view, + void (*callback)(void* context), + void* context); From 645a7c598923bbe1877c1f5ce13bccd20bb5ccac Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Wed, 28 Jun 2023 11:19:10 +0300 Subject: [PATCH 290/370] [FL-3386] Fast FAP Loader (#2790) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FBT: build and add FastFAP(tm) sections * Elf file: fast loading fap files. Really fast, like x15 times faster. * fastfap.py: cleanup unused imports * Toolchain: 23 version * Elf File: remove log messages * Scripts: fix file permissions * FBT: explicit interpreter for fastfap invocation Co-authored-by: あく --- firmware/targets/f18/api_symbols.csv | 3 +- firmware/targets/f7/api_symbols.csv | 3 +- .../api_hashtable/api_hashtable.cpp | 19 +- .../api_hashtable/api_hashtable.h | 12 +- .../elf/elf_api_interface.h | 2 +- lib/flipper_application/elf/elf_file.c | 126 ++++++++++++- lib/flipper_application/elf/elf_file_i.h | 10 +- .../plugins/composite_resolver.c | 4 +- scripts/distfap.py | 0 scripts/fastfap.py | 169 ++++++++++++++++++ scripts/fbt/sdk/collector.py | 8 +- scripts/fbt/sdk/hashes.py | 5 + scripts/fbt_tools/fbt_extapps.py | 15 +- scripts/fwsize.py | 0 scripts/get_env.py | 0 scripts/runfap.py | 0 scripts/sconsdist.py | 0 scripts/selfupdate.py | 0 scripts/slideshow.py | 0 scripts/toolchain/fbtenv.cmd | 2 +- scripts/toolchain/fbtenv.sh | 2 +- scripts/version.py | 0 22 files changed, 338 insertions(+), 42 deletions(-) mode change 100644 => 100755 scripts/distfap.py create mode 100755 scripts/fastfap.py create mode 100644 scripts/fbt/sdk/hashes.py mode change 100644 => 100755 scripts/fwsize.py mode change 100644 => 100755 scripts/get_env.py mode change 100644 => 100755 scripts/runfap.py mode change 100644 => 100755 scripts/sconsdist.py mode change 100644 => 100755 scripts/selfupdate.py mode change 100644 => 100755 scripts/slideshow.py mode change 100644 => 100755 scripts/version.py diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 101ea92a6..56a7f678d 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -679,7 +679,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 5ed26f296..0f782e966 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -808,7 +808,8 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" -Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" +Function,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, uint32_t, Elf32_Addr*" +Function,+,elf_symbolname_hash,uint32_t,const char* Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* diff --git a/lib/flipper_application/api_hashtable/api_hashtable.cpp b/lib/flipper_application/api_hashtable/api_hashtable.cpp index 022792dce..6db5fb5fd 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.cpp +++ b/lib/flipper_application/api_hashtable/api_hashtable.cpp @@ -7,27 +7,22 @@ bool elf_resolve_from_hashtable( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address) { + bool result = false; const HashtableApiInterface* hashtable_interface = static_cast(interface); - bool result = false; - uint32_t gnu_sym_hash = elf_gnu_hash(name); sym_entry key = { - .hash = gnu_sym_hash, + .hash = hash, .address = 0, }; auto find_res = std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key); - if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) { + if((find_res == hashtable_interface->table_cend || (find_res->hash != hash))) { FURI_LOG_W( - TAG, - "Can't find symbol '%s' (hash %lx) @ %p!", - name, - gnu_sym_hash, - hashtable_interface->table_cbegin); + TAG, "Can't find symbol with hash %lx @ %p!", hash, hashtable_interface->table_cbegin); result = false; } else { result = true; @@ -36,3 +31,7 @@ bool elf_resolve_from_hashtable( return result; } + +uint32_t elf_symbolname_hash(const char* s) { + return elf_gnu_hash(s); +} \ No newline at end of file diff --git a/lib/flipper_application/api_hashtable/api_hashtable.h b/lib/flipper_application/api_hashtable/api_hashtable.h index 7e4b4aba1..7ba6aab97 100644 --- a/lib/flipper_application/api_hashtable/api_hashtable.h +++ b/lib/flipper_application/api_hashtable/api_hashtable.h @@ -19,15 +19,17 @@ struct sym_entry { /** * @brief Resolver for API entries using a pre-sorted table with hashes * @param interface pointer to HashtableApiInterface - * @param name function name + * @param hash gnu hash of function name * @param address output for function address * @return true if the table contains a function */ bool elf_resolve_from_hashtable( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address); +uint32_t elf_symbolname_hash(const char* s); + #ifdef __cplusplus } @@ -48,8 +50,10 @@ struct HashtableApiInterface : public ElfApiInterface { .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ } -#define API_VARIABLE(x, var_type) \ - sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), } +#define API_VARIABLE(x, var_type) \ + sym_entry { \ + .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \ + } constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { return k1.hash < k2.hash; diff --git a/lib/flipper_application/elf/elf_api_interface.h b/lib/flipper_application/elf/elf_api_interface.h index f07df4edb..facdc4447 100644 --- a/lib/flipper_application/elf/elf_api_interface.h +++ b/lib/flipper_application/elf/elf_api_interface.h @@ -11,6 +11,6 @@ typedef struct ElfApiInterface { uint16_t api_version_minor; bool (*resolver_callback)( const struct ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address); } ElfApiInterface; diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 0338144a9..fc9dd06ba 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -2,6 +2,7 @@ #include "elf_file.h" #include "elf_file_i.h" #include "elf_api_interface.h" +#include "../api_hashtable/api_hashtable.h" #define TAG "elf" @@ -9,6 +10,7 @@ #define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr)) #define IS_FLAGS_SET(v, m) (((v) & (m)) == (m)) #define RESOLVER_THREAD_YIELD_STEP 30 +#define FAST_RELOCATION_VERSION 1 // #define ELF_DEBUG_LOG 1 @@ -71,6 +73,7 @@ static ELFSection* elf_file_get_or_put_section(ELFFile* elf, const char* name) { .size = 0, .rel_count = 0, .rel_offset = 0, + .fast_rel = NULL, }); section_p = elf_file_get_section(elf, name); } @@ -168,7 +171,8 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) { static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) { if(sym->st_shndx == SHN_UNDEF) { Elf32_Addr addr = 0; - if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) { + uint32_t hash = elf_symbolname_hash(sName); + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { return addr; } } else { @@ -424,6 +428,7 @@ typedef enum { SectionTypeSymTab = 1 << 3, SectionTypeStrTab = 1 << 4, SectionTypeDebugLink = 1 << 5, + SectionTypeFastRelData = 1 << 6, SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab, } SectionType; @@ -505,7 +510,8 @@ static SectionType elf_preload_section( // TODO: how to do it not by name? // .ARM: type 0x70000001, flags SHF_ALLOC | SHF_LINK_ORDER // .rel.ARM: type 0x9, flags SHT_REL - if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.")) { + if(str_prefix(name, ".ARM.") || str_prefix(name, ".rel.ARM.") || + str_prefix(name, ".fast.rel.ARM.")) { FURI_LOG_D(TAG, "Ignoring ARM section"); return SectionTypeUnused; } @@ -536,11 +542,31 @@ static SectionType elf_preload_section( // Load link info section if(section_header->sh_flags & SHF_INFO_LINK) { - name = name + strlen(".rel"); + if(str_prefix(name, ".rel")) { + name = name + strlen(".rel"); + ELFSection* section_p = elf_file_get_or_put_section(elf, name); + section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); + section_p->rel_offset = section_header->sh_offset; + return SectionTypeRelData; + } else { + FURI_LOG_E(TAG, "Unknown link info section '%s'", name); + return SectionTypeERROR; + } + } + + // Load fast rel section + if(str_prefix(name, ".fast.rel")) { + name = name + strlen(".fast.rel"); ELFSection* section_p = elf_file_get_or_put_section(elf, name); - section_p->rel_count = section_header->sh_size / sizeof(Elf32_Rel); - section_p->rel_offset = section_header->sh_offset; - return SectionTypeRelData; + section_p->fast_rel = malloc(sizeof(ELFSection)); + + if(!elf_load_section_data(elf, section_p->fast_rel, section_header)) { + FURI_LOG_E(TAG, "Error loading section '%s'", name); + return SectionTypeERROR; + } + + FURI_LOG_D(TAG, "Loaded fast rel section for '%s'", name); + return SectionTypeFastRelData; } // Load symbol table @@ -571,8 +597,90 @@ static SectionType elf_preload_section( return SectionTypeUnused; } +static Elf32_Addr elf_address_of_by_hash(ELFFile* elf, uint32_t hash) { + Elf32_Addr addr = 0; + if(elf->api_interface->resolver_callback(elf->api_interface, hash, &addr)) { + return addr; + } + return ELF_INVALID_ADDRESS; +} + +static bool elf_relocate_fast(ELFFile* elf, ELFSection* s) { + UNUSED(elf); + const uint8_t* start = s->fast_rel->data; + const uint8_t version = *start; + + if(version != FAST_RELOCATION_VERSION) { + FURI_LOG_E(TAG, "Unsupported fast relocation version %d", version); + return false; + } + start += 1; + + const uint32_t records_count = *((uint32_t*)start); + start += 4; + FURI_LOG_D(TAG, "Fast relocation records count: %ld", records_count); + + for(uint32_t i = 0; i < records_count; i++) { + bool is_section = (*start & (0x1 << 7)) ? true : false; + uint8_t type = *start & 0x7F; + start += 1; + uint32_t hash_or_section_index = *((uint32_t*)start); + start += 4; + + uint32_t section_value = ELF_INVALID_ADDRESS; + if(is_section) { + section_value = *((uint32_t*)start); + start += 4; + } + + const uint32_t offsets_count = *((uint32_t*)start); + start += 4; + + FURI_LOG_D( + TAG, + "Fast relocation record %ld: is_section=%d, type=%d, hash_or_section_index=%lX, offsets_count=%ld", + i, + is_section, + type, + hash_or_section_index, + offsets_count); + + Elf32_Addr address = 0; + if(is_section) { + ELFSection* symSec = elf_section_of(elf, hash_or_section_index); + if(symSec) { + address = ((Elf32_Addr)symSec->data) + section_value; + } + } else { + address = elf_address_of_by_hash(elf, hash_or_section_index); + } + + if(address == ELF_INVALID_ADDRESS) { + FURI_LOG_E(TAG, "Failed to resolve address for hash %lX", hash_or_section_index); + return false; + } + + for(uint32_t j = 0; j < offsets_count; j++) { + uint32_t offset = *((uint32_t*)start) & 0x00FFFFFF; + start += 3; + // FURI_LOG_I(TAG, " Fast relocation offset %ld: %ld", j, offset); + Elf32_Addr relAddr = ((Elf32_Addr)s->data) + offset; + elf_relocate_symbol(elf, relAddr, type, address); + } + } + + aligned_free(s->fast_rel->data); + free(s->fast_rel); + s->fast_rel = NULL; + + return true; +} + static bool elf_relocate_section(ELFFile* elf, ELFSection* section) { - if(section->rel_count) { + if(section->fast_rel) { + FURI_LOG_D(TAG, "Fast relocating section"); + return elf_relocate_fast(elf, section); + } else if(section->rel_count) { FURI_LOG_D(TAG, "Relocating section"); return elf_relocate(elf, section); } else { @@ -630,6 +738,10 @@ void elf_file_free(ELFFile* elf) { if(itref->value.data) { aligned_free(itref->value.data); } + if(itref->value.fast_rel) { + aligned_free(itref->value.fast_rel->data); + free(itref->value.fast_rel); + } free((void*)itref->key); } diff --git a/lib/flipper_application/elf/elf_file_i.h b/lib/flipper_application/elf/elf_file_i.h index af9a1d9b4..39cadfdc6 100644 --- a/lib/flipper_application/elf/elf_file_i.h +++ b/lib/flipper_application/elf/elf_file_i.h @@ -13,14 +13,18 @@ DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST) */ typedef int32_t(entry_t)(void*); -typedef struct { +typedef struct ELFSection ELFSection; + +struct ELFSection { void* data; - uint16_t sec_idx; Elf32_Word size; size_t rel_count; Elf32_Off rel_offset; -} ELFSection; + ELFSection* fast_rel; + + uint16_t sec_idx; +}; DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST) diff --git a/lib/flipper_application/plugins/composite_resolver.c b/lib/flipper_application/plugins/composite_resolver.c index 1402c3ad0..7cc2b340a 100644 --- a/lib/flipper_application/plugins/composite_resolver.c +++ b/lib/flipper_application/plugins/composite_resolver.c @@ -13,12 +13,12 @@ struct CompositeApiResolver { static bool composite_api_resolver_callback( const ElfApiInterface* interface, - const char* name, + uint32_t hash, Elf32_Addr* address) { CompositeApiResolver* resolver = (CompositeApiResolver*)interface; for M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) { - if((*interface)->resolver_callback(*interface, name, address)) { + if((*interface)->resolver_callback(*interface, hash, address)) { return true; } } diff --git a/scripts/distfap.py b/scripts/distfap.py old mode 100644 new mode 100755 diff --git a/scripts/fastfap.py b/scripts/fastfap.py new file mode 100755 index 000000000..95e32c37b --- /dev/null +++ b/scripts/fastfap.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python3 +import hashlib +import os +import struct +import subprocess +import tempfile +from collections import defaultdict +from dataclasses import dataclass + +from elftools.elf.elffile import ELFFile +from elftools.elf.relocation import RelocationSection +from elftools.elf.sections import SymbolTableSection +from fbt.sdk.hashes import gnu_sym_hash +from flipper.app import App + +VERSION = 1 + + +@dataclass +class RelData: + section: int + section_value: int + type: int + offset: int + name: str + + +@dataclass(frozen=True) +class UniqueRelData: + section: int + section_value: int + type: int + name: str + + +@dataclass +class RelSection: + name: str + oringinal_name: str + data: dict[UniqueRelData, list[int]] + + +def serialize_relsection_data(data: dict[UniqueRelData, list[int]]) -> bytes: + result = struct.pack(" 0: + result += struct.pack("> 8) & 0xFF, (offset >> 16) & 0xFF + ) + + return result + + +class Main(App): + def init(self): + self.parser.add_argument("fap_src_path", help="App file to upload") + self.parser.add_argument("objcopy_path", help="Objcopy path") + self.parser.set_defaults(func=self.process) + + def process(self): + fap_path = self.args.fap_src_path + objcopy_path = self.args.objcopy_path + + sections: list[RelSection] = [] + + with open(fap_path, "rb") as f: + elf_file = ELFFile(f) + + relocation_sections: list[RelocationSection] = [] + symtab_section: SymbolTableSection | None = None + + for section in elf_file.iter_sections(): + if isinstance(section, RelocationSection): + relocation_sections.append(section) + + if isinstance(section, SymbolTableSection): + symtab_section = section + + if not symtab_section: + self.logger.error("No symbol table found") + return 1 + + if not relocation_sections: + self.logger.info("No relocation sections found") + return 0 + + for section in relocation_sections: + section_relocations: list[RelData] = [] + + for relocation in section.iter_relocations(): + symbol_id: int = relocation.entry["r_info_sym"] + offset: int = relocation.entry["r_offset"] + type: int = relocation.entry["r_info_type"] + symbol = symtab_section.get_symbol(symbol_id) + section_index: int = symbol["st_shndx"] + section_value: int = symbol["st_value"] + if section_index == "SHN_UNDEF": + section_index = 0 + + section_relocations.append( + RelData(section_index, section_value, type, offset, symbol.name) + ) + + unique_relocations: dict[UniqueRelData, list[int]] = defaultdict(list) + for relocation in section_relocations: + unique = UniqueRelData( + relocation.section, + relocation.section_value, + relocation.type, + relocation.name, + ) + + unique_relocations[unique].append(relocation.offset) + + section_name = section.name + if section_name.startswith(".rel"): + section_name = ".fast.rel" + section_name[4:] + else: + self.logger.error( + "Unknown relocation section name: %s", section_name + ) + return 1 + + sections.append( + RelSection(section_name, section.name, unique_relocations) + ) + + with tempfile.TemporaryDirectory() as temp_dir: + for section in sections: + data = serialize_relsection_data(section.data) + hash_name = hashlib.md5(section.name.encode()).hexdigest() + filename = f"{temp_dir}/{hash_name}.bin" + + if os.path.isfile(filename): + self.logger.error(f"File {filename} already exists") + return 1 + + with open(filename, "wb") as f: + f.write(data) + + exit_code = subprocess.run( + [ + objcopy_path, + "--add-section", + f"{section.name}={filename}", + fap_path, + ], + check=True, + ) + + if exit_code.returncode != 0: + self.logger.error("objcopy failed") + return 1 + + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/fbt/sdk/collector.py b/scripts/fbt/sdk/collector.py index 578a8c7a6..1dd3bc4eb 100644 --- a/scripts/fbt/sdk/collector.py +++ b/scripts/fbt/sdk/collector.py @@ -1,4 +1,5 @@ from typing import List +from .hashes import gnu_sym_hash from cxxheaderparser.parser import CxxParser from . import ( @@ -72,13 +73,6 @@ class SymbolManager: self.api.headers.add(ApiHeader(header)) -def gnu_sym_hash(name: str): - h = 0x1505 - for c in name: - h = (h << 5) + h + ord(c) - return str(hex(h))[-8:] - - class SdkCollector: def __init__(self): self.symbol_manager = SymbolManager() diff --git a/scripts/fbt/sdk/hashes.py b/scripts/fbt/sdk/hashes.py new file mode 100644 index 000000000..fef88ddb5 --- /dev/null +++ b/scripts/fbt/sdk/hashes.py @@ -0,0 +1,5 @@ +def gnu_sym_hash(name: str) -> int: + h = 0x1505 + for c in name: + h = ((h << 5) + h + ord(c)) & 0xFFFFFFFF + return h diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 16d5dcbab..69d700214 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -384,10 +384,16 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature): "${SOURCES} ${TARGET}" ) - actions.append( - Action( - objcopy_str, - "$APPMETAEMBED_COMSTR", + actions.extend( + ( + Action( + objcopy_str, + "$APPMETAEMBED_COMSTR", + ), + Action( + "${PYTHON3} ${FBT_SCRIPT_DIR}/fastfap.py ${TARGET} ${OBJCOPY}", + "$FASTFAP_COMSTR", + ), ) ) @@ -450,6 +456,7 @@ def generate(env, **kw): APPMETA_COMSTR="\tAPPMETA\t${TARGET}", APPFILE_COMSTR="\tAPPFILE\t${TARGET}", APPMETAEMBED_COMSTR="\tFAP\t${TARGET}", + FASTFAP_COMSTR="\tFASTFAP\t${TARGET}", APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", ) diff --git a/scripts/fwsize.py b/scripts/fwsize.py old mode 100644 new mode 100755 diff --git a/scripts/get_env.py b/scripts/get_env.py old mode 100644 new mode 100755 diff --git a/scripts/runfap.py b/scripts/runfap.py old mode 100644 new mode 100755 diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py old mode 100644 new mode 100755 diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py old mode 100644 new mode 100755 diff --git a/scripts/slideshow.py b/scripts/slideshow.py old mode 100644 new mode 100755 diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index 9d45b7e9d..4ae04e2a2 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not ["%FBT_NOENV%"] == [""] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=21" +set "FLIPPER_TOOLCHAIN_VERSION=22" if ["%FBT_TOOLCHAIN_PATH%"] == [""] ( set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 143dce74b..e5548f488 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,7 +4,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"22"}"; if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then FBT_TOOLCHAIN_PATH_WAS_SET=0; diff --git a/scripts/version.py b/scripts/version.py old mode 100644 new mode 100755 From e52fdcf109cd01aaed0175195a3f15c290ae92d6 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 28 Jun 2023 13:05:48 +0400 Subject: [PATCH 291/370] [FL-3388] NFC/RFID detector (#2795) * Field_Validation: add driver fild_validation_rfid * Field_Validation: add fild_validation_nfc * Field_Presence: added field validation functions to furi_hal_nfc * Field_Presence: added field validation functions to furi_hal_rfid * Field_Presence: add "NFC/RFID detector" app * Field_Presence: fix GUI "NFC/RFID detector" * NFC/RFID detector: add auto turn on backlight when field is detected * NFC/RFID detector: fix syntax errors * ApiSymbols: fix incorrect name * FuriHal: filed detect naming * FieldDetector: fix grammar Co-authored-by: Aleksandr Kutuzov --- .../nfc_rfid_detector/application.fam | 13 ++ .../helpers/nfc_rfid_detector_event.h | 7 + .../helpers/nfc_rfid_detector_types.h | 15 ++ .../images/Modern_reader_18x34.png | Bin 0 -> 3670 bytes .../images/Move_flipper_26x39.png | Bin 0 -> 3698 bytes .../images/NFC_detect_45x30.png | Bin 0 -> 168 bytes .../images/Rfid_detect_45x30.png | Bin 0 -> 158 bytes .../nfc_rfid_detector_10px.png | Bin 0 -> 124 bytes .../nfc_rfid_detector/nfc_rfid_detector_app.c | 108 ++++++++++++ .../nfc_rfid_detector_app_i.c | 40 +++++ .../nfc_rfid_detector_app_i.h | 30 ++++ .../scenes/nfc_rfid_detector_scene.c | 31 ++++ .../scenes/nfc_rfid_detector_scene.h | 29 ++++ .../scenes/nfc_rfid_detector_scene_about.c | 69 ++++++++ .../scenes/nfc_rfid_detector_scene_config.h | 3 + .../nfc_rfid_detector_scene_field_presence.c | 60 +++++++ .../scenes/nfc_rfid_detector_scene_start.c | 58 +++++++ .../nfc_rfid_detector_view_field_presence.c | 164 ++++++++++++++++++ .../nfc_rfid_detector_view_field_presence.h | 19 ++ firmware/targets/f18/api_symbols.csv | 2 +- firmware/targets/f7/api_symbols.csv | 7 +- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 14 ++ firmware/targets/f7/furi_hal/furi_hal_nfc.h | 4 + firmware/targets/f7/furi_hal/furi_hal_rfid.c | 159 +++++++++++++++++ firmware/targets/f7/furi_hal/furi_hal_rfid.h | 14 ++ 25 files changed, 844 insertions(+), 2 deletions(-) create mode 100644 applications/external/nfc_rfid_detector/application.fam create mode 100644 applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h create mode 100644 applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h create mode 100644 applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png create mode 100644 applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png create mode 100644 applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png create mode 100644 applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png create mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png create mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c create mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c create mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c create mode 100644 applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c create mode 100644 applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h diff --git a/applications/external/nfc_rfid_detector/application.fam b/applications/external/nfc_rfid_detector/application.fam new file mode 100644 index 000000000..70c91bc84 --- /dev/null +++ b/applications/external/nfc_rfid_detector/application.fam @@ -0,0 +1,13 @@ +App( + appid="nfc_rfid_detector", + name="NFC/RFID detector", + apptype=FlipperAppType.EXTERNAL, + targets=["f7"], + entry_point="nfc_rfid_detector_app", + requires=["gui"], + stack_size=4 * 1024, + order=50, + fap_icon="nfc_rfid_detector_10px.png", + fap_category="Tools", + fap_icon_assets="images", +) diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h new file mode 100644 index 000000000..bbffe2938 --- /dev/null +++ b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + //NfcRfidDetectorCustomEvent + NfcRfidDetectorCustomEventStartId = 100, + +} NfcRfidDetectorCustomEvent; diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h new file mode 100644 index 000000000..5d44b09b7 --- /dev/null +++ b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#define NFC_RFID_DETECTOR_VERSION_APP "0.1" +#define NFC_RFID_DETECTOR_DEVELOPED "SkorP" +#define NFC_RFID_DETECTOR_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +typedef enum { + NfcRfidDetectorViewVariableItemList, + NfcRfidDetectorViewSubmenu, + NfcRfidDetectorViewFieldPresence, + NfcRfidDetectorViewWidget, +} NfcRfidDetectorView; diff --git a/applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png b/applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png new file mode 100644 index 0000000000000000000000000000000000000000..b19c0f30c9f3928d3129acc9da92d5a9e962d084 GIT binary patch literal 3670 zcmaJ@c{r478-GRiEm@Lu#t;&-TAE=jGh>S(jEuAxj4^2zV`?lVl&v}>Wo<-dUn)uo zWeX)lN%pcNI{1zyPGY`s&gp#LA79^lz3=-x&wbs$-~GFn_qyJMgHE9q!yUB8;Xo`l)1P*d0stWcJU1%QZCV+#GO~nqh>yJH zz;sm-2f1P|MJgt1>uE^HABfk;?N@SX*k)}lqSlrZFPxYdd0ELtU;3itd$9?PTZ!jy z$6tK8_A&f+;JezDPaPW%`^=|G7kQOkV)f$Esdh*gqe$r@?CxzJ&bKzVe4Kz-MoDV1 z0D19BKaJpZO(9@4!pv+RxL)ijAQbXON*t&sWYxoV#qs54uo*{$A}O1A7Dgbe50Ok@OvlkEv2fW)fHA8?4 z8GxeAf`{4f`^x2~^aPd4s4%P6LRm+7i5mood3Zo}>vr0!>{B!*Zy{$|LK;IeR1r~z zavv670YFZ&k|5i~^^i{4^3G1<#46e21~bn@`CuQP@r}u@5|$+ZeB?xQZ|FlScSf3u zM$$KK?U@q^I3|^IYUPrDg`DL>AZL2OW0AF48|&OF)&2dG6BF+bG-JKUFFnp~P#cfe zd#s=QBf{+a%JPS&V_H#&qfxdZs~;L)Eji}x>bfd%!Dr}GlI{0LQvC1gZ@|s=KGh^W z#c>yfphSG;@-q zi($!qBa3G@=+;I_h*-6WZzpRE#0&XcBxxp!t7OEiYBbo1C|uG4y@*$I0Xrlc*}+{e z5<%{E>I)e57F663nr15gw%-SrN|&_kymzQnxF%uQ zx9dJvL?Oz$Ucy*}iv^K)TiKBuNlx$W3PHQH47UwPm`Dg;aB0*5rxZFo(0;P*kLDdd z2zVUHPG9q#Leh4qe0V&r*+fer0f*43zOu#s{vBeELXS-k!&P%yzbMPlZl`9-ivhpD z3Nh3*ebBzPmlcJP#gq8d4OxNMU zT;evPq{G;<+$z_*E^&q14NqmFI?gNGJLHw!y8dQofJ(p$?e1sJlWoJ-cRQuM_ULJ! zw*8#;S$K&nEfcGBzBQhztD3b#YzI}9yW?)UW4`K}ORB9zmvhMMG|jQOWccj2fw(fxlxNu z3*(BZg-oKwoe0nM1X0f>$0ldo9haQ@$H!}1KvKS{l_B~Xfifkrr=pCSweNTIpE<2p zlfJHAa|u&il#9Y44-290%eK-a(MoA8(Lw3X9cIss zf|zFN(AL4*TbL7m};H&2IPF{Awe2nbvY-Tx*=(LT|aPEvl`d?Le3z z%w@U~s`K~en>w00wsySgxYhA4!zc>_??X&wO=b0EjXv@|9CBE{s<7%Y#lB+VaK7hU zRV^dtFv>HJQrA-;HY|p!zvYLWz1=UU|P9@pzs7?2NuX<5c^hovII|O` zW;Mlf{?(j)bKHE~%wz;H;(7d)N&Ta?NA1o{%D3JMF*rFDrSyLgmYQ7PfQuBua)hsy9->&~D@I`1iOYdb^z# z?DPm>SAR>cH44>wj?B}atiGUAbfwl&#&I|covoaC8bn86&~@L>rx?WL5MijC)tOOK$tuZz71th`dX)zd(-3Y-6#cv!bjPppDU@$i4vk?<0gT9Uo5 zWA;_$%fTxqH|B5hXB8S1K3=WLi*@iYP$zw=D?Nd#FbfJDlpI&ux-a&SXsOxbi&c8` zUgwfokF@fLI_)q*VAQdOm(dLmg#y1wxl2yQoc%J?H+$5X1oa$!Nd6YfQ!`gexLB?@ zsFJ31?!E3%$fQ~v^X0RQp=%F{N}8+vy8L_mr$3DtWP8b`7N>nmlV!;C4?K_=J@jC9 z`K$FHG_6B-u;zRfuKM;fv&XfRf)||~rWV9I#3kZ4qVZhM@I!LnDx-T&Exh)t;cvZz zUbQRh<}aQOx(m4zdi{GTYxZlED;DJm#nY>)YxJXKPV}JJR^cAubumrZs=n&Cz3M#} zqHEH-eP3*4TYq`F!JFqA$QaAG|9YckOp}EVotR#c7+u*dgC012IlT0v*qdKYt5emX zC$O0dnKoH&nQLA?UQe7~nRmaN843GtJNS#-4MQ`}&;yIa7qo%t=r<|Ug|5rI>%6lO zkUxgJ2X9q{Px*F^o{(eCKauBr?6Kxwnli05?L4yZn6pqZIJw>9u}9`z^l|zOXU1$J z<&AS|&5fGO^6Ddj)pKEW55xUerq!}dI)|6)LVs80zw6CLVTS7#!z(a2{al^7vRdcb<4cyaR{gl)xLymdjiLARL+4J^b8{BEhiq3wW6pPNBrhk);kG7a zB(=xN#D2-%Z;nEZS+LiqzZc-T{JONWRW@#Iw3n+WLnBsuzw~u>r+4S3Eu^J9qo2uJ zpQ-<%dUvp;v1Rwu7a>Uav86+6vklxKuKN7#Q90*{GoW+2{D431FT1@iSW8h&N#TnK zr!Rh=H@X%r_^(vuSd%zzOn(lS%%%WVeoP+<$evE7Qd}uyztEr;6f*!2)};|i91_71 z?aQP?$eTWp5IReM1^_dQ5Ej`tkir4^P^dHp20UN$3=E?AVZa_n1Q>yZqXf|G!q^nI zFejpKSfDS;4{Tu$G7CWq2_OC4Htbb@3!GBjuP%~%ok9RP~ zmGU3G|C2bF7|NnRT`9rLQ*2*B@BB44L$S~}HigV#vWZOQ$sdJ07{KH(g9Df>5CRE- zgLDaGUm9c6viDC2fq=GW1ars?Uy3~*0~U}#Xf!`G9uC9W*z89l_alwqaBKX2BnoZ? zvw|5T_oLv3CfFZXJk$3SoxWBq=v1@TiXR3HYr+1vl>^$(L^fHt@P46oqu&-haqf|+LvhFcAp;MtHb?>>aojMU&+$&Jw2#YyGzNAg?=0N~xO|1%9#hzl z?cEASvP%gSwpPE+s1{*#d-3TN&&Ml8>K`_AH+6iBd^^X2G{h(8k literal 0 HcmV?d00001 diff --git a/applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png b/applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png new file mode 100644 index 0000000000000000000000000000000000000000..ff4af9ff05989e5ff04d3888971b7f7801c59ed4 GIT binary patch literal 3698 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 QfL1Vgy85}Sb4q9e0PSTwjsO4v literal 0 HcmV?d00001 diff --git a/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png b/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png new file mode 100644 index 0000000000000000000000000000000000000000..35c205049bc5a6c7f0bb13a0cb3057963a023c6e GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^xG+l41V{G`^6RB9^&ce?Q{EhH1o(i zcG0sF=jLrIU`gfJ9e<(w?c#$+`{GybuV0XK^{NaL55t;h1_!r?84rQRGkCiCxvX
f4IeWS|hDc0ZJK-P~g93;1_80%_ zf0mxq_>?eVL9oQDG>54hmUMGg2<9ZE<@p-^(r|y*a$w%vQ*}A2s_`AWqh21A3+I&= XdhU3CV!O&Kpm7YIu6{1-oD!M<1Jf%z literal 0 HcmV?d00001 diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c new file mode 100644 index 000000000..cba8b6085 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c @@ -0,0 +1,108 @@ +#include "nfc_rfid_detector_app_i.h" + +#include +#include + +static bool nfc_rfid_detector_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool nfc_rfid_detector_app_back_event_callback(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void nfc_rfid_detector_app_tick_event_callback(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +NfcRfidDetectorApp* nfc_rfid_detector_app_alloc() { + NfcRfidDetectorApp* app = malloc(sizeof(NfcRfidDetectorApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&nfc_rfid_detector_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, nfc_rfid_detector_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcRfidDetectorViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcRfidDetectorViewWidget, widget_get_view(app->widget)); + + // Field Presence + app->nfc_rfid_detector_field_presence = nfc_rfid_detector_view_field_presence_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + NfcRfidDetectorViewFieldPresence, + nfc_rfid_detector_view_field_presence_get_view(app->nfc_rfid_detector_field_presence)); + + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneStart); + + return app; +} + +void nfc_rfid_detector_app_free(NfcRfidDetectorApp* app) { + furi_assert(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); + submenu_free(app->submenu); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewWidget); + widget_free(app->widget); + + // Field Presence + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); + nfc_rfid_detector_view_field_presence_free(app->nfc_rfid_detector_field_presence); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t nfc_rfid_detector_app(void* p) { + UNUSED(p); + NfcRfidDetectorApp* nfc_rfid_detector_app = nfc_rfid_detector_app_alloc(); + + view_dispatcher_run(nfc_rfid_detector_app->view_dispatcher); + + nfc_rfid_detector_app_free(nfc_rfid_detector_app); + + return 0; +} diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c new file mode 100644 index 000000000..c59d40d50 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c @@ -0,0 +1,40 @@ +#include "nfc_rfid_detector_app_i.h" + +#include + +#define TAG "NfcRfidDetector" + +void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app) { + furi_assert(app); + + // start the field presence rfid detection + furi_hal_rfid_field_detect_start(); + + // start the field presence nfc detection + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_field_detect_start(); +} + +void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app) { + furi_assert(app); + + // stop the field presence rfid detection + furi_hal_rfid_field_detect_stop(); + + // stop the field presence nfc detection + furi_hal_nfc_start_sleep(); +} + +bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app) { + furi_assert(app); + + // check if the field presence is nfc + return furi_hal_nfc_field_is_present(); +} + +bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency) { + furi_assert(app); + + // check if the field presence is rfid + return furi_hal_rfid_field_is_present(frequency); +} \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h new file mode 100644 index 000000000..72cb126d4 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "helpers/nfc_rfid_detector_types.h" +#include "helpers/nfc_rfid_detector_event.h" + +#include "scenes/nfc_rfid_detector_scene.h" +#include +#include +#include +#include +#include +#include +#include "views/nfc_rfid_detector_view_field_presence.h" + +typedef struct NfcRfidDetectorApp NfcRfidDetectorApp; + +struct NfcRfidDetectorApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + Submenu* submenu; + Widget* widget; + NfcRfidDetectorFieldPresence* nfc_rfid_detector_field_presence; +}; + +void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app); +void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app); +bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app); +bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency); \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c new file mode 100644 index 000000000..d75eb2884 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c @@ -0,0 +1,31 @@ +#include "../nfc_rfid_detector_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const nfc_rfid_detector_scene_on_enter_handlers[])(void*) = { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const nfc_rfid_detector_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = + { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const nfc_rfid_detector_scene_on_exit_handlers[])(void* context) = { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers nfc_rfid_detector_scene_handlers = { + .on_enter_handlers = nfc_rfid_detector_scene_on_enter_handlers, + .on_event_handlers = nfc_rfid_detector_scene_on_event_handlers, + .on_exit_handlers = nfc_rfid_detector_scene_on_exit_handlers, + .scene_num = NfcRfidDetectorSceneNum, +}; diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h new file mode 100644 index 000000000..74d324b4d --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcRfidDetectorScene##id, +typedef enum { +#include "nfc_rfid_detector_scene_config.h" + NfcRfidDetectorSceneNum, +} NfcRfidDetectorScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_rfid_detector_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c new file mode 100644 index 000000000..ddcb8aac0 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c @@ -0,0 +1,69 @@ +#include "../nfc_rfid_detector_app_i.h" + +void nfc_rfid_detector_scene_about_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcRfidDetectorApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void nfc_rfid_detector_scene_about_on_enter(void* context) { + NfcRfidDetectorApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", NFC_RFID_DETECTOR_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", NFC_RFID_DETECTOR_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", NFC_RFID_DETECTOR_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, + "This application allows\nyou to determine what\ntype of electromagnetic\nfield the reader is using.\nFor LF RFID you can also\nsee the carrier frequency\n\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! NFC/RFID detector \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewWidget); +} + +bool nfc_rfid_detector_scene_about_on_event(void* context, SceneManagerEvent event) { + NfcRfidDetectorApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void nfc_rfid_detector_scene_about_on_exit(void* context) { + NfcRfidDetectorApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h new file mode 100644 index 000000000..ab49ad5c2 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(nfc_rfid_detector, start, Start) +ADD_SCENE(nfc_rfid_detector, about, About) +ADD_SCENE(nfc_rfid_detector, field_presence, FieldPresence) diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c new file mode 100644 index 000000000..ec53b5a0a --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c @@ -0,0 +1,60 @@ +#include "../nfc_rfid_detector_app_i.h" +#include "../views/nfc_rfid_detector_view_field_presence.h" + +void nfc_rfid_detector_scene_field_presence_callback( + NfcRfidDetectorCustomEvent event, + void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +static const NotificationSequence notification_app_display_on = { + + &message_display_backlight_on, + NULL, +}; + +static void nfc_rfid_detector_scene_field_presence_update(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + + uint32_t frequency = 0; + bool nfc_field = nfc_rfid_detector_app_field_presence_is_nfc(app); + bool rfid_field = nfc_rfid_detector_app_field_presence_is_rfid(app, &frequency); + + if(nfc_field || rfid_field) + notification_message(app->notifications, ¬ification_app_display_on); + + nfc_rfid_detector_view_field_presence_update( + app->nfc_rfid_detector_field_presence, nfc_field, rfid_field, frequency); +} + +void nfc_rfid_detector_scene_field_presence_on_enter(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + + // Start detection of field presence + nfc_rfid_detector_app_field_presence_start(app); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); +} + +bool nfc_rfid_detector_scene_field_presence_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + nfc_rfid_detector_scene_field_presence_update(app); + } + + return consumed; +} + +void nfc_rfid_detector_scene_field_presence_on_exit(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + // Stop detection of field presence + nfc_rfid_detector_app_field_presence_stop(app); +} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c new file mode 100644 index 000000000..7b71bd973 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c @@ -0,0 +1,58 @@ +#include "../nfc_rfid_detector_app_i.h" + +typedef enum { + SubmenuIndexNfcRfidDetectorFieldPresence, + SubmenuIndexNfcRfidDetectorAbout, +} SubmenuIndex; + +void nfc_rfid_detector_scene_start_submenu_callback(void* context, uint32_t index) { + NfcRfidDetectorApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_rfid_detector_scene_start_on_enter(void* context) { + UNUSED(context); + NfcRfidDetectorApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Detect field type", + SubmenuIndexNfcRfidDetectorFieldPresence, + nfc_rfid_detector_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + SubmenuIndexNfcRfidDetectorAbout, + nfc_rfid_detector_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcRfidDetectorSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); +} + +bool nfc_rfid_detector_scene_start_on_event(void* context, SceneManagerEvent event) { + NfcRfidDetectorApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcRfidDetectorAbout) { + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexNfcRfidDetectorFieldPresence) { + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneFieldPresence); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, NfcRfidDetectorSceneStart, event.event); + } + + return consumed; +} + +void nfc_rfid_detector_scene_start_on_exit(void* context) { + NfcRfidDetectorApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c new file mode 100644 index 000000000..e65eb8362 --- /dev/null +++ b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c @@ -0,0 +1,164 @@ +#include "nfc_rfid_detector_view_field_presence.h" +#include "../nfc_rfid_detector_app_i.h" +#include + +#include +#include + +#define FIELD_FOUND_WEIGHT 5 + +typedef enum { + NfcRfidDetectorTypeFieldPresenceNfc, + NfcRfidDetectorTypeFieldPresenceRfid, +} NfcRfidDetectorTypeFieldPresence; + +static const Icon* NfcRfidDetectorFieldPresenceIcons[] = { + [NfcRfidDetectorTypeFieldPresenceNfc] = &I_NFC_detect_45x30, + [NfcRfidDetectorTypeFieldPresenceRfid] = &I_Rfid_detect_45x30, +}; + +struct NfcRfidDetectorFieldPresence { + View* view; +}; + +typedef struct { + uint8_t nfc_field; + uint8_t rfid_field; + uint32_t rfid_frequency; +} NfcRfidDetectorFieldPresenceModel; + +void nfc_rfid_detector_view_field_presence_update( + NfcRfidDetectorFieldPresence* instance, + bool nfc_field, + bool rfid_field, + uint32_t rfid_frequency) { + furi_assert(instance); + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + if(nfc_field) { + model->nfc_field = FIELD_FOUND_WEIGHT; + } else if(model->nfc_field) { + model->nfc_field--; + } + if(rfid_field) { + model->rfid_field = FIELD_FOUND_WEIGHT; + model->rfid_frequency = rfid_frequency; + } else if(model->rfid_field) { + model->rfid_field--; + } + }, + true); +} + +void nfc_rfid_detector_view_field_presence_draw( + Canvas* canvas, + NfcRfidDetectorFieldPresenceModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(!model->nfc_field && !model->rfid_field) { + canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34); + canvas_draw_icon(canvas, 22, 12, &I_Move_flipper_26x39); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 56, 36, "Touch the reader"); + } else { + if(model->nfc_field) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 21, 10, "NFC"); + canvas_draw_icon( + canvas, + 9, + 17, + NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceNfc]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 9, 62, "13,56 MHz"); + } + + if(model->rfid_field) { + char str[16]; + snprintf(str, sizeof(str), "%.02f KHz", (double)model->rfid_frequency / 1000); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 76, 10, "LF RFID"); + canvas_draw_icon( + canvas, + 71, + 17, + NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceRfid]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 69, 62, str); + } + } +} + +bool nfc_rfid_detector_view_field_presence_input(InputEvent* event, void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + UNUSED(instance); + + if(event->key == InputKeyBack) { + return false; + } + + return true; +} + +void nfc_rfid_detector_view_field_presence_enter(void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + model->nfc_field = 0; + model->rfid_field = 0; + model->rfid_frequency = 0; + }, + true); +} + +void nfc_rfid_detector_view_field_presence_exit(void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + UNUSED(instance); +} + +NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc() { + NfcRfidDetectorFieldPresence* instance = malloc(sizeof(NfcRfidDetectorFieldPresence)); + + // View allocation and configuration + instance->view = view_alloc(); + + view_allocate_model( + instance->view, ViewModelTypeLocking, sizeof(NfcRfidDetectorFieldPresenceModel)); + view_set_context(instance->view, instance); + view_set_draw_callback( + instance->view, (ViewDrawCallback)nfc_rfid_detector_view_field_presence_draw); + view_set_input_callback(instance->view, nfc_rfid_detector_view_field_presence_input); + view_set_enter_callback(instance->view, nfc_rfid_detector_view_field_presence_enter); + view_set_exit_callback(instance->view, nfc_rfid_detector_view_field_presence_exit); + + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + model->nfc_field = 0; + model->rfid_field = 0; + model->rfid_frequency = 0; + }, + true); + return instance; +} + +void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h new file mode 100644 index 000000000..0ddb4e2cd --- /dev/null +++ b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/nfc_rfid_detector_types.h" +#include "../helpers/nfc_rfid_detector_event.h" + +typedef struct NfcRfidDetectorFieldPresence NfcRfidDetectorFieldPresence; + +void nfc_rfid_detector_view_field_presence_update( + NfcRfidDetectorFieldPresence* instance, + bool nfc_field, + bool rfid_field, + uint32_t rfid_frequency); + +NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc(); + +void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance); + +View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance); diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 56a7f678d..0b4e2d6d6 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,31.0,, +Version,+,31.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0f782e966..5ba66407e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,31.0,, +Version,+,31.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1220,6 +1220,8 @@ Function,+,furi_hal_mpu_protect_disable,void,FuriHalMpuRegion Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_nfc_activate_nfca,_Bool,"uint32_t, uint32_t*" +Function,+,furi_hal_nfc_field_is_present,_Bool, +Function,+,furi_hal_nfc_field_detect_start,void, Function,-,furi_hal_nfc_deinit,void, Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t" Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, FuriHalNfcEmulateCallback, void*, uint32_t" @@ -1304,6 +1306,9 @@ Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_comp_set_callback,void,"FuriHalRfidCompCallback, void*" Function,+,furi_hal_rfid_comp_start,void, Function,+,furi_hal_rfid_comp_stop,void, +Function,+,furi_hal_rfid_field_is_present,_Bool,uint32_t* +Function,+,furi_hal_rfid_field_detect_start,void, +Function,+,furi_hal_rfid_field_detect_stop,void, Function,-,furi_hal_rfid_init,void, Function,+,furi_hal_rfid_pin_pull_pulldown,void, Function,+,furi_hal_rfid_pin_pull_release,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index c4e7ad9f9..b249c8658 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -819,3 +819,17 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( void furi_hal_nfc_ll_poll() { rfalWorker(); } + +void furi_hal_nfc_field_detect_start() { + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_en_fd_mask); + st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ | ST25R3916_REG_MODE_om0); +} + +bool furi_hal_nfc_field_is_present() { + return st25r3916CheckReg( + ST25R3916_REG_AUX_DISPLAY, + ST25R3916_REG_AUX_DISPLAY_efd_o, + ST25R3916_REG_AUX_DISPLAY_efd_o); +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h index dc3f873f3..c87f04a9a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.h @@ -423,6 +423,10 @@ FuriHalNfcReturn furi_hal_nfc_ll_txrx_bits( void furi_hal_nfc_ll_poll(); +void furi_hal_nfc_field_detect_start(); + +bool furi_hal_nfc_field_is_present(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index fa0c19b09..67f11d6ff 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -25,6 +25,19 @@ #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 +// Field presence detection +#define FURI_HAL_RFID_FIELD_FREQUENCY_MIN 80000 +#define FURI_HAL_RFID_FIELD_FREQUENCY_MAX 200000 + +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER TIM2 +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS FuriHalBusTIM2 +#define FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL LL_TIM_CHANNEL_CH3 + +#define FURI_HAL_RFID_FIELD_TIMEOUT_TIMER TIM1 +#define FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS FuriHalBusTIM1 + +#define FURI_HAL_RFID_FIELD_DMAMUX_DMA LL_DMAMUX_REQ_TIM1_UP + /* DMA Channels definition */ #define RFID_DMA DMA2 #define RFID_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 @@ -33,10 +46,16 @@ #define RFID_DMA_CH1_DEF RFID_DMA, RFID_DMA_CH1_CHANNEL #define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL +typedef struct { + uint32_t counter; + uint32_t set_tim_counter_cnt; +} FuriHalRfidField; + typedef struct { FuriHalRfidDMACallback dma_callback; FuriHalRfidReadCaptureCallback read_capture_callback; void* context; + FuriHalRfidField field; } FuriHalRfid; FuriHalRfid* furi_hal_rfid = NULL; @@ -51,6 +70,8 @@ FuriHalRfid* furi_hal_rfid = NULL; void furi_hal_rfid_init() { furi_assert(furi_hal_rfid == NULL); furi_hal_rfid = malloc(sizeof(FuriHalRfid)); + furi_hal_rfid->field.counter = 0; + furi_hal_rfid->field.set_tim_counter_cnt = 0; furi_hal_rfid_pins_reset(); @@ -133,6 +154,23 @@ static void furi_hal_rfid_pins_read() { furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } +static void furi_hal_rfid_pins_field() { + // ibutton low + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); + + // pull pin to timer out + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); + + // pull rfid antenna from carrier side + furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_carrier_out, false); + + furi_hal_gpio_init_ex( + &gpio_rfid_carrier, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn2TIM2); +} + void furi_hal_rfid_pin_pull_release() { furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, true); } @@ -427,3 +465,124 @@ void COMP_IRQHandler() { furi_hal_rfid_comp_callback_context); } } + +static void furi_hal_rfid_field_tim_setup() { + // setup timer counter + furi_hal_bus_enable(FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS); + + LL_TIM_SetPrescaler(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + LL_TIM_SetCounterMode(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0xFFFFFFFF); + LL_TIM_DisableARRPreload(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_SetRepetitionCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + + LL_TIM_SetClockDivision(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_FIELD_COUNTER_TIMER, LL_TIM_CLOCKSOURCE_EXT_MODE2); + LL_TIM_ConfigETR( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + LL_TIM_ETR_POLARITY_INVERTED, + LL_TIM_ETR_PRESCALER_DIV1, + LL_TIM_ETR_FILTER_FDIV1); + + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; + TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; + TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; + TIM_OC_InitStruct.CompareValue = 1; + LL_TIM_OC_Init( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL, + &TIM_OC_InitStruct); + + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_OC_SetPolarity( + FURI_HAL_RFID_FIELD_COUNTER_TIMER, + FURI_HAL_RFID_FIELD_COUNTER_TIMER_CHANNEL, + LL_TIM_OCPOLARITY_HIGH); + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + // setup timer timeouts dma + furi_hal_bus_enable(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS); + + LL_TIM_SetPrescaler(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 64000 - 1); + LL_TIM_SetCounterMode(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 100 - 1); // 100 ms + LL_TIM_SetClockDivision(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, LL_TIM_CLOCKSOURCE_INTERNAL); + + LL_TIM_DisableARRPreload(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + + LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + LL_TIM_GenerateEvent_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); +} + +void furi_hal_rfid_field_detect_start(void) { + // setup pins + furi_hal_rfid_pins_field(); + + // configure timer + furi_hal_rfid_field_tim_setup(); + + // configure DMA "TIM_COUNTER_CNT -> counter" + LL_DMA_SetMemoryAddress(RFID_DMA_CH1_DEF, (uint32_t) & (furi_hal_rfid->field.counter)); + LL_DMA_SetPeriphAddress( + RFID_DMA_CH1_DEF, (uint32_t) & (FURI_HAL_RFID_FIELD_COUNTER_TIMER->CNT)); + LL_DMA_ConfigTransfer( + RFID_DMA_CH1_DEF, + LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_MEDIUM); + LL_DMA_SetDataLength(RFID_DMA_CH1_DEF, 1); + LL_DMA_SetPeriphRequest(RFID_DMA_CH1_DEF, FURI_HAL_RFID_FIELD_DMAMUX_DMA); + LL_DMA_EnableChannel(RFID_DMA_CH1_DEF); + + // configure DMA "mem -> TIM_COUNTER_CNT" + LL_DMA_SetMemoryAddress( + RFID_DMA_CH2_DEF, (uint32_t) & (furi_hal_rfid->field.set_tim_counter_cnt)); + LL_DMA_SetPeriphAddress( + RFID_DMA_CH2_DEF, (uint32_t) & (FURI_HAL_RFID_FIELD_COUNTER_TIMER->CNT)); + LL_DMA_ConfigTransfer( + RFID_DMA_CH2_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_NOINCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_LOW); + LL_DMA_SetDataLength(RFID_DMA_CH2_DEF, 1); + LL_DMA_SetPeriphRequest(RFID_DMA_CH2_DEF, FURI_HAL_RFID_FIELD_DMAMUX_DMA); + LL_DMA_EnableChannel(RFID_DMA_CH2_DEF); + + // start tim counter + LL_TIM_EnableAllOutputs(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + LL_TIM_SetCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + // start tim timeout + LL_TIM_SetCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER, 0); + LL_TIM_EnableCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + LL_TIM_EnableIT_UPDATE(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); +} + +void furi_hal_rfid_field_detect_stop(void) { + LL_TIM_DisableCounter(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + LL_TIM_DisableAllOutputs(FURI_HAL_RFID_FIELD_COUNTER_TIMER); + + LL_TIM_DisableCounter(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER); + + FURI_CRITICAL_ENTER(); + + LL_DMA_DeInit(RFID_DMA_CH1_DEF); + LL_DMA_DeInit(RFID_DMA_CH2_DEF); + + furi_hal_bus_disable(FURI_HAL_RFID_FIELD_COUNTER_TIMER_BUS); + furi_hal_bus_disable(FURI_HAL_RFID_FIELD_TIMEOUT_TIMER_BUS); + + furi_hal_rfid_pins_reset(); + + FURI_CRITICAL_EXIT(); +} + +bool furi_hal_rfid_field_is_present(uint32_t* frequency) { + *frequency = furi_hal_rfid->field.counter * 10; + return ( + (*frequency >= FURI_HAL_RFID_FIELD_FREQUENCY_MIN) && + (*frequency <= FURI_HAL_RFID_FIELD_FREQUENCY_MAX)); +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.h b/firmware/targets/f7/furi_hal/furi_hal_rfid.h index 78d9b6658..7087ba991 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.h +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.h @@ -87,6 +87,20 @@ typedef void (*FuriHalRfidCompCallback)(bool level, void* context); /** Set comparator callback */ void furi_hal_rfid_comp_set_callback(FuriHalRfidCompCallback callback, void* context); +/** Start/Enable Field Presence detect */ +void furi_hal_rfid_field_detect_start(); + +/** Stop/Disable Field Presence detect */ +void furi_hal_rfid_field_detect_stop(); + +/** Check Field Presence + * + * @param[out] frequency pointer to frequency value to be set if filed detected + * + * @return true if field is present, false if not + */ +bool furi_hal_rfid_field_is_present(uint32_t* frequency); + #ifdef __cplusplus } #endif From ee96e347673814a828dfe0901a7d0af360fdc20a Mon Sep 17 00:00:00 2001 From: MMX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 28 Jun 2023 12:25:07 +0300 Subject: [PATCH 292/370] Fix furi_hal_bus related crashes in plugins (#2799) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix furi_hal_bus issues in plugins * Rework pwm is running check * ApiSymbols: update and sync targets Co-authored-by: あく --- .../helpers/avr_isp_worker_rw.c | 19 ++++++++++--- .../scenes/signal_gen_scene_pwm.c | 27 ++++++++++++++++--- firmware/targets/f18/api_symbols.csv | 3 ++- firmware/targets/f7/api_symbols.csv | 3 ++- firmware/targets/f7/furi_hal/furi_hal_pwm.c | 9 +++++++ firmware/targets/f7/furi_hal/furi_hal_pwm.h | 8 ++++++ 6 files changed, 59 insertions(+), 10 deletions(-) diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c index 0ee5cefa1..b4c12cbc3 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c @@ -60,7 +60,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) { AvrIspWorkerRW* instance = context; /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + } FURI_LOG_D(TAG, "Start"); @@ -122,7 +124,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) { } FURI_LOG_D(TAG, "Stop"); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } return 0; } @@ -136,7 +140,12 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { instance->chip_arr_ind = avr_isp_chip_arr_size + 1; /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + bool was_pwm_enabled = false; + if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); + } else { + was_pwm_enabled = true; + } do { if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { @@ -200,7 +209,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { } while(0); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) { + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + } if(instance->callback) { if(instance->chip_arr_ind > avr_isp_chip_arr_size) { diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c index 7ac3fadda..1cadb3a1a 100644 --- a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c +++ b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c @@ -33,7 +33,13 @@ void signal_gen_scene_pwm_on_enter(void* context) { signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app); signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY); - furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + + if(!furi_hal_pwm_is_running(pwm_ch_id[0])) { + furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + } else { + furi_hal_pwm_stop(pwm_ch_id[0]); + furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); + } } bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { @@ -46,8 +52,18 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty); } else if(event.event == SignalGenPwmEventChannelChange) { consumed = true; - furi_hal_pwm_stop(app->pwm_ch_prev); - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + // Stop previous channel PWM + if(furi_hal_pwm_is_running(app->pwm_ch_prev)) { + furi_hal_pwm_stop(app->pwm_ch_prev); + } + + // Start PWM and restart if it was starter already + if(furi_hal_pwm_is_running(app->pwm_ch)) { + furi_hal_pwm_stop(app->pwm_ch); + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } else { + furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); + } } } return consumed; @@ -56,5 +72,8 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { void signal_gen_scene_pwm_on_exit(void* context) { SignalGenApp* app = context; variable_item_list_reset(app->var_item_list); - furi_hal_pwm_stop(app->pwm_ch); + + if(furi_hal_pwm_is_running(app->pwm_ch)) { + furi_hal_pwm_stop(app->pwm_ch); + } } diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 0b4e2d6d6..2e176a5b5 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,31.1,, +Version,+,31.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1061,6 +1061,7 @@ Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, Function,+,furi_hal_power_suppress_charge_enter,void, Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 5ba66407e..ac2b11f38 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,31.1,, +Version,+,31.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1286,6 +1286,7 @@ Function,+,furi_hal_power_sleep,void, Function,+,furi_hal_power_sleep_available,_Bool, Function,+,furi_hal_power_suppress_charge_enter,void, Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.c b/firmware/targets/f7/furi_hal/furi_hal_pwm.c index 7e985cbb1..879460e6b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.c +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.c @@ -82,6 +82,15 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel) { } } +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel) { + if(channel == FuriHalPwmOutputIdTim1PA7) { + return furi_hal_bus_is_enabled(FuriHalBusTIM1); + } else if(channel == FuriHalPwmOutputIdLptim2PA4) { + return furi_hal_bus_is_enabled(FuriHalBusLPTIM2); + } + return false; +} + void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) { furi_assert(freq > 0); uint32_t freq_div = 64000000LU / freq; diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.h b/firmware/targets/f7/furi_hal/furi_hal_pwm.h index a8682c5fb..16acca05e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.h +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.h @@ -9,6 +9,7 @@ extern "C" { #endif #include +#include typedef enum { FuriHalPwmOutputIdTim1PA7, @@ -37,6 +38,13 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel); */ void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty); +/** Is PWM channel running? + * + * @param[in] channel PWM channel (FuriHalPwmOutputId) + * @return bool - true if running +*/ +bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel); + #ifdef __cplusplus } #endif From 6f1c46e11de06fb172b2212f606cb0863a54f64f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Legrelle?= Date: Wed, 28 Jun 2023 11:36:40 +0200 Subject: [PATCH 293/370] Fix fr-FR-mac keylayout (#2809) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../badusb/assets/layouts/fr-FR-mac.kl | Bin 256 -> 256 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/resources/badusb/assets/layouts/fr-FR-mac.kl b/assets/resources/badusb/assets/layouts/fr-FR-mac.kl index 0906936547cd3c8accd9632bac82cb4193a24c25..2887aae0fda061fe85930922db12ab03126a5002 100644 GIT binary patch delta 43 ycmZo*YG9h+#+WkE-H_9i!Ggh*Nq{M3Vxt)=n>m{<+r&NAoGkoox-5b$3=9C%s0beb delta 43 ycmZo*YG9h+#%Max-H Date: Wed, 28 Jun 2023 14:46:42 +0300 Subject: [PATCH 294/370] Fix roll-over in file browser and archive (#2811) --- .../main/archive/views/archive_browser_view.c | 19 +++++++++++-------- .../services/gui/modules/file_browser.c | 17 ++++++++++------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 7e2f84fc2..3c2f13215 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -345,11 +345,13 @@ static bool archive_view_input(InputEvent* event, void* context) { if(event->key == InputKeyUp) { if(model->item_idx < scroll_speed) { - scroll_speed = model->item_idx; + model->button_held_for_ticks = 0; + model->item_idx = model->item_cnt - 1; + } else { + model->item_idx = + ((model->item_idx - scroll_speed) + model->item_cnt) % + model->item_cnt; } - - model->item_idx = - ((model->item_idx - scroll_speed) + model->item_cnt) % model->item_cnt; if(is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadPrevItems, browser->context); @@ -361,11 +363,12 @@ static bool archive_view_input(InputEvent* event, void* context) { model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { int32_t count = model->item_cnt; - if(model->item_idx >= (count - scroll_speed)) { - scroll_speed = model->item_cnt - model->item_idx - 1; + if(model->item_idx + scroll_speed >= count) { + model->button_held_for_ticks = 0; + model->item_idx = 0; + } else { + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; } - - model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; if(is_file_list_load_required(model)) { model->list_loading = true; browser->callback(ArchiveBrowserEventLoadNextItems, browser->context); diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index b2a2c3c23..c764a1cf7 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -602,11 +602,13 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { if(event->key == InputKeyUp) { if(model->item_idx < scroll_speed) { - scroll_speed = model->item_idx; + model->button_held_for_ticks = 0; + model->item_idx = model->item_cnt - 1; + } else { + model->item_idx = + ((model->item_idx - scroll_speed) + model->item_cnt) % + model->item_cnt; } - - model->item_idx = - ((model->item_idx - scroll_speed) + model->item_cnt) % model->item_cnt; if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( @@ -622,10 +624,11 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { } else if(event->key == InputKeyDown) { int32_t count = model->item_cnt; if(model->item_idx + scroll_speed >= count) { - scroll_speed = count - model->item_idx - 1; + model->button_held_for_ticks = 0; + model->item_idx = 0; + } else { + model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; } - - model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; if(browser_is_list_load_required(model)) { model->list_loading = true; int32_t load_offset = CLAMP( From a595231d2554c8dbdc37ea0c096e20f4cbae9361 Mon Sep 17 00:00:00 2001 From: minchogaydarov <134236905+minchogaydarov@users.noreply.github.com> Date: Wed, 28 Jun 2023 17:54:42 +0200 Subject: [PATCH 295/370] Add Mitsubishi MSZ-AP25VGK universal ac remote (#2800) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- assets/resources/infrared/assets/ac.ir | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 142c49243..c7b1aaf7e 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -470,3 +470,40 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 4467 4390 571 1583 572 505 595 1560 572 1583 572 505 572 505 596 1559 596 482 596 481 597 1559 626 451 655 422 625 1529 596 1559 596 481 572 1582 573 1583 571 505 572 1583 595 1560 594 1561 592 1562 594 1561 593 1562 593 484 593 1563 592 485 592 485 592 485 592 485 593 484 593 485 592 485 592 1562 593 485 592 1563 592 1562 593 1562 594 483 593 485 593 1562 592 485 593 1561 593 484 593 484 593 484 593 1562 593 1562 592 5163 4462 4370 592 1563 593 484 592 1563 592 1563 592 485 592 485 593 1562 593 484 593 485 592 1562 593 484 593 485 592 1562 593 1562 593 485 592 1563 592 1563 592 485 592 1563 592 1562 593 1562 593 1563 592 1563 592 1562 592 485 593 1562 592 485 592 485 592 485 592 485 592 485 592 485 592 485 592 1563 592 485 592 1563 591 1563 592 1563 593 485 592 485 592 1563 592 485 592 1563 591 485 593 485 592 485 592 1563 592 1563 591 +# +# Model: Mitsubishi MSZ-AP25VGK +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3531 1667 500 1225 499 1225 499 376 499 377 498 377 498 1224 500 377 498 377 498 1224 500 1225 499 377 527 1195 557 318 556 318 555 1167 530 1194 529 374 499 1224 499 1225 497 377 497 378 497 1228 496 379 496 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 380 495 9028 3526 1672 495 1229 495 1229 495 380 495 380 495 380 495 1230 494 380 495 380 495 1229 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 1229 495 380 495 380 495 1229 495 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 380 495 381 494 381 494 381 494 381 494 381 494 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 381 494 381 494 381 494 1230 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 1230 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 381 494 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 382 493 1231 493 382 493 1231 493 382 493 1231 493 382 493 383 492 382 493 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3561 1666 500 1195 529 1193 531 374 501 375 500 375 500 1196 529 374 501 375 500 1223 501 1223 501 375 529 1194 558 318 557 318 555 1168 530 1193 530 345 529 1194 529 1196 527 348 526 350 525 1200 524 352 522 353 522 1228 496 379 496 379 496 379 496 379 496 379 496 379 497 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 9028 3529 1670 496 1228 496 1228 496 379 496 379 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 379 496 379 496 1228 496 1228 496 379 496 1228 496 1228 496 379 496 379 496 1228 496 379 496 379 496 1228 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 379 496 380 495 379 496 379 496 379 496 379 496 380 495 379 496 380 495 379 496 1229 495 380 495 380 495 379 496 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 380 495 380 495 380 495 1229 495 380 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 496 380 495 380 495 380 495 1229 495 380 495 1229 495 380 495 380 495 1229 495 380 495 1229 495 1229 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 380 495 381 494 380 495 380 495 380 495 380 495 1230 494 380 495 381 494 381 494 380 495 380 495 380 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 495 380 495 381 494 381 494 381 494 381 494 381 494 381 494 381 494 1230 494 381 494 381 494 1230 494 381 494 1230 494 381 494 381 494 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 1637 530 1192 533 1195 529 375 500 375 500 375 500 1195 530 375 501 375 500 1224 500 1224 501 376 528 1195 557 319 556 318 555 1168 530 1193 530 345 530 1194 529 1196 527 348 526 350 525 1201 523 353 522 378 497 1228 496 379 497 379 496 379 497 379 496 379 497 379 497 379 497 379 496 379 496 379 496 379 496 379 497 379 497 379 496 379 496 9030 3530 1671 496 1229 496 1229 495 379 496 379 496 379 497 1229 496 379 496 379 497 1229 496 1229 495 380 496 1229 495 379 497 379 496 1229 496 1229 495 379 497 1229 495 1228 497 379 496 380 496 1229 496 379 497 379 496 1229 496 379 496 380 496 380 495 380 496 379 496 380 496 380 495 380 496 380 496 380 496 379 496 380 496 380 495 380 496 380 495 380 496 380 495 380 496 380 495 380 495 1229 496 380 495 380 495 380 495 380 496 380 495 1229 496 1229 496 380 495 380 496 380 495 380 496 380 496 380 495 380 495 380 496 380 496 380 495 380 496 380 495 1229 495 1230 495 380 495 1229 496 1229 496 380 495 381 495 380 495 380 495 380 496 380 496 380 495 380 496 1229 496 380 495 1230 495 380 495 380 495 1230 495 380 495 1230 495 1230 495 380 495 380 496 380 495 380 495 380 495 380 496 380 496 380 495 380 496 380 495 380 495 380 496 380 495 381 495 380 495 380 495 380 495 381 495 380 495 381 495 380 495 381 494 381 495 381 495 380 495 1230 495 381 495 380 495 381 494 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 495 381 495 381 494 381 495 381 494 381 494 381 495 381 494 381 494 381 494 381 495 1230 495 381 495 1230 495 1230 495 381 494 1230 494 381 495 381 494 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3534 1667 500 1224 501 1224 501 376 500 375 500 376 500 1224 501 376 500 376 499 1224 501 1225 500 376 500 1225 556 320 556 318 555 1167 530 1194 530 345 530 1195 529 1196 528 348 527 377 498 1227 498 378 497 379 496 1229 496 379 496 379 497 379 497 379 497 379 497 380 496 379 497 379 496 379 497 380 496 380 496 380 496 380 496 379 497 379 497 9033 3530 1672 496 1229 496 1229 496 380 496 380 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1229 496 380 496 380 496 1229 496 1229 496 380 496 1230 495 1229 496 380 495 380 496 1229 496 380 496 380 496 1229 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 496 380 495 380 496 380 496 380 496 380 496 380 496 380 496 380 496 1230 495 380 496 380 495 380 496 381 495 380 495 1230 495 1230 495 380 496 380 496 380 496 1230 495 1230 495 1230 495 380 496 380 496 380 496 380 496 380 496 381 495 1230 495 1230 495 381 495 1230 495 1230 495 380 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 1230 495 381 495 1230 495 381 495 380 496 1230 495 381 495 1230 495 1230 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 380 496 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 1231 494 381 495 381 495 381 495 381 495 381 495 381 494 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 381 495 382 494 382 494 1231 494 381 495 1231 494 1231 494 381 495 382 494 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3539 1670 501 1226 502 1226 501 376 501 377 500 376 501 1226 501 376 499 378 500 1226 501 1226 501 377 528 1199 557 320 557 318 529 1197 531 1195 531 346 530 1196 530 1198 528 349 527 352 524 1229 498 379 498 379 498 1230 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9045 3536 1674 496 1231 497 1231 497 380 497 380 497 380 497 1231 496 380 497 380 497 1231 497 1231 496 380 497 1231 496 380 497 380 497 1231 496 1231 497 380 497 1231 496 1231 496 380 497 380 497 1231 496 381 496 380 497 1231 497 381 496 380 497 380 497 380 497 380 497 381 496 381 496 381 496 380 497 380 497 381 496 381 496 381 496 381 496 380 497 381 496 381 496 381 496 381 496 380 497 1231 496 381 496 381 496 380 497 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 1232 495 1232 495 1232 496 1232 495 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 1232 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1232 496 381 496 1232 495 381 496 1232 495 381 496 1232 495 1232 495 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 381 496 382 495 381 496 381 496 381 496 381 496 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1232 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 1233 495 1233 494 1233 495 382 495 382 495 1233 495 1233 494 382 495 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3539 1637 533 1225 502 1193 534 375 501 376 501 376 500 1226 501 376 501 376 500 1226 500 1227 501 376 529 1197 558 320 557 319 555 1169 531 1195 531 346 529 1196 530 1198 528 349 526 352 524 1229 497 379 497 379 497 1230 497 380 497 380 497 379 498 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9042 3535 1674 496 1230 497 1230 497 380 497 380 497 380 497 1230 497 380 497 380 496 1230 497 1230 497 380 497 1230 497 380 497 380 497 1231 496 1230 497 380 497 1231 496 1231 496 380 496 380 497 1231 496 380 497 380 496 1231 496 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 496 380 497 380 497 380 497 380 497 380 496 381 496 380 497 380 497 380 497 381 496 1231 496 381 496 380 497 380 497 381 496 381 496 1231 496 381 496 381 496 380 497 381 495 1231 496 1231 496 1231 496 380 497 381 496 381 496 380 496 381 496 381 496 381 496 381 496 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1231 496 381 496 381 496 1232 495 381 495 1232 495 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 495 381 496 381 496 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 381 495 381 496 381 495 382 495 381 496 382 495 381 495 382 495 381 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 381 495 1232 495 1232 495 1232 495 1232 495 1232 495 382 495 382 495 382 495 From dcf105994bf18ef18fe2f67d3a0f9b1ba033a3be Mon Sep 17 00:00:00 2001 From: Patrick Kilter Date: Wed, 28 Jun 2023 18:17:56 +0200 Subject: [PATCH 296/370] Added Power Button for an unknown Sharp Model (#2787) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested by me, i just asked if I can get the remote. Tested with the universal remote before and thought you would like to add the button data Co-authored-by: あく --- assets/resources/infrared/assets/tv.ir | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index b45171cb1..ba8807123 100644 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1675,3 +1675,9 @@ type: parsed protocol: NEC address: 04 00 00 00 command: 03 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3462 1592 490 332 513 1200 489 331 514 1201 489 355 490 1201 489 356 512 1178 489 356 512 1178 512 334 487 1202 488 1202 488 357 512 1178 512 334 486 1203 487 1202 488 1203 487 1204 486 383 461 1228 488 357 488 357 487 357 487 1203 486 1204 486 359 485 1205 485 360 485 361 484 360 485 360 485 361 484 361 484 361 484 1206 484 360 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 1206 484 361 484 71543 3434 1620 486 359 485 1205 485 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1206 484 360 485 1205 485 1206 484 360 485 1206 484 360 485 1206 484 1206 484 1206 484 1206 484 361 484 1206 484 360 485 360 485 361 484 1206 484 1206 484 360 484 1206 484 360 485 361 484 361 484 360 485 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 361 484 1207 483 361 484 1206 484 361 484 71543 3435 1619 486 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 484 1205 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 1205 485 1206 484 360 485 1205 485 360 485 360 485 360 485 1205 485 1206 484 360 485 1206 484 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1206 484 360 485 360 485 360 485 1205 485 360 485 1206 484 360 485 71542 3436 1619 486 358 487 1204 486 359 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 1206 484 1206 484 1206 484 1206 484 360 485 1206 484 360 485 360 485 361 484 1206 484 1206 484 361 484 1206 484 361 484 361 484 361 484 361 484 361 484 361 484 360 485 1206 484 361 484 361 484 361 484 1206 484 361 484 361 484 360 485 1206 484 361 484 1206 484 361 484 71542 3437 1618 487 358 486 1204 486 359 486 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 360 485 1205 485 1206 484 360 485 1205 485 360 485 1206 484 1205 485 1206 484 1205 485 360 485 1205 485 360 485 360 485 360 485 1205 485 1205 485 360 485 1205 485 360 485 360 485 360 485 360 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 360 485 360 485 1205 485 360 485 1205 485 360 485 From feebf2cd775ace59db7015412a936aecc0e6bd6d Mon Sep 17 00:00:00 2001 From: AloneLiberty <111039319+AloneLiberty@users.noreply.github.com> Date: Wed, 28 Jun 2023 19:35:25 +0300 Subject: [PATCH 297/370] NFC: Improvements to NFC Magic app (#2760) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ability to write gen1b tags (ignore 0x43) Ability to write gen1 7 byte UID tags Fix detection of non magic cards Co-authored-by: あく --- applications/external/nfc_magic/nfc_magic_i.h | 1 + .../external/nfc_magic/nfc_magic_worker.c | 66 ++++++++++--------- .../scenes/nfc_magic_scene_file_select.c | 2 +- .../scenes/nfc_magic_scene_not_magic.c | 3 +- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/applications/external/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h index 4d6b89103..88bc5706f 100644 --- a/applications/external/nfc_magic/nfc_magic_i.h +++ b/applications/external/nfc_magic/nfc_magic_i.h @@ -46,6 +46,7 @@ enum NfcMagicCustomEvent { struct NfcMagicDevice { MagicType type; uint32_t cuid; + uint8_t uid_len; uint32_t password; }; diff --git a/applications/external/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c index 9ee7fd3ee..eb715fe0d 100644 --- a/applications/external/nfc_magic/nfc_magic_worker.c +++ b/applications/external/nfc_magic/nfc_magic_worker.c @@ -107,14 +107,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { } magic_activate(); if(magic_gen1_wupa()) { - if(!magic_gen1_data_access_cmd()) { - FURI_LOG_E( - TAG, "No card response to data access command (not a magic card)"); - nfc_magic_worker->callback( - NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); - done = true; - break; - } + magic_gen1_data_access_cmd(); MfClassicData* mfc_data = &dev_data->mf_classic_data; for(size_t i = 0; i < 64; i++) { @@ -296,6 +289,7 @@ void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { } void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { + FuriHalNfcDevData nfc_data = {}; NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; bool card_found_notified = false; uint8_t gen4_config[MAGIC_GEN4_CONFIG_LEN]; @@ -310,32 +304,44 @@ void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { card_found_notified = true; } - furi_hal_nfc_activate_nfca(200, &magic_dev->cuid); - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - break; - } - - magic_deactivate(); - furi_delay_ms(300); - magic_activate(); - - furi_hal_nfc_activate_nfca(200, &magic_dev->cuid); - if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) { - magic_dev->type = MagicTypeGen4; - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_dev->cuid = nfc_data.cuid; + magic_dev->uid_len = nfc_data.uid_len; + } else { + // wrong BCC + magic_dev->uid_len = 4; } - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); break; - } + } else { + magic_deactivate(); + magic_activate(); + if(furi_hal_nfc_detect(&nfc_data, 200)) { + magic_dev->cuid = nfc_data.cuid; + magic_dev->uid_len = nfc_data.uid_len; + if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) { + magic_dev->type = MagicTypeGen4; + if(!card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); + card_found_notified = true; + } - if(card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; + nfc_magic_worker->callback( + NfcMagicWorkerEventSuccess, nfc_magic_worker->context); + } else { + nfc_magic_worker->callback( + NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); + card_found_notified = true; + } + break; + } else { + if(card_found_notified) { + nfc_magic_worker->callback( + NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); + card_found_notified = false; + } + } } magic_deactivate(); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c index baa6bcccc..04b7024ff 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c @@ -8,7 +8,7 @@ static bool nfc_magic_scene_file_select_is_file_suitable(NfcMagic* nfc_magic) { case MagicTypeClassicDirectWrite: case MagicTypeClassicAPDU: if((nfc_dev->dev_data.mf_classic_data.type != MfClassicType1k) || - (nfc_dev->dev_data.nfc_data.uid_len != 4)) { + (nfc_dev->dev_data.nfc_data.uid_len != nfc_magic->dev->uid_len)) { return false; } return true; diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c index b87f7f383..b4f579f44 100644 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c +++ b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c @@ -13,11 +13,10 @@ void nfc_magic_scene_not_magic_on_enter(void* context) { notification_message(nfc_magic->notifications, &sequence_error); - // widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); widget_add_string_element( widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); widget_add_string_multiline_element( - widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not a magic\ncard"); + widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not magic or unsupported\ncard"); widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_not_magic_widget_callback, nfc_magic); From d1c27b645702081c73756a1cf9817df9e646ed18 Mon Sep 17 00:00:00 2001 From: Dmitry Zinin Date: Wed, 28 Jun 2023 19:49:28 +0300 Subject: [PATCH 298/370] Keynote with vertical layout (#2794) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cherry pick from: * https://github.com/DarkFlippers/unleashed-firmware/pull/428/files * https://github.com/DarkFlippers/unleashed-firmware/pull/524/files Co-authored-by: * MX <10697207+xMasterX@users.noreply.github.com> * gid9798 <30450294+gid9798@users.noreply.github.com> Co-authored-by: MX <10697207+xMasterX@users.noreply.github.com> Co-authored-by: あく --- .../external/hid_app/assets/Space_60x18.png | Bin 0 -> 2871 bytes applications/external/hid_app/hid.c | 12 +++ .../external/hid_app/views/hid_keynote.c | 98 ++++++++++++++++++ .../external/hid_app/views/hid_keynote.h | 2 + 4 files changed, 112 insertions(+) create mode 100644 applications/external/hid_app/assets/Space_60x18.png diff --git a/applications/external/hid_app/assets/Space_60x18.png b/applications/external/hid_app/assets/Space_60x18.png new file mode 100644 index 0000000000000000000000000000000000000000..e29f50ae9220d2f9a9753850dedcc6be0a211e76 GIT binary patch literal 2871 zcmV-73&`||P)004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000Uv zX+uL$Nkc;*aB^>EX>4Tx07%E3mUmQC*A|D*y?1({%`nm#dXp|Nfb=dP9RyJrW(F9_ z0K*JTY>22pL=h1IMUbF?0i&TvtcYSED5zi$NDxqBFp8+CWJcCXe0h2A<>mLsz2Dkr z?{oLrd!Mx~03=TzE-wX^0w9?u;0Jm*(^rK@(6Rjh26%u0rT{Qm>8ZX!?!iDLFE@L0LWj&=4?(nOT_siPRbOditRHZrp6?S8Agej zFG^6va$=5K|`EW#NwP&*~x4%_lS6VhL9s-#7D#h8C*`Lh;NHnGf9}t z74chfY%+(L4giWIwhK6{coCb3n8XhbbP@4#0C1$ZFF5847I3lz;zPNlq-OKEaq$AW zE=!MYYHiJ+dvY?9I0Av8Ka-Wn(gPeepdb@piwLhwjRWWeSr7baCBSDM=|p zK0Q5^$>Pur|2)M1IPkCYSQ^NQ`z*p zYmq4Rp8z$=2uR(a0_5jDfT9oq5_wSE_22vEgAWDbn-``!u{igi1^xT3aEbVl&W-yV z=Mor9X9@Wki)-R*3DAH5Bmou30~MeFbb%o-16IHmI084Y0{DSo5DwM?7KjJQfDbZ3 zF4znTKoQsl_JT@K1L{E|XaOfc2RIEbfXm=IxC!on2Vew@gXdrdyaDqN1YsdEM1kZX zRY(gmfXpBUWDmJPK2RVO4n;$85DyYUxzHA<2r7jtp<1XB`W89`U4X7a1JFHa6qn9`(3jA6(BtSg7z~Dn z(ZN_@JTc*z1k5^2G3EfK6>}alfEmNgVzF3xtO3>z>xX4x1=s@Ye(W*qIqV>I9QzhW z#Hr%UaPGJW91oX=E5|kA&f*4f6S#T26kZE&gZIO;@!9wid_BGke*-^`pC?EYbO?5Y zU_t_6GogaeLbybDNO(mg64i;;!~i0fxQSRnJWjkq93{RZ$&mC(E~H43khGI@gmj*C zkMxR6CTo)&$q{4$c_+D%e3AT^{8oY@VI<)t!Is!4Q6EtGo7CCWGzL)D>rQ4^>|)NiQ$)EQYB*=4e!vRSfKvS(yRXb4T4 z=0!`QmC#PmhG_4XC@*nZ!dbFoNz0PKC3A9$a*lEwxk9;CxjS<2<>~Tn@`>`hkG4N#KjNU~z;vi{c;cwx$aZXSoN&@}N^m;n^upQ1neW`@Jm+HLvfkyqE8^^jVTFG14;RpP@{Py@g^4IZC^Zz~o6W||E74S6BG%z=? zH;57x71R{;CfGT+B=|vyZiq0XJ5(|>GPE&tF3dHoG;Cy*@v8N!u7@jxbHh6$uo0mV z4H2`e-B#~iJsxQhSr9q2MrTddnyYIS)+Vhz6D1kNj5-;Ojt+}%ivGa#W7aWeW4vOj zV`f+`tbMHKY)5t(dx~SnDdkMW+QpW}PR7~A?TMR;cZe^KpXR!7E4eQdJQHdX<`Vr9 zk0dT6g(bBnMJ7e%MIVY;#n-+v{i@=tg`KfG`%5fK4(`J2;_VvR?Xdf3 zsdQ;h>DV6MJ?&-mvcj_0d!zPVEnik%vyZS(xNoGwr=oMe=Kfv#KUBt7-l=k~YOPkP z-cdbwfPG-_pyR=o8s(azn)ipehwj#T)V9}Y*Oec}9L_lWv_7=H_iM)2jSUJ7MGYU1 z@Q#ce4LsV@Xw}%*q|{W>3^xm#r;bG)yZMdlH=QkpEw!z*)}rI!xbXP1Z==5*I^lhy z`y}IJ%XeDeRku;v3frOf?DmPgz@Xmo#D^7KH*><&kZ}k0<(`u)y&d8oAIZHU3 ze|F(q&bit1spqFJ#9bKcj_Q7Jan;4!Jpn!am%J}sx$J)VVy{#0xhr;8PG7aTdg>bE zTE}(E>+O9OeQiHj{Lt2K+24M{>PF{H>ziEz%LmR5It*U8<$CM#ZLizc@2tEtFcdO$ zcQ|r*xkvZnNio#z9&IX9*nWZ zp8u5o(}(f=r{t&Q6RH!9lV+2rr`)G*K3n~4{CVp0`RRh6rGKt|q5I;yUmSnwn^`q8 z{*wQ4;n(6<@~@7(UiP|s)_?Z#o8&k1bA@l^-yVI(c-Q+r?ES=i<_GMDijR69yFPh; zdbp6hu<#rAg!B8%JG^WF000SaNLh0L01m_e01m_fl`9S#0000PbVXQnQ*UN;cVTj6 z06}DLVr3vnZDD6+Qe|Oed2z{QJOBUyO-V#SR9Hvt&&vq_APfZ2?Z4?5q7fA<73t;DzTElPZdnb+W-vX2=^0GVV0s4AyTEkxc3v0wl(p9E_klFChyj!; VN_%sSbR7Ty002ovPDHLkV1hy!X)pi) literal 0 HcmV?d00001 diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index ea408c392..a969a933a 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -7,6 +7,7 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeynote, + HidSubmenuIndexKeynoteVertical, HidSubmenuIndexKeyboard, HidSubmenuIndexMedia, HidSubmenuIndexTikTok, @@ -20,6 +21,11 @@ static void hid_submenu_callback(void* context, uint32_t index) { Hid* app = context; if(index == HidSubmenuIndexKeynote) { app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, false); + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); + } else if(index == HidSubmenuIndexKeynoteVertical) { + app->view_id = HidViewKeynote; + hid_keynote_set_orientation(app->hid_keynote, true); view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); } else if(index == HidSubmenuIndexKeyboard) { app->view_id = HidViewKeyboard; @@ -105,6 +111,12 @@ Hid* hid_alloc(HidTransport transport) { app->device_type_submenu = submenu_alloc(); submenu_add_item( app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, + "Keynote Vertical", + HidSubmenuIndexKeynoteVertical, + hid_submenu_callback, + app); submenu_add_item( app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); submenu_add_item( diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c index 5e5eeb790..543363bf6 100644 --- a/applications/external/hid_app/views/hid_keynote.c +++ b/applications/external/hid_app/views/hid_keynote.c @@ -111,6 +111,91 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) { elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); } +static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidKeynoteModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); + } else { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); + } + + canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); + + const uint8_t x_2 = 23; + const uint8_t x_1 = 2; + const uint8_t x_3 = 44; + + const uint8_t y_1 = 44; + const uint8_t y_2 = 65; + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop); + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom); + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + canvas_draw_icon(canvas, 2, 86, &I_Space_60x18); + if(model->ok_pressed) { + elements_slightly_rounded_box(canvas, 5, 88, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9); + elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space"); + canvas_set_color(canvas, ColorBlack); + + // Back + canvas_draw_icon(canvas, 2, 107, &I_Space_60x18); + if(model->back_pressed) { + elements_slightly_rounded_box(canvas, 5, 109, 55, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8); + elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back"); +} + static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { with_view_model( hid_keynote->view, @@ -212,3 +297,16 @@ void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { with_view_model( hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); } + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) { + furi_assert(hid_keynote); + + if(vertical) { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback); + view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip); + + } else { + view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); + view_set_orientation(hid_keynote->view, ViewOrientationHorizontal); + } +} diff --git a/applications/external/hid_app/views/hid_keynote.h b/applications/external/hid_app/views/hid_keynote.h index 4d4a0a9b1..84bfed4ce 100644 --- a/applications/external/hid_app/views/hid_keynote.h +++ b/applications/external/hid_app/views/hid_keynote.h @@ -12,3 +12,5 @@ void hid_keynote_free(HidKeynote* hid_keynote); View* hid_keynote_get_view(HidKeynote* hid_keynote); void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); + +void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical); From c10c45616dc69a4eb704d21b318562c129a37782 Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Wed, 28 Jun 2023 19:44:34 +0200 Subject: [PATCH 299/370] SLIX2 emulation support / practical use for Dymo printers (#2783) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve digital_signal for longer packets, also clean up code * added SLIX2 specific features like signature and unknown keys (for issue #2781), added WRITE_PASSWORD handling * fix NfcV AFI selection * when NFCV_CMD_READ_MULTI_BLOCK reads beyond memory end, return the maximum possible block's content * added SLIX2 reading * fix NXP SYSTEMINFO response check size * capture the first received password if none was set before * clear stored data before reading SLIX details renamed slix2_dump functions to slix2_read * display card block size values as decimal Co-authored-by: あく --- .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 161 ++++--- .../nfc/scenes/nfc_scene_nfcv_read_success.c | 6 +- lib/digital_signal/digital_signal.c | 179 +++---- lib/nfc/nfc_device.c | 360 +++++++------- lib/nfc/protocols/nfcv.c | 56 ++- lib/nfc/protocols/nfcv.h | 49 +- lib/nfc/protocols/slix.c | 454 ++++++++++++++++-- lib/nfc/protocols/slix.h | 46 +- 8 files changed, 905 insertions(+), 406 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index eb2f939c6..66a9174df 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -7,6 +7,83 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ } } +void nfc_scene_slix_build_string( + FuriString* temp_str, + NfcVData* nfcv_data, + SlixTypeFeatures features, + const char* type) { + furi_string_cat_printf(temp_str, "Type: %s\n", type); + furi_string_cat_printf(temp_str, "Keys:\n"); + if(features & SlixFeatureRead) { + furi_string_cat_printf( + temp_str, + " Read %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyRead) ? "" : " (unset)"); + } + if(features & SlixFeatureWrite) { + furi_string_cat_printf( + temp_str, + " Write %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyWrite) ? "" : " (unset)"); + } + if(features & SlixFeaturePrivacy) { + furi_string_cat_printf( + temp_str, + " Privacy %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyPrivacy) ? "" : " (unset)"); + furi_string_cat_printf( + temp_str, + " Privacy mode %s\n", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ENABLED" : "DISABLED"); + } + if(features & SlixFeatureDestroy) { + furi_string_cat_printf( + temp_str, + " Destroy %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyDestroy) ? "" : " (unset)"); + } + if(features & SlixFeatureEas) { + furi_string_cat_printf( + temp_str, + " EAS %08llX%s\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4), + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyEas) ? "" : " (unset)"); + } + if(features & SlixFeatureSignature) { + furi_string_cat_printf( + temp_str, + "Signature %08llX...\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.signature, 4)); + } + furi_string_cat_printf( + temp_str, + "DSFID: %02X %s\n", + nfcv_data->dsfid, + (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "AFI: %02X %s\n", + nfcv_data->afi, + (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "EAS: %s\n", + (nfcv_data->security_status[0] & NfcVLockBitEas) ? "locked" : "not locked"); + + if(features & SlixFeatureProtection) { + furi_string_cat_printf( + temp_str, + "PPL: %s\n", + (nfcv_data->security_status[0] & NfcVLockBitPpl) ? "locked" : "not locked"); + furi_string_cat_printf(temp_str, "Prot.ptr %02X\n", nfcv_data->sub_data.slix.pp_pointer); + furi_string_cat_printf(temp_str, "Prot.con %02X\n", nfcv_data->sub_data.slix.pp_condition); + } +} + void nfc_scene_nfc_data_info_on_enter(void* context) { Nfc* nfc = context; Widget* widget = nfc->widget; @@ -76,95 +153,25 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { } furi_string_cat_printf(temp_str, "\n"); - furi_string_cat_printf( - temp_str, - "DSFID: %02X %s\n", - nfcv_data->dsfid, - (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); - furi_string_cat_printf( - temp_str, - "AFI: %02X %s\n", - nfcv_data->afi, - (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); - furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); - furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); - furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + furi_string_cat_printf(temp_str, "IC Ref: %d\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %d\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %d\n", nfcv_data->block_size); switch(dev_data->nfcv_data.sub_type) { case NfcVTypePlain: furi_string_cat_printf(temp_str, "Type: Plain\n"); break; case NfcVTypeSlix: - furi_string_cat_printf(temp_str, "Type: SLIX\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix, "SLIX"); break; case NfcVTypeSlixS: - furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Read %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); - furi_string_cat_printf( - temp_str, - " Write %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixS, "SLIX-S"); break; case NfcVTypeSlixL: - furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixL, "SLIX-L"); break; case NfcVTypeSlix2: - furi_string_cat_printf(temp_str, "Type: SLIX2\n"); - furi_string_cat_printf(temp_str, "Keys:\n"); - furi_string_cat_printf( - temp_str, - " Read %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); - furi_string_cat_printf( - temp_str, - " Write %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); - furi_string_cat_printf( - temp_str, - " Privacy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); - furi_string_cat_printf( - temp_str, - " Destroy %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); - furi_string_cat_printf( - temp_str, - " EAS %08llX\n", - nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix2, "SLIX2"); break; default: furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c index bdf7692cc..04e60611d 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c @@ -16,7 +16,6 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) { Nfc* nfc = context; NfcDeviceData* dev_data = &nfc->dev->dev_data; FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; - NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; // Setup view Widget* widget = nfc->widget; widget_add_button_element( @@ -46,13 +45,12 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); break; } - furi_string_cat_printf(temp_str, "UID:"); + furi_string_cat_printf(temp_str, "UID:\n"); for(size_t i = 0; i < nfc_data->uid_len; i++) { furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); } furi_string_cat_printf(temp_str, "\n"); - furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); - furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + furi_string_cat_printf(temp_str, "(see More->Info for details)\n"); widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); furi_string_free(temp_str); diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 39aa9cbc6..25adb878b 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -51,8 +51,16 @@ struct DigitalSignalInternals { #define T_TIM 1562 /* 15.625 ns *100 */ #define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */ +/* end marker in DMA ringbuffer, will get written into timer register at the end */ +#define SEQ_TIMER_MAX 0xFFFFFFFF + +/* time to wait in loops before returning */ +#define SEQ_LOCK_WAIT_MS 10UL +#define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64) + /* maximum entry count of the sequence dma ring buffer */ -#define SEQUENCE_DMA_RINGBUFFER_SIZE 32 +#define RINGBUFFER_SIZE 128 + /* maximum number of DigitalSignals in a sequence */ #define SEQUENCE_SIGNALS_SIZE 32 /* @@ -252,7 +260,7 @@ static void digital_signal_setup_timer() { LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); LL_TIM_SetPrescaler(TIM2, 0); - LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF); + LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX); LL_TIM_SetCounter(TIM2, 0); } @@ -335,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) { sequence->bake = false; sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer)); - sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE; + sequence->dma_buffer->size = RINGBUFFER_SIZE; sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t)); sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; @@ -454,39 +462,23 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) { return ret; } -static void digital_sequence_update_pos(DigitalSequence* sequence) { - struct ReloadBuffer* dma_buffer = sequence->dma_buffer; - - dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); -} - -static const uint32_t wait_ms = 10; -static const uint32_t wait_ticks = wait_ms * 1000 * 64; - static void digital_sequence_finish(DigitalSequence* sequence) { struct ReloadBuffer* dma_buffer = sequence->dma_buffer; if(dma_buffer->dma_active) { uint32_t prev_timer = DWT->CYCCNT; - uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; do { - uint32_t last_pos = dma_buffer->read_pos; - - digital_sequence_update_pos(sequence); - - /* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */ - if(dma_buffer->read_pos == end_pos) { + /* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */ + if(TIM2->ARR == SEQ_TIMER_MAX) { break; } - - if(last_pos != dma_buffer->read_pos) { //-V547 - prev_timer = DWT->CYCCNT; - } - if(DWT->CYCCNT - prev_timer > wait_ticks) { + if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { + dma_buffer->read_pos = + RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); FURI_LOG_D( TAG, "[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)", - wait_ms, + SEQ_LOCK_WAIT_MS, TIM2->ARR, dma_buffer->read_pos, dma_buffer->write_pos); @@ -504,23 +496,30 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len if(dma_buffer->dma_active) { uint32_t prev_timer = DWT->CYCCNT; - uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; do { - uint32_t last_pos = dma_buffer->read_pos; - digital_sequence_update_pos(sequence); + dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2); - if(dma_buffer->read_pos != end_pos) { + uint32_t free = + (RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE; + + if(free > 2) { break; } - if(last_pos != dma_buffer->read_pos) { //-V547 - prev_timer = DWT->CYCCNT; - } - if(DWT->CYCCNT - prev_timer > wait_ticks) { + if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) { FURI_LOG_D( TAG, "[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)", - wait_ms, + SEQ_LOCK_WAIT_MS, + TIM2->ARR, + dma_buffer->read_pos, + dma_buffer->write_pos); + break; + } + if(TIM2->ARR == SEQ_TIMER_MAX) { + FURI_LOG_D( + TAG, + "[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)", TIM2->ARR, dma_buffer->read_pos, dma_buffer->write_pos); @@ -530,8 +529,9 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len } dma_buffer->buffer[dma_buffer->write_pos] = length; - dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size; - dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF; + dma_buffer->write_pos++; + dma_buffer->write_pos %= RINGBUFFER_SIZE; + dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX; } bool digital_sequence_send(DigitalSequence* sequence) { @@ -553,90 +553,97 @@ bool digital_sequence_send(DigitalSequence* sequence) { return true; } - int32_t remainder = 0; - bool traded_first = false; + if(!sequence->sequence_used) { + return false; + } - FURI_CRITICAL_ENTER(); + int32_t remainder = 0; + uint32_t trade_for_next = 0; + uint32_t seq_pos_next = 1; dma_buffer->dma_active = false; - dma_buffer->buffer[0] = 0xFFFFFFFF; + dma_buffer->buffer[0] = SEQ_TIMER_MAX; dma_buffer->read_pos = 0; dma_buffer->write_pos = 0; - for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) { - uint8_t signal_index = sequence->sequence[seq_pos]; - DigitalSignal* sig = sequence->signals[signal_index]; - bool last_signal = ((seq_pos + 1) == sequence->sequence_used); + /* already prepare the current signal pointer */ + DigitalSignal* sig = sequence->signals[sequence->sequence[0]]; + DigitalSignal* sig_next = NULL; + /* re-use the GPIO buffer from the first signal */ + sequence->gpio_buff = sig->internals->gpio_buff; - /* all signals are prepared and we can re-use the GPIO buffer from the fist signal */ - if(seq_pos == 0) { - sequence->gpio_buff = sig->internals->gpio_buff; + FURI_CRITICAL_ENTER(); + + while(sig) { + bool last_signal = (seq_pos_next >= sequence->sequence_used); + + if(!last_signal) { + sig_next = sequence->signals[sequence->sequence[seq_pos_next++]]; } for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) { - if(traded_first) { - traded_first = false; - continue; - } - uint32_t pulse_length = 0; - bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries); + bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries); + uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next; - pulse_length = sig->reload_reg_buff[pulse_pos]; + trade_for_next = 0; /* when we are too late more than half a tick, make the first edge temporarily longer */ if(remainder >= T_TIM_DIV2) { remainder -= T_TIM; pulse_length += 1; } - remainder += sig->internals->reload_reg_remainder; - /* last pulse in that signal and have a next signal? */ - if(last_pulse) { - if((seq_pos + 1) < sequence->sequence_used) { - DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]]; + /* last pulse in current signal and have a next signal? */ + if(last_pulse && sig_next) { + /* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse. + beware, we do not want the level after the last edge, but the last level before that edge */ + bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); - /* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */ - /* beware, we do not want the level after the last edge, but the last level before that edge */ - bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0); - - /* take from the next, add it to the current if they have the same level */ - if(end_level == sig_next->start_level) { - pulse_length += sig_next->reload_reg_buff[0]; - traded_first = true; - } + /* if they have the same level, pass the duration to the next pulse(s) */ + if(end_level == sig_next->start_level) { + trade_for_next = pulse_length; } } - digital_sequence_queue_pulse(sequence, pulse_length); + /* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */ + if(!trade_for_next) { + digital_sequence_queue_pulse(sequence, pulse_length); - /* start transmission when buffer was filled enough */ - bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4); + if(!dma_buffer->dma_active) { + /* start transmission when buffer was filled enough */ + bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2); - /* or it was the last pulse */ - if(last_pulse && last_signal) { - start_send = true; - } + /* or it was the last pulse */ + if(last_pulse && last_signal) { + start_send = true; + } - /* start transmission */ - if(start_send && !dma_buffer->dma_active) { - digital_sequence_setup_dma(sequence); - digital_signal_setup_timer(); + /* start transmission */ + if(start_send) { + digital_sequence_setup_dma(sequence); + digital_signal_setup_timer(); - /* if the send time is specified, wait till the core timer passed beyond that time */ - if(sequence->send_time_active) { - sequence->send_time_active = false; - while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + /* if the send time is specified, wait till the core timer passed beyond that time */ + if(sequence->send_time_active) { + sequence->send_time_active = false; + while(sequence->send_time - DWT->CYCCNT < 0x80000000) { + } + } + digital_signal_start_timer(); + dma_buffer->dma_active = true; } } - digital_signal_start_timer(); - dma_buffer->dma_active = true; } } + + remainder += sig->internals->reload_reg_remainder; + sig = sig_next; + sig_next = NULL; } /* wait until last dma transaction was finished */ - digital_sequence_finish(sequence); FURI_CRITICAL_EXIT(); + digital_sequence_finish(sequence); return true; } diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 952fca254..8abf637d7 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -657,178 +657,167 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } -static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { +static bool nfc_device_save_slix_data( + FlipperFormat* file, + NfcDevice* dev, + SlixTypeFeatures features, + const char* type) { bool saved = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; do { - if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + char msg[64]; + snprintf(msg, sizeof(msg), "%s specific data", type); + if(!flipper_format_write_comment_cstr(file, msg)) break; + if(!flipper_format_write_comment_cstr( + file, "Passwords are optional. If password is omitted, any password is accepted")) break; + + if(features & SlixFeatureRead) { + if(data->flags & NfcVSlixDataFlagsHasKeyRead) { + if(!flipper_format_write_hex( + file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + } + } + if(features & SlixFeatureWrite) { + if(data->flags & NfcVSlixDataFlagsHasKeyWrite) { + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + } + } + if(features & SlixFeaturePrivacy) { + if(data->flags & NfcVSlixDataFlagsHasKeyPrivacy) { + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + } + } + if(features & SlixFeatureDestroy) { + if(data->flags & NfcVSlixDataFlagsHasKeyDestroy) { + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + } + } + if(features & SlixFeatureEas) { + if(data->flags & NfcVSlixDataFlagsHasKeyEas) { + if(!flipper_format_write_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + } + } + if(features & SlixFeatureSignature) { + if(!flipper_format_write_comment_cstr( + file, + "This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key.")) + break; + if(!flipper_format_write_hex( + file, "Signature", data->signature, sizeof(data->signature))) + break; + } + if(features & SlixFeaturePrivacy) { + bool privacy = (data->flags & NfcVSlixDataFlagsPrivacy) ? true : false; + if(!flipper_format_write_bool(file, "Privacy Mode", &privacy, 1)) break; + } + if(features & SlixFeatureProtection) { + if(!flipper_format_write_comment_cstr(file, "Protection pointer configuration")) break; + if(!flipper_format_write_hex(file, "Protection pointer", &data->pp_pointer, 1)) break; + if(!flipper_format_write_hex(file, "Protection condition", &data->pp_condition, 1)) + break; + } saved = true; } while(false); return saved; } -bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { +bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev, SlixTypeFeatures features) { bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; memset(data, 0, sizeof(NfcVSlixData)); do { - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; - if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_write_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_read_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; - - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; - - parsed = true; - } while(false); - - return parsed; -} - -static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { - bool saved = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - - do { - if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; - if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_write_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_write_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_write_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; - saved = true; - } while(false); - - return saved; -} - -bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524 - bool parsed = false; - NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; - memset(data, 0, sizeof(NfcVSlixData)); - - do { - if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) - break; - if(!flipper_format_read_hex( - file, "Password Write", data->key_write, sizeof(data->key_write))) - break; - if(!flipper_format_read_hex( - file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) - break; - if(!flipper_format_read_hex( - file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) - break; - if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) - break; - if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + data->flags = 0; + if(features & SlixFeatureRead) { + if(flipper_format_key_exist(file, "Password Read")) { + if(!flipper_format_read_hex( + file, "Password Read", data->key_read, sizeof(data->key_read))) { + FURI_LOG_D(TAG, "Failed reading Password Read"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyRead; + } + } + if(features & SlixFeatureWrite) { + if(flipper_format_key_exist(file, "Password Write")) { + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) { + FURI_LOG_D(TAG, "Failed reading Password Write"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyWrite; + } + } + if(features & SlixFeaturePrivacy) { + if(flipper_format_key_exist(file, "Password Privacy")) { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) { + FURI_LOG_D(TAG, "Failed reading Password Privacy"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyPrivacy; + } + } + if(features & SlixFeatureDestroy) { + if(flipper_format_key_exist(file, "Password Destroy")) { + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) { + FURI_LOG_D(TAG, "Failed reading Password Destroy"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyDestroy; + } + } + if(features & SlixFeatureEas) { + if(flipper_format_key_exist(file, "Password EAS")) { + if(!flipper_format_read_hex( + file, "Password EAS", data->key_eas, sizeof(data->key_eas))) { + FURI_LOG_D(TAG, "Failed reading Password EAS"); + break; + } + data->flags |= NfcVSlixDataFlagsHasKeyEas; + } + } + if(features & SlixFeatureSignature) { + if(!flipper_format_read_hex( + file, "Signature", data->signature, sizeof(data->signature))) { + FURI_LOG_D(TAG, "Failed reading Signature"); + break; + } + } + if(features & SlixFeaturePrivacy) { + bool privacy; + if(!flipper_format_read_bool(file, "Privacy Mode", &privacy, 1)) { + FURI_LOG_D(TAG, "Failed reading Privacy Mode"); + break; + } + if(privacy) { + data->flags |= NfcVSlixDataFlagsPrivacy; + } + } + if(features & SlixFeatureProtection) { + if(!flipper_format_read_hex(file, "Protection pointer", &(data->pp_pointer), 1)) { + FURI_LOG_D(TAG, "Failed reading Protection pointer"); + break; + } + if(!flipper_format_read_hex(file, "Protection condition", &(data->pp_condition), 1)) { + FURI_LOG_D(TAG, "Failed reading Protection condition"); + break; + } + } parsed = true; } while(false); @@ -859,7 +848,8 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { file, "Data Content", data->data, data->block_num * data->block_size)) break; if(!flipper_format_write_comment_cstr( - file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) + file, + "First byte: DSFID (0x01) / AFI (0x02) / EAS (0x04) / PPL (0x08) lock info, others: block lock info")) break; if(!flipper_format_write_hex( file, "Security Status", data->security_status, 1 + data->block_num)) @@ -877,16 +867,16 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { saved = true; break; case NfcVTypeSlix: - saved = nfc_device_save_slix_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix, "SLIX"); break; case NfcVTypeSlixS: - saved = nfc_device_save_slix_s_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixS, "SLIX-S"); break; case NfcVTypeSlixL: - saved = nfc_device_save_slix_l_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixL, "SLIX-L"); break; case NfcVTypeSlix2: - saved = nfc_device_save_slix2_data(file, dev); + saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix2, "SLIX2"); break; default: break; @@ -906,23 +896,45 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { uint32_t temp_uint32 = 0; uint8_t temp_value = 0; - if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; - if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; - if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; - if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; - data->block_num = temp_uint32; - if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; - if(!flipper_format_read_hex( - file, "Data Content", data->data, data->block_num * data->block_size)) + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) { + FURI_LOG_D(TAG, "Failed reading DSFID"); break; + } + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) { + FURI_LOG_D(TAG, "Failed reading AFI"); + break; + } + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) { + FURI_LOG_D(TAG, "Failed reading IC Reference"); + break; + } + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) { + FURI_LOG_D(TAG, "Failed reading Block Count"); + break; + } + data->block_num = temp_uint32; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) { + FURI_LOG_D(TAG, "Failed reading Block Size"); + break; + } + if(!flipper_format_read_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) { + FURI_LOG_D(TAG, "Failed reading Data Content"); + break; + } /* optional, as added later */ if(flipper_format_key_exist(file, "Security Status")) { if(!flipper_format_read_hex( - file, "Security Status", data->security_status, 1 + data->block_num)) + file, "Security Status", data->security_status, 1 + data->block_num)) { + FURI_LOG_D(TAG, "Failed reading Security Status"); break; + } + } + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) { + FURI_LOG_D(TAG, "Failed reading Subtype"); + break; } - if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; data->sub_type = temp_value; switch(data->sub_type) { @@ -930,16 +942,16 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { parsed = true; break; case NfcVTypeSlix: - parsed = nfc_device_load_slix_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix); break; case NfcVTypeSlixS: - parsed = nfc_device_load_slix_s_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixS); break; case NfcVTypeSlixL: - parsed = nfc_device_load_slix_l_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixL); break; case NfcVTypeSlix2: - parsed = nfc_device_load_slix2_data(file, dev); + parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix2); break; default: break; diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 3c37153d8..017b06cae 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -149,12 +149,18 @@ bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* n return false; } + /* clear all know sub type data before reading them */ + memset(&nfcv_data->sub_data, 0x00, sizeof(nfcv_data->sub_data)); + if(slix_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX detected"); nfcv_data->sub_type = NfcVTypeSlix; } else if(slix2_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX2 detected"); nfcv_data->sub_type = NfcVTypeSlix2; + if(slix2_read_custom(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } } else if(slix_s_check_card_type(nfc_data)) { FURI_LOG_I(TAG, "NXP SLIX-S detected"); nfcv_data->sub_type = NfcVTypeSlixS; @@ -612,9 +618,34 @@ void nfcv_emu_handle_packet( if(ctx->flags & NFCV_REQ_FLAG_AFI) { uint8_t afi = nfcv_data->frame[ctx->payload_offset]; - if(afi == nfcv_data->afi) { - respond = true; + + uint8_t family = (afi & 0xF0); + uint8_t subfamily = (afi & 0x0F); + + if(family) { + if(subfamily) { + /* selected family and subfamily only */ + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + /* selected family, any subfamily */ + if(family == (nfcv_data->afi & 0xf0)) { + respond = true; + } + } + } else { + if(subfamily) { + /* proprietary subfamily only */ + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + /* all families and subfamilies */ + respond = true; + } } + } else { respond = true; } @@ -740,13 +771,19 @@ void nfcv_emu_handle_packet( case NFCV_CMD_READ_MULTI_BLOCK: case NFCV_CMD_READ_BLOCK: { uint8_t block = nfcv_data->frame[ctx->payload_offset]; - uint8_t blocks = 1; + int blocks = 1; if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; } - if(block + blocks <= nfcv_data->block_num) { + /* limit the maximum block count, underflow accepted */ + if(block + blocks > nfcv_data->block_num) { + blocks = nfcv_data->block_num - block; + } + + /* only respond with the valid blocks, if there are any */ + if(blocks > 0) { uint8_t buffer_pos = 0; ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; @@ -773,10 +810,13 @@ void nfcv_emu_handle_packet( ctx->response_flags, ctx->send_time); } else { - ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; - ctx->response_buffer[1] = NFCV_ERROR_GENERIC; - nfcv_emu_send( - tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + /* reply with an error only in addressed or selected mode */ + if(ctx->addressed || ctx->selected) { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } } snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h index 87a696737..e4139de99 100644 --- a/lib/nfc/protocols/nfcv.h +++ b/lib/nfc/protocols/nfcv.h @@ -139,8 +139,10 @@ typedef enum { } NfcVErrorcodes; typedef enum { - NfcVLockBitDsfid = 1, - NfcVLockBitAfi = 2, + NfcVLockBitDsfid = 1 << 0, + NfcVLockBitAfi = 1 << 1, + NfcVLockBitEas = 1 << 2, + NfcVLockBitPpl = 1 << 3, } NfcVLockBits; typedef enum { @@ -168,14 +170,55 @@ typedef enum { NfcVSendFlagsHighRate = 1 << 4 } NfcVSendFlags; +/* SLIX specific config flags */ +typedef enum { + NfcVSlixDataFlagsNone = 0, + NfcVSlixDataFlagsHasKeyRead = 1 << 0, + NfcVSlixDataFlagsHasKeyWrite = 1 << 1, + NfcVSlixDataFlagsHasKeyPrivacy = 1 << 2, + NfcVSlixDataFlagsHasKeyDestroy = 1 << 3, + NfcVSlixDataFlagsHasKeyEas = 1 << 4, + NfcVSlixDataFlagsValidKeyRead = 1 << 8, + NfcVSlixDataFlagsValidKeyWrite = 1 << 9, + NfcVSlixDataFlagsValidKeyPrivacy = 1 << 10, + NfcVSlixDataFlagsValidKeyDestroy = 1 << 11, + NfcVSlixDataFlagsValidKeyEas = 1 << 12, + NfcVSlixDataFlagsPrivacy = 1 << 16, + NfcVSlixDataFlagsDestroyed = 1 << 17 +} NfcVSlixDataFlags; + +/* abstract the file read/write operations for all SLIX types to reduce duplicated code */ +typedef enum { + SlixFeatureRead = 1 << 0, + SlixFeatureWrite = 1 << 1, + SlixFeaturePrivacy = 1 << 2, + SlixFeatureDestroy = 1 << 3, + SlixFeatureEas = 1 << 4, + SlixFeatureSignature = 1 << 5, + SlixFeatureProtection = 1 << 6, + + SlixFeatureSlix = SlixFeatureEas, + SlixFeatureSlixS = + (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy | + SlixFeatureEas), + SlixFeatureSlixL = (SlixFeaturePrivacy | SlixFeatureDestroy | SlixFeatureEas), + SlixFeatureSlix2 = + (SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy | + SlixFeatureEas | SlixFeatureSignature | SlixFeatureProtection), +} SlixTypeFeatures; + typedef struct { + uint32_t flags; uint8_t key_read[4]; uint8_t key_write[4]; uint8_t key_privacy[4]; uint8_t key_destroy[4]; uint8_t key_eas[4]; uint8_t rand[2]; - bool privacy; + uint8_t signature[32]; + /* SLIX2 options */ + uint8_t pp_pointer; + uint8_t pp_condition; } NfcVSlixData; typedef union { diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index 1c14c0bf9..68937d161 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -9,6 +9,120 @@ #define TAG "SLIX" +ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + uint8_t rxBuf[32]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read NXP SYSTEM INFORMATION..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + uint8_t cmd[] = {}; + uint8_t uid[NFCV_UID_LENGTH]; + + /* UID is stored reversed in requests */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + uid, + cmd, + sizeof(cmd), + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret != ERR_NONE || received != 8) { //-V560 + FURI_LOG_D(TAG, "Failed: %d, %d", ret, received); + return ret; + } + FURI_LOG_D(TAG, "Success..."); + + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + slix->pp_pointer = rxBuf[1]; + slix->pp_condition = rxBuf[2]; + + /* convert NXP's to our internal lock bits format */ + nfcv_data->security_status[0] = 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitDsfid) ? NfcVLockBitDsfid : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitAfi) ? NfcVLockBitAfi : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitEas) ? NfcVLockBitEas : 0; + nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitPpl) ? NfcVLockBitPpl : 0; + + return ERR_NONE; +} + +ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + uint8_t rxBuf[64]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read SIGNATURE..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + uint8_t cmd[] = {}; + uint8_t uid[NFCV_UID_LENGTH]; + + /* UID is stored reversed in requests */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_READ_SIGNATURE, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + uid, + cmd, + sizeof(cmd), + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret != ERR_NONE || received != 33) { //-V560 + FURI_LOG_D(TAG, "Failed: %d, %d", ret, received); + return ret; + } + FURI_LOG_D(TAG, "Success..."); + + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + memcpy(slix->signature, &rxBuf[1], 32); + + return ERR_NONE; +} + +ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + ReturnCode ret = ERR_NONE; + + ret = slix2_read_nxp_sysinfo(nfc_data, nfcv_data); + if(ret != ERR_NONE) { + return ret; + } + ret = slix2_read_signature(nfc_data, nfcv_data); + + return ret; +} + static uint32_t slix_read_be(uint8_t* data, uint32_t length) { uint32_t value = 0; @@ -137,6 +251,43 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { return ret; } +static void slix_generic_pass_infos( + uint8_t password_id, + NfcVSlixData* slix, + uint8_t** password, + uint32_t* flag_valid, + uint32_t* flag_set) { + switch(password_id) { + case SLIX_PASS_READ: + *password = slix->key_read; + *flag_valid = NfcVSlixDataFlagsValidKeyRead; + *flag_set = NfcVSlixDataFlagsHasKeyRead; + break; + case SLIX_PASS_WRITE: + *password = slix->key_write; + *flag_valid = NfcVSlixDataFlagsValidKeyWrite; + *flag_set = NfcVSlixDataFlagsHasKeyWrite; + break; + case SLIX_PASS_PRIVACY: + *password = slix->key_privacy; + *flag_valid = NfcVSlixDataFlagsValidKeyPrivacy; + *flag_set = NfcVSlixDataFlagsHasKeyPrivacy; + break; + case SLIX_PASS_DESTROY: + *password = slix->key_destroy; + *flag_valid = NfcVSlixDataFlagsValidKeyDestroy; + *flag_set = NfcVSlixDataFlagsHasKeyDestroy; + break; + case SLIX_PASS_EASAFI: + *password = slix->key_eas; + *flag_valid = NfcVSlixDataFlagsValidKeyEas; + *flag_set = NfcVSlixDataFlagsHasKeyEas; + break; + default: + break; + } +} + bool slix_generic_protocol_filter( FuriHalNfcTxRxContext* tx_rx, FuriHalNfcDevData* nfc_data, @@ -150,7 +301,8 @@ bool slix_generic_protocol_filter( NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; NfcVSlixData* slix = &nfcv_data->sub_data.slix; - if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && + if((slix->flags & NfcVSlixDataFlagsPrivacy) && + ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { snprintf( nfcv_data->last_command, @@ -186,66 +338,73 @@ bool slix_generic_protocol_filter( } case NFCV_CMD_NXP_SET_PASSWORD: { + /* the password to be set is the first parameter */ uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + /* right after that is the XORed password */ + uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + /* only handle if the password type is supported */ if(!(password_id & password_supported)) { break; } - uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + /* fetch the last RAND value */ uint8_t* rand = slix->rand; - uint8_t* password = NULL; + + /* first calc the password that has been sent */ uint8_t password_rcv[4]; - - switch(password_id) { - case SLIX_PASS_READ: - password = slix->key_read; - break; - case SLIX_PASS_WRITE: - password = slix->key_write; - break; - case SLIX_PASS_PRIVACY: - password = slix->key_privacy; - break; - case SLIX_PASS_DESTROY: - password = slix->key_destroy; - break; - case SLIX_PASS_EASAFI: - password = slix->key_eas; - break; - default: - break; + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; } + uint32_t pass_received = slix_read_be(password_rcv, 4); + /* then determine the password type (or even update if not set yet) */ + uint8_t* password = NULL; + uint32_t flag_valid = 0; + uint32_t flag_set = 0; + + slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set); + + /* when the password is not supported, return silently */ if(!password) { break; } - for(int pos = 0; pos < 4; pos++) { - password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; - } - uint32_t pass_expect = slix_read_be(password, 4); - uint32_t pass_received = slix_read_be(password_rcv, 4); + /* check if the password is known */ + bool pass_valid = false; + uint32_t pass_expect = 0; - /* if the password is all-zeroes, just accept any password*/ - if(!pass_expect || pass_expect == pass_received) { + if(slix->flags & flag_set) { + /* if so, fetch the stored password and compare */ + pass_expect = slix_read_be(password, 4); + pass_valid = (pass_expect == pass_received); + } else { + /* if not known, just accept it and store that password */ + memcpy(password, password_rcv, 4); + nfcv_data->modified = true; + slix->flags |= flag_set; + + pass_valid = true; + } + + /* if the pass was valid or accepted for other reasons, continue */ + if(pass_valid) { + slix->flags |= flag_valid; + + /* handle actions when a correct password was given, aside of setting the flag */ switch(password_id) { - case SLIX_PASS_READ: - break; - case SLIX_PASS_WRITE: - break; case SLIX_PASS_PRIVACY: - slix->privacy = false; + slix->flags &= ~NfcVSlixDataFlagsPrivacy; nfcv_data->modified = true; break; case SLIX_PASS_DESTROY: + slix->flags |= NfcVSlixDataFlagsDestroyed; FURI_LOG_D(TAG, "Pooof! Got destroyed"); break; - case SLIX_PASS_EASAFI: - break; default: break; } + ctx->response_buffer[0] = NFCV_NOERROR; nfcv_emu_send( tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); @@ -268,6 +427,49 @@ bool slix_generic_protocol_filter( break; } + case NFCV_CMD_NXP_WRITE_PASSWORD: { + uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + + if(!(password_id & password_supported)) { + break; + } + + uint8_t* new_password = &nfcv_data->frame[ctx->payload_offset + 1]; + uint8_t* password = NULL; + uint32_t flag_valid = 0; + uint32_t flag_set = 0; + + slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set); + + /* when the password is not supported, return silently */ + if(!password) { + break; + } + + bool pass_valid = (slix->flags & flag_valid); + if(!(slix->flags & flag_set)) { + pass_valid = true; + } + + if(pass_valid) { + slix->flags |= flag_valid; + slix->flags |= flag_set; + + memcpy(password, new_password, 4); + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD OK"); + } else { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD FAIL"); + } + handled = true; + break; + } + case NFCV_CMD_NXP_ENABLE_PRIVACY: { ctx->response_buffer[0] = NFCV_NOERROR; @@ -278,7 +480,7 @@ bool slix_generic_protocol_filter( sizeof(nfcv_data->last_command), "NFCV_CMD_NXP_ENABLE_PRIVACY"); - slix->privacy = true; + slix->flags |= NfcVSlixDataFlagsPrivacy; handled = true; break; } @@ -315,7 +517,10 @@ void slix_l_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_l_protocol_filter; @@ -345,7 +550,10 @@ void slix_s_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_s_protocol_filter; @@ -375,7 +583,10 @@ void slix_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix_protocol_filter; @@ -389,6 +600,10 @@ bool slix2_protocol_filter( // -V524 furi_assert(nfc_data); furi_assert(nfcv_data_in); + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + bool handled = false; /* many SLIX share some of the functions, place that in a generic handler */ @@ -396,6 +611,160 @@ bool slix2_protocol_filter( // -V524 return true; } + switch(ctx->command) { + /* override WRITE BLOCK for block 79 (16 bit counter) */ + case NFCV_CMD_WRITE_BLOCK: + case NFCV_CMD_WRITE_MULTI_BLOCK: { + uint8_t resp_len = 1; + uint8_t blocks = 1; + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t data_pos = ctx->payload_offset + 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[data_pos] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[data_pos]; + uint32_t data_len = nfcv_data->block_size * blocks; + + if((block + blocks) <= nfcv_data->block_num && + (data_pos + data_len + 2) == nfcv_data->frame_length) { + ctx->response_buffer[0] = NFCV_NOERROR; + + for(int block_num = block; block_num < block + blocks; block_num++) { + /* special case, 16-bit counter */ + if(block_num == 79) { + uint32_t dest; + uint32_t ctr_old; + + memcpy(&dest, &nfcv_data->frame[data_pos], 4); + memcpy(&ctr_old, &nfcv_data->data[nfcv_data->block_size * block_num], 4); + + uint32_t ctr_new = ctr_old; + bool allowed = true; + + /* increment counter */ + if(dest == 1) { + ctr_new = (ctr_old & 0xFFFF0000) | ((ctr_old + 1) & 0xFFFF); + + /* protection flag set? */ + if(ctr_old & 0x01000000) { //-V1051 + allowed = nfcv_data->sub_data.slix.flags & + NfcVSlixDataFlagsValidKeyRead; + } + } else { + ctr_new = dest; + allowed = nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsValidKeyWrite; + } + + if(allowed) { + memcpy( //-V1086 + &nfcv_data->data[nfcv_data->block_size * block_num], + &ctr_new, + 4); + } else { + /* incorrect read or write password */ + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + resp_len = 2; + } + } else { + memcpy( + &nfcv_data->data[nfcv_data->block_size * block_num], + &nfcv_data->frame[data_pos], + nfcv_data->block_size); + } + data_pos += nfcv_data->block_size; + } + nfcv_data->modified = true; + + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + resp_len = 2; + } + + bool respond = (ctx->response_buffer[0] == NFCV_NOERROR) || + (ctx->addressed || ctx->selected); + + if(respond) { + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + resp_len, + ctx->response_flags, + ctx->send_time); + } + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE MULTI BLOCK %d, %d blocks", + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE BLOCK %d <- %02X %02X %02X %02X", + block, + data[0], + data[1], + data[2], + data[3]); + } + handled = true; + break; + } + + case NFCV_CMD_NXP_READ_SIGNATURE: { + uint32_t len = 0; + ctx->response_buffer[len++] = NFCV_NOERROR; + memcpy(&ctx->response_buffer[len], slix->signature, sizeof(slix->signature)); + len += sizeof(slix->signature); + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_SIGNATURE"); + + handled = true; + break; + } + + case NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION: { + uint32_t len = 0; + uint8_t lock_bits = 0; + + /* convert our internal lock bits format into NXP's */ + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? SlixLockBitDsfid : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitAfi) ? SlixLockBitAfi : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitEas) ? SlixLockBitEas : 0; + lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitPpl) ? SlixLockBitPpl : 0; + + ctx->response_buffer[len++] = NFCV_NOERROR; + ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_pointer; + ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_condition; + ctx->response_buffer[len++] = lock_bits; + ctx->response_buffer[len++] = 0x7F; /* features LSB */ + ctx->response_buffer[len++] = 0x35; /* features */ + ctx->response_buffer[len++] = 0; /* features */ + ctx->response_buffer[len++] = 0; /* features MSB */ + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "GET_NXP_SYSTEM_INFORMATION"); + + handled = true; + break; + } + } + return handled; } @@ -405,7 +774,10 @@ void slix2_prepare(NfcVData* nfcv_data) { FURI_LOG_D( TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); - FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + FURI_LOG_D( + TAG, + " Privacy mode: %s", + (nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF"); NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; ctx->emu_protocol_filter = &slix2_protocol_filter; diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h index 701fa2f82..67f09e46d 100644 --- a/lib/nfc/protocols/slix.h +++ b/lib/nfc/protocols/slix.h @@ -8,19 +8,35 @@ #define NFCV_MANUFACTURER_NXP 0x04 /* ISO15693-3 CUSTOM NXP COMMANDS */ -#define NFCV_CMD_NXP_SET_EAS 0xA2 -#define NFCV_CMD_NXP_RESET_EAS 0xA3 -#define NFCV_CMD_NXP_LOCK_EAS 0xA4 -#define NFCV_CMD_NXP_EAS_ALARM 0xA5 -#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 -#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 -#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 -#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 -#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 -#define NFCV_CMD_NXP_SET_PASSWORD 0xB3 -#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 -#define NFCV_CMD_NXP_DESTROY 0xB9 -#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA +typedef enum { + NFCV_CMD_NXP_SET_EAS = 0xA2, + NFCV_CMD_NXP_RESET_EAS = 0xA3, + NFCV_CMD_NXP_LOCK_EAS = 0xA4, + NFCV_CMD_NXP_EAS_ALARM = 0xA5, + NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI = 0xA6, + NFCV_CMD_NXP_WRITE_EAS_ID = 0xA7, + NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION = 0xAB, + NFCV_CMD_NXP_INVENTORY_PAGE_READ = 0xB0, + NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST = 0xB1, + NFCV_CMD_NXP_GET_RANDOM_NUMBER = 0xB2, + NFCV_CMD_NXP_SET_PASSWORD = 0xB3, + NFCV_CMD_NXP_WRITE_PASSWORD = 0xB4, + NFCV_CMD_NXP_64_BIT_PASSWORD_PROTECTION = 0xB5, + NFCV_CMD_NXP_PROTECT_PAGE = 0xB6, + NFCV_CMD_NXP_LOCK_PAGE_PROTECTION_CONDITION = 0xB7, + NFCV_CMD_NXP_DESTROY = 0xB9, + NFCV_CMD_NXP_ENABLE_PRIVACY = 0xBA, + NFCV_CMD_NXP_STAY_QUIET_PERSISTENT = 0xBC, + NFCV_CMD_NXP_READ_SIGNATURE = 0xBD +} SlixCommands; + +/* lock bit bits used in SLIX's NXP SYSTEM INFORMATION response */ +typedef enum { + SlixLockBitAfi = 1 << 0, + SlixLockBitEas = 1 << 1, + SlixLockBitDsfid = 1 << 2, + SlixLockBitPpl = 1 << 3, +} SlixLockBits; /* available passwords */ #define SLIX_PASS_READ 0x01 @@ -37,6 +53,10 @@ bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); +ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); + ReturnCode slix_get_random(NfcVData* data); ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); From 8f37a6a79dcf27caf6510f2e64acf406e864a96c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 02:03:32 +0200 Subject: [PATCH 300/370] Asset packs in FAPs + cleanup unused icons --- .../debug/battery_test_app/application.fam | 1 - .../external/.mass_storage/application.fam | 1 - .../external/bpmtapper/application.fam | 1 - applications/external/bpmtapper/bpm.c | 2 +- .../bpmtapper/icons/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes applications/external/brainfuck/brainfuck_i.h | 1 + .../brainfuck/icons/ButtonRightSmall_3x5.png | Bin 1738 -> 0 bytes .../brainfuck/icons/KeySaveSelected_24x11.png | Bin 1853 -> 0 bytes .../brainfuck/icons/KeySave_24x11.png | Bin 1863 -> 0 bytes applications/external/counter/application.fam | 3 +- applications/external/counter/counter.c | 1 - .../counter/{icons => }/counter_icon.png | Bin applications/external/dap_link/dap_link.c | 3 +- .../dap_link/icons/ActiveConnection_50x64.png | Bin 3842 -> 0 bytes .../esp32cam_morseflasher/application.fam | 1 - .../assets/KeyBackspaceSelected_16x9.png | Bin 1812 -> 0 bytes .../assets/KeyBackspace_16x9.png | Bin 1829 -> 0 bytes .../assets/KeySaveSelected_24x11.png | Bin 1853 -> 0 bytes .../assets/KeySave_24x11.png | Bin 1863 -> 0 bytes .../assets/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../esp32cam_morseflasher/uart_text_input.c | 2 +- .../external/etch_a_sketch/application.fam | 1 - .../external/flipbip/icons/Auth_62x31.png | Bin 3761 -> 0 bytes .../flipbip/icons/ButtonCenter_7x7.png | Bin 1440 -> 0 bytes .../flipbip/icons/ButtonLeftSmall_3x5.png | Bin 1741 -> 0 bytes .../external/flipbip/icons/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../flipbip/icons/ButtonRightSmall_3x5.png | Bin 1738 -> 0 bytes .../flipbip/icons/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../external/flipbip/icons/Keychain_39x36.png | Bin 3775 -> 0 bytes .../external/flipbip/views/flipbip_scene_1.c | 1 + .../flipbip/views/flipbip_startscreen.c | 1 + .../images/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../images/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../images/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../flipper_i2ctools/images/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../flipper_i2ctools/images/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../flipper_i2ctools/views/main_view.h | 3 +- .../flipper_i2ctools/views/scanner_view.h | 3 +- .../flipper_i2ctools/views/sender_view.h | 3 +- .../flipper_i2ctools/views/sniffer_view.h | 3 +- .../external/gpioreader_b/application.fam | 1 - applications/external/hex_editor/hex_editor.c | 4 +- .../hex_editor/icons/Pin_arrow_up_7x9.png | Bin 3603 -> 0 bytes .../hid_app/assets/Ble_connected_15x15.png | Bin 3634 -> 0 bytes .../hid_app/assets/Ble_disconnected_15x15.png | Bin 657 -> 0 bytes .../hid_app/assets/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../hid_app/assets/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../hid_app/assets/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../external/hid_app/assets/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../external/hid_app/assets/Button_18x18.png | Bin 3609 -> 0 bytes .../external/hid_app/assets/Circles_47x47.png | Bin 3712 -> 0 bytes .../hid_app/assets/Left_mouse_icon_9x9.png | Bin 3622 -> 0 bytes .../external/hid_app/assets/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../hid_app/assets/Ok_btn_pressed_13x13.png | Bin 3625 -> 0 bytes .../hid_app/assets/Pin_arrow_up_7x9.png | Bin 3603 -> 0 bytes .../hid_app/assets/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../hid_app/assets/Pressed_Button_13x13.png | Bin 3606 -> 0 bytes .../hid_app/assets/Right_mouse_icon_9x9.png | Bin 3622 -> 0 bytes .../external/hid_app/assets/Space_65x18.png | Bin 3619 -> 0 bytes .../external/hid_app/assets/Voldwn_6x6.png | Bin 4556 -> 0 bytes .../external/hid_app/assets/Volup_8x6.png | Bin 4564 -> 0 bytes applications/external/hid_app/hid.h | 3 + .../external/hid_app/views/hid_keyboard.c | 1 - .../external/hid_app/views/hid_keynote.c | 2 - .../external/hid_app/views/hid_media.c | 2 - .../external/hid_app/views/hid_mouse.c | 2 - .../hid_app/views/hid_mouse_clicker.c | 2 - .../hid_app/views/hid_mouse_jiggler.c | 2 - .../external/hid_app/views/hid_numpad.c | 1 - .../external/hid_app/views/hid_tikshorts.c | 2 - .../external/ir_remote/application.fam | 1 - .../ir_remote/images/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../ir_remote/images/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../ir_remote/images/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../ir_remote/images/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../external/ir_remote/images/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../external/ir_remote/images/back_10px.png | Bin 154 -> 0 bytes .../external/ir_remote/images/sub1_10px.png | Bin 299 -> 0 bytes .../external/ir_remote/infrared_remote_app.c | 2 +- .../external/metronome/application.fam | 1 - .../external/metronome/gui_extensions.c | 2 +- .../metronome/images/ButtonUp_7x4.png | Bin 102 -> 0 bytes applications/external/mfkey32/application.fam | 2 +- applications/external/mfkey32/mfkey.png | Bin 9060 -> 0 bytes .../external/mifare_fuzzer/application.fam | 3 +- .../{images => }/mifare_fuzzer_10px.png | Bin .../external/mousejacker/application.fam | 1 - .../mousejacker/images/badusb_10px.png | Bin 576 -> 0 bytes .../external/mousejacker/images/sub1_10px.png | Bin 299 -> 0 bytes .../external/mousejacker/mousejacker.c | 4 +- .../external/multi_fuzzer/application.fam | 2 +- applications/external/multi_fuzzer/fuzzer_i.h | 3 +- .../multi_fuzzer/{icons => }/ibutt_10px.png | Bin .../external/multi_fuzzer/icons/125_10px.png | Bin 308 -> 0 bytes .../multi_fuzzer/icons/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../multi_fuzzer/icons/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../multi_fuzzer/icons/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../multi_fuzzer/icons/Pin_arrow_up_7x9.png | Bin 3603 -> 0 bytes .../icons/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../external/music_beeper/application.fam | 1 - .../external/music_beeper/music_beeper.c | 2 +- .../external/music_player/application.fam | 3 +- .../icons => music_player}/music_10px.png | Bin .../external/music_player/music_player.c | 2 +- .../nfc_magic/assets/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes .../nfc_magic/assets/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../nfc_magic/assets/NFC_manual_60x50.png | Bin 3804 -> 0 bytes applications/external/nfc_magic/nfc_magic_i.h | 1 + applications/external/ocarina/application.fam | 3 +- .../external/ocarina/icons/music_10px.png | Bin 142 -> 0 bytes .../icons => ocarina}/music_10px.png | Bin .../external/passgen/icons/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../passgen/icons/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes applications/external/passgen/passgen.c | 1 + .../external/picopass/application.fam | 1 - .../picopass/icons/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes .../picopass/icons/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../external/picopass/icons/Nfc_10px.png | Bin 304 -> 0 bytes .../icons/RFIDDolphinReceive_97x61.png | Bin 1421 -> 0 bytes .../picopass/icons/RFIDDolphinSend_97x61.png | Bin 1418 -> 0 bytes .../external/picopass/picopass_device.c | 2 +- applications/external/picopass/picopass_i.h | 2 +- .../pocsag_pager/images/Fishing_123x52.png | Bin 966 -> 0 bytes .../external/pocsag_pager/images/Lock_7x8.png | Bin 3597 -> 0 bytes .../images/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../pocsag_pager/images/Quest_7x8.png | Bin 3675 -> 0 bytes .../pocsag_pager/images/Scanning_123x52.png | Bin 1690 -> 0 bytes .../pocsag_pager/images/Unlock_7x8.png | Bin 3598 -> 0 bytes .../images/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../views/pocsag_pager_receiver.c | 1 + .../views/pocsag_pager_receiver_info.c | 1 + applications/external/sam/application.fam | 4 - .../external/sam/icons/music_10px.png | Bin 142 -> 0 bytes .../external/signal_generator/application.fam | 1 - .../icons/SmallArrowDown_3x5.png | Bin 3592 -> 0 bytes .../icons/SmallArrowUp_3x5.png | Bin 7976 -> 0 bytes .../signal_generator/views/signal_gen_pwm.c | 2 +- .../simonsays/images/Connected_62x31.png | Bin 3765 -> 0 bytes .../simonsays/images/Cry_dolph_55x52.png | Bin 3898 -> 0 bytes .../simonsays/images/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes .../simonsays/images/DolphinWait_61x59.png | Bin 2023 -> 0 bytes .../simonsays/images/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes applications/external/simonsays/simon_says.c | 1 + .../images/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes .../images/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../images/SDQuestion_35x43.png | Bin 1950 -> 0 bytes .../external/spi_mem_manager/spi_mem_app_i.h | 1 + .../subghz_bruteforcer/application.fam | 3 +- .../images/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../images/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../images/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../images/Sub1ghz_14/frame_01.png | Bin 97 -> 0 bytes .../images/Sub1ghz_14/frame_02.png | Bin 96 -> 0 bytes .../images/Sub1ghz_14/frame_03.png | Bin 90 -> 0 bytes .../images/Sub1ghz_14/frame_04.png | Bin 76 -> 0 bytes .../images/Sub1ghz_14/frame_05.png | Bin 79 -> 0 bytes .../images/Sub1ghz_14/frame_06.png | Bin 90 -> 0 bytes .../images/Sub1ghz_14/frame_rate | 1 - .../subghz_bruteforcer/images/sub1_10px.png | Bin 299 -> 0 bytes .../{images => }/subbrute_10px.png | Bin .../external/subghz_bruteforcer/subbrute_i.h | 2 +- .../external/subghz_playlist/application.fam | 1 - .../images/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../subghz_playlist/images/sub1_10px.png | Bin 299 -> 0 bytes .../external/subghz_playlist/playlist.c | 2 +- .../external/subghz_remote/application.fam | 1 - .../application.fam | 3 +- .../icons/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../icons/remote_scene/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../icons/remote_scene/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../icons/remote_scene/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../icons/remote_scene/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../icons/remote_scene/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../icons/remote_scene/Pin_arrow_up_7x9.png | Bin 3603 -> 0 bytes .../icons/remote_scene/Pin_cell_13x13.png | Bin 3593 -> 0 bytes .../icons/remote_scene/Pin_star_7x7.png | Bin 3600 -> 0 bytes .../icons/remote_scene/back_10px.png | Bin 154 -> 0 bytes .../icons/sub1_10px.png | Bin 299 -> 0 bytes .../subghz_remote_app_i.h | 2 +- .../{icons => }/subrem_10px.png | Bin .../swd_probe/icons/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../external/swd_probe/icons/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../external/swd_probe/swd_probe_app.c | 1 + .../timelapse/icons/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../timelapse/icons/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../timelapse/icons/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../external/timelapse/icons/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../external/timelapse/icons/Pin_star_7x7.png | Bin 3600 -> 0 bytes .../external/timelapse/icons/loading_10px.png | Bin 4349 -> 0 bytes applications/external/timelapse/zeitraffer.c | 1 + .../totp/images/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes .../scenes/app_settings/totp_app_settings.c | 1 + .../authenticate/totp_scene_authenticate.c | 1 + .../totp_scene_generate_token.c | 1 + .../external/totp/ui/scenes/standby/standby.c | 3 +- applications/external/totp/ui/ui_controls.c | 1 + .../external/uart_terminal/application.fam | 1 - .../assets/KeyBackspaceSelected_16x9.png | Bin 1812 -> 0 bytes .../assets/KeyBackspace_16x9.png | Bin 1829 -> 0 bytes .../assets/KeySaveSelected_24x11.png | Bin 1853 -> 0 bytes .../uart_terminal/assets/KeySave_24x11.png | Bin 1863 -> 0 bytes .../assets/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../external/uart_terminal/uart_text_input.c | 2 +- applications/external/unitemp/application.fam | 1 - .../external/wav_player/application.fam | 1 - .../external/wav_player/images/music_10px.png | Bin 142 -> 0 bytes applications/external/wav_player/wav_player.c | 2 +- .../weather_station/images/Fishing_123x52.png | Bin 966 -> 0 bytes .../weather_station/images/Lock_7x8.png | Bin 3597 -> 0 bytes .../images/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../weather_station/images/Quest_7x8.png | Bin 3675 -> 0 bytes .../images/Scanning_123x52.png | Bin 1690 -> 0 bytes .../weather_station/images/Unlock_7x8.png | Bin 3598 -> 0 bytes .../images/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../views/weather_station_receiver.c | 1 + .../views/weather_station_receiver_info.c | 1 + .../assets/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes .../assets/KeyBackspaceSelected_16x9.png | Bin 1812 -> 0 bytes .../assets/KeyBackspace_16x9.png | Bin 1829 -> 0 bytes .../assets/KeyKeyboardSelected_10x11.png | Bin 7210 -> 0 bytes .../assets/KeyKeyboard_10x11.png | Bin 7763 -> 0 bytes .../assets/KeySaveSelected_24x11.png | Bin 1853 -> 0 bytes .../assets/KeySave_24x11.png | Bin 1863 -> 0 bytes .../assets/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../wifi_marauder_app_i.h | 1 + applications/external/yatzee/application.fam | 2 +- .../yatzee/{images => }/yatzee_icon_10px.png | Bin applications/main/bad_kb/application.fam | 1 - applications/main/gpio/application.fam | 1 - applications/main/ibutton/application.fam | 1 - applications/main/infrared/application.fam | 1 - applications/main/lfrfid/application.fam | 1 - applications/main/u2f/application.fam | 1 - applications/main/xtreme_app/application.fam | 1 - assets/SConscript | 3 +- firmware/targets/f7/api_symbols.csv | 202 ++++++++++++++++++ lib/SConscript | 2 + 237 files changed, 268 insertions(+), 85 deletions(-) delete mode 100644 applications/external/bpmtapper/icons/DolphinCommon_56x48.png delete mode 100644 applications/external/brainfuck/icons/ButtonRightSmall_3x5.png delete mode 100644 applications/external/brainfuck/icons/KeySaveSelected_24x11.png delete mode 100644 applications/external/brainfuck/icons/KeySave_24x11.png rename applications/external/counter/{icons => }/counter_icon.png (100%) delete mode 100644 applications/external/dap_link/icons/ActiveConnection_50x64.png delete mode 100644 applications/external/esp32cam_morseflasher/assets/KeyBackspaceSelected_16x9.png delete mode 100644 applications/external/esp32cam_morseflasher/assets/KeyBackspace_16x9.png delete mode 100644 applications/external/esp32cam_morseflasher/assets/KeySaveSelected_24x11.png delete mode 100644 applications/external/esp32cam_morseflasher/assets/KeySave_24x11.png delete mode 100644 applications/external/esp32cam_morseflasher/assets/WarningDolphin_45x42.png delete mode 100644 applications/external/flipbip/icons/Auth_62x31.png delete mode 100644 applications/external/flipbip/icons/ButtonCenter_7x7.png delete mode 100644 applications/external/flipbip/icons/ButtonLeftSmall_3x5.png delete mode 100644 applications/external/flipbip/icons/ButtonLeft_4x7.png delete mode 100644 applications/external/flipbip/icons/ButtonRightSmall_3x5.png delete mode 100644 applications/external/flipbip/icons/ButtonRight_4x7.png delete mode 100644 applications/external/flipbip/icons/Keychain_39x36.png delete mode 100644 applications/external/flipper_i2ctools/images/ButtonDown_7x4.png delete mode 100644 applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png delete mode 100644 applications/external/flipper_i2ctools/images/ButtonRight_4x7.png delete mode 100644 applications/external/flipper_i2ctools/images/ButtonUp_7x4.png delete mode 100644 applications/external/flipper_i2ctools/images/Ok_btn_9x9.png delete mode 100644 applications/external/hex_editor/icons/Pin_arrow_up_7x9.png delete mode 100644 applications/external/hid_app/assets/Ble_connected_15x15.png delete mode 100644 applications/external/hid_app/assets/Ble_disconnected_15x15.png delete mode 100644 applications/external/hid_app/assets/ButtonDown_7x4.png delete mode 100644 applications/external/hid_app/assets/ButtonLeft_4x7.png delete mode 100644 applications/external/hid_app/assets/ButtonRight_4x7.png delete mode 100644 applications/external/hid_app/assets/ButtonUp_7x4.png delete mode 100644 applications/external/hid_app/assets/Button_18x18.png delete mode 100644 applications/external/hid_app/assets/Circles_47x47.png delete mode 100644 applications/external/hid_app/assets/Left_mouse_icon_9x9.png delete mode 100644 applications/external/hid_app/assets/Ok_btn_9x9.png delete mode 100644 applications/external/hid_app/assets/Ok_btn_pressed_13x13.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_up_7x9.png delete mode 100644 applications/external/hid_app/assets/Pin_back_arrow_10x8.png delete mode 100644 applications/external/hid_app/assets/Pressed_Button_13x13.png delete mode 100644 applications/external/hid_app/assets/Right_mouse_icon_9x9.png delete mode 100644 applications/external/hid_app/assets/Space_65x18.png delete mode 100644 applications/external/hid_app/assets/Voldwn_6x6.png delete mode 100644 applications/external/hid_app/assets/Volup_8x6.png delete mode 100644 applications/external/ir_remote/images/ButtonDown_7x4.png delete mode 100644 applications/external/ir_remote/images/ButtonLeft_4x7.png delete mode 100644 applications/external/ir_remote/images/ButtonRight_4x7.png delete mode 100644 applications/external/ir_remote/images/ButtonUp_7x4.png delete mode 100644 applications/external/ir_remote/images/Ok_btn_9x9.png delete mode 100644 applications/external/ir_remote/images/back_10px.png delete mode 100644 applications/external/ir_remote/images/sub1_10px.png delete mode 100644 applications/external/metronome/images/ButtonUp_7x4.png delete mode 100644 applications/external/mfkey32/mfkey.png rename applications/external/mifare_fuzzer/{images => }/mifare_fuzzer_10px.png (100%) delete mode 100644 applications/external/mousejacker/images/badusb_10px.png delete mode 100644 applications/external/mousejacker/images/sub1_10px.png rename applications/external/multi_fuzzer/{icons => }/ibutt_10px.png (100%) delete mode 100644 applications/external/multi_fuzzer/icons/125_10px.png delete mode 100644 applications/external/multi_fuzzer/icons/ButtonLeft_4x7.png delete mode 100644 applications/external/multi_fuzzer/icons/ButtonRight_4x7.png delete mode 100644 applications/external/multi_fuzzer/icons/Ok_btn_9x9.png delete mode 100644 applications/external/multi_fuzzer/icons/Pin_arrow_up_7x9.png delete mode 100644 applications/external/multi_fuzzer/icons/Pin_back_arrow_10x8.png rename applications/external/{music_beeper/icons => music_player}/music_10px.png (100%) delete mode 100644 applications/external/nfc_magic/assets/DolphinCommon_56x48.png delete mode 100644 applications/external/nfc_magic/assets/DolphinNice_96x59.png delete mode 100644 applications/external/nfc_magic/assets/NFC_manual_60x50.png delete mode 100644 applications/external/ocarina/icons/music_10px.png rename applications/external/{music_player/icons => ocarina}/music_10px.png (100%) delete mode 100644 applications/external/passgen/icons/Ok_btn_9x9.png delete mode 100644 applications/external/passgen/icons/Pin_back_arrow_10x8.png delete mode 100644 applications/external/picopass/icons/DolphinMafia_115x62.png delete mode 100644 applications/external/picopass/icons/DolphinNice_96x59.png delete mode 100644 applications/external/picopass/icons/Nfc_10px.png delete mode 100644 applications/external/picopass/icons/RFIDDolphinReceive_97x61.png delete mode 100644 applications/external/picopass/icons/RFIDDolphinSend_97x61.png delete mode 100644 applications/external/pocsag_pager/images/Fishing_123x52.png delete mode 100644 applications/external/pocsag_pager/images/Lock_7x8.png delete mode 100644 applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png delete mode 100644 applications/external/pocsag_pager/images/Quest_7x8.png delete mode 100644 applications/external/pocsag_pager/images/Scanning_123x52.png delete mode 100644 applications/external/pocsag_pager/images/Unlock_7x8.png delete mode 100644 applications/external/pocsag_pager/images/WarningDolphin_45x42.png delete mode 100644 applications/external/sam/icons/music_10px.png delete mode 100644 applications/external/signal_generator/icons/SmallArrowDown_3x5.png delete mode 100644 applications/external/signal_generator/icons/SmallArrowUp_3x5.png delete mode 100644 applications/external/simonsays/images/Connected_62x31.png delete mode 100644 applications/external/simonsays/images/Cry_dolph_55x52.png delete mode 100644 applications/external/simonsays/images/DolphinMafia_115x62.png delete mode 100644 applications/external/simonsays/images/DolphinWait_61x59.png delete mode 100644 applications/external/simonsays/images/WarningDolphin_45x42.png delete mode 100644 applications/external/spi_mem_manager/images/DolphinMafia_115x62.png delete mode 100644 applications/external/spi_mem_manager/images/DolphinNice_96x59.png delete mode 100644 applications/external/spi_mem_manager/images/SDQuestion_35x43.png delete mode 100644 applications/external/subghz_bruteforcer/images/ButtonDown_7x4.png delete mode 100644 applications/external/subghz_bruteforcer/images/ButtonUp_7x4.png delete mode 100644 applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png delete mode 100644 applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_01.png delete mode 100644 applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png delete mode 100644 applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_03.png delete mode 100644 applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_04.png delete mode 100644 applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png delete mode 100644 applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png delete mode 100644 applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_rate delete mode 100644 applications/external/subghz_bruteforcer/images/sub1_10px.png rename applications/external/subghz_bruteforcer/{images => }/subbrute_10px.png (100%) delete mode 100644 applications/external/subghz_playlist/images/ButtonRight_4x7.png delete mode 100644 applications/external/subghz_playlist/images/sub1_10px.png delete mode 100644 applications/external/subghz_remote_configurator/icons/DolphinNice_96x59.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/ButtonDown_7x4.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/ButtonLeft_4x7.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/ButtonRight_4x7.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/ButtonUp_7x4.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/Ok_btn_9x9.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/Pin_arrow_up_7x9.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/Pin_cell_13x13.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/Pin_star_7x7.png delete mode 100644 applications/external/subghz_remote_configurator/icons/remote_scene/back_10px.png delete mode 100644 applications/external/subghz_remote_configurator/icons/sub1_10px.png rename applications/external/subghz_remote_configurator/{icons => }/subrem_10px.png (100%) delete mode 100644 applications/external/swd_probe/icons/ButtonDown_7x4.png delete mode 100644 applications/external/swd_probe/icons/ButtonUp_7x4.png delete mode 100644 applications/external/timelapse/icons/ButtonDown_7x4.png delete mode 100644 applications/external/timelapse/icons/ButtonLeft_4x7.png delete mode 100644 applications/external/timelapse/icons/ButtonRight_4x7.png delete mode 100644 applications/external/timelapse/icons/ButtonUp_7x4.png delete mode 100644 applications/external/timelapse/icons/Pin_star_7x7.png delete mode 100644 applications/external/timelapse/icons/loading_10px.png delete mode 100644 applications/external/totp/images/DolphinCommon_56x48.png delete mode 100644 applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png delete mode 100644 applications/external/uart_terminal/assets/KeyBackspace_16x9.png delete mode 100644 applications/external/uart_terminal/assets/KeySaveSelected_24x11.png delete mode 100644 applications/external/uart_terminal/assets/KeySave_24x11.png delete mode 100644 applications/external/uart_terminal/assets/WarningDolphin_45x42.png delete mode 100644 applications/external/wav_player/images/music_10px.png delete mode 100644 applications/external/weather_station/images/Fishing_123x52.png delete mode 100644 applications/external/weather_station/images/Lock_7x8.png delete mode 100644 applications/external/weather_station/images/Pin_back_arrow_10x8.png delete mode 100644 applications/external/weather_station/images/Quest_7x8.png delete mode 100644 applications/external/weather_station/images/Scanning_123x52.png delete mode 100644 applications/external/weather_station/images/Unlock_7x8.png delete mode 100644 applications/external/weather_station/images/WarningDolphin_45x42.png delete mode 100644 applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeySave_24x11.png delete mode 100644 applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png rename applications/external/yatzee/{images => }/yatzee_icon_10px.png (100%) diff --git a/applications/debug/battery_test_app/application.fam b/applications/debug/battery_test_app/application.fam index f97d10279..b388445cc 100644 --- a/applications/debug/battery_test_app/application.fam +++ b/applications/debug/battery_test_app/application.fam @@ -11,5 +11,4 @@ App( stack_size=1 * 1024, order=130, fap_category="Debug", - fap_libs=["assets"], ) diff --git a/applications/external/.mass_storage/application.fam b/applications/external/.mass_storage/application.fam index 251deed80..5cc57b2b5 100644 --- a/applications/external/.mass_storage/application.fam +++ b/applications/external/.mass_storage/application.fam @@ -9,5 +9,4 @@ App( order=2, # fap_icon="", fap_category="Misc", - fap_libs=["assets"], ) diff --git a/applications/external/bpmtapper/application.fam b/applications/external/bpmtapper/application.fam index 7f9cb79ce..8691a1e75 100644 --- a/applications/external/bpmtapper/application.fam +++ b/applications/external/bpmtapper/application.fam @@ -8,6 +8,5 @@ App( stack_size=2 * 1024, fap_icon="bpm_10px.png", fap_category="Media", - fap_icon_assets="icons", order=15, ) diff --git a/applications/external/bpmtapper/bpm.c b/applications/external/bpmtapper/bpm.c index 397b1a3b5..8353019df 100644 --- a/applications/external/bpmtapper/bpm.c +++ b/applications/external/bpmtapper/bpm.c @@ -4,7 +4,7 @@ #include #include #include -#include "bpm_tapper_icons.h" +#include "assets_icons.h" typedef enum { EventTypeTick, diff --git a/applications/external/bpmtapper/icons/DolphinCommon_56x48.png b/applications/external/bpmtapper/icons/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/applications/external/brainfuck/brainfuck_i.h b/applications/external/brainfuck/brainfuck_i.h index d3d27dcbd..293654c3e 100644 --- a/applications/external/brainfuck/brainfuck_i.h +++ b/applications/external/brainfuck/brainfuck_i.h @@ -30,6 +30,7 @@ typedef unsigned char byte; #include #include #include +#include #include #include diff --git a/applications/external/brainfuck/icons/ButtonRightSmall_3x5.png b/applications/external/brainfuck/icons/ButtonRightSmall_3x5.png deleted file mode 100644 index b9d5f87db1ca55141449cfcc3bf054417eaa84e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1738 zcmcIl%Wm676lEHu%>zLLWYHwZf?zfc+Tjd`l=wgx!?E02K7gZd!)A>b-AnNYDb z=UD-0O?$90FBm_PwI0iHnuo^kzrHc_RD{NpUPPi|OHR_A(^5V@-5v4MBkl`h`li))oId$h zr-Twrdf1}K>IcLLELU%T22?9W66_DYYiq$%XiVz52r!<_X6DQ`RXN6%@B5fgOeq2c zsup?8<|wc3bqoVp@iHyyRONcZ$YOO|hXyEJO(84Rw0YIq1cu=`E3jpfW=b6}iq3{+ z*&1Ed+b2+^)%#K6YP2XM-j|g+F1g%3k$HWuD^^TYt*VLogtqnH|4=CSx?pi!PM7uw zj^$Klz+C~>TIwr;tx~dDl_RC5T~K>nMV(qE)xUm{=0eS?`;DS@fE=(|h6bc&Ap((k zBdZr!eqejwSR^211&yE&1gqKkz)Gaa;ylnO3Wj-Avz*J}AT&UfnWiG(Hm6?C6^L<1 zqL@1bP64XW zqKrwxT^V~c?$~}TQ}}Y&^h4H0l>o-Xk=)_jK{NqDuJ0r$_IQFAaV$sJiQn_7p}()Y zrKYNklmK^aLl-wVr`&ul_BH)&R_4UgD(ZOFr}nOx%X#N!gVeJ4h)=Wyh? zQXrf4V!h1m}&`|!B-3a0&FC= whJ($~<(FI>9{%<2-NwbcKNmOR7sY+;Hox}g7dMW6Yj&IA_U_=9M~Bb;1{rl65&!@I diff --git a/applications/external/brainfuck/icons/KeySaveSelected_24x11.png b/applications/external/brainfuck/icons/KeySaveSelected_24x11.png deleted file mode 100644 index eeb3569d3accc5a5c56829b12c85079172b56729..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1853 zcmcIlPiW*+7#}^zw%YZuf+B)3d$6*;fxiXAXK-X$#&ks`?Z1FR>QX z26aVbT~%`&N5w=X1OWo&yGcQZD9KMx7+O3JvM4Pgkw_&Y^~HA4kU?pcLYz)%lYCqz zD405=sj4ZsOlbo2yrZFUJVocl(hfu!>phe>@9d^rUFW&j&H}!)!;|9lBv{%Lg~)s2 z4%()#|Dlit(}3xA)*qFJ1uF0J7`Su5Y9oEA+srsEMAi|aKWWt3B%(w#g-G)oQNqL^ zf1*@0Ucg(l;0+nNrXfra);gN*63r#X84bG_S5Oapz-U2_2No;}caH=0Jhz?X1x*6p zZZ%{Or9=^P?a(oNj2+}NPLO5lb>xS(hK#yzQ(Fto(z;~|u)ZaN?XnW(`pULU1i&$^ zrW-ON3~kFtm|7MJL)}ES1tUU3N-T@l>$)*vdoGLM%c1>)tfeXjj8tQ`U&jXGx~+pC zJw&zve}0HDBg6^Jz?Y@lahswqGEXq5ZvEhVyV+dJL>TqqMZUhgD7BZGrskL?B8nzU zEO0}S#T1Md#k9-SH0hSMuhLzKa_I5y_(QtDUmB14ku-9rOM~*GXvjh71`cJarlUj3 ze7uCJ^@AP<(j#0_!EzB61Df%LF0|x7U8vqkd`@?cmVP{k{EyPdWes{X>2la%Rk=(? zE%&0TDeAxbb=w#db1i`F%Wmf5GAz>Wv>@jW_p)ho-#0CeC9cGbyZ*s9Cn^o)Rq=_$h#NIZix%+wt GFa8a7)lmol diff --git a/applications/external/brainfuck/icons/KeySave_24x11.png b/applications/external/brainfuck/icons/KeySave_24x11.png deleted file mode 100644 index e7dba987a04dad7dd96001913c55566dbde96c8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1863 zcmcIlO^6&t6dpxnjjSF75f9pQJZy|LUDdzSZ6$n-*6-2D5s-9_fx~uK( zotfQBVDX?Q(UXLzXF6aX)%U*l z-d9y`w^q+Do_PF3p-@5qOL5h2N9RU z^i-~BIziNECdw*wjUcQeOxncsbnKa>(*%1MPoPck0jC)~9$50g-#!ks+4LGwn$d`f zMy;%ZsA3Rsk2!`#ELuW>2#g3fF>;CFBHMCo-El0(@X1&g%&$qdl~*F4Kd~*B3^?Z1 z^bEmDf}0)Wn??sYC6l9$X;6esLO7#_ZCmDy?ZqU3l|%anS#wn!7%f39-Qp(l9fyJ- z(?=x}n~2%2PcX9#VmYdECvH{tWzv)!s%sn^Z&a(TMEXG=KBQ~smzBm!)h4cOBfSV| zapw6l2`LyY2x(Vnan#Li4>BO#dXPeox2Fr~f_P*4)DM)gJ3Y$sMNw8+?gqit>2PpJ znU9yygm%~yKzf8rCa_fc*^nlp(uJ1%rwg^aiBIX^Xz9mu$p0vPT2|JhQCGkYtEqW1 zTD})enxg%?Uw4c#Ggk#{pLa8zmSLH8=LI=?xR>pc=yYsHAgcQUxz^arx`9fR>e$sw zj@}Uy75!kQXF{tT9e=F+z^*!*3|n>nI6oucWq!(t2og`=40-O!tAE1z^ID@;X)nEd zVK7xqj`wg%Ed2Op{k=a*YZpKHX787$ zzhKuN9(?M5ojmn%xcU1}OP>$^`ZD?cW@lIaTn=x3xBtP#z16o~{qVMZ>hecsD?jQQ ME3387mS5lf8`39Il>h($ diff --git a/applications/external/counter/application.fam b/applications/external/counter/application.fam index b287965bf..0db32b7ca 100644 --- a/applications/external/counter/application.fam +++ b/applications/external/counter/application.fam @@ -7,6 +7,5 @@ App( "gui", ], fap_category="Misc", - fap_icon="icons/counter_icon.png", - fap_icon_assets="icons", + fap_icon="counter_icon.png", ) diff --git a/applications/external/counter/counter.c b/applications/external/counter/counter.c index 22fa8cd80..67b45170b 100644 --- a/applications/external/counter/counter.c +++ b/applications/external/counter/counter.c @@ -2,7 +2,6 @@ #include #include #include -#include #define MAX_COUNT 99 #define BOXTIME 2 diff --git a/applications/external/counter/icons/counter_icon.png b/applications/external/counter/counter_icon.png similarity index 100% rename from applications/external/counter/icons/counter_icon.png rename to applications/external/counter/counter_icon.png diff --git a/applications/external/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c index eafb435e7..52d222138 100644 --- a/applications/external/dap_link/dap_link.c +++ b/applications/external/dap_link/dap_link.c @@ -15,6 +15,7 @@ #include "usb/dap_v2_usb.h" #include #include "dap_link_icons.h" +#include "assets_icons.h" /***************************************************************************/ /****************************** DAP COMMON *********************************/ @@ -524,4 +525,4 @@ int32_t dap_link_app(void* p) { dap_app_free(app); return 0; -} \ No newline at end of file +} diff --git a/applications/external/dap_link/icons/ActiveConnection_50x64.png b/applications/external/dap_link/icons/ActiveConnection_50x64.png deleted file mode 100644 index 1d7686dddf8a33b724c7528ed36435514b7518b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3842 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 Cw>kg- diff --git a/applications/external/esp32cam_morseflasher/assets/KeyBackspace_16x9.png b/applications/external/esp32cam_morseflasher/assets/KeyBackspace_16x9.png deleted file mode 100644 index 9946232d953ef1cbfbf0e6754be6645e5ea2747b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1829 zcmcIl&u`pB6gEEvMG+BPN`-{wNT>+Lo*8@XwOcpZ?1t{5I;81J4Y!WR<6SFjkFlNX zCgRjnK|!3jpo$Y0E}Xb=;LeTzpnm{T)f-4i;dy_hpfu#tmAsxAzxTcGz4y(`m)l!6 zS1w(-q$tWtuiM#y_bNQEzxE>h|J=PM>Pg=HtW=aY-mae)lh*~S0I8^$I!Q-a=}mlXitE9+UN$s!YEtd_TB{DI?graxTNXmKb&NR1RCQdP z*p_AEk5q~&HgLlr6cO9QmPZ_Q{?i~@5yjq4=i_-SnEBeUs&daT#^bR*Hg#DH4C1=3 zfvG_$0t-|gW)+*DtXx|lbVSLEB(D;gsWl=C<$mRBz;u>EnlE9qa$Y7Vm@#3wL3CWF zv@i^U^G(xqX_e|ijf0zqnN0f5E;9~PYWYyXtSU!}MEQj(L+?JpJ#W3Q_ zfcbtgnwBTxh8T$yuuHHdQ+~PEE(EJ&(U)?xXw>#1qDqNQ)vI@tERy5$gPPIYL3CIp zd=0ur5T*!|K7p3G9x*>8*u!{c8h{QWRntB?yEl08lWCYa({L}SbyS-h=I2pl*a_8oT+S_c~#IXiq#zs}!z^4spCcOR_0+*o_q`N6;X{rjK1`QsBs`Gcx|z4v#k QTVG_o&8^N)8~5)21Ktij=l}o! diff --git a/applications/external/esp32cam_morseflasher/assets/KeySaveSelected_24x11.png b/applications/external/esp32cam_morseflasher/assets/KeySaveSelected_24x11.png deleted file mode 100644 index eeb3569d3accc5a5c56829b12c85079172b56729..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1853 zcmcIlPiW*+7#}^zw%YZuf+B)3d$6*;fxiXAXK-X$#&ks`?Z1FR>QX z26aVbT~%`&N5w=X1OWo&yGcQZD9KMx7+O3JvM4Pgkw_&Y^~HA4kU?pcLYz)%lYCqz zD405=sj4ZsOlbo2yrZFUJVocl(hfu!>phe>@9d^rUFW&j&H}!)!;|9lBv{%Lg~)s2 z4%()#|Dlit(}3xA)*qFJ1uF0J7`Su5Y9oEA+srsEMAi|aKWWt3B%(w#g-G)oQNqL^ zf1*@0Ucg(l;0+nNrXfra);gN*63r#X84bG_S5Oapz-U2_2No;}caH=0Jhz?X1x*6p zZZ%{Or9=^P?a(oNj2+}NPLO5lb>xS(hK#yzQ(Fto(z;~|u)ZaN?XnW(`pULU1i&$^ zrW-ON3~kFtm|7MJL)}ES1tUU3N-T@l>$)*vdoGLM%c1>)tfeXjj8tQ`U&jXGx~+pC zJw&zve}0HDBg6^Jz?Y@lahswqGEXq5ZvEhVyV+dJL>TqqMZUhgD7BZGrskL?B8nzU zEO0}S#T1Md#k9-SH0hSMuhLzKa_I5y_(QtDUmB14ku-9rOM~*GXvjh71`cJarlUj3 ze7uCJ^@AP<(j#0_!EzB61Df%LF0|x7U8vqkd`@?cmVP{k{EyPdWes{X>2la%Rk=(? zE%&0TDeAxbb=w#db1i`F%Wmf5GAz>Wv>@jW_p)ho-#0CeC9cGbyZ*s9Cn^o)Rq=_$h#NIZix%+wt GFa8a7)lmol diff --git a/applications/external/esp32cam_morseflasher/assets/KeySave_24x11.png b/applications/external/esp32cam_morseflasher/assets/KeySave_24x11.png deleted file mode 100644 index e7dba987a04dad7dd96001913c55566dbde96c8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1863 zcmcIlO^6&t6dpxnjjSF75f9pQJZy|LUDdzSZ6$n-*6-2D5s-9_fx~uK( zotfQBVDX?Q(UXLzXF6aX)%U*l z-d9y`w^q+Do_PF3p-@5qOL5h2N9RU z^i-~BIziNECdw*wjUcQeOxncsbnKa>(*%1MPoPck0jC)~9$50g-#!ks+4LGwn$d`f zMy;%ZsA3Rsk2!`#ELuW>2#g3fF>;CFBHMCo-El0(@X1&g%&$qdl~*F4Kd~*B3^?Z1 z^bEmDf}0)Wn??sYC6l9$X;6esLO7#_ZCmDy?ZqU3l|%anS#wn!7%f39-Qp(l9fyJ- z(?=x}n~2%2PcX9#VmYdECvH{tWzv)!s%sn^Z&a(TMEXG=KBQ~smzBm!)h4cOBfSV| zapw6l2`LyY2x(Vnan#Li4>BO#dXPeox2Fr~f_P*4)DM)gJ3Y$sMNw8+?gqit>2PpJ znU9yygm%~yKzf8rCa_fc*^nlp(uJ1%rwg^aiBIX^Xz9mu$p0vPT2|JhQCGkYtEqW1 zTD})enxg%?Uw4c#Ggk#{pLa8zmSLH8=LI=?xR>pc=yYsHAgcQUxz^arx`9fR>e$sw zj@}Uy75!kQXF{tT9e=F+z^*!*3|n>nI6oucWq!(t2og`=40-O!tAE1z^ID@;X)nEd zVK7xqj`wg%Ed2Op{k=a*YZpKHX787$ zzhKuN9(?M5ojmn%xcU1}OP>$^`ZD?cW@lIaTn=x3xBtP#z16o~{qVMZ>hecsD?jQQ ME3387mS5lf8`39Il>h($ diff --git a/applications/external/esp32cam_morseflasher/assets/WarningDolphin_45x42.png b/applications/external/esp32cam_morseflasher/assets/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb444db1739f2ccd030e506e8bada11ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& diff --git a/applications/external/esp32cam_morseflasher/uart_text_input.c b/applications/external/esp32cam_morseflasher/uart_text_input.c index 5b2b1033d..9a1099468 100644 --- a/applications/external/esp32cam_morseflasher/uart_text_input.c +++ b/applications/external/esp32cam_morseflasher/uart_text_input.c @@ -1,6 +1,6 @@ #include "uart_text_input.h" #include -#include "mayhem_morseflash_icons.h" +#include "assets_icons.h" #include struct UART_TextInput { diff --git a/applications/external/etch_a_sketch/application.fam b/applications/external/etch_a_sketch/application.fam index 9d9b1c1f1..99e97e7d0 100644 --- a/applications/external/etch_a_sketch/application.fam +++ b/applications/external/etch_a_sketch/application.fam @@ -9,5 +9,4 @@ App( order=175, fap_icon="etch-a-sketch-icon.png", fap_category="Media", - fap_libs=["assets"], ) diff --git a/applications/external/flipbip/icons/Auth_62x31.png b/applications/external/flipbip/icons/Auth_62x31.png deleted file mode 100644 index 40f094ac9ba758ea06838c2bb8376cf6f28f8050..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3761 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 diff --git a/applications/external/flipbip/icons/ButtonCenter_7x7.png b/applications/external/flipbip/icons/ButtonCenter_7x7.png deleted file mode 100644 index a66461b227b321f11bc85a38c63afda0d982d78d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1440 zcmbVMO>5LZ7>3B0UTd{6LeL$!>SMgR$1#+OC>f>WbBi&Q4}`hi)=4nY!B^ zRS++F@uGMUJb3aT=s^*^`ByxtKS0pQengRK)xc!Fo_XHy_wM4t+~~;u5yLP>tMip5 zJ#SCPj;;EC7QMf%r=8LK<-{;{ji+Oa@#yhB!`QZ0)RxtQGk2+A_%pkO15A>@E!$;O3zzJQJa2RR6$Nr)W_ z7YND|Tm*y9)Y4+VL6$0~2eS0eHCswW1j0_IV|Q}4jGGXbN+pOK=s1=}Sjk$bXx9pp z14D)iPgpD>C1eOvMp~Dv$~Cp7eG`HxqYaaRo3z7VmqrlVC^e}E^jU_BR^xV0xX&PO z^MHpc(O8^ewU<0EgKtP11Q|L}vp2Lx1KT!4C$+VR!zG@`)tK?w8(QLlp<+=>Dw(w8 zZ|umfGixGJjyY8_u1VP*25NE00vv2R;P538`m&Q8Nu>-P@CsGjHFeamg|L;wH8e#W zO!E1o7!ic}P*3N9PZj+;u=GV^ZBwYvNJGW})m{<-ZE_fe&7L&RBh@fbG-SM5aZRnN zErd@BC0xw0I=6BS)Uc*#9$8bxdfj3UYD_xCJf@Ru_`V|9Y8t`ed;cHbpO1yEAlm+a zo-9J7EZZ{nq_4f4Gr^DZYPq}^%Z6y{i)3l;6sXRY3%FD$SdNX;MDHaWnHPzU>e`@m zF7WGvRa<~wjuFbzGH^|n#-ID<;8^^(3;VR47T2VI$csKKkqyJgp_fv5X;ksj_%dA!DP1fvqs5@M&TiMq)XFS~oQMEEt+wm~CH#1^@v8^9I ze!KeW*2}SvVq)w;aB`3F^wYBk_vc38mDg{_-`&1AJ@Mku!7q1qAAXYd(CX|$<@u>= GH@^cCN4$jq diff --git a/applications/external/flipbip/icons/ButtonLeftSmall_3x5.png b/applications/external/flipbip/icons/ButtonLeftSmall_3x5.png deleted file mode 100644 index 51411acaf8d921da05d3eb2c9dc54af6253d0c5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1741 zcmcIlO>f*p7&ahKo2qaqhfpq-djZk$%y|8=w(3@C9LP#IMCnQmH^wvLT`OykYkRYs zoT^?B;uo|6iC@4U;DCBW;=gbKi5tA$wYLSeAr~xpJ>z}md7t-vAJ2Sqc<|Zvt(#k# zrd=QHM@R5{3+}hCya~VW-FW^7Jg%1eU)Gv-?M8dQp*{ONVOnfB!?F;G2Nuyqc}b zBRG~bnFDtXSh~_z2D_DHjjF~<&H6?i_7Qc9nwI|2!>kZ$QGeVj>K4d|H8M1yYZMV^ z*>FsqfcgO$enLq=$Ql}h4+Lx2<-kg0>DgtVaU6__x?u&EqbM{$l$m8BV7}+tuE$a0 zSq@@}Em1=3A@YVvlBUw7iI6K@us%|J-d3VrzqYMV95^=B4BW&`9Yz?k9m-HjY=#mp zIHKIL++;u<+p&ALFRBc_95#EIwN)j6(XkWHXZ{dLo4UxhZ3_)KOnG8C7G)_(9LvyO z#NAahRmDm`bq9tnc#x_pXW*YKXA_CbV$!-9*ddGktcKQ|b>GJW`KTMsGJR#bfbrJr zp~2fGOJQ`cvsYB_ayr+vi>U-vE z8hTQo0do@}q+|1ew~jl@dIf7Dd~w8;)|a-&elhI1?QpZ%!(?+@$O2NW;4p~JZ~p=Z y$%mt8XME);|MRca^}+V`_7CrGJ^q$H_qAWYe)sq9x5o4Kz!~iwL_gmD>dD{nYamVl diff --git a/applications/external/flipbip/icons/ButtonLeft_4x7.png b/applications/external/flipbip/icons/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d43247083aa705620e9836ac415b42ca46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a diff --git a/applications/external/flipbip/icons/ButtonRightSmall_3x5.png b/applications/external/flipbip/icons/ButtonRightSmall_3x5.png deleted file mode 100644 index b9d5f87db1ca55141449cfcc3bf054417eaa84e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1738 zcmcIl%Wm676lEHu%>zLLWYHwZf?zfc+Tjd`l=wgx!?E02K7gZd!)A>b-AnNYDb z=UD-0O?$90FBm_PwI0iHnuo^kzrHc_RD{NpUPPi|OHR_A(^5V@-5v4MBkl`h`li))oId$h zr-Twrdf1}K>IcLLELU%T22?9W66_DYYiq$%XiVz52r!<_X6DQ`RXN6%@B5fgOeq2c zsup?8<|wc3bqoVp@iHyyRONcZ$YOO|hXyEJO(84Rw0YIq1cu=`E3jpfW=b6}iq3{+ z*&1Ed+b2+^)%#K6YP2XM-j|g+F1g%3k$HWuD^^TYt*VLogtqnH|4=CSx?pi!PM7uw zj^$Klz+C~>TIwr;tx~dDl_RC5T~K>nMV(qE)xUm{=0eS?`;DS@fE=(|h6bc&Ap((k zBdZr!eqejwSR^211&yE&1gqKkz)Gaa;ylnO3Wj-Avz*J}AT&UfnWiG(Hm6?C6^L<1 zqL@1bP64XW zqKrwxT^V~c?$~}TQ}}Y&^h4H0l>o-Xk=)_jK{NqDuJ0r$_IQFAaV$sJiQn_7p}()Y zrKYNklmK^aLl-wVr`&ul_BH)&R_4UgD(ZOFr}nOx%X#N!gVeJ4h)=Wyh? zQXrf4V!h1m}&`|!B-3a0&FC= whJ($~<(FI>9{%<2-NwbcKNmOR7sY+;Hox}g7dMW6Yj&IA_U_=9M~Bb;1{rl65&!@I diff --git a/applications/external/flipbip/icons/ButtonRight_4x7.png b/applications/external/flipbip/icons/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/flipbip/icons/Keychain_39x36.png b/applications/external/flipbip/icons/Keychain_39x36.png deleted file mode 100644 index d15850b5b7f70672f90ff6c67a3e12341071fc61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3775 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 #include #include "flipbip_icons.h" +#include "assets_icons.h" #include "../helpers/flipbip_haptic.h" #include "../helpers/flipbip_led.h" #include "../helpers/flipbip_string.h" diff --git a/applications/external/flipbip/views/flipbip_startscreen.c b/applications/external/flipbip/views/flipbip_startscreen.c index 76b060e95..f2b69c084 100644 --- a/applications/external/flipbip/views/flipbip_startscreen.c +++ b/applications/external/flipbip/views/flipbip_startscreen.c @@ -4,6 +4,7 @@ #include #include #include "flipbip_icons.h" +#include "assets_icons.h" struct FlipBipStartscreen { View* view; diff --git a/applications/external/flipper_i2ctools/images/ButtonDown_7x4.png b/applications/external/flipper_i2ctools/images/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67d1c23c0bb5d765e8d2aa04b9b5adec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE diff --git a/applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png b/applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d43247083aa705620e9836ac415b42ca46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a diff --git a/applications/external/flipper_i2ctools/images/ButtonRight_4x7.png b/applications/external/flipper_i2ctools/images/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/flipper_i2ctools/images/ButtonUp_7x4.png b/applications/external/flipper_i2ctools/images/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/flipper_i2ctools/images/Ok_btn_9x9.png b/applications/external/flipper_i2ctools/images/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da2049f12f7b25f96b11a9c40cd8227302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3605 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 #include #include +#include #define APP_NAME "I2C Tools" #define SCAN_MENU_TEXT "Scan" @@ -35,4 +36,4 @@ typedef struct { void draw_main_view(Canvas* canvas, i2cMainView* main_view); i2cMainView* i2c_main_view_alloc(); -void i2c_main_view_free(i2cMainView* main_view); \ No newline at end of file +void i2c_main_view_free(i2cMainView* main_view); diff --git a/applications/external/flipper_i2ctools/views/scanner_view.h b/applications/external/flipper_i2ctools/views/scanner_view.h index 52f30a7bf..a38d9a917 100644 --- a/applications/external/flipper_i2ctools/views/scanner_view.h +++ b/applications/external/flipper_i2ctools/views/scanner_view.h @@ -2,8 +2,9 @@ #include #include #include +#include #include "../i2cscanner.h" #define SCAN_TEXT "SCAN" -void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner); \ No newline at end of file +void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner); diff --git a/applications/external/flipper_i2ctools/views/sender_view.h b/applications/external/flipper_i2ctools/views/sender_view.h index c4fdd98a2..9b2e75a5c 100644 --- a/applications/external/flipper_i2ctools/views/sender_view.h +++ b/applications/external/flipper_i2ctools/views/sender_view.h @@ -2,8 +2,9 @@ #include #include #include +#include #include "../i2csender.h" #define SEND_TEXT "SEND" -void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender); \ No newline at end of file +void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender); diff --git a/applications/external/flipper_i2ctools/views/sniffer_view.h b/applications/external/flipper_i2ctools/views/sniffer_view.h index 8f2140bba..b827294f8 100644 --- a/applications/external/flipper_i2ctools/views/sniffer_view.h +++ b/applications/external/flipper_i2ctools/views/sniffer_view.h @@ -2,8 +2,9 @@ #include #include #include +#include #include "../i2csniffer.h" #define SNIFF_TEXT "SNIFF" -void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer); \ No newline at end of file +void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer); diff --git a/applications/external/gpioreader_b/application.fam b/applications/external/gpioreader_b/application.fam index aa26060aa..c3a417774 100644 --- a/applications/external/gpioreader_b/application.fam +++ b/applications/external/gpioreader_b/application.fam @@ -7,7 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, order=50, - fap_libs=["assets"], fap_category="GPIO", fap_icon="icon.png", ) diff --git a/applications/external/hex_editor/hex_editor.c b/applications/external/hex_editor/hex_editor.c index 5e5dc807c..5e411a400 100644 --- a/applications/external/hex_editor/hex_editor.c +++ b/applications/external/hex_editor/hex_editor.c @@ -12,7 +12,7 @@ #include #include -// #include +#include #define TAG "HexEditor" @@ -354,4 +354,4 @@ int32_t hex_editor_app(void* p) { hex_editor_free(hex_editor); return 0; -} \ No newline at end of file +} diff --git a/applications/external/hex_editor/icons/Pin_arrow_up_7x9.png b/applications/external/hex_editor/icons/Pin_arrow_up_7x9.png deleted file mode 100644 index a91a6fd5e99a72112e28865cd8a004c7d1933fff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3603 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 AV*mgE diff --git a/applications/external/hid_app/assets/ButtonLeft_4x7.png b/applications/external/hid_app/assets/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d43247083aa705620e9836ac415b42ca46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a diff --git a/applications/external/hid_app/assets/ButtonRight_4x7.png b/applications/external/hid_app/assets/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/hid_app/assets/ButtonUp_7x4.png b/applications/external/hid_app/assets/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/hid_app/assets/Button_18x18.png b/applications/external/hid_app/assets/Button_18x18.png deleted file mode 100644 index 30a5b4fab236d8b57242559ef94fb1c5dbb5d10a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3609 zcmaJ@c{r49`+jVNvSba(81Yt?8Cx+K+gL`~8rw)>jKN@*W(G4tN=nI=Eo(wa4Q%8vkx{u?zYHw>PBq%Eg0DzDc z(hS9!#kL=Q9?lyh8i4}IOAwh8Gn{vj+=2WcHX}wv6 z{-S5$q3oHN^-t@S6WJ3RZH#u2$UR~zN#ptcfIceP0M?_BV27-0s*2>6L=N(TM8}(7 z`|{NTz#I>Q9zlC#w88a|1aJf7E{y|X4MV@8D(qEU08kPz2o{^z#g&Kx8Z{gnC4k1g zz$1sJ-hx0100c6^Ou@i?Az=E4l_4L{Q=Hr{4fN#iE9M8{xPXj4 zwXcCZrZHH9x3-ik()GEPC3j>M9}pamP82cr1R^s`)mi|M9yfs4FW$-nvgXNycGe6Q zdyu19NG_nZIkh$YM5nd{EA_o>$im#H=N(jFoW#zri2 zzHaq}&H-mLjWbGW3!*m9Vu-<|sQ8IyUQrUuD^Y zZ5kLaP)TNrO{v3TljpVO71A~Zl0$?5=4HED+vhu;fXTOrK ztd-`*>@YLleW2Dr)O5#aVKIBW;(Net{L&fmykHDc=SE~9Xfj6PB)GnjQpjCw>YwC} zR9aA{Na)9%HeO5YYXoUs+qhO~shM)&$w{7%+(E`K?kUJ#dz(k?py`OXN2cWmbjX(N zhetloFX}k)Erp$Yef^5L=T)?gtyCsf!S5mvbxHH}AK>JBc4f+;Vyks@FWBQm zv;|XTR&l>#uJV~bgvC9Qkq3mEZj9OrDk>*xS?#h4K=vWk3mpm#J4Nx?)+$qpgr={f z{7)j8p!B5jM3F?h8|zJPM$08&^)bWN0{I6}g(+gkb#X>xymxMCnP%kOKiOKG`;q^C z4D8k^D?(ndJ;dQkvA9l9rgCeR6r#CMy`bxTCf*mn;s=?eRS0~E+HaozKD{&G+s?^} z$*3P8yM-F2nbx$W4+H`tb7MFv+BM zVyUoH=hTSQiTjRDR41b@#{FH651d3EoN*4nYvJ_Nexz97qtt`0VtJ>R#YalpP$8%U z`}UI_1=Sv#7uT>tPcBDWD+@7pXTL<&4 z%LPNuSvw%8_kEZ?Nj^E_XIr_1-##9k)Bl`(yiKu9sO_9OkGhfi<8J>FpOT1@qrIWM z)xBOblo_d+sa|#vImb9hEoTWvfUN`xR2-=|SrJ{)7u5dU@B?;=F)6V0Zb^9ZONZqW z;YY!e^mleQyF=k9REPgaqD-Ks9(JxJ5&JFRCZ5$XcWLO}o@T#_q&mNX4y%GcSSqtu zd`EQY(uO`v(mpSy&R1N2fC0t}uhmyrS6DwQhyL`(& zG5PLev}0iuT2M=HAh~j?a7gD(ab5A7Nf%!^-`mujMP2E;ClZ^*(u32b9SB9&iio#D zn^VVRXDd3NeOM~UdYRQ<@|p1QOAEX{{K2}7MwVQY`x`jhf8pan$LN{4B@!7wn-ktw}#xeLT_EEzFQ3*fLAL; zbVp=F?A*v*KepDqneek_h_N6wZ_DS&^@?kZtLlR6g{M3LJPN!Symxl$^2PDJ+yU8b zC~3M|K*&{rl1!?VUXWYGYWMr9Wp+ruL) z{+L0_z!;VSUM53&HC*D*VXgZb-%pk~(9Y6U)Vi6YuIs*4@$(7A*Iyj#^M6hW_GS79 zq5`qgS*%Fbebxo~m7nJG>0&hT0|GNwN9%g(;8#be+!KMB+S#L-j%hS(=~#dM3+eI6 zw&vUr16N(w#4x?+n_}rtjK-osruLA%c4I|E8+q}COIgu&=GFOe`6nNjvyL0w7|(G| zUDo?@EF7`sciGM&=&iPZ9ZHpvBy;11(xQ#CS@&0F`{%Qt)%8=dQ?d(CLin^Y)lbm! zgXMNUs;bFCql|IFJGta5?^Z^YR;i19l7Z3I9R+2mQhQ-3YsfuSy4zkiIty8aJoQm~ zz-R0Gs?x5DQejnzkL+2Gp7yZluJeQ78uOP@O0f>oAsU+Qs0wd7ey%gT*{}IY+NS+5 z8s)U$&*)!>M@4nsxr0!>=%SNaoYK@xEd6on1y&N1>g~k#Pw#SbK7Uv`)q_c9-Yfn2 z$bvOK>|*QD6}H46^!9!|UjA-o3OQ9cMP#nH);v63nqiQIhLn4AaU_*dHP zQ2(X)*0R=jtvtFI-5Ix*=ghu^+eZqPLvzl%H#={ZJSeaJtkT5nouAA$Ik-3Fq#d+qrDcp7N)W0{b7<)I1R& zppL}tN5aTsS&^jPteMP^XXI0dgjgRTXXGub%YQ|%HAk>P4Y~;~xp_GU;q$Ab7n4Vdyo+*kY>nU_ zGx`}T)*BfC?kC-=d=c%rM$)ud>vE5krp2!l3GQ>1E|a6_gjoA_Sa-zzYeJtgQrJupeGtwb~ zv)29Yp$YVd8`Zs=-*>Kwd_P~d^%z%682ss3>)HOsRfH`pa3yyu<=2NRL!Fi_mR(8~ zN^uD}3JP*UvQ-P-ZOKDLPm09b-$gk8VoXsVObl!eub*f~Z}iOVT8(Y5DP-hN@s|B!#~QYw=)K*F;Y8Th24v;Z;(DaM z@*d7#r3}p+O>-dm&_Xa29AM&2^1^|v2pC@+3WxD#oNdAx0055)-Vseh+gQV}B!UKJ z+ed>=Aal?FU|>WiW3T}@8psRhizmXt?3XoQ5Z)UOcG0zg+K>@AKRhy&f^!J9b;O1S zVD-JhMus2*I*da=z|k-uIw6oqh0)>QKY3xC^|l!T2L0(m3xI?F5{0(02O&rl9O$Tq zraBf1g@TUiYj|V4Fjy}yHINomOA`XsfoSTeL!mHjeVC38=&Lss+)~Qs;Q6QyD}WhOSPeD*a|K!%?vmJeh_k z5kcFG7%x%~4G!i={VN9o`5#&$_3v}yoEU_TAwx7ZpxZh9cC@ki|6K`$f4r$Q6z;!z z|CN~P$ROh&C>)g(M8R?@=cBY8iVQ=&_Npv z7Ej!^9QqStV*|4yQfU|>7H4G!2Xja?@OW<+RM*_}sEH{;SI0tMQ_~z_s%xQZenj8Y z#MBIGc2r;QH`a`V4IPoKl5_wQQ%!g~LUmcOwk{}T)0h=FX^_W#uSw~5n0+sl7im$Uh&`Ef)}$5S}1 zy?{b{ajwM@~ diff --git a/applications/external/hid_app/assets/Circles_47x47.png b/applications/external/hid_app/assets/Circles_47x47.png deleted file mode 100644 index 6a16ebf7bbe999fa143c683bef713a6e2f466cbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3712 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 biU<_Yn+9Io=xs3K`2!qm58ISjpSt)z2v?8| diff --git a/applications/external/hid_app/assets/Left_mouse_icon_9x9.png b/applications/external/hid_app/assets/Left_mouse_icon_9x9.png deleted file mode 100644 index c533d85729f9b778cba33227141c90dea298f5e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3622 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 diff --git a/applications/external/hid_app/assets/Ok_btn_9x9.png b/applications/external/hid_app/assets/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da2049f12f7b25f96b11a9c40cd8227302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3605 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 diff --git a/applications/external/hid_app/assets/Pin_arrow_up_7x9.png b/applications/external/hid_app/assets/Pin_arrow_up_7x9.png deleted file mode 100644 index a91a6fd5e99a72112e28865cd8a004c7d1933fff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3603 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 TFC+3}9cE|kU{!4CefEC<&8td2 diff --git a/applications/external/hid_app/assets/Pressed_Button_13x13.png b/applications/external/hid_app/assets/Pressed_Button_13x13.png deleted file mode 100644 index 823926b842b8f9868fd70d6f1434dff071c04d5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3606 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 diff --git a/applications/external/hid_app/assets/Voldwn_6x6.png b/applications/external/hid_app/assets/Voldwn_6x6.png deleted file mode 100644 index ce487b546812e471bfaf380f3849f954e6c757ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4556 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 diff --git a/applications/external/hid_app/assets/Volup_8x6.png b/applications/external/hid_app/assets/Volup_8x6.png deleted file mode 100644 index ee9045f7fde3e4c5e7104bdee4919d7694e34d7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4564 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 M`5v=e6Zez<0(~)a_5c6? diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index c21d3e265..2b5a8059b 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -25,6 +25,9 @@ #include "views/hid_tikshorts.h" #include "views/hid_mouse_clicker.h" +#include "hid_icons.h" +#include + #include "hid_path.h" typedef enum { diff --git a/applications/external/hid_app/views/hid_keyboard.c b/applications/external/hid_app/views/hid_keyboard.c index 9d29ccf5e..9b8c87b13 100644 --- a/applications/external/hid_app/views/hid_keyboard.c +++ b/applications/external/hid_app/views/hid_keyboard.c @@ -3,7 +3,6 @@ #include #include #include "../hid.h" -#include "hid_icons.h" #define TAG "HidKeyboard" diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c index 7d0e125d7..e3d5f7a99 100644 --- a/applications/external/hid_app/views/hid_keynote.c +++ b/applications/external/hid_app/views/hid_keynote.c @@ -2,8 +2,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidKeynote" struct HidKeynote { diff --git a/applications/external/hid_app/views/hid_media.c b/applications/external/hid_app/views/hid_media.c index 0028ac596..98f88819f 100644 --- a/applications/external/hid_app/views/hid_media.c +++ b/applications/external/hid_app/views/hid_media.c @@ -5,8 +5,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidMedia" struct HidMedia { diff --git a/applications/external/hid_app/views/hid_mouse.c b/applications/external/hid_app/views/hid_mouse.c index 75df53dd1..e93ea3fc1 100644 --- a/applications/external/hid_app/views/hid_mouse.c +++ b/applications/external/hid_app/views/hid_mouse.c @@ -2,8 +2,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidMouse" struct HidMouse { diff --git a/applications/external/hid_app/views/hid_mouse_clicker.c b/applications/external/hid_app/views/hid_mouse_clicker.c index d85affc43..6777034ff 100644 --- a/applications/external/hid_app/views/hid_mouse_clicker.c +++ b/applications/external/hid_app/views/hid_mouse_clicker.c @@ -2,8 +2,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidMouseClicker" #define DEFAULT_CLICK_RATE 1 #define MAXIMUM_CLICK_RATE 60 diff --git a/applications/external/hid_app/views/hid_mouse_jiggler.c b/applications/external/hid_app/views/hid_mouse_jiggler.c index 09c14c668..c2ff93f60 100644 --- a/applications/external/hid_app/views/hid_mouse_jiggler.c +++ b/applications/external/hid_app/views/hid_mouse_jiggler.c @@ -2,8 +2,6 @@ #include #include "../hid.h" -#include "hid_icons.h" - #define TAG "HidMouseJiggler" struct HidMouseJiggler { diff --git a/applications/external/hid_app/views/hid_numpad.c b/applications/external/hid_app/views/hid_numpad.c index bd4788b83..b342bb38d 100644 --- a/applications/external/hid_app/views/hid_numpad.c +++ b/applications/external/hid_app/views/hid_numpad.c @@ -3,7 +3,6 @@ #include #include #include "../hid.h" -#include "hid_icons.h" #define TAG "HidNumpad" diff --git a/applications/external/hid_app/views/hid_tikshorts.c b/applications/external/hid_app/views/hid_tikshorts.c index 0e156fdf0..b494245cf 100644 --- a/applications/external/hid_app/views/hid_tikshorts.c +++ b/applications/external/hid_app/views/hid_tikshorts.c @@ -2,8 +2,6 @@ #include "../hid.h" #include -#include "hid_icons.h" - #define TAG "HidTikShorts" struct HidTikShorts { diff --git a/applications/external/ir_remote/application.fam b/applications/external/ir_remote/application.fam index 1107ae45d..e0af447c5 100644 --- a/applications/external/ir_remote/application.fam +++ b/applications/external/ir_remote/application.fam @@ -10,5 +10,4 @@ App( ], fap_category="Tools", fap_icon="ir_10px.png", - fap_icon_assets="images", ) diff --git a/applications/external/ir_remote/images/ButtonDown_7x4.png b/applications/external/ir_remote/images/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67d1c23c0bb5d765e8d2aa04b9b5adec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE diff --git a/applications/external/ir_remote/images/ButtonLeft_4x7.png b/applications/external/ir_remote/images/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d43247083aa705620e9836ac415b42ca46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a diff --git a/applications/external/ir_remote/images/ButtonRight_4x7.png b/applications/external/ir_remote/images/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/ir_remote/images/ButtonUp_7x4.png b/applications/external/ir_remote/images/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/ir_remote/images/Ok_btn_9x9.png b/applications/external/ir_remote/images/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da2049f12f7b25f96b11a9c40cd8227302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3605 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 #include #include -#include +#include #include #include diff --git a/applications/external/metronome/application.fam b/applications/external/metronome/application.fam index 8acd4b3b0..6c964078e 100644 --- a/applications/external/metronome/application.fam +++ b/applications/external/metronome/application.fam @@ -8,7 +8,6 @@ App( ], fap_icon="metronome_icon.png", fap_category="Media", - fap_icon_assets="images", stack_size=2 * 1024, order=20, ) diff --git a/applications/external/metronome/gui_extensions.c b/applications/external/metronome/gui_extensions.c index f33c5f651..18494098e 100644 --- a/applications/external/metronome/gui_extensions.c +++ b/applications/external/metronome/gui_extensions.c @@ -1,6 +1,6 @@ #include #include -#include +#include //lib can only do bottom left/right void elements_button_top_left(Canvas* canvas, const char* str) { diff --git a/applications/external/metronome/images/ButtonUp_7x4.png b/applications/external/metronome/images/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/mfkey32/application.fam b/applications/external/mfkey32/application.fam index 75fa40bf6..33d932ce9 100644 --- a/applications/external/mfkey32/application.fam +++ b/applications/external/mfkey32/application.fam @@ -9,7 +9,7 @@ App( "storage", ], stack_size=1 * 1024, - fap_icon="mfkey.png", + fap_icon="images/mfkey.png", fap_category="NFC", fap_author="noproto", fap_icon_assets="images", diff --git a/applications/external/mfkey32/mfkey.png b/applications/external/mfkey32/mfkey.png deleted file mode 100644 index 52ab29efb92bf5aa7c790b8043d2d07a7e74704d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9060 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 T2_+RDfDS|^SsNGcaF6&O#taN} diff --git a/applications/external/mifare_fuzzer/application.fam b/applications/external/mifare_fuzzer/application.fam index 13e1a72c9..a186559f2 100644 --- a/applications/external/mifare_fuzzer/application.fam +++ b/applications/external/mifare_fuzzer/application.fam @@ -9,7 +9,6 @@ App( ], stack_size=4 * 1024, order=30, - fap_icon="images/mifare_fuzzer_10px.png", + fap_icon="mifare_fuzzer_10px.png", fap_category="NFC", - fap_icon_assets="images", ) diff --git a/applications/external/mifare_fuzzer/images/mifare_fuzzer_10px.png b/applications/external/mifare_fuzzer/mifare_fuzzer_10px.png similarity index 100% rename from applications/external/mifare_fuzzer/images/mifare_fuzzer_10px.png rename to applications/external/mifare_fuzzer/mifare_fuzzer_10px.png diff --git a/applications/external/mousejacker/application.fam b/applications/external/mousejacker/application.fam index 3b8ae7104..4047f3bcb 100644 --- a/applications/external/mousejacker/application.fam +++ b/applications/external/mousejacker/application.fam @@ -11,7 +11,6 @@ App( order=60, fap_icon="mouse_10px.png", fap_category="GPIO", - fap_icon_assets="images", fap_private_libs=[ Lib( name="nrf24", diff --git a/applications/external/mousejacker/images/badusb_10px.png b/applications/external/mousejacker/images/badusb_10px.png deleted file mode 100644 index 037474aa3bc9c2e1aca79a68483e69980432bcf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576 zcmV-G0>AxEX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu`HWXkp{t5s906j@WK~xyijZi@f05Awj>HlAL zr$MwDdI>{Qf+U53tOUR#xOeyy)jcQo#JNRv)7r6DVVK|+*(cmT+R+EbO(O#X#REG4 O0000<>&pI=m5)cB{fFDGZlI8yr;B3<$MxhJ?+;A4eL&#) z0Ra}bue?07WhLz78x$BO|L3mq-MMxdP^D^#YeY#(Vo9o1a#1RfVlXl=GSoFN)ipE; zF*LF=Hn1|b&^9ozGB8+QQTzo(LvDUbW?CgwgE3G~h=Hk #include #include "mousejacker_ducky.h" -#include +#include #include #define TAG "mousejacker" @@ -112,7 +112,7 @@ static bool open_ducky_script(Stream* stream, PluginState* plugin_state) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( - &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badusb_10px); + &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badkb_10px); browser_options.hide_ext = false; bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); diff --git a/applications/external/multi_fuzzer/application.fam b/applications/external/multi_fuzzer/application.fam index 6cd7969e1..9a765fd08 100644 --- a/applications/external/multi_fuzzer/application.fam +++ b/applications/external/multi_fuzzer/application.fam @@ -11,7 +11,7 @@ App( "notification", ], stack_size=2 * 1024, - fap_icon="icons/ibutt_10px.png", + fap_icon="ibutt_10px.png", fap_category="Tools", fap_private_libs=[ Lib( diff --git a/applications/external/multi_fuzzer/fuzzer_i.h b/applications/external/multi_fuzzer/fuzzer_i.h index 5b58e59d8..c8308adbd 100644 --- a/applications/external/multi_fuzzer/fuzzer_i.h +++ b/applications/external/multi_fuzzer/fuzzer_i.h @@ -21,6 +21,7 @@ #include #include "fuzzer_icons.h" +#include #define FUZZ_TIME_DELAY_MAX (80) @@ -52,4 +53,4 @@ typedef struct { FuzzerWorker* worker; FuzzerPayload* payload; -} PacsFuzzerApp; \ No newline at end of file +} PacsFuzzerApp; diff --git a/applications/external/multi_fuzzer/icons/ibutt_10px.png b/applications/external/multi_fuzzer/ibutt_10px.png similarity index 100% rename from applications/external/multi_fuzzer/icons/ibutt_10px.png rename to applications/external/multi_fuzzer/ibutt_10px.png diff --git a/applications/external/multi_fuzzer/icons/125_10px.png b/applications/external/multi_fuzzer/icons/125_10px.png deleted file mode 100644 index ce01284a2c1f3eb413f581b84f1fb3f3a2a7223b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bWZjP>yH&963)5S4_<9hOs!iIaU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a diff --git a/applications/external/multi_fuzzer/icons/ButtonRight_4x7.png b/applications/external/multi_fuzzer/icons/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/multi_fuzzer/icons/Ok_btn_9x9.png b/applications/external/multi_fuzzer/icons/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da2049f12f7b25f96b11a9c40cd8227302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3605 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 TFC+3}9cE|kU{!4CefEC<&8td2 diff --git a/applications/external/music_beeper/application.fam b/applications/external/music_beeper/application.fam index bdd2980bc..a5af4f37c 100644 --- a/applications/external/music_beeper/application.fam +++ b/applications/external/music_beeper/application.fam @@ -12,7 +12,6 @@ App( stack_size=2 * 1024, order=45, fap_icon="music_10px.png", - fap_icon_assets="icons", fap_category="Media", ) diff --git a/applications/external/music_beeper/music_beeper.c b/applications/external/music_beeper/music_beeper.c index f3b4828d3..ed3021fa4 100644 --- a/applications/external/music_beeper/music_beeper.c +++ b/applications/external/music_beeper/music_beeper.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/applications/external/music_player/application.fam b/applications/external/music_player/application.fam index 496d7dcc8..c1379b8c5 100644 --- a/applications/external/music_player/application.fam +++ b/applications/external/music_player/application.fam @@ -10,9 +10,8 @@ App( provides=["music_player_start"], stack_size=2 * 1024, order=45, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Media", - fap_icon_assets="icons", ) App( diff --git a/applications/external/music_beeper/icons/music_10px.png b/applications/external/music_player/music_10px.png similarity index 100% rename from applications/external/music_beeper/icons/music_10px.png rename to applications/external/music_player/music_10px.png diff --git a/applications/external/music_player/music_player.c b/applications/external/music_player/music_player.c index 28127a575..9ef84d601 100644 --- a/applications/external/music_player/music_player.c +++ b/applications/external/music_player/music_player.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/applications/external/nfc_magic/assets/DolphinCommon_56x48.png b/applications/external/nfc_magic/assets/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/applications/external/nfc_magic/assets/DolphinNice_96x59.png b/applications/external/nfc_magic/assets/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 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 IrsqTc1+$%)k^lez diff --git a/applications/external/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h index 88bc5706f..f0c18db1a 100644 --- a/applications/external/nfc_magic/nfc_magic_i.h +++ b/applications/external/nfc_magic/nfc_magic_i.h @@ -30,6 +30,7 @@ #include #include "nfc_magic_icons.h" +#include #define NFC_APP_FOLDER ANY_PATH("nfc") diff --git a/applications/external/ocarina/application.fam b/applications/external/ocarina/application.fam index e5d27c1bb..e6ba5b787 100644 --- a/applications/external/ocarina/application.fam +++ b/applications/external/ocarina/application.fam @@ -7,7 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, order=30, - fap_icon="icons/music_10px.png", + fap_icon="music_10px.png", fap_category="Media", - fap_icon_assets="icons", ) diff --git a/applications/external/ocarina/icons/music_10px.png b/applications/external/ocarina/icons/music_10px.png deleted file mode 100644 index d41eb0db8c822c60be6c097393b3682680b81a6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIasA%~WHIdey2}7aaTa() z7Bet#3xhBt!>l_x9Asc&(0a_=e)W&n8K5!-Pgg&ebxsLQ0Ao%f>i_@% diff --git a/applications/external/music_player/icons/music_10px.png b/applications/external/ocarina/music_10px.png similarity index 100% rename from applications/external/music_player/icons/music_10px.png rename to applications/external/ocarina/music_10px.png diff --git a/applications/external/passgen/icons/Ok_btn_9x9.png b/applications/external/passgen/icons/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da2049f12f7b25f96b11a9c40cd8227302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3605 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 TFC+3}9cE|kU{!4CefEC<&8td2 diff --git a/applications/external/passgen/passgen.c b/applications/external/passgen/passgen.c index 12cdc10fb..be6656f7a 100644 --- a/applications/external/passgen/passgen.c +++ b/applications/external/passgen/passgen.c @@ -5,6 +5,7 @@ #include #include #include +#include #define PASSGEN_MAX_LENGTH 16 #define PASSGEN_CHARACTERS_LENGTH (26 * 4) diff --git a/applications/external/picopass/application.fam b/applications/external/picopass/application.fam index c5087b804..b14427f2e 100644 --- a/applications/external/picopass/application.fam +++ b/applications/external/picopass/application.fam @@ -18,5 +18,4 @@ App( name="loclass", ), ], - fap_icon_assets="icons", ) diff --git a/applications/external/picopass/icons/DolphinMafia_115x62.png b/applications/external/picopass/icons/DolphinMafia_115x62.png deleted file mode 100644 index 66fdb40ff2651916faed4a2ae1d564cafdbf7bcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/applications/external/picopass/icons/DolphinNice_96x59.png b/applications/external/picopass/icons/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q<>&pI=m5)b(dHL6nbwD9yPZ!4!j_b)k${QZ;XFmLn zjqNsD+YPq1J8W%_*aXBie!OR3*tC!PwU_7Q9H4U564!{5l*E!$tK_0oAjM#0U}UIk zV5)0q5@Kj%Wo%$&Y@uynU}a#izM}XGiiX_$l+3hBs0L%8o)7~QD^p9LQiz6svOM}g O4Gf;HelF{r5}E+GUQp8j diff --git a/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png b/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png deleted file mode 100644 index e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ diff --git a/applications/external/picopass/icons/RFIDDolphinSend_97x61.png b/applications/external/picopass/icons/RFIDDolphinSend_97x61.png deleted file mode 100644 index 380a970d9004cba5520560fd9aa24aa42924e2a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ diff --git a/applications/external/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c index 92fc86c73..a050e4a2c 100644 --- a/applications/external/picopass/picopass_device.c +++ b/applications/external/picopass/picopass_device.c @@ -2,7 +2,7 @@ #include #include -#include +#include #define TAG "PicopassDevice" diff --git a/applications/external/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h index 651ccc010..88c2140ee 100644 --- a/applications/external/picopass/picopass_i.h +++ b/applications/external/picopass/picopass_i.h @@ -26,7 +26,7 @@ #include #include -#include +#include #define PICOPASS_TEXT_STORE_SIZE 128 diff --git a/applications/external/pocsag_pager/images/Fishing_123x52.png b/applications/external/pocsag_pager/images/Fishing_123x52.png deleted file mode 100644 index 1e365de8f4d9c5aeb0b3b11097a2e65eb3451b99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 966 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 a9|QyVQ~@!E@+ey_4zMz}H7hmoyzn0`d`!~- diff --git a/applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png b/applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png deleted file mode 100644 index 3bafabd144864b575144c75b592e5eaf53974566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3606 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 TFC+3}9cE|kU{!4CefEC<&8td2 diff --git a/applications/external/pocsag_pager/images/Quest_7x8.png b/applications/external/pocsag_pager/images/Quest_7x8.png deleted file mode 100644 index 6825247fbeaf98b4d0d9f8aa15d4f2c5f45dcf3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3675 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/applications/external/pocsag_pager/images/Unlock_7x8.png b/applications/external/pocsag_pager/images/Unlock_7x8.png deleted file mode 100644 index 9d82b4daf3c722a5244f537f18b147833c2fb4ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3598 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 MrJY5ondim-0h+K&wEzGB diff --git a/applications/external/pocsag_pager/images/WarningDolphin_45x42.png b/applications/external/pocsag_pager/images/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb444db1739f2ccd030e506e8bada11ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& diff --git a/applications/external/pocsag_pager/views/pocsag_pager_receiver.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c index 64939a956..fd4461cee 100644 --- a/applications/external/pocsag_pager/views/pocsag_pager_receiver.c +++ b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c @@ -1,6 +1,7 @@ #include "pocsag_pager_receiver.h" #include "../pocsag_pager_app_i.h" #include +#include #include #include diff --git a/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c index 4811f3902..91ee69ab5 100644 --- a/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c +++ b/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c @@ -1,6 +1,7 @@ #include "pocsag_pager_receiver.h" #include "../pocsag_pager_app_i.h" #include "pocsag_pager_icons.h" +#include #include "../protocols/pcsg_generic.h" #include #include diff --git a/applications/external/sam/application.fam b/applications/external/sam/application.fam index fa614304e..8a6d70c07 100644 --- a/applications/external/sam/application.fam +++ b/applications/external/sam/application.fam @@ -11,7 +11,6 @@ App( order=20, fap_icon="music_10px.png", fap_category="Media", - fap_icon_assets="icons", ) App( appid="sam_yes", @@ -26,7 +25,6 @@ App( order=20, fap_icon="music_10px.png", fap_category="Media", - fap_icon_assets="icons", ) App( appid="sam_no", @@ -41,7 +39,6 @@ App( order=20, fap_icon="music_10px.png", fap_category="Media", - fap_icon_assets="icons", ) App( appid="sam_wtf", @@ -56,5 +53,4 @@ App( order=20, fap_icon="music_10px.png", fap_category="Media", - fap_icon_assets="icons", ) diff --git a/applications/external/sam/icons/music_10px.png b/applications/external/sam/icons/music_10px.png deleted file mode 100644 index d41eb0db8c822c60be6c097393b3682680b81a6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIasA%~WHIdey2}7aaTa() z7Bet#3xhBt!>l_x9Asc&(0a_=e)W&n8K5!-Pgg&ebxsLQ0Ao%f>i_@% diff --git a/applications/external/signal_generator/application.fam b/applications/external/signal_generator/application.fam index 002651da4..a7b93b817 100644 --- a/applications/external/signal_generator/application.fam +++ b/applications/external/signal_generator/application.fam @@ -8,5 +8,4 @@ App( order=50, fap_icon="signal_gen_10px.png", fap_category="GPIO", - fap_icon_assets="icons", ) diff --git a/applications/external/signal_generator/icons/SmallArrowDown_3x5.png b/applications/external/signal_generator/icons/SmallArrowDown_3x5.png deleted file mode 100644 index 1912e5d246268d75a20984bdc8b996d503f3d166..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3592 zcmaJ^c|26>|35C-x5^UI9YaXWW@{#6nHgKQFf!6&%x2Oo#?)9!BwM;9Wo@KIb`_J-tPDJ$FJ{sopYY&`JB)D{aK&a>p5|Uoo!_#RV4uckg>PJ zxe3N?f=5_fSnxjm$+!goB(3RK>|uK>7R2VTsPxkm00?nD~hiA(w8Os#U?fGBt+hg zz1*@k9(vcmw`%2M+s2bV^XZ~Rep!cDjkt7*ouR97xO6^d&-V9`O%09XlMu@YNi8-Y zFJ4C02wc|`0#?J!%=Uw8#9jbGLETc~K#fyo4QzMJrrc*t`Z1yKOF}i=qyrA(;R=9d zNCM_QU}+;1&QH^J2eL%~pH`CZ1aQ~@@X@*Ou^R~Iucn6z0p8a&6os;r0MJfKEDrEH z2o!Z3xoiy(V1NSEp#cf>8vrnSPpTd8@F`H!E-zIIh)V-7*Vw3ifJi9d)2yi(1YAl7 z6l@ke&HmV5B0sGs$W(f%S%ntTI>KArAVAF16S7CQ-ClXWf(h{#VumH8E;wBU5n&|v ze(?Sn!b0U5xq_WSf#8XS&Zm{>QAm}Mf zxb6r@z-3%nMC5?uFxU3I+S|2B{xGJ$CTu=t3_Lt#E)<$%kawIU{MA86p1`g7umS)J zm8{x#y5hp&ev#uHyv=!wb=&N{KseR@S^xl?z-dA7EoBx>;sAilj?jB(rM6VNOTR{R zckQ;}TB+|oCYLZ;4RsiKj3haHH^*mR(M61IblXF9Js;>hOLe0fSHI|Fwk)L1i+`6(J#F)hxb~s4*BT2kQkYsEJce{)S zdDy8hpgF%FV~*K8PdeBPATEB7uCj$+k0^CTzmtA~t;jP~y<~Go>MfZI&q!3t&V0*x ztct#3a(nu1p`YAfqB*t+R`Y3>m|??d7^JZt^XP!SL^7%M5x7XYuu=8lks{&BxMfnu zBc8~P2N;MBHO#M{p!K_uJ)xc54}JACxea5WeJErvpyTb9k)%eEXjbyL=Jw z7=oR?X77%~olyDESZsr-){ZzVLZ{;DFZPe_;k$Np*>o}8G-velGmY$2HIrWtlKo4? zkk|D=`{ZuPVt=^-Ku`dek=3`pSaJrkKEYfoch+Yt98cq zQ|c$-C7!fQv|?maEKOG>bC=jInhI~%gEYtcD&6raO?a3o{7c$&x?DQTgP>QgcTO>> zMe@d>8`?M2^q~0sg8K!d1yUZ19AHz3Lt7U9k6Dvmc$DsA>dBkyOfp^fmlt3Zu_N7&mA?Y8yCrRwV(R#%q>4_nyFE6)*~nd?Hy)eNnqV|C8t-b0YHMgaIDK}S z%W!k5xWDiILC1rRO>J-5?zHu$8)u^7eTeDI>CC>&v8O&qgO2K#=aoOB*q2Toz3(+w zUd4<$iuB4McpN=mW>d^B-rHMQT$#H)x57EuxiG7jR{!vi^4I10PgNdH^@|RblrzfD z6KTH6w5P91>gSTHlg~dt|JyoROeSVPwov`3dRX9NjsofkYBZz$=A6a(S4$}~P#U2_ zzN6o8qI_rTz6LtqJ+s@ErcA2{j9iS3k8`-#3Q0AGWU4ieG*?d^;w}dq9}nqT=4X~= z*3IS(J(x3@qtC?*-+E(oYhRX^Vc^^PX6$>{sZI;2TQ^|-V?|*uSeFRelW9#T37X_t z-1qQl4zFN^IInE})tqx{!hFKabQCe_b@GjA&C}+mtuFPftdmh=*bADQ@N544YO%)3bXt2- zJ6$&FaM-8bw_?PP#Q6F!X`QH;D9>n%1a>SzwG*Cd%{wZ|N4YAk$Wmk)~c^OESWA1;#AJy&C6Dy@rJgG0+;#!a?g<1RC zX5W;x3|%$7Ie%+&c1PWg@oVKd(GH#l>V%KgMW>LZW&y!Nk`s#C_D3HPEi!v{xm=IY z<5D>5nOYK7tsUazA913#d>l2%@|I00Nd1^9%aj=yd@M6|!_pfKwY3k5Z zn2d!Cn@snNHE&<<=Pqx|J9|HmhJ3dj`c>|xk(pQUp+)>_`rypP?qu3R#})n!{`oM- zpTj;wcgjPjN$q2&Iu2dfUYA6t0FT__!z+UfbsGvfj3B;zypv)M*+ zw@Xvy&B~0Dievs2b0O7FLa8e=YFVc3BTLo6e<*GC_GBT^Bh`x`td&0qjUjkA?TfaR2=9g;O=W?8VMu+ZEBM$c~Mq$1%v)l;rgS&|8a`obQpwX zaVQ{D2;6`KgTX+iNC<^YMEDv~i6ngx0)~J?;ey-L0B(vx7^2`v(BBtWV30$mqTFyc zf14Am&|p6zoJIbf9{LX zPx=1Fl7H@t@lUZ(fiuvp+Wwzf{}2fpXlwdU^9mOKv_FL@=y{Hyx%oF${u^n* BDRuw= diff --git a/applications/external/signal_generator/icons/SmallArrowUp_3x5.png b/applications/external/signal_generator/icons/SmallArrowUp_3x5.png deleted file mode 100644 index 9c6242078d3bcc9a93eb55b6b5f358720ee6662c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7976 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 #include -#include +#include typedef enum { LineIndexChannel, diff --git a/applications/external/simonsays/images/Connected_62x31.png b/applications/external/simonsays/images/Connected_62x31.png deleted file mode 100644 index eeaf660b12ea4647154c8d616b468a3098203356..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3765 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 diff --git a/applications/external/simonsays/images/DolphinWait_61x59.png b/applications/external/simonsays/images/DolphinWait_61x59.png deleted file mode 100644 index 423e079199b00df0d910981caf8944cbaf8ee67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S diff --git a/applications/external/simonsays/images/WarningDolphin_45x42.png b/applications/external/simonsays/images/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb444db1739f2ccd030e506e8bada11ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& diff --git a/applications/external/simonsays/simon_says.c b/applications/external/simonsays/simon_says.c index f537cb364..ad6a68129 100644 --- a/applications/external/simonsays/simon_says.c +++ b/applications/external/simonsays/simon_says.c @@ -13,6 +13,7 @@ /* generated by fbt from .png files in images folder */ #include +#include #define TAG "Simon" // Used for logging #define DEBUG_MSG 1 diff --git a/applications/external/spi_mem_manager/images/DolphinMafia_115x62.png b/applications/external/spi_mem_manager/images/DolphinMafia_115x62.png deleted file mode 100644 index 66fdb40ff2651916faed4a2ae1d564cafdbf7bcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/applications/external/spi_mem_manager/images/DolphinNice_96x59.png b/applications/external/spi_mem_manager/images/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q8gyJRa{_+4UIP z$!=1i2t*O^Q!1)9DGybMBGF3a6SW|L6h5LVfJD{LegT4thW>zPQHvPws{yrXept!t zv3=&;bMHMf^XAC#V8_NS8##{a$PeX4*}aQh-5b`i|CfLH7_-|w{?PLw$KCsIeBHqv zeQy)T-TjJN7>~xyod%|r1hT0`619rY&>XjYN6klgf<(MUimsOtE`R=|z`J%v*qt1RpF9hwQq*vxPN&rD$57IyUT+iM0RsE`QpwMy9wjao*i^BQa%zm^2P4v8i*LT?<9 zA2&z%EDZ>+C4h(lkolCJfSRgm;7MKvGLS%0g0cuT1E>Z}@y(yWq6M~NjOGTKvDi~a zC`FNPNK&<0O;nWx4T=)fbzK6oB+DX0h~cysp_=H0T`h(j331^1kxM;3W<(a9j4}dK z+DM_|w`skwSteF6sfK(BCP1803uv0FLo1awI*j_KSd^yTn-YhGX`e`=B&3r8CjC>y zi@I9D{1T05SfaPk*8co2g*I*n^e2OIy*xISNSRa^cgV1?uFp5J0YMQB3Y3;xjT&i1 zyC$M##e?pUVhLRKj&_L)9?Wld>u%HCq;SStTN}Y*kccThRe>U`i)-U2J}i z;>oxY@%)BuZHgI3yPAgMs43vsNJP47iHfQ!qLpHl$)u9T2onYB=@#3rz-223l~=OH zs_a-*O3@VL0MW4s7KyDoOqHyNDzSA4a2n~Dsk#w2OUpDcsm-dZtbCu(W=8_*xMlVs z93AZA^Zi*3>Y66X2`KP3HXIsM5Hp%vK}90@UNN>klflv*azobR>E=QjBQG^aWtXqJ z(?B?06d3`>ZXmYMeC^(>%xg-hL0c^mM!Jei8nBQ$Q56NGx5!#@TNg^V5+9y5Z&x22##B&{B?u64ye+M3KZ=XlsY71%@jTp=Dy zHDISkcY5&@J8>5Bx!%I~{^i3@-@m}$cNaW+{nKk_j+x(U>F+k}c?6!^@X+fADi3lO z_rLKG{`yum;ZU>ayk!Z+q>VzZOn^bqQ>?UO0Drz@_TNg$rjt zNcQEpt?wS&gMaKe^8V2SorGL{ZtT%R?yPd~`n9FG(}zAOOPl5My;t&Z3(*F9I?g}- zvp)ag@#QCe{o%9hrGrn+&dpu9`rFcD;MqUV>^-?JG4|Gpeamy-PL6)jzu5T`{Cd7~ fwr}T&J8Rt1;5!ej|9##1_yo=O59dzx?S1thQe1#H diff --git a/applications/external/spi_mem_manager/spi_mem_app_i.h b/applications/external/spi_mem_manager/spi_mem_app_i.h index 285ca66d2..ea7cad8cb 100644 --- a/applications/external/spi_mem_manager/spi_mem_app_i.h +++ b/applications/external/spi_mem_manager/spi_mem_app_i.h @@ -19,6 +19,7 @@ #include "scenes/spi_mem_scene.h" #include "lib/spi/spi_mem_worker.h" #include "spi_mem_manager_icons.h" +#include #include "views/spi_mem_view_progress.h" #include "views/spi_mem_view_detect.h" diff --git a/applications/external/subghz_bruteforcer/application.fam b/applications/external/subghz_bruteforcer/application.fam index cfd354f48..6a3fdef3f 100644 --- a/applications/external/subghz_bruteforcer/application.fam +++ b/applications/external/subghz_bruteforcer/application.fam @@ -6,7 +6,6 @@ App( requires=["gui", "dialogs"], stack_size=2 * 1024, order=11, - fap_icon="images/subbrute_10px.png", + fap_icon="subbrute_10px.png", fap_category="Sub-GHz", - fap_icon_assets="images", ) diff --git a/applications/external/subghz_bruteforcer/images/ButtonDown_7x4.png b/applications/external/subghz_bruteforcer/images/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67d1c23c0bb5d765e8d2aa04b9b5adec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE diff --git a/applications/external/subghz_bruteforcer/images/ButtonUp_7x4.png b/applications/external/subghz_bruteforcer/images/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png b/applications/external/subghz_bruteforcer/images/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Qs09sk=B|BJW%k>>fR tFX0r{ATVKKadYB%@iyyxiGP0@l^IrYh`!(``e6go>*?y}vd$@?2>@Gm8_WOz diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_02.png deleted file mode 100644 index 2dff1c031d4748d04edb7ae1d0d3df15121dd0d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlx~Gd{2*>s09sk=B|BJW%k>>fR tFOhVBs09sk=B|BJW%l2-^} mb`v-@PoJm!p244=%s0AOG7K`~Ecu&S~so Xa21wb&|;kR2Bgf>)z4*}Q$iB}IfW9v diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_05.png deleted file mode 100644 index 79b2bc97252d410ee512aa13ec302299bd320dac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nln5Ts0AOG7K`}VzIR8)A( aFUG=PCM_*c^)aXfq|(#X&t;ucLK6T{krEF8 diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_06.png deleted file mode 100644 index 8fce0c44d6ea51958a29a3a5f908064554f420e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^d?3sSBpA$%1!{nlqNj^v2*>s0AOG7KJMP_2-0{Ed m;VCHrCZl_I5`TPV=VfFNUoH6|dD-IGAWfdGelF{r5}E+qb{hZy diff --git a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_rate b/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_rate deleted file mode 100644 index e440e5c84..000000000 --- a/applications/external/subghz_bruteforcer/images/Sub1ghz_14/frame_rate +++ /dev/null @@ -1 +0,0 @@ -3 \ No newline at end of file diff --git a/applications/external/subghz_bruteforcer/images/sub1_10px.png b/applications/external/subghz_bruteforcer/images/sub1_10px.png deleted file mode 100644 index 5a25fdf4ef1c6cf53634aa74675001a3e8c85b7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)cB{fFDGZlI8yr;B3<$MxhJ?+;A4eL&#) z0Ra}bue?07WhLz78x$BO|L3mq-MMxdP^D^#YeY#(Vo9o1a#1RfVlXl=GSoFN)ipE; zF*LF=Hn1|b&^9ozGB8+QQTzo(LvDUbW?CgwgE3G~h=Hk #include -#include "subghz_bruteforcer_icons.h" +#include #include diff --git a/applications/external/subghz_playlist/application.fam b/applications/external/subghz_playlist/application.fam index 6f74f2b55..d0d647ae2 100644 --- a/applications/external/subghz_playlist/application.fam +++ b/applications/external/subghz_playlist/application.fam @@ -8,5 +8,4 @@ App( order=14, fap_icon="playlist_10px.png", fap_category="Sub-GHz", - fap_icon_assets="images", ) diff --git a/applications/external/subghz_playlist/images/ButtonRight_4x7.png b/applications/external/subghz_playlist/images/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/subghz_playlist/images/sub1_10px.png b/applications/external/subghz_playlist/images/sub1_10px.png deleted file mode 100644 index 5a25fdf4ef1c6cf53634aa74675001a3e8c85b7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)cB{fFDGZlI8yr;B3<$MxhJ?+;A4eL&#) z0Ra}bue?07WhLz78x$BO|L3mq-MMxdP^D^#YeY#(Vo9o1a#1RfVlXl=GSoFN)ipE; zF*LF=Hn1|b&^9ozGB8+QQTzo(LvDUbW?CgwgE3G~h=Hk #include -#include +#include #include #include diff --git a/applications/external/subghz_remote/application.fam b/applications/external/subghz_remote/application.fam index e2229e505..42ea9d358 100644 --- a/applications/external/subghz_remote/application.fam +++ b/applications/external/subghz_remote/application.fam @@ -13,7 +13,6 @@ App( ], stack_size=2 * 1024, order=11, - fap_libs=["assets"], fap_icon="subghz_remote_10px.png", fap_category="Sub-GHz", ) diff --git a/applications/external/subghz_remote_configurator/application.fam b/applications/external/subghz_remote_configurator/application.fam index 5589349a0..4604a509b 100644 --- a/applications/external/subghz_remote_configurator/application.fam +++ b/applications/external/subghz_remote_configurator/application.fam @@ -11,6 +11,5 @@ App( order=50, fap_description="File Editor for the SubGhz Remote app", fap_category="Sub-Ghz", - fap_icon_assets="icons", - fap_icon="icons/subrem_10px.png", + fap_icon="subrem_10px.png", ) diff --git a/applications/external/subghz_remote_configurator/icons/DolphinNice_96x59.png b/applications/external/subghz_remote_configurator/icons/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonLeft_4x7.png b/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d43247083aa705620e9836ac415b42ca46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonRight_4x7.png b/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonUp_7x4.png b/applications/external/subghz_remote_configurator/icons/remote_scene/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/Ok_btn_9x9.png b/applications/external/subghz_remote_configurator/icons/remote_scene/Ok_btn_9x9.png deleted file mode 100644 index 9a1539da2049f12f7b25f96b11a9c40cd8227302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3605 zcmaJ@c{r5q+kR|?vSeS9G2*Q(Gqz$f_GQ#q8r!JE7=ytqjlqnNNGaK}Wlbolp-q`& zs|bxHiiEP0&{#s&zVZIv-rx7f*Y_O9^W67+-RF5;*L_{ra~$^-2RmyaK{-JH0EBE1 z7AVdru>JD$aK0bym%#uaXpT2Gcd#)x2azcxAABGV0BC)Aj-lw(6)B^^6`Y8RS?}DV z%)ko(See1!Eb3M$dL6)A6csaRjExg?k&xVzi*Rm;?iNJk#f=mkVEUR~jXN3dd|Lmz z;y}sMh%ol-?E1&`>dD;6jdps6NYoxN)s%@sf4~40YY6LAOtMEbwA4g#OCpANL823^ zSH66W05Hcxr$tg98gFntAOYL}xm$C;Skv&Ym?{TVR{)d(41vWacX1`7fM!jnW(lBK z26*WB#9I(Z1Ast!xEUC@Cj`v=urcBTdP`FWq=DYTy`}s>0vC{VzHdNRvxNFy}ir1|g=xDsrFP&l1P<-Sv zXLqYVYz{b^ZIV@1Ulg->7DEgvM*Min&Y8{8QW! z$_pA434?^wCTq$4%^>Zo8&|8XwbCv;KEd;WJJ{s;T}8R8Zwi7ssk$QWQ5l5+opKfX z;8D*COFEB#4W^*FIrRU%PDSc?B(}+9ZV?N9(yH>0uSnM?xg!>+>;e z{{7tXQQ|ZFXD*7q3XD!pwnih-=66+Qlqtl9;N-D|PHoI&B5d8>^V#i{mE>V0gQgu3+(DG%B z|8W!pl$lbQERt-0eZA%NSfvE4F>VAYP`DpeoF;Zm4`)2id;6xgSysWl6K$pWANcRZ z!ETRXKIU9G=@9lEB?<{ivj7!8FE9WN;qoo2Lr0#c@DmcF=JzU<73PmM3 zbe!-gs`c26Uc(AKz7%U!a0yZ5gsprdo1i51MjJPeHtV6d@Jy=*+_3dJ^>}p#8N#kPK_4t?hltq>u=?m+t z?em(Y%u3Bp_pyV?c_w-4c}p+?Y$aHr>TuPGs@SUj;Er!b@3GVLDS@T8OTts1JFS-p zKZ=&5zp;DRor*`Gy8MTeWdpVJv2(4-*slRM@XXG+i^F&Ku>7i08vKenZHoS4s(!!h zJE}*MHu7PR_IfdNzu*P}3^87K?f&A1;>NMsgKcR6**;aB74NC7tR(NB?{dHT-9QhXa*KoG!kGU1}$l2D>ypo)fSBuG$ zkTW4?+|I1m?6ZH8tD4^fB{cUpoEoZOo%4hl!EtNtQ#?j*jJR)x-Mn0TrxrX2uT_rh ziOh=Jxsktqbd9x{^s{c5z92Pk$LGoQl53o+=7QXXCp-Z>io998w|DCCCGfr20oiRN zX|`KH$W4)wN~)J$kYB~>4EU;NcS^qH&yzeUzXokpMegg_lX$6ve^4}%bY~Sg)%uJ- zZpb$p4x^GS5d{XJP=STbfpHV`58UBH& zKFg&BgS6bV+#-|^KBGeIBee2B zrM-`uTB^_(eS+{-KK1h3l`-Yjpv8X4z*uBwQ3a~pL0Ae2xvNGyC3A|#MARToe$W~8 z+4{DsyenENye9df1M}gNUM9_Leh6G=`9exL-cdSKQ_CGyEdZ3W5uoR!Lb^D)9!bd=7h@R=M%=|JqX9XP;Z6# zFD15Bw7qTP(ZlG?o@#x@=wG;XxM(>n@4P$9WwY#lW$h=`zMi_zq30HbV-zHheqpE0 zR6kXtxdzl&Ml2D#zDIvflJkb*e zIAI?GMjp?JBK76WW`{l{pFAY|%5?nYUxRnT&y6~Kz19AD;C0(z*7?dM{%HhVtqWEc z%+M$z6u@uQu)kg_%2PO_U|n1JE0V1>iVbekOLEOG$U6X^Umc519WC)L$t%`#Di0$ zY1|5H*440_`onhmXeayq`8EIg?x2r9KWe()q}QayqCMEC?c4meb4}#i`HHPaxO&3SPtSVKj@ND?Y+-@R`CDnf-d`T>vTn8RR<=@3 zNXk=Gloyh#S@3R89WHrXBHr;f(&ZO@I_Uo7;O5Bs@ecGx@7%7{_>Q`Adg&sCeZTYp ztVy{^vAUfOpTDzF*4`h%X0odWn`#uZ4s4igIV^UrVVg?c*{>K)hHq^^RxU2CM;WN> z;oK@^sg`J}BguyvilN{DQ*V+N4rD{X_~KAFj5qyk3(gP#cvSIDXe!zk3B!^InwV{j zCXGPmumQl(m`28618`K37tR+?goD{H>cAkpHyrG$XA89@o8$cOh%gGyG0e^h8y0{y z@CF+jfedLdjsO8i#eispKw=P#1_%GG3**eU%@8o?ZwNI24*pM2Xj=!6If;S;9nsX% zz(S!=&=CVoZ;TfP>*b{m(uQhlL7=)2EnN*L6sBVU)71t2^ME<-DBeCWl!etl&NwSL z*pEsj!yu5*&``}#9ZeF&7oufgU;u$?L$tLuI0%g(I+2Q@X%K^ye=Atvg0K`knTjV7 zLEDNLFH$fS4(5dVpED51|H=}B{>c+3V-OmK4AIhrZlCEl(AM_T0=zuK- zizjYd4*pHCwT0ObgQyrH7H4At2XjO;@px~TsgAA%R9|05PuEIcOUu&SOwUTs^00xK zshI`T;)sF%Z>|Li8%)3vslU12|K;lbk-Oav1Tx371&)Fb!FgLzNCeQ|r-tGG9E;W; z_5R^{|2Y=zKXM_QU?AJI{a>~IZQ?Z0_VnM@jcrt7jKN@*#$ZMzB}>VcEo(xFhBigA zRfKF&B$OpfLSqS8d&l#8dVcR8Z}0is_kGT}&h`CX>-l`{D|W}MM1o0W+qqCz&a@8xmO|M3uh;cln|6OUI z@X7fQ&dki(hqbDStcmq@R)<*FE(x{7@jPF^02^V5=v9ihMb|f1hw)0IhxkF_<1H_} z1sVWgmXE~@Wjrum=ebV>cmZ0s_CATm;a}mEc52Q5C=nO}OHAzGNx%Y4+73-pK+|sE zf&F7oVIUa*{8{JBz(BDGF#W^YNC4<9N*a&_dh_-a2?DV^K)SlsK3W zOCXnR0@miQE9D7uc?!4U4XYLag5q!qVkYiDSh|^JD*)2x1yFk>+xS2jzFcTm?NE^$ zEusR=1Jt#ow51*G(vhl2c`F}0KRYy{Jo3{2p&4FwzqpssC^#!EQ$-Rz!G~$z2>|jd zoi8@^jT0uuM~BC~Cj2=+8uB*%W~pE!<+;Jls%yObfcUWvPM_P@SPvhqk>^2RtzXee zpw9{L8C-GI=@-g9A^bLEC5ENHZn8J$mR*yf;vV50J7!cpZdF6S#2Ee38Kw@!gf4MU zH~T|ofioE<=_Pgf;Tvc0l%P^<+(Zk%8H}<#p|aT+abY8Ff9Htq!&92lSLbk7D(t{E zjjU(bM04fllo5%^3-CFm)D5AeU=e^FXGmfr{&k_>d3a+)aa}=xN$7&sHTfNh zfVj6VoV5%9Nwq8SCK^0ITUx;v0I2%9`_$cJSLF_4$)r9^g5d7-;)ha7k^2JBT`QGyenmoI!B!BgFZa^nPSIjjmHP5e8zHBct z>}g(M=h3f$4B-6LI6_z_Ow{YzNBpU4Q5No3aPn%6GK4Xlo>ROYK@oQ-NLryT2hS1Q z#~TwSIW2hlviM8?O9=^9I1CPTS9MyYOrlcISt$H6?B!qJq`S6dsv#09^-K@M!vvfq zTkX5@UgaFs(|?Idx+S6ai8fy!JtnNIngF-nVeN7Z`Pkld>>sQwike&!d8m z!q}j+#PS5O1l#Lt&96qwr4S9#BN(B)eb|Czi6eSM<1zl*H{oXKxy8rZigMly7Dpp) zp0Fn82H8REqlzST12a_HGG$OL1zP#tZ!<{Vq-7t-B%@O3Q}|wsw6|$peqXmwPE3aX z2;M0YDH7g@_E4AelRGO{xVu~ql8(6}@GdRA$pQKSu8{71L+l3C5qDtez&Yu}Hxem` z6sMHXl!;;o#{fs;ZdUOQhkK4<_f9*Vzhmk6*zQY_(0iGC-9?Iy&x;P0wqt{_@pc`@ z-STVPHZH9aL>@&(Sms8e^BoA~ujOKuWnROHb2zgex)a}&rr!-4kCTs9rZGVRYYIV- zvlx3+K(QCwE72=^{7f5<=%`? zl>Nr(;dCk;g6aw$Opx=3=@VvK69`}ZZjdTEXD<)m-PPh#nON_W-)WuySB2X5DDN+N zOj#o@Hg%5&TlX_@z|RoxL4x-e)E6|2*6eRf_RH|9>@0i7Xl-rM9ANjdo2TOpy0iRp z@HHQ+`qyJ4Zd+tE9Emv?)0oNb81R+irnMuZ>Qj# zxib@y+4A&mNoGlXP$qd$YD6l2f7kv+drBW{dVN}WI%9gX}>;*m9J4X{*B+`P?WbMg?R|_dOLt0YC zJHiM_Ty3A^GkR^rdo$!_RLz|l@F22ACA23r zJ#_ne&f4MCmW}wIwZp7=nYm*E?mRDe#(1hP%3plU=f|hSpU!`KyPiO-!1Ha8okr4T zJB37Cl;}y+I@x)J6@t!yw`NAC^c%r!=@Sa8&{j3f-kx1?ksX4A;-S<#E11dFr-IQ# zR{qfyN+h{-*_HEB`wzg2wZ9!NvuB)PENk|#M_tyutK;V4i>^I8-0%C89^}pT^~d@X zrZX$TDvB#EGNXQ4%%w>%B=-r;Tp6wJtw&z@62Lp*pP`dAn&FVjAe4>`?UC_VILOQnvfFm7kYb}KIe$4b!q%cDFE;P^!}5wFhS$flol=(c zKOH`gTJ?#vwG4c%BV>!!U?s|3f2Oiv<7D3Rncea6%ttMQ=SEEn7*BSKM z{I;U9VyY&6%QWwRxn-WhQPHJ&t+6%>}7+sVXoLpPbO)$>wJq(%cIl{yAd4L zao(3TFdv5v@49^(rE$qwH>D`KxrI{ti`zebVW|0ofEcHjRC^^ydT1 zit!QWV{YB&7Fp!JzRyR>-^@&*rwXPh>}8kQ`$wvMO}pPl&We;M%*Bo=xRH;1X50$# zU5slhYkSkir-#>@IobM@-9LZpVE$4__664#r;U<(Fif+aek4~_5ISPczF+n%G&YJPZd_dwhcM)XK$a~zGT6f@?}u{2kzI_J`y5h z5613ABWPopVbs3NnT+5kv=awJUz(1+_-pXaxwBvFzTRqoHSnr!F#SULqTm#orO}0` z4PcuJ1W{iBF zKEPVWtf%|A9(S$wMs?&E%QC)W%H5Wm7d}tKyUte8et?%f`c=!1mLN-!R-v?wVf6iz z)G6X}%Z#&ODdUID)ZtFfy9=wnb=?6Uetyt)y~(QPyq;Dlr>K3}Q=wY9_%mo}MmAXZ zJ7&N&B%XPHy{2#D+xAtlZx_lo9}?@xLqFZ?+&f;mh;c-PqH;Eqf4z$u?y_pN>Q=E- ziH*-zQc@6+ub%g8PZ}Rf89BiysN>^Vu*|b~eTqQIXzO`L8nmD()4q3juuoh;Z zx{Lc)DaWwDG3=>cj9@&S2$*_OJ%}J{GTxhrCE`61Z>_G%gwd42_vIJi(910C^C-NfacQ^Sl-eB6%Xg&U!Xb8ybq}LqdnpiS{AK90(zP z1Ord7u@T6SiQp2Di3~i5N%p4%Aecz--@FL!dP@uegZ@@w_#wgnaSCT+2SQQlM9?8^ zm=*yFg@O(lXcIm0a1R|XJV6r#hr(eH8234(1v`X*>mXnTpnnFKYmn~gg}|Cy{$q~2 zLxO!63>pFg2@Vd{4%X48(!C)t0|NsH6b^yIwYVBu0W1mw&(xv>sQhLyCk7DcBpQQ6 zrGT~=@gCGb1`^D5_CHaOY5&qv0{+PqH)jwgo(6$wL${*(t!QKO|ErS8|7r&?u*CoR z`+pJ#IIw6$2$mQ?4WtvewewQhGDSn6=tMk&N_U`A{eLIY&WFmN2KZ2EAh?b;45V&@ zCy*#xlKp=}Y-|wLlmG^vLLge3Bf(q}Z4${7VPJ`Z>caJO59#RW!C)3BeO)*VWoc## zg<9yK4D<|sW6i0AKr)fS_>J}aFIMl5*sX>j)3}z+iF8sB(bJMnC4>Hs8bSKAFYrI| z{e$)VvoAV-#6q~vK(=c8ziRzk#BHFh<-g6#-Td4BL<+a(>D=bN76lY@FUB@IjDy9m z(5*YN-4s*8oj}&+rVh+L4|neH1o$j1E!71)pl~xe=$Un0lQ15DzW@MRrx z!J?<(q3pT2^$+V+Q`u7+9n4PA$lc;2p&F8~jx^B8sR zx>rCR%LJ^+TUW{z>G}+2%^g|I2L#7s6GcrtfXECp^)>*c&kdOGlW6Awp?LDNx@(7v z-Ko(PNG_nRHMKqcShu!hMe19*kj44oQKivW0gudZG6%)H1;)YI=~>DW$SEFFhY$eB zt#!TJ(l<_=nj9aQ^qvY}e{aa&@}H-Gjg%IKwyLgi^8#Xao$P-1iHTkwY7^JPpj!Xp zlR&>S;5)SDrad5#cS7)O=vpjOf5T*7?k#k)p~7ClUAyK~Ja1KNjl~-M(jK7<$40Dh zzHSYK&I4yMO)^UA3Zgd8;K;$HnE0tyUNb0pbxL`wDf--I{K2kKokyqCrLHbuuT-GH zwoT0Em?R6Omef)4>2t6J#k5U<Kzn-O7ywj#*>mb{iVUie9{?=!&L4Vcx>M+-B&$v&`=vrv zoeVc_hlPpI{yIZ3vmN7+dj)UpNi&sotb_OQK7Gg|m$y4}M6B#3R9|>%Sp3xa8LG?< zk3G4s_EcRG;5BXLm%u5(V|IJS_klb3WisMb#kh|E-FUbw5 zyr@BwG>AK8@-uOu83en!aka`CnsWZ}ah~_wK_<`dD#~4L%nR(I>xjBVrsey0$(8Lx zL_W(e>N@r%hz^8bjmJlJK}Ec;eZ-x*cG=S73RX_FNg6+a)pbtL#VcSB2TRG<<>J`< z`?+HyC1&|gUle;4a3L|#8jHf3-&L7aE)%chcM*uX2z~VjIQg!9nM$bmT0O%P{wNV^ z#ZvvIv`;Bl<@6sS67I>!{UR;b$L$1_R1#q}yKMZC14xZRheD%nF=94KbtaM2@_C&9 zaU=_ro>ZPFnrMH0z2)_Ixg@+HW)vlmzaLYWB7RhtU_8Nl`zFjRBk$hv_Tt?4{P$wu zH&57*@`BM2hs(thIzgE#?OD?1t%Vu|J#RCKKEzdD$TYoD;8WB-%k;PD-Tq&8PESoo zeGd^5z9bygg!DWh>o0p&wrEeeEF=SUhwoi_Mzf>V2bg?@&kfNV6esMVl|x}tNpHkc z;i=B45vf!69GwE4jC+{(b~)a661{)gIsA^5(-ZVqvA}!j`#r@9PA`h}N;@zim;`j^ zarc56_st7G@xqTUMO)=vLKZmU%Nu3ml%yMBgaxcwFU^@}M&190t>?+dYqO|ezIFLv z$XS$wdEh;7mUohO&g7YPE|JDZ!}A6ovyXNtbqIHy)!@-E)_BzGSK?g~QF6FHw7;g` zbB;DAJvYm|wtK=twSZHf3V{x^sfUGo=5?(S~&txT%-E$Ff-_@hGg+hw0I zU51R2H;b~@lcn>SFz9cH^CZFs3hN6S#%m6?r}$@jS9X=Xqqns+s}HjJSS_>h20hvS zxwx8-RRbGw(YGzL8;-{6#Wtn&r-ilhrP-#fvTisVIWwJ?oj*D(2*V8UO@;O6;BUNmvJB!T`eNt3~f!F zko#8I{q)^(LDq|`!IF=p_n+Dj4dM6KZ8fvxTijkF*rwm-SFxjK+QxE!oV zwhoA?P$bG`$gG7+9y|oQr}_1GnFIX{eO0}eHSW6ZQyssMP<-wAkpaJFv|t~WUjQZm zKbut%S#hu8Jmc~Y%Y}4ty2O5gxhv!Kef5YdV}aaL0h!v_-oUDw1g{pcIw>5q*kqCjS7$R7KNBC@T5#Nx%QXnV_={J8w%kIE~K8eX5waZX*) z|8ykW{HO0Fd#j*EZ2^0X8Z$}u`g7$aTW5>j&#camXFh5eq-3XL7hr^mX=Q33w8{^Z z+k302B@2%;CrNMQlP|wn9amlpTpExHh(>i4lwnHIBGM?xT{XtZJtr9z$ZF(?_u50= zTVL0dcU_PUt4@4~u6X#QuY%#aFbuA>d?BqI>mU=N33bC%dNGLe-Qlgit&h_-(W6+5 z)1n`9a4{Ye)qVT6x!MI6oz&u#mR54<_Y=?YQn*wvC$?XD&q?QVhh$RSSya~D(jO14 zDkeu=?A&|8mYJmf{?A9t-^|S*X9{P?tX0?A2S=;@Oncs5ninpSUx=HKcPAbFOurTC zw;bPI*8ZlQM;E6%ce3pnYhdw~UcpLe&N;VM=gpG)B&9!VE;HmQ^~52OSEds${}{Rxc6JQ?81X)1 zkhzN5$nbYN?pEz%-kEDGL;r>k0huQ(;>hkkyMz>yZX3 zyE%WAvUE!<-GSmw55dt0fT1NJfC!FKWRcq89?}qHC*VOEo9>5|N=afxKLY%hDXc9TWKN+GK!-J< z8h9-&Ezn^DO@bE==Be$C!>fZ}S}-UC%DE3~Ko7%V+Hj}==hG=V2Xg(0Afq?-;3kHF~G&l&2Kqi@vV`z{Am47Q(5CZWuB9%_0 zkU`suI8RCt9RcQ;{c9E^>OZpNz`s|Dvt|$mjtYTlYHiQzH_+Dh|A&%D|DXfu7{Y)3 z{;P1HBa=#iUSh0fZq#=_NCA%fxZ+f2&SzG1s$-( z;fdt!$iY7;wzhB^av&W?#uIET5MYjoCXwg`*VTsV=^4Ou4x5@;LZO!CW~Mq82B!L! zmXcl{>#<7uV}wy!_2I{hwS2#|&h9Z~xC;{|<2qXuJDQ@p163R|OV+mP%$Mbu7e(xV|@A;f_?)$#(@ArFM*L_{*^EuaSt<44aW%vOA5U@a* zpxNW@orjl;{a(6JI069tNCFaRYk@?9C{(g1!4D4r^!{wSAWYJ#g#OSfUdYk7Z~k$b zUjzVFWb!r(JLd`C1hAKdMGPCGqWK-g#P?;P92ze5@T0P$M{^HVdKq1hJ{{w5R_D9? zVByoyVAkB+#>b87sjR8Z4o0U?_&yQk#K}A#Ko=dQ2k(=Qw?Q?u)P!@2qlURb!jrA9 zym%S`V4jOX52HOY*yMOf1~>sqkNQE8rjcKfRkq4b04Na{28&GX;YdIO&Fc2eVnDML z@W}3o2S1Pu0Dg=RV=z!G0L=cd(B}dAijoE;fxf)`MZ7>P2atZq{2-^{3&71G0q|Mpou9$XIm2ssfWSCRf{>vb5T0(V+6I7hI057V(RMD7C0DLScinK2 zD?A8>kOnE00v^YOJsxbP>@3Apf^02Tc-#9ocEmKhxHN|Dwu@?Yj z*1BG9>lh?VO^%ODdQSPVel+H7`_7ZW`U(p}+toKXxdCD8PFBC`#6&L_rHSKFK%H;V z8KB=0@E%%o(H!8*J5H%h`P41Gq#yx+dBvvQ`q}QMt$y`k-#IvA1To!#fMM8@+6|dK ziGZ+|7L2h907-Rg@rEiKKzmxj7ywj%l{$MrS<>~E)&DO2kZ5OjdzWQ@8`cGm1-nyUk~r&e)@<@CU;-Ph;aE!sE)wYu*lhn8H(gC zH>sRgQq@=ZxQ&{5MX?I-=zZ>Sec%pW$@DmGFczhCGrRya9W8bW+}KPl;4CusNpwLe zE~-(*bYssNt|tsMgJ9P;uUDHxlOxJbaed$nFnoSrUgr9nT>mbbmXJ$$YMyVGO!)ys z__Msiu9IH_Xh7)oI9zxaRM7LrC+yi9S54inVPuq>BybZLZO3?RoE+v@ptx*(4wl7x zkTWJ+be8wrW#LzTml6`pF_swQeWh8&a*--tC%(wb&{uzflkVG;D+PY9W)DA;my+?roODFJ4&$HEsifKn^4E70#2CS+ME&m<6AzKrvh zg)>2Ei4_S#2{t!3T3(M=h`}49M=kmC4x$T^MNVkr4JNqn-i8^c=N6x8FUtAATO19) zecFPU8)yr$yILfw6_BCSo+*KBEl|tvd6z-(BCL8trfF4tpCb>LroBt+_WinhdTKiI zN6=n@D*};CDEC9szS0+@3#BTgA?cR)c;2U_H`{A`gvq9R-4eP*cEB82IT9kC_*NtZ zp5mAimNHdr@8IuX(8DO+WB<4uOsfYFugtYL9z;N<2%#N{;mh_t*Bj z&r##I1#=Yz*lv&>Qq%!)j&Y!H~sgx8OAi<^4n#>>Cau}%fuh~ z%aY$%y{sVeJJsJo_FjVEG`#x$k&r-rohq*|q}GH*HRJ2D)X9X~QHde6?N&JcT@{A^{N zGWTY}Gh3hCFUc%v2+Sl7iH(ZIAMQT9Y)9&c&Th`~&t}Z-n$umut|+Y#S32d|_KV2% z9;Y1-q0$1{0{tk}GX*1BuZtRrUQauD$$H)K&tB4&ymvC8RU|DiP1257c)gHxJGeDv zLgsr__tW>w`I#>=2TMK?KYVUOG=@Iduu{*IZE<;xU>W_GU&V}`ZyU=l%q)DhlrRN3 z7kJM3+(yj-n2aZ{3RjSvSI1lvuFlapQQ&F~Lz2ArtY0%a==@JDvOPZf%}eo)^0yd-cVQ z_wori%Ttrc^^%LSYdFn8FV&1L@wdF$;-_WTHQJOd5A^PfyVA)!BpgP*w`Mur_KY`r z*xWC=Ql224F1Z#ecK8UaSpD0nay#02+Nx?VbKH5ut0rzCzUapD;{!g=sDWNgA3wAo zZZ@+ryt245f`0X<=|Y+aP4pn&+_mwBz6Qj#F@Me}zYNW+@eKP^8m@F=Fz>nK*pdK?zI9eHHo{sWbFSR1NC%2hAbR z?Qd&}doD?Y)FeEzt$g&PuafS(Fbu9UeIcP3V<#D;4s}6SdC&>--Jz}Ct!1fOwxbxd z!=evka4`-Y*?speQst79R!UKFODn1L$LZ%dacqi*1Is6^=ZxdUBa$huObYXU>CZ=I zm6M}R)~-Dv%M4z$6*gRk3%(l1sl^Uk0cD&6q9 z0H#_#F&A;ChV}JEezx2>IrG|zUtuih7%remJKiZLH~SD`VQu_U(paHKVNSNS0pdgY zAY;{XGu_waluL~lvNOj(lJ?!Q!gaM}>C05S%X~HE2YA(eK&j$n38EBX9!A+3K|MS} zp24rS&N=Co(tcRY9PeVizqsyG-{b%B=SOvy+l(64n_1ZklJe*Ml}c61KLc0hB!l?B zTMoJe$I~Bf*7k3G+r2LI?PB@%V|+bv_@`UFTjy(MA(kND)tv3*U+=Gubep%C_b8ev z#>QvM%gYML)GT^*B#ji76^eGg4Rid(nDKuwHMBLlak3M$**CvuEvB=slu@)qWj!c* z2yaqslCSPyAQtXzmUIk+vMO0sLrpdE>4!EAw{4fY)^SaR?`&4}r$V+jA*+{{Ho|q4 z_ObserD>)ZnjP7b7KEkZ0V5BxJ04^~#CqY;c&rEGd<$L=0Jshj>@hTql_eZUCaPn1 zFzR$7h0O*4Jp(!gi}S_PK<;=i0to?Ty{H3&2p$NqleU$H6$Od+CZK|;c)MV0dt9(D zPS*o$pbyfc!`T8vJPiw?6a7g3a5@6~w=SGL-!VhLpuZtBUj+C+L1CCCs^dMdFn3K)EKU^!(||!CQ1*RH4SEa?(}Y8HLH}G}wnM6iCmd~J_K!RE z3IX<}(I{{TBq%6IJxEiXO!b05b#-+i8ZZb9rp897`7=l~EM1M{ulQTR1n-Zd5-2nR znFQKV#JZCMXb3Pn*#Bffr2H#O^8e?g*k=ZzV<`}*y2egczkya(|38#S{1@#{L*xG& z@Bb<6Z_l9MA!ximIe>~|*UnRM#}x&Rq~ftOGS!|;_WOO1w%%kK+25N?0l_rYp`b%n zSR8@0V>$dc#mWk9LGq_zNjSWP2?ER(Q6~^Q;7Bc`rjaR9`S)2BNHb$2 z4GmLGq^`E^Z>|X$7eK_5Xur80|K%S2BX_4Eh!nPG6Fij=i1#p~l8K~IZDKdj&h+2rWiS41e>{oZ^Hg?oM~n|nus@7lwwCs$ z?D1C^P>lR^nmv=VFfp>H_q)5fd2lQ2GLzyiQ{d*V|Ea<2ASCPtaP|Sxo{WhCHW08d LwKgd=cDwXHDN#*w diff --git a/applications/external/subghz_remote_configurator/icons/remote_scene/back_10px.png b/applications/external/subghz_remote_configurator/icons/remote_scene/back_10px.png deleted file mode 100644 index f9c615a99e69c0100b03a9ae7b2df903da4ecd66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVk{1FcVbv~PUa<$!;&U>c zv7h@-A}f&37T^=&`v3obAT#vPO>_%)r1c48n{Iv*t(u1!X;5977~7Co?ed rv9U?Av01aVRT(gMJdt+jXk=uN>R^g!*w%ImsF1<>&pI=m5)cB{fFDGZlI8yr;B3<$MxhJ?+;A4eL&#) z0Ra}bue?07WhLz78x$BO|L3mq-MMxdP^D^#YeY#(Vo9o1a#1RfVlXl=GSoFN)ipE; zF*LF=Hn1|b&^9ozGB8+QQTzo(LvDUbW?CgwgE3G~h=Hk +#include #include "views/remote.h" #include "views/edit_menu.h" diff --git a/applications/external/subghz_remote_configurator/icons/subrem_10px.png b/applications/external/subghz_remote_configurator/subrem_10px.png similarity index 100% rename from applications/external/subghz_remote_configurator/icons/subrem_10px.png rename to applications/external/subghz_remote_configurator/subrem_10px.png diff --git a/applications/external/swd_probe/icons/ButtonDown_7x4.png b/applications/external/swd_probe/icons/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67d1c23c0bb5d765e8d2aa04b9b5adec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE diff --git a/applications/external/swd_probe/icons/ButtonUp_7x4.png b/applications/external/swd_probe/icons/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c index 5b646141d..3e15cd02c 100644 --- a/applications/external/swd_probe/swd_probe_app.c +++ b/applications/external/swd_probe/swd_probe_app.c @@ -2,6 +2,7 @@ #include "swd_probe_app.h" #include "swd_probe_icons.h" +#include #include "jep106.h" #include "adi.h" diff --git a/applications/external/timelapse/icons/ButtonDown_7x4.png b/applications/external/timelapse/icons/ButtonDown_7x4.png deleted file mode 100644 index 2954bb6a67d1c23c0bb5d765e8d2aa04b9b5adec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE diff --git a/applications/external/timelapse/icons/ButtonLeft_4x7.png b/applications/external/timelapse/icons/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d43247083aa705620e9836ac415b42ca46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a diff --git a/applications/external/timelapse/icons/ButtonRight_4x7.png b/applications/external/timelapse/icons/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/timelapse/icons/ButtonUp_7x4.png b/applications/external/timelapse/icons/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/timelapse/icons/Pin_star_7x7.png b/applications/external/timelapse/icons/Pin_star_7x7.png deleted file mode 100644 index 42fdea86e489e278376e797b1110114697a0ad98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3600 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 LwKgd=cDwXHDN#*w diff --git a/applications/external/timelapse/icons/loading_10px.png b/applications/external/timelapse/icons/loading_10px.png deleted file mode 100644 index 4f626b3d58c1800c375ebbfd358db6c72374dbcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4349 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 rrz))zzTsgFa-)vR)4GM #include "gpio_item.h" #include "gpio_timelapse_icons.h" +#include #define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/timelapse" #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/timelapse.conf" diff --git a/applications/external/totp/images/DolphinCommon_56x48.png b/applications/external/totp/images/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index bf76b7c82..0f2e0cf86 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -1,6 +1,7 @@ #include "totp_app_settings.h" #include #include +#include #include "../../ui_controls.h" #include "../../common_dialogs.h" #include "../../scene_director.h" diff --git a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c index 218e5e397..23a919ed7 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -1,6 +1,7 @@ #include "totp_scene_authenticate.h" #include #include +#include #include "../../../types/common.h" #include "../../constants.h" #include "../../../services/config/config.h" diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index da99623fd..ab1b730d3 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "totp_scene_generate_token.h" #include "../../../types/token_info.h" diff --git a/applications/external/totp/ui/scenes/standby/standby.c b/applications/external/totp/ui/scenes/standby/standby.c index 5cd6bae6a..8524cf2a8 100644 --- a/applications/external/totp/ui/scenes/standby/standby.c +++ b/applications/external/totp/ui/scenes/standby/standby.c @@ -1,5 +1,6 @@ #include "standby.h" #include +#include #include "../../constants.h" void totp_scene_standby_render(Canvas* const canvas) { @@ -9,4 +10,4 @@ void totp_scene_standby_render(Canvas* const canvas) { canvas_draw_str_aligned(canvas, 5, 10, AlignLeft, AlignTop, "CLI command"); canvas_draw_str_aligned(canvas, 5, 24, AlignLeft, AlignTop, "is running now"); -} \ No newline at end of file +} diff --git a/applications/external/totp/ui/ui_controls.c b/applications/external/totp/ui/ui_controls.c index d5e86aa58..c6ad3e1fe 100644 --- a/applications/external/totp/ui/ui_controls.c +++ b/applications/external/totp/ui/ui_controls.c @@ -1,5 +1,6 @@ #include "ui_controls.h" #include +#include #include "constants.h" #define TEXT_BOX_HEIGHT (13) diff --git a/applications/external/uart_terminal/application.fam b/applications/external/uart_terminal/application.fam index eb879458e..3f57ba314 100644 --- a/applications/external/uart_terminal/application.fam +++ b/applications/external/uart_terminal/application.fam @@ -8,5 +8,4 @@ App( order=90, fap_icon="uart_terminal.png", fap_category="GPIO", - fap_icon_assets="assets", ) diff --git a/applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png b/applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png deleted file mode 100644 index 7cc0759a8ca6acdb9b9c2e3dc00edde2f0e93a67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1812 zcmcIl&x_()N{W z-JN@vFI~T+Y1-v}FWiIo6?k5J;aT|qw)dv2CwcE-scA1=t)FMK&%d~)Y0rO}4EC%2 z=aK4R$F?2(hE6fX7H(UFBH{$t4v4EaKLer_Vi@d&Z#A)C)-lFal?RqJo6XEw%T&e4 zBEIiim|Bz~ut4QeR$2KDgeVQ)Gl9#&Q7)}LS*mHl<@TY>s++4|`B+t|9IGdATYvr+ zL&4Vp^Jy_zlt*w&PGkz$CD@V$zdYy`l2xi0C^cC%YIhY;r^KZCtp`aa)U15HX4E*y zkX5o{K-UPu6k#$T&@w-;?b`$g7%xpD(1BnTyO^;O$?)hRrco61v$A3tm;JC~04Xy` zM9{K5&YYn{cI-;z+O~({*abcDHnS;pNXu(4c!7VY__VG>?Z1?*P#iGU)eM+zGa?B_ zvgI?>CU%T`*JH?wZ6SP@IW~7zXzvsW>>M^Zjasu3fJoi8ARJ`vf+uoa+eOUrBobcB zw>cJ!4w<1pj@wleRYXcabz7&```zwtp@zu>K9qa+w)FmX*CD>+AZijr7d#lMB4r@7 zBxNIM<=Lo~JFR)Z8qvz(k!=8Gk?gq@8g zfS#k0rCF(l)r=K#a|A8Qr4h!%R?w1c= z{pq*skHW8}pZxgsP>68$^3NZ9>0PQ& Cw>kg- diff --git a/applications/external/uart_terminal/assets/KeyBackspace_16x9.png b/applications/external/uart_terminal/assets/KeyBackspace_16x9.png deleted file mode 100644 index 9946232d953ef1cbfbf0e6754be6645e5ea2747b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1829 zcmcIl&u`pB6gEEvMG+BPN`-{wNT>+Lo*8@XwOcpZ?1t{5I;81J4Y!WR<6SFjkFlNX zCgRjnK|!3jpo$Y0E}Xb=;LeTzpnm{T)f-4i;dy_hpfu#tmAsxAzxTcGz4y(`m)l!6 zS1w(-q$tWtuiM#y_bNQEzxE>h|J=PM>Pg=HtW=aY-mae)lh*~S0I8^$I!Q-a=}mlXitE9+UN$s!YEtd_TB{DI?graxTNXmKb&NR1RCQdP z*p_AEk5q~&HgLlr6cO9QmPZ_Q{?i~@5yjq4=i_-SnEBeUs&daT#^bR*Hg#DH4C1=3 zfvG_$0t-|gW)+*DtXx|lbVSLEB(D;gsWl=C<$mRBz;u>EnlE9qa$Y7Vm@#3wL3CWF zv@i^U^G(xqX_e|ijf0zqnN0f5E;9~PYWYyXtSU!}MEQj(L+?JpJ#W3Q_ zfcbtgnwBTxh8T$yuuHHdQ+~PEE(EJ&(U)?xXw>#1qDqNQ)vI@tERy5$gPPIYL3CIp zd=0ur5T*!|K7p3G9x*>8*u!{c8h{QWRntB?yEl08lWCYa({L}SbyS-h=I2pl*a_8oT+S_c~#IXiq#zs}!z^4spCcOR_0+*o_q`N6;X{rjK1`QsBs`Gcx|z4v#k QTVG_o&8^N)8~5)21Ktij=l}o! diff --git a/applications/external/uart_terminal/assets/KeySaveSelected_24x11.png b/applications/external/uart_terminal/assets/KeySaveSelected_24x11.png deleted file mode 100644 index eeb3569d3accc5a5c56829b12c85079172b56729..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1853 zcmcIlPiW*+7#}^zw%YZuf+B)3d$6*;fxiXAXK-X$#&ks`?Z1FR>QX z26aVbT~%`&N5w=X1OWo&yGcQZD9KMx7+O3JvM4Pgkw_&Y^~HA4kU?pcLYz)%lYCqz zD405=sj4ZsOlbo2yrZFUJVocl(hfu!>phe>@9d^rUFW&j&H}!)!;|9lBv{%Lg~)s2 z4%()#|Dlit(}3xA)*qFJ1uF0J7`Su5Y9oEA+srsEMAi|aKWWt3B%(w#g-G)oQNqL^ zf1*@0Ucg(l;0+nNrXfra);gN*63r#X84bG_S5Oapz-U2_2No;}caH=0Jhz?X1x*6p zZZ%{Or9=^P?a(oNj2+}NPLO5lb>xS(hK#yzQ(Fto(z;~|u)ZaN?XnW(`pULU1i&$^ zrW-ON3~kFtm|7MJL)}ES1tUU3N-T@l>$)*vdoGLM%c1>)tfeXjj8tQ`U&jXGx~+pC zJw&zve}0HDBg6^Jz?Y@lahswqGEXq5ZvEhVyV+dJL>TqqMZUhgD7BZGrskL?B8nzU zEO0}S#T1Md#k9-SH0hSMuhLzKa_I5y_(QtDUmB14ku-9rOM~*GXvjh71`cJarlUj3 ze7uCJ^@AP<(j#0_!EzB61Df%LF0|x7U8vqkd`@?cmVP{k{EyPdWes{X>2la%Rk=(? zE%&0TDeAxbb=w#db1i`F%Wmf5GAz>Wv>@jW_p)ho-#0CeC9cGbyZ*s9Cn^o)Rq=_$h#NIZix%+wt GFa8a7)lmol diff --git a/applications/external/uart_terminal/assets/KeySave_24x11.png b/applications/external/uart_terminal/assets/KeySave_24x11.png deleted file mode 100644 index e7dba987a04dad7dd96001913c55566dbde96c8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1863 zcmcIlO^6&t6dpxnjjSF75f9pQJZy|LUDdzSZ6$n-*6-2D5s-9_fx~uK( zotfQBVDX?Q(UXLzXF6aX)%U*l z-d9y`w^q+Do_PF3p-@5qOL5h2N9RU z^i-~BIziNECdw*wjUcQeOxncsbnKa>(*%1MPoPck0jC)~9$50g-#!ks+4LGwn$d`f zMy;%ZsA3Rsk2!`#ELuW>2#g3fF>;CFBHMCo-El0(@X1&g%&$qdl~*F4Kd~*B3^?Z1 z^bEmDf}0)Wn??sYC6l9$X;6esLO7#_ZCmDy?ZqU3l|%anS#wn!7%f39-Qp(l9fyJ- z(?=x}n~2%2PcX9#VmYdECvH{tWzv)!s%sn^Z&a(TMEXG=KBQ~smzBm!)h4cOBfSV| zapw6l2`LyY2x(Vnan#Li4>BO#dXPeox2Fr~f_P*4)DM)gJ3Y$sMNw8+?gqit>2PpJ znU9yygm%~yKzf8rCa_fc*^nlp(uJ1%rwg^aiBIX^Xz9mu$p0vPT2|JhQCGkYtEqW1 zTD})enxg%?Uw4c#Ggk#{pLa8zmSLH8=LI=?xR>pc=yYsHAgcQUxz^arx`9fR>e$sw zj@}Uy75!kQXF{tT9e=F+z^*!*3|n>nI6oucWq!(t2og`=40-O!tAE1z^ID@;X)nEd zVK7xqj`wg%Ed2Op{k=a*YZpKHX787$ zzhKuN9(?M5ojmn%xcU1}OP>$^`ZD?cW@lIaTn=x3xBtP#z16o~{qVMZ>hecsD?jQQ ME3387mS5lf8`39Il>h($ diff --git a/applications/external/uart_terminal/assets/WarningDolphin_45x42.png b/applications/external/uart_terminal/assets/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb444db1739f2ccd030e506e8bada11ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& diff --git a/applications/external/uart_terminal/uart_text_input.c b/applications/external/uart_terminal/uart_text_input.c index 7d400b81e..62884550d 100644 --- a/applications/external/uart_terminal/uart_text_input.c +++ b/applications/external/uart_terminal/uart_text_input.c @@ -1,6 +1,6 @@ #include "uart_text_input.h" #include -#include "uart_terminal_icons.h" +#include #include "uart_terminal_app_i.h" #include diff --git a/applications/external/unitemp/application.fam b/applications/external/unitemp/application.fam index b0e0aa6ee..ec865dff9 100644 --- a/applications/external/unitemp/application.fam +++ b/applications/external/unitemp/application.fam @@ -14,5 +14,4 @@ App( fap_category="GPIO", fap_icon="icon.png", fap_icon_assets="assets", - fap_libs=["assets"], ) diff --git a/applications/external/wav_player/application.fam b/applications/external/wav_player/application.fam index c71527c9d..f3a6ec251 100644 --- a/applications/external/wav_player/application.fam +++ b/applications/external/wav_player/application.fam @@ -7,5 +7,4 @@ App( order=46, fap_icon="wav_10px.png", fap_category="Media", - fap_icon_assets="images", ) diff --git a/applications/external/wav_player/images/music_10px.png b/applications/external/wav_player/images/music_10px.png deleted file mode 100644 index d41eb0db8c822c60be6c097393b3682680b81a6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIasA%~WHIdey2}7aaTa() z7Bet#3xhBt!>l_x9Asc&(0a_=e)W&n8K5!-Pgg&ebxsLQ0Ao%f>i_@% diff --git a/applications/external/wav_player/wav_player.c b/applications/external/wav_player/wav_player.c index 3a48f41e8..2fa590f9e 100644 --- a/applications/external/wav_player/wav_player.c +++ b/applications/external/wav_player/wav_player.c @@ -12,7 +12,7 @@ #include "wav_player_view.h" #include -#include +#include #define TAG "WavPlayer" diff --git a/applications/external/weather_station/images/Fishing_123x52.png b/applications/external/weather_station/images/Fishing_123x52.png deleted file mode 100644 index 1e365de8f4d9c5aeb0b3b11097a2e65eb3451b99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 966 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 a9|QyVQ~@!E@+ey_4zMz}H7hmoyzn0`d`!~- diff --git a/applications/external/weather_station/images/Pin_back_arrow_10x8.png b/applications/external/weather_station/images/Pin_back_arrow_10x8.png deleted file mode 100644 index 3bafabd144864b575144c75b592e5eaf53974566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3606 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 TFC+3}9cE|kU{!4CefEC<&8td2 diff --git a/applications/external/weather_station/images/Quest_7x8.png b/applications/external/weather_station/images/Quest_7x8.png deleted file mode 100644 index 6825247fbeaf98b4d0d9f8aa15d4f2c5f45dcf3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3675 zcmaJ@c{r47*ncTY)~u%~hshSn%$TuGCNaiR3}a^w88d^yEM_LdAR<{3Nh(yX(oIy^+JsdjWd| z;-*7j(JK(}&%1H^d49~d-0aWdjAW(s;{NT((e+Y36U%SRx_Ec>Ff}zoT zMF7lwrnJd);qh@*sKHO%2OWDhFAR(ES#36vKg`$_$8OubDtBrEfR0mbQ$bkd$+oY` z*k`hZN%IKhqIT6JkVRr9^n`sI(4jKVso;gqO6 zqk8uky1uZ`_j7&lGXDd}$y8bZ^+j$t6P|9!e>Tq~J)>iyW(K0!S!&~@4_xs3egqUu zoyk|mXL;Z~_Gf`I&)`b7AFLawEzB!7imbmwB=oJt&)?m2_y~A+B?Z*XO5(fD0Lc6N zV9vH=_S8W@6%!fQy!<50e=IEVCt(L_@sZP=Wh$Bn==jW0QX-ZNpV_qqHN)5oIo_wq@H*}wZTvN07aDKM7(QxUSt za4kn*YomgZxSrO1aYJERdY_Hop0A(_fn$MtdZGbUKDmxva=Co$vj<_jTpr0A@*7n0 zub=haE78X!YcPKi{F_LWbgy=;wbR>-H=}3wiHO zj-B=vY~cI6cQ@f6-2CjsL1!ybcyt$7kR(}eddwayD}g}=@0FA`tM8F75k4GuIM1U* z>YF@Lz%#nSY*!D;Up6b|Ox$p*uuV*9CA?hxK&#lmp4IcQqk0U58-ml1zAj zEGZ_xKn!-Q~s0XJ1+$lxk3p-Sw7Lm4jebXgInV>qV_W0_622SlI zL`P%UOd49MHltea0=KOG5Nd(@QVe>IJ!j z2FcZV)$Y~K)qW&Pe_`9~Da^_Ij2>*ydH=<08qi>m7WZnR_4CV*)mY3VW(rfG-mKoG z{wQ;Ca^@55Q{tzGlSe0%G;?KF-DM3kj(D^Mf7%f8T=s?tIshQ@gJsqXJ$TzcUQ+gU+}O$5}|$H zosEyEt*xHG-*>~hQ#>$uXS_I~L@dfeXFN%7XlRgI@P#tV(Z8zCpDm-`Jg|RAeMo;0 z3+Z?7cK2$I=)%5Fp|}Pb_}KlHdf$X(GL}2_h+V=89V;2_2nk}`V7y|TU?8VfS_a!P z7vD`8Py38l4^K8|jeQ*T_%O7nJ}y7zGP641`5x8XI2hU9+CsefG|aBH__t}=?*u3r zdeya{ze}V{Zq{`rG`%6VL8~!m{lmsm6gi=MXM<;%8RA{qdb9Ei{sejq- z^Y$@7<_{%%xh35mU6?_oL4vfbT(9hk`hZcL>bhwHEdf?|)CsN&uhn5gy7bC*gGd?6 zx4)EC#A}^nwH{Tel**G5m#Qgy@3QELQlv<^?=`Bm@U!j9DhrhBQ@?|fQ3E|mMuIM; zNL-*LeSfqI$ zQx6zg^-vjOnE>f2=`HD0RfuYw+CBC0%LVCn%cRi6hFh{3SIV!Pb&Bnc=}ptku5F|s zBIsw($SY0ijgH6VwrsxaIUR?OD*&y6oI!L18e!*a?YCV0t@=w1hh#TVHyzO^aWCaw z#Zgyn4r}29xA@Dw1G(Zl2Oby%1a*xVHgytTzkG4-MPhbT2clE!MR=oH&`H-O=J%q_ zsymAKY*AH_b%EBmLBG8TvZPMa7Dot8#O)NjxVe@bvKLiBr4la4EAQ;Ev1fVH}DR9qGN4JO23U{>iNTthM;M_=P@h@BMyCe}+=KLbu^& z?XlXXwZQiNi{c{U7;&Z4rIcg^apR%a{%-~b3VWSii5ZAy7pGtpAAY?!Yj9Khy!O32 zwSD>Hf7C6l*U$@^e@2c*=5MHulb&-tMx1}c4T-$XTb*0YOj%D!>t5!^i2%^3{2 z7fD~)N_!npT-M!jOVjA2VRlr==r7&%gP%*Mi=l0v`({%GgSUdN5qw8ox5bv3}hhgQ;0sv|Dj`0oq zDuwc#AU4L0?MU}!a|lc_U`nFKgEj6Bs+4kPDE}X z(TJpMatv%7isTVc$!r2Rlo~{1AwyBhfAS)E^Bp%-8T@AmI}oM(mnb(|doY^LB!l%K zFl{0XrVlnSf{+M41fq}65ilGE*MY)xp*p(SFc=bHgw)jq|NSZR(lJTCNC$I^zmxG+ zC}n>(n}LKvIUEjzgMiSPeo!4FBO@pb4u!+Dc@f&IFdCZ>s!e05{9rIAvxrOzgH55+ zz&nftANpxFN|`71uNtU~e`sl}zxRo^W6)3n1F8do?bP%m(AM_<52aH7iDt1K$p5SN zUx`^xVGJ_Vfy|$7a@~1Pva5zL4tYJ$a zQfNCK%|9Wwwn%Fli%p;r$=2p5WgZEHLLnhB7W$_821alTLqlC19gLY79HwuMurM;x z#puFJ5%3>ab2{-fl}uy*z>;`a9W-3u2m{mQVfFqMyVDL-1~0QYnMnyDlPs8YD)`T; zk(B?|0{d?*e_=`gqUG;8bp8_y<%xmrobCTP>mM#&1MN)zX)>*yJ)S*p|h?~Q6$-f1d>2NNH}5%Lt|z{KrzQc(y-YyStJQ+g^C9Q zSWv-YttjHfh@wyt@GLFhMTjB}M;LSv6%;{^;@J%X_DAW??0&~Q&+|U-`@P@n?x@Hx zJ8Nfa0)b%14d?LjF%^HQmS*_Zy;Prz4^CJ}G`0p!z*2-Nm=GjEMKHicgo!X87D}`~ zG{XJ_!fZF0AR3G2MKHxELKK=XL=B?E*#v@rphhVa%V7)_o@3{?OoMWF~y##kV3^-~Ura#~iQo~#pIF_K28B$0`bDW@qQkN5vj1er#wF+Tj+ z?|%xb1zIIc;=^h*StZ6#E@7!Dl#l0+R;G}k zDeC1D1RjscRj4tcLJV^`ED)C<%48Czw=bJb<4}SjatMt~4q?;jbe~|z8=_EsXfzI; zGR5Vf;$#F?U{hSlXD)k2uBjOiB_5drt7MyCNvH}%fQg)$vYEXwX4ISHN@n&FG$WUU zn<1G__FpGGwS~8jX*%7w_+q;CVFljrD!j4V;}c)t_r;dW2@+`9`eU1O>Hy1H=Z_x? z#)vXQ4`HsunYK6jxY4-M*|zlR`rg;$+V*St8!R`}`J(H(M(Fn4S4n;$pnW-UN$QA` z?fY?KM*pGHedF?8-QC`CtG>sHp2-iZetB?^`cj>4f5g#9o;N06J$3^rWaoX6Zp!iN6AS8ml(v%micb;q2O2KuEfM&;;GfOLnbEbQ;Xh2MB~uUb$O z=Tf34Z;Mngv^s8}xvK?|Q)X5xaeMf&yLz=D);7(;s_;xKaAkeDy_0K?%Yn2|k9C6T z^kdg4x7}!2l%F~e-2N&yKK{I*<bm{PN1M%~EqIS+ z#{g%nih7mt;>v=&E{mA(e4W(c;<>|5`bM6gWBHY@vRSt9LE+^Uhns6zS!2UzZw(cU zoUfWE)n;PLGTwze=xNtR#E5s54 z?u4@P3{ljfm#4cr?==i}&Q~nx+6o_SALMl>7g+8+b*EGr+g0b>QusCdoqSG@XTkT} zJKW>*p7M7!ScsC+I$SWe`PeH**g7LKd+2^e4BT{9(i5Fx*_t#~^L&3q*^<`Bg8SLL zJr^&8Wvz+nlWyNPFyz7qEt>zJmbmY7f19~I|Kz}R|I(kU_SSdG*|X+`TiP_=YR)O9 z%>w2`t#*GaxqGd${KiVYrAK$bi$_|DyT^Fr>F$>+;8w9Tw&Z$d+!FWSfdT)vdK%bz zZxc14Zjp1X%kW^EaVRy?Y1r_3XHB46)J>z~?!08J+0&8}hNJx?^62efuWTu{t@zFL zVZ4jin2pbcGJ}2f!HjR`wC}%p=#A$7DVOR4RjdwPz~ZWrAMHE3V&KiltB9(rl{Yu) zpLzA}yq2xqYW6IxKLj-^F^5Q^3s;lX5 N!3~Mzlm%~0{|8*@s)qmo diff --git a/applications/external/weather_station/images/Unlock_7x8.png b/applications/external/weather_station/images/Unlock_7x8.png deleted file mode 100644 index 9d82b4daf3c722a5244f537f18b147833c2fb4ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3598 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 MrJY5ondim-0h+K&wEzGB diff --git a/applications/external/weather_station/images/WarningDolphin_45x42.png b/applications/external/weather_station/images/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb444db1739f2ccd030e506e8bada11ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& diff --git a/applications/external/weather_station/views/weather_station_receiver.c b/applications/external/weather_station/views/weather_station_receiver.c index e994e7830..22f5343f1 100644 --- a/applications/external/weather_station/views/weather_station_receiver.c +++ b/applications/external/weather_station/views/weather_station_receiver.c @@ -1,6 +1,7 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" #include +#include #include #include diff --git a/applications/external/weather_station/views/weather_station_receiver_info.c b/applications/external/weather_station/views/weather_station_receiver_info.c index b3b3f2193..c58993a98 100644 --- a/applications/external/weather_station/views/weather_station_receiver_info.c +++ b/applications/external/weather_station/views/weather_station_receiver_info.c @@ -1,6 +1,7 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" #include "weather_station_icons.h" +#include #include "../protocols/ws_generic.h" #include #include diff --git a/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png b/applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png deleted file mode 100644 index 7cc0759a8ca6acdb9b9c2e3dc00edde2f0e93a67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1812 zcmcIl&x_()N{W z-JN@vFI~T+Y1-v}FWiIo6?k5J;aT|qw)dv2CwcE-scA1=t)FMK&%d~)Y0rO}4EC%2 z=aK4R$F?2(hE6fX7H(UFBH{$t4v4EaKLer_Vi@d&Z#A)C)-lFal?RqJo6XEw%T&e4 zBEIiim|Bz~ut4QeR$2KDgeVQ)Gl9#&Q7)}LS*mHl<@TY>s++4|`B+t|9IGdATYvr+ zL&4Vp^Jy_zlt*w&PGkz$CD@V$zdYy`l2xi0C^cC%YIhY;r^KZCtp`aa)U15HX4E*y zkX5o{K-UPu6k#$T&@w-;?b`$g7%xpD(1BnTyO^;O$?)hRrco61v$A3tm;JC~04Xy` zM9{K5&YYn{cI-;z+O~({*abcDHnS;pNXu(4c!7VY__VG>?Z1?*P#iGU)eM+zGa?B_ zvgI?>CU%T`*JH?wZ6SP@IW~7zXzvsW>>M^Zjasu3fJoi8ARJ`vf+uoa+eOUrBobcB zw>cJ!4w<1pj@wleRYXcabz7&```zwtp@zu>K9qa+w)FmX*CD>+AZijr7d#lMB4r@7 zBxNIM<=Lo~JFR)Z8qvz(k!=8Gk?gq@8g zfS#k0rCF(l)r=K#a|A8Qr4h!%R?w1c= z{pq*skHW8}pZxgsP>68$^3NZ9>0PQ& Cw>kg- diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png deleted file mode 100644 index 9946232d953ef1cbfbf0e6754be6645e5ea2747b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1829 zcmcIl&u`pB6gEEvMG+BPN`-{wNT>+Lo*8@XwOcpZ?1t{5I;81J4Y!WR<6SFjkFlNX zCgRjnK|!3jpo$Y0E}Xb=;LeTzpnm{T)f-4i;dy_hpfu#tmAsxAzxTcGz4y(`m)l!6 zS1w(-q$tWtuiM#y_bNQEzxE>h|J=PM>Pg=HtW=aY-mae)lh*~S0I8^$I!Q-a=}mlXitE9+UN$s!YEtd_TB{DI?graxTNXmKb&NR1RCQdP z*p_AEk5q~&HgLlr6cO9QmPZ_Q{?i~@5yjq4=i_-SnEBeUs&daT#^bR*Hg#DH4C1=3 zfvG_$0t-|gW)+*DtXx|lbVSLEB(D;gsWl=C<$mRBz;u>EnlE9qa$Y7Vm@#3wL3CWF zv@i^U^G(xqX_e|ijf0zqnN0f5E;9~PYWYyXtSU!}MEQj(L+?JpJ#W3Q_ zfcbtgnwBTxh8T$yuuHHdQ+~PEE(EJ&(U)?xXw>#1qDqNQ)vI@tERy5$gPPIYL3CIp zd=0ur5T*!|K7p3G9x*>8*u!{c8h{QWRntB?yEl08lWCYa({L}SbyS-h=I2pl*a_8oT+S_c~#IXiq#zs}!z^4spCcOR_0+*o_q`N6;X{rjK1`QsBs`Gcx|z4v#k QTVG_o&8^N)8~5)21Ktij=l}o! diff --git a/applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png b/applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png deleted file mode 100644 index 231880386a9d9e83911088c9fcd083f84a7f75bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7210 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 diff --git a/applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png b/applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png deleted file mode 100644 index 1f4c03478f68cb15c74ef99888ce4faf50e0a140..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7763 zcmeHMcT|(hwg*8VAW}pT5e%V7OCh9?P?TPzgMbtONN-0#Ksbt`qQDF2@p$jMYrS>WdiTGRwen?V&)&b;`?vSZ`eqVoY^cr2evF-g zfq_$3N5ho90!va7!EG^n_JOLaXvtIPYQ|bLIl$M+=)P1CL*XpT|J)pSw`V*_Er$lGUV9}kPM4|w*oUQF?ll7f#pMss4lb=^o zxTHFHbLTtx?Ok4rEw7t>D(VkxZfcfTJ7sHGXLrI1f{9&ic>1nxr^!utU*9dyBa?uh zt<9U4zTNzMCU9G9(Yh;AD>cajAe{zue8xJ@Yg`6yOC0zLuJnr?{uC$hz$J!1t$O8Z z{Ij)7kNrj6`_Cz6d1P2Fte8CNzMOA6aWZ8mTM&she@Jegv}p%ETlw%=%BfQ0?9PX~ zT<5QUd04Y5|8c}-@_bFLkydQn+`w{A*uwddyO(PoJR82gHV_($1!NJc)^8hP1lU9Zl+Z^xD0YX>z1jRVXA=ShPzIG{j?tUd1$5uXpzb%b+lTD z2b_);b06!E6=dp6f=I-iVUTcm69n&=VQe7T!ul5pu5cE0(s6SWJI}i_jE6#mB*~3b zo+*ONne`OHMhVIaB2&sl@&(l{@-|78&dRPay-Nyenss(>xytRJBXX7ZBCWrZ8?SG2 zx?CcyK~JPT+0;;cXyw5(k@ua5@1=`W z8c3y;Ti?ENlwI$2_72c<)8{JlK<3$m@hAXh<&Tq6(7sBM9BfW9Dgz_Fly>T@yj?Lc zQ?nbWBB-Sl&boy_$x^=>j?9_78^8SihTeAQ)XJ2m-Q$A=a;E(d5lzX-UdYO|cS42h618SdzYS-CI9A7@(~mPVzJ3&;CuY?)844V_d` zdtM*YoBGXmqH6?{;ArSfbo~91V-={sw)#ULSMlAtwJht8T_cr_b0Eq_rM~{dTXu!W z;MrSTyi|xmiS& zg@pIknVWuh8jj=#3i?h(nqB7av~MqXrsY^7>fu;MzzM-dPt5F-l@JE|c29152hi_tgqdSUNxv_%CV5xX$KDkx3 z3CnUcOS{-qoZ%kl`(>>L@$q*+;jgFCS!bmJL`RvVio!W=Y2t9e_$6*hO`Frj0qcBL zS#X;i;h-nwCW732Xblr5#s}ruxdHY-oD&1lCxlpp;<;$SEL;&?bkD&tPIiEu?O-%| zy1xBY_3O|X#={?YKLND%WxD3hhuNxrodE=`jl%Z%3CrYrddKNXkr+9k1JXi4!nFfX zJ4?6t!y+@2G4b;{Qt7KA48G3_Ca3=}wKzjT-r#}N7bAazy*FCU5gM8c89CChRP@Sy z*mBJKlGm+$n|!e0Lv_}w{!?yu()7yog*3jzdz*+g2_>N|O*A^|C0~|D&)!5;Bid(b zt`7M?w(D5sYbFw389(#>FrsLMJW332BLYztaf%<)Pp;OW-R!T;)x6~}x7df8%8z({i?9Fw30dqk zPFb2k7$5N44No0AQ9nT30q=8Z4{D0MIONu6$5=MHUL0{gaBVsu_;8FGf98lLYh>ZT z^?kafil5OYR+UbcvM;f>$Z5<|#{)QeY3>&MfH@eea_4cXsQ7O~!+A@il8Ymz2VWd= zInDZh;oSl-*`)5mqd`027ZM?#>hq)#Su0L5n;O^e#9)zUSL_qlgrsA=>bUkZ zKCOJ5{hk(2g(0EjQn3vgIf>)gLUmOSnhG=Gv-jiIX@)56y zA8rlm%E#nBlj$2dnJ_`}iammVPPKa6SX$B=<-sa$cR*sHwMuOK>Nd;Rp#1ZI3*-D@ zW16!}9pyPOp6pZSfmh#bM|fH;vG>_Yo)kWkX_gbTD%#hmjVa&cXfqwupI$n=as6rS zy86Q&#b9=xS6awoj!Z@ugsmnwZyJwt#^aSU8kb~|z|USRw_^i9zN!^9N^k?u5^Gdu z)u}w~yt+2hhwSzKLY+_LE!$C4F^hM(+q~fvlY5hoxdj<=xt=MyY>WU{#Yd5iZly3%=9Omm?!k--5ALzv6@ z=i9Y_(81sXSV0+Hlu5GNn{h3D*)n^E`r|`pcXqO&LU-(!8P{bigU@H({%lixSvwSR z-!cx!-_ZO#u=BIv%Wo9ykgTa1G-$*+cKZ7#P6&0~shFV$A0reAMkRzP!iAsIt_ zV2@Iur*CdojvIBy`uBMU8hH;=tJkAel%T0gxL2$PxQ48on#%(Z-oxcEj4!Z2ZulGBZDQCVYdz*#i$zj-uZtXHT5_3S4J6*ao2xHk%-+V%QZ?2vf5D`s zD9`tRgN|P3!SVrGhCG{$@b zhlmo5Bq(hwp|apUmxq zdXRHWoJB8#)@m+eoqhNCN4_}-WD z>DF<9Y#!@qD?0Y_LbAg_iW!`$<<(E8KNxUq6f;}9-(&s~92=I|aMpKTICX_FBGZI8 zNAAvB#6QFBlUQVyIg%aeed9?%m@JQ2+=ULdM>=FzD?fGyN>EyFg{tkrY&GGf3XgZC zC2L=D86IptIWcfTu(3=MWXAn5QBg%O#5QJf0nkNZnd3gy2&uMsno`>4qar@mIVpBp z^~jr>T%#{e)gN-wx}9j}TA6pH>8+SM)nD9M?A%c9xk3bQaunz3FYcm@z~<|!N#jgG zVVx?iw&;|taD~P>19V-}BGao;pT*5YG}^oh~!SY!b5t?b(u1-{z$rMM6WlBOZ~~6Il?In)6pwm3Z(WUwlTAlq@roV};8S z1}EZ7_(}s^YJ6U29}N@&6j;jNnz|M?Sakh4MN4hT>)CqwYYg#4FEIDSjh%p{MvgiWNOgoT0nFGwKFX)YE7f#2o^Aw3+UqQTo`GZ!l^-V&A;@lwdBx+)-% zMtVk}-f39Sv0O+oboNNcD63hl-SguP^r)?-m`M}{mB)LsuQg336{`#T?;K6Yk-f$+ zc0GYZJYGA>QDgGSfpfyaddJw8zNWRCT+H3%Z#S7$+lro*)cy#K${p1D_a~eA&TWM# z%8)&OXN@TGnHkU|9`XuMb(b6=podP)K?Gs+)1=0O#xG2|e|G#bcMF ztesUK_b@^FinJVZiMu_0xp$oITRtKYx3oSDa+M7r$nRTYxc}GC80h6Pkqn8M zSI%oQ9~D2Y+t@dooof7O#8Yaq)shns(c;DwDCzZn5y$oEf@s{E^K61G7n6JZ&(>!} zrUAqc#qqQY)CjDx8E^V}S;YLe1cekg&~FJ2PihMN9WFowBHP0UC)2 zlYCt=H3T%bPNjAynUvpz!WbABL1cAxV_kLiKTo~%vu@^Sz;3=#N1-(Zt1?!+~3k7dtTw&iL2D@ok@;Z z!5eqd6@$dU)Usg!Bvi&{hVv>oqqzK7259;7HlS)@Jtcf7E%jqz&e!!P+O3au918~t zet?)u9s$;*)Uz(*8v)w0tmT5@WU)ZrH7#Aj$m&L-uS1~HTi4X9Z`vQeE%N?j(K9Np zHwb`t(6fGFo`oM>(N*?TTa|*nNWbN8_B44sf*s>`JZcj(aaV#;-?N?qkMFcPkD7_q z$VPE}ZJu&I;W$LXQ&X?_Syg37J0e0pXB=-w{|D=wtB|R|t+kaUeo`UES$-2n-BLr+nRUcxNIF=sYG78 zGC>(+i8O#2xT_PL$U6R>L^FRwbG*MZ9zy_~Qeju}#nJ&>i8LJ0*VV<1iuF|n?crkS z?Oibx1l&`hIV*##42*&56i*@$0YN}uU`=1LHyotG4pj0akg%p2T0bD@J!Oy+jpmMp zLVbLEAU^UCil-w~4uio!VQ?rM4yJ2>seW!WoG;joD!L2t9Ycdi#e0(7X=I8Ua2FHj zK=Gm}gFy6g;Gh1vx*Hh$gmbuCCBuEvPh2Z#u{ihyJ4l)tr9S z0yQO4DPEp|jVAgl1Oflk-rdX7WiK289!hi}y3$pt^jYQpWlC*b1LL0-yA(K* zUETMr=w$zkCXGz`i>!a~ZFgiZoL?QGoBzc97wtb|-&3Y*85m$SD0r`3_jEOsLA&!~ z2^2h;fZc1NF(`Q$3XKQL5#$}f2n84tj6uSYU?dy|L!;3|IR`nyFHpK}R2t3=Puzu~ zgG0!490hrK3`P!v2BXkuc`yP7R{&#BI5ZdzM=2;E5d<6xPx=MI$dgR3N}S8DUhP5= zcAB9w`HZVPU`MQwU^|-~Xk(yLf;~ z-&3wbrqbv4+Y^0XQD#Js@4fF`7xLax0s{9I1r~?@9s(8TP5hp3I@b3gyc5pNkx1V? zew6EAx!J%S)>LeS-Wnmb9nWrR3qxXieCzQocfxiXAXK-X$#&ks`?Z1FR>QX z26aVbT~%`&N5w=X1OWo&yGcQZD9KMx7+O3JvM4Pgkw_&Y^~HA4kU?pcLYz)%lYCqz zD405=sj4ZsOlbo2yrZFUJVocl(hfu!>phe>@9d^rUFW&j&H}!)!;|9lBv{%Lg~)s2 z4%()#|Dlit(}3xA)*qFJ1uF0J7`Su5Y9oEA+srsEMAi|aKWWt3B%(w#g-G)oQNqL^ zf1*@0Ucg(l;0+nNrXfra);gN*63r#X84bG_S5Oapz-U2_2No;}caH=0Jhz?X1x*6p zZZ%{Or9=^P?a(oNj2+}NPLO5lb>xS(hK#yzQ(Fto(z;~|u)ZaN?XnW(`pULU1i&$^ zrW-ON3~kFtm|7MJL)}ES1tUU3N-T@l>$)*vdoGLM%c1>)tfeXjj8tQ`U&jXGx~+pC zJw&zve}0HDBg6^Jz?Y@lahswqGEXq5ZvEhVyV+dJL>TqqMZUhgD7BZGrskL?B8nzU zEO0}S#T1Md#k9-SH0hSMuhLzKa_I5y_(QtDUmB14ku-9rOM~*GXvjh71`cJarlUj3 ze7uCJ^@AP<(j#0_!EzB61Df%LF0|x7U8vqkd`@?cmVP{k{EyPdWes{X>2la%Rk=(? zE%&0TDeAxbb=w#db1i`F%Wmf5GAz>Wv>@jW_p)ho-#0CeC9cGbyZ*s9Cn^o)Rq=_$h#NIZix%+wt GFa8a7)lmol diff --git a/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png deleted file mode 100644 index e7dba987a04dad7dd96001913c55566dbde96c8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1863 zcmcIlO^6&t6dpxnjjSF75f9pQJZy|LUDdzSZ6$n-*6-2D5s-9_fx~uK( zotfQBVDX?Q(UXLzXF6aX)%U*l z-d9y`w^q+Do_PF3p-@5qOL5h2N9RU z^i-~BIziNECdw*wjUcQeOxncsbnKa>(*%1MPoPck0jC)~9$50g-#!ks+4LGwn$d`f zMy;%ZsA3Rsk2!`#ELuW>2#g3fF>;CFBHMCo-El0(@X1&g%&$qdl~*F4Kd~*B3^?Z1 z^bEmDf}0)Wn??sYC6l9$X;6esLO7#_ZCmDy?ZqU3l|%anS#wn!7%f39-Qp(l9fyJ- z(?=x}n~2%2PcX9#VmYdECvH{tWzv)!s%sn^Z&a(TMEXG=KBQ~smzBm!)h4cOBfSV| zapw6l2`LyY2x(Vnan#Li4>BO#dXPeox2Fr~f_P*4)DM)gJ3Y$sMNw8+?gqit>2PpJ znU9yygm%~yKzf8rCa_fc*^nlp(uJ1%rwg^aiBIX^Xz9mu$p0vPT2|JhQCGkYtEqW1 zTD})enxg%?Uw4c#Ggk#{pLa8zmSLH8=LI=?xR>pc=yYsHAgcQUxz^arx`9fR>e$sw zj@}Uy75!kQXF{tT9e=F+z^*!*3|n>nI6oucWq!(t2og`=40-O!tAE1z^ID@;X)nEd zVK7xqj`wg%Ed2Op{k=a*YZpKHX787$ zzhKuN9(?M5ojmn%xcU1}OP>$^`ZD?cW@lIaTn=x3xBtP#z16o~{qVMZ>hecsD?jQQ ME3387mS5lf8`39Il>h($ diff --git a/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png b/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb444db1739f2ccd030e506e8bada11ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index d6a0d37c7..9e24d3964 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -22,6 +22,7 @@ #include "wifi_marauder_text_input.h" #include +#include #include #include #include diff --git a/applications/external/yatzee/application.fam b/applications/external/yatzee/application.fam index 1c59d634b..45f0f181f 100644 --- a/applications/external/yatzee/application.fam +++ b/applications/external/yatzee/application.fam @@ -6,7 +6,7 @@ App( requires=["gui"], stack_size=4 * 1024, order=99, - fap_icon="images/yatzee_icon_10px.png", + fap_icon="yatzee_icon_10px.png", fap_category="Games", fap_icon_assets="images", ) diff --git a/applications/external/yatzee/images/yatzee_icon_10px.png b/applications/external/yatzee/yatzee_icon_10px.png similarity index 100% rename from applications/external/yatzee/images/yatzee_icon_10px.png rename to applications/external/yatzee/yatzee_icon_10px.png diff --git a/applications/main/bad_kb/application.fam b/applications/main/bad_kb/application.fam index 74764f8b8..165634967 100644 --- a/applications/main/bad_kb/application.fam +++ b/applications/main/bad_kb/application.fam @@ -11,5 +11,4 @@ App( stack_size=2 * 1024, icon="A_BadKb_14", order=70, - fap_libs=["assets"], ) diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index f18b8318e..0e99eb45a 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -8,5 +8,4 @@ App( stack_size=1 * 1024, icon="A_GPIO_14", order=50, - fap_libs=["assets"], ) diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index e5f2907af..f37f98a5c 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -13,7 +13,6 @@ App( icon="A_iButton_14", stack_size=2 * 1024, order=60, - fap_libs=["assets"], ) App( diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index c08cb80bd..5c65cd079 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -13,7 +13,6 @@ App( icon="A_Infrared_14", stack_size=3 * 1024, order=40, - fap_libs=["assets"], ) App( diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 60d5f8ac2..aa887cdd7 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -15,7 +15,6 @@ App( icon="A_125khz_14", stack_size=2 * 1024, order=20, - fap_libs=["assets"], ) App( diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam index 82010ffb4..6b32e0225 100644 --- a/applications/main/u2f/application.fam +++ b/applications/main/u2f/application.fam @@ -11,5 +11,4 @@ App( stack_size=2 * 1024, icon="A_U2F_14", order=80, - fap_libs=["assets"], ) diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam index fe8d59bd9..854e72199 100644 --- a/applications/main/xtreme_app/application.fam +++ b/applications/main/xtreme_app/application.fam @@ -11,5 +11,4 @@ App( stack_size=2 * 1024, icon="A_Xtreme_14", order=90, - fap_libs=["assets"], ) diff --git a/assets/SConscript b/assets/SConscript index 9bd273626..87242b78a 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -9,8 +9,9 @@ assetsenv = env.Clone( ) assetsenv.ApplyLibFlags() +icons_path = assetsenv.Dir("#/build/icons") if assetsenv["IS_BASE_FIRMWARE"] else assetsenv.Dir("compiled") icons = assetsenv.CompileIcons( - assetsenv.Dir("compiled"), assetsenv.Dir("#/assets/icons") + icons_path, assetsenv.Dir("#/assets/icons") ) assetsenv.Alias("icons", icons) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 734862ceb..6b70d8481 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -39,6 +39,7 @@ Header,+,applications/services/notification/notification_messages.h,, Header,+,applications/services/power/power_service/power.h,, Header,+,applications/services/rpc/rpc_app.h,, Header,+,applications/services/storage/storage.h,, +Header,+,build/icons/assets_icons.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_bus.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, @@ -3245,7 +3246,208 @@ Function,-,yn,double,"int, double" Function,-,ynf,float,"int, float" Variable,-,AHBPrescTable,const uint32_t[16], Variable,-,APBPrescTable,const uint32_t[8], +Variable,+,A_125khz_14,Icon, +Variable,+,A_BadKb_14,Icon, +Variable,+,A_GPIO_14,Icon, +Variable,+,A_Infrared_14,Icon, +Variable,+,A_Levelup_128x64,Icon, +Variable,+,A_Loading_24,Icon, +Variable,+,A_NFC_14,Icon, +Variable,+,A_Plugins_14,Icon, +Variable,+,A_Round_loader_8x8,Icon, +Variable,+,A_Settings_14,Icon, +Variable,+,A_Sub1ghz_14,Icon, +Variable,+,A_U2F_14,Icon, +Variable,+,A_Xtreme_14,Icon, +Variable,+,A_iButton_14,Icon, +Variable,+,ICON_PATHS,const IconPath[], +Variable,+,ICON_PATHS_COUNT,const size_t, Variable,-,ITM_RxBuffer,volatile int32_t, +Variable,+,I_125_10px,Icon, +Variable,+,I_ActiveConnection_50x64,Icon, +Variable,+,I_Apps_10px,Icon, +Variable,+,I_ArrowC_1_36x36,Icon, +Variable,+,I_ArrowUpEmpty_14x15,Icon, +Variable,+,I_ArrowUpFilled_14x15,Icon, +Variable,+,I_Auth_62x31,Icon, +Variable,+,I_BLE_Pairing_128x64,Icon, +Variable,+,I_Background_128x11,Icon, +Variable,+,I_BatteryBody_52x28,Icon, +Variable,+,I_Battery_16x16,Icon, +Variable,+,I_Battery_25x8,Icon, +Variable,+,I_Ble_connected_15x15,Icon, +Variable,+,I_Ble_disconnected_15x15,Icon, +Variable,+,I_Bluetooth_Connected_16x8,Icon, +Variable,+,I_Bluetooth_Idle_5x8,Icon, +Variable,+,I_ButtonCenter_7x7,Icon, +Variable,+,I_ButtonDown_7x4,Icon, +Variable,+,I_ButtonLeftSmall_3x5,Icon, +Variable,+,I_ButtonLeft_4x7,Icon, +Variable,+,I_ButtonRightSmall_3x5,Icon, +Variable,+,I_ButtonRight_4x7,Icon, +Variable,+,I_ButtonUp_7x4,Icon, +Variable,+,I_Button_18x18,Icon, +Variable,+,I_CC_Bluetooth_16x16,Icon, +Variable,+,I_CC_DarkMode_16x16,Icon, +Variable,+,I_CC_LefthandedMode_16x16,Icon, +Variable,+,I_CC_Lock_16x16,Icon, +Variable,+,I_CC_Settings_16x16,Icon, +Variable,+,I_CC_Xtreme_16x16,Icon, +Variable,+,I_Certification1_103x56,Icon, +Variable,+,I_Certification2_98x33,Icon, +Variable,+,I_Charging_lightning_9x10,Icon, +Variable,+,I_Charging_lightning_mask_9x10,Icon, +Variable,+,I_Circles_47x47,Icon, +Variable,+,I_Clock_18x18,Icon, +Variable,+,I_Connect_me_62x31,Icon, +Variable,+,I_Connected_62x31,Icon, +Variable,+,I_CoolHi_25x27,Icon, +Variable,+,I_CoolHi_hvr_25x27,Icon, +Variable,+,I_CoolLo_25x27,Icon, +Variable,+,I_CoolLo_hvr_25x27,Icon, +Variable,+,I_Cry_dolph_55x52,Icon, +Variable,+,I_DFU_128x50,Icon, +Variable,+,I_Dehumidify_25x27,Icon, +Variable,+,I_Dehumidify_hvr_25x27,Icon, +Variable,+,I_DolphinCommon_56x48,Icon, +Variable,+,I_DolphinMafia_115x62,Icon, +Variable,+,I_DolphinNice_96x59,Icon, +Variable,+,I_DolphinReadingSuccess_59x63,Icon, +Variable,+,I_DolphinWait_61x59,Icon, +Variable,+,I_Down_25x27,Icon, +Variable,+,I_Down_hvr_25x27,Icon, +Variable,+,I_Drive_112x35,Icon, +Variable,+,I_Dynamic_9x7,Icon, +Variable,+,I_Erase_pin_128x64,Icon, +Variable,+,I_Error_18x18,Icon, +Variable,+,I_Error_62x31,Icon, +Variable,+,I_EviSmile1_18x21,Icon, +Variable,+,I_EviSmile2_18x21,Icon, +Variable,+,I_EviWaiting1_18x21,Icon, +Variable,+,I_EviWaiting2_18x21,Icon, +Variable,+,I_Exit_25x27,Icon, +Variable,+,I_Exit_hvr_25x27,Icon, +Variable,+,I_FaceCharging_29x14,Icon, +Variable,+,I_FaceConfused_29x14,Icon, +Variable,+,I_FaceNopower_29x14,Icon, +Variable,+,I_FaceNormal_29x14,Icon, +Variable,+,I_Fishing_123x52,Icon, +Variable,+,I_Flash_25x27,Icon, +Variable,+,I_Flash_hvr_25x27,Icon, +Variable,+,I_Health_16x16,Icon, +Variable,+,I_HeatHi_25x27,Icon, +Variable,+,I_HeatHi_hvr_25x27,Icon, +Variable,+,I_HeatLo_25x27,Icon, +Variable,+,I_HeatLo_hvr_25x27,Icon, +Variable,+,I_Hidden_window_9x8,Icon, +Variable,+,I_InfraredArrowDown_4x8,Icon, +Variable,+,I_InfraredArrowUp_4x8,Icon, +Variable,+,I_InfraredLearnShort_128x31,Icon, +Variable,+,I_Input_25x27,Icon, +Variable,+,I_Input_hvr_25x27,Icon, +Variable,+,I_KeyBackspaceSelected_16x9,Icon, +Variable,+,I_KeyBackspace_16x9,Icon, +Variable,+,I_KeyKeyboardSelected_10x11,Icon, +Variable,+,I_KeyKeyboard_10x11,Icon, +Variable,+,I_KeySaveSelected_24x11,Icon, +Variable,+,I_KeySave_24x11,Icon, +Variable,+,I_Keychain_39x36,Icon, +Variable,+,I_Left_mouse_icon_9x9,Icon, +Variable,+,I_Lock_7x8,Icon, +Variable,+,I_Lockscreen,Icon, +Variable,+,I_MHz_25x11,Icon, +Variable,+,I_Mode_25x27,Icon, +Variable,+,I_Mode_hvr_25x27,Icon, +Variable,+,I_Modern_reader_18x34,Icon, +Variable,+,I_Move_flipper_26x39,Icon, +Variable,+,I_Mute_25x27,Icon, +Variable,+,I_Mute_hvr_25x27,Icon, +Variable,+,I_Muted_8x8,Icon, +Variable,+,I_NFC_dolphin_emulation_47x61,Icon, +Variable,+,I_NFC_manual_60x50,Icon, +Variable,+,I_Nfc_10px,Icon, +Variable,+,I_Off_25x27,Icon, +Variable,+,I_Off_hvr_25x27,Icon, +Variable,+,I_Ok_btn_9x9,Icon, +Variable,+,I_Ok_btn_pressed_13x13,Icon, +Variable,+,I_Pause_25x27,Icon, +Variable,+,I_Pause_hvr_25x27,Icon, +Variable,+,I_Percent_10x14,Icon, +Variable,+,I_Pin_arrow_up_7x9,Icon, +Variable,+,I_Pin_attention_dpad_29x29,Icon, +Variable,+,I_Pin_back_arrow_10x8,Icon, +Variable,+,I_Pin_cell_13x13,Icon, +Variable,+,I_Pin_pointer_5x3,Icon, +Variable,+,I_Pin_star_7x7,Icon, +Variable,+,I_Play_25x27,Icon, +Variable,+,I_Play_hvr_25x27,Icon, +Variable,+,I_Power_25x27,Icon, +Variable,+,I_Power_hvr_25x27,Icon, +Variable,+,I_Pressed_Button_13x13,Icon, +Variable,+,I_Quest_7x8,Icon, +Variable,+,I_RFIDDolphinReceive_97x61,Icon, +Variable,+,I_RFIDDolphinSend_97x61,Icon, +Variable,+,I_RFIDDolphinSuccess_108x57,Icon, +Variable,+,I_RFIDSmallChip_14x14,Icon, +Variable,+,I_Raw_9x7,Icon, +Variable,+,I_Release_arrow_18x15,Icon, +Variable,+,I_Right_mouse_icon_9x9,Icon, +Variable,+,I_Rotate_25x27,Icon, +Variable,+,I_Rotate_hvr_25x27,Icon, +Variable,+,I_Rpc_active_7x8,Icon, +Variable,+,I_SDQuestion_35x43,Icon, +Variable,+,I_SDcardFail_11x8,Icon, +Variable,+,I_SDcardMounted_11x8,Icon, +Variable,+,I_Scanning_123x52,Icon, +Variable,+,I_SmallArrowDown_3x5,Icon, +Variable,+,I_SmallArrowUp_3x5,Icon, +Variable,+,I_Smile_18x18,Icon, +Variable,+,I_Space_65x18,Icon, +Variable,+,I_Static_9x7,Icon, +Variable,+,I_Stop_25x27,Icon, +Variable,+,I_Stop_hvr_25x27,Icon, +Variable,+,I_Temperature_16x16,Icon, +Variable,+,I_Timer_25x27,Icon, +Variable,+,I_Timer_hvr_25x27,Icon, +Variable,+,I_TrackNext_25x27,Icon, +Variable,+,I_TrackNext_hvr_25x27,Icon, +Variable,+,I_TrackPrev_25x27,Icon, +Variable,+,I_TrackPrev_hvr_25x27,Icon, +Variable,+,I_Unlock_7x8,Icon, +Variable,+,I_Unplug_bg_bottom_128x10,Icon, +Variable,+,I_Unplug_bg_top_128x14,Icon, +Variable,+,I_Up_25x27,Icon, +Variable,+,I_Up_hvr_25x27,Icon, +Variable,+,I_Updating_32x40,Icon, +Variable,+,I_UsbTree_48x22,Icon, +Variable,+,I_Vol_down_25x27,Icon, +Variable,+,I_Vol_down_hvr_25x27,Icon, +Variable,+,I_Vol_up_25x27,Icon, +Variable,+,I_Vol_up_hvr_25x27,Icon, +Variable,+,I_Voldwn_6x6,Icon, +Variable,+,I_Voltage_16x16,Icon, +Variable,+,I_Volup_8x6,Icon, +Variable,+,I_WarningDolphin_45x42,Icon, +Variable,+,I_Warning_30x23,Icon, +Variable,+,I_back_10px,Icon, +Variable,+,I_badkb_10px,Icon, +Variable,+,I_dir_10px,Icon, +Variable,+,I_iButtonDolphinVerySuccess_108x52,Icon, +Variable,+,I_iButtonKey_49x44,Icon, +Variable,+,I_ibutt_10px,Icon, +Variable,+,I_ir_10px,Icon, +Variable,+,I_keyboard_10px,Icon, +Variable,+,I_loading_10px,Icon, +Variable,+,I_music_10px,Icon, +Variable,+,I_passport_DB,Icon, +Variable,+,I_passport_bad_46x49,Icon, +Variable,+,I_passport_happy_46x49,Icon, +Variable,+,I_passport_okay_46x49,Icon, +Variable,+,I_sub1_10px,Icon, +Variable,+,I_subrem_10px,Icon, +Variable,+,I_u2f_10px,Icon, +Variable,+,I_unknown_10px,Icon, +Variable,+,I_update_10px,Icon, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, diff --git a/lib/SConscript b/lib/SConscript index 3344a5661..711f281f1 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -26,6 +26,7 @@ env.Append( "#/lib/mlib", # Ugly hack Dir("../assets/compiled"), + Dir("../../icons"), ], SDK_HEADERS=[ *( @@ -43,6 +44,7 @@ env.Append( "variant", ) ), + File("../../icons/assets_icons.h"), File("xtreme/xtreme.h"), ], CPPDEFINES=[ From bc8ba134275bf23cf05711601158f2c62a5fb027 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 02:04:18 +0200 Subject: [PATCH 301/370] Remove apps screenshots --- applications/external/game15/images/Game15.png | Bin 2078 -> 0 bytes .../external/game15/images/Game15Popup.png | Bin 2267 -> 0 bytes .../external/game15/images/Game15Restore.png | Bin 2156 -> 0 bytes .../external/metronome/img/wave_left_4x14.png | Bin 144 -> 0 bytes .../external/metronome/img/wave_right_4x14.png | Bin 132 -> 0 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 applications/external/game15/images/Game15.png delete mode 100644 applications/external/game15/images/Game15Popup.png delete mode 100644 applications/external/game15/images/Game15Restore.png delete mode 100644 applications/external/metronome/img/wave_left_4x14.png delete mode 100644 applications/external/metronome/img/wave_right_4x14.png diff --git a/applications/external/game15/images/Game15.png b/applications/external/game15/images/Game15.png deleted file mode 100644 index f13c2907b531fa5441541ac53a96347f7684ae72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2078 zcmaJ?X;4#F7`+dqh_X0ztW`v?j-!=P+GYf?grJ}-Do{lzm_SiW1f-zs2@%F+T!Fd) zk+4{*gs@f#2mu0a86hN!VN;US5hMi2Lm+uVBxz6_E_T`3_;ZqOSZN7<&5%IS4 z*LF(`o=#Zvo=hG;$FLnRx+6=oFtPJ>d3NwEx5oHrO>2AKsr}~4w^BmNG~%2W?45#u zyww0~Hv*|XDdie{u11rf%ulo!zk;@>M+G+Eyr#%W^)&z%vJZbT`a(S< zvG3x&-!$^>b_ZvCV4S*hpmHg2UXOq!o&cOS2jJ#{xyQi9Wlt9sMx=3q3v){;(}}!8 z_!voEtiXSWG(C0LZ!VbCX6!6fICeCuhK4x9LYyv&A~3^l1ytJZ%Npn9W7S0#b)(+t& z4;#$&%m;~=BHq6Wfa-iUP74h(LC1go&kXCS$WmhMDq;hz zI4eC=;PE*{SLG6NeS7e}s9^b8I>b%j2`ljX>$sf?>u&#a&imPy&RKFX-Ld_y>$3I__Z!llU*Q*@2`Iu?(j!6A^!GKIi=>PUr%T3tnP z$bovDsw5aiFT-7#ruDSSno*?b)a(+q1UZAKy}7l1`AI<%a`8ksNdL68D!mO%r-Z5> zl_#?NY%!KP~q$IWQ@km1MSxDkD+0D8`s^Lwj&Ou5g z+Pc=b-mo|^+e|3nJ|K1djS|-o)MJhk6UCECh>rt&&?5Yck^SXx^vH;NAgM&k$>HP~ zwk~H4xG3pJma29GO@FRsU3fglE|V&bOU_*vTl3P94+$sodwu4O9{bRmh`!)7O0O7e zDsGpa#o%%q$-tjDScG=k6XI;GkKgk@Bn^ah8I)j0!<6Md;-RoAw@w*1c#_jWcFU*q!kW6W+?j{p9m@O=YWJLH zMIS^n8V~5f>nt7zfW^X0F7wfj{VH_gp+= z?@vjQkL+p?vNW0wto$A0Dx}rV9Ght;)Mo~jyzHlp{6s25rPB%b_M#UWi1E?kq={c4 z4OizOzUtScUiR51*BzQo)!j0hc34zr&gaWnXj$I6ELFe;+Bbj)?R=4EVyCP`C;69r ze(0lm@*C+js97Cj*|m=|8@=#~qRu!Lof_fSn}v(9Jvl!{cE`|^OTJxEH-3!3-XuBn zNOA;HGOOP>_3kaa;U82|J?{dZ4e_ose&^=v^fqQNVO%0lxCg@+iP!&HdB0ud?X9-- zD#Ju|?Yr@(u$!1EVO-_Cpuk{OR60`{m5Wv#VV5hoE`beeiaK}L!o}N+LAUe*eu!?L zoIaJqH3T`Irtin4Ryh?=gqeP*F}8J{QPO13k;AYSwfc#!rUn_$?r+ik@PWq;FW2(z HznuIBq;E*m diff --git a/applications/external/game15/images/Game15Popup.png b/applications/external/game15/images/Game15Popup.png deleted file mode 100644 index 1df14729fc0e2bf59b9603ba5a0ee159ccadc6e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2267 zcmZWre^3)=8vX*M@y8fw9hy50#9qfKQ_h2;9E}=4%N4O#VJZOwLU7b#;Y1(=G$D|) z?J(L?`~~Wv1d3D$7%Tzf5!qy}RuoAhN|FkMFmSS%01Lz_|L*ZPK_SXY><4p9FQr*^AZ}ehiLg?V$r3hE=udcN7n^T#lr#PX6^&avB8#rhG{L_5NXdYu2;vxGc>+uaZ<7qn zOG?L9fen}NJvRcd6AL9zw=2+_MR|}mNeCHHquJEQ0Wog_Q2PdmZnzK*gt|;FMC+BB zZn;hc15tw4((3jFnkvndEiE)af6_S8o5V=7V@BH5r6a7~2!rZ=`K>8|W~8`RldSWPNthz7uA504-&SX*FuPcT%i zRdf&j_($Uy=(m~FB#=w>)E!37wa&!WUq=GDfGd`xbLP)1A>a=yJ%kyemDiB)u9y0j zmdHJFhcHo5B1hks;tIU}qN*G@VG<0rF+?nlJXM*BRiUdzyFBF{revHnyC%|{4<=sf zW2Ke$d$Kl5QB)h$h0|2vQU&2evgR40tR}~SScWOna;K!ul`br2;B9`(qb9CL2QGAF zAtWoXR7JHfTP!K2)TAmWq?kUrvD|D&uOkLqmQmwqv@yp3_$UT%u3L=>pEoXz>my3D zet|Chm43Kr{oe#!RT%|)#WL6RkihhMk1`zvYid&IlikojC678c@vwhinbNz-Am2|~ z0(5id0@Fvbz~S`P>FOMzwM$Rv;e~Kf+mIY_etf-9X#L5OO35kq9gO7A2gMcT0bwVci}Xe4{jlUgyv7e4l165j3nl(7;}H|U!o zJzed};mqD=R%T)ojYyLC%BJ55d(Oj?H6x2V%SzB$8x@@YwP9{-*ce6*H8e-gjcWs6 zo;ll*Pk+}*6<*(06MhIe=OjH*@69YaJFbuCKrXKh7d)q_ zz1sWd{3ucTK5FB|1?MZ_qSdyEP>IAwQ;p9AR~u-mGPYoZZvGSvfNZjOtn(-f@%?l5 z(2=VI$FcI`hfgD;MTKLLZFOB=`?Dsx>TyF6Oa4IYJ+5Dr{2Uu}fC9FT+Z_kyi2z~b zbIl{^_iu0I0->4MxJ%)pM^EiLPG-g0`_1}HBvjmXm;yv5Pnn62(xU|(wqA)dN3}n| zMQNUO5MAC)Q$>8@%+vdxU2jCj2@YKlPlI6fG&S z7~x%Fh_ZY5h^9IC8ryexoe`aVY#=yX@9-WECD%}kJ2wmGHzR_dswaAM-yMUFFW36{ z>V};#wnei&l5LCJo+=CE%*Z0&=WLr5OyLqHnMMh*+78iGr?rZD`D&OQ()imqS7BZh z5Vib^AjK_&9Lg_tc#CL8`vAKhdO-l&#ElXu?$W{FV+^b!gqqupxtD|Cw>SJ9DotBX zcF7o12KsaALp~_h*CM6=Y3I!;4&iOy>uFJUdUQnYBA83D3GZoXnKrbNa6%8S0aw1T zA;uzsJAH!Q^(5tGDXGm;nszrNq0E3lK3twa+0m&qx=iY)lfo@{|3m+m$J1xpT*v$8 zzwf0dN05OCfL-o^Q_)PiVLSg!6LH4jU{2!WMoe_XM0?(Wf$Q%+~On5Hk_s_zHjXEfF3agM-R@i0#6?upOWGKC0zBu z>8viq)ru$P@GQPH)-qr>rr*#QIyyN+98E3(6`v@6-Gm@Tsf)A_W><9R0RFcE;{LKH KM!GZOr_%0p2jT8RpRf&qLG3|7O#kd%P<=;%;^ z7Evn(NEL>sC@2(!1Z-6ztr8UyC6b3j%}PQNP?BvDNE!+qFZ|fKyR&=F{l0U)^WDwa z8Ae_>|I_&ZfQ6x3H|+*smi-n3oF9p;8!rBhcbT*En)2)7pF)xYyF==)=IuMO@l*e{OJ@D*oSLM+jKd*Ccg75QMYeTr zaH%3F3xLdX_7q*ZJw%w^-=R04CZ{ znTOJ8LxemQZB?zvXDBnUXbA{jiJ9CZsmEb!RbQQPeJM}h61PLj=DF&Sg6Ba5!43rR z?@@V@$FB$8I@elkd;@nZ9-=u4oGCee~zL z7r3i^NTRBZS}w4iB9>VWe6J`cIL|$G@_)z)SfwQ6UERR7f^Uo~kkbV}avnfgnuai- zyqdk@2Z)RJASTzy9z0SZq1mhL+Xb_WQ5iuK_9;ZAn#l&9o+{N$epp&+(8u1P(5EG1%bJCjf6jNFb@ zAc*!&$Q)<`@f}A{e48Uuc^PNTQf7ZFQ+&m5?4Ibs#u0f^W0ZBr%P5)c+cIP;#J)Fb z^Rba|%N2gpFZo2TWS;;4?BU*a8L8hPpavCEUT1q=d^*O7)5fLi7hi*FVF#}Wm3ivc z*BC_aVl|#?A@Pz?;BBPXdILA#_m8w-NaJS8NCnkAYjO-^NrV}RR>{$A`FDEe3i zm=}q9Mx~u6$}|=n1UDiStE^z&@zC-$Y=fY>Oy5292%q*HLu@}jUbdz+ z%@Zom9_!Nkbiap%99Oa4-#v?F=sP3Uw;PI@OAGgU=))L?kTwdhRaZ)y(f(%}($()N zB)xk}wThG4f#AEvCU0vHtm8j9An8Jfs*Y(^m{^pak*h(E?y29_Wpm*PD|bvz)wUd> z9IW`NySLZKpWSIVtO)PrcUxy^%^+8ZO&{@p6?5^jmp)wYLe zp5_km|2psw$=BmE@YHEiT@hRqGjy}#TysQ_5GcB!A@Gnnyz1+rF-1%L_S0G@x@QSm z+v6DY`gCOtmy9|nkIOVxHz5Pk@(m8CwH$Hie>gp%yY_WBwM_9ns#4=-hwHWduNKi& zAIkKRxvWtVrB-;)EPJlcRGT9+St>#;kM(>zNjNOGss@14Wy;N%C$LI-C2c;nf3*8e zzOc9x+M#I>bHDC*jiu)T1k!F~d#MB2;o~O~DdpVmkZU@WkEC`V@Ldk9!?AiV->ag0hRvw#+QW;u` z-~Q&n5k;a^_l`x&uOx{uU^xK0rwmxuDC%19pHcV>^^TC!)t0hD+hI9K`B)Ht*l_Q3 zL34&Hb%t@E#Aqq;pr${&+Yq3LdAahsCmgy>?pS|!v5-#dytrPDCzKSb8~UStI&#pD z2xgoJLIF0uF`rFaXwB`Bq}F%7JRPvChXkAldfzml@fEHX0ts!h_P zZ!?q35mjtL4peF44ac8{bx$jsJGtrd)?{i`{f8&dh6&jwE@9&Kza3vQ)#-9of4Fop=sqpgMTI96j$<3RQTtCJWJ6MH_o@P zD_jWxC+o>Bhwa7-VjvTv@pwi0^a7*xES`?yIx21;u<_<=FgGRmwwJ*QqE&~yiL`$r zfGa!lvI6;>1s;*bKn;gMnDKc2iWH!rs;7%%h(vhu4}NAIhlT`43l26e_H&E4*!3EE jnV&mbIvhA~fPrE2w#elHp1mi48W=oX{an^LB{Ts5YlI{H diff --git a/applications/external/metronome/img/wave_right_4x14.png b/applications/external/metronome/img/wave_right_4x14.png deleted file mode 100644 index af249ee5b94055ba631122c6cd4588ca69e00b62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^EI`c1!2~4RIkfyi0_l#vjtmSN`?>!lvI6;>1s;*b zKsAR!nDKc2iWH!rnx~6nh(vfYL(_i)hDQf}DsY`U{@}pRkcSEq8Zs8#ry4i>`~RPx Yfj2vHxnZ!40#FBor>mdKI;Vst0FNsqT>t<8 From 788b395be2edff7c2344aecad6807e831e96e943 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 02:06:34 +0200 Subject: [PATCH 302/370] Format --- assets/SConscript | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/assets/SConscript b/assets/SConscript index 87242b78a..848b730f4 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -9,10 +9,12 @@ assetsenv = env.Clone( ) assetsenv.ApplyLibFlags() -icons_path = assetsenv.Dir("#/build/icons") if assetsenv["IS_BASE_FIRMWARE"] else assetsenv.Dir("compiled") -icons = assetsenv.CompileIcons( - icons_path, assetsenv.Dir("#/assets/icons") +icons_path = ( + assetsenv.Dir("#/build/icons") + if assetsenv["IS_BASE_FIRMWARE"] + else assetsenv.Dir("compiled") ) +icons = assetsenv.CompileIcons(icons_path, assetsenv.Dir("#/assets/icons")) assetsenv.Alias("icons", icons) From 5e2d01f1590a092e460a78087ba6ded47dc763f1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jun 2023 04:37:39 +0300 Subject: [PATCH 303/370] Update OFW PR 2782 --- lib/nfc/nfc_worker.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index d6618b5b7..60847c3c7 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -1029,7 +1029,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { mf_classic_get_sector_trailer_by_sector(data, i); uint8_t current_key[6]; - memcpy(current_key, &key, 6); + nfc_util_num2bytes(key, 6, current_key); if(mf_classic_is_key_found(data, i, MfClassicKeyA) && memcmp(sec_trailer->key_a, current_key, 6) == 0) { @@ -1055,7 +1055,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { mf_classic_get_sector_trailer_by_sector(data, i); uint8_t current_key[6]; - memcpy(current_key, &key, 6); + nfc_util_num2bytes(key, 6, current_key); if(mf_classic_is_key_found(data, i, MfClassicKeyB) && memcmp(sec_trailer->key_b, current_key, 6) == 0) { @@ -1074,7 +1074,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } - memcpy(&prev_key, &key, sizeof(key)); + prev_key = key; } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; mf_classic_read_sector(&tx_rx, data, i); From 0c3a8a3ca1982103e7c43c68896dc1aa7a4bd92b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 03:39:09 +0200 Subject: [PATCH 304/370] Fix scroll acceleration wrap-around --- .../main/archive/views/archive_browser_view.c | 24 +++++++++++++++++++ .../main/archive/views/archive_browser_view.h | 2 +- .../services/gui/modules/file_browser.c | 24 ++++++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 4d330532e..f3a586cee 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -425,11 +425,20 @@ static bool archive_view_input(InputEvent* event, void* context) { } else { scroll_speed = model->button_held_for_ticks > 9 ? 4 : 2; } + } else if(model->button_held_for_ticks < 0) { + scroll_speed = 0; } if(event->key == InputKeyUp) { if(model->item_idx < scroll_speed) { scroll_speed = model->item_idx; + if(scroll_speed == 0) { + if(model->button_held_for_ticks > 0) { + model->button_held_for_ticks = -1; + } else { + scroll_speed = 1; + } + } } model->item_idx = @@ -442,11 +451,22 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFavMoveUp, browser->context); } model->scroll_counter = 0; + + if(model->button_held_for_ticks < -1) { + model->button_held_for_ticks = 0; + } model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { int32_t count = model->item_cnt; if(model->item_idx >= (count - scroll_speed)) { scroll_speed = model->item_cnt - model->item_idx - 1; + if(scroll_speed == 0) { + if(model->button_held_for_ticks > 0) { + model->button_held_for_ticks = -1; + } else { + scroll_speed = 1; + } + } } model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; @@ -458,6 +478,10 @@ static bool archive_view_input(InputEvent* event, void* context) { browser->callback(ArchiveBrowserEventFavMoveDown, browser->context); } model->scroll_counter = 0; + + if(model->button_held_for_ticks < -1) { + model->button_held_for_ticks = 0; + } model->button_held_for_ticks += 1; } }, diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 47e820599..43322eaf8 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -111,7 +111,7 @@ typedef struct { int32_t list_offset; size_t scroll_counter; - uint32_t button_held_for_ticks; + int32_t button_held_for_ticks; } ArchiveBrowserViewModel; void archive_browser_set_callback( diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 4e6a96183..b547077f7 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -148,7 +148,7 @@ typedef struct { bool hide_ext; size_t scroll_counter; - uint32_t button_held_for_ticks; + int32_t button_held_for_ticks; } FileBrowserModel; static const Icon* BrowserItemIcons[] = { @@ -670,11 +670,20 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { } else { scroll_speed = model->button_held_for_ticks > 9 ? 5 : 3; } + } else if(model->button_held_for_ticks < 0) { + scroll_speed = 0; } if(event->key == InputKeyUp) { if(model->item_idx < scroll_speed) { scroll_speed = model->item_idx; + if(scroll_speed == 0) { + if(model->button_held_for_ticks > 0) { + model->button_held_for_ticks = -1; + } else { + scroll_speed = 1; + } + } } model->item_idx = @@ -690,11 +699,21 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { } model->scroll_counter = 0; + if(model->button_held_for_ticks < -1) { + model->button_held_for_ticks = 0; + } model->button_held_for_ticks += 1; } else if(event->key == InputKeyDown) { int32_t count = model->item_cnt; if(model->item_idx + scroll_speed >= count) { scroll_speed = count - model->item_idx - 1; + if(scroll_speed == 0) { + if(model->button_held_for_ticks > 0) { + model->button_held_for_ticks = -1; + } else { + scroll_speed = 1; + } + } } model->item_idx = (model->item_idx + scroll_speed) % model->item_cnt; @@ -709,6 +728,9 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { } model->scroll_counter = 0; + if(model->button_held_for_ticks < -1) { + model->button_held_for_ticks = 0; + } model->button_held_for_ticks += 1; } }, From b68dc9087aeb8ddadcf8044bd55a58104f15a429 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 04:22:29 +0200 Subject: [PATCH 305/370] Desktop new full keybind system + fix settng logic --- applications/services/desktop/desktop.c | 50 +++++- applications/services/desktop/desktop.h | 3 + .../services/desktop/desktop_settings.h | 27 ++- .../desktop/scenes/desktop_scene_lock_menu.c | 2 - .../desktop/scenes/desktop_scene_locked.c | 1 - .../desktop/scenes/desktop_scene_main.c | 30 +--- .../services/desktop/views/desktop_events.h | 7 +- .../desktop/views/desktop_view_main.c | 35 +--- .../desktop_settings/desktop_settings_app.c | 4 +- .../desktop_settings/desktop_settings_app.h | 5 +- .../scenes/desktop_settings_scene_config.h | 4 +- .../scenes/desktop_settings_scene_favorite.c | 134 -------------- .../desktop_settings_scene_keybinds_choose.c | 168 ++++++++++++++++++ .../desktop_settings_scene_keybinds_key.c | 57 ++++++ .../desktop_settings_scene_keybinds_type.c | 56 ++++++ .../scenes/desktop_settings_scene_pin_auth.c | 5 +- .../desktop_settings_scene_pin_disable.c | 6 +- .../scenes/desktop_settings_scene_pin_menu.c | 2 +- .../desktop_settings_scene_pin_setup_done.c | 5 +- .../scenes/desktop_settings_scene_start.c | 45 ++--- 20 files changed, 399 insertions(+), 247 deletions(-) delete mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_type.c diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index f78150102..4e42b4104 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -153,12 +153,7 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { return true; case DesktopGlobalAfterAppFinished: animation_manager_load_and_continue_animation(desktop->animation_manager); - // TODO: Implement a message mechanism for loading settings and (optionally) - // locking and unlocking - DESKTOP_SETTINGS_LOAD(&desktop->settings); - desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); - desktop_auto_lock_arm(desktop); return true; case DesktopGlobalAutoLock: @@ -450,6 +445,43 @@ FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance) { return instance->status_pubsub; } +static const KeybindType keybind_types[] = { + [InputTypeShort] = KeybindTypePress, + [InputTypeLong] = KeybindTypeHold, +}; + +static const KeybindKey keybind_keys[] = { + [InputKeyUp] = KeybindKeyUp, + [InputKeyDown] = KeybindKeyDown, + [InputKeyRight] = KeybindKeyRight, + [InputKeyLeft] = KeybindKeyLeft, +}; + +void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key) { + if(_type != InputTypeShort && _type != InputTypeLong) return; + if(_key != InputKeyUp && _key != InputKeyDown && _key != InputKeyRight && _key != InputKeyLeft) + return; + + KeybindType type = keybind_types[_type]; + KeybindKey key = keybind_keys[_key]; + const char* keybind = instance->settings.keybinds[type][key].data; + if(!strnlen(keybind, MAX_KEYBIND_LENGTH)) return; + + if(!strncmp(keybind, "Archive", MAX_KEYBIND_LENGTH)) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventOpenArchive); + } else if(!strncmp(keybind, "Device Info", MAX_KEYBIND_LENGTH)) { + loader_start_with_gui_error(instance->loader, "Power", "about_battery"); + } else if(!strncmp(keybind, "Lock Menu", MAX_KEYBIND_LENGTH)) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventOpenLockMenu); + } else if(!strncmp(keybind, "Lock Keypad", MAX_KEYBIND_LENGTH)) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventLockKeypad); + } else if(!strncmp(keybind, "Lock with PIN", MAX_KEYBIND_LENGTH)) { + view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventLockWithPin); + } else { + loader_start_with_gui_error(instance->loader, keybind, NULL); + } +} + int32_t desktop_srv(void* p) { UNUSED(p); @@ -466,6 +498,14 @@ int32_t desktop_srv(void* p) { } if(!ok) { memset(&desktop->settings, 0, sizeof(desktop->settings)); + strcpy(desktop->settings.keybinds[KeybindTypePress][KeybindKeyUp].data, "Lock Menu"); + strcpy(desktop->settings.keybinds[KeybindTypePress][KeybindKeyDown].data, "Archive"); + strcpy(desktop->settings.keybinds[KeybindTypePress][KeybindKeyRight].data, "Passport"); + strcpy( + desktop->settings.keybinds[KeybindTypePress][KeybindKeyLeft].data, + EXT_PATH("apps/Misc/nightstand.fap")); + strcpy(desktop->settings.keybinds[KeybindTypeHold][KeybindKeyRight].data, "Device Info"); + strcpy(desktop->settings.keybinds[KeybindTypeHold][KeybindKeyLeft].data, "Lock with PIN"); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); furi_hal_rtc_set_pin_fails(0); } diff --git a/applications/services/desktop/desktop.h b/applications/services/desktop/desktop.h index 4eab24fcc..4c7551f5c 100644 --- a/applications/services/desktop/desktop.h +++ b/applications/services/desktop/desktop.h @@ -1,6 +1,7 @@ #pragma once #include +#include typedef struct Desktop Desktop; @@ -15,3 +16,5 @@ typedef struct { } DesktopStatus; FuriPubSub* desktop_api_get_status_pubsub(Desktop* instance); + +void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key); diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index fb269eaff..74acf6bfb 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -5,9 +5,8 @@ #include #include #include -#include -#define DESKTOP_SETTINGS_VER (9) +#define DESKTOP_SETTINGS_VER (11) #define DESKTOP_SETTINGS_OLD_PATH CFG_PATH("desktop.settings") #define DESKTOP_SETTINGS_PATH INT_PATH(".desktop.settings") @@ -18,7 +17,7 @@ #define MAX_PIN_SIZE 10 #define MIN_PIN_SIZE 4 -#define MAX_APP_LENGTH 128 +#define MAX_KEYBIND_LENGTH 64 typedef struct { InputKey data[MAX_PIN_SIZE]; @@ -26,16 +25,28 @@ typedef struct { } PinCode; typedef struct { - bool is_external; - char name_or_path[MAX_APP_LENGTH]; -} FavoriteApp; + char data[MAX_KEYBIND_LENGTH]; +} Keybind; + +typedef enum { + KeybindTypePress, + KeybindTypeHold, + KeybindTypeCount, +} KeybindType; + +typedef enum { + KeybindKeyUp, + KeybindKeyDown, + KeybindKeyRight, + KeybindKeyLeft, + KeybindKeyCount, +} KeybindKey; typedef struct { - FavoriteApp favorite_primary; - FavoriteApp favorite_secondary; PinCode pin_code; uint32_t auto_lock_delay_ms; bool auto_lock_with_pin; + Keybind keybinds[KeybindTypeCount][KeybindKeyCount]; } DesktopSettings; bool DESKTOP_SETTINGS_SAVE(DesktopSettings* x); diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index 480bef951..d47a79319 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -23,7 +23,6 @@ void desktop_scene_lock_menu_callback(DesktopEvent event, void* context) { void desktop_scene_lock_menu_on_enter(void* context) { Desktop* desktop = (Desktop*)context; - DESKTOP_SETTINGS_LOAD(&desktop->settings); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); desktop_lock_menu_set_pin_state( @@ -62,7 +61,6 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { int check_pin_changed = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); if(check_pin_changed) { - DESKTOP_SETTINGS_LOAD(&desktop->settings); if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock_menu_set_pin_state(desktop->lock_menu, true); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index 3121d1d38..ce2a6b96e 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -54,7 +54,6 @@ void desktop_scene_locked_on_enter(void* context) { furi_record_close(RECORD_GUI); if(pin_locked) { - DESKTOP_SETTINGS_LOAD(&desktop->settings); desktop_view_locked_lock(desktop->locked_view, true); uint32_t pin_timeout = desktop_pin_lock_get_fail_timeout(); if(pin_timeout > 0) { diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 1d425d73a..b4ab2e4e8 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -61,12 +61,6 @@ static void } #endif -static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) { - if(strlen(application->name_or_path) > 0) { - loader_start_with_gui_error(desktop->loader, application->name_or_path, NULL); - } -} - void desktop_scene_main_callback(DesktopEvent event, void* context) { Desktop* desktop = (Desktop*)context; if(desktop->in_transition) return; @@ -113,7 +107,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; break; - case DesktopMainEventLock: + case DesktopMainEventLockKeypad: + desktop_lock(desktop, false); + consumed = true; + break; + + case DesktopMainEventLockWithPin: desktop_lock(desktop, true); consumed = true; break; @@ -130,16 +129,6 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; break; } - case DesktopMainEventOpenFavoritePrimary: - DESKTOP_SETTINGS_LOAD(&desktop->settings); - desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_primary); - consumed = true; - break; - case DesktopMainEventOpenFavoriteSecondary: - DESKTOP_SETTINGS_LOAD(&desktop->settings); - desktop_scene_main_start_favorite(desktop, &desktop->settings.favorite_secondary); - consumed = true; - break; case DesktopAnimationEventCheckAnimation: animation_manager_check_blocking_process(desktop->animation_manager); consumed = true; @@ -150,15 +139,10 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; case DesktopAnimationEventInteractAnimation: if(!animation_manager_interact_process(desktop->animation_manager)) { - loader_start(desktop->loader, "Passport", NULL, NULL); + desktop_run_keybind(desktop, InputTypeShort, InputKeyRight); } consumed = true; break; - case DesktopMainEventOpenClock: { - loader_start_with_gui_error( - desktop->loader, EXT_PATH("apps/Misc/Nightstand.fap"), NULL); - break; - } case DesktopLockedEventUpdate: desktop_view_locked_update(desktop->locked_view); consumed = true; diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index c0da403da..5fada45c1 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -3,14 +3,11 @@ typedef enum { DesktopMainEventOpenLockMenu, DesktopMainEventOpenArchive, - DesktopMainEventOpenFavoritePrimary, - DesktopMainEventOpenFavoriteSecondary, DesktopMainEventOpenMenu, DesktopMainEventOpenDebug, DesktopMainEventOpenPowerOff, - DesktopMainEventLock, - - DesktopMainEventOpenClock, + DesktopMainEventLockKeypad, + DesktopMainEventLockWithPin, DesktopLockedEventOpenPowerOff, DesktopLockedEventUnlocked, diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index bd2625f97..54c2f5566 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -36,34 +36,15 @@ bool desktop_main_input_callback(InputEvent* event, void* context) { DesktopMainView* main_view = context; - if(event->type == InputTypeShort) { + // DesktopMainEventOpenDebug + if(event->type == InputTypeShort || event->type == InputTypeLong) { if(event->key == InputKeyOk) { - main_view->callback(DesktopMainEventOpenMenu, main_view->context); - } else if(event->key == InputKeyUp) { - main_view->callback(DesktopMainEventOpenLockMenu, main_view->context); - } else if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpenArchive, main_view->context); - } else if(event->key == InputKeyRight) { - Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "Passport", NULL, NULL); - furi_record_close(RECORD_LOADER); - } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventOpenClock, main_view->context); - } - // Right key is handled by animation manager - } else if(event->type == InputTypeLong) { - if(event->key == InputKeyOk) { - main_view->callback(DesktopAnimationEventNewIdleAnimation, main_view->context); - } else if(event->key == InputKeyUp) { - main_view->callback(DesktopMainEventOpenFavoritePrimary, main_view->context); - } else if(event->key == InputKeyDown) { - main_view->callback(DesktopMainEventOpenFavoriteSecondary, main_view->context); - } else if(event->key == InputKeyRight) { - Loader* loader = furi_record_open(RECORD_LOADER); - loader_start(loader, "Power", "about_battery", NULL); - furi_record_close(RECORD_LOADER); - } else if(event->key == InputKeyLeft) { - main_view->callback(DesktopMainEventLock, main_view->context); + main_view->callback( + event->type == InputTypeShort ? DesktopMainEventOpenMenu : + DesktopAnimationEventNewIdleAnimation, + main_view->context); + } else { + desktop_run_keybind((Desktop*)main_view->context, event->type, event->key); } } diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index afb5d59ec..563ddb0ed 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -22,6 +22,7 @@ DesktopSettingsApp* desktop_settings_app_alloc() { DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp)); app->gui = furi_record_open(RECORD_GUI); + app->desktop = furi_record_open(RECORD_DESKTOP); app->dialogs = furi_record_open(RECORD_DIALOGS); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app); @@ -85,13 +86,13 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { scene_manager_free(app->scene_manager); // Records furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_DESKTOP); furi_record_close(RECORD_GUI); free(app); } extern int32_t desktop_settings_app(void* p) { DesktopSettingsApp* app = desktop_settings_app_alloc(); - DESKTOP_SETTINGS_LOAD(&app->settings); if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) { scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto); } else { @@ -99,6 +100,7 @@ extern int32_t desktop_settings_app(void* p) { } view_dispatcher_run(app->view_dispatcher); + DESKTOP_SETTINGS_SAVE(&app->desktop->settings); desktop_settings_app_free(app); return 0; } diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index 6f97564c9..b4eba1e89 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include "views/desktop_settings_view_pin_setup_howto.h" #include "views/desktop_settings_view_pin_setup_howto2.h" @@ -24,9 +24,8 @@ typedef enum { } DesktopSettingsAppView; typedef struct { - DesktopSettings settings; - Gui* gui; + Desktop* desktop; DialogsApp* dialogs; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h index 5bc52172b..f78c0d6ba 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h @@ -1,5 +1,7 @@ ADD_SCENE(desktop_settings, start, Start) -ADD_SCENE(desktop_settings, favorite, Favorite) +ADD_SCENE(desktop_settings, keybinds_type, KeybindsType) +ADD_SCENE(desktop_settings, keybinds_key, KeybindsKey) +ADD_SCENE(desktop_settings, keybinds_choose, KeybindsChoose) ADD_SCENE(desktop_settings, pin_menu, PinMenu) ADD_SCENE(desktop_settings, pin_auth, PinAuth) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c deleted file mode 100644 index 77c10a05f..000000000 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ /dev/null @@ -1,134 +0,0 @@ -#include "../desktop_settings_app.h" -#include "applications.h" -#include "desktop_settings_scene.h" -#include -#include -#include - -#define EXTERNAL_APPLICATION_NAME ("[External Application]") -#define EXTERNAL_APPLICATION_INDEX (FLIPPER_APPS_COUNT + 1) - -static bool favorite_fap_selector_item_callback( - FuriString* file_path, - void* context, - uint8_t** icon_ptr, - FuriString* item_name) { - UNUSED(context); - Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); - furi_record_close(RECORD_STORAGE); - return success; -} - -static bool favorite_fap_selector_file_exists(char* file_path) { - Storage* storage = furi_record_open(RECORD_STORAGE); - bool exists = storage_file_exists(storage, file_path); - furi_record_close(RECORD_STORAGE); - return exists; -} - -static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) { - DesktopSettingsApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void desktop_settings_scene_favorite_on_enter(void* context) { - DesktopSettingsApp* app = context; - Submenu* submenu = app->submenu; - submenu_reset(submenu); - - uint32_t primary_favorite = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); - uint32_t pre_select_item = 0; - FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary : - &app->settings.favorite_secondary; - - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - submenu_add_item( - submenu, - FLIPPER_APPS[i].name, - i, - desktop_settings_scene_favorite_submenu_callback, - app); - - // Select favorite item in submenu - if(!curr_favorite_app->is_external && - !strcmp(FLIPPER_APPS[i].name, curr_favorite_app->name_or_path)) { - pre_select_item = i; - } - } - - submenu_add_item( - submenu, - EXTERNAL_APPLICATION_NAME, - EXTERNAL_APPLICATION_INDEX, - desktop_settings_scene_favorite_submenu_callback, - app); - if(curr_favorite_app->is_external) { - pre_select_item = EXTERNAL_APPLICATION_INDEX; - } - - submenu_set_header( - submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:"); - submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. - - view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); -} - -bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { - DesktopSettingsApp* app = context; - bool consumed = false; - FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); - - uint32_t primary_favorite = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); - FavoriteApp* curr_favorite_app = primary_favorite ? &app->settings.favorite_primary : - &app->settings.favorite_secondary; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == EXTERNAL_APPLICATION_INDEX) { - const DialogsFileBrowserOptions browser_options = { - .extension = ".fap", - .icon = &I_unknown_10px, - .skip_assets = true, - .hide_ext = true, - .item_loader_callback = favorite_fap_selector_item_callback, - .item_loader_context = app, - .base_path = EXT_PATH("apps"), - }; - - // Select favorite fap in file browser - if(favorite_fap_selector_file_exists(curr_favorite_app->name_or_path)) { - furi_string_set_str(temp_path, curr_favorite_app->name_or_path); - } - - if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { - submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene - curr_favorite_app->is_external = true; - strncpy( - curr_favorite_app->name_or_path, - furi_string_get_cstr(temp_path), - MAX_APP_LENGTH); - consumed = true; - } - } else { - curr_favorite_app->is_external = false; - strncpy( - curr_favorite_app->name_or_path, FLIPPER_APPS[event.event].name, MAX_APP_LENGTH); - consumed = true; - } - if(consumed) { - scene_manager_previous_scene(app->scene_manager); - }; - consumed = true; - } - - furi_string_free(temp_path); - return consumed; -} - -void desktop_settings_scene_favorite_on_exit(void* context) { - DesktopSettingsApp* app = context; - DESKTOP_SETTINGS_SAVE(&app->settings); - submenu_reset(app->submenu); -} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c new file mode 100644 index 000000000..d65a20e5b --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c @@ -0,0 +1,168 @@ +#include "../desktop_settings_app.h" +#include "applications.h" +#include "desktop_settings_scene.h" +#include +#include +#include + +static const char* EXTRA_OPTIONS[] = { + "Apps", + "Archive", + "Device Info", + "Lock Menu", + "Lock Keypad", + "Lock with PIN", + "Passport", +}; +#define EXTRA_OPTIONS_COUNT COUNT_OF(EXTRA_OPTIONS) + +static bool keybinds_fap_selector_item_callback( + FuriString* file_path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + UNUSED(context); + Storage* storage = furi_record_open(RECORD_STORAGE); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); + furi_record_close(RECORD_STORAGE); + return success; +} + +static void desktop_settings_scene_keybinds_choose_main_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + KeybindType type = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); + KeybindKey key = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); + char* keybind = app->desktop->settings.keybinds[type][key].data; + + strncpy(keybind, FLIPPER_APPS[index].name, MAX_KEYBIND_LENGTH); + scene_manager_previous_scene(app->scene_manager); + scene_manager_previous_scene(app->scene_manager); + scene_manager_previous_scene(app->scene_manager); +} + +static void desktop_settings_scene_keybinds_choose_ext_callback(void* context, uint32_t index) { + UNUSED(index); + DesktopSettingsApp* app = context; + KeybindType type = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); + KeybindKey key = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); + char* keybind = app->desktop->settings.keybinds[type][key].data; + + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .icon = &I_unknown_10px, + .skip_assets = true, + .hide_ext = true, + .item_loader_callback = keybinds_fap_selector_item_callback, + .item_loader_context = app, + .base_path = EXT_PATH("apps"), + }; + + // Select keybind fap in file browser + FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); + if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { + furi_string_set_str(temp_path, keybind); + } + furi_record_close(RECORD_STORAGE); + + if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { + submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene + strncpy(keybind, furi_string_get_cstr(temp_path), MAX_KEYBIND_LENGTH); + scene_manager_previous_scene(app->scene_manager); + scene_manager_previous_scene(app->scene_manager); + scene_manager_previous_scene(app->scene_manager); + } + furi_string_free(temp_path); +} + +static void desktop_settings_scene_keybinds_choose_extra_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + KeybindType type = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); + KeybindKey key = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); + char* keybind = app->desktop->settings.keybinds[type][key].data; + + strncpy(keybind, EXTRA_OPTIONS[index - FLIPPER_APPS_COUNT], MAX_KEYBIND_LENGTH); + scene_manager_previous_scene(app->scene_manager); + scene_manager_previous_scene(app->scene_manager); + scene_manager_previous_scene(app->scene_manager); +} + +void desktop_settings_scene_keybinds_choose_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + KeybindType type = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); + KeybindKey key = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); + uint32_t pre_select_item = 0; + char* keybind = app->desktop->settings.keybinds[type][key].data; + size_t submenu_i = -1; + + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + submenu_add_item( + submenu, + FLIPPER_APPS[i].name, + ++submenu_i, + desktop_settings_scene_keybinds_choose_main_callback, + app); + + // Select keybind item in submenu + FURI_LOG_I("URMOM", "%s %s", keybind, FLIPPER_APPS[i].name); + if(!strncmp(FLIPPER_APPS[i].name, keybind, MAX_KEYBIND_LENGTH)) { + pre_select_item = submenu_i; + } + } + + submenu_add_item( + submenu, + "[External Application]", + ++submenu_i, + desktop_settings_scene_keybinds_choose_ext_callback, + app); + if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { + pre_select_item = submenu_i; + } + furi_record_close(RECORD_STORAGE); + + for(size_t i = 0; i < EXTRA_OPTIONS_COUNT; i++) { + submenu_add_item( + submenu, + EXTRA_OPTIONS[i], + ++submenu_i, + desktop_settings_scene_keybinds_choose_extra_callback, + app); + + // Select keybind item in submenu + if(!strncmp(EXTRA_OPTIONS[i], keybind, MAX_KEYBIND_LENGTH)) { + pre_select_item = submenu_i; + } + } + + submenu_set_header(submenu, "Keybind action:"); + submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_choose_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + } + + return consumed; +} + +void desktop_settings_scene_keybinds_choose_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c new file mode 100644 index 000000000..3c3b61b49 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c @@ -0,0 +1,57 @@ +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" + +static void desktop_settings_scene_keybinds_key_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_keybinds_key_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + submenu_add_item( + submenu, "Up", KeybindKeyUp, desktop_settings_scene_keybinds_key_submenu_callback, app); + + submenu_add_item( + submenu, "Down", KeybindKeyDown, desktop_settings_scene_keybinds_key_submenu_callback, app); + + submenu_add_item( + submenu, + "Right", + KeybindKeyRight, + desktop_settings_scene_keybinds_key_submenu_callback, + app); + + submenu_add_item( + submenu, "Left", KeybindKeyLeft, desktop_settings_scene_keybinds_key_submenu_callback, app); + + submenu_set_header(submenu, "Keybind key:"); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey)); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_key_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsKey, event.event); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsChoose); + } + return consumed; +} + +void desktop_settings_scene_keybinds_key_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_type.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_type.c new file mode 100644 index 000000000..645f88738 --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_type.c @@ -0,0 +1,56 @@ +#include + +#include "../desktop_settings_app.h" +#include "desktop_settings_scene.h" + +static void desktop_settings_scene_keybinds_type_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void desktop_settings_scene_keybinds_type_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + submenu_reset(submenu); + + submenu_add_item( + submenu, + "Press", + KeybindTypePress, + desktop_settings_scene_keybinds_type_submenu_callback, + app); + + submenu_add_item( + submenu, + "Hold", + KeybindTypeHold, + desktop_settings_scene_keybinds_type_submenu_callback, + app); + + submenu_set_header(submenu, "Keybind type:"); + + submenu_set_selected_item( + submenu, + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType)); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_type_on_event(void* context, SceneManagerEvent event) { + DesktopSettingsApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsType, event.event); + scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey, 0); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); + } + return consumed; +} + +void desktop_settings_scene_keybinds_type_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c index 86886ead9..26b58d9ac 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -18,7 +18,7 @@ static void pin_auth_done_callback(const PinCode* pin_code, void* context) { DesktopSettingsApp* app = context; app->pincode_buffer = *pin_code; - if(desktop_pin_compare(&app->settings.pin_code, pin_code)) { + if(desktop_pin_compare(&app->desktop->settings.pin_code, pin_code)) { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL); } else { view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT); @@ -33,8 +33,7 @@ static void pin_auth_back_callback(void* context) { void desktop_settings_scene_pin_auth_on_enter(void* context) { DesktopSettingsApp* app = context; - DESKTOP_SETTINGS_LOAD(&app->settings); - furi_assert(desktop_pin_is_valid(&app->settings.pin_code)); + furi_assert(desktop_pin_is_valid(&app->desktop->settings.pin_code)); desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index 2969db9ea..3db86f234 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -18,9 +18,9 @@ static void pin_disable_back_callback(void* context) { void desktop_settings_scene_pin_disable_on_enter(void* context) { furi_assert(context); DesktopSettingsApp* app = context; - app->settings.pin_code.length = 0; - memset(app->settings.pin_code.data, '0', sizeof(app->settings.pin_code.data)); - DESKTOP_SETTINGS_SAVE(&app->settings); + app->desktop->settings.pin_code.length = 0; + memset( + app->desktop->settings.pin_code.data, '0', sizeof(app->desktop->settings.pin_code.data)); popup_set_context(app->popup, app); popup_set_callback(app->popup, pin_disable_back_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index bd9dda727..d9738f7a4 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -20,7 +20,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { Submenu* submenu = app->submenu; submenu_reset(submenu); - if(!desktop_pin_is_valid(&app->settings.pin_code)) { + if(!desktop_pin_is_valid(&app->desktop->settings.pin_code)) { submenu_add_item( submenu, "Set Pin", diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index f75be807a..40f508a49 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -23,8 +23,7 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) { void desktop_settings_scene_pin_setup_done_on_enter(void* context) { DesktopSettingsApp* app = context; - app->settings.pin_code = app->pincode_buffer; - DESKTOP_SETTINGS_SAVE(&app->settings); + memcpy(&app->desktop->settings.pin_code, &app->pincode_buffer, sizeof(PinCode)); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); furi_record_close(RECORD_NOTIFICATION); @@ -32,7 +31,7 @@ void desktop_settings_scene_pin_setup_done_on_enter(void* context) { desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback); - desktop_view_pin_input_set_pin(app->pin_input_view, &app->settings.pin_code); + desktop_view_pin_input_set_pin(app->pin_input_view, &app->desktop->settings.pin_code); desktop_view_pin_input_set_label_button(app->pin_input_view, "Done"); desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN Activated!"); desktop_view_pin_input_set_label_secondary( diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index baa3a9af4..a22a35b8f 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -3,13 +3,13 @@ #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" -#include -#define SCENE_EVENT_SELECT_FAVORITE_PRIMARY 0 -#define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1 -#define SCENE_EVENT_SELECT_PIN_SETUP 2 -#define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3 -#define SCENE_EVENT_SELECT_AUTO_LOCK_PIN 4 +enum VarItemListIndex { + VarItemListIndexKeybinds, + VarItemListIndexPinSetup, + VarItemListIndexAutoLockTime, + VarItemListIndexAutoLockPin, +}; #define AUTO_LOCK_DELAY_COUNT 9 const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { @@ -36,7 +36,7 @@ static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* i uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, auto_lock_delay_text[index]); - app->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; + app->desktop->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; } static void desktop_settings_scene_start_auto_lock_pin_changed(VariableItem* item) { @@ -44,7 +44,7 @@ static void desktop_settings_scene_start_auto_lock_pin_changed(VariableItem* ite uint8_t value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - app->settings.auto_lock_with_pin = value; + app->desktop->settings.auto_lock_with_pin = value; } void desktop_settings_scene_start_on_enter(void* context) { @@ -54,9 +54,7 @@ void desktop_settings_scene_start_on_enter(void* context) { VariableItem* item; uint8_t value_index; - variable_item_list_add(variable_item_list, "Primary Fav App (Up)", 1, NULL, NULL); - - variable_item_list_add(variable_item_list, "Secondary Fav App (Down)", 1, NULL, NULL); + variable_item_list_add(variable_item_list, "Keybinds Setup", 1, NULL, NULL); variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL); @@ -68,7 +66,7 @@ void desktop_settings_scene_start_on_enter(void* context) { app); value_index = value_index_uint32( - app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT); + app->desktop->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]); @@ -79,8 +77,9 @@ void desktop_settings_scene_start_on_enter(void* context) { desktop_settings_scene_start_auto_lock_pin_changed, app); - variable_item_set_current_value_index(item, app->settings.auto_lock_with_pin); - variable_item_set_current_value_text(item, app->settings.auto_lock_with_pin ? "ON" : "OFF"); + variable_item_set_current_value_index(item, app->desktop->settings.auto_lock_with_pin); + variable_item_set_current_value_text( + item, app->desktop->settings.auto_lock_with_pin ? "ON" : "OFF"); variable_item_list_set_enter_callback( variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app); @@ -94,23 +93,16 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case SCENE_EVENT_SELECT_FAVORITE_PRIMARY: - scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 1); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); + case VarItemListIndexKeybinds: + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsType, 0); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsType); consumed = true; break; - case SCENE_EVENT_SELECT_FAVORITE_SECONDARY: - scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 0); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite); - consumed = true; - break; - case SCENE_EVENT_SELECT_PIN_SETUP: + case VarItemListIndexPinSetup: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu); consumed = true; break; - case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: - consumed = true; - break; } } return consumed; @@ -119,5 +111,4 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even void desktop_settings_scene_start_on_exit(void* context) { DesktopSettingsApp* app = context; variable_item_list_reset(app->variable_item_list); - DESKTOP_SETTINGS_SAVE(&app->settings); } From d547ef7bcc380b1ac028e0f28278c48540e10733 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 04:24:19 +0200 Subject: [PATCH 306/370] Fix desktop settings resetting without PIN set --- applications/services/desktop/desktop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 4e42b4104..ed2776f0e 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -493,7 +493,7 @@ int32_t desktop_srv(void* p) { Desktop* desktop = desktop_alloc(); bool ok = DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(ok) { + if(ok && desktop->settings.pin_code.length) { ok = desktop_pin_is_valid(&desktop->settings.pin_code); } if(!ok) { From 380734f804d6915e8c9c6f48cfd6e5e1e87e3206 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jun 2023 05:38:44 +0300 Subject: [PATCH 307/370] Allow api mismatch bypass again :C for those who explicitly doesn't update their manually installed apps and want to ignore warnings and issues caused by using outdated apps --- applications/services/loader/loader.c | 46 +++++++++++++++++++++++---- applications/services/loader/loader.h | 1 + 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index ab7876a03..833954bf7 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -254,7 +254,8 @@ static LoaderStatus loader_start_external_app( Storage* storage, const char* path, const char* args, - FuriString* error_message) { + FuriString* error_message, + bool ignore_mismatch) { LoaderStatus status = loader_make_success_status(error_message); do { @@ -266,10 +267,38 @@ static LoaderStatus loader_start_external_app( FlipperApplicationPreloadStatus preload_res = flipper_application_preload(loader->app.fap, path); if(preload_res != FlipperApplicationPreloadStatusSuccess) { - const char* err_msg = flipper_application_preload_status_to_string(preload_res); - status = loader_make_status_error( - LoaderStatusErrorInternal, error_message, "Preload failed %s: %s", path, err_msg); - break; + if(preload_res == FlipperApplicationPreloadStatusApiMismatch) { + if(!ignore_mismatch) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header( + message, "API Mismatch", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, "Cancel", NULL, "Continue"); + dialog_message_set_text( + message, + "This app might not\nwork correctly\nContinue anyways?", + 64, + 32, + AlignCenter, + AlignCenter); + if(dialog_message_show(dialogs, message) == DialogMessageButtonRight) { + status = loader_make_status_error( + LoaderStatusErrorApiMismatch, error_message, "API Mismatch"); + } + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + break; + } + } else { + const char* err_msg = flipper_application_preload_status_to_string(preload_res); + status = loader_make_status_error( + LoaderStatusErrorInternal, + error_message, + "Preload failed %s: %s", + path, + err_msg); + break; + } } FURI_LOG_I(TAG, "Mapping"); @@ -383,7 +412,12 @@ static LoaderStatus loader_do_start_by_name( { Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_file_exists(storage, name)) { - status = loader_start_external_app(loader, storage, name, args, error_message); + status = + loader_start_external_app(loader, storage, name, args, error_message, false); + if(status == LoaderStatusErrorApiMismatch) { + status = loader_start_external_app( + loader, storage, name, args, error_message, true); + } furi_record_close(RECORD_STORAGE); break; } diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index 9fc4059f2..550d3d508 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -15,6 +15,7 @@ typedef enum { LoaderStatusErrorAppStarted, LoaderStatusErrorUnknownApp, LoaderStatusErrorInternal, + LoaderStatusErrorApiMismatch, } LoaderStatus; typedef enum { From 259979a76a45df62a0c5cb74b0fdc6ab037b8503 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jun 2023 05:51:14 +0300 Subject: [PATCH 308/370] fix issue with None app used in favourites User desktop settings will be reset to defaults --- applications/services/desktop/desktop_settings.h | 2 +- applications/services/desktop/scenes/desktop_scene_main.c | 2 +- .../scenes/desktop_settings_scene_favorite.c | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 5946873f0..5f06afa19 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -8,7 +8,7 @@ #include #include -#define DESKTOP_SETTINGS_VER (10) +#define DESKTOP_SETTINGS_VER (11) #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_MAGIC (0x17) diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 420775489..d11b7a585 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -70,7 +70,7 @@ static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* } static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) { - if(strlen(application->name_or_path) > 0) { + if(strlen(application->name_or_path) > 4) { loader_start_with_gui_error(desktop->loader, application->name_or_path, NULL); } else { // No favourite app is set! So we skipping this part diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index 6ca793a54..d54989814 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -6,6 +6,7 @@ #define EXTERNAL_APPLICATION_NAME ("[External Application]") #define EXTERNAL_APPLICATION_INDEX (FLIPPER_APPS_COUNT + 1) +#define NONE_APPLICATION_INDEX (FLIPPER_APPS_COUNT + 2) static bool favorite_fap_selector_item_callback( FuriString* file_path, @@ -89,7 +90,7 @@ void desktop_settings_scene_favorite_on_enter(void* context) { submenu_add_item( submenu, "None (disable)", - FLIPPER_APPS_COUNT + 2, + NONE_APPLICATION_INDEX, desktop_settings_scene_favorite_submenu_callback, app); @@ -154,6 +155,10 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e MAX_APP_LENGTH); consumed = true; } + } else if(event.event == NONE_APPLICATION_INDEX) { + curr_favorite_app->is_external = false; + strncpy(curr_favorite_app->name_or_path, "no", MAX_APP_LENGTH); + consumed = true; } else { curr_favorite_app->is_external = false; strncpy( From 293e01b80031518f4ce2c6bae2972e7addbcc7c5 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jun 2023 06:39:52 +0300 Subject: [PATCH 309/370] update chaneglog --- CHANGELOG.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 846ead301..37d8c680d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,15 @@ ## New changes -* Plugins: UniTemp update merged PRs -> Heat Index Feature (by @ClementGre) + Append carbon dioxide sensor (SCD40) (by @divinebird) -* Plugins: Fix furi_hal_bus issues in AVR Programmer and Signal Generator (fixes issue #525) -* Plugins: USB / BLE Remote - Updated UI in keynote vertical and numpad (by @gid9798 | PR #524) -* Plugins: Update TOTP [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) -* Plugins: Fixed ESP32 WiFi Marauder crashes when reopening app -* Infrared: Updated universal remote asstes (by @amec0e | PR #522) -* OFW PR 2783: SLIX2 emulation support / practical use for Dymo printers (by @g3gg0) -* OFW PR 2782: NFC: Fix key invalidation logic (by @AloneLiberty) -* OFW: Debug: sync apps on attach, makes it possible to debug already started app that has crashed +* OFW PR: Update OFW PR 2782 +* OFW: Add Mitsubishi MSZ-AP25VGK universal ac remote +* OFW: Fix roll-over in file browser and archive +* OFW: Fix fr-FR-mac keylayout +* OFW: NFC/RFID detector app +* OFW: Fast FAP Loader +* OFW: LF-RFID debug: make it work +* OFW: Fix M*LIB usage +* OFW: fix: make `dialog_file_browser_set_basic_options` initialize all fields +* OFW: Scroll acceleration +* OFW: Loader refaptoring: second encounter ---- From e5ae3e22b39f1ae3c2c305dc1f98ff3522ae095c Mon Sep 17 00:00:00 2001 From: AloneLiberty <111039319+AloneLiberty@users.noreply.github.com> Date: Thu, 29 Jun 2023 11:24:13 +0300 Subject: [PATCH 310/370] NFC: Fix key invalidation logic (#2782) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * NFC: Fix key invalidation logic * NFC: Fix crash in CLI with empty response * Fix incorrect key conversions * Proper call to nfc_util Co-authored-by: あく Co-authored-by: Astra --- applications/main/nfc/nfc_cli.c | 4 ++++ lib/nfc/nfc_worker.c | 18 +++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 6e6e04ca9..0b7e75475 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -144,6 +144,10 @@ static void nfc_cli_apdu(Cli* cli, FuriString* args) { break; } resp_size = (tx_rx.rx_bits / 8) * 2; + if(!resp_size) { + printf("No response\r\n"); + break; + } resp_buffer = malloc(resp_size); uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size); resp_buffer[resp_size] = 0; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index a6bb93f59..a39531c8c 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -940,14 +940,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key A is marked as found and matches the searching key, invalidate it - uint8_t found_key[6]; - memcpy(found_key, data->block[i].value, 6); + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, i); uint8_t current_key[6]; - memcpy(current_key, &key, 6); + nfc_util_num2bytes(key, 6, current_key); if(mf_classic_is_key_found(data, i, MfClassicKeyA) && - memcmp(found_key, current_key, 6) == 0) { + memcmp(sec_trailer->key_a, current_key, 6) == 0) { mf_classic_set_key_not_found(data, i, MfClassicKeyA); is_key_a_found = false; FURI_LOG_D(TAG, "Key %dA not found in attack", i); @@ -966,14 +966,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } else { // If the key B is marked as found and matches the searching key, invalidate it - uint8_t found_key[6]; - memcpy(found_key, data->block[i].value + 10, 6); + MfClassicSectorTrailer* sec_trailer = + mf_classic_get_sector_trailer_by_sector(data, i); uint8_t current_key[6]; - memcpy(current_key, &key, 6); + nfc_util_num2bytes(key, 6, current_key); if(mf_classic_is_key_found(data, i, MfClassicKeyB) && - memcmp(found_key, current_key, 6) == 0) { + memcmp(sec_trailer->key_b, current_key, 6) == 0) { mf_classic_set_key_not_found(data, i, MfClassicKeyB); is_key_b_found = false; FURI_LOG_D(TAG, "Key %dB not found in attack", i); @@ -989,7 +989,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; } - memcpy(&prev_key, &key, sizeof(key)); + prev_key = key; } if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; mf_classic_read_sector(&tx_rx, data, i); From 3aa44d9e44e6f2b9f498094b68a65ef392e16581 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:11:45 +0300 Subject: [PATCH 311/370] testing --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index d1659b756..cb1758bfd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -316,7 +316,7 @@ steps: commands: - export DIST_SUFFIX=${DRONE_BUILD_NUMBER} - export WORKFLOW_BRANCH_OR_TAG=dev-cfw - - ./fbt COMPACT=1 DEBUG=0 updater_package + - ./fbt VERBOSE=1 COMPACT=1 DEBUG=0 updater_package - mkdir artifacts-default - mv dist/f7-C/* artifacts-default/ - ls -laS artifacts-default From 052a55fe968c779b029c4f7fcd1b527d419b8df4 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jun 2023 13:52:15 +0300 Subject: [PATCH 312/370] testing2 --- scripts/toolchain/fbtenv.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 81df6c0af..d911c2334 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,7 +4,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"22"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then FBT_TOOLCHAIN_PATH_WAS_SET=0; From ab323c53a6784d048c83507c0a4f124963d14fe7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:12:47 +0300 Subject: [PATCH 313/370] back --- .drone.yml | 2 +- scripts/toolchain/fbtenv.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index cb1758bfd..d1659b756 100644 --- a/.drone.yml +++ b/.drone.yml @@ -316,7 +316,7 @@ steps: commands: - export DIST_SUFFIX=${DRONE_BUILD_NUMBER} - export WORKFLOW_BRANCH_OR_TAG=dev-cfw - - ./fbt VERBOSE=1 COMPACT=1 DEBUG=0 updater_package + - ./fbt COMPACT=1 DEBUG=0 updater_package - mkdir artifacts-default - mv dist/f7-C/* artifacts-default/ - ls -laS artifacts-default diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index d911c2334..81df6c0af 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -4,7 +4,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"22"}"; if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then FBT_TOOLCHAIN_PATH_WAS_SET=0; From 95c1585df6bfc9825e849644522be205da50f57f Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 29 Jun 2023 14:23:04 +0300 Subject: [PATCH 314/370] [FL-3211][FL-3212] Debug apps: speaker, uart_echo with baudrate (#2812) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Music player: move music_worker to library * Music player: drop cli * Debug: speaker debug app * Debug: baudrate arg in uart_echo app * Libs: add music_worker to api * Libs: add music_worker to targets linker_dependencies Co-authored-by: あく --- applications/debug/application.fam | 1 + .../debug/speaker_debug/application.fam | 11 ++ .../debug/speaker_debug/speaker_debug.c | 120 ++++++++++++++++++ applications/debug/uart_echo/uart_echo.c | 20 ++- .../external/music_player/application.fam | 10 +- .../external/music_player/music_player.c | 27 ++-- .../external/music_player/music_player_cli.c | 48 ------- .../music_player/music_player_worker.h | 38 ------ firmware/targets/f18/api_symbols.csv | 15 ++- firmware/targets/f18/target.json | 1 + firmware/targets/f7/api_symbols.csv | 21 ++- firmware/targets/f7/target.json | 1 + lib/SConscript | 2 + lib/music_worker/SConscript | 27 ++++ .../music_worker/music_worker.c | 69 +++++----- lib/music_worker/music_worker.h | 37 ++++++ 16 files changed, 295 insertions(+), 153 deletions(-) create mode 100644 applications/debug/speaker_debug/application.fam create mode 100644 applications/debug/speaker_debug/speaker_debug.c delete mode 100644 applications/external/music_player/music_player_cli.c delete mode 100644 applications/external/music_player/music_player_worker.h create mode 100644 lib/music_worker/SConscript rename applications/external/music_player/music_player_worker.c => lib/music_worker/music_worker.c (85%) create mode 100644 lib/music_worker/music_worker.h diff --git a/applications/debug/application.fam b/applications/debug/application.fam index a33b3693d..cdbf8fe18 100644 --- a/applications/debug/application.fam +++ b/applications/debug/application.fam @@ -12,5 +12,6 @@ App( "display_test", "text_box_test", "file_browser_test", + "speaker_debug", ], ) diff --git a/applications/debug/speaker_debug/application.fam b/applications/debug/speaker_debug/application.fam new file mode 100644 index 000000000..68d8b188b --- /dev/null +++ b/applications/debug/speaker_debug/application.fam @@ -0,0 +1,11 @@ +App( + appid="speaker_debug", + name="Speaker Debug", + apptype=FlipperAppType.DEBUG, + entry_point="speaker_debug_app", + requires=["gui", "notification"], + stack_size=2 * 1024, + order=10, + fap_category="Debug", + fap_libs=["music_worker"], +) diff --git a/applications/debug/speaker_debug/speaker_debug.c b/applications/debug/speaker_debug/speaker_debug.c new file mode 100644 index 000000000..e01d5b8ec --- /dev/null +++ b/applications/debug/speaker_debug/speaker_debug.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include + +#define TAG "SpeakerDebug" +#define CLI_COMMAND "speaker_debug" + +typedef enum { + SpeakerDebugAppMessageTypeStop, +} SpeakerDebugAppMessageType; + +typedef struct { + SpeakerDebugAppMessageType type; +} SpeakerDebugAppMessage; + +typedef struct { + MusicWorker* music_worker; + FuriMessageQueue* message_queue; + Cli* cli; +} SpeakerDebugApp; + +static SpeakerDebugApp* speaker_app_alloc() { + SpeakerDebugApp* app = (SpeakerDebugApp*)malloc(sizeof(SpeakerDebugApp)); + app->music_worker = music_worker_alloc(); + app->message_queue = furi_message_queue_alloc(8, sizeof(SpeakerDebugAppMessage)); + app->cli = furi_record_open(RECORD_CLI); + return app; +} + +static void speaker_app_free(SpeakerDebugApp* app) { + music_worker_free(app->music_worker); + furi_message_queue_free(app->message_queue); + furi_record_close(RECORD_CLI); + free(app); +} + +static void speaker_app_cli(Cli* cli, FuriString* args, void* context) { + UNUSED(cli); + + SpeakerDebugApp* app = (SpeakerDebugApp*)context; + SpeakerDebugAppMessage message; + FuriString* cmd = furi_string_alloc(); + + if(!args_read_string_and_trim(args, cmd)) { + furi_string_free(cmd); + printf("Usage:\r\n"); + printf("\t" CLI_COMMAND " stop\r\n"); + return; + } + + if(furi_string_cmp(cmd, "stop") == 0) { + message.type = SpeakerDebugAppMessageTypeStop; + FuriStatus status = furi_message_queue_put(app->message_queue, &message, 100); + if(status != FuriStatusOk) { + printf("Failed to send message\r\n"); + } else { + printf("Stopping\r\n"); + } + } else { + printf("Usage:\r\n"); + printf("\t" CLI_COMMAND " stop\r\n"); + } + + furi_string_free(cmd); +} + +static bool speaker_app_music_play(SpeakerDebugApp* app, const char* rtttl) { + if(music_worker_is_playing(app->music_worker)) { + music_worker_stop(app->music_worker); + } + + if(!music_worker_load_rtttl_from_string(app->music_worker, rtttl)) { + FURI_LOG_E(TAG, "Failed to load RTTTL"); + return false; + } + + music_worker_set_volume(app->music_worker, 1.0f); + music_worker_start(app->music_worker); + + return true; +} + +static void speaker_app_music_stop(SpeakerDebugApp* app) { + if(music_worker_is_playing(app->music_worker)) { + music_worker_stop(app->music_worker); + } +} + +static void speaker_app_run(SpeakerDebugApp* app, const char* arg) { + if(!arg || !speaker_app_music_play(app, arg)) { + FURI_LOG_E(TAG, "Provided RTTTL is invalid"); + return; + } + + cli_add_command(app->cli, CLI_COMMAND, CliCommandFlagParallelSafe, speaker_app_cli, app); + + SpeakerDebugAppMessage message; + FuriStatus status; + while(true) { + status = furi_message_queue_get(app->message_queue, &message, FuriWaitForever); + + if(status == FuriStatusOk) { + if(message.type == SpeakerDebugAppMessageTypeStop) { + speaker_app_music_stop(app); + break; + } + } + } + + cli_delete_command(app->cli, CLI_COMMAND); +} + +int32_t speaker_debug_app(void* arg) { + SpeakerDebugApp* app = speaker_app_alloc(); + speaker_app_run(app, arg); + speaker_app_free(app); + return 0; +} diff --git a/applications/debug/uart_echo/uart_echo.c b/applications/debug/uart_echo/uart_echo.c index dc1327529..4bede9ab4 100644 --- a/applications/debug/uart_echo/uart_echo.c +++ b/applications/debug/uart_echo/uart_echo.c @@ -10,6 +10,8 @@ #define LINES_ON_SCREEN 6 #define COLUMNS_ON_SCREEN 21 +#define TAG "UartEcho" +#define DEFAULT_BAUD_RATE 230400 typedef struct UartDumpModel UartDumpModel; @@ -179,7 +181,7 @@ static int32_t uart_echo_worker(void* context) { return 0; } -static UartEchoApp* uart_echo_app_alloc() { +static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) { UartEchoApp* app = malloc(sizeof(UartEchoApp)); app->rx_stream = furi_stream_buffer_alloc(2048, 1); @@ -220,7 +222,7 @@ static UartEchoApp* uart_echo_app_alloc() { // Enable uart listener furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); + furi_hal_uart_set_br(FuriHalUartIdUSART1, baudrate); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); return app; @@ -263,8 +265,18 @@ static void uart_echo_app_free(UartEchoApp* app) { } int32_t uart_echo_app(void* p) { - UNUSED(p); - UartEchoApp* app = uart_echo_app_alloc(); + uint32_t baudrate = DEFAULT_BAUD_RATE; + if(p) { + const char* baudrate_str = p; + if(sscanf(baudrate_str, "%lu", &baudrate) != 1) { + FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str); + baudrate = DEFAULT_BAUD_RATE; + } + } + + FURI_LOG_I(TAG, "Using baudrate: %lu", baudrate); + + UartEchoApp* app = uart_echo_app_alloc(baudrate); view_dispatcher_run(app->view_dispatcher); uart_echo_app_free(app); return 0; diff --git a/applications/external/music_player/application.fam b/applications/external/music_player/application.fam index 3414c0a48..c9cd5e44d 100644 --- a/applications/external/music_player/application.fam +++ b/applications/external/music_player/application.fam @@ -7,18 +7,10 @@ App( "gui", "dialogs", ], - provides=["music_player_start"], stack_size=2 * 1024, order=20, fap_icon="icons/music_10px.png", fap_category="Media", fap_icon_assets="icons", -) - -App( - appid="music_player_start", - apptype=FlipperAppType.STARTUP, - entry_point="music_player_on_system_start", - requires=["music_player"], - order=30, + fap_libs=["music_worker"], ) diff --git a/applications/external/music_player/music_player.c b/applications/external/music_player/music_player.c index 2380d7d17..8b0b758c1 100644 --- a/applications/external/music_player/music_player.c +++ b/applications/external/music_player/music_player.c @@ -1,4 +1,4 @@ -#include "music_player_worker.h" +#include #include #include @@ -34,7 +34,7 @@ typedef struct { ViewPort* view_port; Gui* gui; - MusicPlayerWorker* worker; + MusicWorker* worker; } MusicPlayer; static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1}; @@ -218,7 +218,7 @@ static void input_callback(InputEvent* input_event, void* ctx) { } } -static void music_player_worker_callback( +static void music_worker_callback( uint8_t semitone, uint8_t dots, uint8_t duration, @@ -250,7 +250,7 @@ static void music_player_worker_callback( void music_player_clear(MusicPlayer* instance) { memset(instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); memset(instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); - music_player_worker_clear(instance->worker); + music_worker_clear(instance->worker); } MusicPlayer* music_player_alloc() { @@ -263,10 +263,9 @@ MusicPlayer* music_player_alloc() { instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - instance->worker = music_player_worker_alloc(); - music_player_worker_set_volume( - instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]); - music_player_worker_set_callback(instance->worker, music_player_worker_callback, instance); + instance->worker = music_worker_alloc(); + music_worker_set_volume(instance->worker, MUSIC_PLAYER_VOLUMES[instance->model->volume]); + music_worker_set_callback(instance->worker, music_worker_callback, instance); music_player_clear(instance); @@ -286,7 +285,7 @@ void music_player_free(MusicPlayer* instance) { furi_record_close(RECORD_GUI); view_port_free(instance->view_port); - music_player_worker_free(instance->worker); + music_worker_free(instance->worker); furi_message_queue_free(instance->input_queue); @@ -330,12 +329,12 @@ int32_t music_player_app(void* p) { } } - if(!music_player_worker_load(music_player->worker, furi_string_get_cstr(file_path))) { + if(!music_worker_load(music_player->worker, furi_string_get_cstr(file_path))) { FURI_LOG_E(TAG, "Unable to load file"); break; } - music_player_worker_start(music_player->worker); + music_worker_start(music_player->worker); InputEvent input; while(furi_message_queue_get(music_player->input_queue, &input, FuriWaitForever) == @@ -349,11 +348,11 @@ int32_t music_player_app(void* p) { } else if(input.key == InputKeyUp) { if(music_player->model->volume < COUNT_OF(MUSIC_PLAYER_VOLUMES) - 1) music_player->model->volume++; - music_player_worker_set_volume( + music_worker_set_volume( music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]); } else if(input.key == InputKeyDown) { if(music_player->model->volume > 0) music_player->model->volume--; - music_player_worker_set_volume( + music_worker_set_volume( music_player->worker, MUSIC_PLAYER_VOLUMES[music_player->model->volume]); } @@ -361,7 +360,7 @@ int32_t music_player_app(void* p) { view_port_update(music_player->view_port); } - music_player_worker_stop(music_player->worker); + music_worker_stop(music_player->worker); if(p && strlen(p)) break; // Exit instead of going to browser if launched with arg music_player_clear(music_player); } while(1); diff --git a/applications/external/music_player/music_player_cli.c b/applications/external/music_player/music_player_cli.c deleted file mode 100644 index 90060d7ee..000000000 --- a/applications/external/music_player/music_player_cli.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include "music_player_worker.h" - -static void music_player_cli(Cli* cli, FuriString* args, void* context) { - UNUSED(context); - MusicPlayerWorker* music_player_worker = music_player_worker_alloc(); - Storage* storage = furi_record_open(RECORD_STORAGE); - - do { - if(storage_common_stat(storage, furi_string_get_cstr(args), NULL) == FSE_OK) { - if(!music_player_worker_load(music_player_worker, furi_string_get_cstr(args))) { - printf("Failed to open file %s\r\n", furi_string_get_cstr(args)); - break; - } - } else { - if(!music_player_worker_load_rtttl_from_string( - music_player_worker, furi_string_get_cstr(args))) { - printf("Argument is not a file or RTTTL\r\n"); - break; - } - } - - printf("Press CTRL+C to stop\r\n"); - music_player_worker_set_volume(music_player_worker, 1.0f); - music_player_worker_start(music_player_worker); - while(!cli_cmd_interrupt_received(cli)) { - furi_delay_ms(50); - } - music_player_worker_stop(music_player_worker); - } while(0); - - furi_record_close(RECORD_STORAGE); - music_player_worker_free(music_player_worker); -} - -void music_player_on_system_start() { -#ifdef SRV_CLI - Cli* cli = furi_record_open(RECORD_CLI); - - cli_add_command(cli, "music_player", CliCommandFlagDefault, music_player_cli, NULL); - - furi_record_close(RECORD_CLI); -#else - UNUSED(music_player_cli); -#endif -} diff --git a/applications/external/music_player/music_player_worker.h b/applications/external/music_player/music_player_worker.h deleted file mode 100644 index 00320b11f..000000000 --- a/applications/external/music_player/music_player_worker.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -typedef void (*MusicPlayerWorkerCallback)( - uint8_t semitone, - uint8_t dots, - uint8_t duration, - float position, - void* context); - -typedef struct MusicPlayerWorker MusicPlayerWorker; - -MusicPlayerWorker* music_player_worker_alloc(); - -void music_player_worker_clear(MusicPlayerWorker* instance); - -void music_player_worker_free(MusicPlayerWorker* instance); - -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string); - -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context); - -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume); - -void music_player_worker_start(MusicPlayerWorker* instance); - -void music_player_worker_stop(MusicPlayerWorker* instance); diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 2e176a5b5..a689d5a21 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,31.2,, +Version,+,31.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -128,6 +128,7 @@ Header,+,lib/mlib/m-list.h,, Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, +Header,+,lib/music_worker/music_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, Header,+,lib/one_wire/one_wire_host.h,, Header,+,lib/one_wire/one_wire_slave.h,, @@ -1519,6 +1520,18 @@ Function,-,mkstemps,int,"char*, int" Function,-,mktemp,char*,char* Function,-,mktime,time_t,tm* Function,-,mrand48,long, +Function,-,music_worker_alloc,MusicWorker*, +Function,-,music_worker_clear,void,MusicWorker* +Function,-,music_worker_free,void,MusicWorker* +Function,-,music_worker_is_playing,_Bool,MusicWorker* +Function,-,music_worker_load,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_fmf_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_string,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_set_callback,void,"MusicWorker*, MusicWorkerCallback, void*" +Function,-,music_worker_set_volume,void,"MusicWorker*, float" +Function,-,music_worker_start,void,MusicWorker* +Function,-,music_worker_stop,void,MusicWorker* Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" diff --git a/firmware/targets/f18/target.json b/firmware/targets/f18/target.json index 14d395d22..2d14813f6 100644 --- a/firmware/targets/f18/target.json +++ b/firmware/targets/f18/target.json @@ -25,6 +25,7 @@ "appframe", "assets", "one_wire", + "music_worker", "misc", "flipper_application", "flipperformat", diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ac2b11f38..0183540f6 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,31.2,, +Version,+,31.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -146,6 +146,7 @@ Header,+,lib/mlib/m-list.h,, Header,+,lib/mlib/m-rbtree.h,, Header,+,lib/mlib/m-tuple.h,, Header,+,lib/mlib/m-variant.h,, +Header,+,lib/music_worker/music_worker.h,, Header,+,lib/nfc/nfc_device.h,, Header,+,lib/nfc/protocols/nfc_util.h,, Header,+,lib/one_wire/maxim_crc.h,, @@ -1220,12 +1221,12 @@ Function,+,furi_hal_mpu_protect_disable,void,FuriHalMpuRegion Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" Function,+,furi_hal_nfc_activate_nfca,_Bool,"uint32_t, uint32_t*" -Function,+,furi_hal_nfc_field_is_present,_Bool, -Function,+,furi_hal_nfc_field_detect_start,void, Function,-,furi_hal_nfc_deinit,void, Function,+,furi_hal_nfc_detect,_Bool,"FuriHalNfcDevData*, uint32_t" Function,+,furi_hal_nfc_emulate_nfca,_Bool,"uint8_t*, uint8_t, uint8_t*, uint8_t, FuriHalNfcEmulateCallback, void*, uint32_t" Function,+,furi_hal_nfc_exit_sleep,void, +Function,+,furi_hal_nfc_field_detect_start,void, +Function,+,furi_hal_nfc_field_is_present,_Bool, Function,+,furi_hal_nfc_field_off,void, Function,+,furi_hal_nfc_field_on,void, Function,-,furi_hal_nfc_init,void, @@ -1307,9 +1308,9 @@ Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_comp_set_callback,void,"FuriHalRfidCompCallback, void*" Function,+,furi_hal_rfid_comp_start,void, Function,+,furi_hal_rfid_comp_stop,void, -Function,+,furi_hal_rfid_field_is_present,_Bool,uint32_t* Function,+,furi_hal_rfid_field_detect_start,void, Function,+,furi_hal_rfid_field_detect_stop,void, +Function,+,furi_hal_rfid_field_is_present,_Bool,uint32_t* Function,-,furi_hal_rfid_init,void, Function,+,furi_hal_rfid_pin_pull_pulldown,void, Function,+,furi_hal_rfid_pin_pull_release,void, @@ -2063,6 +2064,18 @@ Function,-,modf,double,"double, double*" Function,-,modff,float,"float, float*" Function,-,modfl,long double,"long double, long double*" Function,-,mrand48,long, +Function,-,music_worker_alloc,MusicWorker*, +Function,-,music_worker_clear,void,MusicWorker* +Function,-,music_worker_free,void,MusicWorker* +Function,-,music_worker_is_playing,_Bool,MusicWorker* +Function,-,music_worker_load,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_fmf_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_file,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_load_rtttl_from_string,_Bool,"MusicWorker*, const char*" +Function,-,music_worker_set_callback,void,"MusicWorker*, MusicWorkerCallback, void*" +Function,-,music_worker_set_volume,void,"MusicWorker*, float" +Function,-,music_worker_start,void,MusicWorker* +Function,-,music_worker_stop,void,MusicWorker* Function,-,nan,double,const char* Function,-,nanf,float,const char* Function,-,nanl,long double,const char* diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json index e3dc78325..9bb87000c 100644 --- a/firmware/targets/f7/target.json +++ b/firmware/targets/f7/target.json @@ -38,6 +38,7 @@ "assets", "one_wire", "ibutton", + "music_worker", "misc", "mbedtls", "lfrfid", diff --git a/lib/SConscript b/lib/SConscript index 495ba4bfe..ab78c6ea4 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -15,6 +15,7 @@ env.Append( Dir("u8g2"), Dir("update_util"), Dir("print"), + Dir("music_worker"), ], ) @@ -100,6 +101,7 @@ libs = env.BuildModules( "misc", "lfrfid", "flipper_application", + "music_worker", ], ) diff --git a/lib/music_worker/SConscript b/lib/music_worker/SConscript new file mode 100644 index 000000000..36d01d859 --- /dev/null +++ b/lib/music_worker/SConscript @@ -0,0 +1,27 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/music_worker", + ], + SDK_HEADERS=[ + File("music_worker.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="music_worker") +libenv.ApplyLibFlags() + +libenv.AppendUnique( + CCFLAGS=[ + # Required for lib to be linkable with .faps + "-mword-relocations", + "-mlong-calls", + ], +) + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/applications/external/music_player/music_player_worker.c b/lib/music_worker/music_worker.c similarity index 85% rename from applications/external/music_player/music_player_worker.c rename to lib/music_worker/music_worker.c index ee350ee80..61fc838f2 100644 --- a/applications/external/music_player/music_player_worker.c +++ b/lib/music_worker/music_worker.c @@ -1,4 +1,4 @@ -#include "music_player_worker.h" +#include "music_worker.h" #include #include @@ -9,7 +9,7 @@ #include #include -#define TAG "MusicPlayerWorker" +#define TAG "MusicWorker" #define MUSIC_PLAYER_FILETYPE "Flipper Music Format" #define MUSIC_PLAYER_VERSION 0 @@ -28,11 +28,11 @@ typedef struct { ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); -struct MusicPlayerWorker { +struct MusicWorker { FuriThread* thread; bool should_work; - MusicPlayerWorkerCallback callback; + MusicWorkerCallback callback; void* callback_context; float volume; @@ -42,9 +42,9 @@ struct MusicPlayerWorker { NoteBlockArray_t notes; }; -static int32_t music_player_worker_thread_callback(void* context) { +static int32_t music_worker_thread_callback(void* context) { furi_assert(context); - MusicPlayerWorker* instance = context; + MusicWorker* instance = context; NoteBlockArray_it_t it; NoteBlockArray_it(it, instance->notes); @@ -97,24 +97,24 @@ static int32_t music_player_worker_thread_callback(void* context) { return 0; } -MusicPlayerWorker* music_player_worker_alloc() { - MusicPlayerWorker* instance = malloc(sizeof(MusicPlayerWorker)); +MusicWorker* music_worker_alloc() { + MusicWorker* instance = malloc(sizeof(MusicWorker)); NoteBlockArray_init(instance->notes); - instance->thread = furi_thread_alloc_ex( - "MusicPlayerWorker", 1024, music_player_worker_thread_callback, instance); + instance->thread = + furi_thread_alloc_ex("MusicWorker", 1024, music_worker_thread_callback, instance); instance->volume = 1.0f; return instance; } -void music_player_worker_clear(MusicPlayerWorker* instance) { +void music_worker_clear(MusicWorker* instance) { NoteBlockArray_reset(instance->notes); } -void music_player_worker_free(MusicPlayerWorker* instance) { +void music_worker_free(MusicWorker* instance) { furi_assert(instance); furi_thread_free(instance->thread); NoteBlockArray_clear(instance->notes); @@ -186,11 +186,8 @@ static size_t skip_till(const char* string, const char symbol) { return ret; } -static bool music_player_worker_add_note( - MusicPlayerWorker* instance, - uint8_t semitone, - uint8_t duration, - uint8_t dots) { +static bool + music_worker_add_note(MusicWorker* instance, uint8_t semitone, uint8_t duration, uint8_t dots) { NoteBlock note_block; note_block.semitone = semitone; @@ -228,7 +225,7 @@ static int8_t note_to_semitone(const char note) { } } -static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const char* string) { +static bool music_worker_parse_notes(MusicWorker* instance, const char* string) { const char* cursor = string; bool result = true; @@ -286,7 +283,7 @@ static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const c semitone += sharp_char == '#' ? 1 : 0; } - if(music_player_worker_add_note(instance, semitone, duration, dots)) { + if(music_worker_add_note(instance, semitone, duration, dots)) { FURI_LOG_D( TAG, "Added note: %c%c%lu.%lu = %u %lu", @@ -316,20 +313,20 @@ static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const c return result; } -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); bool ret = false; if(strcasestr(file_path, ".fmf")) { - ret = music_player_worker_load_fmf_from_file(instance, file_path); + ret = music_worker_load_fmf_from_file(instance, file_path); } else { - ret = music_player_worker_load_rtttl_from_file(instance, file_path); + ret = music_worker_load_rtttl_from_file(instance, file_path); } return ret; } -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); @@ -369,7 +366,7 @@ bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const c break; } - if(!music_player_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { + if(!music_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { break; } @@ -383,7 +380,7 @@ bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const c return result; } -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path) { +bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path) { furi_assert(instance); furi_assert(file_path); @@ -414,7 +411,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const break; } - if(!music_player_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { + if(!music_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { FURI_LOG_E(TAG, "Invalid file content"); break; } @@ -429,7 +426,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const return result; } -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string) { +bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string) { furi_assert(instance); const char* cursor = string; @@ -470,28 +467,25 @@ bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, con return false; } cursor++; - if(!music_player_worker_parse_notes(instance, cursor)) { + if(!music_worker_parse_notes(instance, cursor)) { return false; } return true; } -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context) { +void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context) { furi_assert(instance); instance->callback = callback; instance->callback_context = context; } -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume) { +void music_worker_set_volume(MusicWorker* instance, float volume) { furi_assert(instance); instance->volume = volume; } -void music_player_worker_start(MusicPlayerWorker* instance) { +void music_worker_start(MusicWorker* instance) { furi_assert(instance); furi_assert(instance->should_work == false); @@ -499,10 +493,15 @@ void music_player_worker_start(MusicPlayerWorker* instance) { furi_thread_start(instance->thread); } -void music_player_worker_stop(MusicPlayerWorker* instance) { +void music_worker_stop(MusicWorker* instance) { furi_assert(instance); furi_assert(instance->should_work == true); instance->should_work = false; furi_thread_join(instance->thread); } + +bool music_worker_is_playing(MusicWorker* instance) { + furi_assert(instance); + return instance->should_work; +} diff --git a/lib/music_worker/music_worker.h b/lib/music_worker/music_worker.h new file mode 100644 index 000000000..5a7cb4936 --- /dev/null +++ b/lib/music_worker/music_worker.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +typedef void (*MusicWorkerCallback)( + uint8_t semitone, + uint8_t dots, + uint8_t duration, + float position, + void* context); + +typedef struct MusicWorker MusicWorker; + +MusicWorker* music_worker_alloc(); + +void music_worker_clear(MusicWorker* instance); + +void music_worker_free(MusicWorker* instance); + +bool music_worker_load(MusicWorker* instance, const char* file_path); + +bool music_worker_load_fmf_from_file(MusicWorker* instance, const char* file_path); + +bool music_worker_load_rtttl_from_file(MusicWorker* instance, const char* file_path); + +bool music_worker_load_rtttl_from_string(MusicWorker* instance, const char* string); + +void music_worker_set_callback(MusicWorker* instance, MusicWorkerCallback callback, void* context); + +void music_worker_set_volume(MusicWorker* instance, float volume); + +void music_worker_start(MusicWorker* instance); + +void music_worker_stop(MusicWorker* instance); + +bool music_worker_is_playing(MusicWorker* instance); From b51f0b2c784d3c6eb3de2dc08f2ed119a7c320b0 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 29 Jun 2023 19:18:49 +0300 Subject: [PATCH 315/370] Manifestos --- applications/external/arkanoid/application.fam | 3 +++ applications/external/barcode_generator/application.fam | 3 +++ applications/external/blackjack/application.fam | 5 ++++- applications/external/bomberduck/application.fam | 3 +++ applications/external/dtmf_dolphin/application.fam | 3 +++ applications/external/esp8266_deauth/application.fam | 3 +++ applications/external/flappy_bird/application.fam | 3 +++ applications/external/flipper_i2ctools/application.fam | 3 +++ applications/external/game15/application.fam | 3 +++ applications/external/game_2048/application.fam | 5 ++++- applications/external/gps_nmea_uart/application.fam | 3 +++ applications/external/hc_sr04/application.fam | 3 +++ applications/external/heap_defence_game/application.fam | 3 +++ applications/external/hex_viewer/application.fam | 3 +++ applications/external/ir_scope/application.fam | 3 +++ applications/external/lightmeter/application.fam | 3 +++ applications/external/metronome/application.fam | 3 +++ applications/external/minesweeper/application.fam | 3 +++ applications/external/morse_code/application.fam | 6 ++++-- applications/external/mousejacker/application.fam | 3 +++ applications/external/multi_converter/application.fam | 3 +++ applications/external/nrfsniff/application.fam | 3 +++ applications/external/playlist/application.fam | 3 +++ applications/external/pocsag_pager/application.fam | 3 +++ applications/external/protoview/application.fam | 3 +++ applications/external/sentry_safe/application.fam | 3 +++ applications/external/solitaire/application.fam | 5 ++++- applications/external/spectrum_analyzer/application.fam | 3 +++ applications/external/swd_probe/application.fam | 5 ++++- applications/external/tetris_game/application.fam | 3 +++ applications/external/text_viewer/application.fam | 3 +++ applications/external/tictactoe_game/application.fam | 3 +++ applications/external/uart_terminal/application.fam | 3 +++ applications/external/unitemp/application.fam | 5 +++-- applications/external/wav_player/application.fam | 3 +++ applications/external/wifi_scanner/application.fam | 3 +++ applications/external/zombiez/application.fam | 3 +++ 37 files changed, 116 insertions(+), 8 deletions(-) diff --git a/applications/external/arkanoid/application.fam b/applications/external/arkanoid/application.fam index 9092c32c6..cdd5f4e8a 100644 --- a/applications/external/arkanoid/application.fam +++ b/applications/external/arkanoid/application.fam @@ -8,4 +8,7 @@ App( order=30, fap_icon="arkanoid_10px.png", fap_category="Games", + fap_author="@xMasterX & @gotnull", + fap_version="1.0", + fap_description="Arkanoid Game", ) diff --git a/applications/external/barcode_generator/application.fam b/applications/external/barcode_generator/application.fam index 4f9f234cc..61d6b7394 100644 --- a/applications/external/barcode_generator/application.fam +++ b/applications/external/barcode_generator/application.fam @@ -11,4 +11,7 @@ App( order=50, fap_icon="barcode_10px.png", fap_category="Misc", + fap_author="@xMasterX & @msvsergey & @McAzzaMan", + fap_version="1.0", + fap_description="App displays Barcode on flipper screen and allows to edit it", ) \ No newline at end of file diff --git a/applications/external/blackjack/application.fam b/applications/external/blackjack/application.fam index 139d94f2e..271620d72 100644 --- a/applications/external/blackjack/application.fam +++ b/applications/external/blackjack/application.fam @@ -8,5 +8,8 @@ App( order=30, fap_icon="blackjack_10px.png", fap_category="Games", - fap_icon_assets="assets" + fap_icon_assets="assets", + fap_author="@teeebor", + fap_version="1.0", + fap_description="Blackjack Game", ) \ No newline at end of file diff --git a/applications/external/bomberduck/application.fam b/applications/external/bomberduck/application.fam index 2f8246af9..5242d0f8c 100644 --- a/applications/external/bomberduck/application.fam +++ b/applications/external/bomberduck/application.fam @@ -11,4 +11,7 @@ App( fap_icon="bomb.png", fap_category="Games", fap_icon_assets="assets", + fap_author="@leo-need-more-coffee & @xMasterX", + fap_version="1.0", + fap_description="Bomberduck(Bomberman) Game", ) diff --git a/applications/external/dtmf_dolphin/application.fam b/applications/external/dtmf_dolphin/application.fam index 460f6ded6..5f01bc9a0 100644 --- a/applications/external/dtmf_dolphin/application.fam +++ b/applications/external/dtmf_dolphin/application.fam @@ -12,4 +12,7 @@ App( stack_size=8 * 1024, order=20, fap_category="Tools", + fap_author="@litui & @xMasterX", + fap_version="1.0", + fap_description="DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.", ) diff --git a/applications/external/esp8266_deauth/application.fam b/applications/external/esp8266_deauth/application.fam index 5db7bb40e..60d23ec96 100644 --- a/applications/external/esp8266_deauth/application.fam +++ b/applications/external/esp8266_deauth/application.fam @@ -8,4 +8,7 @@ App( order=100, fap_icon="wifi_10px.png", fap_category="GPIO", + fap_author="@SequoiaSan & @xMasterX", + fap_version="1.0", + fap_description="DSTIKE Deauther module interface, based on ESP8266", ) diff --git a/applications/external/flappy_bird/application.fam b/applications/external/flappy_bird/application.fam index 9bfab454f..ad18e57e1 100644 --- a/applications/external/flappy_bird/application.fam +++ b/applications/external/flappy_bird/application.fam @@ -9,4 +9,7 @@ App( fap_icon="flappy_10px.png", fap_category="Games", fap_icon_assets="assets", + fap_author="@DroomOne & @xMasterX", + fap_version="1.0", + fap_description="Flappy Bird Game", ) diff --git a/applications/external/flipper_i2ctools/application.fam b/applications/external/flipper_i2ctools/application.fam index a6bab122a..f91bcceba 100644 --- a/applications/external/flipper_i2ctools/application.fam +++ b/applications/external/flipper_i2ctools/application.fam @@ -9,4 +9,7 @@ App( fap_icon="i2ctools.png", fap_category="GPIO", fap_icon_assets="images", + fap_author="@NaejEL", + fap_version="1.0", + fap_description="Set of i2c tools", ) \ No newline at end of file diff --git a/applications/external/game15/application.fam b/applications/external/game15/application.fam index 969cb536a..d6b1e10a0 100644 --- a/applications/external/game15/application.fam +++ b/applications/external/game15/application.fam @@ -8,4 +8,7 @@ App( fap_icon="game15_10px.png", order=30, fap_category="Games", + fap_author="@x27", + fap_version="1.0", + fap_description="Logic Game", ) diff --git a/applications/external/game_2048/application.fam b/applications/external/game_2048/application.fam index 1be179dca..b6e6640ab 100644 --- a/applications/external/game_2048/application.fam +++ b/applications/external/game_2048/application.fam @@ -9,5 +9,8 @@ App( stack_size=1 * 1024, order=90, fap_icon="game_2048.png", - fap_category="Games" + fap_category="Games", + fap_author="@eugene-kirzhanov", + fap_version="1.0", + fap_description="2048 Game", ) \ No newline at end of file diff --git a/applications/external/gps_nmea_uart/application.fam b/applications/external/gps_nmea_uart/application.fam index e02785744..c3897e38a 100644 --- a/applications/external/gps_nmea_uart/application.fam +++ b/applications/external/gps_nmea_uart/application.fam @@ -8,4 +8,7 @@ App( order=35, fap_icon="gps_10px.png", fap_category="GPIO", + fap_author="@ezod & @xMasterX", + fap_version="1.0", + fap_description="Works with GPS modules via UART, using NMEA protocol.", ) diff --git a/applications/external/hc_sr04/application.fam b/applications/external/hc_sr04/application.fam index c10d7c42f..f697f00fc 100644 --- a/applications/external/hc_sr04/application.fam +++ b/applications/external/hc_sr04/application.fam @@ -10,4 +10,7 @@ App( order=20, fap_icon="dist_sensor10px.png", fap_category="GPIO", + fap_author="@xMasterX (first implementation by @Sanqui)", + fap_version="1.0", + fap_description="HC-SR(04) Distance sensor reader", ) \ No newline at end of file diff --git a/applications/external/heap_defence_game/application.fam b/applications/external/heap_defence_game/application.fam index b132531d1..d19b48ebb 100644 --- a/applications/external/heap_defence_game/application.fam +++ b/applications/external/heap_defence_game/application.fam @@ -8,4 +8,7 @@ App( fap_category="Games", fap_icon="box.png", fap_icon_assets="assets_images", + fap_author="@xMasterX (original implementation by @wquinoa & @Vedmein)", + fap_version="1.0", + fap_description="Heap Defence game from hackathon (aka Stack Attack)", ) diff --git a/applications/external/hex_viewer/application.fam b/applications/external/hex_viewer/application.fam index 9bb15dc86..03967ffa5 100644 --- a/applications/external/hex_viewer/application.fam +++ b/applications/external/hex_viewer/application.fam @@ -12,4 +12,7 @@ App( fap_icon="icons/hex_10px.png", fap_category="Misc", fap_icon_assets="icons", + fap_author="@QtRoS", + fap_version="1.0", + fap_description="App allows to view various files as HEX.", ) diff --git a/applications/external/ir_scope/application.fam b/applications/external/ir_scope/application.fam index f99e14515..f6fb6fbdc 100644 --- a/applications/external/ir_scope/application.fam +++ b/applications/external/ir_scope/application.fam @@ -8,4 +8,7 @@ App( stack_size=2 * 1024, fap_icon="ir_scope.png", fap_category="Tools", + fap_author="@kallanreed", + fap_version="1.0", + fap_description="App allows to see incoming IR signals.", ) diff --git a/applications/external/lightmeter/application.fam b/applications/external/lightmeter/application.fam index 7df664517..fc46550fe 100644 --- a/applications/external/lightmeter/application.fam +++ b/applications/external/lightmeter/application.fam @@ -20,4 +20,7 @@ App( ), ], fap_icon_assets="icons", + fap_author="@oleksiikutuzov", + fap_version="1.0", + fap_description="Lightmeter app for photography based on BH1750 sensor", ) diff --git a/applications/external/metronome/application.fam b/applications/external/metronome/application.fam index 8acd4b3b0..dc10e386f 100644 --- a/applications/external/metronome/application.fam +++ b/applications/external/metronome/application.fam @@ -11,4 +11,7 @@ App( fap_icon_assets="images", stack_size=2 * 1024, order=20, + fap_author="@panki27 & @xMasterX", + fap_version="1.0", + fap_description="Metronome app", ) diff --git a/applications/external/minesweeper/application.fam b/applications/external/minesweeper/application.fam index 0a4066279..07b3e97bb 100644 --- a/applications/external/minesweeper/application.fam +++ b/applications/external/minesweeper/application.fam @@ -8,4 +8,7 @@ App( fap_category="Games", fap_icon="minesweeper_icon.png", order=35, + fap_author="@panki27 & @xMasterX", + fap_version="1.0", + fap_description="Minesweeper Game", ) diff --git a/applications/external/morse_code/application.fam b/applications/external/morse_code/application.fam index 5db0f6a94..9e73ce710 100644 --- a/applications/external/morse_code/application.fam +++ b/applications/external/morse_code/application.fam @@ -9,6 +9,8 @@ App( stack_size=1 * 1024, order=20, fap_icon="morse_code_10px.png", - fap_category="Media" - + fap_category="Media", + fap_author="@wh00hw & @xMasterX", + fap_version="1.0", + fap_description="Simple Morse Code parser", ) \ No newline at end of file diff --git a/applications/external/mousejacker/application.fam b/applications/external/mousejacker/application.fam index 3b8ae7104..1e16538c2 100644 --- a/applications/external/mousejacker/application.fam +++ b/applications/external/mousejacker/application.fam @@ -11,6 +11,9 @@ App( order=60, fap_icon="mouse_10px.png", fap_category="GPIO", + fap_author="@mothball187 & @xMasterX", + fap_version="1.0", + fap_description="App works with NRF24 Sniffer app to perform mousejack attacks", fap_icon_assets="images", fap_private_libs=[ Lib( diff --git a/applications/external/multi_converter/application.fam b/applications/external/multi_converter/application.fam index 8d5e7bb44..84eaa805b 100644 --- a/applications/external/multi_converter/application.fam +++ b/applications/external/multi_converter/application.fam @@ -8,4 +8,7 @@ App( order=19, fap_icon="converter_10px.png", fap_category="Misc", + fap_author="@theisolinearchip", + fap_version="1.0", + fap_description="A multi-unit converter written with an easy and expandable system for adding new units and conversion methods", ) diff --git a/applications/external/nrfsniff/application.fam b/applications/external/nrfsniff/application.fam index 1ec1718c1..03646724d 100644 --- a/applications/external/nrfsniff/application.fam +++ b/applications/external/nrfsniff/application.fam @@ -8,6 +8,9 @@ App( order=70, fap_icon="nrfsniff_10px.png", fap_category="GPIO", + fap_author="@mothball187 & @xMasterX", + fap_version="1.0", + fap_description="App captures addresses to use with NRF24 Mouse Jacker app to perform mousejack attacks", fap_private_libs=[ Lib( name="nrf24", diff --git a/applications/external/playlist/application.fam b/applications/external/playlist/application.fam index 6f74f2b55..d5ec9fd81 100644 --- a/applications/external/playlist/application.fam +++ b/applications/external/playlist/application.fam @@ -9,4 +9,7 @@ App( fap_icon="playlist_10px.png", fap_category="Sub-GHz", fap_icon_assets="images", + fap_author="@darmiel", + fap_version="1.0", + fap_description="App works with list of sub-ghz files from .txt file that contains paths to target files.", ) diff --git a/applications/external/pocsag_pager/application.fam b/applications/external/pocsag_pager/application.fam index 3cef05374..cb893f66f 100644 --- a/applications/external/pocsag_pager/application.fam +++ b/applications/external/pocsag_pager/application.fam @@ -9,4 +9,7 @@ App( fap_icon="pocsag_pager_10px.png", fap_category="Sub-GHz", fap_icon_assets="images", + fap_author="@xMasterX & @Shmuma", + fap_version="1.0", + fap_description="App can capture POCSAG 1200 messages on CC1101 supported frequencies.", ) diff --git a/applications/external/protoview/application.fam b/applications/external/protoview/application.fam index 29cef0e10..942552daa 100644 --- a/applications/external/protoview/application.fam +++ b/applications/external/protoview/application.fam @@ -8,4 +8,7 @@ App( order=50, fap_icon="appicon.png", fap_category="Sub-GHz", + fap_author="@antirez & (fixes by @xMasterX)", + fap_version="1.0", + fap_description="Digital signal detection, visualization, editing and reply tool", ) diff --git a/applications/external/sentry_safe/application.fam b/applications/external/sentry_safe/application.fam index b9254c58d..7c114df6e 100644 --- a/applications/external/sentry_safe/application.fam +++ b/applications/external/sentry_safe/application.fam @@ -8,4 +8,7 @@ App( order=80, fap_icon="safe_10px.png", fap_category="GPIO", + fap_author="@H4ckd4ddy & @xMasterX (ported to latest firmware)", + fap_version="1.0", + fap_description="App exploiting vulnerability to open any Sentry Safe and Master Lock electronic safe without any pin code via UART pins.", ) diff --git a/applications/external/solitaire/application.fam b/applications/external/solitaire/application.fam index 7b5910c49..99278ce25 100644 --- a/applications/external/solitaire/application.fam +++ b/applications/external/solitaire/application.fam @@ -8,5 +8,8 @@ App( order=30, fap_icon="solitaire_10px.png", fap_category="Games", - fap_icon_assets="assets" + fap_icon_assets="assets", + fap_author="@teeebor", + fap_version="1.0", + fap_description="Solitaire game", ) \ No newline at end of file diff --git a/applications/external/spectrum_analyzer/application.fam b/applications/external/spectrum_analyzer/application.fam index 79effb3a7..30d0412ac 100644 --- a/applications/external/spectrum_analyzer/application.fam +++ b/applications/external/spectrum_analyzer/application.fam @@ -8,4 +8,7 @@ App( order=12, fap_icon="spectrum_10px.png", fap_category="Sub-GHz", + fap_author="@xMasterX & @theY4Kman (original by @jolcese)", + fap_version="1.0", + fap_description="Shows received signals on spectrum, not actual analyzer, more like a demo app", ) diff --git a/applications/external/swd_probe/application.fam b/applications/external/swd_probe/application.fam index c255e6f81..4e29f9b1e 100644 --- a/applications/external/swd_probe/application.fam +++ b/applications/external/swd_probe/application.fam @@ -8,5 +8,8 @@ App( order=10, fap_icon="icons/app.png", fap_category="GPIO", - fap_icon_assets="icons" + fap_icon_assets="icons", + fap_author="@g3gg0 & (fixes by @xMasterX)", + fap_version="1.0", + fap_description="ARM SWD (Single Wire Debug) Probe", ) diff --git a/applications/external/tetris_game/application.fam b/applications/external/tetris_game/application.fam index a6c433b9e..c883f3919 100644 --- a/applications/external/tetris_game/application.fam +++ b/applications/external/tetris_game/application.fam @@ -8,4 +8,7 @@ App( order=20, fap_icon="tetris_10px.png", fap_category="Games", + fap_author="@xMasterX & @jeffplang", + fap_version="1.0", + fap_description="Tetris Game", ) diff --git a/applications/external/text_viewer/application.fam b/applications/external/text_viewer/application.fam index 518626f41..e36b7a870 100644 --- a/applications/external/text_viewer/application.fam +++ b/applications/external/text_viewer/application.fam @@ -12,4 +12,7 @@ App( fap_icon="icons/text_10px.png", fap_category="Misc", fap_icon_assets="icons", + fap_author="@kowalski7cc & @kyhwana", + fap_version="1.0", + fap_description="Text viewer application", ) diff --git a/applications/external/tictactoe_game/application.fam b/applications/external/tictactoe_game/application.fam index 4d50bc1a2..cd3ae6f27 100644 --- a/applications/external/tictactoe_game/application.fam +++ b/applications/external/tictactoe_game/application.fam @@ -8,4 +8,7 @@ App( order=40, fap_icon="tictactoe_10px.png", fap_category="Games", + fap_author="@xMasterX & @gotnull", + fap_version="1.0", + fap_description="Tic Tac Toe game, for 2 players, play on one device", ) diff --git a/applications/external/uart_terminal/application.fam b/applications/external/uart_terminal/application.fam index b167ab83e..e930ff2f7 100644 --- a/applications/external/uart_terminal/application.fam +++ b/applications/external/uart_terminal/application.fam @@ -9,4 +9,7 @@ App( fap_icon="uart_terminal.png", fap_category="GPIO", fap_icon_assets="assets", + fap_author="@cool4uma & (some fixes by @xMasterX)", + fap_version="1.0", + fap_description="App to control various devices via UART interface.", ) diff --git a/applications/external/unitemp/application.fam b/applications/external/unitemp/application.fam index 3971f5138..c7c3263ad 100644 --- a/applications/external/unitemp/application.fam +++ b/applications/external/unitemp/application.fam @@ -9,8 +9,9 @@ App( stack_size=2 * 1024, order=100, fap_description = "Universal temperature sensors reader", - fap_author = "Quenon", - fap_weburl = "https://github.com/quen0n/Unitemp-Flipper-Zero-Plugin", + fap_version="1.4", + fap_author = "@quen0n & (fixes by @xMasterX)", + fap_weburl = "https://github.com/quen0n/unitemp-flipperzero", fap_category="GPIO", fap_icon="icon.png", fap_icon_assets="assets", diff --git a/applications/external/wav_player/application.fam b/applications/external/wav_player/application.fam index c71527c9d..b476f8e43 100644 --- a/applications/external/wav_player/application.fam +++ b/applications/external/wav_player/application.fam @@ -8,4 +8,7 @@ App( fap_icon="wav_10px.png", fap_category="Media", fap_icon_assets="images", + fap_author="@DrZlo13 & (ported, fixed by @xMasterX), (improved by @LTVA1)", + fap_version="1.0", + fap_description="Audio player for WAV files, recommended to convert files to unsigned 8-bit PCM stereo, but it may work with others too", ) diff --git a/applications/external/wifi_scanner/application.fam b/applications/external/wifi_scanner/application.fam index 144354b0f..1375931ef 100644 --- a/applications/external/wifi_scanner/application.fam +++ b/applications/external/wifi_scanner/application.fam @@ -8,4 +8,7 @@ App( order=110, fap_icon="wifi_10px.png", fap_category="GPIO", + fap_author="@SequoiaSan & @xMasterX", + fap_version="1.0", + fap_description="WiFi scanner module interface, based on ESP8266", ) diff --git a/applications/external/zombiez/application.fam b/applications/external/zombiez/application.fam index 69f5a9379..ca6120899 100644 --- a/applications/external/zombiez/application.fam +++ b/applications/external/zombiez/application.fam @@ -8,4 +8,7 @@ App( order=280, fap_icon="zombie_10px.png", fap_category="Games", + fap_author="@DevMilanIan & @xMasterX, (original By @Dooskington)", + fap_version="1.0", + fap_description="Defend your walls from the zombies", ) From 72ed3660e537c59c18ef6962d26099ea765bdb35 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 18:35:35 +0200 Subject: [PATCH 316/370] Move desktop keybinds to external file --- applications/services/desktop/desktop.c | 22 +++++++++++-------- applications/services/desktop/desktop_i.h | 1 + .../services/desktop/desktop_settings.c | 10 +++++++++ .../services/desktop/desktop_settings.h | 13 +++++++---- .../desktop_settings_scene_keybinds_choose.c | 11 ++++++---- 5 files changed, 40 insertions(+), 17 deletions(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index ed2776f0e..1cf35caad 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -464,7 +464,7 @@ void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key) { KeybindType type = keybind_types[_type]; KeybindKey key = keybind_keys[_key]; - const char* keybind = instance->settings.keybinds[type][key].data; + const char* keybind = instance->keybinds[type][key].data; if(!strnlen(keybind, MAX_KEYBIND_LENGTH)) return; if(!strncmp(keybind, "Archive", MAX_KEYBIND_LENGTH)) { @@ -498,18 +498,22 @@ int32_t desktop_srv(void* p) { } if(!ok) { memset(&desktop->settings, 0, sizeof(desktop->settings)); - strcpy(desktop->settings.keybinds[KeybindTypePress][KeybindKeyUp].data, "Lock Menu"); - strcpy(desktop->settings.keybinds[KeybindTypePress][KeybindKeyDown].data, "Archive"); - strcpy(desktop->settings.keybinds[KeybindTypePress][KeybindKeyRight].data, "Passport"); - strcpy( - desktop->settings.keybinds[KeybindTypePress][KeybindKeyLeft].data, - EXT_PATH("apps/Misc/nightstand.fap")); - strcpy(desktop->settings.keybinds[KeybindTypeHold][KeybindKeyRight].data, "Device Info"); - strcpy(desktop->settings.keybinds[KeybindTypeHold][KeybindKeyLeft].data, "Lock with PIN"); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); furi_hal_rtc_set_pin_fails(0); } + if(!DESKTOP_KEYBINDS_LOAD(&desktop->keybinds, sizeof(desktop->keybinds))) { + memset(&desktop->keybinds, 0, sizeof(desktop->keybinds)); + strcpy(desktop->keybinds[KeybindTypePress][KeybindKeyUp].data, "Lock Menu"); + strcpy(desktop->keybinds[KeybindTypePress][KeybindKeyDown].data, "Archive"); + strcpy(desktop->keybinds[KeybindTypePress][KeybindKeyRight].data, "Passport"); + strcpy( + desktop->keybinds[KeybindTypePress][KeybindKeyLeft].data, + EXT_PATH("apps/Misc/nightstand.fap")); + strcpy(desktop->keybinds[KeybindTypeHold][KeybindKeyRight].data, "Device Info"); + strcpy(desktop->keybinds[KeybindTypeHold][KeybindKeyLeft].data, "Lock with PIN"); + } + desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 65c09d112..d42bf2616 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -55,6 +55,7 @@ struct Desktop { ViewStack* locked_view_stack; DesktopSettings settings; + Keybind keybinds[KeybindTypeCount][KeybindKeyCount]; DesktopViewPinInput* pin_input_view; ViewPort* lock_icon_viewport; diff --git a/applications/services/desktop/desktop_settings.c b/applications/services/desktop/desktop_settings.c index d1df4a9a6..48d12b3ce 100644 --- a/applications/services/desktop/desktop_settings.c +++ b/applications/services/desktop/desktop_settings.c @@ -17,3 +17,13 @@ bool DESKTOP_SETTINGS_LOAD(DesktopSettings* x) { DESKTOP_SETTINGS_MAGIC, DESKTOP_SETTINGS_VER); } + +bool DESKTOP_KEYBINDS_SAVE(Keybind (*x)[KeybindTypeCount][KeybindKeyCount], size_t size) { + return saved_struct_save( + DESKTOP_KEYBINDS_PATH, x, size, DESKTOP_KEYBINDS_MAGIC, DESKTOP_KEYBINDS_VER); +} + +bool DESKTOP_KEYBINDS_LOAD(Keybind (*x)[KeybindTypeCount][KeybindKeyCount], size_t size) { + return saved_struct_load( + DESKTOP_KEYBINDS_PATH, x, size, DESKTOP_KEYBINDS_MAGIC, DESKTOP_KEYBINDS_VER); +} diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 74acf6bfb..3cd85870e 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -6,12 +6,14 @@ #include #include -#define DESKTOP_SETTINGS_VER (11) - #define DESKTOP_SETTINGS_OLD_PATH CFG_PATH("desktop.settings") #define DESKTOP_SETTINGS_PATH INT_PATH(".desktop.settings") #define DESKTOP_SETTINGS_MAGIC (0x17) -#define PIN_MAX_LENGTH 12 +#define DESKTOP_SETTINGS_VER (11) + +#define DESKTOP_KEYBINDS_PATH CFG_PATH(".desktop.keybinds") +#define DESKTOP_KEYBINDS_MAGIC (0x14) +#define DESKTOP_KEYBINDS_VER (1) #define DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG "run_pin_setup" @@ -46,9 +48,12 @@ typedef struct { PinCode pin_code; uint32_t auto_lock_delay_ms; bool auto_lock_with_pin; - Keybind keybinds[KeybindTypeCount][KeybindKeyCount]; } DesktopSettings; bool DESKTOP_SETTINGS_SAVE(DesktopSettings* x); bool DESKTOP_SETTINGS_LOAD(DesktopSettings* x); + +bool DESKTOP_KEYBINDS_SAVE(Keybind (*x)[KeybindTypeCount][KeybindKeyCount], size_t size); + +bool DESKTOP_KEYBINDS_LOAD(Keybind (*x)[KeybindTypeCount][KeybindKeyCount], size_t size); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c index d65a20e5b..363ad057d 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c @@ -34,9 +34,10 @@ static void desktop_settings_scene_keybinds_choose_main_callback(void* context, scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); KeybindKey key = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); - char* keybind = app->desktop->settings.keybinds[type][key].data; + char* keybind = app->desktop->keybinds[type][key].data; strncpy(keybind, FLIPPER_APPS[index].name, MAX_KEYBIND_LENGTH); + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); scene_manager_previous_scene(app->scene_manager); scene_manager_previous_scene(app->scene_manager); scene_manager_previous_scene(app->scene_manager); @@ -49,7 +50,7 @@ static void desktop_settings_scene_keybinds_choose_ext_callback(void* context, u scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); KeybindKey key = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); - char* keybind = app->desktop->settings.keybinds[type][key].data; + char* keybind = app->desktop->keybinds[type][key].data; const DialogsFileBrowserOptions browser_options = { .extension = ".fap", @@ -71,6 +72,7 @@ static void desktop_settings_scene_keybinds_choose_ext_callback(void* context, u if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene strncpy(keybind, furi_string_get_cstr(temp_path), MAX_KEYBIND_LENGTH); + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); scene_manager_previous_scene(app->scene_manager); scene_manager_previous_scene(app->scene_manager); scene_manager_previous_scene(app->scene_manager); @@ -84,9 +86,10 @@ static void desktop_settings_scene_keybinds_choose_extra_callback(void* context, scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); KeybindKey key = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); - char* keybind = app->desktop->settings.keybinds[type][key].data; + char* keybind = app->desktop->keybinds[type][key].data; strncpy(keybind, EXTRA_OPTIONS[index - FLIPPER_APPS_COUNT], MAX_KEYBIND_LENGTH); + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); scene_manager_previous_scene(app->scene_manager); scene_manager_previous_scene(app->scene_manager); scene_manager_previous_scene(app->scene_manager); @@ -102,7 +105,7 @@ void desktop_settings_scene_keybinds_choose_on_enter(void* context) { KeybindKey key = scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); uint32_t pre_select_item = 0; - char* keybind = app->desktop->settings.keybinds[type][key].data; + char* keybind = app->desktop->keybinds[type][key].data; size_t submenu_i = -1; for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { From c85cb0bacb21fa3a5cdd0d2df799200ae4b0a51a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 20:03:15 +0200 Subject: [PATCH 317/370] Rework keybinds menu + save pin when clicking done --- applications/services/desktop/desktop.c | 4 +- .../desktop_settings/desktop_settings_app.c | 23 ++- .../desktop_settings/desktop_settings_app.h | 14 ++ .../scenes/desktop_settings_scene_config.h | 3 +- .../desktop_settings_scene_keybinds_action.c | 83 +++++++++ ...ktop_settings_scene_keybinds_action_type.c | 147 +++++++++++++++ .../desktop_settings_scene_keybinds_choose.c | 171 ------------------ .../desktop_settings_scene_keybinds_key.c | 6 +- .../desktop_settings_scene_pin_disable.c | 1 + .../desktop_settings_scene_pin_setup_done.c | 5 +- .../scenes/desktop_settings_scene_start.c | 2 + 11 files changed, 282 insertions(+), 177 deletions(-) create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action.c create mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action_type.c delete mode 100644 applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 1cf35caad..fe1a2344a 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -467,7 +467,9 @@ void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key) { const char* keybind = instance->keybinds[type][key].data; if(!strnlen(keybind, MAX_KEYBIND_LENGTH)) return; - if(!strncmp(keybind, "Archive", MAX_KEYBIND_LENGTH)) { + if(!strncmp(keybind, "Apps Menu", MAX_KEYBIND_LENGTH)) { + loader_start_with_gui_error(instance->loader, LOADER_APPLICATIONS_NAME, NULL); + } else if(!strncmp(keybind, "Archive", MAX_KEYBIND_LENGTH)) { view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventOpenArchive); } else if(!strncmp(keybind, "Device Info", MAX_KEYBIND_LENGTH)) { loader_start_with_gui_error(instance->loader, "Power", "about_battery"); diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index 563ddb0ed..d394f389c 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -6,6 +6,17 @@ #include "scenes/desktop_settings_scene.h" #include +const char* EXTRA_KEYBINDS[] = { + "Apps Menu", + "Archive", + "Device Info", + "Lock Menu", + "Lock Keypad", + "Lock with PIN", + "Passport", +}; +const size_t EXTRA_KEYBINDS_COUNT = COUNT_OF(EXTRA_KEYBINDS); + static bool desktop_settings_custom_event_callback(void* context, uint32_t event) { furi_assert(context); DesktopSettingsApp* app = context; @@ -18,6 +29,14 @@ static bool desktop_settings_back_event_callback(void* context) { return scene_manager_handle_back_event(app->scene_manager); } +char* desktop_settings_app_get_keybind(DesktopSettingsApp* app) { + KeybindType type = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); + KeybindKey key = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); + return app->desktop->keybinds[type][key].data; +} + DesktopSettingsApp* desktop_settings_app_alloc() { DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp)); @@ -100,7 +119,9 @@ extern int32_t desktop_settings_app(void* p) { } view_dispatcher_run(app->view_dispatcher); - DESKTOP_SETTINGS_SAVE(&app->desktop->settings); + if(app->save_settings) { + DESKTOP_SETTINGS_SAVE(&app->desktop->settings); + } desktop_settings_app_free(app); return 0; } diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index b4eba1e89..092ed8e86 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -23,6 +23,16 @@ typedef enum { DesktopSettingsAppViewIdPinSetupHowto2, } DesktopSettingsAppView; +typedef enum { + DesktopSettingsAppKeybindActionTypeMainApp, + DesktopSettingsAppKeybindActionTypeExternalApp, + DesktopSettingsAppKeybindActionTypeMoreActions, + DesktopSettingsAppKeybindActionTypeRemoveKeybind, +} DesktopSettingsAppKeybindActionType; + +extern const char* EXTRA_KEYBINDS[]; +extern const size_t EXTRA_KEYBINDS_COUNT; + typedef struct { Gui* gui; Desktop* desktop; @@ -40,4 +50,8 @@ typedef struct { bool pincode_buffer_filled; uint8_t menu_idx; + + bool save_settings; } DesktopSettingsApp; + +char* desktop_settings_app_get_keybind(DesktopSettingsApp* app); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h index f78c0d6ba..458ba6bf2 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_config.h @@ -1,7 +1,8 @@ ADD_SCENE(desktop_settings, start, Start) ADD_SCENE(desktop_settings, keybinds_type, KeybindsType) ADD_SCENE(desktop_settings, keybinds_key, KeybindsKey) -ADD_SCENE(desktop_settings, keybinds_choose, KeybindsChoose) +ADD_SCENE(desktop_settings, keybinds_action_type, KeybindsActionType) +ADD_SCENE(desktop_settings, keybinds_action, KeybindsAction) ADD_SCENE(desktop_settings, pin_menu, PinMenu) ADD_SCENE(desktop_settings, pin_auth, PinAuth) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action.c new file mode 100644 index 000000000..4d2f01fca --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action.c @@ -0,0 +1,83 @@ +#include "../desktop_settings_app.h" +#include "applications.h" +#include "desktop_settings_scene.h" + +static void + desktop_settings_scene_keybinds_action_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + DesktopSettingsAppKeybindActionType action_type = scene_manager_get_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsActionType); + char* keybind = desktop_settings_app_get_keybind(app); + + if(action_type == DesktopSettingsAppKeybindActionTypeMainApp) { + strncpy(keybind, FLIPPER_APPS[index].name, MAX_KEYBIND_LENGTH); + } else if(action_type == DesktopSettingsAppKeybindActionTypeMoreActions) { + strncpy(keybind, EXTRA_KEYBINDS[index], MAX_KEYBIND_LENGTH); + } + + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppSceneStart); +} + +void desktop_settings_scene_keybinds_action_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + char* keybind = desktop_settings_app_get_keybind(app); + submenu_reset(submenu); + + uint32_t pre_select_item = 0; + DesktopSettingsAppKeybindActionType action_type = scene_manager_get_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsActionType); + + if(action_type == DesktopSettingsAppKeybindActionTypeMainApp) { + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + submenu_add_item( + submenu, + FLIPPER_APPS[i].name, + i, + desktop_settings_scene_keybinds_action_submenu_callback, + app); + + // Select keybind item in submenu + if(!strncmp(FLIPPER_APPS[i].name, keybind, MAX_KEYBIND_LENGTH)) { + pre_select_item = i; + } + } + } else if(action_type == DesktopSettingsAppKeybindActionTypeMoreActions) { + for(size_t i = 0; i < EXTRA_KEYBINDS_COUNT; i++) { + submenu_add_item( + submenu, + EXTRA_KEYBINDS[i], + i, + desktop_settings_scene_keybinds_action_submenu_callback, + app); + + // Select keybind item in submenu + if(!strncmp(EXTRA_KEYBINDS[i], keybind, MAX_KEYBIND_LENGTH)) { + pre_select_item = i; + } + } + } + + // submenu_set_header(submenu, "Keybind action:"); + submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_action_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + } + + return consumed; +} + +void desktop_settings_scene_keybinds_action_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action_type.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action_type.c new file mode 100644 index 000000000..9fd078f7e --- /dev/null +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_action_type.c @@ -0,0 +1,147 @@ +#include "../desktop_settings_app.h" +#include "applications.h" +#include "desktop_settings_scene.h" +#include +#include +#include + +static bool keybinds_fap_selector_item_callback( + FuriString* file_path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + UNUSED(context); + Storage* storage = furi_record_open(RECORD_STORAGE); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); + furi_record_close(RECORD_STORAGE); + return success; +} + +static void + desktop_settings_scene_keybinds_action_type_submenu_callback(void* context, uint32_t index) { + DesktopSettingsApp* app = context; + scene_manager_set_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsActionType, index); + char* keybind = desktop_settings_app_get_keybind(app); + + switch(index) { + case DesktopSettingsAppKeybindActionTypeMainApp: + case DesktopSettingsAppKeybindActionTypeMoreActions: + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsAction); + break; + case DesktopSettingsAppKeybindActionTypeExternalApp: { + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .icon = &I_unknown_10px, + .skip_assets = true, + .hide_ext = true, + .item_loader_callback = keybinds_fap_selector_item_callback, + .item_loader_context = app, + .base_path = EXT_PATH("apps"), + }; + FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); + if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { + furi_string_set_str(temp_path, keybind); + } + furi_record_close(RECORD_STORAGE); + if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { + submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene + strncpy(keybind, furi_string_get_cstr(temp_path), MAX_KEYBIND_LENGTH); + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppSceneStart); + } + furi_string_free(temp_path); + break; + } + case DesktopSettingsAppKeybindActionTypeRemoveKeybind: + strncpy(keybind, "", MAX_KEYBIND_LENGTH); + DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, DesktopSettingsAppSceneStart); + break; + default: + break; + } +} + +void desktop_settings_scene_keybinds_action_type_on_enter(void* context) { + DesktopSettingsApp* app = context; + Submenu* submenu = app->submenu; + char* keybind = desktop_settings_app_get_keybind(app); + submenu_reset(submenu); + + submenu_add_item( + submenu, + "Main App", + DesktopSettingsAppKeybindActionTypeMainApp, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + + submenu_add_item( + submenu, + "External App", + DesktopSettingsAppKeybindActionTypeExternalApp, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + + submenu_add_item( + submenu, + "More Actions", + DesktopSettingsAppKeybindActionTypeMoreActions, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + + submenu_add_item( + submenu, + "Remove Keybind", + DesktopSettingsAppKeybindActionTypeRemoveKeybind, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + + DesktopSettingsAppKeybindActionType selected = scene_manager_get_scene_state( + app->scene_manager, DesktopSettingsAppSceneKeybindsActionType); + if(selected == DesktopSettingsAppKeybindActionTypeRemoveKeybind) { + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { + if(!strncmp(FLIPPER_APPS[i].name, keybind, MAX_KEYBIND_LENGTH)) { + selected = DesktopSettingsAppKeybindActionTypeMainApp; + } + } + + if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { + selected = DesktopSettingsAppKeybindActionTypeExternalApp; + } + furi_record_close(RECORD_STORAGE); + + for(size_t i = 0; i < EXTRA_KEYBINDS_COUNT; i++) { + if(!strncmp(EXTRA_KEYBINDS[i], keybind, MAX_KEYBIND_LENGTH)) { + selected = DesktopSettingsAppKeybindActionTypeMoreActions; + } + } + + if(!strnlen(keybind, MAX_KEYBIND_LENGTH)) { + selected = DesktopSettingsAppKeybindActionTypeRemoveKeybind; + } + } + + submenu_set_header(submenu, "Keybind action:"); + submenu_set_selected_item(submenu, selected); + + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); +} + +bool desktop_settings_scene_keybinds_action_type_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + } + + return consumed; +} + +void desktop_settings_scene_keybinds_action_type_on_exit(void* context) { + DesktopSettingsApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c deleted file mode 100644 index 363ad057d..000000000 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_choose.c +++ /dev/null @@ -1,171 +0,0 @@ -#include "../desktop_settings_app.h" -#include "applications.h" -#include "desktop_settings_scene.h" -#include -#include -#include - -static const char* EXTRA_OPTIONS[] = { - "Apps", - "Archive", - "Device Info", - "Lock Menu", - "Lock Keypad", - "Lock with PIN", - "Passport", -}; -#define EXTRA_OPTIONS_COUNT COUNT_OF(EXTRA_OPTIONS) - -static bool keybinds_fap_selector_item_callback( - FuriString* file_path, - void* context, - uint8_t** icon_ptr, - FuriString* item_name) { - UNUSED(context); - Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); - furi_record_close(RECORD_STORAGE); - return success; -} - -static void desktop_settings_scene_keybinds_choose_main_callback(void* context, uint32_t index) { - DesktopSettingsApp* app = context; - KeybindType type = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); - KeybindKey key = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); - char* keybind = app->desktop->keybinds[type][key].data; - - strncpy(keybind, FLIPPER_APPS[index].name, MAX_KEYBIND_LENGTH); - DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); - scene_manager_previous_scene(app->scene_manager); - scene_manager_previous_scene(app->scene_manager); - scene_manager_previous_scene(app->scene_manager); -} - -static void desktop_settings_scene_keybinds_choose_ext_callback(void* context, uint32_t index) { - UNUSED(index); - DesktopSettingsApp* app = context; - KeybindType type = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); - KeybindKey key = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); - char* keybind = app->desktop->keybinds[type][key].data; - - const DialogsFileBrowserOptions browser_options = { - .extension = ".fap", - .icon = &I_unknown_10px, - .skip_assets = true, - .hide_ext = true, - .item_loader_callback = keybinds_fap_selector_item_callback, - .item_loader_context = app, - .base_path = EXT_PATH("apps"), - }; - - // Select keybind fap in file browser - FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); - if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { - furi_string_set_str(temp_path, keybind); - } - furi_record_close(RECORD_STORAGE); - - if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { - submenu_reset(app->submenu); // Prevent menu from being shown when we exiting scene - strncpy(keybind, furi_string_get_cstr(temp_path), MAX_KEYBIND_LENGTH); - DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); - scene_manager_previous_scene(app->scene_manager); - scene_manager_previous_scene(app->scene_manager); - scene_manager_previous_scene(app->scene_manager); - } - furi_string_free(temp_path); -} - -static void desktop_settings_scene_keybinds_choose_extra_callback(void* context, uint32_t index) { - DesktopSettingsApp* app = context; - KeybindType type = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); - KeybindKey key = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); - char* keybind = app->desktop->keybinds[type][key].data; - - strncpy(keybind, EXTRA_OPTIONS[index - FLIPPER_APPS_COUNT], MAX_KEYBIND_LENGTH); - DESKTOP_KEYBINDS_SAVE(&app->desktop->keybinds, sizeof(app->desktop->keybinds)); - scene_manager_previous_scene(app->scene_manager); - scene_manager_previous_scene(app->scene_manager); - scene_manager_previous_scene(app->scene_manager); -} - -void desktop_settings_scene_keybinds_choose_on_enter(void* context) { - DesktopSettingsApp* app = context; - Submenu* submenu = app->submenu; - submenu_reset(submenu); - - KeybindType type = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsType); - KeybindKey key = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneKeybindsKey); - uint32_t pre_select_item = 0; - char* keybind = app->desktop->keybinds[type][key].data; - size_t submenu_i = -1; - - for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - submenu_add_item( - submenu, - FLIPPER_APPS[i].name, - ++submenu_i, - desktop_settings_scene_keybinds_choose_main_callback, - app); - - // Select keybind item in submenu - FURI_LOG_I("URMOM", "%s %s", keybind, FLIPPER_APPS[i].name); - if(!strncmp(FLIPPER_APPS[i].name, keybind, MAX_KEYBIND_LENGTH)) { - pre_select_item = submenu_i; - } - } - - submenu_add_item( - submenu, - "[External Application]", - ++submenu_i, - desktop_settings_scene_keybinds_choose_ext_callback, - app); - if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { - pre_select_item = submenu_i; - } - furi_record_close(RECORD_STORAGE); - - for(size_t i = 0; i < EXTRA_OPTIONS_COUNT; i++) { - submenu_add_item( - submenu, - EXTRA_OPTIONS[i], - ++submenu_i, - desktop_settings_scene_keybinds_choose_extra_callback, - app); - - // Select keybind item in submenu - if(!strncmp(EXTRA_OPTIONS[i], keybind, MAX_KEYBIND_LENGTH)) { - pre_select_item = submenu_i; - } - } - - submenu_set_header(submenu, "Keybind action:"); - submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. - - view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); -} - -bool desktop_settings_scene_keybinds_choose_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - } - - return consumed; -} - -void desktop_settings_scene_keybinds_choose_on_exit(void* context) { - DesktopSettingsApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c index 3c3b61b49..7ac1e72f5 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_keybinds_key.c @@ -46,7 +46,11 @@ bool desktop_settings_scene_keybinds_key_on_event(void* context, SceneManagerEve consumed = true; scene_manager_set_scene_state( app->scene_manager, DesktopSettingsAppSceneKeybindsKey, event.event); - scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsChoose); + scene_manager_set_scene_state( + app->scene_manager, + DesktopSettingsAppSceneKeybindsActionType, + DesktopSettingsAppKeybindActionTypeRemoveKeybind); + scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsActionType); } return consumed; } diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index 3db86f234..dfa5f8b91 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -21,6 +21,7 @@ void desktop_settings_scene_pin_disable_on_enter(void* context) { app->desktop->settings.pin_code.length = 0; memset( app->desktop->settings.pin_code.data, '0', sizeof(app->desktop->settings.pin_code.data)); + app->save_settings = true; popup_set_context(app->popup, app); popup_set_callback(app->popup, pin_disable_back_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c index 40f508a49..24ac68006 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_setup_done.c @@ -23,7 +23,6 @@ static void pin_setup_done_callback(const PinCode* pin_code, void* context) { void desktop_settings_scene_pin_setup_done_on_enter(void* context) { DesktopSettingsApp* app = context; - memcpy(&app->desktop->settings.pin_code, &app->pincode_buffer, sizeof(PinCode)); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); notification_message(notification, &sequence_single_vibro); furi_record_close(RECORD_NOTIFICATION); @@ -31,7 +30,7 @@ void desktop_settings_scene_pin_setup_done_on_enter(void* context) { desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL); desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback); - desktop_view_pin_input_set_pin(app->pin_input_view, &app->desktop->settings.pin_code); + desktop_view_pin_input_set_pin(app->pin_input_view, &app->pincode_buffer); desktop_view_pin_input_set_label_button(app->pin_input_view, "Done"); desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN Activated!"); desktop_view_pin_input_set_label_secondary( @@ -48,6 +47,8 @@ bool desktop_settings_scene_pin_setup_done_on_event(void* context, SceneManagerE if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { case SCENE_EVENT_DONE: { + memcpy(&app->desktop->settings.pin_code, &app->pincode_buffer, sizeof(PinCode)); + app->save_settings = true; bool scene_found = false; scene_found = scene_manager_search_and_switch_to_previous_scene( app->scene_manager, DesktopSettingsAppScenePinMenu); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index a22a35b8f..05a18b9ea 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -37,6 +37,7 @@ static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* i variable_item_set_current_value_text(item, auto_lock_delay_text[index]); app->desktop->settings.auto_lock_delay_ms = auto_lock_delay_value[index]; + app->save_settings = true; } static void desktop_settings_scene_start_auto_lock_pin_changed(VariableItem* item) { @@ -45,6 +46,7 @@ static void desktop_settings_scene_start_auto_lock_pin_changed(VariableItem* ite variable_item_set_current_value_text(item, value ? "ON" : "OFF"); app->desktop->settings.auto_lock_with_pin = value; + app->save_settings = true; } void desktop_settings_scene_start_on_enter(void* context) { From a7ec16f79b564973bf48bc4ee34485edeab55ff2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 29 Jun 2023 20:26:35 +0200 Subject: [PATCH 318/370] Add nfc rfid detector from merge Edited to support asset packs --- .../nfc_rfid_detector/application.fam | 13 ++ .../helpers/nfc_rfid_detector_event.h | 7 + .../helpers/nfc_rfid_detector_types.h | 15 ++ .../images/NFC_detect_45x30.png | Bin 0 -> 168 bytes .../images/Rfid_detect_45x30.png | Bin 0 -> 158 bytes .../nfc_rfid_detector_10px.png | Bin 0 -> 124 bytes .../nfc_rfid_detector/nfc_rfid_detector_app.c | 108 ++++++++++++ .../nfc_rfid_detector_app_i.c | 40 +++++ .../nfc_rfid_detector_app_i.h | 30 ++++ .../scenes/nfc_rfid_detector_scene.c | 31 ++++ .../scenes/nfc_rfid_detector_scene.h | 29 +++ .../scenes/nfc_rfid_detector_scene_about.c | 69 ++++++++ .../scenes/nfc_rfid_detector_scene_config.h | 3 + .../nfc_rfid_detector_scene_field_presence.c | 60 +++++++ .../scenes/nfc_rfid_detector_scene_start.c | 58 ++++++ .../nfc_rfid_detector_view_field_presence.c | 165 ++++++++++++++++++ .../nfc_rfid_detector_view_field_presence.h | 19 ++ 17 files changed, 647 insertions(+) create mode 100644 applications/external/nfc_rfid_detector/application.fam create mode 100644 applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h create mode 100644 applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h create mode 100644 applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png create mode 100644 applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png create mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png create mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c create mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c create mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c create mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c create mode 100644 applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c create mode 100644 applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h diff --git a/applications/external/nfc_rfid_detector/application.fam b/applications/external/nfc_rfid_detector/application.fam new file mode 100644 index 000000000..70c91bc84 --- /dev/null +++ b/applications/external/nfc_rfid_detector/application.fam @@ -0,0 +1,13 @@ +App( + appid="nfc_rfid_detector", + name="NFC/RFID detector", + apptype=FlipperAppType.EXTERNAL, + targets=["f7"], + entry_point="nfc_rfid_detector_app", + requires=["gui"], + stack_size=4 * 1024, + order=50, + fap_icon="nfc_rfid_detector_10px.png", + fap_category="Tools", + fap_icon_assets="images", +) diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h new file mode 100644 index 000000000..bbffe2938 --- /dev/null +++ b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + //NfcRfidDetectorCustomEvent + NfcRfidDetectorCustomEventStartId = 100, + +} NfcRfidDetectorCustomEvent; diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h new file mode 100644 index 000000000..5d44b09b7 --- /dev/null +++ b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#define NFC_RFID_DETECTOR_VERSION_APP "0.1" +#define NFC_RFID_DETECTOR_DEVELOPED "SkorP" +#define NFC_RFID_DETECTOR_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" + +typedef enum { + NfcRfidDetectorViewVariableItemList, + NfcRfidDetectorViewSubmenu, + NfcRfidDetectorViewFieldPresence, + NfcRfidDetectorViewWidget, +} NfcRfidDetectorView; diff --git a/applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png b/applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png new file mode 100644 index 0000000000000000000000000000000000000000..9d8a6f2abf9b0b6e041d134ce791ec8283f1474d GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^xnimJQg+KMf>kx^NwAax?J03VOnh9H^F_DX?m;7uKb-foq?HQV>E-q&-2^V QfL1Vgy85}Sb4q9e0PSTwjsO4v literal 0 HcmV?d00001 diff --git a/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png b/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png new file mode 100644 index 0000000000000000000000000000000000000000..35c205049bc5a6c7f0bb13a0cb3057963a023c6e GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^xG+l41V{G`^6RB9^&ce?Q{EhH1o(i zcG0sF=jLrIU`gfJ9e<(w?c#$+`{GybuV0XK^{NaL55t;h1_!r?84rQRGkCiCxvXf4IeWS|hDc0ZJK-P~g93;1_80%_ zf0mxq_>?eVL9oQDG>54hmUMGg2<9ZE<@p-^(r|y*a$w%vQ*}A2s_`AWqh21A3+I&= XdhU3CV!O&Kpm7YIu6{1-oD!M<1Jf%z literal 0 HcmV?d00001 diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c new file mode 100644 index 000000000..cba8b6085 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c @@ -0,0 +1,108 @@ +#include "nfc_rfid_detector_app_i.h" + +#include +#include + +static bool nfc_rfid_detector_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool nfc_rfid_detector_app_back_event_callback(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void nfc_rfid_detector_app_tick_event_callback(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +NfcRfidDetectorApp* nfc_rfid_detector_app_alloc() { + NfcRfidDetectorApp* app = malloc(sizeof(NfcRfidDetectorApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&nfc_rfid_detector_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, nfc_rfid_detector_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, nfc_rfid_detector_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + // SubMenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcRfidDetectorViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcRfidDetectorViewWidget, widget_get_view(app->widget)); + + // Field Presence + app->nfc_rfid_detector_field_presence = nfc_rfid_detector_view_field_presence_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + NfcRfidDetectorViewFieldPresence, + nfc_rfid_detector_view_field_presence_get_view(app->nfc_rfid_detector_field_presence)); + + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneStart); + + return app; +} + +void nfc_rfid_detector_app_free(NfcRfidDetectorApp* app) { + furi_assert(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); + submenu_free(app->submenu); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewWidget); + widget_free(app->widget); + + // Field Presence + view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); + nfc_rfid_detector_view_field_presence_free(app->nfc_rfid_detector_field_presence); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t nfc_rfid_detector_app(void* p) { + UNUSED(p); + NfcRfidDetectorApp* nfc_rfid_detector_app = nfc_rfid_detector_app_alloc(); + + view_dispatcher_run(nfc_rfid_detector_app->view_dispatcher); + + nfc_rfid_detector_app_free(nfc_rfid_detector_app); + + return 0; +} diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c new file mode 100644 index 000000000..c59d40d50 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c @@ -0,0 +1,40 @@ +#include "nfc_rfid_detector_app_i.h" + +#include + +#define TAG "NfcRfidDetector" + +void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app) { + furi_assert(app); + + // start the field presence rfid detection + furi_hal_rfid_field_detect_start(); + + // start the field presence nfc detection + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_field_detect_start(); +} + +void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app) { + furi_assert(app); + + // stop the field presence rfid detection + furi_hal_rfid_field_detect_stop(); + + // stop the field presence nfc detection + furi_hal_nfc_start_sleep(); +} + +bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app) { + furi_assert(app); + + // check if the field presence is nfc + return furi_hal_nfc_field_is_present(); +} + +bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency) { + furi_assert(app); + + // check if the field presence is rfid + return furi_hal_rfid_field_is_present(frequency); +} \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h new file mode 100644 index 000000000..72cb126d4 --- /dev/null +++ b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h @@ -0,0 +1,30 @@ +#pragma once + +#include "helpers/nfc_rfid_detector_types.h" +#include "helpers/nfc_rfid_detector_event.h" + +#include "scenes/nfc_rfid_detector_scene.h" +#include +#include +#include +#include +#include +#include +#include "views/nfc_rfid_detector_view_field_presence.h" + +typedef struct NfcRfidDetectorApp NfcRfidDetectorApp; + +struct NfcRfidDetectorApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + Submenu* submenu; + Widget* widget; + NfcRfidDetectorFieldPresence* nfc_rfid_detector_field_presence; +}; + +void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app); +void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app); +bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app); +bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency); \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c new file mode 100644 index 000000000..d75eb2884 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c @@ -0,0 +1,31 @@ +#include "../nfc_rfid_detector_app_i.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const nfc_rfid_detector_scene_on_enter_handlers[])(void*) = { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const nfc_rfid_detector_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = + { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const nfc_rfid_detector_scene_on_exit_handlers[])(void* context) = { +#include "nfc_rfid_detector_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers nfc_rfid_detector_scene_handlers = { + .on_enter_handlers = nfc_rfid_detector_scene_on_enter_handlers, + .on_event_handlers = nfc_rfid_detector_scene_on_event_handlers, + .on_exit_handlers = nfc_rfid_detector_scene_on_exit_handlers, + .scene_num = NfcRfidDetectorSceneNum, +}; diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h new file mode 100644 index 000000000..74d324b4d --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcRfidDetectorScene##id, +typedef enum { +#include "nfc_rfid_detector_scene_config.h" + NfcRfidDetectorSceneNum, +} NfcRfidDetectorScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_rfid_detector_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_rfid_detector_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c new file mode 100644 index 000000000..ddcb8aac0 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c @@ -0,0 +1,69 @@ +#include "../nfc_rfid_detector_app_i.h" + +void nfc_rfid_detector_scene_about_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + NfcRfidDetectorApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void nfc_rfid_detector_scene_about_on_enter(void* context) { + NfcRfidDetectorApp* app = context; + + FuriString* temp_str; + temp_str = furi_string_alloc(); + furi_string_printf(temp_str, "\e#%s\n", "Information"); + + furi_string_cat_printf(temp_str, "Version: %s\n", NFC_RFID_DETECTOR_VERSION_APP); + furi_string_cat_printf(temp_str, "Developed by: %s\n", NFC_RFID_DETECTOR_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", NFC_RFID_DETECTOR_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf( + temp_str, + "This application allows\nyou to determine what\ntype of electromagnetic\nfield the reader is using.\nFor LF RFID you can also\nsee the carrier frequency\n\n"); + + widget_add_text_box_element( + app->widget, + 0, + 0, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! \e!\n", + false); + widget_add_text_box_element( + app->widget, + 0, + 2, + 128, + 14, + AlignCenter, + AlignBottom, + "\e#\e! NFC/RFID detector \e!\n", + false); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewWidget); +} + +bool nfc_rfid_detector_scene_about_on_event(void* context, SceneManagerEvent event) { + NfcRfidDetectorApp* app = context; + bool consumed = false; + UNUSED(app); + UNUSED(event); + + return consumed; +} + +void nfc_rfid_detector_scene_about_on_exit(void* context) { + NfcRfidDetectorApp* app = context; + + // Clear views + widget_reset(app->widget); +} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h new file mode 100644 index 000000000..ab49ad5c2 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h @@ -0,0 +1,3 @@ +ADD_SCENE(nfc_rfid_detector, start, Start) +ADD_SCENE(nfc_rfid_detector, about, About) +ADD_SCENE(nfc_rfid_detector, field_presence, FieldPresence) diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c new file mode 100644 index 000000000..ec53b5a0a --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c @@ -0,0 +1,60 @@ +#include "../nfc_rfid_detector_app_i.h" +#include "../views/nfc_rfid_detector_view_field_presence.h" + +void nfc_rfid_detector_scene_field_presence_callback( + NfcRfidDetectorCustomEvent event, + void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +static const NotificationSequence notification_app_display_on = { + + &message_display_backlight_on, + NULL, +}; + +static void nfc_rfid_detector_scene_field_presence_update(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + + uint32_t frequency = 0; + bool nfc_field = nfc_rfid_detector_app_field_presence_is_nfc(app); + bool rfid_field = nfc_rfid_detector_app_field_presence_is_rfid(app, &frequency); + + if(nfc_field || rfid_field) + notification_message(app->notifications, ¬ification_app_display_on); + + nfc_rfid_detector_view_field_presence_update( + app->nfc_rfid_detector_field_presence, nfc_field, rfid_field, frequency); +} + +void nfc_rfid_detector_scene_field_presence_on_enter(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + + // Start detection of field presence + nfc_rfid_detector_app_field_presence_start(app); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); +} + +bool nfc_rfid_detector_scene_field_presence_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeTick) { + nfc_rfid_detector_scene_field_presence_update(app); + } + + return consumed; +} + +void nfc_rfid_detector_scene_field_presence_on_exit(void* context) { + furi_assert(context); + NfcRfidDetectorApp* app = context; + // Stop detection of field presence + nfc_rfid_detector_app_field_presence_stop(app); +} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c new file mode 100644 index 000000000..7b71bd973 --- /dev/null +++ b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c @@ -0,0 +1,58 @@ +#include "../nfc_rfid_detector_app_i.h" + +typedef enum { + SubmenuIndexNfcRfidDetectorFieldPresence, + SubmenuIndexNfcRfidDetectorAbout, +} SubmenuIndex; + +void nfc_rfid_detector_scene_start_submenu_callback(void* context, uint32_t index) { + NfcRfidDetectorApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_rfid_detector_scene_start_on_enter(void* context) { + UNUSED(context); + NfcRfidDetectorApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Detect field type", + SubmenuIndexNfcRfidDetectorFieldPresence, + nfc_rfid_detector_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "About", + SubmenuIndexNfcRfidDetectorAbout, + nfc_rfid_detector_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcRfidDetectorSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); +} + +bool nfc_rfid_detector_scene_start_on_event(void* context, SceneManagerEvent event) { + NfcRfidDetectorApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcRfidDetectorAbout) { + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexNfcRfidDetectorFieldPresence) { + scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneFieldPresence); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, NfcRfidDetectorSceneStart, event.event); + } + + return consumed; +} + +void nfc_rfid_detector_scene_start_on_exit(void* context) { + NfcRfidDetectorApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c new file mode 100644 index 000000000..faf253977 --- /dev/null +++ b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c @@ -0,0 +1,165 @@ +#include "nfc_rfid_detector_view_field_presence.h" +#include "../nfc_rfid_detector_app_i.h" +#include +#include + +#include +#include + +#define FIELD_FOUND_WEIGHT 5 + +typedef enum { + NfcRfidDetectorTypeFieldPresenceNfc, + NfcRfidDetectorTypeFieldPresenceRfid, +} NfcRfidDetectorTypeFieldPresence; + +static const Icon* NfcRfidDetectorFieldPresenceIcons[] = { + [NfcRfidDetectorTypeFieldPresenceNfc] = &I_NFC_detect_45x30, + [NfcRfidDetectorTypeFieldPresenceRfid] = &I_Rfid_detect_45x30, +}; + +struct NfcRfidDetectorFieldPresence { + View* view; +}; + +typedef struct { + uint8_t nfc_field; + uint8_t rfid_field; + uint32_t rfid_frequency; +} NfcRfidDetectorFieldPresenceModel; + +void nfc_rfid_detector_view_field_presence_update( + NfcRfidDetectorFieldPresence* instance, + bool nfc_field, + bool rfid_field, + uint32_t rfid_frequency) { + furi_assert(instance); + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + if(nfc_field) { + model->nfc_field = FIELD_FOUND_WEIGHT; + } else if(model->nfc_field) { + model->nfc_field--; + } + if(rfid_field) { + model->rfid_field = FIELD_FOUND_WEIGHT; + model->rfid_frequency = rfid_frequency; + } else if(model->rfid_field) { + model->rfid_field--; + } + }, + true); +} + +void nfc_rfid_detector_view_field_presence_draw( + Canvas* canvas, + NfcRfidDetectorFieldPresenceModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(!model->nfc_field && !model->rfid_field) { + canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34); + canvas_draw_icon(canvas, 22, 12, &I_Move_flipper_26x39); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 56, 36, "Touch the reader"); + } else { + if(model->nfc_field) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 21, 10, "NFC"); + canvas_draw_icon( + canvas, + 9, + 17, + NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceNfc]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 9, 62, "13,56 MHz"); + } + + if(model->rfid_field) { + char str[16]; + snprintf(str, sizeof(str), "%.02f KHz", (double)model->rfid_frequency / 1000); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 76, 10, "LF RFID"); + canvas_draw_icon( + canvas, + 71, + 17, + NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceRfid]); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 69, 62, str); + } + } +} + +bool nfc_rfid_detector_view_field_presence_input(InputEvent* event, void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + UNUSED(instance); + + if(event->key == InputKeyBack) { + return false; + } + + return true; +} + +void nfc_rfid_detector_view_field_presence_enter(void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + model->nfc_field = 0; + model->rfid_field = 0; + model->rfid_frequency = 0; + }, + true); +} + +void nfc_rfid_detector_view_field_presence_exit(void* context) { + furi_assert(context); + NfcRfidDetectorFieldPresence* instance = context; + UNUSED(instance); +} + +NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc() { + NfcRfidDetectorFieldPresence* instance = malloc(sizeof(NfcRfidDetectorFieldPresence)); + + // View allocation and configuration + instance->view = view_alloc(); + + view_allocate_model( + instance->view, ViewModelTypeLocking, sizeof(NfcRfidDetectorFieldPresenceModel)); + view_set_context(instance->view, instance); + view_set_draw_callback( + instance->view, (ViewDrawCallback)nfc_rfid_detector_view_field_presence_draw); + view_set_input_callback(instance->view, nfc_rfid_detector_view_field_presence_input); + view_set_enter_callback(instance->view, nfc_rfid_detector_view_field_presence_enter); + view_set_exit_callback(instance->view, nfc_rfid_detector_view_field_presence_exit); + + with_view_model( + instance->view, + NfcRfidDetectorFieldPresenceModel * model, + { + model->nfc_field = 0; + model->rfid_field = 0; + model->rfid_frequency = 0; + }, + true); + return instance; +} + +void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h new file mode 100644 index 000000000..0ddb4e2cd --- /dev/null +++ b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/nfc_rfid_detector_types.h" +#include "../helpers/nfc_rfid_detector_event.h" + +typedef struct NfcRfidDetectorFieldPresence NfcRfidDetectorFieldPresence; + +void nfc_rfid_detector_view_field_presence_update( + NfcRfidDetectorFieldPresence* instance, + bool nfc_field, + bool rfid_field, + uint32_t rfid_frequency); + +NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc(); + +void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance); + +View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance); From 5d40193308471914ab3bb9f63bd7969ef8ade3dd Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Thu, 29 Jun 2023 22:41:35 +0300 Subject: [PATCH 319/370] increased timeouts (#2816) Co-authored-by: doomwastaken --- .github/workflows/unit_tests.yml | 6 +++--- .github/workflows/updater_test.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 4cb112c77..9c6c6b2db 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -29,7 +29,7 @@ jobs: - name: 'Flash unit tests firmware' id: flashing if: success() - timeout-minutes: 5 + timeout-minutes: 10 run: | ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 @@ -45,7 +45,7 @@ jobs: - name: 'Copy assets and unit data, reboot and wait for flipper' id: copy if: steps.format_ext.outcome == 'success' - timeout-minutes: 3 + timeout-minutes: 5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/resources /ext @@ -56,7 +56,7 @@ jobs: - name: 'Run units and validate results' id: run_units if: steps.copy.outcome == 'success' - timeout-minutes: 5 + timeout-minutes: 7 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index 1d383d9eb..27a181c46 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -30,7 +30,7 @@ jobs: - name: 'Flashing target firmware' id: first_full_flash - timeout-minutes: 5 + timeout-minutes: 10 run: | source scripts/toolchain/fbtenv.sh ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 @@ -38,7 +38,7 @@ jobs: - name: 'Validating updater' id: second_full_flash - timeout-minutes: 5 + timeout-minutes: 10 if: success() run: | source scripts/toolchain/fbtenv.sh From 6d9de2549432a27367bb89d3842809b17a24d6bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Fri, 30 Jun 2023 18:52:43 +0900 Subject: [PATCH 320/370] Furi,FuriHal: various improvements (#2819) * Lib: adjust default contrast for ERC displays * Furi: various improvements in check module * Format Sources * FurHal: ble early hardfault detection --------- Co-authored-by: hedger --- applications/debug/crash_test/application.fam | 10 ++ applications/debug/crash_test/crash_test.c | 128 ++++++++++++++++++ firmware/targets/f7/furi_hal/furi_hal_bt.c | 58 +++++--- furi/core/check.c | 6 +- furi/core/check.h | 47 +++++-- lib/u8g2/u8g2_glue.c | 2 +- 6 files changed, 218 insertions(+), 33 deletions(-) create mode 100644 applications/debug/crash_test/application.fam create mode 100644 applications/debug/crash_test/crash_test.c diff --git a/applications/debug/crash_test/application.fam b/applications/debug/crash_test/application.fam new file mode 100644 index 000000000..55f62f86d --- /dev/null +++ b/applications/debug/crash_test/application.fam @@ -0,0 +1,10 @@ +App( + appid="crash_test", + name="Crash Test", + apptype=FlipperAppType.DEBUG, + entry_point="crash_test_app", + cdefines=["APP_CRASH_TEST"], + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/crash_test/crash_test.c b/applications/debug/crash_test/crash_test.c new file mode 100644 index 000000000..92f1668be --- /dev/null +++ b/applications/debug/crash_test/crash_test.c @@ -0,0 +1,128 @@ +#include +#include + +#include +#include +#include + +#define TAG "CrashTest" + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + Submenu* submenu; +} CrashTest; + +typedef enum { + CrashTestViewSubmenu, +} CrashTestView; + +typedef enum { + CrashTestSubmenuCheck, + CrashTestSubmenuCheckMessage, + CrashTestSubmenuAssert, + CrashTestSubmenuAssertMessage, + CrashTestSubmenuCrash, + CrashTestSubmenuHalt, +} CrashTestSubmenu; + +static void crash_test_submenu_callback(void* context, uint32_t index) { + CrashTest* instance = (CrashTest*)context; + UNUSED(instance); + + switch(index) { + case CrashTestSubmenuCheck: + furi_check(false); + break; + case CrashTestSubmenuCheckMessage: + furi_check(false, "Crash test: furi_check with message"); + break; + case CrashTestSubmenuAssert: + furi_assert(false); + break; + case CrashTestSubmenuAssertMessage: + furi_assert(false, "Crash test: furi_assert with message"); + break; + case CrashTestSubmenuCrash: + furi_crash("Crash test: furi_crash"); + break; + case CrashTestSubmenuHalt: + furi_halt("Crash test: furi_halt"); + break; + default: + furi_crash("Programming error"); + } +} + +static uint32_t crash_test_exit_callback(void* context) { + UNUSED(context); + return VIEW_NONE; +} + +CrashTest* crash_test_alloc() { + CrashTest* instance = malloc(sizeof(CrashTest)); + + View* view = NULL; + + instance->gui = furi_record_open(RECORD_GUI); + instance->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + + // Menu + instance->submenu = submenu_alloc(); + view = submenu_get_view(instance->submenu); + view_set_previous_callback(view, crash_test_exit_callback); + view_dispatcher_add_view(instance->view_dispatcher, CrashTestViewSubmenu, view); + submenu_add_item( + instance->submenu, "Check", CrashTestSubmenuCheck, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, + "Check with message", + CrashTestSubmenuCheckMessage, + crash_test_submenu_callback, + instance); + submenu_add_item( + instance->submenu, "Assert", CrashTestSubmenuAssert, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, + "Assert with message", + CrashTestSubmenuAssertMessage, + crash_test_submenu_callback, + instance); + submenu_add_item( + instance->submenu, "Crash", CrashTestSubmenuCrash, crash_test_submenu_callback, instance); + submenu_add_item( + instance->submenu, "Halt", CrashTestSubmenuHalt, crash_test_submenu_callback, instance); + + return instance; +} + +void crash_test_free(CrashTest* instance) { + view_dispatcher_remove_view(instance->view_dispatcher, CrashTestViewSubmenu); + submenu_free(instance->submenu); + + view_dispatcher_free(instance->view_dispatcher); + furi_record_close(RECORD_GUI); + + free(instance); +} + +int32_t crash_test_run(CrashTest* instance) { + view_dispatcher_switch_to_view(instance->view_dispatcher, CrashTestViewSubmenu); + view_dispatcher_run(instance->view_dispatcher); + return 0; +} + +int32_t crash_test_app(void* p) { + UNUSED(p); + + CrashTest* instance = crash_test_alloc(); + + int32_t ret = crash_test_run(instance); + + crash_test_free(instance); + + return ret; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 6ff9f0e3b..57aee0bf2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -21,8 +21,17 @@ #define FURI_HAL_BT_HARDFAULT_INFO_MAGIC 0x1170FD0F -FuriMutex* furi_hal_bt_core2_mtx = NULL; -static FuriHalBtStack furi_hal_bt_stack = FuriHalBtStackUnknown; +typedef struct { + FuriMutex* core2_mtx; + FuriTimer* hardfault_check_timer; + FuriHalBtStack stack; +} FuriHalBt; + +static FuriHalBt furi_hal_bt = { + .core2_mtx = NULL, + .hardfault_check_timer = NULL, + .stack = FuriHalBtStackUnknown, +}; typedef void (*FuriHalBtProfileStart)(void); typedef void (*FuriHalBtProfileStop)(void); @@ -79,6 +88,13 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = { }; FuriHalBtProfileConfig* current_profile = NULL; +static void furi_hal_bt_hardfault_check(void* context) { + UNUSED(context); + if(furi_hal_bt_get_hardfault_info()) { + furi_crash("ST(R) Copro(R) HardFault"); + } +} + void furi_hal_bt_init() { furi_hal_bus_enable(FuriHalBusHSEM); furi_hal_bus_enable(FuriHalBusIPCC); @@ -86,9 +102,15 @@ void furi_hal_bt_init() { furi_hal_bus_enable(FuriHalBusPKA); furi_hal_bus_enable(FuriHalBusCRC); - if(!furi_hal_bt_core2_mtx) { - furi_hal_bt_core2_mtx = furi_mutex_alloc(FuriMutexTypeNormal); - furi_assert(furi_hal_bt_core2_mtx); + if(!furi_hal_bt.core2_mtx) { + furi_hal_bt.core2_mtx = furi_mutex_alloc(FuriMutexTypeNormal); + furi_assert(furi_hal_bt.core2_mtx); + } + + if(!furi_hal_bt.hardfault_check_timer) { + furi_hal_bt.hardfault_check_timer = + furi_timer_alloc(furi_hal_bt_hardfault_check, FuriTimerTypePeriodic, NULL); + furi_timer_start(furi_hal_bt.hardfault_check_timer, 5000); } // Explicitly tell that we are in charge of CLK48 domain @@ -99,13 +121,13 @@ void furi_hal_bt_init() { } void furi_hal_bt_lock_core2() { - furi_assert(furi_hal_bt_core2_mtx); - furi_check(furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever) == FuriStatusOk); + furi_assert(furi_hal_bt.core2_mtx); + furi_check(furi_mutex_acquire(furi_hal_bt.core2_mtx, FuriWaitForever) == FuriStatusOk); } void furi_hal_bt_unlock_core2() { - furi_assert(furi_hal_bt_core2_mtx); - furi_check(furi_mutex_release(furi_hal_bt_core2_mtx) == FuriStatusOk); + furi_assert(furi_hal_bt.core2_mtx); + furi_check(furi_mutex_release(furi_hal_bt.core2_mtx) == FuriStatusOk); } static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { @@ -113,26 +135,26 @@ static bool furi_hal_bt_radio_stack_is_supported(const BleGlueC2Info* info) { if(info->StackType == INFO_STACK_TYPE_BLE_LIGHT) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { - furi_hal_bt_stack = FuriHalBtStackLight; + furi_hal_bt.stack = FuriHalBtStackLight; supported = true; } } else if(info->StackType == INFO_STACK_TYPE_BLE_FULL) { if(info->VersionMajor >= FURI_HAL_BT_STACK_VERSION_MAJOR && info->VersionMinor >= FURI_HAL_BT_STACK_VERSION_MINOR) { - furi_hal_bt_stack = FuriHalBtStackFull; + furi_hal_bt.stack = FuriHalBtStackFull; supported = true; } } else { - furi_hal_bt_stack = FuriHalBtStackUnknown; + furi_hal_bt.stack = FuriHalBtStackUnknown; } return supported; } bool furi_hal_bt_start_radio_stack() { bool res = false; - furi_assert(furi_hal_bt_core2_mtx); + furi_assert(furi_hal_bt.core2_mtx); - furi_mutex_acquire(furi_hal_bt_core2_mtx, FuriWaitForever); + furi_mutex_acquire(furi_hal_bt.core2_mtx, FuriWaitForever); // Explicitly tell that we are in charge of CLK48 domain furi_check(LL_HSEM_1StepLock(HSEM, CFG_HW_CLK48_CONFIG_SEMID) == 0); @@ -166,17 +188,17 @@ bool furi_hal_bt_start_radio_stack() { } res = true; } while(false); - furi_mutex_release(furi_hal_bt_core2_mtx); + furi_mutex_release(furi_hal_bt.core2_mtx); return res; } FuriHalBtStack furi_hal_bt_get_radio_stack() { - return furi_hal_bt_stack; + return furi_hal_bt.stack; } bool furi_hal_bt_is_ble_gatt_gap_supported() { - if(furi_hal_bt_stack == FuriHalBtStackLight || furi_hal_bt_stack == FuriHalBtStackFull) { + if(furi_hal_bt.stack == FuriHalBtStackLight || furi_hal_bt.stack == FuriHalBtStackFull) { return true; } else { return false; @@ -184,7 +206,7 @@ bool furi_hal_bt_is_ble_gatt_gap_supported() { } bool furi_hal_bt_is_testing_supported() { - if(furi_hal_bt_stack == FuriHalBtStackFull) { + if(furi_hal_bt.stack == FuriHalBtStackFull) { return true; } else { return false; diff --git a/furi/core/check.c b/furi/core/check.c index 478f3aacc..c5c4ef1a4 100644 --- a/furi/core/check.c +++ b/furi/core/check.c @@ -166,7 +166,11 @@ FURI_NORETURN void __furi_crash() { RESTORE_REGISTERS_AND_HALT_MCU(true); #ifndef FURI_DEBUG } else { - furi_hal_rtc_set_fault_data((uint32_t)__furi_check_message); + uint32_t ptr = (uint32_t)__furi_check_message; + if(ptr < FLASH_BASE || ptr > (FLASH_BASE + FLASH_SIZE)) { + ptr = (uint32_t) "Check serial logs"; + } + furi_hal_rtc_set_fault_data(ptr); furi_hal_console_puts("\r\nRebooting system.\r\n"); furi_hal_console_puts("\033[0m\r\n"); furi_hal_power_reset(); diff --git a/furi/core/check.h b/furi/core/check.h index ea83f2219..004422e80 100644 --- a/furi/core/check.h +++ b/furi/core/check.h @@ -13,6 +13,8 @@ */ #pragma once +#include + #ifdef __cplusplus extern "C" { #define FURI_NORETURN [[noreturn]] @@ -48,28 +50,47 @@ FURI_NORETURN void __furi_halt(); } while(0) /** Check condition and crash if check failed */ -#define furi_check(__e) \ - do { \ - if(!(__e)) { \ - furi_crash(__FURI_CHECK_MESSAGE_FLAG); \ - } \ +#define __furi_check(__e, __m) \ + do { \ + if(!(__e)) { \ + furi_crash(__m); \ + } \ } while(0) +/** Check condition and crash if failed + * + * @param condition to check + * @param optional message + */ +#define furi_check(...) \ + M_APPLY(__furi_check, M_DEFAULT_ARGS(2, (__FURI_CHECK_MESSAGE_FLAG), __VA_ARGS__)) + /** Only in debug build: Assert condition and crash if assert failed */ #ifdef FURI_DEBUG -#define furi_assert(__e) \ - do { \ - if(!(__e)) { \ - furi_crash(__FURI_ASSERT_MESSAGE_FLAG); \ - } \ +#define __furi_assert(__e, __m) \ + do { \ + if(!(__e)) { \ + furi_crash(__m); \ + } \ } while(0) #else -#define furi_assert(__e) \ - do { \ - ((void)(__e)); \ +#define __furi_assert(__e, __m) \ + do { \ + ((void)(__e)); \ + ((void)(__m)); \ } while(0) #endif +/** Assert condition and crash if failed + * + * @warning only will do check if firmware compiled in debug mode + * + * @param condition to check + * @param optional message + */ +#define furi_assert(...) \ + M_APPLY(__furi_assert, M_DEFAULT_ARGS(2, (__FURI_ASSERT_MESSAGE_FLAG), __VA_ARGS__)) + #ifdef __cplusplus } #endif diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index 0d4879bce..0142e3e2f 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -2,7 +2,7 @@ #include -#define CONTRAST_ERC 32 +#define CONTRAST_ERC 31 #define CONTRAST_MGG 31 uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { From 8c93695d01d3af4acbd3cb0d5bc756c30c0564dc Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Fri, 30 Jun 2023 18:03:36 +0400 Subject: [PATCH 321/370] [FL-3375] SubGhz: add CC1101 module external (#2747) * SubGhz: add CC1101 Ext driver * SubGhz: move TIM2 -> TIM17 use cc1101_ext * FuriHal: SPI move channel DMA 3,4 -> 6.7 * Documentation: fix font * SubGhz: add work with SubGhz devices by link to device * SubGhz: add support switching external/internal cc1101 "subghz chat" * SubGhz: add support switching external/internal cc1101 "subghz tx" and "subghz rx" * SubGhz: add "Radio Settings" scene * SubGhz: add icon * SubGhz: add supported CC1101 external module in SubGhz app * SubGhz: fix check frequency supported radio device * SubGhz: fix clang-formatted * Sughz: move dirver CC1101_Ext to lib , compile cmd ./fbt launch_app APPSRC=radio_device_cc1101_ext * SubGhz: fix CLI * SubGhz: fix PVS * SubGhz: delete comments * SubGhz: fix unit_test * Format sources * Update api symbols and drivers targets * Drivers: find proper place for target option * SubGhz: external device connected method naming * Format sources * SubGhz: fix module selection menu, when external is not connected * SubGhz: fix furi_assert(device); * SubGhz: fix split h and c * SubGhz: furi_hal_subghz remove preset load function by name * SubGhz: deleted comments * Format Sources * SubGhz: add some consts and fix unit tests * Sync API Symbols Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 11 +- applications/drivers/application.fam | 6 + applications/drivers/subghz/application.fam | 8 + .../drivers/subghz/cc1101_ext/cc1101_ext.c | 765 ++++++++++++++++++ .../drivers/subghz/cc1101_ext/cc1101_ext.h | 206 +++++ .../cc1101_ext/cc1101_ext_interconnect.c | 110 +++ .../cc1101_ext/cc1101_ext_interconnect.h | 8 + .../main/subghz/helpers/subghz_chat.c | 7 +- .../main/subghz/helpers/subghz_chat.h | 6 +- .../subghz/helpers/subghz_threshold_rssi.c | 3 +- .../subghz/helpers/subghz_threshold_rssi.h | 3 +- .../main/subghz/helpers/subghz_txrx.c | 166 +++- .../main/subghz/helpers/subghz_txrx.h | 46 ++ .../main/subghz/helpers/subghz_txrx_i.h | 2 + .../main/subghz/helpers/subghz_types.h | 7 + .../main/subghz/scenes/subghz_scene_config.h | 1 + .../scenes/subghz_scene_radio_setting.c | 70 ++ .../subghz/scenes/subghz_scene_read_raw.c | 11 +- .../subghz/scenes/subghz_scene_receiver.c | 6 +- .../subghz/scenes/subghz_scene_save_name.c | 3 +- .../main/subghz/scenes/subghz_scene_start.c | 14 +- .../subghz/scenes/subghz_scene_transmitter.c | 2 + applications/main/subghz/subghz_cli.c | 215 +++-- applications/main/subghz/subghz_i.c | 5 +- applications/main/subghz/views/receiver.c | 44 +- applications/main/subghz/views/receiver.h | 4 + .../subghz/views/subghz_frequency_analyzer.c | 3 +- .../main/subghz/views/subghz_read_raw.c | 20 +- .../main/subghz/views/subghz_read_raw.h | 5 + .../main/subghz/views/subghz_test_carrier.c | 3 +- .../main/subghz/views/subghz_test_packet.c | 3 +- .../main/subghz/views/subghz_test_static.c | 3 +- applications/main/subghz/views/transmitter.c | 23 +- applications/main/subghz/views/transmitter.h | 5 + .../icons/SubGhz/External_antenna_20x12.png | Bin 0 -> 990 bytes .../icons/SubGhz/Internal_antenna_20x12.png | Bin 0 -> 994 bytes assets/icons/SubGhz/Scanning_123x52.png | Bin 1690 -> 0 bytes assets/icons/SubGhz/Scanning_short_96x52.png | Bin 0 -> 1388 bytes documentation/FuriHalBus.md | 16 +- firmware/targets/f18/api_symbols.csv | 3 +- firmware/targets/f7/api_symbols.csv | 73 +- firmware/targets/f7/furi_hal/furi_hal_spi.c | 28 +- .../targets/f7/furi_hal/furi_hal_subghz.c | 65 +- .../targets/f7/furi_hal/furi_hal_subghz.h | 51 +- .../f7/furi_hal/furi_hal_subghz_configs.h | 314 ------- .../f7/platform_specific/intrinsic_export.h | 2 + lib/subghz/SConscript | 1 + lib/subghz/devices/cc1101_configs.c | 431 ++++++++++ lib/subghz/devices/cc1101_configs.h | 18 + .../cc1101_int/cc1101_int_interconnect.c | 96 +++ .../cc1101_int/cc1101_int_interconnect.h | 8 + lib/subghz/devices/device_registry.h | 13 + lib/subghz/devices/devices.c | 236 ++++++ lib/subghz/devices/devices.h | 52 ++ lib/subghz/devices/preset.h | 13 + lib/subghz/devices/registry.c | 76 ++ lib/subghz/devices/registry.h | 40 + lib/subghz/devices/types.h | 91 +++ lib/subghz/protocols/raw.c | 25 +- lib/subghz/protocols/raw.h | 6 +- lib/subghz/subghz_file_encoder_worker.c | 13 +- lib/subghz/subghz_file_encoder_worker.h | 7 +- lib/subghz/subghz_setting.c | 32 +- lib/subghz/subghz_tx_rx_worker.c | 65 +- lib/subghz/subghz_tx_rx_worker.h | 7 +- lib/subghz/types.h | 6 + site_scons/commandline.scons | 1 + 67 files changed, 2932 insertions(+), 651 deletions(-) create mode 100644 applications/drivers/application.fam create mode 100644 applications/drivers/subghz/application.fam create mode 100644 applications/drivers/subghz/cc1101_ext/cc1101_ext.c create mode 100644 applications/drivers/subghz/cc1101_ext/cc1101_ext.h create mode 100644 applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c create mode 100644 applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h create mode 100644 applications/main/subghz/scenes/subghz_scene_radio_setting.c create mode 100644 assets/icons/SubGhz/External_antenna_20x12.png create mode 100644 assets/icons/SubGhz/Internal_antenna_20x12.png delete mode 100644 assets/icons/SubGhz/Scanning_123x52.png create mode 100644 assets/icons/SubGhz/Scanning_short_96x52.png delete mode 100644 firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h create mode 100644 lib/subghz/devices/cc1101_configs.c create mode 100644 lib/subghz/devices/cc1101_configs.h create mode 100644 lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c create mode 100644 lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h create mode 100644 lib/subghz/devices/device_registry.h create mode 100644 lib/subghz/devices/devices.c create mode 100644 lib/subghz/devices/devices.h create mode 100644 lib/subghz/devices/preset.h create mode 100644 lib/subghz/devices/registry.c create mode 100644 lib/subghz/devices/registry.h create mode 100644 lib/subghz/devices/types.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index f1ab92653..6bdaa641e 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #define TAG "SubGhz TEST" #define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") @@ -49,12 +51,15 @@ static void subghz_test_init(void) { subghz_environment_set_protocol_registry( environment_handler, (void*)&subghz_protocol_registry); + subghz_devices_init(); + receiver_handler = subghz_receiver_alloc_init(environment_handler); subghz_receiver_set_filter(receiver_handler, SubGhzProtocolFlag_Decodable); subghz_receiver_set_rx_callback(receiver_handler, subghz_test_rx_callback, NULL); } static void subghz_test_deinit(void) { + subghz_devices_deinit(); subghz_receiver_free(receiver_handler); subghz_environment_free(environment_handler); } @@ -68,7 +73,7 @@ static bool subghz_decoder_test(const char* path, const char* name_decoder) { if(decoder) { file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { + if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path, NULL)) { // the worker needs a file in order to open and read part of the file furi_delay_ms(100); @@ -108,7 +113,7 @@ static bool subghz_decode_random_test(const char* path) { uint32_t test_start = furi_get_tick(); file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { + if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path, NULL)) { // the worker needs a file in order to open and read part of the file furi_delay_ms(100); @@ -318,7 +323,7 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) { SubGhzHalAsyncTxTest test = {0}; test.type = type; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); furi_hal_subghz_set_frequency_and_path(433920000); if(!furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test)) { diff --git a/applications/drivers/application.fam b/applications/drivers/application.fam new file mode 100644 index 000000000..dc70e630c --- /dev/null +++ b/applications/drivers/application.fam @@ -0,0 +1,6 @@ +# Placeholder +App( + appid="drivers", + name="Drivers device", + apptype=FlipperAppType.METAPACKAGE, +) diff --git a/applications/drivers/subghz/application.fam b/applications/drivers/subghz/application.fam new file mode 100644 index 000000000..aaf0e1bd9 --- /dev/null +++ b/applications/drivers/subghz/application.fam @@ -0,0 +1,8 @@ +App( + appid="radio_device_cc1101_ext", + apptype=FlipperAppType.PLUGIN, + targets=["f7"], + entry_point="subghz_device_cc1101_ext_ep", + requires=["subghz"], + fap_libs=["hwdrivers"], +) diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c new file mode 100644 index 000000000..896b9bd2f --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -0,0 +1,765 @@ +#include "cc1101_ext.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define TAG "SubGhz_Device_CC1101_Ext" + +#define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2 + +/* DMA Channels definition */ +#define SUBGHZ_DEVICE_CC1101_EXT_DMA DMA2 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL LL_DMA_CHANNEL_3 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL LL_DMA_CHANNEL_4 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL LL_DMA_CHANNEL_5 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ FuriHalInterruptIdDma2Ch3 +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_CHANNEL +#define SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF \ + SUBGHZ_DEVICE_CC1101_EXT_DMA, SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_CHANNEL + +/** Low level buffer dimensions and guard times */ +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL (256) +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF \ + (SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL / 2) +#define SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME 999 + +/** SubGhz state */ +typedef enum { + SubGhzDeviceCC1101ExtStateInit, /**< Init pending */ + SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */ + SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */ + SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */ + SubGhzDeviceCC1101ExtStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ +} SubGhzDeviceCC1101ExtState; + +/** SubGhz regulation, receive transmission on the current frequency for the + * region */ +typedef enum { + SubGhzDeviceCC1101ExtRegulationOnlyRx, /**only Rx*/ + SubGhzDeviceCC1101ExtRegulationTxRx, /**TxRx*/ +} SubGhzDeviceCC1101ExtRegulation; + +typedef struct { + uint32_t* buffer; + LevelDuration carry_ld; + SubGhzDeviceCC1101ExtCallback callback; + void* callback_context; + uint32_t gpio_tx_buff[2]; + uint32_t debug_gpio_buff[2]; +} SubGhzDeviceCC1101ExtAsyncTx; + +typedef struct { + uint32_t capture_delta_duration; + SubGhzDeviceCC1101ExtCaptureCallback capture_callback; + void* capture_callback_context; +} SubGhzDeviceCC1101ExtAsyncRx; + +typedef struct { + volatile SubGhzDeviceCC1101ExtState state; + volatile SubGhzDeviceCC1101ExtRegulation regulation; + const GpioPin* async_mirror_pin; + FuriHalSpiBusHandle* spi_bus_handle; + const GpioPin* g0_pin; + SubGhzDeviceCC1101ExtAsyncTx async_tx; + SubGhzDeviceCC1101ExtAsyncRx async_rx; +} SubGhzDeviceCC1101Ext; + +static SubGhzDeviceCC1101Ext* subghz_device_cc1101_ext = NULL; + +static bool subghz_device_cc1101_ext_check_init() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateInit); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; + + bool ret = false; + + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(100 * 1000); + do { + // Reset + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + + // Prepare GD0 for power on self test + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + + // GD0 low + cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + while(furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin) != false) { + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + } + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + + // GD0 high + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, + CC1101_IOCFG0, + CC1101IocfgHW | CC1101_IOCFG_INV); + while(furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin) != true) { + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + } + if(furi_hal_cortex_timer_is_expired(timer)) { + //timeout + break; + } + + // Reset GD0 to floating state + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + // RF switches + furi_hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW); + + // Go to sleep + cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + ret = true; + } while(false); + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + if(ret) { + FURI_LOG_I(TAG, "Init OK"); + } else { + FURI_LOG_E(TAG, "Init failed"); + } + return ret; +} + +bool subghz_device_cc1101_ext_alloc() { + furi_assert(subghz_device_cc1101_ext == NULL); + subghz_device_cc1101_ext = malloc(sizeof(SubGhzDeviceCC1101Ext)); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateInit; + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationTxRx; + subghz_device_cc1101_ext->async_mirror_pin = NULL; + subghz_device_cc1101_ext->spi_bus_handle = &furi_hal_spi_bus_handle_external; + subghz_device_cc1101_ext->g0_pin = SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO; + + subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0; + + furi_hal_spi_bus_handle_init(subghz_device_cc1101_ext->spi_bus_handle); + return subghz_device_cc1101_ext_check_init(); +} + +void subghz_device_cc1101_ext_free() { + furi_assert(subghz_device_cc1101_ext != NULL); + furi_hal_spi_bus_handle_deinit(subghz_device_cc1101_ext->spi_bus_handle); + free(subghz_device_cc1101_ext); + subghz_device_cc1101_ext = NULL; +} + +void subghz_device_cc1101_ext_set_async_mirror_pin(const GpioPin* pin) { + subghz_device_cc1101_ext->async_mirror_pin = pin; +} + +const GpioPin* subghz_device_cc1101_ext_get_data_gpio() { + return subghz_device_cc1101_ext->g0_pin; +} + +bool subghz_device_cc1101_ext_is_connect() { + bool ret = false; + + if(subghz_device_cc1101_ext == NULL) { // not initialized + ret = subghz_device_cc1101_ext_alloc(); + subghz_device_cc1101_ext_free(); + } else { // initialized + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t partnumber = cc1101_get_partnumber(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + ret = (partnumber != 0) && (partnumber != 0xFF); + } + + return ret; +} + +void subghz_device_cc1101_ext_sleep() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_dump_state() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + printf( + "[subghz_device_cc1101_ext] cc1101 chip %d, version %d\r\n", + cc1101_get_partnumber(subghz_device_cc1101_ext->spi_bus_handle), + cc1101_get_version(subghz_device_cc1101_ext->spi_bus_handle)); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data) { + //load config + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t i = 0; + uint8_t pa[8] = {0}; + while(preset_data[i]) { + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, preset_data[i], preset_data[i + 1]); + i += 2; + } + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + //load pa table + memcpy(&pa[0], &preset_data[i + 2], 8); + subghz_device_cc1101_ext_load_patable(pa); + + //show debug + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + i = 0; + FURI_LOG_D(TAG, "Loading custom preset"); + while(preset_data[i]) { + FURI_LOG_D(TAG, "Reg[%lu]: %02X=%02X", i, preset_data[i], preset_data[i + 1]); + i += 2; + } + for(uint8_t y = i; y < i + 10; y++) { + FURI_LOG_D(TAG, "PA[%u]: %02X", y, preset_data[y]); + } + } +} + +void subghz_device_cc1101_ext_load_registers(const uint8_t* data) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t i = 0; + while(data[i]) { + cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, data[i], data[i + 1]); + i += 2; + } + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_load_patable(const uint8_t data[8]) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_set_pa_table(subghz_device_cc1101_ext->spi_bus_handle, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_write_packet(const uint8_t* data, uint8_t size) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_tx(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_write_reg(subghz_device_cc1101_ext->spi_bus_handle, CC1101_FIFO, size); + cc1101_write_fifo(subghz_device_cc1101_ext->spi_bus_handle, data, size); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_flush_rx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_rx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_flush_tx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_flush_tx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +bool subghz_device_cc1101_ext_rx_pipe_not_empty() { + CC1101RxBytes status[1]; + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, + (CC1101_STATUS_RXBYTES) | CC1101_BURST, + (uint8_t*)status); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + // TODO: you can add a buffer overflow flag if needed + if(status->NUM_RXBYTES > 0) { + return true; + } else { + return false; + } +} + +bool subghz_device_cc1101_ext_is_rx_data_crc_valid() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t data[1]; + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + if(((data[0] >> 7) & 0x01)) { + return true; + } else { + return false; + } +} + +void subghz_device_cc1101_ext_read_packet(uint8_t* data, uint8_t* size) { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_read_fifo(subghz_device_cc1101_ext->spi_bus_handle, data, size); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_shutdown() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + // Reset and shutdown + cc1101_shutdown(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_reset() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_reset(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_write_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_idle() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +void subghz_device_cc1101_ext_rx() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); +} + +bool subghz_device_cc1101_ext_tx() { + if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return true; +} + +float subghz_device_cc1101_ext_get_rssi() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + int32_t rssi_dec = cc1101_get_rssi(subghz_device_cc1101_ext->spi_bus_handle); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + + float rssi = rssi_dec; + if(rssi_dec >= 128) { + rssi = ((rssi - 256.0f) / 2.0f) - 74.0f; + } else { + rssi = (rssi / 2.0f) - 74.0f; + } + + return rssi; +} + +uint8_t subghz_device_cc1101_ext_get_lqi() { + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint8_t data[1]; + cc1101_read_reg( + subghz_device_cc1101_ext->spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return data[0] & 0x7F; +} + +bool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value) { + if(!(value >= 299999755 && value <= 348000335) && + !(value >= 386999938 && value <= 464000000) && + !(value >= 778999847 && value <= 928000000)) { + return false; + } + + return true; +} + +uint32_t subghz_device_cc1101_ext_set_frequency(uint32_t value) { + if(furi_hal_region_is_frequency_allowed(value)) { + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationTxRx; + } else { + subghz_device_cc1101_ext->regulation = SubGhzDeviceCC1101ExtRegulationOnlyRx; + } + + furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); + uint32_t real_frequency = + cc1101_set_frequency(subghz_device_cc1101_ext->spi_bus_handle, value); + cc1101_calibrate(subghz_device_cc1101_ext->spi_bus_handle); + + while(true) { + CC1101Status status = cc1101_get_status(subghz_device_cc1101_ext->spi_bus_handle); + if(status.STATE == CC1101StateIDLE) break; + } + + furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle); + return real_frequency; +} + +static bool subghz_device_cc1101_ext_start_debug() { + bool ret = false; + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) { + furi_hal_gpio_init( + subghz_device_cc1101_ext->async_mirror_pin, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh); + ret = true; + } + return ret; +} + +static bool subghz_device_cc1101_ext_stop_debug() { + bool ret = false; + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) { + furi_hal_gpio_init( + subghz_device_cc1101_ext->async_mirror_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + ret = true; + } + return ret; +} + +static void subghz_device_cc1101_ext_capture_ISR() { + if(!furi_hal_gpio_read(subghz_device_cc1101_ext->g0_pin)) { + if(subghz_device_cc1101_ext->async_rx.capture_callback) { + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); + + subghz_device_cc1101_ext->async_rx.capture_callback( + true, + LL_TIM_GetCounter(TIM17), + (void*)subghz_device_cc1101_ext->async_rx.capture_callback_context); + } + } else { + if(subghz_device_cc1101_ext->async_rx.capture_callback) { + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, true); + + subghz_device_cc1101_ext->async_rx.capture_callback( + false, + LL_TIM_GetCounter(TIM17), + (void*)subghz_device_cc1101_ext->async_rx.capture_callback_context); + } + } + LL_TIM_SetCounter(TIM17, 6); +} + +void subghz_device_cc1101_ext_start_async_rx( + SubGhzDeviceCC1101ExtCaptureCallback callback, + void* context) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncRx; + + subghz_device_cc1101_ext->async_rx.capture_callback = callback; + subghz_device_cc1101_ext->async_rx.capture_callback_context = context; + + furi_hal_bus_enable(FuriHalBusTIM17); + + // Configure TIM + LL_TIM_SetPrescaler(TIM17, 64 - 1); + LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(TIM17, 0xFFFF); + LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + + // Timer: advanced + LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM17); + LL_TIM_DisableDMAReq_TRIG(TIM17); + LL_TIM_DisableIT_TRIG(TIM17); + + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_gpio_remove_int_callback(subghz_device_cc1101_ext->g0_pin); + furi_hal_gpio_add_int_callback( + subghz_device_cc1101_ext->g0_pin, + subghz_device_cc1101_ext_capture_ISR, + subghz_device_cc1101_ext->async_rx.capture_callback); + + // Start timer + LL_TIM_SetCounter(TIM17, 0); + LL_TIM_EnableCounter(TIM17); + + // Start debug + subghz_device_cc1101_ext_start_debug(); + + // Switch to RX + subghz_device_cc1101_ext_rx(); + + //Clear the variable after the end of the session + subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0; +} + +void subghz_device_cc1101_ext_stop_async_rx() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncRx); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; + + // Shutdown radio + subghz_device_cc1101_ext_idle(); + + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(FuriHalBusTIM17); + + // Stop debug + subghz_device_cc1101_ext_stop_debug(); + + FURI_CRITICAL_EXIT(); + furi_hal_gpio_remove_int_callback(subghz_device_cc1101_ext->g0_pin); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t samples) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); + while(samples > 0) { + bool is_odd = samples % 2; + LevelDuration ld; + if(level_duration_is_reset(subghz_device_cc1101_ext->async_tx.carry_ld)) { + ld = subghz_device_cc1101_ext->async_tx.callback( + subghz_device_cc1101_ext->async_tx.callback_context); + } else { + ld = subghz_device_cc1101_ext->async_tx.carry_ld; + subghz_device_cc1101_ext->async_tx.carry_ld = level_duration_reset(); + } + + if(level_duration_is_wait(ld)) { + *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; + } else if(level_duration_is_reset(ld)) { + *buffer = 0; + buffer++; + samples--; + LL_DMA_DisableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_DisableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_TIM_EnableIT_UPDATE(TIM17); + break; + } else { + bool level = level_duration_get_level(ld); + + // Inject guard time if level is incorrect + if(is_odd != level) { + *buffer = SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_GUARD_TIME; + buffer++; + samples--; + + // Special case: prevent buffer overflow if sample is last + if(samples == 0) { + subghz_device_cc1101_ext->async_tx.carry_ld = ld; + break; + } + } + + uint32_t duration = level_duration_get_duration(ld); + furi_assert(duration > 0); + *buffer = duration - 1; + buffer++; + samples--; + } + } +} + +static void subghz_device_cc1101_ext_async_tx_dma_isr() { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx); + +#if SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_CHANNEL == LL_DMA_CHANNEL_3 + if(LL_DMA_IsActiveFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_HT3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer, + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF); + } + if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) { + LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA); + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer + + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF, + SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_HALF); + } +#else +#error Update this code. Would you kindly? +#endif +} + +static void subghz_device_cc1101_ext_async_tx_timer_isr() { + if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { + if(LL_TIM_GetAutoReload(TIM17) == 0) { + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + if(subghz_device_cc1101_ext->async_mirror_pin != NULL) + furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false); + LL_TIM_DisableCounter(TIM17); + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd; + } + LL_TIM_ClearFlag_UPDATE(TIM17); + } +} + +bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) { + furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle); + furi_assert(callback); + + //If transmission is prohibited by regional settings + if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false; + + subghz_device_cc1101_ext->async_tx.callback = callback; + subghz_device_cc1101_ext->async_tx.callback_context = context; + + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTx; + + subghz_device_cc1101_ext->async_tx.buffer = + malloc(SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); + + //Signal generation with mem-to-mem DMA + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init( + subghz_device_cc1101_ext->g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + + // Configure DMA update timer + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, (uint32_t)subghz_device_cc1101_ext->async_tx.buffer); + LL_DMA_SetPeriphAddress(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, (uint32_t) & (TIM17->ARR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_MODE_NORMAL); + LL_DMA_SetDataLength( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP); + + LL_DMA_EnableIT_TC(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_EnableIT_HT(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + + furi_hal_interrupt_set_isr( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, subghz_device_cc1101_ext_async_tx_dma_isr, NULL); + + furi_hal_bus_enable(FuriHalBusTIM17); + + // Configure TIM + LL_TIM_SetPrescaler(TIM17, 64 - 1); + LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetAutoReload(TIM17, 0xFFFF); + LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM17); + + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL); + + subghz_device_cc1101_ext_async_tx_refill( + subghz_device_cc1101_ext->async_tx.buffer, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL); + + // Configure tx gpio dma + const GpioPin* gpio = subghz_device_cc1101_ext->g0_pin; + + subghz_device_cc1101_ext->async_tx.gpio_tx_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + subghz_device_cc1101_ext->async_tx.gpio_tx_buff[1] = gpio->pin; + + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, + (uint32_t)subghz_device_cc1101_ext->async_tx.gpio_tx_buff); + LL_DMA_SetPeriphAddress(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, (uint32_t) & (gpio->port->BSRR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetDataLength(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, 2); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF, LL_DMAMUX_REQ_TIM17_UP); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); + + // Start debug + if(subghz_device_cc1101_ext_start_debug()) { + gpio = subghz_device_cc1101_ext->async_mirror_pin; + subghz_device_cc1101_ext->async_tx.debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; + subghz_device_cc1101_ext->async_tx.debug_gpio_buff[1] = gpio->pin; + + LL_DMA_SetMemoryAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, + (uint32_t)subghz_device_cc1101_ext->async_tx.debug_gpio_buff); + LL_DMA_SetPeriphAddress( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, (uint32_t) & (gpio->port->BSRR)); + LL_DMA_ConfigTransfer( + SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD | + LL_DMA_PRIORITY_LOW); + LL_DMA_SetDataLength(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, 2); + LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF, LL_DMAMUX_REQ_TIM17_UP); + LL_DMA_EnableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); + } + + // Start counter + LL_TIM_EnableDMAReq_UPDATE(TIM17); + LL_TIM_GenerateEvent_UPDATE(TIM17); + + subghz_device_cc1101_ext_tx(); + + LL_TIM_SetCounter(TIM17, 0); + LL_TIM_EnableCounter(TIM17); + + return true; +} + +bool subghz_device_cc1101_ext_is_async_tx_complete() { + return subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd; +} + +void subghz_device_cc1101_ext_stop_async_tx() { + furi_assert( + subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx || + subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd); + + // Shutdown radio + subghz_device_cc1101_ext_idle(); + + // Deinitialize Timer + FURI_CRITICAL_ENTER(); + furi_hal_bus_disable(FuriHalBusTIM17); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); + + // Deinitialize DMA + LL_DMA_DeInit(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF); + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH4_DEF); + furi_hal_interrupt_set_isr(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_IRQ, NULL, NULL); + + // Deinitialize GPIO + furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false); + furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + // Stop debug + if(subghz_device_cc1101_ext_stop_debug()) { + LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH5_DEF); + } + + FURI_CRITICAL_EXIT(); + + free(subghz_device_cc1101_ext->async_tx.buffer); + + subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateIdle; +} diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h new file mode 100644 index 000000000..d972fcb66 --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h @@ -0,0 +1,206 @@ +/** + * @file furi_hal_subghz.h + * SubGhz HAL API + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Mirror RX/TX async modulation signal to specified pin + * + * @warning Configures pin to output mode. Make sure it is not connected + * directly to power or ground. + * + * @param[in] pin pointer to the gpio pin structure or NULL to disable + */ +void subghz_device_cc1101_ext_set_async_mirror_pin(const GpioPin* pin); + +/** Get data GPIO + * + * @return pointer to the gpio pin structure + */ +const GpioPin* subghz_device_cc1101_ext_get_data_gpio(); + +/** Initialize device + * + * @return true if success + */ +bool subghz_device_cc1101_ext_alloc(); + +/** Deinitialize device + */ +void subghz_device_cc1101_ext_free(); + +/** Check and switch to power save mode Used by internal API-HAL + * initialization routine Can be used to reinitialize device to safe state and + * send it to sleep + */ +bool subghz_device_cc1101_ext_is_connect(); + +/** Send device to sleep mode + */ +void subghz_device_cc1101_ext_sleep(); + +/** Dump info to stdout + */ +void subghz_device_cc1101_ext_dump_state(); + +/** Load custom registers from preset + * + * @param preset_data registers to load + */ +void subghz_device_cc1101_ext_load_custom_preset(const uint8_t* preset_data); + +/** Load registers + * + * @param data Registers data + */ +void subghz_device_cc1101_ext_load_registers(const uint8_t* data); + +/** Load PATABLE + * + * @param data 8 uint8_t values + */ +void subghz_device_cc1101_ext_load_patable(const uint8_t data[8]); + +/** Write packet to FIFO + * + * @param data bytes array + * @param size size + */ +void subghz_device_cc1101_ext_write_packet(const uint8_t* data, uint8_t size); + +/** Check if receive pipe is not empty + * + * @return true if not empty + */ +bool subghz_device_cc1101_ext_rx_pipe_not_empty(); + +/** Check if received data crc is valid + * + * @return true if valid + */ +bool subghz_device_cc1101_ext_is_rx_data_crc_valid(); + +/** Read packet from FIFO + * + * @param data pointer + * @param size size + */ +void subghz_device_cc1101_ext_read_packet(uint8_t* data, uint8_t* size); + +/** Flush rx FIFO buffer + */ +void subghz_device_cc1101_ext_flush_rx(); + +/** Flush tx FIFO buffer + */ +void subghz_device_cc1101_ext_flush_tx(); + +/** Shutdown Issue SPWD command + * @warning registers content will be lost + */ +void subghz_device_cc1101_ext_shutdown(); + +/** Reset Issue reset command + * @warning registers content will be lost + */ +void subghz_device_cc1101_ext_reset(); + +/** Switch to Idle + */ +void subghz_device_cc1101_ext_idle(); + +/** Switch to Receive + */ +void subghz_device_cc1101_ext_rx(); + +/** Switch to Transmit + * + * @return true if the transfer is allowed by belonging to the region + */ +bool subghz_device_cc1101_ext_tx(); + +/** Get RSSI value in dBm + * + * @return RSSI value + */ +float subghz_device_cc1101_ext_get_rssi(); + +/** Get LQI + * + * @return LQI value + */ +uint8_t subghz_device_cc1101_ext_get_lqi(); + +/** Check if frequency is in valid range + * + * @param value frequency in Hz + * + * @return true if frequency is valid, otherwise false + */ +bool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value); + +/** Set frequency + * + * @param value frequency in Hz + * + * @return real frequency in Hz + */ +uint32_t subghz_device_cc1101_ext_set_frequency(uint32_t value); + +/* High Level API */ + +/** Signal Timings Capture callback */ +typedef void (*SubGhzDeviceCC1101ExtCaptureCallback)(bool level, uint32_t duration, void* context); + +/** Enable signal timings capture Initializes GPIO and TIM2 for timings capture + * + * @param callback SubGhzDeviceCC1101ExtCaptureCallback + * @param context callback context + */ +void subghz_device_cc1101_ext_start_async_rx( + SubGhzDeviceCC1101ExtCaptureCallback callback, + void* context); + +/** Disable signal timings capture Resets GPIO and TIM2 + */ +void subghz_device_cc1101_ext_stop_async_rx(); + +/** Async TX callback type + * @param context callback context + * @return LevelDuration + */ +typedef LevelDuration (*SubGhzDeviceCC1101ExtCallback)(void* context); + +/** Start async TX Initializes GPIO, TIM2 and DMA1 for signal output + * + * @param callback SubGhzDeviceCC1101ExtCallback + * @param context callback context + * + * @return true if the transfer is allowed by belonging to the region + */ +bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context); + +/** Wait for async transmission to complete + * + * @return true if TX complete + */ +bool subghz_device_cc1101_ext_is_async_tx_complete(); + +/** Stop async transmission and cleanup resources Resets GPIO, TIM2, and DMA1 + */ +void subghz_device_cc1101_ext_stop_async_tx(); + +#ifdef __cplusplus +} +#endif diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c new file mode 100644 index 000000000..51f5a0d1d --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.c @@ -0,0 +1,110 @@ +#include "cc1101_ext_interconnect.h" +#include "cc1101_ext.h" +#include + +#define TAG "SubGhzDeviceCC1101Ext" + +static bool subghz_device_cc1101_ext_interconnect_is_frequency_valid(uint32_t frequency) { + bool ret = subghz_device_cc1101_ext_is_frequency_valid(frequency); + if(!ret) { + furi_crash("SubGhz: Incorrect frequency."); + } + return ret; +} + +static uint32_t subghz_device_cc1101_ext_interconnect_set_frequency(uint32_t frequency) { + subghz_device_cc1101_ext_interconnect_is_frequency_valid(frequency); + return subghz_device_cc1101_ext_set_frequency(frequency); +} + +static bool subghz_device_cc1101_ext_interconnect_start_async_tx(void* callback, void* context) { + return subghz_device_cc1101_ext_start_async_tx( + (SubGhzDeviceCC1101ExtCallback)callback, context); +} + +static void subghz_device_cc1101_ext_interconnect_start_async_rx(void* callback, void* context) { + subghz_device_cc1101_ext_start_async_rx( + (SubGhzDeviceCC1101ExtCaptureCallback)callback, context); +} + +static void subghz_device_cc1101_ext_interconnect_load_preset( + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + switch(preset) { + case FuriHalSubGhzPresetOok650Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_ook_650khz_async_regs); + break; + case FuriHalSubGhzPresetOok270Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_ook_270khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev238Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev476Async: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); + break; + case FuriHalSubGhzPresetMSK99_97KbAsync: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_msk_99_97kb_async_regs); + break; + case FuriHalSubGhzPresetGFSK9_99KbAsync: + subghz_device_cc1101_ext_load_custom_preset( + subghz_device_cc1101_preset_gfsk_9_99kb_async_regs); + break; + + default: + subghz_device_cc1101_ext_load_custom_preset(preset_data); + } +} + +const SubGhzDeviceInterconnect subghz_device_cc1101_ext_interconnect = { + .begin = subghz_device_cc1101_ext_alloc, + .end = subghz_device_cc1101_ext_free, + .is_connect = subghz_device_cc1101_ext_is_connect, + .reset = subghz_device_cc1101_ext_reset, + .sleep = subghz_device_cc1101_ext_sleep, + .idle = subghz_device_cc1101_ext_idle, + .load_preset = subghz_device_cc1101_ext_interconnect_load_preset, + .set_frequency = subghz_device_cc1101_ext_interconnect_set_frequency, + .is_frequency_valid = subghz_device_cc1101_ext_is_frequency_valid, + .set_async_mirror_pin = subghz_device_cc1101_ext_set_async_mirror_pin, + .get_data_gpio = subghz_device_cc1101_ext_get_data_gpio, + + .set_tx = subghz_device_cc1101_ext_tx, + .flush_tx = subghz_device_cc1101_ext_flush_tx, + .start_async_tx = subghz_device_cc1101_ext_interconnect_start_async_tx, + .is_async_complete_tx = subghz_device_cc1101_ext_is_async_tx_complete, + .stop_async_tx = subghz_device_cc1101_ext_stop_async_tx, + + .set_rx = subghz_device_cc1101_ext_rx, + .flush_rx = subghz_device_cc1101_ext_flush_rx, + .start_async_rx = subghz_device_cc1101_ext_interconnect_start_async_rx, + .stop_async_rx = subghz_device_cc1101_ext_stop_async_rx, + + .get_rssi = subghz_device_cc1101_ext_get_rssi, + .get_lqi = subghz_device_cc1101_ext_get_lqi, + + .rx_pipe_not_empty = subghz_device_cc1101_ext_rx_pipe_not_empty, + .is_rx_data_crc_valid = subghz_device_cc1101_ext_is_rx_data_crc_valid, + .read_packet = subghz_device_cc1101_ext_read_packet, + .write_packet = subghz_device_cc1101_ext_write_packet, +}; + +const SubGhzDevice subghz_device_cc1101_ext = { + .name = SUBGHZ_DEVICE_CC1101_EXT_NAME, + .interconnect = &subghz_device_cc1101_ext_interconnect, +}; + +static const FlipperAppPluginDescriptor subghz_device_cc1101_ext_descriptor = { + .appid = SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID, + .ep_api_version = SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION, + .entry_point = &subghz_device_cc1101_ext, +}; + +const FlipperAppPluginDescriptor* subghz_device_cc1101_ext_ep() { + return &subghz_device_cc1101_ext_descriptor; +} \ No newline at end of file diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h new file mode 100644 index 000000000..cf1ff3ee0 --- /dev/null +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h @@ -0,0 +1,8 @@ +#pragma once +#include + +#define SUBGHZ_DEVICE_CC1101_EXT_NAME "cc1101_ext" + +typedef struct SubGhzDeviceCC1101Ext SubGhzDeviceCC1101Ext; + +const FlipperAppPluginDescriptor* subghz_device_cc1101_ext_ep(); diff --git a/applications/main/subghz/helpers/subghz_chat.c b/applications/main/subghz/helpers/subghz_chat.c index dbf34c970..bbe219fd2 100644 --- a/applications/main/subghz/helpers/subghz_chat.c +++ b/applications/main/subghz/helpers/subghz_chat.c @@ -76,12 +76,15 @@ void subghz_chat_worker_free(SubGhzChatWorker* instance) { free(instance); } -bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency) { +bool subghz_chat_worker_start( + SubGhzChatWorker* instance, + const SubGhzDevice* device, + uint32_t frequency) { furi_assert(instance); furi_assert(!instance->worker_running); bool res = false; - if(subghz_tx_rx_worker_start(instance->subghz_txrx, frequency)) { + if(subghz_tx_rx_worker_start(instance->subghz_txrx, device, frequency)) { furi_message_queue_reset(instance->event_queue); subghz_tx_rx_worker_set_callback_have_read( instance->subghz_txrx, subghz_chat_worker_update_rx_event_chat, instance); diff --git a/applications/main/subghz/helpers/subghz_chat.h b/applications/main/subghz/helpers/subghz_chat.h index b418bbdbf..2c454b75d 100644 --- a/applications/main/subghz/helpers/subghz_chat.h +++ b/applications/main/subghz/helpers/subghz_chat.h @@ -1,5 +1,6 @@ #pragma once #include "../subghz_i.h" +#include #include typedef struct SubGhzChatWorker SubGhzChatWorker; @@ -20,7 +21,10 @@ typedef struct { SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli); void subghz_chat_worker_free(SubGhzChatWorker* instance); -bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); +bool subghz_chat_worker_start( + SubGhzChatWorker* instance, + const SubGhzDevice* device, + uint32_t frequency); void subghz_chat_worker_stop(SubGhzChatWorker* instance); bool subghz_chat_worker_is_running(SubGhzChatWorker* instance); SubGhzChatEvent subghz_chat_worker_get_event_chat(SubGhzChatWorker* instance); diff --git a/applications/main/subghz/helpers/subghz_threshold_rssi.c b/applications/main/subghz/helpers/subghz_threshold_rssi.c index 04a06bc17..07d7bccf9 100644 --- a/applications/main/subghz/helpers/subghz_threshold_rssi.c +++ b/applications/main/subghz/helpers/subghz_threshold_rssi.c @@ -32,9 +32,8 @@ float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance) { return instance->threshold_rssi; } -SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance) { +SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance, float rssi) { furi_assert(instance); - float rssi = furi_hal_subghz_get_rssi(); SubGhzThresholdRssiData ret = {.rssi = rssi, .is_above = false}; if(float_is_equal(instance->threshold_rssi, SUBGHZ_RAW_THRESHOLD_MIN)) { diff --git a/applications/main/subghz/helpers/subghz_threshold_rssi.h b/applications/main/subghz/helpers/subghz_threshold_rssi.h index e28092acb..1d588e271 100644 --- a/applications/main/subghz/helpers/subghz_threshold_rssi.h +++ b/applications/main/subghz/helpers/subghz_threshold_rssi.h @@ -38,6 +38,7 @@ float subghz_threshold_rssi_get(SubGhzThresholdRssi* instance); /** Check threshold * * @param instance Pointer to a SubGhzThresholdRssi + * @param rssi Current RSSI * @return SubGhzThresholdRssiData */ -SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance); +SubGhzThresholdRssiData subghz_threshold_get_rssi_data(SubGhzThresholdRssi* instance, float rssi); diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c index 1517cb998..f117d3974 100644 --- a/applications/main/subghz/helpers/subghz_txrx.c +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -1,9 +1,26 @@ #include "subghz_txrx_i.h" #include +#include +#include #define TAG "SubGhz" +static void subghz_txrx_radio_device_power_on(SubGhzTxRx* instance) { + UNUSED(instance); + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + //CC1101 power-up time + furi_delay_ms(10); + } +} + +static void subghz_txrx_radio_device_power_off(SubGhzTxRx* instance) { + UNUSED(instance); + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + SubGhzTxRx* subghz_txrx_alloc() { SubGhzTxRx* instance = malloc(sizeof(SubGhzTxRx)); instance->setting = subghz_setting_alloc(); @@ -23,16 +40,15 @@ SubGhzTxRx* subghz_txrx_alloc() { instance->fff_data = flipper_format_string_alloc(); instance->environment = subghz_environment_alloc(); - instance->is_database_loaded = subghz_environment_load_keystore( - instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); - subghz_environment_load_keystore( - instance->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); + instance->is_database_loaded = + subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_NAME); + subghz_environment_load_keystore(instance->environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); subghz_environment_set_came_atomo_rainbow_table_file_name( - instance->environment, EXT_PATH("subghz/assets/came_atomo")); + instance->environment, SUBGHZ_CAME_ATOMO_DIR_NAME); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - instance->environment, EXT_PATH("subghz/assets/alutech_at_4n")); + instance->environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - instance->environment, EXT_PATH("subghz/assets/nice_flor_s")); + instance->environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); subghz_environment_set_protocol_registry( instance->environment, (void*)&subghz_protocol_registry); instance->receiver = subghz_receiver_alloc_init(instance->environment); @@ -43,18 +59,32 @@ SubGhzTxRx* subghz_txrx_alloc() { instance->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); subghz_worker_set_context(instance->worker, instance->receiver); + //set default device External + subghz_devices_init(); + instance->radio_device_type = SubGhzRadioDeviceTypeInternal; + instance->radio_device_type = + subghz_txrx_radio_device_set(instance, SubGhzRadioDeviceTypeExternalCC1101); + return instance; } void subghz_txrx_free(SubGhzTxRx* instance) { furi_assert(instance); + if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { + subghz_txrx_radio_device_power_off(instance); + subghz_devices_end(instance->radio_device); + } + + subghz_devices_deinit(); + subghz_worker_free(instance->worker); subghz_receiver_free(instance->receiver); subghz_environment_free(instance->environment); flipper_format_free(instance->fff_data); furi_string_free(instance->preset->name); subghz_setting_free(instance->setting); + free(instance->preset); free(instance); } @@ -122,29 +152,26 @@ void subghz_txrx_get_frequency_and_modulation( static void subghz_txrx_begin(SubGhzTxRx* instance, uint8_t* preset_data) { furi_assert(instance); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - furi_hal_subghz_load_custom_preset(preset_data); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_devices_reset(instance->radio_device); + subghz_devices_idle(instance->radio_device); + subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetCustom, preset_data); instance->txrx_state = SubGhzTxRxStateIDLE; } static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - furi_crash("SubGhz: Incorrect RX frequency."); - } + furi_assert( instance->txrx_state != SubGhzTxRxStateRx && instance->txrx_state != SubGhzTxRxStateSleep); - furi_hal_subghz_idle(); - uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); - furi_hal_subghz_flush_rx(); - subghz_txrx_speaker_on(instance); - furi_hal_subghz_rx(); + subghz_devices_idle(instance->radio_device); - furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, instance->worker); + uint32_t value = subghz_devices_set_frequency(instance->radio_device, frequency); + subghz_devices_flush_rx(instance->radio_device); + subghz_txrx_speaker_on(instance); + + subghz_devices_start_async_rx( + instance->radio_device, subghz_worker_rx_callback, instance->worker); subghz_worker_start(instance->worker); instance->txrx_state = SubGhzTxRxStateRx; return value; @@ -153,7 +180,7 @@ static uint32_t subghz_txrx_rx(SubGhzTxRx* instance, uint32_t frequency) { static void subghz_txrx_idle(SubGhzTxRx* instance) { furi_assert(instance); furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); - furi_hal_subghz_idle(); + subghz_devices_idle(instance->radio_device); subghz_txrx_speaker_off(instance); instance->txrx_state = SubGhzTxRxStateIDLE; } @@ -164,30 +191,26 @@ static void subghz_txrx_rx_end(SubGhzTxRx* instance) { if(subghz_worker_is_running(instance->worker)) { subghz_worker_stop(instance->worker); - furi_hal_subghz_stop_async_rx(); + subghz_devices_stop_async_rx(instance->radio_device); } - furi_hal_subghz_idle(); + subghz_devices_idle(instance->radio_device); subghz_txrx_speaker_off(instance); instance->txrx_state = SubGhzTxRxStateIDLE; } void subghz_txrx_sleep(SubGhzTxRx* instance) { furi_assert(instance); - furi_hal_subghz_sleep(); + subghz_devices_sleep(instance->radio_device); instance->txrx_state = SubGhzTxRxStateSleep; } static bool subghz_txrx_tx(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - furi_crash("SubGhz: Incorrect TX frequency."); - } furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); - furi_hal_subghz_idle(); - furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - bool ret = furi_hal_subghz_tx(); + subghz_devices_idle(instance->radio_device); + subghz_devices_set_frequency(instance->radio_device, frequency); + + bool ret = subghz_devices_set_tx(instance->radio_device); if(ret) { subghz_txrx_speaker_on(instance); instance->txrx_state = SubGhzTxRxStateTx; @@ -249,8 +272,8 @@ SubGhzTxRxStartTxState subghz_txrx_tx_start(SubGhzTxRx* instance, FlipperFormat* if(ret == SubGhzTxRxStartTxStateOk) { //Start TX - furi_hal_subghz_start_async_tx( - subghz_transmitter_yield, instance->transmitter); + subghz_devices_start_async_tx( + instance->radio_device, subghz_transmitter_yield, instance->transmitter); } } else { ret = SubGhzTxRxStartTxStateErrorParserOthers; @@ -293,7 +316,7 @@ static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { furi_assert(instance); furi_assert(instance->txrx_state == SubGhzTxRxStateTx); //Stop TX - furi_hal_subghz_stop_async_tx(); + subghz_devices_stop_async_tx(instance->radio_device); subghz_transmitter_stop(instance->transmitter); subghz_transmitter_free(instance->transmitter); @@ -306,7 +329,6 @@ static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { subghz_txrx_idle(instance); subghz_txrx_speaker_off(instance); //Todo: Show message - // notification_message(notifications, &sequence_reset_red); } FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) { @@ -356,7 +378,7 @@ void subghz_txrx_hopper_update(SubGhzTxRx* instance) { float rssi = -127.0f; if(instance->hopper_state != SubGhzHopperStateRSSITimeOut) { // See RSSI Calculation timings in CC1101 17.3 RSSI - rssi = furi_hal_subghz_get_rssi(); + rssi = subghz_devices_get_rssi(instance->radio_device); // Stay if RSSI is high enough if(rssi > -90.0f) { @@ -414,7 +436,7 @@ void subghz_txrx_speaker_on(SubGhzTxRx* instance) { furi_assert(instance); if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_acquire(30)) { - furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); } else { instance->speaker_state = SubGhzSpeakerStateDisable; } @@ -425,7 +447,7 @@ void subghz_txrx_speaker_off(SubGhzTxRx* instance) { furi_assert(instance); if(instance->speaker_state != SubGhzSpeakerStateDisable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(NULL); + subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); furi_hal_speaker_release(); if(instance->speaker_state == SubGhzSpeakerStateShutdown) instance->speaker_state = SubGhzSpeakerStateDisable; @@ -437,7 +459,7 @@ void subghz_txrx_speaker_mute(SubGhzTxRx* instance) { furi_assert(instance); if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(NULL); + subghz_devices_set_async_mirror_pin(instance->radio_device, NULL); } } } @@ -446,7 +468,7 @@ void subghz_txrx_speaker_unmute(SubGhzTxRx* instance) { furi_assert(instance); if(instance->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + subghz_devices_set_async_mirror_pin(instance->radio_device, &gpio_speaker); } } } @@ -519,3 +541,63 @@ void subghz_txrx_set_raw_file_encoder_worker_callback_end( callback, context); } + +bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name) { + furi_assert(instance); + + bool is_connect = false; + bool is_otg_enabled = furi_hal_power_is_otg_enabled(); + + if(!is_otg_enabled) { + subghz_txrx_radio_device_power_on(instance); + } + + is_connect = subghz_devices_is_connect(subghz_devices_get_by_name(name)); + + if(!is_otg_enabled) { + subghz_txrx_radio_device_power_off(instance); + } + return is_connect; +} + +SubGhzRadioDeviceType + subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type) { + furi_assert(instance); + + if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && + subghz_txrx_radio_device_is_external_connected(instance, SUBGHZ_DEVICE_CC1101_EXT_NAME)) { + subghz_txrx_radio_device_power_on(instance); + instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + subghz_devices_begin(instance->radio_device); + instance->radio_device_type = SubGhzRadioDeviceTypeExternalCC1101; + } else { + subghz_txrx_radio_device_power_off(instance); + if(instance->radio_device_type != SubGhzRadioDeviceTypeInternal) { + subghz_devices_end(instance->radio_device); + } + instance->radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + instance->radio_device_type = SubGhzRadioDeviceTypeInternal; + } + + return instance->radio_device_type; +} + +SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance) { + furi_assert(instance); + return instance->radio_device_type; +} + +float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance) { + furi_assert(instance); + return subghz_devices_get_rssi(instance->radio_device); +} + +const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) { + furi_assert(instance); + return subghz_devices_get_name(instance->radio_device); +} + +bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) { + furi_assert(instance); + return subghz_devices_is_frequency_valid(instance->radio_device, frequency); +} \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_txrx.h b/applications/main/subghz/helpers/subghz_txrx.h index 0f2daf05d..e49789206 100644 --- a/applications/main/subghz/helpers/subghz_txrx.h +++ b/applications/main/subghz/helpers/subghz_txrx.h @@ -7,6 +7,7 @@ #include #include #include +#include typedef struct SubGhzTxRx SubGhzTxRx; @@ -288,3 +289,48 @@ void subghz_txrx_set_raw_file_encoder_worker_callback_end( SubGhzTxRx* instance, SubGhzProtocolEncoderRAWCallbackEnd callback, void* context); + +/* Checking if an external radio device is connected +* +* @param instance Pointer to a SubGhzTxRx +* @param name Name of external radio device +* @return bool True if is connected to the external radio device +*/ +bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const char* name); + +/* Set the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @param radio_device_type Radio device type +* @return SubGhzRadioDeviceType Type of installed radio device +*/ +SubGhzRadioDeviceType + subghz_txrx_radio_device_set(SubGhzTxRx* instance, SubGhzRadioDeviceType radio_device_type); + +/* Get the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return SubGhzRadioDeviceType Type of installed radio device +*/ +SubGhzRadioDeviceType subghz_txrx_radio_device_get(SubGhzTxRx* instance); + +/* Get RSSI the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return float RSSI +*/ +float subghz_txrx_radio_device_get_rssi(SubGhzTxRx* instance); + +/* Get name the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return const char* Name of installed radio device +*/ +const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance); + +/* Get get intelligence whether frequency the selected radio device to use +* +* @param instance Pointer to a SubGhzTxRx +* @return bool True if the frequency is valid +*/ +bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency); \ No newline at end of file diff --git a/applications/main/subghz/helpers/subghz_txrx_i.h b/applications/main/subghz/helpers/subghz_txrx_i.h index bd0ad8b7b..b7d74fd49 100644 --- a/applications/main/subghz/helpers/subghz_txrx_i.h +++ b/applications/main/subghz/helpers/subghz_txrx_i.h @@ -21,6 +21,8 @@ struct SubGhzTxRx { SubGhzTxRxState txrx_state; SubGhzSpeakerState speaker_state; + const SubGhzDevice* radio_device; + SubGhzRadioDeviceType radio_device_type; SubGhzTxRxNeedSaveCallback need_save_callback; void* need_save_context; diff --git a/applications/main/subghz/helpers/subghz_types.h b/applications/main/subghz/helpers/subghz_types.h index 46bf940f4..8beb7b9ed 100644 --- a/applications/main/subghz/helpers/subghz_types.h +++ b/applications/main/subghz/helpers/subghz_types.h @@ -35,6 +35,13 @@ typedef enum { SubGhzSpeakerStateEnable, } SubGhzSpeakerState; +/** SubGhzRadioDeviceType */ +typedef enum { + SubGhzRadioDeviceTypeAuto, + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +} SubGhzRadioDeviceType; + /** SubGhzRxKeyState state */ typedef enum { SubGhzRxKeyStateIDLE, diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index 86a307317..97aa946e8 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -24,3 +24,4 @@ ADD_SCENE(subghz, delete_raw, DeleteRAW) ADD_SCENE(subghz, need_saving, NeedSaving) ADD_SCENE(subghz, rpc, Rpc) ADD_SCENE(subghz, region_info, RegionInfo) +ADD_SCENE(subghz, radio_settings, RadioSettings) diff --git a/applications/main/subghz/scenes/subghz_scene_radio_setting.c b/applications/main/subghz/scenes/subghz_scene_radio_setting.c new file mode 100644 index 000000000..0a47d5bfd --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_radio_setting.c @@ -0,0 +1,70 @@ +#include "../subghz_i.h" +#include +#include + +enum SubGhzRadioSettingIndex { + SubGhzRadioSettingIndexDevice, +}; + +#define RADIO_DEVICE_COUNT 2 +const char* const radio_device_text[RADIO_DEVICE_COUNT] = { + "Internal", + "External", +}; + +const uint32_t radio_device_value[RADIO_DEVICE_COUNT] = { + SubGhzRadioDeviceTypeInternal, + SubGhzRadioDeviceTypeExternalCC1101, +}; + +static void subghz_scene_radio_settings_set_device(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + if(!subghz_txrx_radio_device_is_external_connected( + subghz->txrx, SUBGHZ_DEVICE_CC1101_EXT_NAME) && + radio_device_value[index] == SubGhzRadioDeviceTypeExternalCC1101) { + //ToDo correct if there is more than 1 module + index = 0; + } + variable_item_set_current_value_text(item, radio_device_text[index]); + subghz_txrx_radio_device_set(subghz->txrx, radio_device_value[index]); +} + +void subghz_scene_radio_settings_on_enter(void* context) { + SubGhz* subghz = context; + VariableItem* item; + uint8_t value_index; + + uint8_t value_count_device = RADIO_DEVICE_COUNT; + if(subghz_txrx_radio_device_get(subghz->txrx) == SubGhzRadioDeviceTypeInternal && + !subghz_txrx_radio_device_is_external_connected(subghz->txrx, SUBGHZ_DEVICE_CC1101_EXT_NAME)) + value_count_device = 1; + item = variable_item_list_add( + subghz->variable_item_list, + "Module", + value_count_device, + subghz_scene_radio_settings_set_device, + subghz); + value_index = value_index_uint32( + subghz_txrx_radio_device_get(subghz->txrx), radio_device_value, value_count_device); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, radio_device_text[value_index]); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); +} + +bool subghz_scene_radio_settings_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + UNUSED(subghz); + UNUSED(event); + + return consumed; +} + +void subghz_scene_radio_settings_on_exit(void* context) { + SubGhz* subghz = context; + variable_item_list_set_selected_item(subghz->variable_item_list, 0); + variable_item_list_reset(subghz->variable_item_list); +} diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index a29f86a07..58e4b0429 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -48,6 +48,9 @@ static void subghz_scene_read_raw_update_statusbar(void* context) { furi_string_free(frequency_str); furi_string_free(modulation_str); + + subghz_read_raw_set_radio_device_type( + subghz->subghz_read_raw, subghz_txrx_radio_device_get(subghz->txrx)); } void subghz_scene_read_raw_callback(SubGhzCustomEvent event, void* context) { @@ -238,7 +241,9 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { furi_string_printf( temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, RAW_FILE_NAME, SUBGHZ_APP_EXTENSION); subghz_protocol_raw_gen_fff_data( - subghz_txrx_get_fff_data(subghz->txrx), furi_string_get_cstr(temp_str)); + subghz_txrx_get_fff_data(subghz->txrx), + furi_string_get_cstr(temp_str), + subghz_txrx_radio_device_get_name(subghz->txrx)); furi_string_free(temp_str); if(spl_count > 0) { @@ -298,8 +303,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { subghz_read_raw_update_sample_write( subghz->subghz_read_raw, subghz_protocol_raw_get_sample_write(decoder_raw)); - SubGhzThresholdRssiData ret_rssi = - subghz_threshold_get_rssi_data(subghz->threshold_rssi); + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); subghz_read_raw_add_data_rssi( subghz->subghz_read_raw, ret_rssi.rssi, ret_rssi.is_above); subghz_protocol_raw_save_to_file_pause(decoder_raw, !ret_rssi.is_above); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 6771f8213..6ab443579 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -56,6 +56,9 @@ static void subghz_scene_receiver_update_statusbar(void* context) { subghz->state_notifications = SubGhzNotificationStateIDLE; } furi_string_free(history_stat_str); + + subghz_view_receiver_set_radio_device_type( + subghz->subghz_receiver, subghz_txrx_radio_device_get(subghz->txrx)); } void subghz_scene_receiver_callback(SubGhzCustomEvent event, void* context) { @@ -189,7 +192,8 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_scene_receiver_update_statusbar(subghz); } - SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data(subghz->threshold_rssi); + SubGhzThresholdRssiData ret_rssi = subghz_threshold_get_rssi_data( + subghz->threshold_rssi, subghz_txrx_radio_device_get_rssi(subghz->txrx)); subghz_receiver_rssi(subghz->subghz_receiver, ret_rssi.rssi); subghz_protocol_decoder_bin_raw_data_input_rssi( diff --git a/applications/main/subghz/scenes/subghz_scene_save_name.c b/applications/main/subghz/scenes/subghz_scene_save_name.c index 7d0a4f4f8..86eddfe8e 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_name.c +++ b/applications/main/subghz/scenes/subghz_scene_save_name.c @@ -122,7 +122,8 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) { SubGhzCustomEventManagerNoSet) { subghz_protocol_raw_gen_fff_data( subghz_txrx_get_fff_data(subghz->txrx), - furi_string_get_cstr(subghz->file_path)); + furi_string_get_cstr(subghz->file_path), + subghz_txrx_radio_device_get_name(subghz->txrx)); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet); } else { diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index 0ab5f123e..ce631b398 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -8,7 +8,8 @@ enum SubmenuIndex { SubmenuIndexAddManually, SubmenuIndexFrequencyAnalyzer, SubmenuIndexReadRAW, - SubmenuIndexShowRegionInfo + SubmenuIndexShowRegionInfo, + SubmenuIndexRadioSetting, }; void subghz_scene_start_submenu_callback(void* context, uint32_t index) { @@ -49,6 +50,12 @@ void subghz_scene_start_on_enter(void* context) { SubmenuIndexShowRegionInfo, subghz_scene_start_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "Radio Settings", + SubmenuIndexRadioSetting, + subghz_scene_start_submenu_callback, + subghz); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { submenu_add_item( subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz); @@ -104,6 +111,11 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { subghz->scene_manager, SubGhzSceneStart, SubmenuIndexShowRegionInfo); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRegionInfo); return true; + } else if(event.event == SubmenuIndexRadioSetting) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRadioSetting); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRadioSettings); + return true; } } return false; diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 274dd61ad..f83e44a0a 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -35,6 +35,8 @@ bool subghz_scene_transmitter_update_data_show(void* context) { furi_string_free(modulation_str); furi_string_free(key_str); } + subghz_view_transmitter_set_radio_device_type( + subghz->subghz_transmitter, subghz_txrx_radio_device_get(subghz->txrx)); return ret; } diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 60845ac99..bc7be507e 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -10,6 +10,10 @@ #include #include #include +#include +#include +#include +#include #include "helpers/subghz_chat.h" @@ -24,6 +28,19 @@ #define SUBGHZ_REGION_FILENAME "/int/.region_data" +static void subghz_cli_radio_device_power_on() { + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + //CC1101 power-up time + furi_delay_ms(10); + } +} + +static void subghz_cli_radio_device_power_off() { + if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg(); +} + void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; @@ -44,7 +61,7 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { } furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); frequency = furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); @@ -88,7 +105,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { } furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); frequency = furi_hal_subghz_set_frequency_and_path(frequency); printf("Receiving at frequency %lu Hz\r\n", frequency); printf("Press CTRL+C to stop\r\n"); @@ -109,44 +126,70 @@ void subghz_cli_command_rx_carrier(Cli* cli, FuriString* args, void* context) { furi_hal_subghz_sleep(); } +static const SubGhzDevice* subghz_cli_command_get_device(uint32_t device_ind) { + const SubGhzDevice* device = NULL; + switch(device_ind) { + case 1: + subghz_cli_radio_device_power_on(); + device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + break; + + default: + device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + break; + } + return device; +} + void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; uint32_t key = 0x0074BADE; uint32_t repeat = 10; uint32_t te = 403; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = - sscanf(furi_string_get_cstr(args), "%lx %lu %lu %lu", &key, &frequency, &te, &repeat); - if(ret != 4) { + int ret = sscanf( + furi_string_get_cstr(args), + "%lx %lu %lu %lu %lu", + &key, + &frequency, + &te, + &repeat, + &device_ind); + if(ret != 5) { printf( - "sscanf returned %d, key: %lx, frequency: %lu, te:%lu, repeat: %lu\r\n", + "sscanf returned %d, key: %lx, frequency: %lu, te: %lu, repeat: %lu, device: %lu\r\n ", ret, key, frequency, te, - repeat); + repeat, + device_ind); cli_print_usage( "subghz tx", - "<3 Byte Key: in hex> ", + "<3 Byte Key: in hex> ", furi_string_get_cstr(args)); return; } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); - return; - } } - + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } printf( - "Transmitting at %lu, key %lx, te %lu, repeat %lu. Press CTRL+C to stop\r\n", + "Transmitting at %lu, key %lx, te %lu, repeat %lu device %lu. Press CTRL+C to stop\r\n", frequency, key, te, - repeat); + repeat, + device_ind); FuriString* flipper_format_string = furi_string_alloc_printf( "Protocol: Princeton\n" @@ -170,25 +213,29 @@ void subghz_cli_command_tx(Cli* cli, FuriString* args, void* context) { SubGhzTransmitter* transmitter = subghz_transmitter_alloc_init(environment, "Princeton"); subghz_transmitter_deserialize(transmitter, flipper_format); - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - frequency = furi_hal_subghz_set_frequency_and_path(frequency); + subghz_devices_begin(device); + subghz_devices_reset(device); + subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL); + frequency = subghz_devices_set_frequency(device, frequency); furi_hal_power_suppress_charge_enter(); - - if(furi_hal_subghz_start_async_tx(subghz_transmitter_yield, transmitter)) { - while(!(furi_hal_subghz_is_async_tx_complete() || cli_cmd_interrupt_received(cli))) { + if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) { + while(!(subghz_devices_is_async_complete_tx(device) || cli_cmd_interrupt_received(cli))) { printf("."); fflush(stdout); furi_delay_ms(333); } - furi_hal_subghz_stop_async_tx(); + subghz_devices_stop_async_tx(device); } else { printf("Transmission on this frequency is restricted in your region\r\n"); } - furi_hal_subghz_sleep(); + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + furi_hal_power_suppress_charge_exit(); flipper_format_free(flipper_format); @@ -233,21 +280,29 @@ static void subghz_cli_command_rx_callback( void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { UNUSED(context); uint32_t frequency = 433920000; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); - cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); + if(ret != 2) { printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); + "sscanf returned %d, frequency: %lu device: %lu\r\n", ret, frequency, device_ind); + cli_print_usage( + "subghz rx", + " ", + furi_string_get_cstr(args)); return; } } + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } // Allocate context and buffers SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); @@ -256,14 +311,14 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_check(instance->stream); SubGhzEnvironment* environment = subghz_environment_alloc(); - subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); - subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); + subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME); + subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME); subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/came_atomo")); + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/alutech_at_4n")); + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/nice_flor_s")); + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); @@ -271,18 +326,21 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance); // Configure radio - furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - frequency = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_devices_begin(device); + subghz_devices_reset(device); + subghz_devices_load_preset(device, FuriHalSubGhzPresetOok650Async, NULL); + frequency = subghz_devices_set_frequency(device, frequency); furi_hal_power_suppress_charge_enter(); // Prepare and start RX - furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance); + subghz_devices_start_async_rx(device, subghz_cli_command_rx_capture_callback, instance); // Wait for packets to arrive - printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); + printf( + "Listening at frequency: %lu device: %lu. Press CTRL+C to stop\r\n", + frequency, + device_ind); LevelDuration level_duration; while(!cli_cmd_interrupt_received(cli)) { int ret = furi_stream_buffer_receive( @@ -300,8 +358,11 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { } // Shutdown radio - furi_hal_subghz_stop_async_rx(); - furi_hal_subghz_sleep(); + subghz_devices_stop_async_rx(device); + subghz_devices_sleep(device); + subghz_devices_end(device); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); furi_hal_power_suppress_charge_exit(); @@ -341,7 +402,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { // Configure radio furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_270khz_async_regs); frequency = furi_hal_subghz_set_frequency_and_path(frequency); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); @@ -389,6 +450,7 @@ void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { furi_stream_buffer_free(instance->stream); free(instance); } + void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* file_name; @@ -442,25 +504,23 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); SubGhzEnvironment* environment = subghz_environment_alloc(); - if(subghz_environment_load_keystore( - environment, EXT_PATH("subghz/assets/keeloq_mfcodes"))) { + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n"); } else { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n"); } - if(subghz_environment_load_keystore( - environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user"))) { + if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) { printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n"); } else { printf( "SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n"); } subghz_environment_set_came_atomo_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/came_atomo")); + environment, SUBGHZ_CAME_ATOMO_DIR_NAME); subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/alutech_at_4n")); + environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( - environment, EXT_PATH("subghz/assets/nice_flor_s")); + environment, SUBGHZ_NICE_FLOR_S_DIR_NAME); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment); @@ -468,7 +528,8 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { subghz_receiver_set_rx_callback(receiver, subghz_cli_command_rx_callback, instance); SubGhzFileEncoderWorker* file_worker_encoder = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start(file_worker_encoder, furi_string_get_cstr(file_name))) { + if(subghz_file_encoder_worker_start( + file_worker_encoder, furi_string_get_cstr(file_name), NULL)) { //the worker needs a file in order to open and read part of the file furi_delay_ms(100); } @@ -510,10 +571,11 @@ static void subghz_cli_command_print_usage() { printf("subghz \r\n"); printf("Cmd list:\r\n"); - printf("\tchat \t - Chat with other Flippers\r\n"); printf( - "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); - printf("\trx \t - Receive\r\n"); + "\tchat \t - Chat with other Flippers\r\n"); + printf( + "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); + printf("\trx \t - Receive\r\n"); printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); @@ -611,21 +673,29 @@ static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { static void subghz_cli_command_chat(Cli* cli, FuriString* args) { uint32_t frequency = 433920000; + uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT if(furi_string_size(args)) { - int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); - if(ret != 1) { - printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); - cli_print_usage("subghz chat", "", furi_string_get_cstr(args)); - return; - } - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - printf( - "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", - frequency); + int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &frequency, &device_ind); + if(ret != 2) { + printf("sscanf returned %d, Frequency: %lu\r\n", ret, frequency); + printf("sscanf returned %d, Device: %lu\r\n", ret, device_ind); + cli_print_usage( + "subghz chat", + " ", + furi_string_get_cstr(args)); return; } } + subghz_devices_init(); + const SubGhzDevice* device = subghz_cli_command_get_device(device_ind); + if(!subghz_devices_is_frequency_valid(device, frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", frequency); + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + return; + } if(!furi_hal_region_is_frequency_allowed(frequency)) { printf( "In your region, only reception on this frequency (%lu) is allowed,\r\n" @@ -635,7 +705,8 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { } SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli); - if(!subghz_chat_worker_start(subghz_chat, frequency)) { + + if(!subghz_chat_worker_start(subghz_chat, device, frequency)) { printf("Startup error SubGhzChatWorker\r\n"); if(subghz_chat_worker_is_running(subghz_chat)) { @@ -781,6 +852,10 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args) { furi_string_free(name); furi_string_free(output); furi_string_free(sysmsg); + + subghz_devices_deinit(); + subghz_cli_radio_device_power_off(); + furi_hal_power_suppress_charge_exit(); furi_record_close(RECORD_NOTIFICATION); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index 8036ed5f7..55036846c 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -115,7 +115,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { break; } - if(!furi_hal_subghz_is_frequency_valid(temp_data32)) { + if(!subghz_txrx_radio_device_is_frequecy_valid(subghz->txrx, temp_data32)) { FURI_LOG_E(TAG, "Frequency not supported"); break; } @@ -163,7 +163,8 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { //if RAW subghz->load_type_file = SubGhzLoadTypeFileRaw; - subghz_protocol_raw_gen_fff_data(fff_data, file_path); + subghz_protocol_raw_gen_fff_data( + fff_data, file_path, subghz_txrx_radio_device_get_name(subghz->txrx)); } else { subghz->load_type_file = SubGhzLoadTypeFileKey; stream_copy_full( diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index f84ddfed0..e1014b811 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -62,6 +62,7 @@ typedef struct { uint16_t history_item; SubGhzViewReceiverBarShow bar_show; uint8_t u_rssi; + SubGhzRadioDeviceType device_type; } SubGhzViewReceiverModel; void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { @@ -173,6 +174,17 @@ void subghz_view_receiver_add_data_statusbar( true); } +void subghz_view_receiver_set_radio_device_type( + SubGhzViewReceiver* subghz_receiver, + SubGhzRadioDeviceType device_type) { + furi_assert(subghz_receiver); + with_view_model( + subghz_receiver->view, + SubGhzViewReceiverModel * model, + { model->device_type = device_type; }, + true); +} + static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); @@ -190,9 +202,9 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { for(uint8_t i = 1; i < model->u_rssi; i++) { if(i % 5) { - canvas_draw_dot(canvas, 46 + i, 50); - canvas_draw_dot(canvas, 47 + i, 51); canvas_draw_dot(canvas, 46 + i, 52); + canvas_draw_dot(canvas, 47 + i, 53); + canvas_draw_dot(canvas, 46 + i, 54); } } } @@ -232,22 +244,28 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_set_color(canvas, ColorBlack); if(model->history_item == 0) { - canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); + canvas_draw_icon(canvas, 0, 0, &I_Scanning_short_96x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 44, "Scanning..."); canvas_set_font(canvas, FontSecondary); } + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 0, &I_External_antenna_20x12); + } + subghz_view_rssi_draw(canvas, model); switch(model->bar_show) { case SubGhzViewReceiverBarShowLock: - canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); - canvas_draw_str(canvas, 74, 62, "Locked"); + canvas_draw_icon(canvas, 64, 56, &I_Lock_7x8); + canvas_draw_str(canvas, 74, 64, "Locked"); break; case SubGhzViewReceiverBarShowToUnlockPress: - canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + canvas_draw_str(canvas, 44, 64, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 64, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 97, 64, furi_string_get_cstr(model->history_stat_str)); canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 8, 99, 48); elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); @@ -258,13 +276,13 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_draw_dot(canvas, 17, 61); break; case SubGhzViewReceiverBarShowUnlock: - canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8); - canvas_draw_str(canvas, 74, 62, "Unlocked"); + canvas_draw_icon(canvas, 64, 56, &I_Unlock_7x8); + canvas_draw_str(canvas, 74, 64, "Unlocked"); break; default: - canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); + canvas_draw_str(canvas, 44, 64, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 79, 64, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 97, 64, furi_string_get_cstr(model->history_stat_str)); break; } } diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 5119105e9..c91c06938 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -29,6 +29,10 @@ void subghz_view_receiver_add_data_statusbar( const char* preset_str, const char* history_stat_str); +void subghz_view_receiver_set_radio_device_type( + SubGhzViewReceiver* subghz_receiver, + SubGhzRadioDeviceType device_type); + void subghz_view_receiver_add_item_to_menu( SubGhzViewReceiver* subghz_receiver, const char* name, diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 325664f4a..d90401678 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -177,7 +177,8 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel } subghz_frequency_analyzer_log_frequency_draw(canvas, model); } else { - canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); + canvas_draw_str(canvas, 0, 8, "Frequency Analyzer"); + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); canvas_draw_str(canvas, 0, 64, "RSSI"); subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20, 64); diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index 2ff598b60..88ac129ca 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -29,6 +29,7 @@ typedef struct { uint8_t ind_sin; SubGhzReadRAWStatus status; float raw_threshold_rssi; + SubGhzRadioDeviceType device_type; } SubGhzReadRAWModel; void subghz_read_raw_set_callback( @@ -56,6 +57,14 @@ void subghz_read_raw_add_data_statusbar( true); } +void subghz_read_raw_set_radio_device_type( + SubGhzReadRAW* instance, + SubGhzRadioDeviceType device_type) { + furi_assert(instance); + with_view_model( + instance->view, SubGhzReadRAWModel * model, { model->device_type = device_type; }, true); +} + void subghz_read_raw_add_data_rssi(SubGhzReadRAW* instance, float rssi, bool trace) { furi_assert(instance); uint8_t u_rssi = 0; @@ -279,11 +288,16 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { uint8_t graphics_mode = 1; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 5, 7, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 40, 7, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 0, 9, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 35, 9, furi_string_get_cstr(model->preset_str)); canvas_draw_str_aligned( - canvas, 126, 0, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write)); + canvas, 106, 2, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write)); + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 0, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 0, &I_External_antenna_20x12); + } canvas_draw_line(canvas, 0, 14, 115, 14); canvas_draw_line(canvas, 0, 48, 115, 48); canvas_draw_line(canvas, 115, 14, 115, 48); diff --git a/applications/main/subghz/views/subghz_read_raw.h b/applications/main/subghz/views/subghz_read_raw.h index 31aa9db6f..83403e975 100644 --- a/applications/main/subghz/views/subghz_read_raw.h +++ b/applications/main/subghz/views/subghz_read_raw.h @@ -1,6 +1,7 @@ #pragma once #include +#include "../helpers/subghz_types.h" #include "../helpers/subghz_custom_event.h" #define SUBGHZ_RAW_THRESHOLD_MIN -90.0f @@ -36,6 +37,10 @@ void subghz_read_raw_add_data_statusbar( const char* frequency_str, const char* preset_str); +void subghz_read_raw_set_radio_device_type( + SubGhzReadRAW* instance, + SubGhzRadioDeviceType device_type); + void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample); void subghz_read_raw_stop_send(SubGhzReadRAW* instance); diff --git a/applications/main/subghz/views/subghz_test_carrier.c b/applications/main/subghz/views/subghz_test_carrier.c index e533a6aac..254a4127b 100644 --- a/applications/main/subghz/views/subghz_test_carrier.c +++ b/applications/main/subghz/views/subghz_test_carrier.c @@ -1,6 +1,7 @@ #include "subghz_test_carrier.h" #include "../subghz_i.h" #include "../helpers/subghz_testing.h" +#include #include #include @@ -138,7 +139,7 @@ void subghz_test_carrier_enter(void* context) { SubGhzTestCarrier* subghz_test_carrier = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); diff --git a/applications/main/subghz/views/subghz_test_packet.c b/applications/main/subghz/views/subghz_test_packet.c index 43502180c..bc2c474b5 100644 --- a/applications/main/subghz/views/subghz_test_packet.c +++ b/applications/main/subghz/views/subghz_test_packet.c @@ -1,6 +1,7 @@ #include "subghz_test_packet.h" #include "../subghz_i.h" #include "../helpers/subghz_testing.h" +#include #include #include @@ -194,7 +195,7 @@ void subghz_test_packet_enter(void* context) { SubGhzTestPacket* instance = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); with_view_model( instance->view, diff --git a/applications/main/subghz/views/subghz_test_static.c b/applications/main/subghz/views/subghz_test_static.c index 6abefda76..197af21fb 100644 --- a/applications/main/subghz/views/subghz_test_static.c +++ b/applications/main/subghz/views/subghz_test_static.c @@ -1,6 +1,7 @@ #include "subghz_test_static.h" #include "../subghz_i.h" #include "../helpers/subghz_testing.h" +#include #include #include @@ -141,7 +142,7 @@ void subghz_test_static_enter(void* context) { SubGhzTestStatic* instance = context; furi_hal_subghz_reset(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_cc1101_g0, false); diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 86dc17a38..2a876f8c2 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -15,6 +15,7 @@ typedef struct { FuriString* preset_str; FuriString* key_str; bool show_button; + SubGhzRadioDeviceType device_type; } SubGhzViewTransmitterModel; void subghz_view_transmitter_set_callback( @@ -46,6 +47,17 @@ void subghz_view_transmitter_add_data_to_show( true); } +void subghz_view_transmitter_set_radio_device_type( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzRadioDeviceType device_type) { + furi_assert(subghz_transmitter); + with_view_model( + subghz_transmitter->view, + SubGhzViewTransmitterModel * model, + { model->device_type = device_type; }, + true); +} + static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str) { const uint8_t button_height = 12; const uint8_t vertical_offset = 3; @@ -56,7 +68,7 @@ static void subghz_view_transmitter_button_right(Canvas* canvas, const char* str const uint8_t icon_width_with_offset = icon_get_width(icon) + icon_offset; const uint8_t button_width = string_width + horizontal_offset * 2 + icon_width_with_offset; - const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 40; + const uint8_t x = (canvas_width(canvas) - button_width) / 2 + 44; const uint8_t y = canvas_height(canvas); canvas_draw_box(canvas, x, y - button_height, button_width, button_height); @@ -88,7 +100,14 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str)); canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); - if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send"); + if(model->show_button) { + if(model->device_type == SubGhzRadioDeviceTypeInternal) { + canvas_draw_icon(canvas, 108, 39, &I_Internal_antenna_20x12); + } else { + canvas_draw_icon(canvas, 108, 39, &I_External_antenna_20x12); + } + subghz_view_transmitter_button_right(canvas, "Send"); + } } bool subghz_view_transmitter_input(InputEvent* event, void* context) { diff --git a/applications/main/subghz/views/transmitter.h b/applications/main/subghz/views/transmitter.h index 06aae7c6b..19da3145c 100644 --- a/applications/main/subghz/views/transmitter.h +++ b/applications/main/subghz/views/transmitter.h @@ -1,6 +1,7 @@ #pragma once #include +#include "../helpers/subghz_types.h" #include "../helpers/subghz_custom_event.h" typedef struct SubGhzViewTransmitter SubGhzViewTransmitter; @@ -12,6 +13,10 @@ void subghz_view_transmitter_set_callback( SubGhzViewTransmitterCallback callback, void* context); +void subghz_view_transmitter_set_radio_device_type( + SubGhzViewTransmitter* subghz_transmitter, + SubGhzRadioDeviceType device_type); + SubGhzViewTransmitter* subghz_view_transmitter_alloc(); void subghz_view_transmitter_free(SubGhzViewTransmitter* subghz_transmitter); diff --git a/assets/icons/SubGhz/External_antenna_20x12.png b/assets/icons/SubGhz/External_antenna_20x12.png new file mode 100644 index 0000000000000000000000000000000000000000..940087071a1c4225eb48383e3a93d5c841b638b2 GIT binary patch literal 990 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/assets/icons/SubGhz/Scanning_short_96x52.png b/assets/icons/SubGhz/Scanning_short_96x52.png new file mode 100644 index 0000000000000000000000000000000000000000..718d0e695a2560bbb7ccd538c062375d2a318ffb GIT binary patch literal 1388 zcmeAS@N?(olHy`uVBq!ia0vp^2|#SZ!3-pwBG0}CQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS=07??FPLn2Bde0{8v^KOF1ees}+T7#dW-K+~^CEYLU9GXQxDrqI_HztY@Xxa#7Ppj3o=u^L<) zQdy9yACy|0Us{w5jJz~ukW~d%&PAz-CHX}m`T04pkPOJkFUc>?$S+VZGSM?t(C|%6 z&ddXeXo9u)`dWGB6_+IDC8v72*eU@H(aX$Cv2rvwbTM{yvotVrF*h`Hb+t5ib#ZZZ zGBYr7HZyQ^c7^G6$xklLP0cHT=}kfCb;PL`lp=BqfHu3N7G;*DrnnX5=PH1GZIy}J zE#^4QgX&Ge?G_81di8;h(FesXQe4A?fC&i1gr{C02cG^@^MHxI2$;HE)=X;#<_xv~ zpAgso|NjG-p@%NlF)%P5_jGX#skrrK#_ef`6*ydP{{Me@b6A>-Lx{=S+e`Fw7pH3% zdAs~uznOdQKDGRP;?HGHaQkgH+0OUlYP}u%=DQ!xddxlW<9jtjncLh35yqCy>x5P@ z99nmph1Eg!gFqI;m$L2IZAmtkId|qVUWuQv{-flTsXLiZSp3i5+YxWBeR>{4#?3=K zQI)4O879AZQ!=f=OphVwlFMhA6*GHUS2Jun{nDgi4&yF~hwUt~FTby;Ok>#fvsCFC zgLT90@}S8k5r-}+1~*7Jtl;ZBy)z@GLQshF(2R*k^si5`SIfI4o-plV-QgL*9Sk=a zbl8*MN4oOyJ#{*I*J09={JmL|?tkbM6X($R#JRC=Vu_J%Y75Vf{xaL$l#n(7>J9Z>Rsez4>?m+v^4&(=dnl-7VtQ3hUR|<$h?CUGgiufh#fR z%(}0KHCtpPcD3>gZd$&^{MVzu8kg;Bj?5L<)Uo%9dbPy-BY$?*-Bo?}_2;U(sTYb~ z&aKl-jNh-QDG;}};pXvkx4&CUAG$r=-JXeuLGKE4-|ks^mx9VaPgg&ebxsLQ09S_o Aa{vGU literal 0 HcmV?d00001 diff --git a/documentation/FuriHalBus.md b/documentation/FuriHalBus.md index 5c754018b..230a98050 100644 --- a/documentation/FuriHalBus.md +++ b/documentation/FuriHalBus.md @@ -78,9 +78,9 @@ The system will take over any given peripheral only when the respective feature | ADC | | | | QUADSPI | | | | TIM1 | yes | subghz, lfrfid, nfc, infrared, etc... | -| TIM2 | yes | -- | +| TIM2 | yes | subghz, infrared, etc... | | TIM16 | yes | speaker | -| TIM17 | | | +| TIM17 | yes | cc1101_ext | | LPTIM1 | yes | tickless idle timer | | LPTIM2 | yes | pwm | | SAI1 | | | @@ -104,10 +104,10 @@ Below is the list of DMA channels and their usage by the system. | -- | 5 | | | | -- | 6 | | | | -- | 7 | | | -| DMA2 | 1 | yes | infrared, lfrfid, subghz | +| DMA2 | 1 | yes | infrared, lfrfid, subghz, | | -- | 2 | yes | -- | -| -- | 3 | yes | SPI | -| -- | 4 | yes | SPI | -| -- | 5 | | | -| -- | 6 | | | -| -- | 7 | | | +| -- | 3 | yes | cc1101_ext | +| -- | 4 | yes | cc1101_ext | +| -- | 5 | yes | cc1101_ext | +| -- | 6 | yes | SPI | +| -- | 7 | yes | SPI | diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index a689d5a21..1884fe433 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,31.3,, +Version,+,32.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -283,6 +283,7 @@ Function,-,LL_mDelay,void,uint32_t Function,-,SystemCoreClockUpdate,void, Function,-,SystemInit,void, Function,-,_Exit,void,int +Function,+,__aeabi_uldivmod,void*,"uint64_t, uint64_t" Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" Function,+,__clear_cache,void,"void*, void*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0183540f6..f4dec15a7 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,31.3,, +Version,+,32.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -56,7 +56,6 @@ Header,+,firmware/targets/f7/furi_hal/furi_hal_rfid.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_config.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_types.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_subghz.h,, -Header,+,firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_target_hw.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_uart.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_usb_cdc.h,, @@ -186,6 +185,7 @@ Header,+,lib/subghz/blocks/decoder.h,, Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, +Header,+,lib/subghz/devices/cc1101_configs.h,, Header,+,lib/subghz/environment.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, @@ -317,6 +317,7 @@ Function,-,LL_mDelay,void,uint32_t Function,-,SystemCoreClockUpdate,void, Function,-,SystemInit,void, Function,-,_Exit,void,int +Function,+,__aeabi_uldivmod,void*,"uint64_t, uint64_t" Function,-,__assert,void,"const char*, int, const char*" Function,+,__assert_func,void,"const char*, int, const char*, const char*" Function,+,__clear_cache,void,"void*, void*" @@ -657,26 +658,6 @@ Function,+,canvas_width,uint8_t,const Canvas* Function,-,cbrt,double,double Function,-,cbrtf,float,float Function,-,cbrtl,long double,long double -Function,+,cc1101_calibrate,void,FuriHalSpiBusHandle* -Function,+,cc1101_flush_rx,void,FuriHalSpiBusHandle* -Function,+,cc1101_flush_tx,void,FuriHalSpiBusHandle* -Function,-,cc1101_get_partnumber,uint8_t,FuriHalSpiBusHandle* -Function,+,cc1101_get_rssi,uint8_t,FuriHalSpiBusHandle* -Function,+,cc1101_get_status,CC1101Status,FuriHalSpiBusHandle* -Function,-,cc1101_get_version,uint8_t,FuriHalSpiBusHandle* -Function,+,cc1101_read_fifo,uint8_t,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*" -Function,+,cc1101_read_reg,CC1101Status,"FuriHalSpiBusHandle*, uint8_t, uint8_t*" -Function,+,cc1101_reset,void,FuriHalSpiBusHandle* -Function,+,cc1101_set_frequency,uint32_t,"FuriHalSpiBusHandle*, uint32_t" -Function,-,cc1101_set_intermediate_frequency,uint32_t,"FuriHalSpiBusHandle*, uint32_t" -Function,+,cc1101_set_pa_table,void,"FuriHalSpiBusHandle*, const uint8_t[8]" -Function,+,cc1101_shutdown,void,FuriHalSpiBusHandle* -Function,+,cc1101_strobe,CC1101Status,"FuriHalSpiBusHandle*, uint8_t" -Function,+,cc1101_switch_to_idle,void,FuriHalSpiBusHandle* -Function,+,cc1101_switch_to_rx,void,FuriHalSpiBusHandle* -Function,+,cc1101_switch_to_tx,void,FuriHalSpiBusHandle* -Function,+,cc1101_write_fifo,uint8_t,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t" -Function,+,cc1101_write_reg,CC1101Status,"FuriHalSpiBusHandle*, uint8_t, uint8_t" Function,-,ceil,double,double Function,-,ceilf,float,float Function,-,ceill,long double,long double @@ -1383,6 +1364,7 @@ Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, Function,+,furi_hal_subghz_flush_tx,void, +Function,+,furi_hal_subghz_get_data_gpio,const GpioPin*, Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, Function,+,furi_hal_subghz_idle,void, @@ -1390,10 +1372,9 @@ Function,-,furi_hal_subghz_init,void, Function,+,furi_hal_subghz_is_async_tx_complete,_Bool, Function,+,furi_hal_subghz_is_frequency_valid,_Bool,uint32_t Function,+,furi_hal_subghz_is_rx_data_crc_valid,_Bool, -Function,+,furi_hal_subghz_load_custom_preset,void,uint8_t* +Function,+,furi_hal_subghz_load_custom_preset,void,const uint8_t* Function,+,furi_hal_subghz_load_patable,void,const uint8_t[8] -Function,+,furi_hal_subghz_load_preset,void,FuriHalSubGhzPreset -Function,+,furi_hal_subghz_load_registers,void,uint8_t* +Function,+,furi_hal_subghz_load_registers,void,const uint8_t* Function,+,furi_hal_subghz_read_packet,void,"uint8_t*, uint8_t*" Function,+,furi_hal_subghz_reset,void, Function,+,furi_hal_subghz_rx,void, @@ -1401,7 +1382,7 @@ Function,+,furi_hal_subghz_rx_pipe_not_empty,_Bool, Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t -Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath +Function,-,furi_hal_subghz_set_path,void,FuriHalSubGhzPath Function,-,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" @@ -2686,6 +2667,36 @@ Function,+,subghz_block_generic_deserialize,SubGhzProtocolStatus,"SubGhzBlockGen Function,+,subghz_block_generic_deserialize_check_count_bit,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, uint16_t" Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" Function,+,subghz_block_generic_serialize,SubGhzProtocolStatus,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_devices_begin,_Bool,const SubGhzDevice* +Function,+,subghz_devices_deinit,void, +Function,+,subghz_devices_end,void,const SubGhzDevice* +Function,+,subghz_devices_flush_rx,void,const SubGhzDevice* +Function,+,subghz_devices_flush_tx,void,const SubGhzDevice* +Function,+,subghz_devices_get_by_name,const SubGhzDevice*,const char* +Function,+,subghz_devices_get_data_gpio,const GpioPin*,const SubGhzDevice* +Function,+,subghz_devices_get_lqi,uint8_t,const SubGhzDevice* +Function,+,subghz_devices_get_name,const char*,const SubGhzDevice* +Function,+,subghz_devices_get_rssi,float,const SubGhzDevice* +Function,+,subghz_devices_idle,void,const SubGhzDevice* +Function,+,subghz_devices_init,void, +Function,+,subghz_devices_is_async_complete_tx,_Bool,const SubGhzDevice* +Function,+,subghz_devices_is_connect,_Bool,const SubGhzDevice* +Function,+,subghz_devices_is_frequency_valid,_Bool,"const SubGhzDevice*, uint32_t" +Function,+,subghz_devices_is_rx_data_crc_valid,_Bool,const SubGhzDevice* +Function,+,subghz_devices_load_preset,void,"const SubGhzDevice*, FuriHalSubGhzPreset, uint8_t*" +Function,+,subghz_devices_read_packet,void,"const SubGhzDevice*, uint8_t*, uint8_t*" +Function,+,subghz_devices_reset,void,const SubGhzDevice* +Function,+,subghz_devices_rx_pipe_not_empty,_Bool,const SubGhzDevice* +Function,+,subghz_devices_set_async_mirror_pin,void,"const SubGhzDevice*, const GpioPin*" +Function,+,subghz_devices_set_frequency,uint32_t,"const SubGhzDevice*, uint32_t" +Function,+,subghz_devices_set_rx,void,const SubGhzDevice* +Function,+,subghz_devices_set_tx,_Bool,const SubGhzDevice* +Function,+,subghz_devices_sleep,void,const SubGhzDevice* +Function,+,subghz_devices_start_async_rx,void,"const SubGhzDevice*, void*, void*" +Function,+,subghz_devices_start_async_tx,_Bool,"const SubGhzDevice*, void*, void*" +Function,+,subghz_devices_stop_async_rx,void,const SubGhzDevice* +Function,+,subghz_devices_stop_async_tx,void,const SubGhzDevice* +Function,+,subghz_devices_write_packet,void,"const SubGhzDevice*, const uint8_t*, uint8_t" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* Function,+,subghz_environment_get_alutech_at_4n_rainbow_table_file_name,const char*,SubGhzEnvironment* @@ -2744,7 +2755,7 @@ Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" -Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" +Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*" Function,+,subghz_protocol_raw_save_to_file_pause,void,"SubGhzProtocolDecoderRAW*, _Bool" @@ -2788,7 +2799,7 @@ Function,+,subghz_tx_rx_worker_free,void,SubGhzTxRxWorker* Function,+,subghz_tx_rx_worker_is_running,_Bool,SubGhzTxRxWorker* Function,+,subghz_tx_rx_worker_read,size_t,"SubGhzTxRxWorker*, uint8_t*, size_t" Function,+,subghz_tx_rx_worker_set_callback_have_read,void,"SubGhzTxRxWorker*, SubGhzTxRxWorkerCallbackHaveRead, void*" -Function,+,subghz_tx_rx_worker_start,_Bool,"SubGhzTxRxWorker*, uint32_t" +Function,+,subghz_tx_rx_worker_start,_Bool,"SubGhzTxRxWorker*, const SubGhzDevice*, uint32_t" Function,+,subghz_tx_rx_worker_stop,void,SubGhzTxRxWorker* Function,+,subghz_tx_rx_worker_write,_Bool,"SubGhzTxRxWorker*, uint8_t*, size_t" Function,+,subghz_worker_alloc,SubGhzWorker*, @@ -3389,6 +3400,12 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, +Variable,+,subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_gfsk_9_99kb_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_msk_99_97kb_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_ook_270khz_async_regs,const uint8_t[], +Variable,+,subghz_device_cc1101_preset_ook_650khz_async_regs,const uint8_t[], Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 42b854799..17769832b 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -12,10 +12,10 @@ #define TAG "FuriHalSpi" #define SPI_DMA DMA2 -#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_3 -#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_4 -#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch3 -#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch4 +#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_6 +#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_7 +#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch6 +#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch7 #define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL #define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL @@ -170,18 +170,18 @@ bool furi_hal_spi_bus_trx( } static void spi_dma_isr() { -#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 - if(LL_DMA_IsActiveFlag_TC3(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { - LL_DMA_ClearFlag_TC3(SPI_DMA); +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 + if(LL_DMA_IsActiveFlag_TC6(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { + LL_DMA_ClearFlag_TC6(SPI_DMA); furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); } #else #error Update this code. Would you kindly? #endif -#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 - if(LL_DMA_IsActiveFlag_TC4(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { - LL_DMA_ClearFlag_TC4(SPI_DMA); +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7 + if(LL_DMA_IsActiveFlag_TC7(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { + LL_DMA_ClearFlag_TC7(SPI_DMA); furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); } #else @@ -241,8 +241,8 @@ bool furi_hal_spi_bus_trx_dma( dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); -#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 - LL_DMA_ClearFlag_TC4(SPI_DMA); +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_7 + LL_DMA_ClearFlag_TC7(SPI_DMA); #else #error Update this code. Would you kindly? #endif @@ -315,8 +315,8 @@ bool furi_hal_spi_bus_trx_dma( dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); -#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 - LL_DMA_ClearFlag_TC3(SPI_DMA); +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_6 + LL_DMA_ClearFlag_TC6(SPI_DMA); #else #error Update this code. Would you kindly? #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 6d671a9e1..ac5adefb8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,6 +1,5 @@ #include -#include - +#include #include #include #include @@ -27,17 +26,36 @@ static uint32_t furi_hal_subghz_debug_gpio_buff[2]; #define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL #define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL +/** SubGhz state */ +typedef enum { + SubGhzStateInit, /**< Init pending */ + + SubGhzStateIdle, /**< Idle, energy save mode */ + + SubGhzStateAsyncRx, /**< Async RX started */ + + SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ + SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */ + SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ + +} SubGhzState; + +/** SubGhz regulation, receive transmission on the current frequency for the + * region */ +typedef enum { + SubGhzRegulationOnlyRx, /**only Rx*/ + SubGhzRegulationTxRx, /**TxRx*/ +} SubGhzRegulation; + typedef struct { volatile SubGhzState state; volatile SubGhzRegulation regulation; - volatile FuriHalSubGhzPreset preset; const GpioPin* async_mirror_pin; } FuriHalSubGhz; volatile FuriHalSubGhz furi_hal_subghz = { .state = SubGhzStateInit, .regulation = SubGhzRegulationTxRx, - .preset = FuriHalSubGhzPresetIDLE, .async_mirror_pin = NULL, }; @@ -45,10 +63,13 @@ void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } +const GpioPin* furi_hal_subghz_get_data_gpio() { + return &gpio_cc1101_g0; +} + void furi_hal_subghz_init() { furi_assert(furi_hal_subghz.state == SubGhzStateInit); furi_hal_subghz.state = SubGhzStateIdle; - furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); @@ -102,8 +123,6 @@ void furi_hal_subghz_sleep() { cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - - furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; } void furi_hal_subghz_dump_state() { @@ -115,34 +134,7 @@ void furi_hal_subghz_dump_state() { furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); } -void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { - if(preset == FuriHalSubGhzPresetOok650Async) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable); - } else if(preset == FuriHalSubGhzPresetOok270Async) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable); - } else if(preset == FuriHalSubGhzPreset2FSKDev238Async) { - furi_hal_subghz_load_registers( - (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - } else if(preset == FuriHalSubGhzPreset2FSKDev476Async) { - furi_hal_subghz_load_registers( - (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_2fsk_async_patable); - } else if(preset == FuriHalSubGhzPresetMSK99_97KbAsync) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_msk_99_97kb_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_msk_async_patable); - } else if(preset == FuriHalSubGhzPresetGFSK9_99KbAsync) { - furi_hal_subghz_load_registers((uint8_t*)furi_hal_subghz_preset_gfsk_9_99kb_async_regs); - furi_hal_subghz_load_patable(furi_hal_subghz_preset_gfsk_async_patable); - } else { - furi_crash("SubGhz: Missing config."); - } - furi_hal_subghz.preset = preset; -} - -void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { +void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data) { //load config furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_reset(&furi_hal_spi_bus_handle_subghz); @@ -157,7 +149,6 @@ void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { //load pa table memcpy(&pa[0], &preset_data[i + 2], 8); furi_hal_subghz_load_patable(pa); - furi_hal_subghz.preset = FuriHalSubGhzPresetCustom; //show debug if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { @@ -173,7 +164,7 @@ void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { } } -void furi_hal_subghz_load_registers(uint8_t* data) { +void furi_hal_subghz_load_registers(const uint8_t* data) { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); cc1101_reset(&furi_hal_spi_bus_handle_subghz); uint32_t i = 0; diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index 102981dbe..855ce3161 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -5,6 +5,8 @@ #pragma once +#include + #include #include #include @@ -20,18 +22,6 @@ extern "C" { #define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2) #define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999 -/** Radio Presets */ -typedef enum { - FuriHalSubGhzPresetIDLE, /**< default configuration */ - FuriHalSubGhzPresetOok270Async, /**< OOK, bandwidth 270kHz, asynchronous */ - FuriHalSubGhzPresetOok650Async, /**< OOK, bandwidth 650kHz, asynchronous */ - FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */ - FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */ - FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */ - FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */ - FuriHalSubGhzPresetCustom, /**Custom Preset*/ -} FuriHalSubGhzPreset; - /** Switchable Radio Paths */ typedef enum { FuriHalSubGhzPathIsolate, /**< Isolate Radio from antenna */ @@ -40,27 +30,6 @@ typedef enum { FuriHalSubGhzPath868, /**< Center Frequency: 868MHz. Path 3: SW1RF3-SW2RF3, LCLC */ } FuriHalSubGhzPath; -/** SubGhz state */ -typedef enum { - SubGhzStateInit, /**< Init pending */ - - SubGhzStateIdle, /**< Idle, energy save mode */ - - SubGhzStateAsyncRx, /**< Async RX started */ - - SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */ - SubGhzStateAsyncTxLast, /**< Async TX continue, DMA completed and timer got last value to go */ - SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */ - -} SubGhzState; - -/** SubGhz regulation, receive transmission on the current frequency for the - * region */ -typedef enum { - SubGhzRegulationOnlyRx, /**only Rx*/ - SubGhzRegulationTxRx, /**TxRx*/ -} SubGhzRegulation; - /* Mirror RX/TX async modulation signal to specified pin * * @warning Configures pin to output mode. Make sure it is not connected @@ -70,6 +39,12 @@ typedef enum { */ void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin); +/** Get data GPIO + * + * @return pointer to the gpio pin structure + */ +const GpioPin* furi_hal_subghz_get_data_gpio(); + /** Initialize and switch to power save mode Used by internal API-HAL * initialization routine Can be used to reinitialize device to safe state and * send it to sleep @@ -84,23 +59,17 @@ void furi_hal_subghz_sleep(); */ void furi_hal_subghz_dump_state(); -/** Load registers from preset by preset name - * - * @param preset to load - */ -void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset); - /** Load custom registers from preset * * @param preset_data registers to load */ -void furi_hal_subghz_load_custom_preset(uint8_t* preset_data); +void furi_hal_subghz_load_custom_preset(const uint8_t* preset_data); /** Load registers * * @param data Registers data */ -void furi_hal_subghz_load_registers(uint8_t* data); +void furi_hal_subghz_load_registers(const uint8_t* data); /** Load PATABLE * diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h deleted file mode 100644 index 5ea17b6dd..000000000 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h +++ /dev/null @@ -1,314 +0,0 @@ -#pragma once - -#include - -static const uint8_t furi_hal_subghz_preset_ook_270khz_async_regs[][2] = { - // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x47}, // The only important bit is ADC_RETENTION, FIFO Tx=33 Rx=32 - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - // Modem Configuration - {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz - {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz - {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync - {CC1101_MDMCFG3, 0x32}, // Data rate is 3.79372 kBaud - {CC1101_MDMCFG4, 0x67}, // Rx BW filter is 270.833333kHz - - /* Main Radio Control State Machine */ - {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) - - /* Frequency Offset Compensation Configuration */ - {CC1101_FOCCFG, - 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - {CC1101_AGCCTRL0, - 0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB - - /* Wake on radio and timeouts control */ - {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours - - /* Frontend configuration */ - {CC1101_FREND0, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1] - {CC1101_FREND1, 0xB6}, // - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_ook_650khz_async_regs[][2] = { - // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* FIFO and internals */ - {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - // Modem Configuration - {CC1101_MDMCFG0, 0x00}, // Channel spacing is 25kHz - {CC1101_MDMCFG1, 0x00}, // Channel spacing is 25kHz - {CC1101_MDMCFG2, 0x30}, // Format ASK/OOK, No preamble/sync - {CC1101_MDMCFG3, 0x32}, // Data rate is 3.79372 kBaud - {CC1101_MDMCFG4, 0x17}, // Rx BW filter is 650.000kHz - - /* Main Radio Control State Machine */ - {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) - - /* Frequency Offset Compensation Configuration */ - {CC1101_FOCCFG, - 0x18}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - // {CC1101_AGCTRL0,0x40}, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary - // {CC1101_AGCTRL1,0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - // {CC1101_AGCCTRL2, 0x03}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB - //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. - {CC1101_AGCCTRL0, - 0x91}, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary - {CC1101_AGCCTRL1, - 0x0}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB - - /* Wake on radio and timeouts control */ - {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours - - /* Frontend configuration */ - {CC1101_FREND0, 0x11}, // Adjusts current TX LO buffer + high is PATABLE[1] - {CC1101_FREND1, 0xB6}, // - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs[][2] = { - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - {CC1101_PKTCTRL1, 0x04}, - - // // Modem Configuration - {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud - {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz - {CC1101_DEVIATN, 0x04}, //Deviation 2.380371 kHz - - /* Main Radio Control State Machine */ - {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) - - /* Frequency Offset Compensation Configuration */ - {CC1101_FOCCFG, - 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - {CC1101_AGCCTRL0, - 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB - - /* Wake on radio and timeouts control */ - {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours - - /* Frontend configuration */ - {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0x56}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs[][2] = { - - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x0D}, // GD0 as async serial data output/input - - /* Frequency Synthesizer Control */ - {CC1101_FSCTRL1, 0x06}, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz - - /* Packet engine */ - {CC1101_PKTCTRL0, 0x32}, // Async, continious, no whitening - {CC1101_PKTCTRL1, 0x04}, - - // // Modem Configuration - {CC1101_MDMCFG0, 0x00}, - {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) - {CC1101_MDMCFG3, 0x83}, // Data rate is 4.79794 kBaud - {CC1101_MDMCFG4, 0x67}, //Rx BW filter is 270.833333 kHz - {CC1101_DEVIATN, 0x47}, //Deviation 47.60742 kHz - - /* Main Radio Control State Machine */ - {CC1101_MCSM0, 0x18}, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) - - /* Frequency Offset Compensation Configuration */ - {CC1101_FOCCFG, - 0x16}, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off - - /* Automatic Gain Control */ - {CC1101_AGCCTRL0, - 0x91}, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary - {CC1101_AGCCTRL1, - 0x00}, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET - {CC1101_AGCCTRL2, 0x07}, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB - - /* Wake on radio and timeouts control */ - {CC1101_WORCTRL, 0xFB}, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours - - /* Frontend configuration */ - {CC1101_FREND0, 0x10}, // Adjusts current TX LO buffer - {CC1101_FREND1, 0x56}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_msk_99_97kb_async_regs[][2] = { - /* GPIO GD0 */ - {CC1101_IOCFG0, 0x06}, - - {CC1101_FIFOTHR, 0x07}, // The only important bit is ADC_RETENTION - {CC1101_SYNC1, 0x46}, - {CC1101_SYNC0, 0x4C}, - {CC1101_ADDR, 0x00}, - {CC1101_PKTLEN, 0x00}, - {CC1101_CHANNR, 0x00}, - - {CC1101_PKTCTRL0, 0x05}, - - {CC1101_FSCTRL0, 0x23}, - {CC1101_FSCTRL1, 0x06}, - - {CC1101_MDMCFG0, 0xF8}, - {CC1101_MDMCFG1, 0x22}, - {CC1101_MDMCFG2, 0x72}, - {CC1101_MDMCFG3, 0xF8}, - {CC1101_MDMCFG4, 0x5B}, - {CC1101_DEVIATN, 0x47}, - - {CC1101_MCSM0, 0x18}, - {CC1101_FOCCFG, 0x16}, - - {CC1101_AGCCTRL0, 0xB2}, - {CC1101_AGCCTRL1, 0x00}, - {CC1101_AGCCTRL2, 0xC7}, - - {CC1101_FREND0, 0x10}, - {CC1101_FREND1, 0x56}, - - {CC1101_BSCFG, 0x1C}, - {CC1101_FSTEST, 0x59}, - - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_gfsk_9_99kb_async_regs[][2] = { - - {CC1101_IOCFG0, 0x06}, //GDO0 Output Pin Configuration - {CC1101_FIFOTHR, 0x47}, //RX FIFO and TX FIFO Thresholds - - //1 : CRC calculation in TX and CRC check in RX enabled, - //1 : Variable packet length mode. Packet length configured by the first byte after sync word - {CC1101_PKTCTRL0, 0x05}, - - {CC1101_FSCTRL1, 0x06}, //Frequency Synthesizer Control - - {CC1101_SYNC1, 0x46}, - {CC1101_SYNC0, 0x4C}, - {CC1101_ADDR, 0x00}, - {CC1101_PKTLEN, 0x00}, - - {CC1101_MDMCFG4, 0xC8}, //Modem Configuration 9.99 - {CC1101_MDMCFG3, 0x93}, //Modem Configuration - {CC1101_MDMCFG2, 0x12}, // 2: 16/16 sync word bits detected - - {CC1101_DEVIATN, 0x34}, //Deviation = 19.042969 - {CC1101_MCSM0, 0x18}, //Main Radio Control State Machine Configuration - {CC1101_FOCCFG, 0x16}, //Frequency Offset Compensation Configuration - - {CC1101_AGCCTRL2, 0x43}, //AGC Control - {CC1101_AGCCTRL1, 0x40}, - {CC1101_AGCCTRL0, 0x91}, - - {CC1101_WORCTRL, 0xFB}, //Wake On Radio Control - /* End */ - {0, 0}, -}; - -static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = { - 0x00, - 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_ook_async_patable_au[8] = { - 0x00, - 0x37, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_msk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - -static const uint8_t furi_hal_subghz_preset_gfsk_async_patable[8] = { - 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; diff --git a/firmware/targets/f7/platform_specific/intrinsic_export.h b/firmware/targets/f7/platform_specific/intrinsic_export.h index 8dbc4bd03..ca343a128 100644 --- a/firmware/targets/f7/platform_specific/intrinsic_export.h +++ b/firmware/targets/f7/platform_specific/intrinsic_export.h @@ -1,10 +1,12 @@ #include +#include #ifdef __cplusplus extern "C" { #endif void __clear_cache(void*, void*); +void* __aeabi_uldivmod(uint64_t, uint64_t); #ifdef __cplusplus } diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 3a0325b71..2c42a5157 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -19,6 +19,7 @@ env.Append( File("blocks/math.h"), File("subghz_setting.h"), File("subghz_protocol_registry.h"), + File("devices/cc1101_configs.h"), ], ) diff --git a/lib/subghz/devices/cc1101_configs.c b/lib/subghz/devices/cc1101_configs.c new file mode 100644 index 000000000..35ddccd54 --- /dev/null +++ b/lib/subghz/devices/cc1101_configs.c @@ -0,0 +1,431 @@ +#include "cc1101_configs.h" +#include + +const uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[] = { + // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* FIFO and internals */ + CC1101_FIFOTHR, + 0x47, // The only important bit is ADC_RETENTION, FIFO Tx=33 Rx=32 + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + // Modem Configuration + CC1101_MDMCFG0, + 0x00, // Channel spacing is 25kHz + CC1101_MDMCFG1, + 0x00, // Channel spacing is 25kHz + CC1101_MDMCFG2, + 0x30, // Format ASK/OOK, No preamble/sync + CC1101_MDMCFG3, + 0x32, // Data rate is 3.79372 kBaud + CC1101_MDMCFG4, + 0x67, // Rx BW filter is 270.833333kHz + + /* Main Radio Control State Machine */ + CC1101_MCSM0, + 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + CC1101_FOCCFG, + 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + CC1101_AGCCTRL0, + 0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + CC1101_AGCCTRL1, + 0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + + /* Wake on radio and timeouts control */ + CC1101_WORCTRL, + 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + CC1101_FREND0, + 0x11, // Adjusts current TX LO buffer + high is PATABLE[1] + CC1101_FREND1, + 0xB6, // + + /* End load reg */ + 0, + 0, + + //ook_async_patable[8] + 0x00, + 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[] = { + // https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* FIFO and internals */ + CC1101_FIFOTHR, + 0x07, // The only important bit is ADC_RETENTION + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + // Modem Configuration + CC1101_MDMCFG0, + 0x00, // Channel spacing is 25kHz + CC1101_MDMCFG1, + 0x00, // Channel spacing is 25kHz + CC1101_MDMCFG2, + 0x30, // Format ASK/OOK, No preamble/sync + CC1101_MDMCFG3, + 0x32, // Data rate is 3.79372 kBaud + CC1101_MDMCFG4, + 0x17, // Rx BW filter is 650.000kHz + + /* Main Radio Control State Machine */ + CC1101_MCSM0, + 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + CC1101_FOCCFG, + 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + // CC1101_AGCTRL0,0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // CC1101_AGCTRL1,0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + // CC1101_AGCCTRL2, 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + CC1101_AGCCTRL0, + 0x91, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + CC1101_AGCCTRL1, + 0x0, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB + + /* Wake on radio and timeouts control */ + CC1101_WORCTRL, + 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + CC1101_FREND0, + 0x11, // Adjusts current TX LO buffer + high is PATABLE[1] + CC1101_FREND1, + 0xB6, // + + /* End load reg */ + 0, + 0, + + //ook_async_patable[8] + 0x00, + 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[] = { + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + CC1101_PKTCTRL1, + 0x04, + + // // Modem Configuration + CC1101_MDMCFG0, + 0x00, + CC1101_MDMCFG1, + 0x02, + CC1101_MDMCFG2, + 0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + CC1101_MDMCFG3, + 0x83, // Data rate is 4.79794 kBaud + CC1101_MDMCFG4, + 0x67, //Rx BW filter is 270.833333 kHz + CC1101_DEVIATN, + 0x04, //Deviation 2.380371 kHz + + /* Main Radio Control State Machine */ + CC1101_MCSM0, + 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + CC1101_FOCCFG, + 0x16, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + CC1101_AGCCTRL0, + 0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + CC1101_AGCCTRL1, + 0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB + + /* Wake on radio and timeouts control */ + CC1101_WORCTRL, + 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + CC1101_FREND0, + 0x10, // Adjusts current TX LO buffer + CC1101_FREND1, + 0x56, + + /* End load reg */ + 0, + 0, + + // 2fsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[] = { + + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x0D, // GD0 as async serial data output/input + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL1, + 0x06, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz + + /* Packet engine */ + CC1101_PKTCTRL0, + 0x32, // Async, continious, no whitening + CC1101_PKTCTRL1, + 0x04, + + // // Modem Configuration + CC1101_MDMCFG0, + 0x00, + CC1101_MDMCFG1, + 0x02, + CC1101_MDMCFG2, + 0x04, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized) + CC1101_MDMCFG3, + 0x83, // Data rate is 4.79794 kBaud + CC1101_MDMCFG4, + 0x67, //Rx BW filter is 270.833333 kHz + CC1101_DEVIATN, + 0x47, //Deviation 47.60742 kHz + + /* Main Radio Control State Machine */ + CC1101_MCSM0, + 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + CC1101_FOCCFG, + 0x16, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + CC1101_AGCCTRL0, + 0x91, //10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + CC1101_AGCCTRL1, + 0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x07, // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAIN_TARGET 42 dB + + /* Wake on radio and timeouts control */ + CC1101_WORCTRL, + 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + CC1101_FREND0, + 0x10, // Adjusts current TX LO buffer + CC1101_FREND1, + 0x56, + + /* End load reg */ + 0, + 0, + + // 2fsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[] = { + /* GPIO GD0 */ + CC1101_IOCFG0, + 0x06, + + CC1101_FIFOTHR, + 0x07, // The only important bit is ADC_RETENTION + CC1101_SYNC1, + 0x46, + CC1101_SYNC0, + 0x4C, + CC1101_ADDR, + 0x00, + CC1101_PKTLEN, + 0x00, + CC1101_CHANNR, + 0x00, + + CC1101_PKTCTRL0, + 0x05, + + CC1101_FSCTRL0, + 0x23, + CC1101_FSCTRL1, + 0x06, + + CC1101_MDMCFG0, + 0xF8, + CC1101_MDMCFG1, + 0x22, + CC1101_MDMCFG2, + 0x72, + CC1101_MDMCFG3, + 0xF8, + CC1101_MDMCFG4, + 0x5B, + CC1101_DEVIATN, + 0x47, + + CC1101_MCSM0, + 0x18, + CC1101_FOCCFG, + 0x16, + + CC1101_AGCCTRL0, + 0xB2, + CC1101_AGCCTRL1, + 0x00, + CC1101_AGCCTRL2, + 0xC7, + + CC1101_FREND0, + 0x10, + CC1101_FREND1, + 0x56, + + CC1101_BSCFG, + 0x1C, + CC1101_FSTEST, + 0x59, + + /* End load reg */ + 0, + 0, + + // msk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; + +const uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[] = { + + CC1101_IOCFG0, + 0x06, //GDO0 Output Pin Configuration + CC1101_FIFOTHR, + 0x47, //RX FIFO and TX FIFO Thresholds + + //1 : CRC calculation in TX and CRC check in RX enabled, + //1 : Variable packet length mode. Packet length configured by the first byte after sync word + CC1101_PKTCTRL0, + 0x05, + + CC1101_FSCTRL1, + 0x06, //Frequency Synthesizer Control + + CC1101_SYNC1, + 0x46, + CC1101_SYNC0, + 0x4C, + CC1101_ADDR, + 0x00, + CC1101_PKTLEN, + 0x00, + + CC1101_MDMCFG4, + 0xC8, //Modem Configuration 9.99 + CC1101_MDMCFG3, + 0x93, //Modem Configuration + CC1101_MDMCFG2, + 0x12, // 2: 16/16 sync word bits detected + + CC1101_DEVIATN, + 0x34, //Deviation = 19.042969 + CC1101_MCSM0, + 0x18, //Main Radio Control State Machine Configuration + CC1101_FOCCFG, + 0x16, //Frequency Offset Compensation Configuration + + CC1101_AGCCTRL2, + 0x43, //AGC Control + CC1101_AGCCTRL1, + 0x40, + CC1101_AGCCTRL0, + 0x91, + + CC1101_WORCTRL, + 0xFB, //Wake On Radio Control + + /* End load reg */ + 0, + 0, + + // gfsk_async_patable[8] + 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; diff --git a/lib/subghz/devices/cc1101_configs.h b/lib/subghz/devices/cc1101_configs.h new file mode 100644 index 000000000..eecab01d9 --- /dev/null +++ b/lib/subghz/devices/cc1101_configs.h @@ -0,0 +1,18 @@ +#pragma once +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern const uint8_t subghz_device_cc1101_preset_ook_270khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_ook_650khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_msk_99_97kb_async_regs[]; +extern const uint8_t subghz_device_cc1101_preset_gfsk_9_99kb_async_regs[]; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c new file mode 100644 index 000000000..41a0609df --- /dev/null +++ b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.c @@ -0,0 +1,96 @@ +#include "cc1101_int_interconnect.h" +#include +#include "../cc1101_configs.h" + +#define TAG "SubGhzDeviceCC1101Int" + +static bool subghz_device_cc1101_int_interconnect_is_frequency_valid(uint32_t frequency) { + bool ret = furi_hal_subghz_is_frequency_valid(frequency); + if(!ret) { + furi_crash("SubGhz: Incorrect frequency."); + } + return ret; +} + +static uint32_t subghz_device_cc1101_int_interconnect_set_frequency(uint32_t frequency) { + subghz_device_cc1101_int_interconnect_is_frequency_valid(frequency); + return furi_hal_subghz_set_frequency_and_path(frequency); +} + +static bool subghz_device_cc1101_int_interconnect_start_async_tx(void* callback, void* context) { + return furi_hal_subghz_start_async_tx((FuriHalSubGhzAsyncTxCallback)callback, context); +} + +static void subghz_device_cc1101_int_interconnect_start_async_rx(void* callback, void* context) { + furi_hal_subghz_start_async_rx((FuriHalSubGhzCaptureCallback)callback, context); +} + +static void subghz_device_cc1101_int_interconnect_load_preset( + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + switch(preset) { + case FuriHalSubGhzPresetOok650Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_650khz_async_regs); + break; + case FuriHalSubGhzPresetOok270Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_ook_270khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev238Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); + break; + case FuriHalSubGhzPreset2FSKDev476Async: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); + break; + case FuriHalSubGhzPresetMSK99_97KbAsync: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_msk_99_97kb_async_regs); + break; + case FuriHalSubGhzPresetGFSK9_99KbAsync: + furi_hal_subghz_load_custom_preset(subghz_device_cc1101_preset_gfsk_9_99kb_async_regs); + break; + + default: + furi_hal_subghz_load_custom_preset(preset_data); + } +} + +static bool subghz_device_cc1101_int_interconnect_is_connect(void) { + return true; +} + +const SubGhzDeviceInterconnect subghz_device_cc1101_int_interconnect = { + .begin = NULL, + .end = furi_hal_subghz_shutdown, + .is_connect = subghz_device_cc1101_int_interconnect_is_connect, + .reset = furi_hal_subghz_reset, + .sleep = furi_hal_subghz_sleep, + .idle = furi_hal_subghz_idle, + .load_preset = subghz_device_cc1101_int_interconnect_load_preset, + .set_frequency = subghz_device_cc1101_int_interconnect_set_frequency, + .is_frequency_valid = furi_hal_subghz_is_frequency_valid, + .set_async_mirror_pin = furi_hal_subghz_set_async_mirror_pin, + .get_data_gpio = furi_hal_subghz_get_data_gpio, + + .set_tx = furi_hal_subghz_tx, + .flush_tx = furi_hal_subghz_flush_tx, + .start_async_tx = subghz_device_cc1101_int_interconnect_start_async_tx, + .is_async_complete_tx = furi_hal_subghz_is_async_tx_complete, + .stop_async_tx = furi_hal_subghz_stop_async_tx, + + .set_rx = furi_hal_subghz_rx, + .flush_rx = furi_hal_subghz_flush_rx, + .start_async_rx = subghz_device_cc1101_int_interconnect_start_async_rx, + .stop_async_rx = furi_hal_subghz_stop_async_rx, + + .get_rssi = furi_hal_subghz_get_rssi, + .get_lqi = furi_hal_subghz_get_lqi, + + .rx_pipe_not_empty = furi_hal_subghz_rx_pipe_not_empty, + .is_rx_data_crc_valid = furi_hal_subghz_is_rx_data_crc_valid, + .read_packet = furi_hal_subghz_read_packet, + .write_packet = furi_hal_subghz_write_packet, +}; + +const SubGhzDevice subghz_device_cc1101_int = { + .name = SUBGHZ_DEVICE_CC1101_INT_NAME, + .interconnect = &subghz_device_cc1101_int_interconnect, +}; diff --git a/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h new file mode 100644 index 000000000..629d1264c --- /dev/null +++ b/lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h @@ -0,0 +1,8 @@ +#pragma once +#include "../types.h" + +#define SUBGHZ_DEVICE_CC1101_INT_NAME "cc1101_int" + +typedef struct SubGhzDeviceCC1101Int SubGhzDeviceCC1101Int; + +extern const SubGhzDevice subghz_device_cc1101_int; diff --git a/lib/subghz/devices/device_registry.h b/lib/subghz/devices/device_registry.h new file mode 100644 index 000000000..70a0db4b2 --- /dev/null +++ b/lib/subghz/devices/device_registry.h @@ -0,0 +1,13 @@ +#pragma once + +#include "registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzDeviceRegistry subghz_device_registry; + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/devices.c b/lib/subghz/devices/devices.c new file mode 100644 index 000000000..55db61e11 --- /dev/null +++ b/lib/subghz/devices/devices.c @@ -0,0 +1,236 @@ +#include "devices.h" + +#include "registry.h" + +void subghz_devices_init() { + furi_check(!subghz_device_registry_is_valid()); + subghz_device_registry_init(); +} + +void subghz_devices_deinit(void) { + furi_check(subghz_device_registry_is_valid()); + subghz_device_registry_deinit(); +} + +const SubGhzDevice* subghz_devices_get_by_name(const char* device_name) { + furi_check(subghz_device_registry_is_valid()); + const SubGhzDevice* device = subghz_device_registry_get_by_name(device_name); + return device; +} + +const char* subghz_devices_get_name(const SubGhzDevice* device) { + const char* ret = NULL; + if(device) { + ret = device->name; + } + return ret; +} + +bool subghz_devices_begin(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->begin) { + ret = device->interconnect->begin(); + } + return ret; +} + +void subghz_devices_end(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->end) { + device->interconnect->end(); + } +} + +bool subghz_devices_is_connect(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_connect) { + ret = device->interconnect->is_connect(); + } + return ret; +} + +void subghz_devices_reset(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->reset) { + device->interconnect->reset(); + } +} + +void subghz_devices_sleep(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->sleep) { + device->interconnect->sleep(); + } +} + +void subghz_devices_idle(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->idle) { + device->interconnect->idle(); + } +} + +void subghz_devices_load_preset( + const SubGhzDevice* device, + FuriHalSubGhzPreset preset, + uint8_t* preset_data) { + furi_assert(device); + if(device->interconnect->load_preset) { + device->interconnect->load_preset(preset, preset_data); + } +} + +uint32_t subghz_devices_set_frequency(const SubGhzDevice* device, uint32_t frequency) { + uint32_t ret = 0; + furi_assert(device); + if(device->interconnect->set_frequency) { + ret = device->interconnect->set_frequency(frequency); + } + return ret; +} + +bool subghz_devices_is_frequency_valid(const SubGhzDevice* device, uint32_t frequency) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_frequency_valid) { + ret = device->interconnect->is_frequency_valid(frequency); + } + return ret; +} + +void subghz_devices_set_async_mirror_pin(const SubGhzDevice* device, const GpioPin* gpio) { + furi_assert(device); + if(device->interconnect->set_async_mirror_pin) { + device->interconnect->set_async_mirror_pin(gpio); + } +} + +const GpioPin* subghz_devices_get_data_gpio(const SubGhzDevice* device) { + const GpioPin* ret = NULL; + furi_assert(device); + if(device->interconnect->get_data_gpio) { + ret = device->interconnect->get_data_gpio(); + } + return ret; +} + +bool subghz_devices_set_tx(const SubGhzDevice* device) { + bool ret = 0; + furi_assert(device); + if(device->interconnect->set_tx) { + ret = device->interconnect->set_tx(); + } + return ret; +} + +void subghz_devices_flush_tx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->flush_tx) { + device->interconnect->flush_tx(); + } +} + +bool subghz_devices_start_async_tx(const SubGhzDevice* device, void* callback, void* context) { + bool ret = false; + furi_assert(device); + if(device->interconnect->start_async_tx) { + ret = device->interconnect->start_async_tx(callback, context); + } + return ret; +} + +bool subghz_devices_is_async_complete_tx(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_async_complete_tx) { + ret = device->interconnect->is_async_complete_tx(); + } + return ret; +} + +void subghz_devices_stop_async_tx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->stop_async_tx) { + device->interconnect->stop_async_tx(); + } +} + +void subghz_devices_set_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->set_rx) { + device->interconnect->set_rx(); + } +} + +void subghz_devices_flush_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->flush_rx) { + device->interconnect->flush_rx(); + } +} + +void subghz_devices_start_async_rx(const SubGhzDevice* device, void* callback, void* context) { + furi_assert(device); + if(device->interconnect->start_async_rx) { + device->interconnect->start_async_rx(callback, context); + } +} + +void subghz_devices_stop_async_rx(const SubGhzDevice* device) { + furi_assert(device); + if(device->interconnect->stop_async_rx) { + device->interconnect->stop_async_rx(); + } +} + +float subghz_devices_get_rssi(const SubGhzDevice* device) { + float ret = 0; + furi_assert(device); + if(device->interconnect->get_rssi) { + ret = device->interconnect->get_rssi(); + } + return ret; +} + +uint8_t subghz_devices_get_lqi(const SubGhzDevice* device) { + uint8_t ret = 0; + furi_assert(device); + if(device->interconnect->get_lqi) { + ret = device->interconnect->get_lqi(); + } + return ret; +} + +bool subghz_devices_rx_pipe_not_empty(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->rx_pipe_not_empty) { + ret = device->interconnect->rx_pipe_not_empty(); + } + return ret; +} + +bool subghz_devices_is_rx_data_crc_valid(const SubGhzDevice* device) { + bool ret = false; + furi_assert(device); + if(device->interconnect->is_rx_data_crc_valid) { + ret = device->interconnect->is_rx_data_crc_valid(); + } + return ret; +} + +void subghz_devices_read_packet(const SubGhzDevice* device, uint8_t* data, uint8_t* size) { + furi_assert(device); + if(device->interconnect->read_packet) { + device->interconnect->read_packet(data, size); + } +} + +void subghz_devices_write_packet(const SubGhzDevice* device, const uint8_t* data, uint8_t size) { + furi_assert(device); + if(device->interconnect->write_packet) { + device->interconnect->write_packet(data, size); + } +} diff --git a/lib/subghz/devices/devices.h b/lib/subghz/devices/devices.h new file mode 100644 index 000000000..dad3c9aeb --- /dev/null +++ b/lib/subghz/devices/devices.h @@ -0,0 +1,52 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzDevice SubGhzDevice; + +void subghz_devices_init(); +void subghz_devices_deinit(void); + +const SubGhzDevice* subghz_devices_get_by_name(const char* device_name); +const char* subghz_devices_get_name(const SubGhzDevice* device); +bool subghz_devices_begin(const SubGhzDevice* device); +void subghz_devices_end(const SubGhzDevice* device); +bool subghz_devices_is_connect(const SubGhzDevice* device); +void subghz_devices_reset(const SubGhzDevice* device); +void subghz_devices_sleep(const SubGhzDevice* device); +void subghz_devices_idle(const SubGhzDevice* device); +void subghz_devices_load_preset( + const SubGhzDevice* device, + FuriHalSubGhzPreset preset, + uint8_t* preset_data); +uint32_t subghz_devices_set_frequency(const SubGhzDevice* device, uint32_t frequency); +bool subghz_devices_is_frequency_valid(const SubGhzDevice* device, uint32_t frequency); +void subghz_devices_set_async_mirror_pin(const SubGhzDevice* device, const GpioPin* gpio); +const GpioPin* subghz_devices_get_data_gpio(const SubGhzDevice* device); + +bool subghz_devices_set_tx(const SubGhzDevice* device); +void subghz_devices_flush_tx(const SubGhzDevice* device); +bool subghz_devices_start_async_tx(const SubGhzDevice* device, void* callback, void* context); +bool subghz_devices_is_async_complete_tx(const SubGhzDevice* device); +void subghz_devices_stop_async_tx(const SubGhzDevice* device); + +void subghz_devices_set_rx(const SubGhzDevice* device); +void subghz_devices_flush_rx(const SubGhzDevice* device); +void subghz_devices_start_async_rx(const SubGhzDevice* device, void* callback, void* context); +void subghz_devices_stop_async_rx(const SubGhzDevice* device); + +float subghz_devices_get_rssi(const SubGhzDevice* device); +uint8_t subghz_devices_get_lqi(const SubGhzDevice* device); + +bool subghz_devices_rx_pipe_not_empty(const SubGhzDevice* device); +bool subghz_devices_is_rx_data_crc_valid(const SubGhzDevice* device); +void subghz_devices_read_packet(const SubGhzDevice* device, uint8_t* data, uint8_t* size); +void subghz_devices_write_packet(const SubGhzDevice* device, const uint8_t* data, uint8_t size); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/preset.h b/lib/subghz/devices/preset.h new file mode 100644 index 000000000..8716f2e23 --- /dev/null +++ b/lib/subghz/devices/preset.h @@ -0,0 +1,13 @@ +#pragma once + +/** Radio Presets */ +typedef enum { + FuriHalSubGhzPresetIDLE, /**< default configuration */ + FuriHalSubGhzPresetOok270Async, /**< OOK, bandwidth 270kHz, asynchronous */ + FuriHalSubGhzPresetOok650Async, /**< OOK, bandwidth 650kHz, asynchronous */ + FuriHalSubGhzPreset2FSKDev238Async, /**< FM, deviation 2.380371 kHz, asynchronous */ + FuriHalSubGhzPreset2FSKDev476Async, /**< FM, deviation 47.60742 kHz, asynchronous */ + FuriHalSubGhzPresetMSK99_97KbAsync, /**< MSK, deviation 47.60742 kHz, 99.97Kb/s, asynchronous */ + FuriHalSubGhzPresetGFSK9_99KbAsync, /**< GFSK, deviation 19.042969 kHz, 9.996Kb/s, asynchronous */ + FuriHalSubGhzPresetCustom, /**Custom Preset*/ +} FuriHalSubGhzPreset; diff --git a/lib/subghz/devices/registry.c b/lib/subghz/devices/registry.c new file mode 100644 index 000000000..c0d5bb292 --- /dev/null +++ b/lib/subghz/devices/registry.c @@ -0,0 +1,76 @@ +#include "registry.h" + +#include "cc1101_int/cc1101_int_interconnect.h" +#include +#include + +#define TAG "SubGhzDeviceRegistry" + +struct SubGhzDeviceRegistry { + const SubGhzDevice** items; + size_t size; + PluginManager* manager; +}; + +static SubGhzDeviceRegistry* subghz_device_registry = NULL; + +void subghz_device_registry_init(void) { + SubGhzDeviceRegistry* subghz_device = + (SubGhzDeviceRegistry*)malloc(sizeof(SubGhzDeviceRegistry)); + subghz_device->manager = plugin_manager_alloc( + SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID, + SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION, + firmware_api_interface); + + //ToDo: fix path to plugins + if(plugin_manager_load_all(subghz_device->manager, "/any/apps_data/subghz/plugins") != + //if(plugin_manager_load_all(subghz_device->manager, APP_DATA_PATH("plugins")) != + PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load all libs"); + } + + subghz_device->size = plugin_manager_get_count(subghz_device->manager) + 1; + subghz_device->items = + (const SubGhzDevice**)malloc(sizeof(SubGhzDevice*) * subghz_device->size); + subghz_device->items[0] = &subghz_device_cc1101_int; + for(uint32_t i = 1; i < subghz_device->size; i++) { + const SubGhzDevice* plugin = plugin_manager_get_ep(subghz_device->manager, i - 1); + subghz_device->items[i] = plugin; + } + + FURI_LOG_I(TAG, "Loaded %zu radio device", subghz_device->size); + subghz_device_registry = subghz_device; +} + +void subghz_device_registry_deinit(void) { + plugin_manager_free(subghz_device_registry->manager); + free(subghz_device_registry->items); + free(subghz_device_registry); + subghz_device_registry = NULL; +} + +bool subghz_device_registry_is_valid(void) { + return subghz_device_registry != NULL; +} + +const SubGhzDevice* subghz_device_registry_get_by_name(const char* name) { + furi_assert(subghz_device_registry); + + if(name != NULL) { + for(size_t i = 0; i < subghz_device_registry->size; i++) { + if(strcmp(name, subghz_device_registry->items[i]->name) == 0) { + return subghz_device_registry->items[i]; + } + } + } + return NULL; +} + +const SubGhzDevice* subghz_device_registry_get_by_index(size_t index) { + furi_assert(subghz_device_registry); + if(index < subghz_device_registry->size) { + return subghz_device_registry->items[index]; + } else { + return NULL; + } +} diff --git a/lib/subghz/devices/registry.h b/lib/subghz/devices/registry.h new file mode 100644 index 000000000..520058920 --- /dev/null +++ b/lib/subghz/devices/registry.h @@ -0,0 +1,40 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzDevice SubGhzDevice; + +void subghz_device_registry_init(void); + +void subghz_device_registry_deinit(void); + +bool subghz_device_registry_is_valid(void); + +/** + * Registration by name SubGhzDevice. + * @param name SubGhzDevice name + * @return SubGhzDevice* pointer to a SubGhzDevice instance + */ +const SubGhzDevice* subghz_device_registry_get_by_name(const char* name); + +/** + * Registration subghzdevice by index in array SubGhzDevice. + * @param index SubGhzDevice by index in array + * @return SubGhzDevice* pointer to a SubGhzDevice instance + */ +const SubGhzDevice* subghz_device_registry_get_by_index(size_t index); + +/** + * Getting the number of registered subghzdevices. + * @param subghz_device SubGhzDeviceRegistry + * @return Number of subghzdevices + */ +size_t subghz_device_registry_count(void); + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/devices/types.h b/lib/subghz/devices/types.h new file mode 100644 index 000000000..8a4198426 --- /dev/null +++ b/lib/subghz/devices/types.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "preset.h" + +#include + +#define SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID "subghz_radio_device" +#define SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION 1 + +typedef struct SubGhzDeviceRegistry SubGhzDeviceRegistry; +typedef struct SubGhzDevice SubGhzDevice; + +typedef bool (*SubGhzBegin)(void); +typedef void (*SubGhzEnd)(void); +typedef bool (*SubGhzIsConnect)(void); +typedef void (*SubGhzReset)(void); +typedef void (*SubGhzSleep)(void); +typedef void (*SubGhzIdle)(void); +typedef void (*SubGhzLoadPreset)(FuriHalSubGhzPreset preset, uint8_t* preset_data); +typedef uint32_t (*SubGhzSetFrequency)(uint32_t frequency); +typedef bool (*SubGhzIsFrequencyValid)(uint32_t frequency); + +typedef void (*SubGhzSetAsyncMirrorPin)(const GpioPin* gpio); +typedef const GpioPin* (*SubGhzGetDataGpio)(void); + +typedef bool (*SubGhzSetTx)(void); +typedef void (*SubGhzFlushTx)(void); +typedef bool (*SubGhzStartAsyncTx)(void* callback, void* context); +typedef bool (*SubGhzIsAsyncCompleteTx)(void); +typedef void (*SubGhzStopAsyncTx)(void); + +typedef void (*SubGhzSetRx)(void); +typedef void (*SubGhzFlushRx)(void); +typedef void (*SubGhzStartAsyncRx)(void* callback, void* context); +typedef void (*SubGhzStopAsyncRx)(void); + +typedef float (*SubGhzGetRSSI)(void); +typedef uint8_t (*SubGhzGetLQI)(void); + +typedef bool (*SubGhzRxPipeNotEmpty)(void); +typedef bool (*SubGhzRxIsDataCrcValid)(void); +typedef void (*SubGhzReadPacket)(uint8_t* data, uint8_t* size); +typedef void (*SubGhzWritePacket)(const uint8_t* data, uint8_t size); + +typedef struct { + SubGhzBegin begin; + SubGhzEnd end; + + SubGhzIsConnect is_connect; + SubGhzReset reset; + SubGhzSleep sleep; + SubGhzIdle idle; + + SubGhzLoadPreset load_preset; + SubGhzSetFrequency set_frequency; + SubGhzIsFrequencyValid is_frequency_valid; + SubGhzSetAsyncMirrorPin set_async_mirror_pin; + SubGhzGetDataGpio get_data_gpio; + + SubGhzSetTx set_tx; + SubGhzFlushTx flush_tx; + SubGhzStartAsyncTx start_async_tx; + SubGhzIsAsyncCompleteTx is_async_complete_tx; + SubGhzStopAsyncTx stop_async_tx; + + SubGhzSetRx set_rx; + SubGhzFlushRx flush_rx; + SubGhzStartAsyncRx start_async_rx; + SubGhzStopAsyncRx stop_async_rx; + + SubGhzGetRSSI get_rssi; + SubGhzGetLQI get_lqi; + + SubGhzRxPipeNotEmpty rx_pipe_not_empty; + SubGhzRxIsDataCrcValid is_rx_data_crc_valid; + SubGhzReadPacket read_packet; + SubGhzWritePacket write_packet; + +} SubGhzDeviceInterconnect; + +struct SubGhzDevice { + const char* name; + const SubGhzDeviceInterconnect* interconnect; +}; diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index 66358698a..1288c9573 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -40,6 +40,7 @@ struct SubGhzProtocolEncoderRAW { bool is_running; FuriString* file_name; + FuriString* radio_device_name; SubGhzFileEncoderWorker* file_worker_encoder; }; @@ -282,6 +283,7 @@ void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { instance->base.protocol = &subghz_protocol_raw; instance->file_name = furi_string_alloc(); + instance->radio_device_name = furi_string_alloc(); instance->is_running = false; return instance; } @@ -300,6 +302,7 @@ void subghz_protocol_encoder_raw_free(void* context) { SubGhzProtocolEncoderRAW* instance = context; subghz_protocol_encoder_raw_stop(instance); furi_string_free(instance->file_name); + furi_string_free(instance->radio_device_name); free(instance); } @@ -318,7 +321,9 @@ static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* in instance->file_worker_encoder = subghz_file_encoder_worker_alloc(); if(subghz_file_encoder_worker_start( - instance->file_worker_encoder, furi_string_get_cstr(instance->file_name))) { + instance->file_worker_encoder, + furi_string_get_cstr(instance->file_name), + furi_string_get_cstr(instance->radio_device_name))) { //the worker needs a file in order to open and read part of the file furi_delay_ms(100); instance->is_running = true; @@ -328,7 +333,10 @@ static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* in return instance->is_running; } -void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path) { +void subghz_protocol_raw_gen_fff_data( + FlipperFormat* flipper_format, + const char* file_path, + const char* radio_device_name) { do { stream_clean(flipper_format_get_raw_stream(flipper_format)); if(!flipper_format_write_string_cstr(flipper_format, "Protocol", "RAW")) { @@ -340,6 +348,12 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* FURI_LOG_E(TAG, "Unable to add File_name"); break; } + + if(!flipper_format_write_string_cstr( + flipper_format, "Radio_device_name", radio_device_name)) { + FURI_LOG_E(TAG, "Unable to add Radio_device_name"); + break; + } } while(false); } @@ -364,6 +378,13 @@ SubGhzProtocolStatus } furi_string_set(instance->file_name, temp_str); + if(!flipper_format_read_string(flipper_format, "Radio_device_name", temp_str)) { + FURI_LOG_E(TAG, "Missing Radio_device_name"); + res = SubGhzProtocolStatusErrorParserOthers; + break; + } + furi_string_set(instance->radio_device_name, temp_str); + if(!subghz_protocol_encoder_raw_worker_init(instance)) { res = SubGhzProtocolStatusErrorEncoderGetUpload; break; diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 4f67a4e2f..6d791bb36 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -126,8 +126,12 @@ void subghz_protocol_raw_file_encoder_worker_set_callback_end( * File generation for RAW work. * @param flipper_format Pointer to a FlipperFormat instance * @param file_path File path + * @param radio_dev_name Radio device name */ -void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path); +void subghz_protocol_raw_gen_fff_data( + FlipperFormat* flipper_format, + const char* file_path, + const char* radio_dev_name); /** * Deserialize and generating an upload to send. diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 5c4d36f78..519ff3fdc 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "SubGhzFileEncoderWorker" @@ -21,6 +22,7 @@ struct SubGhzFileEncoderWorker { bool is_storage_slow; FuriString* str_data; FuriString* file_path; + const SubGhzDevice* device; SubGhzFileEncoderWorkerCallbackEnd callback_end; void* context_end; @@ -156,10 +158,13 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { if(instance->is_storage_slow) { FURI_LOG_E(TAG, "Storage is slow"); } + FURI_LOG_I(TAG, "End read file"); - while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) { + while(instance->device && !subghz_devices_is_async_complete_tx(instance->device) && + instance->worker_running) { furi_delay_ms(5); } + FURI_LOG_I(TAG, "End transmission"); while(instance->worker_running) { if(instance->worker_stoping) { @@ -206,12 +211,16 @@ void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { free(instance); } -bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path) { +bool subghz_file_encoder_worker_start( + SubGhzFileEncoderWorker* instance, + const char* file_path, + const char* radio_device_name) { furi_assert(instance); furi_assert(!instance->worker_running); furi_stream_buffer_reset(instance->stream); furi_string_set(instance->file_path, file_path); + instance->device = subghz_devices_get_by_name(radio_device_name); instance->worker_running = true; furi_thread_start(instance->thread); diff --git a/lib/subghz/subghz_file_encoder_worker.h b/lib/subghz/subghz_file_encoder_worker.h index a87be5cd6..e66c66d76 100644 --- a/lib/subghz/subghz_file_encoder_worker.h +++ b/lib/subghz/subghz_file_encoder_worker.h @@ -38,9 +38,14 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context); /** * Start SubGhzFileEncoderWorker. * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @param file_path File path + * @param radio_device_name Radio device name * @return bool - true if ok */ -bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path); +bool subghz_file_encoder_worker_start( + SubGhzFileEncoderWorker* instance, + const char* file_path, + const char* radio_device_name); /** * Stop SubGhzFileEncoderWorker diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 656580043..9804f8277 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -4,7 +4,7 @@ #include #include -#include +#include #define TAG "SubGhzSetting" @@ -218,8 +218,7 @@ void subghz_setting_free(SubGhzSetting* instance) { static void subghz_setting_load_default_preset( SubGhzSetting* instance, const char* preset_name, - const uint8_t* preset_data, - const uint8_t preset_pa_table[8]) { + const uint8_t* preset_data) { furi_assert(instance); furi_assert(preset_data); uint32_t preset_data_count = 0; @@ -235,10 +234,8 @@ static void subghz_setting_load_default_preset( preset_data_count += 2; item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8; item->custom_preset_data = malloc(item->custom_preset_data_size); - //load preset register - memcpy(&item->custom_preset_data[0], &preset_data[0], preset_data_count); - //load pa table - memcpy(&item->custom_preset_data[preset_data_count], &preset_pa_table[0], 8); + //load preset register + pa table + memcpy(&item->custom_preset_data[0], &preset_data[0], item->custom_preset_data_size); } static void subghz_setting_load_default_region( @@ -262,25 +259,13 @@ static void subghz_setting_load_default_region( } subghz_setting_load_default_preset( - instance, - "AM270", - (uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs, - furi_hal_subghz_preset_ook_async_patable); + instance, "AM270", subghz_device_cc1101_preset_ook_270khz_async_regs); subghz_setting_load_default_preset( - instance, - "AM650", - (uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs, - furi_hal_subghz_preset_ook_async_patable); + instance, "AM650", subghz_device_cc1101_preset_ook_650khz_async_regs); subghz_setting_load_default_preset( - instance, - "FM238", - (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs, - furi_hal_subghz_preset_2fsk_async_patable); + instance, "FM238", subghz_device_cc1101_preset_2fsk_dev2_38khz_async_regs); subghz_setting_load_default_preset( - instance, - "FM476", - (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs, - furi_hal_subghz_preset_2fsk_async_patable); + instance, "FM476", subghz_device_cc1101_preset_2fsk_dev47_6khz_async_regs); } void subghz_setting_load_default(SubGhzSetting* instance) { @@ -359,6 +344,7 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { } while(flipper_format_read_uint32( fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) { + //Todo: add a frequency support check depending on the selected radio device if(furi_hal_subghz_is_frequency_valid(temp_data32)) { FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32); FrequencyList_push_back(instance->frequencies, temp_data32); diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 42124bebc..250e6666f 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -21,6 +21,8 @@ struct SubGhzTxRxWorker { SubGhzTxRxWorkerStatus status; uint32_t frequency; + const SubGhzDevice* device; + const GpioPin* device_data_gpio; SubGhzTxRxWorkerCallbackHaveRead callback_have_read; void* context_have_read; @@ -65,33 +67,33 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* uint8_t timeout = 100; bool ret = false; if(instance->status != SubGhzTxRxWorkerStatusRx) { - furi_hal_subghz_rx(); + subghz_devices_set_rx(instance->device); instance->status = SubGhzTxRxWorkerStatusRx; furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { + while(furi_hal_gpio_read(instance->device_data_gpio)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); + subghz_devices_flush_rx(instance->device); + subghz_devices_set_rx(instance->device); break; } } - if(furi_hal_subghz_rx_pipe_not_empty()) { + if(subghz_devices_rx_pipe_not_empty(instance->device)) { FURI_LOG_I( TAG, "RSSI: %03.1fdbm LQI: %d", - (double)furi_hal_subghz_get_rssi(), - furi_hal_subghz_get_lqi()); - if(furi_hal_subghz_is_rx_data_crc_valid()) { - furi_hal_subghz_read_packet(data, size); + (double)subghz_devices_get_rssi(instance->device), + subghz_devices_get_lqi(instance->device)); + if(subghz_devices_is_rx_data_crc_valid(instance->device)) { + subghz_devices_read_packet(instance->device, data, size); ret = true; } - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); + subghz_devices_flush_rx(instance->device); + subghz_devices_set_rx(instance->device); } return ret; } @@ -99,26 +101,28 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { uint8_t timeout = 200; if(instance->status != SubGhzTxRxWorkerStatusIDLE) { - furi_hal_subghz_idle(); + subghz_devices_idle(instance->device); } - furi_hal_subghz_write_packet(data, size); - furi_hal_subghz_tx(); //start send + subghz_devices_write_packet(instance->device, data, size); + subghz_devices_set_tx(instance->device); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read( + instance->device_data_gpio)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read( + instance->device_data_gpio)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); break; } } - furi_hal_subghz_idle(); + subghz_devices_idle(instance->device); instance->status = SubGhzTxRxWorkerStatusIDLE; } /** Worker thread @@ -128,16 +132,19 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si */ static int32_t subghz_tx_rx_worker_thread(void* context) { SubGhzTxRxWorker* instance = context; + furi_assert(instance->device); FURI_LOG_I(TAG, "Worker start"); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); - //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + subghz_devices_begin(instance->device); + instance->device_data_gpio = subghz_devices_get_data_gpio(instance->device); + subghz_devices_reset(instance->device); + subghz_devices_idle(instance->device); + subghz_devices_load_preset(instance->device, FuriHalSubGhzPresetGFSK9_99KbAsync, NULL); - furi_hal_subghz_set_frequency_and_path(instance->frequency); - furi_hal_subghz_flush_rx(); + furi_hal_gpio_init(instance->device_data_gpio, GpioModeInput, GpioPullNo, GpioSpeedLow); + + subghz_devices_set_frequency(instance->device, instance->frequency); + subghz_devices_flush_rx(instance->device); uint8_t data[SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE + 1] = {0}; size_t size_tx = 0; @@ -191,8 +198,8 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_delay_tick(1); } - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); + subghz_devices_sleep(instance->device); + subghz_devices_end(instance->device); FURI_LOG_I(TAG, "Worker stop"); return 0; @@ -224,7 +231,10 @@ void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) { free(instance); } -bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { +bool subghz_tx_rx_worker_start( + SubGhzTxRxWorker* instance, + const SubGhzDevice* device, + uint32_t frequency) { furi_assert(instance); furi_assert(!instance->worker_running); bool res = false; @@ -235,6 +245,7 @@ bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { if(furi_hal_region_is_frequency_allowed(frequency)) { instance->frequency = frequency; + instance->device = device; res = true; } diff --git a/lib/subghz/subghz_tx_rx_worker.h b/lib/subghz/subghz_tx_rx_worker.h index ddc02e749..56bdb0a1f 100644 --- a/lib/subghz/subghz_tx_rx_worker.h +++ b/lib/subghz/subghz_tx_rx_worker.h @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -67,9 +68,13 @@ void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance); /** * Start SubGhzTxRxWorker * @param instance Pointer to a SubGhzTxRxWorker instance + * @param device Pointer to a SubGhzDevice instance * @return bool - true if ok */ -bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency); +bool subghz_tx_rx_worker_start( + SubGhzTxRxWorker* instance, + const SubGhzDevice* device, + uint32_t frequency); /** * Stop SubGhzTxRxWorker diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 719beff45..d87a0dc76 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -21,6 +21,12 @@ #define SUBGHZ_RAW_FILE_VERSION 1 #define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" +#define SUBGHZ_KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") +#define SUBGHZ_KEYSTORE_DIR_USER_NAME EXT_PATH("subghz/assets/keeloq_mfcodes_user") +#define SUBGHZ_CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") +#define SUBGHZ_NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") +#define SUBGHZ_ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") + typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; typedef struct SubGhzEnvironment SubGhzEnvironment; diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index b70b5cff5..776977cda 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -228,6 +228,7 @@ vars.AddVariables( ("applications/debug", False), ("applications/external", False), ("applications/examples", False), + ("applications/drivers", False), ("applications_user", False), ], ), From 88f8f68e292629d2c11c12334804c1f14742d032 Mon Sep 17 00:00:00 2001 From: Slavik Nychkalo Date: Fri, 30 Jun 2023 18:50:46 +0300 Subject: [PATCH 322/370] fix width of submenu items on Vertical orientation (#2306) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix width of submenu items on vertical view * Gui: slightly better canvas width handling in submenu * Gui: remove unused include Co-authored-by: あく --- applications/services/gui/modules/submenu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 9d81c30b6..3ba35edde 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -63,7 +63,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { SubmenuModel* model = _model; const uint8_t item_height = 16; - const uint8_t item_width = 123; + uint8_t item_width = canvas_width(canvas) - 5; canvas_clear(canvas); From e7bd547d05c9cb70c5da26caa39ad1efcfd04e74 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Fri, 30 Jun 2023 20:56:41 +0300 Subject: [PATCH 323/370] SubGHz: properly working with missing external driver (#2821) --- applications/main/subghz/helpers/subghz_txrx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c index f117d3974..b911f4434 100644 --- a/applications/main/subghz/helpers/subghz_txrx.c +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -552,7 +552,10 @@ bool subghz_txrx_radio_device_is_external_connected(SubGhzTxRx* instance, const subghz_txrx_radio_device_power_on(instance); } - is_connect = subghz_devices_is_connect(subghz_devices_get_by_name(name)); + const SubGhzDevice* device = subghz_devices_get_by_name(name); + if(device) { + is_connect = subghz_devices_is_connect(device); + } if(!is_otg_enabled) { subghz_txrx_radio_device_power_off(instance); From 690d4eb6613226227af994f6ffff1e9659269409 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 1 Jul 2023 02:31:37 +0200 Subject: [PATCH 324/370] Tidy up all applications data and resources paths --- .../.mass_storage/mass_storage_app_i.h | 2 +- applications/external/airmouse/air_mouse.c | 6 + .../airmouse/tracking/calibration_data.h | 3 +- applications/external/asteroids/app.c | 11 +- applications/external/blackjack/util.c | 5 +- applications/external/brainfuck/brainfuck.c | 4 +- .../scenes/brainfuck_scene_file_create.c | 2 +- .../scenes/brainfuck_scene_file_select.c | 4 +- .../wifi_marauder_app_i.h | 2 +- .../flizzer_tracker/flizzer_tracker.c | 7 +- .../flizzer_tracker/flizzer_tracker.h | 7 +- applications/external/game15/game15.c | 12 +- applications/external/game_2048/game_2048.c | 10 +- applications/external/hex_editor/hex_editor.c | 2 +- applications/external/hex_viewer/hex_viewer.c | 2 +- applications/external/hid_app/hid.c | 2 +- applications/external/ifttt/application.fam | 2 +- .../external/ifttt/ifttt_virtual_button.c | 10 +- .../external/ir_remote/infrared_remote_app.c | 6 +- applications/external/multi_fuzzer/fuzzer.c | 8 +- .../external/music_beeper/music_beeper.c | 2 +- .../external/music_player/music_player.c | 2 +- applications/external/nfc_magic/nfc_magic_i.h | 2 +- .../{nrf24_batch => nrf24batch}/LICENSE | 0 .../application.fam | 2 +- .../lib/nrf24/nrf24.c | 0 .../lib/nrf24/nrf24.h | 0 .../{nrf24_batch => nrf24batch}/nrf24batch.c | 3 +- .../{nrf24_batch => nrf24batch}/nrf24batch.h | 0 .../nrf24batch_10px.png | Bin .../application.fam | 2 +- .../lib/nrf24/nrf24.c | 0 .../lib/nrf24/nrf24.h | 0 .../mouse_10px.png | Bin .../mousejacker.c | 6 +- .../mousejacker_ducky.c | 0 .../mousejacker_ducky.h | 0 .../external/nrf24scan/application.fam | 2 +- applications/external/nrf24scan/nrf24scan.c | 3 +- .../{nrfsniff => nrf24sniff}/application.fam | 2 +- .../lib/nrf24/nrf24.c | 0 .../lib/nrf24/nrf24.h | 0 .../{nrfsniff => nrf24sniff}/nrfsniff.c | 3 +- .../nrfsniff_10px.png | Bin applications/external/picopass/picopass.c | 2 +- .../external/picopass/picopass_device.c | 2 +- .../external/picopass/picopass_device.h | 2 +- applications/external/qrcode/qrcode_app.c | 5 +- applications/external/reversi/game_reversi.c | 10 +- applications/external/simonsays/simon_says.c | 10 +- .../scenes/subbrute_scene_load_file.c | 4 +- .../external/subghz_playlist/playlist.c | 2 +- .../subghz_remote/subghz_remote_app.c | 1 + .../subghz_remote_app.c | 1 + .../external/swd_probe/swd_probe_app.c | 10 +- applications/external/tama_p1/tama.h | 4 +- applications/external/tama_p1/tama_p1.c | 1 + applications/external/text2sam/sam_app.cpp | 3 +- .../external/text_viewer/text_viewer.c | 4 +- .../external/timelapse/application.fam | 2 +- applications/external/timelapse/zeitraffer.c | 9 +- applications/external/unitemp/Sensors.c | 2 - applications/external/unitemp/unitemp.c | 3 +- applications/external/unitemp/unitemp.h | 2 +- applications/external/wav_player/wav_player.c | 2 +- .../barcode_data/codabar_encodings.txt | 22 +++ .../barcode_data/code128c_encodings.txt | 106 +++++++++++ .../apps_data/nrf24batch/CO2_mini.txt | 83 +++++++++ .../apps_data/nrf24batch/CO2_mini_old.txt | 83 +++++++++ .../nrf24batch/Kitchen Vent Dimmer.txt | 164 ++++++++++++++++++ .../apps_data/nrf24batch/Kitchen Vent.txt | 127 ++++++++++++++ .../resources/{ => apps_data}/tama_p1/rom.bin | Bin .../example_uids_cyfral.txt | 0 .../example_uids_ds1990.txt | 0 .../example_uids_metakom.txt | 0 .../mifare_fuzzer/example_uids04.txt | 9 + .../mifare_fuzzer/example_uids07.txt | 10 ++ .../example_uids_em4100.txt | 0 .../example_uids_h10301.txt | 0 .../example_uids_hidprox.txt | 0 .../example_uids_pac.txt | 0 81 files changed, 700 insertions(+), 121 deletions(-) rename applications/external/{nrf24_batch => nrf24batch}/LICENSE (100%) rename applications/external/{nrf24_batch => nrf24batch}/application.fam (94%) rename applications/external/{nrf24_batch => nrf24batch}/lib/nrf24/nrf24.c (100%) rename applications/external/{nrf24_batch => nrf24batch}/lib/nrf24/nrf24.h (100%) rename applications/external/{nrf24_batch => nrf24batch}/nrf24batch.c (99%) rename applications/external/{nrf24_batch => nrf24batch}/nrf24batch.h (100%) rename applications/external/{nrf24_batch => nrf24batch}/nrf24batch_10px.png (100%) rename applications/external/{mousejacker => nrf24mousejacker}/application.fam (94%) rename applications/external/{mousejacker => nrf24mousejacker}/lib/nrf24/nrf24.c (100%) rename applications/external/{mousejacker => nrf24mousejacker}/lib/nrf24/nrf24.h (100%) rename applications/external/{mousejacker => nrf24mousejacker}/mouse_10px.png (100%) rename applications/external/{mousejacker => nrf24mousejacker}/mousejacker.c (98%) rename applications/external/{mousejacker => nrf24mousejacker}/mousejacker_ducky.c (100%) rename applications/external/{mousejacker => nrf24mousejacker}/mousejacker_ducky.h (100%) rename applications/external/{nrfsniff => nrf24sniff}/application.fam (95%) rename applications/external/{nrfsniff => nrf24sniff}/lib/nrf24/nrf24.c (100%) rename applications/external/{nrfsniff => nrf24sniff}/lib/nrf24/nrf24.h (100%) rename applications/external/{nrfsniff => nrf24sniff}/nrfsniff.c (99%) rename applications/external/{nrfsniff => nrf24sniff}/nrfsniff_10px.png (100%) create mode 100644 assets/resources/apps_data/barcode_data/codabar_encodings.txt create mode 100644 assets/resources/apps_data/barcode_data/code128c_encodings.txt create mode 100644 assets/resources/apps_data/nrf24batch/CO2_mini.txt create mode 100644 assets/resources/apps_data/nrf24batch/CO2_mini_old.txt create mode 100644 assets/resources/apps_data/nrf24batch/Kitchen Vent Dimmer.txt create mode 100644 assets/resources/apps_data/nrf24batch/Kitchen Vent.txt rename assets/resources/{ => apps_data}/tama_p1/rom.bin (100%) rename assets/resources/{ibtnfuzzer => ibutton_fuzzer}/example_uids_cyfral.txt (100%) rename assets/resources/{ibtnfuzzer => ibutton_fuzzer}/example_uids_ds1990.txt (100%) rename assets/resources/{ibtnfuzzer => ibutton_fuzzer}/example_uids_metakom.txt (100%) create mode 100644 assets/resources/mifare_fuzzer/example_uids04.txt create mode 100644 assets/resources/mifare_fuzzer/example_uids07.txt rename assets/resources/{rfidfuzzer => rfid_fuzzer}/example_uids_em4100.txt (100%) rename assets/resources/{rfidfuzzer => rfid_fuzzer}/example_uids_h10301.txt (100%) rename assets/resources/{rfidfuzzer => rfid_fuzzer}/example_uids_hidprox.txt (100%) rename assets/resources/{rfidfuzzer => rfid_fuzzer}/example_uids_pac.txt (100%) diff --git a/applications/external/.mass_storage/mass_storage_app_i.h b/applications/external/.mass_storage/mass_storage_app_i.h index 7fc829aba..4db7a40f3 100644 --- a/applications/external/.mass_storage/mass_storage_app_i.h +++ b/applications/external/.mass_storage/mass_storage_app_i.h @@ -16,7 +16,7 @@ #include #include "views/mass_storage_view.h" -#define MASS_STORAGE_APP_PATH_FOLDER "/any/mass_storage" +#define MASS_STORAGE_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX // #define MASS_STORAGE_APP_EXTENSION ".iso" #define MASS_STORAGE_FILE_NAME_LEN 40 diff --git a/applications/external/airmouse/air_mouse.c b/applications/external/airmouse/air_mouse.c index 3bb7253b5..5ca5df21c 100644 --- a/applications/external/airmouse/air_mouse.c +++ b/applications/external/airmouse/air_mouse.c @@ -1,4 +1,5 @@ #include "air_mouse.h" +#include #include @@ -52,6 +53,11 @@ uint32_t air_mouse_exit(void* context) { AirMouse* air_mouse_app_alloc() { AirMouse* app = malloc(sizeof(AirMouse)); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate( + storage, EXT_PATH(".calibration.data"), APP_DATA_PATH("calibration.data")); + furi_record_close(RECORD_STORAGE); + // Gui app->gui = furi_record_open(RECORD_GUI); diff --git a/applications/external/airmouse/tracking/calibration_data.h b/applications/external/airmouse/tracking/calibration_data.h index 7d240775a..522a09a9a 100644 --- a/applications/external/airmouse/tracking/calibration_data.h +++ b/applications/external/airmouse/tracking/calibration_data.h @@ -7,8 +7,7 @@ #include "util/vector.h" #define CALIBRATION_DATA_VER (1) -#define CALIBRATION_DATA_FILE_NAME ".calibration.data" -#define CALIBRATION_DATA_PATH EXT_PATH(CALIBRATION_DATA_FILE_NAME) +#define CALIBRATION_DATA_PATH APP_DATA_PATH("calibration.data") #define CALIBRATION_DATA_MAGIC (0x23) #define CALIBRATION_DATA_SAVE(x) \ diff --git a/applications/external/asteroids/app.c b/applications/external/asteroids/app.c index 495542a24..cd2f50357 100644 --- a/applications/external/asteroids/app.c +++ b/applications/external/asteroids/app.c @@ -28,7 +28,8 @@ #define MAXPOWERUPS 3 /* Max powerups allowed on screen */ #define POWERUPSTTL 400 /* Max powerup time to live, in ticks. */ #define SHIP_HIT_ANIMATION_LEN 15 -#define SAVING_DIRECTORY "/ext/apps/Games" +// Tick runs in thread, cant use APP_DATA_PATH() +#define SAVING_DIRECTORY EXT_PATH("apps_data/asteroids") #define SAVING_FILENAME SAVING_DIRECTORY "/game_asteroids.save" #ifndef PI #define PI 3.14159265358979f @@ -1146,6 +1147,8 @@ void game_tick(void* ctx) { bool load_game(AsteroidsApp* app) { Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_mkdir(storage, SAVING_DIRECTORY); + storage_common_migrate(storage, EXT_PATH("apps/Games/game_asteroids.save"), SAVING_FILENAME); File* file = storage_file_alloc(storage); uint16_t bytes_readed = 0; @@ -1163,12 +1166,6 @@ bool load_game(AsteroidsApp* app) { void save_game(AsteroidsApp* app) { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { - return; - } - } - File* file = storage_file_alloc(storage); if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { storage_file_write(file, app, sizeof(AsteroidsApp)); diff --git a/applications/external/blackjack/util.c b/applications/external/blackjack/util.c index 8e88c2231..c1cb01c13 100644 --- a/applications/external/blackjack/util.c +++ b/applications/external/blackjack/util.c @@ -1,7 +1,7 @@ #include #include "util.h" -const char* CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings"); +const char* CONFIG_FILE_PATH = APP_DATA_PATH("blackjack.settings"); void save_settings(Settings settings) { Storage* storage = furi_record_open(RECORD_STORAGE); @@ -59,6 +59,7 @@ Settings load_settings() { FURI_LOG_D(APP_NAME, "Opening storage"); Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH(".blackjack.settings"), CONFIG_FILE_PATH); FURI_LOG_D(APP_NAME, "Allocating file"); FlipperFormat* file = flipper_format_file_alloc(storage); @@ -120,4 +121,4 @@ Settings load_settings() { flipper_format_free(file); furi_record_close(RECORD_STORAGE); return settings; -} \ No newline at end of file +} diff --git a/applications/external/brainfuck/brainfuck.c b/applications/external/brainfuck/brainfuck.c index 4577de68b..84a810eaf 100644 --- a/applications/external/brainfuck/brainfuck.c +++ b/applications/external/brainfuck/brainfuck.c @@ -137,7 +137,7 @@ int32_t brainfuck_app(void* p) { } Storage* storage = furi_record_open(RECORD_STORAGE); - storage_simply_mkdir(storage, "/ext/brainfuck"); + storage_common_migrate(storage, EXT_PATH("brainfuck"), STORAGE_APP_DATA_PATH_PREFIX); scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneStart); @@ -146,4 +146,4 @@ int32_t brainfuck_app(void* p) { brainfuck_free(brainfuck); return 0; -} \ No newline at end of file +} diff --git a/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c b/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c index 9f8885977..96df831c9 100644 --- a/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c +++ b/applications/external/brainfuck/scenes/brainfuck_scene_file_create.c @@ -25,7 +25,7 @@ bool brainfuck_scene_file_create_on_event(void* context, SceneManagerEvent event bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == brainfuckCustomEventTextInputDone) { - furi_string_cat_printf(app->BF_file_path, "/ext/brainfuck/%s.b", tmpName); + furi_string_cat_printf(app->BF_file_path, APP_DATA_PATH("%s.b"), tmpName); //remove old file Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c b/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c index 33c06ee81..10692b32f 100644 --- a/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c +++ b/applications/external/brainfuck/scenes/brainfuck_scene_file_select.c @@ -6,11 +6,11 @@ void brainfuck_scene_file_select_on_enter(void* context) { DialogsApp* dialogs = furi_record_open("dialogs"); FuriString* path; path = furi_string_alloc(); - furi_string_set(path, "/ext/brainfuck"); + furi_string_set(path, STORAGE_APP_DATA_PATH_PREFIX); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, ".b", &I_bfico); - browser_options.base_path = "/ext/brainfuck"; + browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; browser_options.hide_ext = false; bool selected = dialog_file_browser_show(dialogs, path, path, &browser_options); diff --git a/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h b/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h index 62b858cea..bd1fb3071 100644 --- a/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/esp32cam_marauder_companion/wifi_marauder_app_i.h @@ -25,7 +25,7 @@ #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) #define MARAUDER_APP_FOLDER_USER "apps_data/marauder" -#define MARAUDER_APP_FOLDER ANY_PATH(MARAUDER_APP_FOLDER_USER) +#define MARAUDER_APP_FOLDER EXT_PATH(MARAUDER_APP_FOLDER_USER) #define MARAUDER_APP_FOLDER_PCAPS MARAUDER_APP_FOLDER "/pcaps" #define MARAUDER_APP_FOLDER_LOGS MARAUDER_APP_FOLDER "/logs" #define MARAUDER_APP_FOLDER_USER_PCAPS MARAUDER_APP_FOLDER_USER "/pcaps" diff --git a/applications/external/flizzer_tracker/flizzer_tracker.c b/applications/external/flizzer_tracker/flizzer_tracker.c index 2c0729a43..ed696cdc6 100644 --- a/applications/external/flizzer_tracker/flizzer_tracker.c +++ b/applications/external/flizzer_tracker/flizzer_tracker.c @@ -99,10 +99,7 @@ int32_t flizzer_tracker_app(void* p) { UNUSED(p); Storage* storage = furi_record_open(RECORD_STORAGE); - bool st = storage_simply_mkdir(storage, APPSDATA_FOLDER); - st = storage_simply_mkdir(storage, FLIZZER_TRACKER_FOLDER); - st = storage_simply_mkdir(storage, FLIZZER_TRACKER_INSTRUMENTS_FOLDER); - UNUSED(st); + storage_simply_mkdir(storage, FLIZZER_TRACKER_INSTRUMENTS_FOLDER); furi_record_close(RECORD_STORAGE); FlizzerTrackerApp* tracker = init_tracker(44100, 50, true, 1024); @@ -215,4 +212,4 @@ int32_t flizzer_tracker_app(void* p) { deinit_tracker(tracker); return 0; -} \ No newline at end of file +} diff --git a/applications/external/flizzer_tracker/flizzer_tracker.h b/applications/external/flizzer_tracker/flizzer_tracker.h index 97269a98e..6ece14e20 100644 --- a/applications/external/flizzer_tracker/flizzer_tracker.h +++ b/applications/external/flizzer_tracker/flizzer_tracker.h @@ -22,9 +22,8 @@ #include "sound_engine/sound_engine_filter.h" #include "tracker_engine/tracker_engine_defs.h" -#define APPSDATA_FOLDER "/ext/apps_data" -#define FLIZZER_TRACKER_FOLDER "/ext/apps_data/flizzer_tracker" -#define FLIZZER_TRACKER_INSTRUMENTS_FOLDER "/ext/apps_data/flizzer_tracker/instruments" +#define FLIZZER_TRACKER_FOLDER STORAGE_APP_DATA_PATH_PREFIX +#define FLIZZER_TRACKER_INSTRUMENTS_FOLDER APP_DATA_PATH("instruments") #define FILE_NAME_LEN 64 typedef enum { @@ -223,4 +222,4 @@ typedef struct { } TrackerViewModel; void draw_callback(Canvas* canvas, void* ctx); -bool input_callback(InputEvent* input_event, void* ctx); \ No newline at end of file +bool input_callback(InputEvent* input_event, void* ctx); diff --git a/applications/external/game15/game15.c b/applications/external/game15/game15.c index c87265719..32d47b944 100644 --- a/applications/external/game15/game15.c +++ b/applications/external/game15/game15.c @@ -11,8 +11,7 @@ #define CELL_HEIGHT 8 #define MOVE_TICKS 5 #define KEY_STACK_SIZE 16 -#define SAVING_DIRECTORY "/ext/apps/Games" -#define SAVING_FILENAME SAVING_DIRECTORY "/game15.save" +#define SAVING_FILENAME APP_DATA_PATH("game15.save") #define POPUP_MENU_ITEMS 2 typedef enum { @@ -116,8 +115,9 @@ static int key_stack_push(uint8_t value) { static bool storage_game_state_load() { Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); + storage_common_migrate(storage, EXT_PATH("apps/Games/game15.save"), SAVING_FILENAME); + File* file = storage_file_alloc(storage); uint16_t bytes_readed = 0; if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) bytes_readed = storage_file_read(file, &game_state, sizeof(game_state_t)); @@ -130,12 +130,6 @@ static bool storage_game_state_load() { static void storage_game_state_save() { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { - return; - } - } - File* file = storage_file_alloc(storage); if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { storage_file_write(file, &game_state, sizeof(game_state_t)); diff --git a/applications/external/game_2048/game_2048.c b/applications/external/game_2048/game_2048.c index 71c68ad48..430bc5b5d 100644 --- a/applications/external/game_2048/game_2048.c +++ b/applications/external/game_2048/game_2048.c @@ -23,8 +23,7 @@ #define FRAME_TOP 1 #define FRAME_SIZE 61 -#define SAVING_DIRECTORY "/ext/apps/Games" -#define SAVING_FILENAME SAVING_DIRECTORY "/game_2048.save" +#define SAVING_FILENAME APP_DATA_PATH("game_2048.save") typedef enum { GameStateMenu, @@ -300,6 +299,7 @@ void init_game(GameState* const game_state, bool clear_top_score) { bool load_game(GameState* game_state) { Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("apps/Games/game_2048.save"), SAVING_FILENAME); File* file = storage_file_alloc(storage); uint16_t bytes_readed = 0; @@ -317,12 +317,6 @@ bool load_game(GameState* game_state) { void save_game(GameState* game_state) { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { - return; - } - } - File* file = storage_file_alloc(storage); if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { storage_file_write(file, game_state, sizeof(GameState)); diff --git a/applications/external/hex_editor/hex_editor.c b/applications/external/hex_editor/hex_editor.c index 5e411a400..8a4d86c93 100644 --- a/applications/external/hex_editor/hex_editor.c +++ b/applications/external/hex_editor/hex_editor.c @@ -194,7 +194,7 @@ int32_t hex_editor_app(void* p) { if(p && strlen(p)) { furi_string_set(file_path, (const char*)p); } else { - furi_string_set(file_path, "/any"); + furi_string_set(file_path, EXT_PATH("")); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, "*", &I_edit_10px); diff --git a/applications/external/hex_viewer/hex_viewer.c b/applications/external/hex_viewer/hex_viewer.c index bd4afa1a8..c4c7e75ae 100644 --- a/applications/external/hex_viewer/hex_viewer.c +++ b/applications/external/hex_viewer/hex_viewer.c @@ -13,7 +13,7 @@ #define TAG "HexViewer" -#define HEX_VIEWER_APP_PATH_FOLDER "/any" +#define HEX_VIEWER_APP_PATH_FOLDER EXT_PATH("") #define HEX_VIEWER_APP_EXTENSION "*" #define HEX_VIEWER_BYTES_PER_LINE 4u diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index 0c6559e0c..7b136e63f 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -428,7 +428,7 @@ int32_t hid_ble_app(void* p) { // Migrate data from old sd-card folder Storage* storage = furi_record_open(RECORD_STORAGE); - storage_common_rename( + storage_common_migrate( storage, EXT_PATH("apps/Tools/" HID_BT_KEYS_STORAGE_NAME), APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); diff --git a/applications/external/ifttt/application.fam b/applications/external/ifttt/application.fam index 1a86000a4..195c8a9eb 100644 --- a/applications/external/ifttt/application.fam +++ b/applications/external/ifttt/application.fam @@ -1,5 +1,5 @@ App( - appid="esp8266_ifttt_virtual_button", + appid="ifttt", name="[ESP8266] IFTTT Button", apptype=FlipperAppType.EXTERNAL, entry_point="ifttt_virtual_button_app", diff --git a/applications/external/ifttt/ifttt_virtual_button.c b/applications/external/ifttt/ifttt_virtual_button.c index ba1684daf..01a02b0f3 100644 --- a/applications/external/ifttt/ifttt_virtual_button.c +++ b/applications/external/ifttt/ifttt_virtual_button.c @@ -1,8 +1,7 @@ #include "ifttt_virtual_button.h" -#define IFTTT_FOLDER "/ext/apps_data/ifttt" -#define IFTTT_CONFIG_FOLDER "/ext/apps_data/ifttt/config" -const char* CONFIG_FILE_PATH = "/ext/apps_data/ifttt/config/config.settings"; +#define IFTTT_CONFIG_FOLDER APP_DATA_PATH("config") +const char* CONFIG_FILE_PATH = APP_DATA_PATH("config/config.settings"); #define FLIPPERZERO_SERIAL_BAUD 115200 typedef enum ESerialCommand { ESerialCommand_Config } ESerialCommand; @@ -219,10 +218,7 @@ int32_t ifttt_virtual_button_app(void* p) { UNUSED(p); Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, IFTTT_FOLDER)) { - } - if(!storage_simply_mkdir(storage, IFTTT_CONFIG_FOLDER)) { - } + storage_simply_mkdir(storage, IFTTT_CONFIG_FOLDER); furi_record_close(RECORD_STORAGE); uint32_t first_scene = VirtualButtonAppSceneStart; diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c index 8fc39cf59..0041fbaba 100644 --- a/applications/external/ir_remote/infrared_remote_app.c +++ b/applications/external/ir_remote/infrared_remote_app.c @@ -148,9 +148,9 @@ int32_t infrared_remote_app(void* p) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px); FuriString* map_file = furi_string_alloc(); - furi_string_set(map_file, "/ext/infrared/remote"); - if(!storage_file_exists(storage, ANY_PATH("infrared/remote"))) { - storage_common_mkdir(storage, ANY_PATH("infrared/remote")); //Make Folder If dir not exist + furi_string_set(map_file, EXT_PATH("infrared/remote")); + if(!storage_file_exists(storage, EXT_PATH("infrared/remote"))) { + storage_common_mkdir(storage, EXT_PATH("infrared/remote")); //Make Folder If dir not exist } bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); diff --git a/applications/external/multi_fuzzer/fuzzer.c b/applications/external/multi_fuzzer/fuzzer.c index fd0ec90db..f226fa209 100644 --- a/applications/external/multi_fuzzer/fuzzer.c +++ b/applications/external/multi_fuzzer/fuzzer.c @@ -129,10 +129,10 @@ int32_t fuzzer_start_ibtn(void* p) { PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); FuzzerConsts app_const = { - .custom_dict_folder = "/ext/ibtnfuzzer", + .custom_dict_folder = EXT_PATH("ibutton_fuzzer"), .custom_dict_extension = ".txt", .key_extension = ".ibtn", - .path_key_folder = "/ext/ibutton", + .path_key_folder = EXT_PATH("ibutton"), .key_icon = &I_ibutt_10px, }; fuzzer_app->fuzzer_const = &app_const; @@ -148,10 +148,10 @@ int32_t fuzzer_start_rfid(void* p) { PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); FuzzerConsts app_const = { - .custom_dict_folder = "/ext/rfidfuzzer", + .custom_dict_folder = EXT_PATH("rfid_fuzzer"), .custom_dict_extension = ".txt", .key_extension = ".rfid", - .path_key_folder = "/ext/lfrfid", + .path_key_folder = EXT_PATH("lfrfid"), .key_icon = &I_125_10px, }; fuzzer_app->fuzzer_const = &app_const; diff --git a/applications/external/music_beeper/music_beeper.c b/applications/external/music_beeper/music_beeper.c index ed3021fa4..15eba821b 100644 --- a/applications/external/music_beeper/music_beeper.c +++ b/applications/external/music_beeper/music_beeper.c @@ -10,7 +10,7 @@ #define TAG "MusicBeeper" -#define MUSIC_BEEPER_APP_PATH_FOLDER ANY_PATH("music_player") +#define MUSIC_BEEPER_APP_PATH_FOLDER EXT_PATH("music_player") #define MUSIC_BEEPER_APP_EXTENSION "*" #define MUSIC_BEEPER_SEMITONE_HISTORY_SIZE 4 diff --git a/applications/external/music_player/music_player.c b/applications/external/music_player/music_player.c index 55eddf3a6..181eb60d6 100644 --- a/applications/external/music_player/music_player.c +++ b/applications/external/music_player/music_player.c @@ -10,7 +10,7 @@ #define TAG "MusicPlayer" -#define MUSIC_PLAYER_APP_PATH_FOLDER ANY_PATH("music_player") +#define MUSIC_PLAYER_APP_PATH_FOLDER EXT_PATH("music_player") #define MUSIC_PLAYER_APP_EXTENSION "*" #define MUSIC_PLAYER_SEMITONE_HISTORY_SIZE 4 diff --git a/applications/external/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h index f0c18db1a..e7875989f 100644 --- a/applications/external/nfc_magic/nfc_magic_i.h +++ b/applications/external/nfc_magic/nfc_magic_i.h @@ -32,7 +32,7 @@ #include "nfc_magic_icons.h" #include -#define NFC_APP_FOLDER ANY_PATH("nfc") +#define NFC_APP_FOLDER EXT_PATH("nfc") enum NfcMagicCustomEvent { // Reserve first 100 events for button types and indexes, starting from 0 diff --git a/applications/external/nrf24_batch/LICENSE b/applications/external/nrf24batch/LICENSE similarity index 100% rename from applications/external/nrf24_batch/LICENSE rename to applications/external/nrf24batch/LICENSE diff --git a/applications/external/nrf24_batch/application.fam b/applications/external/nrf24batch/application.fam similarity index 94% rename from applications/external/nrf24_batch/application.fam rename to applications/external/nrf24batch/application.fam index 3d09a2e7f..ff8e5546e 100644 --- a/applications/external/nrf24_batch/application.fam +++ b/applications/external/nrf24batch/application.fam @@ -1,5 +1,5 @@ App( - appid="nrf24_batch", + appid="nrf24batch", name="[NRF24] Batch", apptype=FlipperAppType.EXTERNAL, entry_point="nrf24batch_app", diff --git a/applications/external/nrf24_batch/lib/nrf24/nrf24.c b/applications/external/nrf24batch/lib/nrf24/nrf24.c similarity index 100% rename from applications/external/nrf24_batch/lib/nrf24/nrf24.c rename to applications/external/nrf24batch/lib/nrf24/nrf24.c diff --git a/applications/external/nrf24_batch/lib/nrf24/nrf24.h b/applications/external/nrf24batch/lib/nrf24/nrf24.h similarity index 100% rename from applications/external/nrf24_batch/lib/nrf24/nrf24.h rename to applications/external/nrf24batch/lib/nrf24/nrf24.h diff --git a/applications/external/nrf24_batch/nrf24batch.c b/applications/external/nrf24batch/nrf24batch.c similarity index 99% rename from applications/external/nrf24_batch/nrf24batch.c rename to applications/external/nrf24batch/nrf24batch.c index 047504105..a41de8210 100644 --- a/applications/external/nrf24_batch/nrf24batch.c +++ b/applications/external/nrf24batch/nrf24batch.c @@ -16,7 +16,7 @@ #define TAG "nrf24batch" #define VERSION "1.9" -#define SCAN_APP_PATH_FOLDER "/ext/nrf24batch" +#define SCAN_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define LOG_FILEEXT ".txt" #define NRF_READ_TIMEOUT 300UL // ms #define WORK_PERIOD 2 // ms, Timer period @@ -1510,6 +1510,7 @@ int32_t nrf24batch_app(void* p) { gui_add_view_port(APP->gui, view_port, GuiLayerFullscreen); APP->notification = furi_record_open(RECORD_NOTIFICATION); APP->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(APP->storage, EXT_PATH("nrf24batch"), SCAN_APP_PATH_FOLDER); storage_common_mkdir(APP->storage, SCAN_APP_PATH_FOLDER); file_stream = file_stream_alloc(APP->storage); FuriTimer* work_timer = diff --git a/applications/external/nrf24_batch/nrf24batch.h b/applications/external/nrf24batch/nrf24batch.h similarity index 100% rename from applications/external/nrf24_batch/nrf24batch.h rename to applications/external/nrf24batch/nrf24batch.h diff --git a/applications/external/nrf24_batch/nrf24batch_10px.png b/applications/external/nrf24batch/nrf24batch_10px.png similarity index 100% rename from applications/external/nrf24_batch/nrf24batch_10px.png rename to applications/external/nrf24batch/nrf24batch_10px.png diff --git a/applications/external/mousejacker/application.fam b/applications/external/nrf24mousejacker/application.fam similarity index 94% rename from applications/external/mousejacker/application.fam rename to applications/external/nrf24mousejacker/application.fam index 04c3b800b..f5a9b9dbc 100644 --- a/applications/external/mousejacker/application.fam +++ b/applications/external/nrf24mousejacker/application.fam @@ -1,5 +1,5 @@ App( - appid="nrf24_mouse_jacker", + appid="nrf24mousejacker", name="[NRF24] Mouse Jacker", apptype=FlipperAppType.EXTERNAL, entry_point="mousejacker_app", diff --git a/applications/external/mousejacker/lib/nrf24/nrf24.c b/applications/external/nrf24mousejacker/lib/nrf24/nrf24.c similarity index 100% rename from applications/external/mousejacker/lib/nrf24/nrf24.c rename to applications/external/nrf24mousejacker/lib/nrf24/nrf24.c diff --git a/applications/external/mousejacker/lib/nrf24/nrf24.h b/applications/external/nrf24mousejacker/lib/nrf24/nrf24.h similarity index 100% rename from applications/external/mousejacker/lib/nrf24/nrf24.h rename to applications/external/nrf24mousejacker/lib/nrf24/nrf24.h diff --git a/applications/external/mousejacker/mouse_10px.png b/applications/external/nrf24mousejacker/mouse_10px.png similarity index 100% rename from applications/external/mousejacker/mouse_10px.png rename to applications/external/nrf24mousejacker/mouse_10px.png diff --git a/applications/external/mousejacker/mousejacker.c b/applications/external/nrf24mousejacker/mousejacker.c similarity index 98% rename from applications/external/mousejacker/mousejacker.c rename to applications/external/nrf24mousejacker/mousejacker.c index 92ec1df07..31ed1bcab 100644 --- a/applications/external/mousejacker/mousejacker.c +++ b/applications/external/nrf24mousejacker/mousejacker.c @@ -15,10 +15,10 @@ #define TAG "mousejacker" #define LOGITECH_MAX_CHANNEL 85 -#define NRFSNIFF_APP_PATH_FOLDER "/ext/nrfsniff" +#define NRFSNIFF_APP_PATH_FOLDER EXT_PATH("apps_data/nrf24sniff") #define NRFSNIFF_APP_PATH_EXTENSION ".txt" #define NRFSNIFF_APP_FILENAME "addresses.txt" -#define MOUSEJACKER_APP_PATH_FOLDER "/ext/mousejacker" +#define MOUSEJACKER_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define MOUSEJACKER_APP_PATH_EXTENSION ".txt" #define MAX_ADDRS 100 @@ -309,6 +309,8 @@ int32_t mousejacker_app(void* p) { gui_add_view_port(gui, view_port, GuiLayerFullscreen); plugin_state->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate( + plugin_state->storage, EXT_PATH("mousejacker"), MOUSEJACKER_APP_PATH_FOLDER); storage_common_mkdir(plugin_state->storage, MOUSEJACKER_APP_PATH_FOLDER); plugin_state->file_stream = file_stream_alloc(plugin_state->storage); diff --git a/applications/external/mousejacker/mousejacker_ducky.c b/applications/external/nrf24mousejacker/mousejacker_ducky.c similarity index 100% rename from applications/external/mousejacker/mousejacker_ducky.c rename to applications/external/nrf24mousejacker/mousejacker_ducky.c diff --git a/applications/external/mousejacker/mousejacker_ducky.h b/applications/external/nrf24mousejacker/mousejacker_ducky.h similarity index 100% rename from applications/external/mousejacker/mousejacker_ducky.h rename to applications/external/nrf24mousejacker/mousejacker_ducky.h diff --git a/applications/external/nrf24scan/application.fam b/applications/external/nrf24scan/application.fam index 1e7bd3efe..1be252f7c 100644 --- a/applications/external/nrf24scan/application.fam +++ b/applications/external/nrf24scan/application.fam @@ -1,5 +1,5 @@ App( - appid="nrf24_scanner", + appid="nrf24scan", name="[NRF24] Scanner", apptype=FlipperAppType.EXTERNAL, entry_point="nrf24scan_app", diff --git a/applications/external/nrf24scan/nrf24scan.c b/applications/external/nrf24scan/nrf24scan.c index fbfbbafa7..0bd4b6b3b 100644 --- a/applications/external/nrf24scan/nrf24scan.c +++ b/applications/external/nrf24scan/nrf24scan.c @@ -19,7 +19,7 @@ #define MAX_CHANNEL 125 #define MAX_ADDR 6 -#define SCAN_APP_PATH_FOLDER "/ext/nrf24scan" +#define SCAN_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define SETTINGS_FILENAME "addresses.txt" // Settings file format (1 parameter per line): // SNIFF - if present then sniff mode // Rate: 0/1/2 - rate in Mbps (=0.25/1/2) @@ -1373,6 +1373,7 @@ int32_t nrf24scan_app(void* p) { gui_add_view_port(APP->gui, APP->view_port, GuiLayerFullscreen); APP->notification = furi_record_open(RECORD_NOTIFICATION); APP->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(APP->storage, EXT_PATH("nrf24scan"), SCAN_APP_PATH_FOLDER); storage_common_mkdir(APP->storage, SCAN_APP_PATH_FOLDER); Stream* file_stream = file_stream_alloc(APP->storage); FuriString* path = furi_string_alloc(); diff --git a/applications/external/nrfsniff/application.fam b/applications/external/nrf24sniff/application.fam similarity index 95% rename from applications/external/nrfsniff/application.fam rename to applications/external/nrf24sniff/application.fam index 6a763531d..244677e38 100644 --- a/applications/external/nrfsniff/application.fam +++ b/applications/external/nrf24sniff/application.fam @@ -1,5 +1,5 @@ App( - appid="nrf24_sniffer", + appid="nrf24sniff", name="[NRF24] Sniffer", apptype=FlipperAppType.EXTERNAL, entry_point="nrfsniff_app", diff --git a/applications/external/nrfsniff/lib/nrf24/nrf24.c b/applications/external/nrf24sniff/lib/nrf24/nrf24.c similarity index 100% rename from applications/external/nrfsniff/lib/nrf24/nrf24.c rename to applications/external/nrf24sniff/lib/nrf24/nrf24.c diff --git a/applications/external/nrfsniff/lib/nrf24/nrf24.h b/applications/external/nrf24sniff/lib/nrf24/nrf24.h similarity index 100% rename from applications/external/nrfsniff/lib/nrf24/nrf24.h rename to applications/external/nrf24sniff/lib/nrf24/nrf24.h diff --git a/applications/external/nrfsniff/nrfsniff.c b/applications/external/nrf24sniff/nrfsniff.c similarity index 99% rename from applications/external/nrfsniff/nrfsniff.c rename to applications/external/nrf24sniff/nrfsniff.c index 822d151c1..9d5fffe80 100644 --- a/applications/external/nrfsniff/nrfsniff.c +++ b/applications/external/nrf24sniff/nrfsniff.c @@ -15,7 +15,7 @@ #define MAX_ADDRS 100 #define MAX_CONFIRMED 32 -#define NRFSNIFF_APP_PATH_FOLDER "/ext/nrfsniff" +#define NRFSNIFF_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define NRFSNIFF_APP_FILENAME "addresses.txt" #define TAG "nrfsniff" @@ -343,6 +343,7 @@ int32_t nrfsniff_app(void* p) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("nrfsniff"), NRFSNIFF_APP_PATH_FOLDER); storage_common_mkdir(storage, NRFSNIFF_APP_PATH_FOLDER); PluginEvent event; diff --git a/applications/external/nrfsniff/nrfsniff_10px.png b/applications/external/nrf24sniff/nrfsniff_10px.png similarity index 100% rename from applications/external/nrfsniff/nrfsniff_10px.png rename to applications/external/nrf24sniff/nrfsniff_10px.png diff --git a/applications/external/picopass/picopass.c b/applications/external/picopass/picopass.c index 8f7dd2f51..52ceab08a 100644 --- a/applications/external/picopass/picopass.c +++ b/applications/external/picopass/picopass.c @@ -201,7 +201,7 @@ void picopass_show_loading_popup(void* context, bool show) { static void picopass_migrate_from_old_folder() { Storage* storage = furi_record_open(RECORD_STORAGE); - storage_common_migrate(storage, "/ext/picopass", STORAGE_APP_DATA_PATH_PREFIX); + storage_common_migrate(storage, EXT_PATH("picopass"), STORAGE_APP_DATA_PATH_PREFIX); furi_record_close(RECORD_STORAGE); } diff --git a/applications/external/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c index a050e4a2c..c4cb6aed0 100644 --- a/applications/external/picopass/picopass_device.c +++ b/applications/external/picopass/picopass_device.c @@ -128,7 +128,7 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) { return picopass_device_save_file( dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, PICOPASS_APP_EXTENSION, true); } else if(dev->format == PicopassDeviceSaveFormatLF) { - return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true); + return picopass_device_save_file(dev, dev_name, EXT_PATH("lfrfid"), ".rfid", true); } return false; diff --git a/applications/external/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h index a07032f4a..96a586a93 100644 --- a/applications/external/picopass/picopass_device.h +++ b/applications/external/picopass/picopass_device.h @@ -38,7 +38,7 @@ // Read Access, 1 meanns anonymous read enabled, 0 means must auth to read applicaion #define PICOPASS_FUSE_RA 0x01 -#define PICOPASS_APP_FOLDER ANY_PATH("picopass") +#define PICOPASS_APP_FOLDER EXT_PATH("picopass") #define PICOPASS_APP_EXTENSION ".picopass" #define PICOPASS_APP_SHADOW_EXTENSION ".pas" diff --git a/applications/external/qrcode/qrcode_app.c b/applications/external/qrcode/qrcode_app.c index 056f04eb9..c605c249e 100644 --- a/applications/external/qrcode/qrcode_app.c +++ b/applications/external/qrcode/qrcode_app.c @@ -11,7 +11,7 @@ #include "qrcode.h" #define TAG "qrcode" -#define QRCODE_FOLDER ANY_PATH("qrcodes") +#define QRCODE_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define QRCODE_EXTENSION ".qrcode" #define QRCODE_FILETYPE "QRCode" #define QRCODE_FILE_VERSION 0 @@ -518,6 +518,9 @@ static void qrcode_app_free(QRCodeApp* instance) { int32_t qrcode_app(void* p) { QRCodeApp* instance = qrcode_app_alloc(); FuriString* file_path = furi_string_alloc(); + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("qrcodes"), QRCODE_FOLDER); + furi_record_close(RECORD_STORAGE); do { if(p && strlen(p)) { diff --git a/applications/external/reversi/game_reversi.c b/applications/external/reversi/game_reversi.c index 8e42a9ad4..9b0802706 100644 --- a/applications/external/reversi/game_reversi.c +++ b/applications/external/reversi/game_reversi.c @@ -12,8 +12,7 @@ #define FRAME_TOP 3 #define FRAME_CELL_SIZE 7 -#define SAVING_DIRECTORY "/ext/apps/Games" -#define SAVING_FILENAME SAVING_DIRECTORY "/game_reversi.save" +#define SAVING_FILENAME APP_DATA_PATH("game_reversi.save") typedef enum { AppScreenGame, AppScreenMenu } AppScreen; @@ -171,6 +170,7 @@ static void gray_canvas(Canvas* const canvas) { bool load_game(GameState* game_state) { Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("apps/Games/game_reversi.save"), SAVING_FILENAME); File* file = storage_file_alloc(storage); uint16_t bytes_readed = 0; @@ -188,12 +188,6 @@ bool load_game(GameState* game_state) { void save_game(const GameState* game_state) { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { - return; - } - } - File* file = storage_file_alloc(storage); if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { storage_file_write(file, game_state, sizeof(GameState)); diff --git a/applications/external/simonsays/simon_says.c b/applications/external/simonsays/simon_says.c index ad6a68129..724b1e607 100644 --- a/applications/external/simonsays/simon_says.c +++ b/applications/external/simonsays/simon_says.c @@ -22,8 +22,7 @@ #define BOARD_X 72 // Used for board placement #define BOARD_Y 8 #define GAME_START_LIVES 3 -#define SAVING_DIRECTORY "/ext/apps/Games" -#define SAVING_FILENAME SAVING_DIRECTORY "/game_simon_says.save" +#define SAVING_FILENAME APP_DATA_PATH("game_simon_says.save") // Define Notes // Shamelessly stolen from Ocarina application @@ -372,6 +371,7 @@ void simon_input_callback(InputEvent* input_event, void* ctx) { bool load_game(SimonData* app) { Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("apps/Games/game_simon_says.save"), SAVING_FILENAME); File* file = storage_file_alloc(storage); @@ -395,12 +395,6 @@ bool load_game(SimonData* app) { void save_game(SimonData* app) { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { - return; - } - } - File* file = storage_file_alloc(storage); if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { storage_file_write(file, app, sizeof(SimonData)); diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c index 8aae1bcad..13fc85909 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c @@ -20,7 +20,7 @@ void subbrute_scene_load_file_on_enter(void* context) { // TODO: DELETE IT #ifdef SUBBRUTE_FAST_TRACK bool res = true; - furi_string_printf(load_path, "%s", "/ext/subghz/princeton.sub"); + furi_string_printf(load_path, "%s", EXT_PATH("subghz/princeton.sub")); #else bool res = dialog_file_browser_show(instance->dialogs, load_path, app_folder, &browser_options); @@ -88,4 +88,4 @@ bool subbrute_scene_load_file_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); return false; -} \ No newline at end of file +} diff --git a/applications/external/subghz_playlist/playlist.c b/applications/external/subghz_playlist/playlist.c index 6e0fdf932..4ada9aa72 100644 --- a/applications/external/subghz_playlist/playlist.c +++ b/applications/external/subghz_playlist/playlist.c @@ -22,7 +22,7 @@ #include "playlist_file.h" #include "canvas_helper.h" -#define PLAYLIST_FOLDER "/ext/subghz/playlist" +#define PLAYLIST_FOLDER EXT_PATH("subghz/playlist") #define PLAYLIST_EXT ".txt" #define TAG "Playlist" diff --git a/applications/external/subghz_remote/subghz_remote_app.c b/applications/external/subghz_remote/subghz_remote_app.c index 4fcd19d34..4f88f6358 100644 --- a/applications/external/subghz_remote/subghz_remote_app.c +++ b/applications/external/subghz_remote/subghz_remote_app.c @@ -23,6 +23,7 @@ SubGhzRemoteApp* subghz_remote_app_alloc() { SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp)); Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER); storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREM_APP_FOLDER); if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) { diff --git a/applications/external/subghz_remote_configurator/subghz_remote_app.c b/applications/external/subghz_remote_configurator/subghz_remote_app.c index 84100a233..73408b739 100644 --- a/applications/external/subghz_remote_configurator/subghz_remote_app.c +++ b/applications/external/subghz_remote_configurator/subghz_remote_app.c @@ -23,6 +23,7 @@ SubGhzRemoteApp* subghz_remote_app_alloc() { Storage* storage = furi_record_open(RECORD_STORAGE); storage_common_migrate(storage, EXT_PATH("unirf"), SUBREM_APP_FOLDER); + storage_common_migrate(storage, EXT_PATH("subghz/unirf"), SUBREM_APP_FOLDER); if(!storage_simply_mkdir(storage, SUBREM_APP_FOLDER)) { //FURI_LOG_E(TAG, "Could not create folder %s", SUBREM_APP_FOLDER); diff --git a/applications/external/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c index 3e15cd02c..c53500ae4 100644 --- a/applications/external/swd_probe/swd_probe_app.c +++ b/applications/external/swd_probe/swd_probe_app.c @@ -2927,10 +2927,10 @@ static bool swd_message_process(AppFSM* ctx) { break; case ModePageScan: { - FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd_scripts")); + FuriString* result_path = furi_string_alloc_printf(EXT_PATH("swd_scripts")); FuriString* preselected = furi_string_alloc_printf( (strlen(ctx->script_detected) > 0) ? ctx->script_detected : - ANY_PATH("swd_scripts")); + EXT_PATH("swd_scripts")); DialogsFileBrowserOptions options; dialog_file_browser_set_basic_options(&options, "swd", &I_swd); @@ -3000,10 +3000,10 @@ static bool swd_message_process(AppFSM* ctx) { } } else if((ctx->mode_page == ModePageScan) || (ctx->mode_page == ModePageFound)) { uint32_t mode_page = ctx->mode_page; - FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd_scripts")); + FuriString* result_path = furi_string_alloc_printf(EXT_PATH("swd_scripts")); FuriString* preselected = furi_string_alloc_printf( (strlen(ctx->script_detected) > 0) ? ctx->script_detected : - ANY_PATH("swd_scripts")); + EXT_PATH("swd_scripts")); DialogsFileBrowserOptions options; dialog_file_browser_set_basic_options(&options, "swd", &I_swd); @@ -3129,7 +3129,7 @@ int32_t swd_probe_app_main(void* p) { notification_message(app->notification, &sequence_display_backlight_enforce_on); DBGS("swd_execute_script"); - swd_execute_script(app, ANY_PATH("swd_scripts/startup.swd")); + swd_execute_script(app, EXT_PATH("swd_scripts/startup.swd")); // dolphin_deed(DolphinDeedPluginGameStart); diff --git a/applications/external/tama_p1/tama.h b/applications/external/tama_p1/tama.h index e8eecc945..7cbcbcfb1 100644 --- a/applications/external/tama_p1/tama.h +++ b/applications/external/tama_p1/tama.h @@ -4,14 +4,14 @@ #include "tamalib/tamalib.h" #define TAG "TamaP1" -#define TAMA_ROM_PATH EXT_PATH("tama_p1/rom.bin") +#define TAMA_ROM_PATH APP_DATA_PATH("rom.bin") #define TAMA_SCREEN_SCALE_FACTOR 2 #define TAMA_LCD_ICON_SIZE 14 #define TAMA_LCD_ICON_MARGIN 1 #define STATE_FILE_MAGIC "TLST" #define STATE_FILE_VERSION 2 -#define TAMA_SAVE_PATH EXT_PATH("tama_p1/save.bin") +#define TAMA_SAVE_PATH APP_DATA_PATH("save.bin") typedef struct { FuriThread* thread; diff --git a/applications/external/tama_p1/tama_p1.c b/applications/external/tama_p1/tama_p1.c index 93db6775d..f098aa157 100644 --- a/applications/external/tama_p1/tama_p1.c +++ b/applications/external/tama_p1/tama_p1.c @@ -720,6 +720,7 @@ static void tama_p1_init(TamaApp* const ctx) { // Load ROM Storage* storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("tama_p1"), STORAGE_APP_DATA_PATH_PREFIX); FileInfo fi; if(storage_common_stat(storage, TAMA_ROM_PATH, &fi) == FSE_OK) { File* rom_file = storage_file_alloc(storage); diff --git a/applications/external/text2sam/sam_app.cpp b/applications/external/text2sam/sam_app.cpp index e28e9e1bc..3c7e42a74 100644 --- a/applications/external/text2sam/sam_app.cpp +++ b/applications/external/text2sam/sam_app.cpp @@ -14,7 +14,7 @@ #include "stm32_sam.h" #define TAG "SAM" -#define SAM_SAVE_PATH EXT_PATH("sam.txt") +#define SAM_SAVE_PATH APP_DATA_PATH("message.txt") #define TEXT_BUFFER_SIZE 256 STM32SAM voice; @@ -89,6 +89,7 @@ static void save_message(FuriString* save_string) { static bool load_messages() { Storage* storage = (Storage*)furi_record_open(RECORD_STORAGE); + storage_common_migrate(storage, EXT_PATH("sam.txt"), SAM_SAVE_PATH); File* file = storage_file_alloc(storage); uint16_t bytes_read = 0; if(storage_file_open(file, SAM_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { diff --git a/applications/external/text_viewer/text_viewer.c b/applications/external/text_viewer/text_viewer.c index b5ccb6ef3..7b742be7f 100644 --- a/applications/external/text_viewer/text_viewer.c +++ b/applications/external/text_viewer/text_viewer.c @@ -13,7 +13,7 @@ #define TAG "TextViewer" -#define TEXT_VIEWER_APP_PATH_FOLDER ANY_PATH("") +#define TEXT_VIEWER_APP_PATH_FOLDER EXT_PATH("") #define TEXT_VIEWER_APP_EXTENSION "*" #define TEXT_VIEWER_BYTES_PER_LINE 20u @@ -282,4 +282,4 @@ int32_t text_viewer_app(void* p) { text_viewer_free(text_viewer); return 0; -} \ No newline at end of file +} diff --git a/applications/external/timelapse/application.fam b/applications/external/timelapse/application.fam index f813b1f8e..47a3e750e 100644 --- a/applications/external/timelapse/application.fam +++ b/applications/external/timelapse/application.fam @@ -1,5 +1,5 @@ App( - appid="gpio_timelapse", + appid="timelapse", name="[GPIO] Timelapse", apptype=FlipperAppType.EXTERNAL, entry_point="zeitraffer_app", diff --git a/applications/external/timelapse/zeitraffer.c b/applications/external/timelapse/zeitraffer.c index 859d256ee..4ffdba5f2 100644 --- a/applications/external/timelapse/zeitraffer.c +++ b/applications/external/timelapse/zeitraffer.c @@ -5,11 +5,10 @@ #include #include #include "gpio_item.h" -#include "gpio_timelapse_icons.h" +#include "timelapse_icons.h" #include -#define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/timelapse" -#define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/timelapse.conf" +#define CONFIG_FILE_PATH APP_DATA_PATH("timelapse.conf") // Часть кода покрадена из https://github.com/zmactep/flipperzero-hello-world @@ -154,10 +153,6 @@ int32_t zeitraffer_app(void* p) { FlipperFormat* load = flipper_format_file_alloc(storage); do { - if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { - notification_message(notifications, &sequence_error); - break; - } if(!flipper_format_file_open_existing(load, CONFIG_FILE_PATH)) { notification_message(notifications, &sequence_error); break; diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c index 33dd3fa88..11e5a602b 100644 --- a/applications/external/unitemp/Sensors.c +++ b/applications/external/unitemp/Sensors.c @@ -397,8 +397,6 @@ bool unitemp_sensors_save(void) { FuriString* filepath = furi_string_alloc(); //Составление пути к файлу furi_string_printf(filepath, "%s/%s", APP_PATH_FOLDER, APP_FILENAME_SENSORS); - //Создание папки плагина - storage_common_mkdir(app->storage, APP_PATH_FOLDER); //Открытие потока if(!file_stream_open( app->file_stream, furi_string_get_cstr(filepath), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { diff --git a/applications/external/unitemp/unitemp.c b/applications/external/unitemp/unitemp.c index 9fa3b7038..483ff9553 100644 --- a/applications/external/unitemp/unitemp.c +++ b/applications/external/unitemp/unitemp.c @@ -74,8 +74,6 @@ bool unitemp_saveSettings(void) { FuriString* filepath = furi_string_alloc(); //Составление пути к файлу furi_string_printf(filepath, "%s/%s", APP_PATH_FOLDER, APP_FILENAME_SETTINGS); - //Создание папки плагина - storage_common_mkdir(app->storage, APP_PATH_FOLDER); //Открытие потока if(!file_stream_open( app->file_stream, furi_string_get_cstr(filepath), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { @@ -224,6 +222,7 @@ static bool unitemp_alloc(void) { //Открытие хранилища (?) app->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(app->storage, EXT_PATH("unitemp"), APP_PATH_FOLDER); //Уведомления app->notifications = furi_record_open(RECORD_NOTIFICATION); diff --git a/applications/external/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h index c2b61b899..d6d163eb8 100644 --- a/applications/external/unitemp/unitemp.h +++ b/applications/external/unitemp/unitemp.h @@ -42,7 +42,7 @@ //Версия приложения #define UNITEMP_APP_VER "1.4" //Путь хранения файлов плагина -#define APP_PATH_FOLDER "/ext/unitemp" +#define APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX //Имя файла с настройками #define APP_FILENAME_SETTINGS "settings.cfg" //Имя файла с датчиками diff --git a/applications/external/wav_player/wav_player.c b/applications/external/wav_player/wav_player.c index 2fa590f9e..14fd8a1e1 100644 --- a/applications/external/wav_player/wav_player.c +++ b/applications/external/wav_player/wav_player.c @@ -16,7 +16,7 @@ #define TAG "WavPlayer" -#define WAVPLAYER_FOLDER "/ext/wav_player" +#define WAVPLAYER_FOLDER EXT_PATH("wav_player") static bool open_wav_stream(Stream* stream) { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); diff --git a/assets/resources/apps_data/barcode_data/codabar_encodings.txt b/assets/resources/apps_data/barcode_data/codabar_encodings.txt new file mode 100644 index 000000000..5f0684cbd --- /dev/null +++ b/assets/resources/apps_data/barcode_data/codabar_encodings.txt @@ -0,0 +1,22 @@ +# alternates between bars and spaces, always begins with bar +# 0 for narrow, 1 for wide +0: 0000011 +1: 0000110 +2: 0001001 +3: 1100000 +4: 0010010 +5: 1000010 +6: 0100001 +7: 0100100 +8: 0110000 +9: 1001000 +-: 0001100 +$: 0011000 +:: 1000101 +/: 1010001 +.: 1010100 ++: 0010101 +A: 0011010 +B: 0101001 +C: 0001011 +D: 0001110 \ No newline at end of file diff --git a/assets/resources/apps_data/barcode_data/code128c_encodings.txt b/assets/resources/apps_data/barcode_data/code128c_encodings.txt new file mode 100644 index 000000000..75cc71135 --- /dev/null +++ b/assets/resources/apps_data/barcode_data/code128c_encodings.txt @@ -0,0 +1,106 @@ +00: 11011001100 +01: 11001101100 +02: 11001100110 +03: 10010011000 +04: 10010001100 +05: 10001001100 +06: 10011001000 +07: 10011000100 +08: 10001100100 +09: 11001001000 +10: 11001000100 +11: 11000100100 +12: 10110011100 +13: 10011011100 +14: 10011001110 +15: 10111001100 +16: 10011101100 +17: 10011100110 +18: 11001110010 +19: 11001011100 +20: 11001001110 +21: 11011100100 +22: 11001110100 +23: 11101101110 +24: 11101001100 +25: 11100101100 +26: 11100100110 +27: 11101100100 +28: 11100110100 +29: 11100110010 +30: 11011011000 +31: 11011000110 +32: 11000110110 +33: 10100011000 +34: 10001011000 +35: 10001000110 +36: 10110001000 +37: 10001101000 +38: 10001100010 +39: 11010001000 +40: 11000101000 +41: 11000100010 +42: 10110111000 +43: 10110001110 +44: 10001101110 +45: 10111011000 +46: 10111000110 +47: 10001110110 +48: 11101110110 +49: 11010001110 +50: 11000101110 +51: 11011101000 +52: 11011100010 +53: 11011101110 +54: 11101011000 +55: 11101000110 +56: 11100010110 +57: 11101101000 +58: 11101100010 +59: 11100011010 +60: 11101111010 +61: 11001000010 +62: 11110001010 +63: 10100110000 +64: 10100001100 +65: 10010110000 +66: 10010000110 +67: 10000101100 +68: 10000100110 +69: 10110010000 +70: 10110000100 +71: 10011010000 +72: 10011000010 +73: 10000110100 +74: 10000110010 +75: 11000010010 +76: 11001010000 +77: 11110111010 +78: 11000010100 +79: 10001111010 +80: 10100111100 +81: 10010111100 +82: 10010011110 +83: 10111100100 +84: 10011110100 +85: 10011110010 +86: 11110100100 +87: 11110010100 +88: 11110010010 +89: 11011011110 +90: 11011110110 +91: 11110110110 +92: 10101111000 +93: 10100011110 +94: 10001011110 +95: 10111101000 +96: 10111100010 +97: 11110101000 +98: 11110100010 +99: 10111011110 +100: 10111101110 +101: 11101011110 +102: 11110101110 +103: 11010000100 +104: 11010010000 +105: 11010011100 diff --git a/assets/resources/apps_data/nrf24batch/CO2_mini.txt b/assets/resources/apps_data/nrf24batch/CO2_mini.txt new file mode 100644 index 000000000..1fb281b41 --- /dev/null +++ b/assets/resources/apps_data/nrf24batch/CO2_mini.txt @@ -0,0 +1,83 @@ +Info: CO2 sensor mini +Address: C8C8CF +Rate: 1 +Ch: 122 +CRC: 2 +DPL: 0 +RETR: 0x0F +Resend: 3 +Delay_ms: 30 +ReadCmd repeat: 3 + +Payload struct: 2,1,1 +ROM=0x41;ROM2=0x42;WROM=0x81;WROM2=0x82;RAM=0x51;RAM2=0x52;WRAM=0x91;WRAM2=0x92;PGM=0x61;SET=0xC0 + +R default: ,,ROM +W default: n,,WROM +Write start: 0,0,0x2F + +Listen: C8C8C1=CO2,Alarm,- + +R: ID*=,0,0x65 + +R: _CO2*2=0x6C,,RAM2 +R: _PORTA=0x39,,RAM +W: _PORTA=,0x3B,WRAM +R: _PORTB=0x36,,RAM +W: _PORTB=,0x38,WRAM +R: _OSCCAL=0x51,,RAM +W: _OSCCAL=,0x51,WRAM +R: OSCCAL_EMEM=0 + +R: RxAddr=1# +W: RxAddr=,1 + +R: Ch=2 +W: Ch=,2 + +R: nRF RETR=3# +W: nRF RETR=,3 + +R: Send period=4 +W: Send period=,4 + +R: CO2 threshold*2=5,,ROM2 +W: CO2 threshold=,5,WROM2 + +R: CO2 correct*2=7,,ROM2 +W: CO2 correct*2=,7,WROM2 + +R: FanLSB[10]=i:9# +W: FanLSB=,i:9 + +R: Transmit pause=19 +W: Transmit pause=,19 + +R: Flags=20# +W: Flags=,20 + +W: Reset=0xEEEE,14,SET + +R: _LED Warning=0x74,,RAM +W: _LED Warning=,0x74,WRAM + +S: LED=,0,SET + +SBatch: LED On: LED=1 +SBatch: LED Off: LED=0 + +RBatch: Settings: ID;RxAddr;Ch;Send period;CO2 threshold;CO2 correct;FanLSB;nRF RETR;Transmit pause;Flags + +WBatch: Default: RxAddr=0xCF;Ch=122;Send period=30;CO2 threshold=1000;CO2 correct=0;FanLSB={0xC1,0,0,0,0,0,0,0};nRF RETR=0x3;Transmit pause=1;Flags=0;Reset +WBatch: CO2: Send period=60;CO2 threshold=1000;CO2 correct=0 +WBatch: Fan: FanLSB={0xC1,0,0,0,0,0,0,0,0,0};Reset +WBatch: RETR: nRF RETR=0x2F;Reset +WBatch: Transmit pause: Transmit pause=30 +WBatch: Flags: Flags=0x00 + +WBatch: LED Warning: _LED Warning=0x30 +WBatch: PORTA: _PORTA=0x0C +WBatch: PORTB: _PORTB=0xC +WBatch: OSCCAL: _OSCCAL=128 + +WBatch: Reset: Reset diff --git a/assets/resources/apps_data/nrf24batch/CO2_mini_old.txt b/assets/resources/apps_data/nrf24batch/CO2_mini_old.txt new file mode 100644 index 000000000..9aaa05db0 --- /dev/null +++ b/assets/resources/apps_data/nrf24batch/CO2_mini_old.txt @@ -0,0 +1,83 @@ +Info: CO2 sensor mini +Address: C8C8CF +Rate: 1 +Ch: 122 +CRC: 2 +DPL: 0 +RETR: 0x0F +Resend: 3 +Delay_ms: 30 +ReadCmd repeat: 3 + +Payload struct: 2,1,1 +EEPROM=0; RAM=1; PROGMEM=2; ID=3; RESET=4; WRAM=0x89 + +R default: ,EEPROM,0xC1 +W default: n,,0x81 +Write start: 0,0,0x8F + +Listen: C8C8C1=CO2,Alarm,- + +R: ID*=,ID + +R: _CO2*2=0x6C,RAM,0xC2 +R: _PORTA=0x39,RAM +W: _PORTA=,0x3B,WRAM +R: _PORTB=0x36,RAM +W: _PORTB=,0x38,WRAM +R: _OSCCAL=0x51,RAM +W: _OSCCAL=,0x51,WRAM +R: OSCCAL_EMEM=0 + +R: RxAddr=1# +W: RxAddr=,1 + +R: Ch=2 +W: Ch=,2 + +R: nRF RETR=3# +W: nRF RETR=,3 + +R: Send period=4 +W: Send period=,4 + +R: CO2 threshold*2=5,,0xC2 +W: CO2 threshold=,5,0x82 + +R: CO2 correct*2=7,,0xC2 +W: CO2 correct*2=,7,0x82 + +R: FanLSB[10]=i:9# +W: FanLSB=,i:9 + +R: Transmit pause=19 +W: Transmit pause=,19 + +R: Flags=20# +W: Flags=,20 + +W: Reset=,RESET,0xC1 + +R: _LED Warning=0x74,RAM +W: _LED Warning=,0x74,WRAM + +S: LED=,0,0x40 + +SBatch: LED On: LED=1 +SBatch: LED Off: LED=0 + +RBatch: Settings: ID;RxAddr;Ch;Send period;CO2 threshold;CO2 correct;FanLSB;nRF RETR;Transmit pause;Flags + +WBatch: Default: RxAddr=0xCF;Ch=122;Send period=30;CO2 threshold=1000;CO2 correct=0;FanLSB={0xC1,0,0,0,0,0,0,0};nRF RETR=0x3;Transmit pause=1;Flags=0;Reset +WBatch: CO2: CO2 threshold=1000;CO2 correct=0 +WBatch: Fan: FanLSB={0xC1,0,0,0,0,0,0,0,0,0};Reset +WBatch: RETR: nRF RETR=0x2F;Reset +WBatch: Transmit pause: Transmit pause=30 +WBatch: Flags: Flags=0x00 + +WBatch: LED Warning: _LED Warning=0x30 +WBatch: PORTA: _PORTA=0x0C +WBatch: PORTB: _PORTB=0xC +WBatch: OSCCAL: _OSCCAL=128 + +WBatch: Reset: Reset diff --git a/assets/resources/apps_data/nrf24batch/Kitchen Vent Dimmer.txt b/assets/resources/apps_data/nrf24batch/Kitchen Vent Dimmer.txt new file mode 100644 index 000000000..65b036c48 --- /dev/null +++ b/assets/resources/apps_data/nrf24batch/Kitchen Vent Dimmer.txt @@ -0,0 +1,164 @@ +Info: Kitchen Vent Dimmer +Address: C8C8C1 +Rate: 1 +Ch: 122 +CRC: 2 +DPL: 0 +RETR: 0x2F +Resend: 3 +Delay_ms: 50 + +Payload struct: 2,1,1 +ROM=0x41;ROM2=0x42;WROM=0x81;WROM2=0x82;RAM=0x51;RAM2=0x52;WRAM=0x91;WRAM2=0x92;PGM=0x61;SET=0xC0 + +R default: ,,ROM +W default: n,,WROM +Write start: 0,0,0x2F + +R: ID*=,0,0x65 + +R: CO2 level*2[3]=i:4,,ROM2 +W: CO2 level*2=,i:4,WROM2 + +R: Fan speeds=25 +W: Fan speeds=,25 +R: FanSpeed[3]=i:19 +W: FanSpeed=,i:19 +R: FanCook speeds=26 +W: FanCook speeds=,26 +R: FanCookSpeed[3]=i:22 +W: FanCookSpeed=,i:22 + +R: FanOnTime=10 +W: FanOnTime=,10 + +R: FanOffTime=11 +W: FanOffTime=,11 + +R: FanCookOnTime=12 +W: FanCookOnTime=,12 + +R: FanCookOffTime=13 +W: FanCookOffTime=,13 + +R: FanSleep=14 +W: FanSleep=,14 + +R: FanCookSleep=15 +W: FanCookSleep=,15 + +R: SpeedInitIdx=16 +W: SpeedInitIdx=,16 + +R: SpeedKeyIdx=17 +W: SpeedKeyIdx=,17 + +R: OutSpeedMax=18 +W: OutSpeedMax=,18 + +R: PauseSetByCO2,min=27 +W: PauseSetByCO2,min=,27 + +R: SSR_PulseWidth,us*2=28,,ROM2 +W: SSR_PulseWidth,us*2=,28,WROM2 + +R: SSR_PulseSafeTime,us*2=30,,ROM2 +W: SSR_PulseSafeTime,us*2=,30,WROM2 + +R: SSR2_PulseWidth,us*2=32,,ROM2 +W: SSR2_PulseWidth,us*2=,32,WROM2 + +R: DamperOpenTime=34 +W: DamperOpenTime=,34 + +R: FanCookSpIdxDnKey=35 +W: FanCookSpIdxDnKey=,35 +R: FanCookSpIdxUpKey=36 +W: FanCookSpIdxUpKey=,36 + +R: IRRemotes=37 +W: IRRemotes=,37 + +R: IRRemotesHash*2[27]=i:38,,ROM2# +W: IRRemotesHash*2=,i:38,WROM2 + +R: Flags=1# +W: Flags=,1 + +R: RxAddr=2# +W: RxAddr=,2 + +R: Ch=3 +W: Ch=,3 + +R: OSCCAL_EMEM=0 +R: _OSCCAL=0x51,,RAM +W: _OSCCAL=,0x51,,WRAM +R: _PORTA=0x39,,RAM +W: _PORTA=,0x3B,,WRAM +R: _PORTB=0x36,,RAM +W: _PORTB=,0x38,WRAM + +R: _LED_Warning=0x71,,RAM +W: _LED_Warning=,0x71,WRAM +R: _IRHashLast*2=0x89,,RAM2# +R: _IRCntLast=0x87,,RAM +R: _IRHash*2=0x8B,,RAM2# +R: _SleepTimer=0x8E,,RAM +R: _FanOn=0x7F,,RAM +R: _FanOnNext=0x7D,,RAM +R: _FanOnNextCnt=0x7B,,RAM +R: _FanOnLast=0x7C,,RAM + +R: _OutSpeedMax=0x90,,RAM +W: _OutSpeedMax=,0x90,WRAM + +R: _Fanspeed=0x7E,,RAM +R: _SSR_full_period*2=0x64,,RAM2 + +R: _PulseDelayAfterZero*2=0x9F,,RAM2 +W: _PulseDelayAfterZero*2=,0x9F,WRAM2 +R: _PulseWidth*2=0x92,,RAM2 +W: _PulseWidth*2=,0x92,WRAM2 +R: _PulseWidth2=0xA3,,RAM +W: _PulseWidth2=,0xA3,WRAM + +W: Reset=0xEEEE,14,SET + +S: Lamp=,0,SET +S: Fan=,1,SET +S: FanAdd=,2,SET +S: FanSpdUp=,3,SET +S: FanSpdDn=,4,SET +S: FanSpdSave=,5,SET +S: SetupIR=,6,SET + +SBatch: Fan Cooker Max: Fan=6 +SBatch: Fan Cooker Min: Fan=4 +SBatch: Fan Max: Fan=3 +SBatch: Fan Min: Fan=1 +SBatch: Fan Off: Fan=0 +SBatch: Fan +: FanAdd=1 +SBatch: Fan -: FanAdd=-1 +SBatch: Lamp On: Lamp=1 +SBatch: Lamp Off: Lamp=0 +SBatch: Fan Up: FanSpdUp=0 +SBatch: Fan Down: FanSpdDn=0 +SBatch: Fan SAVE: FanSpdSave=0; +SBatch: Add new IR(Set,Off,FUp,FDn,CUp,CDn,C1,C2,C3): SetupIR=1 + +RBatch: Work: ID;CO2 level;FanCookSpeed;FanCookOnTime;FanCookOffTime;FanCookSleep;FanCookSpIdxDnKey;FanCookSpIdxUpKey;FanSpeed;FanOnTime;FanOffTime;FanSleep;DamperOpenTime;PauseSetByCO2,min +RBatch: Hardware: ID;RxAddr;Ch;SpeedInitIdx;Flags;OutSpeedMax;IRRemotes;SSR_PulseWidth,us;SSR_PulseSafeTime,us;SSR2_PulseWidth;OSCCAL_EMEM +RBatch: All: ID;CO2 level;FanCookSpeed;FanCookOnTime;FanCookOffTime;FanCookSleep;FanCookSpIdxDnKey;FanCookSpIdxUpKey;FanSpeed;FanOnTime;FanOffTime;FanSleep;DamperOpenTime;PauseSetByCO2,min;RxAddr;Ch;SpeedInitIdx;Flags;OutSpeedMax;SSR_PulseWidth,us;SSR_PulseSafeTime,us;SSR2_PulseWidth,us;IRRemotes;IRRemotesHash + +WBatch: Init(Cook2spd,Lamp-F2.2): RxAddr=0xC1;Ch=122;CO2 level={820,910,1000};Fan speeds=3;FanSpeed={10,12,16};FanCook speeds=3;FanCookSpeed={12,16,32};FanSleep=20;FanCookSleep=6;FanOnTime=1;FanCookOnTime=3;FanOffTime=100;FanCookOffTime=0;DamperOpenTime=60;SpeedInitIdx=0;SpeedKeyIdx=6;Flags=0x0D;OutSpeedMax=16;SSR_PulseWidth,us=9990 +WBatch: IRRemotes clear: IRRemotes=0 +WBatch: IRRemotes 1 kitchen: IRRemotes=1;IRRemotesHash={0x6DA5,0xFCA5,0x7CA5,0xB425,0x3425,0x1625,0x8525} +WBatch: Fans Speeds: Fan speeds=3;FanSpeed={13,15,20};FanCook speeds=3;FanCookSpeed={14,20,40};OutSpeedMax=20 +WBatch: FanCooker Down/Up key idx: FanCookSpIdxDnKey=5;FanCookSpIdxUpKey=6 +WBatch: LED Warning: _LED Warning=0x10 +WBatch: OSCCAL: _OSCCAL=146 +WBatch: CO2 Level: CO2 level={820,910,1000};PauseSetByCO2,min=10 +WBatch: Zero cross EEPROM: SSR_PulseWidth,us=9990;SSR2_PulseWidth,us=0;SSR_PulseSafeTime,us=100;Reset +WBatch: Zero cross RAM: _PulseDelayAfterZero=0001;_PulseWidth=200;_PulseWidth2=200 +WBatch: Reset: Reset diff --git a/assets/resources/apps_data/nrf24batch/Kitchen Vent.txt b/assets/resources/apps_data/nrf24batch/Kitchen Vent.txt new file mode 100644 index 000000000..485338105 --- /dev/null +++ b/assets/resources/apps_data/nrf24batch/Kitchen Vent.txt @@ -0,0 +1,127 @@ +Info: Kitchen Vent (FanControl) +Address: C8C8C1 +Rate: 1 +Ch: 122 +CRC: 2 +DPL: 0 +RETR: 0x0F +Resend: 3 +Delay_ms: 30 + +Payload struct: 2,1,1 +ROM=0x41;ROM2=0x42;WROM=0x81;WROM2=0x82;RAM=0x51;RAM2=0x52;WRAM=0x91;WRAM2=0x92;PGM=0x61;SET=0xC0 + +R default: ,,ROM +W default: n,,WROM +Write start: 0,0,0x2F + +R: ID*=,0,0x65 + +R: CO2 level*2[3]=i:4,,ROM2 +W: CO2 level*2=,i:4,WROM2 + +R: Fan speeds=25 +W: Fan speeds=,25 +R: FanSpeed[3]=i:19 +W: FanSpeed=,i:19 +R: FanCook speeds=26 +W: FanCook speeds=,26 +R: FanCookSpeed[3]=i:22 +W: FanCookSpeed=,i:22 + +R: FanOnTime=10 +W: FanOnTime=,10 + +R: FanOffTime=11 +W: FanOffTime=,11 + +R: FanCookOnTime=12 +W: FanCookOnTime=,12 + +R: FanCookOffTime=13 +W: FanCookOffTime=,13 + +R: FanSleep=14 +W: FanSleep=,14 + +R: FanCookSleep=15 +W: FanCookSleep=,15 + +R: SpeedInitIdx=16 +W: SpeedInitIdx=,16 + +R: SpeedKeyIdx=17 +W: SpeedKeyIdx=,17 + +R: IRRemotes=32 +W: IRRemotes=,32 + +R: IRRemotesHash*2[70]=i:33,,ROM2# +W: IRRemotesHash*2=,i:33,WROM2 + +R: OutPeriod=18 +W: OutPeriod=,18 + +R: Flags=1# +W: Flags=,1 + +R: RxAddr=2# +W: RxAddr=,2 + +R: Ch=3 +W: Ch=,3 + +R: OSCCAL_EMEM=0 +R: _OSCCAL=0x51,,RAM +W: _OSCCAL=,0x51,WRAM + +R: _PORTA=0x39,,RAM +W: _PORTA=,0x3B,WRAM +R: _PORTB=0x36,,RAM +W: _PORTB=,0x38,WRAM + +R: _OutPeriod=0x8B,,RAM +W: _OutPeriod=,0x8B,WRAM + +R: _Fanspeed=0x74,,RAM +R: _FanOn=0x75,,RAM +R: _FanOnNext=0x73,,RAM +R: _FanOnNextCnt=0x71,,RAM +R: _FanOnLast=0x72,,RAM +R: _IRHashLast*2=0x79,,RAM2# + +R: _LED Warning=0x6B,,RAM +W: _LED Warning=,0x6B,WRAM + +W: Reset=0xEEEE,14,SET + +S: Lamp=,0,SET +S: Fan=,1,SET +S: FanSpdUp=,2,SET +S: FanSpdDn=,3,SET +S: FanSpdSave=,4,SET +S: SetupIR=,5,SET + +SBatch: Fan Cooker On: Fan=6 +SBatch: Fan On: Fan=3 +SBatch: Fan Off: Fan=0 +SBatch: Lamp On: Lamp=1 +SBatch: Lamp Off: Lamp=0 +SBatch: Fan Up: FanSpdUp=0 +SBatch: Fan Down: FanSpdDn=0 +SBatch: Fan SAVE: FanSpdSave=0; +SBatch: Add new IR: SetupIR=1 + +RBatch: Work: ID;CO2 level;FanCookSpeed;FanCookOnTime;FanCookOffTime;FanCookSleep;FanSpeed;FanOnTime;FanOffTime;FanSleep +RBatch: Hardware: ID;RxAddr;Ch;SpeedInitIdx;Flags;OutPeriod;IRRemotes;OSCCAL_EMEM +RBatch: All: ID;CO2 level;FanCookSpeed;FanCookOnTime;FanCookOffTime;FanCookSleep;FanSpeed;FanOnTime;FanOffTime;FanSleep;RxAddr;Ch;SpeedInitIdx;Flags;OutPeriod;IRRemotes;IRRemotesHash + +WBatch: Init(Cook2spd,Lamp-F2.2): RxAddr=0xC1;Ch=122;CO2 level={950,1050,1200};Fan speeds=1;FanSpeed={1,1,1};FanCook speeds=2;FanCookSpeed={1,2,2};FanSleep=20;FanCookSleep=6;FanOnTime=150;FanCookOnTime=0;FanOffTime=100;FanCookOffTime=0;SpeedInitIdx=0;SpeedKeyIdx=6;Flags=0x0D;OutPeriod=1 +WBatch: IRRemotes clear: IRRemotes=0 +WBatch: IRRemotes 1 kitchen: IRRemotes=1;IRRemotesHash={0x6DA5,0xFCA5,0x7CA5,0xB425,0x3425,0x1625,0x8525} +WBatch: Fans Speeds: Fan speeds=1;FanSpeed={1,1,1};FanCook speeds=2;FanCookSpeed={1,2,2} +WBatch: LED Warning: _LED Warning=0x10 +WBatch: OutPeriod: _OutPeriod=1 +WBatch: OSCCAL: _OSCCAL=146 +WBatch: CO2 Level: CO2 level={750,850,1000} +WBatch: Reset: Reset diff --git a/assets/resources/tama_p1/rom.bin b/assets/resources/apps_data/tama_p1/rom.bin similarity index 100% rename from assets/resources/tama_p1/rom.bin rename to assets/resources/apps_data/tama_p1/rom.bin diff --git a/assets/resources/ibtnfuzzer/example_uids_cyfral.txt b/assets/resources/ibutton_fuzzer/example_uids_cyfral.txt similarity index 100% rename from assets/resources/ibtnfuzzer/example_uids_cyfral.txt rename to assets/resources/ibutton_fuzzer/example_uids_cyfral.txt diff --git a/assets/resources/ibtnfuzzer/example_uids_ds1990.txt b/assets/resources/ibutton_fuzzer/example_uids_ds1990.txt similarity index 100% rename from assets/resources/ibtnfuzzer/example_uids_ds1990.txt rename to assets/resources/ibutton_fuzzer/example_uids_ds1990.txt diff --git a/assets/resources/ibtnfuzzer/example_uids_metakom.txt b/assets/resources/ibutton_fuzzer/example_uids_metakom.txt similarity index 100% rename from assets/resources/ibtnfuzzer/example_uids_metakom.txt rename to assets/resources/ibutton_fuzzer/example_uids_metakom.txt diff --git a/assets/resources/mifare_fuzzer/example_uids04.txt b/assets/resources/mifare_fuzzer/example_uids04.txt new file mode 100644 index 000000000..0bd1b2996 --- /dev/null +++ b/assets/resources/mifare_fuzzer/example_uids04.txt @@ -0,0 +1,9 @@ +# One UID per line +# Keep an empty line at the end +01020304 +02030405 +03040506 +04050607 +05060708 +06070809 + diff --git a/assets/resources/mifare_fuzzer/example_uids07.txt b/assets/resources/mifare_fuzzer/example_uids07.txt new file mode 100644 index 000000000..fc53dcdea --- /dev/null +++ b/assets/resources/mifare_fuzzer/example_uids07.txt @@ -0,0 +1,10 @@ +# One UID per line +# Keep an empty line at the end +01020304050607 +02030405060701 +03040506070102 +04050607010203 +05060701020304 +06070102030405 +07010203040506 + diff --git a/assets/resources/rfidfuzzer/example_uids_em4100.txt b/assets/resources/rfid_fuzzer/example_uids_em4100.txt similarity index 100% rename from assets/resources/rfidfuzzer/example_uids_em4100.txt rename to assets/resources/rfid_fuzzer/example_uids_em4100.txt diff --git a/assets/resources/rfidfuzzer/example_uids_h10301.txt b/assets/resources/rfid_fuzzer/example_uids_h10301.txt similarity index 100% rename from assets/resources/rfidfuzzer/example_uids_h10301.txt rename to assets/resources/rfid_fuzzer/example_uids_h10301.txt diff --git a/assets/resources/rfidfuzzer/example_uids_hidprox.txt b/assets/resources/rfid_fuzzer/example_uids_hidprox.txt similarity index 100% rename from assets/resources/rfidfuzzer/example_uids_hidprox.txt rename to assets/resources/rfid_fuzzer/example_uids_hidprox.txt diff --git a/assets/resources/rfidfuzzer/example_uids_pac.txt b/assets/resources/rfid_fuzzer/example_uids_pac.txt similarity index 100% rename from assets/resources/rfidfuzzer/example_uids_pac.txt rename to assets/resources/rfid_fuzzer/example_uids_pac.txt From b3a92a0c73fe1f543818ddc92fbb1309844da9c2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 1 Jul 2023 02:31:54 +0200 Subject: [PATCH 325/370] IR remote base path --- applications/external/ir_remote/infrared_remote_app.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c index 0041fbaba..7381a7139 100644 --- a/applications/external/ir_remote/infrared_remote_app.c +++ b/applications/external/ir_remote/infrared_remote_app.c @@ -147,6 +147,7 @@ int32_t infrared_remote_app(void* p) { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px); + browser_options.base_path = EXT_PATH("infrared/remote"); FuriString* map_file = furi_string_alloc(); furi_string_set(map_file, EXT_PATH("infrared/remote")); if(!storage_file_exists(storage, EXT_PATH("infrared/remote"))) { From 0e60876b31fa53489b4c76dd1f17fa83af450e84 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 1 Jul 2023 03:10:58 +0200 Subject: [PATCH 326/370] Consistent lfrfid fuzzer folder name --- applications/external/multi_fuzzer/fuzzer.c | 2 +- .../{rfid_fuzzer => lfrfid_fuzzer}/example_uids_em4100.txt | 0 .../{rfid_fuzzer => lfrfid_fuzzer}/example_uids_h10301.txt | 0 .../{rfid_fuzzer => lfrfid_fuzzer}/example_uids_hidprox.txt | 0 .../{rfid_fuzzer => lfrfid_fuzzer}/example_uids_pac.txt | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename assets/resources/{rfid_fuzzer => lfrfid_fuzzer}/example_uids_em4100.txt (100%) rename assets/resources/{rfid_fuzzer => lfrfid_fuzzer}/example_uids_h10301.txt (100%) rename assets/resources/{rfid_fuzzer => lfrfid_fuzzer}/example_uids_hidprox.txt (100%) rename assets/resources/{rfid_fuzzer => lfrfid_fuzzer}/example_uids_pac.txt (100%) diff --git a/applications/external/multi_fuzzer/fuzzer.c b/applications/external/multi_fuzzer/fuzzer.c index f226fa209..01a1d60df 100644 --- a/applications/external/multi_fuzzer/fuzzer.c +++ b/applications/external/multi_fuzzer/fuzzer.c @@ -148,7 +148,7 @@ int32_t fuzzer_start_rfid(void* p) { PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); FuzzerConsts app_const = { - .custom_dict_folder = EXT_PATH("rfid_fuzzer"), + .custom_dict_folder = EXT_PATH("lfrfid_fuzzer"), .custom_dict_extension = ".txt", .key_extension = ".rfid", .path_key_folder = EXT_PATH("lfrfid"), diff --git a/assets/resources/rfid_fuzzer/example_uids_em4100.txt b/assets/resources/lfrfid_fuzzer/example_uids_em4100.txt similarity index 100% rename from assets/resources/rfid_fuzzer/example_uids_em4100.txt rename to assets/resources/lfrfid_fuzzer/example_uids_em4100.txt diff --git a/assets/resources/rfid_fuzzer/example_uids_h10301.txt b/assets/resources/lfrfid_fuzzer/example_uids_h10301.txt similarity index 100% rename from assets/resources/rfid_fuzzer/example_uids_h10301.txt rename to assets/resources/lfrfid_fuzzer/example_uids_h10301.txt diff --git a/assets/resources/rfid_fuzzer/example_uids_hidprox.txt b/assets/resources/lfrfid_fuzzer/example_uids_hidprox.txt similarity index 100% rename from assets/resources/rfid_fuzzer/example_uids_hidprox.txt rename to assets/resources/lfrfid_fuzzer/example_uids_hidprox.txt diff --git a/assets/resources/rfid_fuzzer/example_uids_pac.txt b/assets/resources/lfrfid_fuzzer/example_uids_pac.txt similarity index 100% rename from assets/resources/rfid_fuzzer/example_uids_pac.txt rename to assets/resources/lfrfid_fuzzer/example_uids_pac.txt From 523e063d196177439d14b6f9c9d89ed97cb6a4b2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 1 Jul 2023 11:38:56 +0100 Subject: [PATCH 327/370] Fix cross-dependant fap icons --- applications/debug/usb_mouse/application.fam | 2 +- applications/debug/usb_mouse/mouse_10px.png | Bin 0 -> 1634 bytes applications/external/nfc_magic/Nfc_10px.png | Bin 0 -> 304 bytes applications/external/nfc_magic/application.fam | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 applications/debug/usb_mouse/mouse_10px.png create mode 100644 applications/external/nfc_magic/Nfc_10px.png diff --git a/applications/debug/usb_mouse/application.fam b/applications/debug/usb_mouse/application.fam index ab91e7aa2..55ba7d869 100644 --- a/applications/debug/usb_mouse/application.fam +++ b/applications/debug/usb_mouse/application.fam @@ -7,6 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, order=60, - fap_icon="../../external/mousejacker/mouse_10px.png", + fap_icon="mouse_10px.png", fap_category="Debug", ) diff --git a/applications/debug/usb_mouse/mouse_10px.png b/applications/debug/usb_mouse/mouse_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..94c3a7a14143c00d6747ed59274c96e98d178b41 GIT binary patch literal 1634 zcmcIlJB-{!7&a%6$Vmq+qQUr@@EFhdxoa!V>DWEk75F$^R?1O;7|)D%&8k)*M)rWN~tLNw!9yhU*9=Vl!v}cMn~0A_>xa} zs$VxlvP-rr_-$T=Yh85^~G2&Lrq;ms^dW0l?K(L@IdYHf~g(d1Oy?Zc0ApP zTnBf&XH^rKCPXeYEMRne+w~1wZ7xliD`@N=_MdNziCLgcsw(#(tm|5@Eu9x*Xn39n zkqJ!`2m~k>S%v2yE4SASeO8hp$*Y8Cs$|3wpH+b-q^^8O^OiO%n>dMx!8y*Mp(E+j z8W@IWL({ZHm(|{hu+r$gD{(nm5h8wYLZc2mrqGW%>mZO6$><( zHGn)|I3ol3mhD^JXOQJ1w1S4B5Gbzj`M{he@!~wtF!YC6S>cSZVL#A>C_PE2Z#b6e zA`^+?A|~kCb__hy?E>T?f~+p~3>GhSiSvCW(+XPalJUQ4JS6%Y%dpy_;m2}c2=p13PwU>H;$+aT+fJrXV^CA8eJDyj!mt| zH6x3fno5Zu^CHEfPLdRl8O*b>rv9g<$EUmqITbOtWHk$R;l&^+Md}vK_i=^2*^H)% zx-2>$6IttlMr4v1)tcqxF3~%m&J}GvVS@CGJd@c8GYe#Fi=y7t`c2_ZJ`!mY~bs@U%7a!kv zLv-$)VSjh@*ash7`}z?DJ=lBVr(3s*`kMXuyR>}z`rcsU?e{MJy!q)5e_j6NkHv+- tXNTsuyY-KM{|0Z~_<>&pI=m5)b(dHL6nbwD9yPZ!4!j_b)k${QZ;XFmLn zjqNsD+YPq1J8W%_*aXBie!OR3*tC!PwU_7Q9H4U564!{5l*E!$tK_0oAjM#0U}UIk zV5)0q5@Kj%Wo%$&Y@uynU}a#izM}XGiiX_$l+3hBs0L%8o)7~QD^p9LQiz6svOM}g O4Gf;HelF{r5}E+GUQp8j literal 0 HcmV?d00001 diff --git a/applications/external/nfc_magic/application.fam b/applications/external/nfc_magic/application.fam index 717387d58..83a148f21 100644 --- a/applications/external/nfc_magic/application.fam +++ b/applications/external/nfc_magic/application.fam @@ -10,7 +10,7 @@ App( ], stack_size=4 * 1024, order=30, - fap_icon="../../../assets/icons/Archive/Nfc_10px.png", + fap_icon="Nfc_10px.png", fap_category="NFC", fap_private_libs=[ Lib( From 565713ba982b2f68ce4fd370b22f74cbf0cd3f06 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 1 Jul 2023 11:44:12 +0100 Subject: [PATCH 328/370] Temp fix for input hang on loader error on keybind --- applications/services/desktop/desktop.c | 6 ++-- .../desktop/scenes/desktop_scene_lock_menu.c | 2 +- applications/services/loader/loader.c | 35 +++++++++++++++++++ applications/services/loader/loader.h | 8 +++++ applications/services/loader/loader_i.h | 1 + firmware/targets/f7/api_symbols.csv | 1 + 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index fe1a2344a..8ecf0e936 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -468,11 +468,11 @@ void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key) { if(!strnlen(keybind, MAX_KEYBIND_LENGTH)) return; if(!strncmp(keybind, "Apps Menu", MAX_KEYBIND_LENGTH)) { - loader_start_with_gui_error(instance->loader, LOADER_APPLICATIONS_NAME, NULL); + loader_start_detached_with_gui_error(instance->loader, LOADER_APPLICATIONS_NAME, NULL); } else if(!strncmp(keybind, "Archive", MAX_KEYBIND_LENGTH)) { view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventOpenArchive); } else if(!strncmp(keybind, "Device Info", MAX_KEYBIND_LENGTH)) { - loader_start_with_gui_error(instance->loader, "Power", "about_battery"); + loader_start_detached_with_gui_error(instance->loader, "Power", "about_battery"); } else if(!strncmp(keybind, "Lock Menu", MAX_KEYBIND_LENGTH)) { view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventOpenLockMenu); } else if(!strncmp(keybind, "Lock Keypad", MAX_KEYBIND_LENGTH)) { @@ -480,7 +480,7 @@ void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key) { } else if(!strncmp(keybind, "Lock with PIN", MAX_KEYBIND_LENGTH)) { view_dispatcher_send_custom_event(instance->view_dispatcher, DesktopMainEventLockWithPin); } else { - loader_start_with_gui_error(instance->loader, keybind, NULL); + loader_start_detached_with_gui_error(instance->loader, keybind, NULL); } } diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index d47a79319..bcc8eee20 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -122,7 +122,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventXtreme: desktop_scene_lock_menu_save_settings(desktop); - loader_start_with_gui_error(desktop->loader, "Xtreme", NULL); + loader_start_detached_with_gui_error(desktop->loader, "Xtreme", NULL); consumed = true; break; case DesktopLockMenuEventStealthModeOn: diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 7733c3237..35809c104 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -57,6 +57,15 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const return status; } +void loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args) { + LoaderMessage message; + + message.type = LoaderMessageTypeStartByNameDetachedWithGuiError; + message.start.name = name; + message.start.args = args; + furi_message_queue_put(loader->queue, &message, FuriWaitForever); +} + bool loader_lock(Loader* loader) { LoaderMessage message; LoaderMessageBoolResult result; @@ -569,6 +578,32 @@ int32_t loader_srv(void* p) { loader, message.start.name, message.start.args, message.start.error_message); api_lock_unlock(message.api_lock); break; + case LoaderMessageTypeStartByNameDetachedWithGuiError: { + FuriString* error_message = furi_string_alloc(); + LoaderStatus status = loader_do_start_by_name( + loader, message.start.name, message.start.args, error_message); + if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, NULL); + + furi_string_replace(error_message, ":", "\n"); + dialog_message_set_text( + message, + furi_string_get_cstr(error_message), + 64, + 32, + AlignCenter, + AlignCenter); + + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + } + furi_string_free(error_message); + break; + } case LoaderMessageTypeShowMenu: loader_do_menu_show(loader, false); break; diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index f88439593..0073df3de 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -46,6 +46,14 @@ LoaderStatus */ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args); +/** + * @brief Start application detached with GUI error message + * @param[in] instance loader instance + * @param[in] name application name + * @param[in] args application arguments + */ +void loader_start_detached_with_gui_error(Loader* loader, const char* name, const char* args); + /** * @brief Lock application start * @param[in] instance loader instance diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index cbe01c7f3..5ed0441ba 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -25,6 +25,7 @@ struct Loader { typedef enum { LoaderMessageTypeStartByName, + LoaderMessageTypeStartByNameDetachedWithGuiError, LoaderMessageTypeAppClosed, LoaderMessageTypeShowMenu, LoaderMessageTypeShowSettings, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 00f9ee8ea..ba1c2d6b5 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1928,6 +1928,7 @@ Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void,Loader* Function,+,loader_show_settings,void,Loader* Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*, FuriString*" +Function,+,loader_start_detached_with_gui_error,void,"Loader*, const char*, const char*" Function,+,loader_start_with_gui_error,LoaderStatus,"Loader*, const char*, const char*" Function,+,loader_unlock,void,Loader* Function,+,loading_alloc,Loading*, From e4f8d39041fb38f653270f87d9d0a0a2ad9cd348 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 1 Jul 2023 16:12:49 +0200 Subject: [PATCH 329/370] Shared code for loader gui error --- applications/services/loader/loader.c | 30 +++++++-------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 35809c104..f8fe9f699 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -32,10 +32,7 @@ LoaderStatus return result.value; } -LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) { - FuriString* error_message = furi_string_alloc(); - LoaderStatus status = loader_start(loader, name, args, error_message); - +static void loader_show_gui_error(LoaderStatus status, FuriString* error_message) { // TODO: we have many places where we can emit a double start, ex: desktop, menu // so i prefer to not show LoaderStatusErrorAppStarted error message for now if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) { @@ -52,7 +49,12 @@ LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const dialog_message_free(message); furi_record_close(RECORD_DIALOGS); } +} +LoaderStatus loader_start_with_gui_error(Loader* loader, const char* name, const char* args) { + FuriString* error_message = furi_string_alloc(); + LoaderStatus status = loader_start(loader, name, args, error_message); + loader_show_gui_error(status, error_message); furi_string_free(error_message); return status; } @@ -582,25 +584,7 @@ int32_t loader_srv(void* p) { FuriString* error_message = furi_string_alloc(); LoaderStatus status = loader_do_start_by_name( loader, message.start.name, message.start.args, error_message); - if(status == LoaderStatusErrorUnknownApp || status == LoaderStatusErrorInternal) { - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header(message, "Error", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(message, NULL, NULL, NULL); - - furi_string_replace(error_message, ":", "\n"); - dialog_message_set_text( - message, - furi_string_get_cstr(error_message), - 64, - 32, - AlignCenter, - AlignCenter); - - dialog_message_show(dialogs, message); - dialog_message_free(message); - furi_record_close(RECORD_DIALOGS); - } + loader_show_gui_error(status, error_message); furi_string_free(error_message); break; } From 8c5931a62d353a50ce9c58cb09d8b44b97579d31 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 2 Jul 2023 09:39:16 +0300 Subject: [PATCH 330/370] Add new ACs and TVs in universal remote Hitachi taken from OFW PR 2826 by minchogaydarov Thomson TV from OFW PR 2818 by eze-kiel --- assets/resources/infrared/assets/ac.ir | 74 ++++++++++++++++++++++++++ assets/resources/infrared/assets/tv.ir | 38 +++++++++++++ 2 files changed, 112 insertions(+) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index a7cae05cb..4edb9eb66 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -655,3 +655,77 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3539 1637 533 1225 502 1193 534 375 501 376 501 376 500 1226 501 376 501 376 500 1226 500 1227 501 376 529 1197 558 320 557 319 555 1169 531 1195 531 346 529 1196 530 1198 528 349 526 352 524 1229 497 379 497 379 497 1230 497 380 497 380 497 379 498 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 9042 3535 1674 496 1230 497 1230 497 380 497 380 497 380 497 1230 497 380 497 380 496 1230 497 1230 497 380 497 1230 497 380 497 380 497 1231 496 1230 497 380 497 1231 496 1231 496 380 496 380 497 1231 496 380 497 380 496 1231 496 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 497 380 496 380 497 380 497 380 497 380 497 380 496 381 496 380 497 380 497 380 497 381 496 1231 496 381 496 380 497 380 497 381 496 381 496 1231 496 381 496 381 496 380 497 381 495 1231 496 1231 496 1231 496 380 497 381 496 381 496 380 496 381 496 381 496 381 496 381 496 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 1231 496 381 496 381 496 1232 495 381 495 1232 495 381 496 1231 496 1231 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 496 381 495 381 496 381 496 381 496 381 496 1232 495 381 496 381 496 381 496 381 496 381 495 381 496 381 495 382 495 381 496 382 495 381 495 382 495 381 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 382 495 381 495 1232 495 1232 495 1232 495 1232 495 1232 495 382 495 382 495 382 495 +# +# Model: Subtropic SUB/in-07HN1 +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9019 4453 576 1665 603 1639 602 504 602 505 601 507 599 508 598 1644 573 1669 573 1669 572 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 535 572 535 572 535 572 535 572 535 572 535 572 1670 572 1670 572 1670 571 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 536 571 1670 572 535 572 1670 572 535 572 535 572 536 571 536 571 536 571 536 571 535 572 536 571 536 571 536 571 536 571 536 571 536 571 1670 572 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 536 571 537 570 536 571 537 570 536 571 537 570 537 570 537 570 537 570 537 570 537 570 538 569 538 569 538 569 561 546 562 545 562 545 562 545 1696 546 562 545 1697 545 562 545 562 545 562 545 562 545 562 545 1696 546 1697 545 1697 545 562 545 562 545 1697 545 1697 545 1697 545 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9048 4454 577 1665 576 1665 576 531 601 506 600 508 599 509 572 1670 572 1670 572 1670 572 1670 572 1670 572 535 572 535 572 536 571 1670 572 536 571 536 571 536 571 535 572 536 571 536 571 1670 572 1670 572 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 1671 571 536 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 537 570 536 571 537 570 537 570 536 571 537 570 1671 571 537 570 536 571 537 570 536 571 537 570 537 570 537 570 537 570 536 571 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 1671 571 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 571 537 570 537 570 1672 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 1672 570 1672 570 538 569 1672 570 537 570 537 570 1672 570 1672 570 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9049 4454 603 1638 604 1638 603 504 602 506 600 507 600 508 598 1644 573 1670 572 1670 572 1670 572 1670 572 535 572 535 572 535 572 1670 597 1645 572 535 573 535 572 535 597 510 573 535 572 1670 572 1670 572 1670 572 535 597 510 572 535 573 535 572 535 572 535 572 535 572 535 572 535 572 535 572 536 571 535 572 535 572 1670 572 536 571 1671 571 536 571 536 571 536 572 536 571 536 571 536 572 536 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 537 570 1672 570 537 570 537 570 537 570 538 569 537 570 537 570 561 546 562 545 562 545 562 546 562 545 562 545 562 545 562 546 562 545 562 545 562 545 562 545 562 546 562 545 1697 545 1697 545 562 545 562 545 562 545 562 545 562 545 562 545 562 546 562 545 562 545 562 545 562 545 562 545 563 544 562 546 562 545 563 544 562 545 562 545 562 545 1697 545 563 545 1697 545 1698 544 1697 545 563 544 1698 544 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9048 4454 576 1665 602 1640 603 504 602 506 600 507 599 509 573 1669 573 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 1670 572 535 572 535 573 535 572 535 572 535 572 535 573 1670 572 1670 572 1670 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 535 572 1670 572 535 572 1670 572 535 572 535 572 536 572 535 572 535 572 535 572 535 572 536 571 535 572 536 571 535 572 536 571 536 571 536 571 536 571 1671 571 536 571 536 571 536 571 536 596 511 571 536 572 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 572 536 571 1671 595 1647 571 536 571 536 571 536 572 536 571 536 595 512 571 536 571 536 571 536 572 536 571 1671 571 536 571 536 571 537 570 536 571 536 571 537 570 537 570 1672 570 1671 571 537 570 537 571 1671 571 1672 570 1672 570 537 570 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9017 4455 577 1665 576 1665 576 531 575 532 600 508 574 535 572 1670 572 1671 571 1671 571 1671 571 1671 571 1671 571 1671 571 1671 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 1671 571 1671 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 536 571 1671 571 536 571 1671 571 537 570 537 570 537 570 537 570 537 570 537 570 537 570 538 569 538 569 538 569 538 569 538 569 538 569 1695 546 538 569 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 561 546 562 545 561 546 561 546 561 546 1696 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 563 544 563 544 563 544 563 544 563 544 563 544 1698 544 563 544 564 543 563 544 564 543 564 543 564 543 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9046 4455 577 1665 577 1665 576 531 601 506 600 508 599 509 573 1670 572 1670 572 1670 572 1671 571 1670 572 1671 571 1671 571 1671 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 1671 571 1671 571 1671 571 536 571 536 571 536 571 536 571 536 571 536 571 537 570 536 571 536 571 536 571 536 571 537 570 536 571 1672 570 537 570 1671 571 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 537 570 1672 570 537 570 538 569 538 569 537 570 537 570 537 570 537 570 537 570 538 569 537 570 538 569 537 570 538 569 538 569 538 569 538 569 562 545 561 546 539 569 561 546 562 545 562 545 1696 546 562 545 562 546 562 545 562 545 562 545 562 545 562 545 562 545 562 545 562 545 1696 546 562 545 562 545 562 545 562 545 562 545 562 545 562 545 1697 545 1697 545 562 545 562 545 562 545 1697 545 562 545 562 545 +# +# Model: Hitachi RAK-50PEB +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30683 50966 3411 1600 493 1186 493 347 492 348 491 348 491 349 490 349 490 350 489 351 488 351 488 352 487 352 488 351 488 1192 487 352 487 351 488 352 487 352 487 352 488 352 488 351 488 1192 487 1191 488 352 487 352 487 352 487 352 487 352 487 352 487 352 487 352 487 1192 487 352 487 1192 487 1192 487 1192 487 1192 488 1192 487 1192 488 352 487 1192 487 1192 487 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 1192 487 352 487 352 488 352 487 1192 487 352 487 352 487 352 487 352 487 1192 487 352 487 353 486 1192 487 353 486 353 486 353 486 353 486 1193 486 353 487 353 486 353 486 353 486 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 353 487 353 486 353 486 353 486 1193 486 353 486 353 486 1193 486 353 487 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 1193 486 1193 486 353 487 353 486 353 486 353 486 354 485 353 486 354 485 353 486 354 486 353 486 353 486 354 486 353 486 353 487 1193 486 1194 485 353 487 353 486 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 486 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 354 485 355 484 355 484 355 484 354 485 354 485 355 484 355 484 355 484 355 484 354 486 355 484 378 461 356 484 378 461 355 485 355 484 355 484 355 485 355 484 378 461 378 461 355 484 356 483 355 484 378 461 378 462 355 485 378 461 378 462 378 461 379 461 356 483 378 461 1195 484 378 461 379 461 356 484 378 461 379 460 379 461 378 461 378 462 378 461 379 461 378 461 378 461 379 460 379 460 379 461 378 461 378 461 378 461 378 461 378 461 379 460 379 460 379 460 379 461 379 460 1219 460 1219 460 379 461 1219 460 379 461 1219 460 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30684 50965 3412 1599 494 1185 494 346 493 346 493 347 492 348 491 349 490 349 490 350 489 351 489 350 489 350 489 351 489 1191 488 351 488 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 351 488 351 488 351 488 351 488 351 488 351 489 351 488 351 488 1191 488 351 489 1191 488 1191 488 1191 488 1191 488 1191 488 1191 488 351 488 1191 488 1192 487 351 488 352 487 351 488 351 488 352 487 352 487 352 487 352 488 1192 487 1192 487 1216 463 1192 487 1192 488 1192 487 1193 486 1192 488 352 487 352 487 352 487 1193 486 376 463 376 463 376 464 352 487 1216 463 376 463 376 463 1216 464 376 463 376 463 376 463 1216 463 376 463 376 463 353 486 353 487 376 463 376 463 376 463 1216 463 376 463 1216 463 376 464 376 463 376 463 376 464 376 463 376 463 376 463 376 463 1216 463 376 463 1216 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 463 376 464 376 463 1216 463 1217 463 376 463 377 462 377 462 377 463 376 463 376 463 377 462 376 463 377 462 377 462 377 463 376 463 377 462 377 462 1217 462 1216 463 377 463 377 462 377 462 377 462 377 463 376 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 1217 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 463 377 462 377 463 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 377 462 1217 462 377 462 377 462 377 462 377 462 378 461 377 462 377 462 378 462 377 462 377 462 377 463 377 462 378 461 378 462 377 462 378 461 377 462 378 461 378 461 378 461 378 462 377 462 378 462 1217 462 1218 461 1218 461 378 461 378 461 1218 462 377 462 378 462 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30747 50897 3484 1554 543 1137 542 310 529 309 530 309 530 309 530 310 529 309 530 310 529 309 530 309 530 309 530 309 530 1138 541 309 531 309 530 309 530 309 530 309 530 309 530 309 530 1138 541 1138 541 310 529 309 530 309 531 309 530 309 530 309 530 309 530 310 529 1139 541 309 530 1138 541 1138 541 1138 541 1138 542 1138 541 1138 541 310 529 1138 541 1138 541 309 530 309 530 309 531 310 529 309 530 309 530 309 530 309 530 1139 541 1139 540 1139 541 1138 541 1139 540 1139 540 1138 541 1139 540 310 529 310 529 309 530 1139 541 310 529 309 530 309 530 309 530 1139 540 309 530 309 530 1139 541 309 530 309 530 309 530 1139 540 309 531 309 530 1139 541 309 530 309 530 309 530 309 530 309 530 309 530 1139 540 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 1139 540 310 529 309 530 309 530 309 530 309 530 309 531 310 529 310 529 309 531 310 529 1140 540 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 310 529 1140 539 1140 539 309 530 309 530 309 530 309 530 310 529 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 1140 540 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 530 310 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 531 309 531 309 530 309 530 309 530 309 530 309 530 309 531 309 530 309 531 309 530 309 531 309 530 309 530 309 531 309 530 309 530 309 530 309 530 1141 538 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 531 309 530 309 530 309 530 309 530 309 530 309 530 309 530 310 530 309 531 309 530 309 530 309 530 309 530 309 530 309 531 1142 537 309 530 1142 538 309 530 1141 538 309 530 309 530 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30694 50951 3483 1555 542 1137 542 308 531 309 530 308 531 308 531 308 531 308 531 308 531 308 531 308 532 308 531 308 532 1139 541 308 531 308 531 308 531 308 531 308 531 309 531 308 531 1139 541 1139 540 308 531 308 531 308 531 308 531 309 530 308 531 308 531 308 532 1139 541 308 532 1139 540 1139 541 1139 540 1139 540 1139 540 1139 541 309 530 1139 540 1139 540 308 531 308 531 308 531 308 532 308 531 308 531 308 531 308 531 1140 540 1139 541 1139 540 1139 540 1139 540 1139 541 1139 540 1139 540 308 531 308 531 308 531 1140 540 309 530 308 531 308 532 308 531 1140 540 308 531 308 532 1140 539 308 531 308 531 308 531 308 532 308 531 308 531 1140 540 308 531 308 531 308 531 308 531 308 532 308 531 1140 540 308 531 308 531 308 531 308 531 308 531 308 531 1140 540 1140 540 1140 539 308 531 1140 539 308 531 308 531 308 532 308 531 306 533 308 531 306 533 308 531 307 533 308 531 1141 539 308 532 308 531 308 531 306 534 306 533 306 534 306 533 306 533 306 533 306 533 306 534 307 532 307 533 306 533 308 532 1141 539 1141 539 308 531 308 532 308 531 307 533 307 481 352 538 307 533 307 532 307 533 307 481 352 539 307 532 307 533 306 482 352 487 352 537 306 534 307 482 352 538 307 482 352 487 1192 539 309 530 307 531 306 483 352 487 352 538 307 482 352 538 306 483 352 487 352 487 352 487 353 486 352 488 353 486 352 487 352 487 353 487 353 486 353 486 353 487 352 487 353 486 353 486 353 486 353 486 353 487 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 487 353 486 353 487 352 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 487 353 486 353 487 353 486 1193 537 306 483 353 486 353 487 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 487 353 486 353 486 353 486 353 486 353 486 353 486 353 486 1194 485 353 487 1193 538 1142 486 1193 486 353 486 353 486 353 568 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30703 50953 3432 1606 490 1189 490 349 490 349 490 349 490 350 489 350 489 350 489 350 490 350 489 350 489 350 490 350 489 1190 490 350 489 350 489 350 489 350 489 350 490 350 489 350 490 1190 490 1190 489 350 489 350 489 350 489 350 490 350 490 350 489 350 490 350 490 1190 489 350 490 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 350 490 1190 489 1190 489 350 490 350 489 350 490 350 489 350 489 350 490 350 489 350 490 1190 489 1190 489 1190 490 1190 489 1190 490 1190 489 1190 489 1191 489 350 489 350 489 350 490 1190 490 350 489 350 490 350 489 350 490 1190 489 350 490 350 489 1190 490 350 489 350 490 350 490 350 489 350 489 350 489 1191 489 350 489 350 490 350 489 350 489 1191 489 1190 489 350 489 350 490 350 489 350 490 350 489 351 489 350 489 350 489 350 489 350 490 350 489 350 489 1191 489 350 489 351 489 351 488 351 489 351 488 351 489 350 489 351 489 351 489 1191 488 351 488 351 488 351 489 350 489 351 488 350 490 350 489 351 488 351 488 351 489 350 489 350 489 351 489 351 489 350 489 1191 488 1191 489 350 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 489 351 489 350 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 488 351 489 351 489 1191 488 351 489 351 488 351 489 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 351 489 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 489 351 489 351 488 351 489 351 488 351 488 351 489 351 488 1192 488 351 488 351 488 351 489 351 488 351 488 351 489 351 489 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 488 351 489 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1191 488 352 488 351 488 352 488 351 489 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 30675 50953 3432 1606 490 1190 489 350 489 350 489 350 489 350 489 351 488 350 489 351 489 351 488 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 351 488 351 489 351 488 1191 489 1191 489 351 488 351 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 1191 489 1191 488 1191 488 1191 488 1191 488 1191 488 351 489 1191 488 1191 489 351 488 351 489 351 488 351 489 351 488 351 488 351 488 351 488 1191 488 1191 488 1191 488 1191 489 1191 488 1191 488 1191 489 1191 488 351 488 351 489 351 488 1191 488 351 489 351 488 351 488 351 489 1191 488 351 489 351 488 1191 488 351 488 351 488 351 489 351 489 351 488 351 489 1191 488 351 488 351 489 351 488 351 488 1191 488 1191 488 351 488 351 489 351 488 351 489 351 488 351 489 351 489 1191 488 1192 488 1191 488 351 489 1191 489 351 488 351 488 351 489 351 489 351 488 351 488 351 489 351 488 351 488 351 488 1192 488 351 489 351 488 351 488 351 489 351 489 351 488 351 488 351 488 352 487 352 488 351 488 352 488 351 488 351 488 351 488 1192 488 1192 487 352 488 352 487 352 487 352 488 352 487 352 488 351 488 352 488 352 488 352 487 352 488 351 488 351 488 352 488 352 487 352 488 352 487 352 488 352 488 352 488 352 487 1192 487 352 487 352 488 352 488 352 488 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 487 352 488 352 488 352 487 352 487 352 487 352 487 352 487 352 488 352 487 352 487 352 487 1193 486 352 487 352 488 352 487 352 487 352 488 352 487 352 488 352 488 352 487 352 488 352 487 353 486 353 487 352 487 352 488 352 488 352 487 352 487 352 487 352 487 353 487 352 488 352 488 352 487 1193 486 1193 486 1193 486 1193 487 353 487 352 487 353 486 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 2d2d98071..f64c58bc8 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -2275,3 +2275,41 @@ type: parsed protocol: NEC address: A0 00 00 00 command: 5F 00 00 00 +# +# Thomson Remote +# Model RC3000E02 +name: Power +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 54 00 00 00 +# +name: Vol_up +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: F4 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 +# +name: Ch_next +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +# +name: Mute +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 From ad12071f4c8fd370d66519051df6e64c9be4228a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 2 Jul 2023 10:01:57 +0300 Subject: [PATCH 331/370] fix text bug --- applications/main/subghz/views/receiver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 279d0d350..eb7a5a154 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -282,6 +282,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } else { canvas_set_color(canvas, ColorBlack); } + elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 7 : MAX_LEN_PX); canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); furi_string_reset(str_buff); From d4f4a720fe46f3611b56dde04386956c15c4edf8 Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Sun, 2 Jul 2023 12:50:24 +0100 Subject: [PATCH 332/370] Update tv.ir Reverted previously added additions as they are duplicates of TCL remote. Added Thomson comment to TCL codes. --- assets/resources/infrared/assets/tv.ir | 44 ++------------------------ 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index f64c58bc8..32ddfded2 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 16th Jun, 2023 -# Last Checked 16th Jun, 2023 +# Last Updated 1st Jul, 2023 +# Last Checked 1st Jul, 2023 # name: Power type: parsed @@ -2052,7 +2052,7 @@ protocol: Samsung32 address: 3E 00 00 00 command: 0D 00 00 00 # -# TCL LED49D2930 +# TCL LED49D2930 / Thomson Remote RC3000E02 # name: Power type: parsed @@ -2275,41 +2275,3 @@ type: parsed protocol: NEC address: A0 00 00 00 command: 5F 00 00 00 -# -# Thomson Remote -# Model RC3000E02 -name: Power -type: parsed -protocol: RCA -address: 0F 00 00 00 -command: 54 00 00 00 -# -name: Vol_up -type: parsed -protocol: RCA -address: 0F 00 00 00 -command: F4 00 00 00 -# -name: Vol_dn -type: parsed -protocol: RCA -address: 0F 00 00 00 -command: 74 00 00 00 -# -name: Ch_next -type: parsed -protocol: RCA -address: 0F 00 00 00 -command: B4 00 00 00 -# -name: Ch_prev -type: parsed -protocol: RCA -address: 0F 00 00 00 -command: 34 00 00 00 -# -name: Mute -type: parsed -protocol: RCA -address: 0F 00 00 00 -command: FC 00 00 00 From 9b86e7d2f1ea449e183f93ef3002328ff37213fc Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Sun, 2 Jul 2023 12:51:00 +0100 Subject: [PATCH 333/370] Update projectors.ir Updated last checked, no new additions --- assets/resources/infrared/assets/projectors.ir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 4842f470a..7df0fe127 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 16th Jun, 2023 -# Last Checked 16th Jun, 2023 +# Last Checked 1st Jul, 2023 # # ON name: Power From 167576e0f762f5cacec78515f57fb2d88bafd2da Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Sun, 2 Jul 2023 12:51:25 +0100 Subject: [PATCH 334/370] Update fans.ir Updated fans with new additions --- assets/resources/infrared/assets/fans.ir | 58 +++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 67646b343..185573ece 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 16th Jun, 2023 -# Last Checked 16th Jun, 2023 +# Last Updated 1st Jul, 2023 +# Last Checked 1st Jul, 2023 # name: Power type: raw @@ -1779,3 +1779,57 @@ type: parsed protocol: NEC address: 04 00 00 00 command: 07 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9248 4429 664 479 662 504 663 478 664 478 663 1588 687 1611 663 478 662 480 661 1615 659 1616 659 1616 658 1616 659 483 658 483 658 1616 658 1616 658 1616 658 1617 657 1616 658 483 658 1617 657 483 658 483 658 1616 658 483 658 483 658 483 658 1617 657 483 657 1617 657 1617 657 484 657 39720 9237 2194 658 +# +name: Speed_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9281 4426 667 503 640 503 666 477 666 477 666 1609 666 1610 665 478 664 480 662 1614 661 1615 661 1615 661 1615 661 482 661 482 661 1615 661 1615 661 482 661 1615 661 1616 660 482 661 483 660 482 661 482 660 1616 660 1616 660 483 660 483 660 1616 659 1616 660 1616 660 1616 659 483 660 39712 9244 2192 659 +# +name: Mode +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9278 4401 665 478 663 479 687 479 664 477 664 1586 688 1610 664 478 662 479 661 1614 660 1615 659 1615 660 1615 660 482 659 482 659 1615 660 1615 659 1615 659 1615 659 1615 659 482 659 482 659 482 659 482 659 1615 659 482 659 482 659 482 659 1615 659 1615 659 1615 659 1615 659 482 659 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9261 4448 641 499 642 499 642 498 643 498 643 1632 643 1632 667 475 666 475 666 1609 665 1610 664 1611 663 1612 662 479 662 479 662 1613 661 1614 660 479 662 479 662 1613 661 1612 662 1637 637 504 637 504 637 1637 637 1637 637 1613 661 504 637 504 637 504 636 1637 637 1637 637 504 636 39707 9242 2184 662 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9265 4406 688 455 686 479 662 479 662 479 662 1612 661 1613 660 481 659 482 658 1617 657 1618 656 1618 656 1618 656 485 656 485 655 1618 656 1618 656 1618 656 485 655 1618 655 485 656 1618 655 485 656 485 655 1618 655 485 655 1619 655 485 655 1619 654 486 654 1619 655 1619 655 486 654 39717 9230 2198 654 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1261 422 1260 422 514 1169 1260 422 1260 422 418 1265 418 1266 417 1266 514 1169 514 1169 514 1168 1262 7159 1261 422 1261 422 418 1265 1261 422 1260 422 418 1265 418 1266 417 1266 417 1266 418 1264 419 1265 1261 7159 1260 422 1260 422 418 1265 1261 421 1262 422 418 1266 514 1168 516 1169 515 1167 516 1168 515 1168 1261 7160 1261 422 1261 421 515 1168 1262 422 1261 421 515 1169 515 1167 516 1167 516 1167 516 1168 515 1168 1261 7160 1260 422 1260 422 514 1168 1262 421 1261 421 419 1264 515 1169 515 1169 514 1169 515 1169 515 1168 1262 7160 1260 421 1261 421 418 1266 1260 422 1261 422 417 1265 418 1265 418 1266 417 1265 418 1265 418 1266 1260 7159 1261 422 1260 422 418 1266 1260 421 1261 422 417 1265 418 1266 417 1266 417 1265 418 1266 417 1265 1261 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1341 341 1341 341 498 1186 1340 341 1341 342 497 1186 521 1160 499 1186 1340 342 522 1162 521 1162 521 7900 1340 342 1340 343 521 1163 1339 343 1339 343 521 1162 521 1162 521 1162 1340 343 521 1163 521 1163 520 7902 1338 344 1338 343 496 1187 1340 343 1339 343 521 1164 520 1164 519 1163 1339 344 519 1163 496 1187 521 7901 1339 343 1339 344 495 1187 1340 342 1341 343 496 1187 497 1186 497 1187 1340 343 496 1188 495 1187 496 7927 1337 343 1339 343 496 1187 1339 343 1339 343 496 1188 520 1163 520 1163 1340 343 496 1187 520 1163 520 +# +name: Mode +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1342 341 1341 340 500 1183 1343 341 1341 341 499 1184 499 1184 499 1184 499 1183 1343 340 499 1185 499 7923 1341 340 1342 341 498 1184 1342 341 1341 341 498 1185 498 1186 497 1184 500 1186 1341 342 497 1185 498 7923 1341 342 1340 342 497 1185 1342 341 1341 342 521 1162 498 1186 497 1185 498 1186 1340 342 521 1162 521 7899 1341 341 1341 341 498 1185 1341 342 1340 341 498 1186 497 1185 499 1185 498 1185 1341 341 498 1185 499 7923 1341 342 1341 341 499 1184 1342 342 1341 342 497 1184 499 1185 498 1184 499 1185 1341 342 497 1185 499 7922 1342 341 1341 341 499 1185 1341 341 1341 341 499 1183 500 1185 498 1185 498 1184 1342 342 497 1185 499 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1286 396 1287 396 444 1240 1286 396 1286 397 443 1239 444 1240 1285 396 444 1239 444 1240 443 1241 442 7977 1286 396 1286 396 500 1183 1287 396 1286 395 501 1183 501 1184 1286 395 500 1184 499 1183 500 1183 501 7922 1340 341 1341 341 499 1184 1342 341 1341 340 500 1184 499 1183 1343 340 500 1184 499 1183 500 1184 499 7922 1342 340 1342 340 500 1184 1342 340 1342 340 500 1185 498 1184 1342 340 500 1184 499 1184 499 1186 498 7922 1341 341 1341 341 499 1184 1342 341 1341 340 500 1183 500 1184 1342 341 499 1184 500 1184 499 1184 499 From c611c2d1bbd1952774c41c1d20f3c9e5f70c9fda Mon Sep 17 00:00:00 2001 From: amec0e <88857687+amec0e@users.noreply.github.com> Date: Sun, 2 Jul 2023 12:51:52 +0100 Subject: [PATCH 335/370] Update audio.ir Updated audio with new addition --- assets/resources/infrared/assets/audio.ir | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 705753aa4..74538cad1 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 16th Jun, 2023 -# Last Checked 16th Jun, 2023 +# Last Updated 1st Jul, 2023 +# Last Checked 1st Jul, 2023 # name: Power type: parsed @@ -3662,3 +3662,21 @@ type: parsed protocol: NEC address: 00 00 00 00 command: 44 00 00 00 +# +name: Power +type: parsed +protocol: SIRC15 +address: 30 00 00 00 +command: 2F 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 05 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 02 00 00 00 From a9948003b1cc843f1ac25bf49409f6b37d24f36a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 2 Jul 2023 19:02:26 +0300 Subject: [PATCH 336/370] =?UTF-8?q?=D0=BF=D0=BE=D1=87=D0=B8=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- applications/services/desktop/scenes/desktop_scene_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index d11b7a585..420775489 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -70,7 +70,7 @@ static void desktop_scene_main_open_app_or_profile(Desktop* desktop, const char* } static void desktop_scene_main_start_favorite(Desktop* desktop, FavoriteApp* application) { - if(strlen(application->name_or_path) > 4) { + if(strlen(application->name_or_path) > 0) { loader_start_with_gui_error(desktop->loader, application->name_or_path, NULL); } else { // No favourite app is set! So we skipping this part From 1c7eec04ddbd5853880667c27793494ac370d65c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 2 Jul 2023 19:10:51 +0200 Subject: [PATCH 337/370] Expose playlist and remote apps paths in headers --- applications/external/ir_remote/infrared_remote.h | 2 ++ applications/external/ir_remote/infrared_remote_app.c | 8 ++++---- applications/external/subghz_playlist/playlist.c | 2 -- applications/external/subghz_playlist/playlist_file.h | 3 +++ applications/external/subghz_remote/subghz_remote_app_i.h | 4 ---- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/applications/external/ir_remote/infrared_remote.h b/applications/external/ir_remote/infrared_remote.h index 6eac193d3..00ca2e107 100644 --- a/applications/external/ir_remote/infrared_remote.h +++ b/applications/external/ir_remote/infrared_remote.h @@ -4,6 +4,8 @@ #include "infrared_remote_button.h" +#define IR_REMOTE_PATH EXT_PATH("infrared/remote") + typedef struct InfraredRemote InfraredRemote; InfraredRemote* infrared_remote_alloc(); diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c index 7381a7139..409bfddd3 100644 --- a/applications/external/ir_remote/infrared_remote_app.c +++ b/applications/external/ir_remote/infrared_remote_app.c @@ -147,11 +147,11 @@ int32_t infrared_remote_app(void* p) { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px); - browser_options.base_path = EXT_PATH("infrared/remote"); + browser_options.base_path = IR_REMOTE_PATH; FuriString* map_file = furi_string_alloc(); - furi_string_set(map_file, EXT_PATH("infrared/remote")); - if(!storage_file_exists(storage, EXT_PATH("infrared/remote"))) { - storage_common_mkdir(storage, EXT_PATH("infrared/remote")); //Make Folder If dir not exist + furi_string_set(map_file, IR_REMOTE_PATH); + if(!storage_file_exists(storage, IR_REMOTE_PATH)) { + storage_common_mkdir(storage, IR_REMOTE_PATH); //Make Folder If dir not exist } bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); diff --git a/applications/external/subghz_playlist/playlist.c b/applications/external/subghz_playlist/playlist.c index 4ada9aa72..c300a0020 100644 --- a/applications/external/subghz_playlist/playlist.c +++ b/applications/external/subghz_playlist/playlist.c @@ -22,8 +22,6 @@ #include "playlist_file.h" #include "canvas_helper.h" -#define PLAYLIST_FOLDER EXT_PATH("subghz/playlist") -#define PLAYLIST_EXT ".txt" #define TAG "Playlist" #define STATE_NONE 0 diff --git a/applications/external/subghz_playlist/playlist_file.h b/applications/external/subghz_playlist/playlist_file.h index fb708edc7..7e853fb28 100644 --- a/applications/external/subghz_playlist/playlist_file.h +++ b/applications/external/subghz_playlist/playlist_file.h @@ -4,4 +4,7 @@ #include +#define PLAYLIST_FOLDER EXT_PATH("subghz/playlist") +#define PLAYLIST_EXT ".txt" + int playlist_count_playlist_items(Storage* storage, const char* file_path); diff --git a/applications/external/subghz_remote/subghz_remote_app_i.h b/applications/external/subghz_remote/subghz_remote_app_i.h index 9291d54f2..a2ecc0c73 100644 --- a/applications/external/subghz_remote/subghz_remote_app_i.h +++ b/applications/external/subghz_remote/subghz_remote_app_i.h @@ -6,11 +6,7 @@ #include "helpers/txrx/subghz_txrx.h" -#ifdef APP_SUBGHZREMOTE #include -#else -#include -#endif #include "views/remote.h" From 4e30ce2c255cfeeb1785380a37651175e7ec5376 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 2 Jul 2023 19:38:32 +0200 Subject: [PATCH 338/370] Consistent remote and playlist icon names --- .../external/subghz_playlist/application.fam | 2 +- .../{playlist_10px.png => subplaylist_10px.png} | Bin applications/external/subghz_remote/application.fam | 2 +- .../{subghz_remote_10px.png => subrem_10px.png} | Bin 4 files changed, 2 insertions(+), 2 deletions(-) rename applications/external/subghz_playlist/{playlist_10px.png => subplaylist_10px.png} (100%) rename applications/external/subghz_remote/{subghz_remote_10px.png => subrem_10px.png} (100%) diff --git a/applications/external/subghz_playlist/application.fam b/applications/external/subghz_playlist/application.fam index 319e15cd7..8fe9ccbaf 100644 --- a/applications/external/subghz_playlist/application.fam +++ b/applications/external/subghz_playlist/application.fam @@ -6,7 +6,7 @@ App( requires=["storage", "gui", "dialogs", "subghz"], stack_size=2 * 1024, order=14, - fap_icon="playlist_10px.png", + fap_icon="subplaylist_10px.png", fap_category="Sub-GHz", fap_author="@darmiel", fap_version="1.0", diff --git a/applications/external/subghz_playlist/playlist_10px.png b/applications/external/subghz_playlist/subplaylist_10px.png similarity index 100% rename from applications/external/subghz_playlist/playlist_10px.png rename to applications/external/subghz_playlist/subplaylist_10px.png diff --git a/applications/external/subghz_remote/application.fam b/applications/external/subghz_remote/application.fam index 42ea9d358..14c7f14ec 100644 --- a/applications/external/subghz_remote/application.fam +++ b/applications/external/subghz_remote/application.fam @@ -13,6 +13,6 @@ App( ], stack_size=2 * 1024, order=11, - fap_icon="subghz_remote_10px.png", + fap_icon="subrem_10px.png", fap_category="Sub-GHz", ) diff --git a/applications/external/subghz_remote/subghz_remote_10px.png b/applications/external/subghz_remote/subrem_10px.png similarity index 100% rename from applications/external/subghz_remote/subghz_remote_10px.png rename to applications/external/subghz_remote/subrem_10px.png From a05bdfe788087ddb691817e3b97060d92078b09b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 2 Jul 2023 23:08:58 +0200 Subject: [PATCH 339/370] Only use favorite timeout where supported --- applications/main/archive/scenes/archive_scene_browser.c | 5 ++++- applications/main/bad_kb/bad_kb_app.c | 2 -- applications/main/infrared/infrared.c | 2 -- applications/system/updater/updater.c | 2 -- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 551f68924..c09b3705a 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -62,7 +62,10 @@ static void loader_start_with_gui_error(loader, app_name, param); } else { const char* str = furi_string_get_cstr(selected->path); - if(favorites) { + if(favorites && + (selected->type == ArchiveFileTypeIButton || + selected->type == ArchiveFileTypeLFRFID || selected->type == ArchiveFileTypeNFC || + selected->type == ArchiveFileTypeSubGhz)) { char arg[strlen(str) + 4]; snprintf(arg, sizeof(arg), "fav%s", str); loader_start_with_gui_error(loader, app_name, arg); diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index d2044afe4..e62ad4d3a 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -116,7 +115,6 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->file_path = furi_string_alloc(); app->keyboard_layout = furi_string_alloc(); - process_favorite_launch(&arg); if(arg && strlen(arg)) { furi_string_set(app->file_path, arg); } diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index 2166c448a..d4bfde99f 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -2,7 +2,6 @@ #include #include -#include #define INFRARED_TX_MIN_INTERVAL_MS 50U @@ -450,7 +449,6 @@ int32_t infrared_app(char* p) { bool is_remote_loaded = false; bool is_rpc_mode = false; - process_favorite_launch(&p); if(p && strlen(p)) { uint32_t rpc_ctx = 0; if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { diff --git a/applications/system/updater/updater.c b/applications/system/updater/updater.c index 59af8a3b0..9d3a7bab8 100644 --- a/applications/system/updater/updater.c +++ b/applications/system/updater/updater.c @@ -7,7 +7,6 @@ #include #include #include -#include static bool updater_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -35,7 +34,6 @@ static void Updater* updater_alloc(char* arg) { Updater* updater = malloc(sizeof(Updater)); - process_favorite_launch(&arg); if(arg && strlen(arg)) { updater->startup_arg = furi_string_alloc_set(arg); furi_string_replace(updater->startup_arg, ANY_PATH(""), EXT_PATH("")); From 964f3cf78edfa2e404204304c514f231c54a0389 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 00:40:41 +0200 Subject: [PATCH 340/370] Playlist and remote apps open arg file path --- .../external/ir_remote/infrared_remote_app.c | 27 ++++++++++--------- .../external/subghz_playlist/playlist.c | 6 +++-- .../subghz_remote/subghz_remote_app.c | 22 ++++++++------- .../subghz_remote/subghz_remote_app_i.h | 2 ++ 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c index 409bfddd3..5ced9e71f 100644 --- a/applications/external/ir_remote/infrared_remote_app.c +++ b/applications/external/ir_remote/infrared_remote_app.c @@ -110,8 +110,7 @@ static void app_input_callback(InputEvent* input_event, void* ctx) { furi_message_queue_put(event_queue, input_event, FuriWaitForever); } -int32_t infrared_remote_app(void* p) { - UNUSED(p); +int32_t infrared_remote_app(char* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); dolphin_deed(DolphinDeedPluginStart); @@ -141,22 +140,26 @@ int32_t infrared_remote_app(void* p) { InputEvent event; + FuriString* map_file = furi_string_alloc(); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_file_alloc(storage); - - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px); - browser_options.base_path = IR_REMOTE_PATH; - FuriString* map_file = furi_string_alloc(); - furi_string_set(map_file, IR_REMOTE_PATH); if(!storage_file_exists(storage, IR_REMOTE_PATH)) { storage_common_mkdir(storage, IR_REMOTE_PATH); //Make Folder If dir not exist } - bool res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); - - furi_record_close(RECORD_DIALOGS); + bool res; + if(p && strlen(p)) { + furi_string_set(map_file, p); + res = true; + } else { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".txt", &I_sub1_10px); + browser_options.base_path = IR_REMOTE_PATH; + furi_string_set(map_file, IR_REMOTE_PATH); + res = dialog_file_browser_show(dialogs, map_file, map_file, &browser_options); + furi_record_close(RECORD_DIALOGS); + } // if user didn't choose anything, free everything and exit if(!res) { diff --git a/applications/external/subghz_playlist/playlist.c b/applications/external/subghz_playlist/playlist.c index c300a0020..12191dc96 100644 --- a/applications/external/subghz_playlist/playlist.c +++ b/applications/external/subghz_playlist/playlist.c @@ -693,7 +693,7 @@ void playlist_free(Playlist* app) { free(app); } -int32_t playlist_app(void* p) { +int32_t playlist_app(char* p) { UNUSED(p); dolphin_deed(DolphinDeedPluginStart); @@ -723,7 +723,9 @@ int32_t playlist_app(void* p) { furi_hal_power_suppress_charge_enter(); // select playlist file - { + if(p && strlen(p)) { + furi_string_set(app->file_path, p); + } else { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, PLAYLIST_EXT, &I_sub1_10px); diff --git a/applications/external/subghz_remote/subghz_remote_app.c b/applications/external/subghz_remote/subghz_remote_app.c index 4f88f6358..c0682c75e 100644 --- a/applications/external/subghz_remote/subghz_remote_app.c +++ b/applications/external/subghz_remote/subghz_remote_app.c @@ -19,7 +19,7 @@ static void subghz_remote_app_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -SubGhzRemoteApp* subghz_remote_app_alloc() { +SubGhzRemoteApp* subghz_remote_app_alloc(char* p) { SubGhzRemoteApp* app = malloc(sizeof(SubGhzRemoteApp)); Storage* storage = furi_record_open(RECORD_STORAGE); @@ -91,13 +91,19 @@ SubGhzRemoteApp* subghz_remote_app_alloc() { subghz_txrx_set_need_save_callback(app->txrx, subrem_save_active_sub, app); + if(p && strlen(p)) { + furi_string_set(app->file_path, p); + subrem_map_file_load(app, furi_string_get_cstr(app->file_path)); + scene_manager_next_scene(app->scene_manager, SubRemSceneRemote); + } else { #ifdef SUBREM_LIGHT - scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile); + scene_manager_next_scene(app->scene_manager, SubRemSceneOpenMapFile); #else - scene_manager_next_scene(app->scene_manager, SubRemSceneStart); - scene_manager_set_scene_state( - app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile); + scene_manager_next_scene(app->scene_manager, SubRemSceneStart); + scene_manager_set_scene_state( + app->scene_manager, SubRemSceneStart, SubmenuIndexSubRemOpenMapFile); #endif + } return app; } @@ -146,12 +152,10 @@ void subghz_remote_app_free(SubGhzRemoteApp* app) { free(app); } -int32_t subghz_remote_app(void* p) { +int32_t subghz_remote_app(char* p) { UNUSED(p); dolphin_deed(DolphinDeedPluginStart); - SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc(); - - furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER); + SubGhzRemoteApp* subghz_remote_app = subghz_remote_app_alloc(p); view_dispatcher_run(subghz_remote_app->view_dispatcher); diff --git a/applications/external/subghz_remote/subghz_remote_app_i.h b/applications/external/subghz_remote/subghz_remote_app_i.h index a2ecc0c73..0d25134ad 100644 --- a/applications/external/subghz_remote/subghz_remote_app_i.h +++ b/applications/external/subghz_remote/subghz_remote_app_i.h @@ -52,3 +52,5 @@ bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset); bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced); void subrem_save_active_sub(void* context); + +SubRemLoadMapState subrem_map_file_load(SubGhzRemoteApp* app, const char* file_path); From 0ccdc60921073b571bb09ff71bfdacedea0916bd Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 02:40:26 +0200 Subject: [PATCH 341/370] Support remote and playlist files in archive --- .../main/archive/helpers/archive_browser.h | 3 ++ .../main/archive/helpers/archive_files.c | 26 +++++++++++++++--- .../main/archive/helpers/archive_files.h | 3 ++ .../archive/scenes/archive_scene_browser.c | 6 ++++ .../main/archive/views/archive_browser_view.c | 3 ++ assets/icons/Archive/ir_scope_10px.png | Bin 0 -> 169 bytes assets/icons/Archive/subplaylist_10px.png | Bin 0 -> 5001 bytes firmware/targets/f7/api_symbols.csv | 2 ++ 8 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 assets/icons/Archive/ir_scope_10px.png create mode 100644 assets/icons/Archive/subplaylist_10px.png diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index c2d4c59f8..1b39e9c72 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -27,6 +27,9 @@ static const char* known_ext[] = { [ArchiveFileTypeSubGhz] = ".sub", [ArchiveFileTypeLFRFID] = ".rfid", [ArchiveFileTypeInfrared] = ".ir", + [ArchiveFileTypeSubghzPlaylist] = ".txt", + [ArchiveFileTypeSubghzRemote] = ".txt", + [ArchiveFileTypeInfraredRemote] = ".txt", [ArchiveFileTypeBadKb] = ".txt", [ArchiveFileTypeU2f] = "?", [ArchiveFileTypeApplication] = ".fap", diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index f4c97fe47..cabb8f899 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -1,6 +1,9 @@ #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include +#include +#include #define TAG "Archive" @@ -16,11 +19,26 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder for(size_t i = 0; i < COUNT_OF(known_ext); i++) { if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; if(furi_string_search(file->path, known_ext[i], 0) != FURI_STRING_FAILURE) { - if(i == ArchiveFileTypeBadKb) { - if(furi_string_search(file->path, archive_get_default_path(ArchiveTabBadKb)) == - 0) { + // Check for .txt containing folder + if(strcmp(known_ext[i], ".txt") == 0) { + const char* path = NULL; + switch(i) { + case ArchiveFileTypeSubghzPlaylist: + path = PLAYLIST_FOLDER; + break; + case ArchiveFileTypeSubghzRemote: + path = SUBREM_APP_FOLDER; + break; + case ArchiveFileTypeInfraredRemote: + path = IR_REMOTE_PATH; + break; + case ArchiveFileTypeBadKb: + path = archive_get_default_path(ArchiveTabBadKb); + break; + } + if(path != NULL && furi_string_search(file->path, path) == 0) { file->type = i; - return; // *.txt file is a BadKB script only if it is in BadKB folder + return; } } else { file->type = i; diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index ff74121d7..eccf731c5 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -15,6 +15,9 @@ typedef enum { ArchiveFileTypeSubGhz, ArchiveFileTypeLFRFID, ArchiveFileTypeInfrared, + ArchiveFileTypeSubghzPlaylist, + ArchiveFileTypeSubghzRemote, + ArchiveFileTypeInfraredRemote, ArchiveFileTypeBadKb, ArchiveFileTypeU2f, ArchiveFileTypeApplication, diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index c09b3705a..aa95737b5 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -23,6 +23,12 @@ const char* archive_get_flipper_app_name(ArchiveFileTypeEnum file_type) { return "RFID"; case ArchiveFileTypeInfrared: return "Infrared"; + case ArchiveFileTypeSubghzPlaylist: + return EXT_PATH("apps/Sub-Ghz/subghz_playlist.fap"); + case ArchiveFileTypeSubghzRemote: + return EXT_PATH("apps/Sub-Ghz/subghz_remote.fap"); + case ArchiveFileTypeInfraredRemote: + return EXT_PATH("apps/Tools/ir_remote.fap"); case ArchiveFileTypeBadKb: return "Bad KB"; case ArchiveFileTypeU2f: diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index f3a586cee..e0caa9494 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -29,6 +29,9 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeSubGhz] = &I_sub1_10px, [ArchiveFileTypeLFRFID] = &I_125_10px, [ArchiveFileTypeInfrared] = &I_ir_10px, + [ArchiveFileTypeSubghzPlaylist] = &I_subplaylist_10px, + [ArchiveFileTypeSubghzRemote] = &I_subrem_10px, + [ArchiveFileTypeInfraredRemote] = &I_ir_scope_10px, [ArchiveFileTypeBadKb] = &I_badkb_10px, [ArchiveFileTypeU2f] = &I_u2f_10px, [ArchiveFileTypeApplication] = &I_Apps_10px, diff --git a/assets/icons/Archive/ir_scope_10px.png b/assets/icons/Archive/ir_scope_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d7eaba0e2c1b18ed85bb9ec348ecbfb63a6575 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ihk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!Gcbs$f-s|Jkje+3pq;0SV~9p@X|E$6g969lAODvNCJJxz;NQg8 z`;6IJfzu^AD}H}xN$}#DrmMn?wY|KxYc03`d0F11Iahh@4*6-C`yFp}^IHK;V(@hJ Kb6Mw<&;$VC0WZS< literal 0 HcmV?d00001 diff --git a/assets/icons/Archive/subplaylist_10px.png b/assets/icons/Archive/subplaylist_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..3d3f1d27f64f9cf8361fbd4201db0700db307a5d GIT binary patch literal 5001 zcmeHLeQ?v}85eQJhm)`bx`u6ms17$mGFh@DOR{DM5<4Vba0tO|Qno9TrB{g-+j49r zcG7FFp=_ljv-qJ zK|gZ0{;|8*XG?nD=lMO)^Lu~KE1e#%sjkQ}f6I&@NS3$KQwxv!xq0Sv_`i|-%OQAp zz1?50)e3Pm5>;g-2vDsp0#J}pWCTf^3)TE!_7QaEt)aUfOb6Nh+~Agvvj#uu+`@!T zd5;Ka!JO0Y#WS|g{(c_!bWzcqtbP0Xy85`c2D>d+2loEs*6zm+Ed9=F;jWiA4CS}; zS+Bp{)!(`DxkJQXGmc$ZlfUfK?3*4*YA$Fgc&l(B+gP%v5j%q~Z~DXYPt0w9M?Trp zuk>wmfTldJMiOY@GdSyjGi@A~=vJkr1Dl^55G`^$2Q)~()o z?~JFa4)5@~H($C`c<}DLTlM?+gWN|SUM!lqHXrS=R&TJ@UK-w9);aI%PyB1(%7x2U z=A(VbmmlB*kL*4#7vN8c#DAs{O=kN1*)Tex`Edjl;`B5SVhavmRkcl+~o zC+^Rjx&DYv?q&w25A^O1MJ_CwePGwVIos#1=+;UvRrC}uINWSK_}y#0Fa50g#N5Xo zKU((f;!y8~-OHT5yp{InuK%R#jiL{*H&_4e*fT@#Xl+GnntJ$r^uT=OQ-)>dYta zy54s2-(9aR&d9uw=4yICJ@L@i5=8m1sLhm@mv;GU#~v7Ji^m^Shqoz%L#Azm(SzLn zw9^QZ9a5me>V3<3Q4LuINeuvNA{2qrLlEbZL_`pqfQAM@gA#UGuD){Ef+~{BQfK!O zzK9z%DwXX~u&TYu_;iQxe;F{(r~MGd1mrVvnDG?&E!=h4ypLJ^-Y1s{%$umJhM z6G8;HSqVH8!pD2WwDML6GLq08Jz{>?sCX@isVz|vl(&MgRxlny5>x(>mT1r@M-p)m z1R-b|gHdf0LRNTvH7O6Bf(9iNF}xtz6D%W*s;0&o)kw;GLV~Gjqp`%j+4{f~1Oy#HW(s1%V-LlmswV5+sbJXaWlm9E}0e z&dIEUwaIq-I6!YWrU_vY=zuVURe@k~fV7ho!C#iZ@GBv(N)vQWZ4AqCHpb2{97(Yw zWgB?+foKd0S;w>yRw`M|7>8~(s78IN1Y8!%K-Sldhjl{c1-%0xwlNON;-EX6K+RoElF+uS}GVzqP8uhDc&}GqcLG=_;U_RK^lAsAGIOt6AHrj7v zc{{@sOff->hl5Efa@!YK>rIS0VR)#ZN+qVL(Kcf!+1aZ=bMh*A8B~mxMp2_Vc|lAT zEE;7>My3!gIjP)1JzsjsQz$W2*5X1Glr}(qU6xXP??h`xyALHPs~s(@Q^JxOk1d{< zK)ey?tN59;<6ok(n9vITt1huo2sZ$@KaXq(pIfiY(T)HsV6;p!7(vkh;{=hyYymqf zi828wlAD{$GcWn3ofTx}S zMlysg^HqLE8vm;dfud7ECdKa*T~l;Tih)TPPpNB)u1PU4DdQ=1{m^YS-~a(BI-U)Xl}=`e&ryyexNp0XdU{SVrp&jSDe literal 0 HcmV?d00001 diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index daa9a7448..b7b141ced 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -3456,6 +3456,7 @@ Variable,+,I_iButtonDolphinVerySuccess_108x52,Icon, Variable,+,I_iButtonKey_49x44,Icon, Variable,+,I_ibutt_10px,Icon, Variable,+,I_ir_10px,Icon, +Variable,+,I_ir_scope_10px,Icon, Variable,+,I_keyboard_10px,Icon, Variable,+,I_loading_10px,Icon, Variable,+,I_music_10px,Icon, @@ -3464,6 +3465,7 @@ Variable,+,I_passport_bad_46x49,Icon, Variable,+,I_passport_happy_46x49,Icon, Variable,+,I_passport_okay_46x49,Icon, Variable,+,I_sub1_10px,Icon, +Variable,+,I_subplaylist_10px,Icon, Variable,+,I_subrem_10px,Icon, Variable,+,I_u2f_10px,Icon, Variable,+,I_unknown_10px,Icon, From cdfdbed730a6c1f00f17e20ec495e3775e03e401 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 03:05:17 +0200 Subject: [PATCH 342/370] Archive fix sort idx=0 + hide cursor while loading --- applications/main/archive/helpers/archive_browser.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index a87d23c8f..5234b64da 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -28,7 +28,7 @@ static void { files_array_reset(model->files); model->item_cnt = item_cnt; - model->item_idx = (file_idx > 0) ? file_idx : 0; + model->item_idx = file_idx; load_offset = CLAMP(model->item_idx - FILE_LIST_BUF_LEN / 2, (int32_t)model->item_cnt, 0); model->array_offset = 0; @@ -76,7 +76,7 @@ static void archive_list_item_cb( { if(model->item_cnt <= BROWSER_SORT_THRESHOLD) { FuriString* selected = NULL; - if(model->item_idx > 0) { + if(model->item_idx >= 0) { selected = furi_string_alloc_set( files_array_get(model->files, model->item_idx)->path); } @@ -91,6 +91,10 @@ static void archive_list_item_cb( } } } + + if(model->item_idx < 0) { + model->item_idx = 0; + } } model->list_loading = false; }, From 8a6f321e57fc829c50ee3505e9c3473d895ea587 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:04:06 +0200 Subject: [PATCH 343/370] File browser apis to only change extension filter --- .../services/gui/modules/file_browser_worker.c | 15 +++++++++++++++ .../services/gui/modules/file_browser_worker.h | 7 +++++++ firmware/targets/f7/api_symbols.csv | 2 ++ 3 files changed, 24 insertions(+) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 5f2b14053..6f5461631 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -514,6 +514,21 @@ void file_browser_worker_set_config( furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); } +const char* file_browser_worker_get_filter_ext(BrowserWorker* browser) { + furi_assert(browser); + return furi_string_get_cstr(browser->filter_extension); +} + +void file_browser_worker_set_filter_ext( + BrowserWorker* browser, + FuriString* path, + const char* filter_ext) { + furi_assert(browser); + furi_string_set(browser->path_next, path); + furi_string_set(browser->filter_extension, filter_ext); + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); +} + void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx) { furi_assert(browser); furi_string_set(browser->path_next, path); diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index 02d7266e3..b4b731092 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -58,6 +58,13 @@ void file_browser_worker_set_config( bool skip_assets, bool hide_dot_files); +const char* file_browser_worker_get_filter_ext(BrowserWorker* browser); + +void file_browser_worker_set_filter_ext( + BrowserWorker* browser, + FuriString* path, + const char* filter_ext); + void file_browser_worker_folder_enter(BrowserWorker* browser, FuriString* path, int32_t item_idx); bool file_browser_worker_is_in_start_folder(BrowserWorker* browser); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b7b141ced..97fa8b27b 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -931,10 +931,12 @@ Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, i Function,+,file_browser_worker_folder_exit,void,BrowserWorker* Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, const char*" Function,+,file_browser_worker_free,void,BrowserWorker* +Function,+,file_browser_worker_get_filter_ext,const char*,BrowserWorker* Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_set_filter_ext,void,"BrowserWorker*, FuriString*, const char*" Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback" Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback" Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback" From f993368115325b08b884c7b6f348bcfb57d200b2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:07:06 +0200 Subject: [PATCH 344/370] Prev selection on config change in same parent --- .../gui/modules/file_browser_worker.c | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 6f5461631..06e95425a 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -336,12 +336,41 @@ static int32_t browser_worker(void* context) { furi_assert((flags & FuriFlagError) == 0); if(flags & WorkerEvtConfigChange) { - // If start path is a path to the file - try finding index of this file in a folder - if(browser_path_is_file(browser->path_next)) { - path_extract_filename(browser->path_next, filename, false); - } + if(furi_string_start_with(path, browser->path_next)) { + // New path is parent of current, keep prev selected in new view + furi_string_set(filename, path); + furi_string_right(filename, furi_string_size(browser->path_next)); + furi_string_trim(filename, "/"); + size_t pos = furi_string_search_char(filename, '/'); + if(pos != FURI_STRING_FAILURE) { + furi_string_left(filename, pos); + } - furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); + furi_string_set(path, browser->path_next); + bool is_root = browser_folder_check_and_switch(path); + + int32_t file_idx = 0; + browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + furi_string_set(browser->path_current, path); + FURI_LOG_D( + TAG, + "Config to: %s items: %lu idx: %ld", + furi_string_get_cstr(path), + items_cnt, + file_idx); + if(browser->folder_cb) { + browser->folder_cb(browser->cb_ctx, items_cnt, file_idx, is_root); + } + furi_string_reset(filename); + + } else { + // If start path is a path to the file - try finding index of this file in a folder + if(browser_path_is_file(browser->path_next)) { + path_extract_filename(browser->path_next, filename, false); + } + + furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderEnter); + } } if(flags & WorkerEvtFolderEnter) { From 266538906e8b66e3cba384c37cbe9416014062c4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:12:29 +0200 Subject: [PATCH 345/370] Archive switch types for remote/playlist in tabs --- .../main/archive/helpers/archive_browser.c | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 5234b64da..567ff9c73 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -555,14 +555,32 @@ void archive_enter_dir(ArchiveBrowserView* browser, FuriString* path) { furi_assert(browser); furi_assert(path); - int32_t idx_temp = 0; - - with_view_model( - browser->view, ArchiveBrowserViewModel * model, { idx_temp = model->item_idx; }, false); - furi_string_set(browser->path, path); - file_browser_worker_folder_enter(browser->worker, path, idx_temp); + const char* switch_ext = NULL; + switch(archive_get_tab(browser)) { + case ArchiveTabSubGhz: + if(furi_string_cmp_str(browser->path, EXT_PATH("subghz/playlist")) == 0) { + switch_ext = known_ext[ArchiveFileTypeSubghzPlaylist]; + } else if(furi_string_cmp_str(browser->path, EXT_PATH("subghz/remote")) == 0) { + switch_ext = known_ext[ArchiveFileTypeSubghzRemote]; + } + break; + case ArchiveTabInfrared: + if(furi_string_cmp_str(browser->path, EXT_PATH("infrared/remote")) == 0) { + switch_ext = known_ext[ArchiveFileTypeInfraredRemote]; + } + break; + default: + break; + } + + if(switch_ext != NULL && + strcmp(switch_ext, file_browser_worker_get_filter_ext(browser->worker)) != 0) { + file_browser_worker_set_filter_ext(browser->worker, browser->path, switch_ext); + } else { + file_browser_worker_folder_enter(browser->worker, path, 0); + } } void archive_leave_dir(ArchiveBrowserView* browser) { @@ -571,7 +589,28 @@ void archive_leave_dir(ArchiveBrowserView* browser) { size_t dirname_start = furi_string_search_rchar(browser->path, '/'); furi_string_left(browser->path, dirname_start); - file_browser_worker_folder_exit(browser->worker); + const char* switch_ext = NULL; + switch(archive_get_tab(browser)) { + case ArchiveTabSubGhz: + if(furi_string_cmp_str(browser->path, EXT_PATH("subghz")) == 0) { + switch_ext = known_ext[ArchiveFileTypeSubGhz]; + } + break; + case ArchiveTabInfrared: + if(furi_string_cmp_str(browser->path, EXT_PATH("infrared")) == 0) { + switch_ext = known_ext[ArchiveFileTypeInfrared]; + } + break; + default: + break; + } + + if(switch_ext != NULL && + strcmp(switch_ext, file_browser_worker_get_filter_ext(browser->worker)) != 0) { + file_browser_worker_set_filter_ext(browser->worker, browser->path, switch_ext); + } else { + file_browser_worker_folder_exit(browser->worker); + } } void archive_refresh_dir(ArchiveBrowserView* browser) { From 145f3027f97b90ac633f8ef7a533865fda1d3a7a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 16:19:28 +0200 Subject: [PATCH 346/370] Fix cursor selection bug between archive tabs --- applications/services/gui/modules/file_browser_worker.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 06e95425a..d740cc9d4 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -39,6 +39,7 @@ struct BrowserWorker { FuriString* path_start; FuriString* path_current; FuriString* path_next; + bool keep_selection; uint32_t load_offset; uint32_t load_count; bool skip_assets; @@ -336,7 +337,7 @@ static int32_t browser_worker(void* context) { furi_assert((flags & FuriFlagError) == 0); if(flags & WorkerEvtConfigChange) { - if(furi_string_start_with(path, browser->path_next)) { + if(browser->keep_selection && furi_string_start_with(path, browser->path_next)) { // New path is parent of current, keep prev selected in new view furi_string_set(filename, path); furi_string_right(filename, furi_string_size(browser->path_next)); @@ -537,6 +538,7 @@ void file_browser_worker_set_config( bool hide_dot_files) { furi_assert(browser); furi_string_set(browser->path_next, path); + browser->keep_selection = false; furi_string_set(browser->filter_extension, filter_ext); browser->skip_assets = skip_assets; browser->hide_dot_files = hide_dot_files; @@ -554,6 +556,7 @@ void file_browser_worker_set_filter_ext( const char* filter_ext) { furi_assert(browser); furi_string_set(browser->path_next, path); + browser->keep_selection = true; furi_string_set(browser->filter_extension, filter_ext); furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtConfigChange); } From f96993493d68c02236ff9787abe66023bf90341c Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 18:02:29 +0200 Subject: [PATCH 347/370] Fix small string usage bug --- applications/main/archive/helpers/archive_files.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index cabb8f899..66d547886 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -18,25 +18,25 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder } else { for(size_t i = 0; i < COUNT_OF(known_ext); i++) { if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; - if(furi_string_search(file->path, known_ext[i], 0) != FURI_STRING_FAILURE) { + if(furi_string_end_with_str(file->path, known_ext[i])) { // Check for .txt containing folder if(strcmp(known_ext[i], ".txt") == 0) { - const char* path = NULL; + const char* txt_path = NULL; switch(i) { case ArchiveFileTypeSubghzPlaylist: - path = PLAYLIST_FOLDER; + txt_path = PLAYLIST_FOLDER; break; case ArchiveFileTypeSubghzRemote: - path = SUBREM_APP_FOLDER; + txt_path = SUBREM_APP_FOLDER; break; case ArchiveFileTypeInfraredRemote: - path = IR_REMOTE_PATH; + txt_path = IR_REMOTE_PATH; break; case ArchiveFileTypeBadKb: - path = archive_get_default_path(ArchiveTabBadKb); + txt_path = archive_get_default_path(ArchiveTabBadKb); break; } - if(path != NULL && furi_string_search(file->path, path) == 0) { + if(txt_path != NULL && furi_string_start_with_str(file->path, txt_path)) { file->type = i; return; } From 5520fd3d66c7879f40c778db5891f264260c4cb6 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 18:06:28 +0200 Subject: [PATCH 348/370] Archive apps use switches for easier additions --- .../main/archive/helpers/archive_apps.c | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index b7037e6b7..978911072 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -27,16 +27,20 @@ bool archive_app_is_available(void* context, const char* path) { furi_assert(path); ArchiveAppTypeEnum app = archive_get_app_type(path); + bool res = false; - if(app == ArchiveAppTypeU2f) { - Storage* storage = furi_record_open(RECORD_STORAGE); - bool file_exists = storage_file_exists(storage, U2F_KEY_FILE) && - storage_file_exists(storage, U2F_CNT_FILE); - furi_record_close(RECORD_STORAGE); - return file_exists; - } else { - return false; + Storage* storage = furi_record_open(RECORD_STORAGE); + switch(app) { + case ArchiveAppTypeU2f: + res = storage_file_exists(storage, U2F_KEY_FILE) && + storage_file_exists(storage, U2F_CNT_FILE); + break; + default: + break; } + furi_record_close(RECORD_STORAGE); + + return res; } bool archive_app_read_dir(void* context, const char* path) { @@ -48,10 +52,11 @@ bool archive_app_read_dir(void* context, const char* path) { ArchiveAppTypeEnum app = archive_get_app_type(path); - if(app == ArchiveAppTypeU2f) { + switch(app) { + case ArchiveAppTypeU2f: archive_add_app_item(browser, "/app:u2f/U2F Token"); return true; - } else { + default: return false; } } @@ -64,16 +69,19 @@ void archive_app_delete_file(void* context, const char* path) { ArchiveAppTypeEnum app = archive_get_app_type(path); bool res = false; - if(app == ArchiveAppTypeU2f) { - Storage* fs_api = furi_record_open(RECORD_STORAGE); + Storage* fs_api = furi_record_open(RECORD_STORAGE); + switch(app) { + case ArchiveAppTypeU2f: res = (storage_common_remove(fs_api, U2F_KEY_FILE) == FSE_OK); res |= (storage_common_remove(fs_api, U2F_CNT_FILE) == FSE_OK); - furi_record_close(RECORD_STORAGE); - if(archive_is_favorite("/app:u2f/U2F Token")) { archive_favorites_delete("/app:u2f/U2F Token"); } + break; + default: + break; } + furi_record_close(RECORD_STORAGE); if(res) { archive_file_array_rm_selected(browser); From 761ea8045419eb023a665f46951a8c1ac6473633 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 22:31:54 +0200 Subject: [PATCH 349/370] Archive: New tab for searching files --- applications/main/archive/archive.c | 13 +++ .../main/archive/helpers/archive_apps.c | 12 +- .../main/archive/helpers/archive_apps.h | 2 + .../main/archive/helpers/archive_browser.c | 17 +++ .../main/archive/helpers/archive_browser.h | 3 + .../main/archive/helpers/archive_files.h | 1 + .../archive/scenes/archive_scene_browser.c | 13 +++ .../archive/scenes/archive_scene_config.h | 1 + .../main/archive/scenes/archive_scene_info.c | 2 +- .../archive/scenes/archive_scene_search.c | 105 ++++++++++++++++++ .../main/archive/views/archive_browser_view.c | 20 +++- .../main/archive/views/archive_browser_view.h | 4 + assets/icons/Archive/search_10px.png | Bin 0 -> 600 bytes firmware/targets/f7/api_symbols.csv | 1 + 14 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 applications/main/archive/scenes/archive_scene_search.c create mode 100644 assets/icons/Archive/search_10px.png diff --git a/applications/main/archive/archive.c b/applications/main/archive/archive.c index 5b7638152..2f77eb47c 100644 --- a/applications/main/archive/archive.c +++ b/applications/main/archive/archive.c @@ -51,6 +51,11 @@ static ArchiveApp* archive_alloc() { view_dispatcher, ArchiveViewStack, view_stack_get_view(archive->view_stack)); archive->browser = browser_alloc(); + with_view_model( + archive->browser->view, + ArchiveBrowserViewModel * model, + { model->archive = archive; }, + true); view_dispatcher_add_view( archive->view_dispatcher, ArchiveViewBrowser, archive_browser_get_view(archive->browser)); @@ -64,6 +69,14 @@ void archive_free(ArchiveApp* archive) { furi_assert(archive); ViewDispatcher* view_dispatcher = archive->view_dispatcher; + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneInfo, false); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + // Loading loading_free(archive->loading); diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index 978911072..8fdb14c79 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -5,6 +5,7 @@ static const char* known_apps[] = { [ArchiveAppTypeU2f] = "u2f", + [ArchiveAppTypeSearch] = "search", }; ArchiveAppTypeEnum archive_get_app_type(const char* path) { @@ -35,6 +36,9 @@ bool archive_app_is_available(void* context, const char* path) { res = storage_file_exists(storage, U2F_KEY_FILE) && storage_file_exists(storage, U2F_CNT_FILE); break; + case ArchiveAppTypeSearch: + res = true; + break; default: break; } @@ -48,15 +52,17 @@ bool archive_app_read_dir(void* context, const char* path) { furi_assert(path); ArchiveBrowserView* browser = context; - archive_file_array_rm_all(browser); - ArchiveAppTypeEnum app = archive_get_app_type(path); switch(app) { case ArchiveAppTypeU2f: archive_add_app_item(browser, "/app:u2f/U2F Token"); + archive_file_array_rm_all(browser); + return true; + case ArchiveAppTypeSearch: return true; default: + archive_file_array_rm_all(browser); return false; } } @@ -78,6 +84,8 @@ void archive_app_delete_file(void* context, const char* path) { archive_favorites_delete("/app:u2f/U2F Token"); } break; + case ArchiveAppTypeSearch: + break; default: break; } diff --git a/applications/main/archive/helpers/archive_apps.h b/applications/main/archive/helpers/archive_apps.h index 8bc904587..2e3535e5d 100644 --- a/applications/main/archive/helpers/archive_apps.h +++ b/applications/main/archive/helpers/archive_apps.h @@ -2,12 +2,14 @@ typedef enum { ArchiveAppTypeU2f, + ArchiveAppTypeSearch, ArchiveAppTypeUnknown, ArchiveAppsTotal, } ArchiveAppTypeEnum; static const ArchiveFileTypeEnum app_file_types[] = { [ArchiveAppTypeU2f] = ArchiveFileTypeU2f, + [ArchiveAppTypeSearch] = ArchiveFileTypeSearch, [ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 567ff9c73..c0325ff16 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -489,6 +489,18 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { furi_assert(browser); ArchiveTabEnum tab = archive_get_tab(browser); + if(tab == ArchiveTabSearch) { + ArchiveApp* archive; + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { archive = model->archive; }, false); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + } + browser->last_tab_switch_dir = key; for(int i = 0; i < 2; i++) { @@ -516,6 +528,11 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { if(app_name != NULL) { if(archive_app_is_available(browser, furi_string_get_cstr(browser->path))) { tab_empty = false; + if(tab == ArchiveTabSearch) { + archive_file_array_rm_all(browser); + archive_add_app_item(browser, "/app:search/Search for files"); + archive_set_item_count(browser, 1); + } } } } else { diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 1b39e9c72..8074d2532 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -17,6 +17,7 @@ static const char* tab_default_paths[] = { [ArchiveTabBadKb] = EXT_PATH("badkb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = EXT_PATH("apps"), + [ArchiveTabSearch] = "/app:search", [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_EXT_PATH_PREFIX, }; @@ -33,6 +34,7 @@ static const char* known_ext[] = { [ArchiveFileTypeBadKb] = ".txt", [ArchiveFileTypeU2f] = "?", [ArchiveFileTypeApplication] = ".fap", + [ArchiveFileTypeSearch] = "*", [ArchiveFileTypeUpdateManifest] = ".fuf", [ArchiveFileTypeFolder] = "?", [ArchiveFileTypeUnknown] = "*", @@ -48,6 +50,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadKb] = ArchiveFileTypeBadKb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, + [ArchiveTabSearch] = ArchiveFileTypeSearch, [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index eccf731c5..84a35ec30 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -21,6 +21,7 @@ typedef enum { ArchiveFileTypeBadKb, ArchiveFileTypeU2f, ArchiveFileTypeApplication, + ArchiveFileTypeSearch, ArchiveFileTypeUpdateManifest, ArchiveFileTypeFolder, ArchiveFileTypeUnknown, diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index aa95737b5..df9243f1d 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -301,6 +301,19 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_enter_dir(browser, selected->path); consumed = true; break; + case ArchiveBrowserEventSearch: { + bool open = + !scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneSearch); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + if(open) scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneSearch); + consumed = true; + break; + } case ArchiveBrowserEventFavMoveUp: archive_file_array_swap(browser, 1); consumed = true; diff --git a/applications/main/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h index 63631c65c..00ff4cf38 100644 --- a/applications/main/archive/scenes/archive_scene_config.h +++ b/applications/main/archive/scenes/archive_scene_config.h @@ -2,5 +2,6 @@ ADD_SCENE(archive, browser, Browser) ADD_SCENE(archive, new_dir, NewDir) ADD_SCENE(archive, rename, Rename) ADD_SCENE(archive, delete, Delete) +ADD_SCENE(archive, search, Search) ADD_SCENE(archive, info, Info) ADD_SCENE(archive, show, Show) diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 19bbc900e..14725eaed 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -134,7 +134,7 @@ void archive_scene_info_on_enter(void* context) { if(is_dir) { scene_manager_set_scene_state(instance->scene_manager, ArchiveAppSceneInfo, true); instance->thread = furi_thread_alloc_ex( - "ArchiveDirWalk", 1024, (FuriThreadCallback)archive_scene_info_dirwalk, instance); + "ArchiveInfoDirWalk", 1024, (FuriThreadCallback)archive_scene_info_dirwalk, instance); furi_thread_start(instance->thread); } } diff --git a/applications/main/archive/scenes/archive_scene_search.c b/applications/main/archive/scenes/archive_scene_search.c new file mode 100644 index 000000000..06f995038 --- /dev/null +++ b/applications/main/archive/scenes/archive_scene_search.c @@ -0,0 +1,105 @@ +#include "../archive_i.h" +#include "../helpers/archive_favorites.h" +#include "../helpers/archive_files.h" +#include "../helpers/archive_browser.h" +#include "archive/views/archive_browser_view.h" +#include "toolbox/path.h" +#include + +#define TAG "Archive" + +#define SCENE_SEARCH_CUSTOM_EVENT (0UL) + +void archive_scene_search_text_input_callback(void* context) { + ArchiveApp* archive = (ArchiveApp*)context; + view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_SEARCH_CUSTOM_EVENT); +} + +void archive_scene_search_on_enter(void* context) { + ArchiveApp* archive = context; + + TextInput* text_input = archive->text_input; + strlcpy(archive->text_store, "", MAX_NAME_LEN); + text_input_set_header_text(text_input, "Search for files:"); + + text_input_set_result_callback( + text_input, + archive_scene_search_text_input_callback, + context, + archive->text_store, + MAX_NAME_LEN, + false); + + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); +} + +uint32_t archive_scene_search_dirwalk(void* context) { + furi_assert(context); + ArchiveApp* archive = context; + + uint32_t count = 1; + DirWalk* dir_walk = dir_walk_alloc(furi_record_open(RECORD_STORAGE)); + FuriString* path = furi_string_alloc(); + FileInfo fileinfo; + + if(dir_walk_open(dir_walk, STORAGE_EXT_PATH_PREFIX)) { + while(scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneSearch)) { + DirWalkResult result = dir_walk_read(dir_walk, path, &fileinfo); + if(result == DirWalkError) { + archive_add_app_item(archive->browser, "/app:search/Error while searching!"); + archive_set_item_count(archive->browser, ++count); + break; + } + if(result == DirWalkLast) { + break; + } + if(!file_info_is_dir(&fileinfo) && + furi_string_search_str(path, archive->text_store) != FURI_STRING_FAILURE) { + archive_add_file_item(archive->browser, false, furi_string_get_cstr(path)); + archive_set_item_count(archive->browser, ++count); + } + } + } else { + archive_add_app_item(archive->browser, "/app:search/Error while searching!"); + archive_set_item_count(archive->browser, ++count); + } + furi_string_set( + archive_get_file_at(archive->browser, 0)->path, "/app:search/Search for files"); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + + furi_string_free(path); + dir_walk_free(dir_walk); + return 0; +} + +bool archive_scene_search_on_event(void* context, SceneManagerEvent event) { + ArchiveApp* archive = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_SEARCH_CUSTOM_EVENT) { + archive_file_array_rm_all(archive->browser); + archive_add_app_item(archive->browser, "/app:search/Cancel search"); + archive_set_item_count(archive->browser, 1); + + // Thread here is fine because only the info pane uses it too, + // but only for directories, which are ignored for search + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, true); + archive->thread = furi_thread_alloc_ex( + "ArchiveSearchDirWalk", + 1024, + (FuriThreadCallback)archive_scene_search_dirwalk, + archive); + furi_thread_start(archive->thread); + + scene_manager_previous_scene(archive->scene_manager); + consumed = true; + } + } + return consumed; +} + +void archive_scene_search_on_exit(void* context) { + ArchiveApp* archive = context; + text_input_reset(archive->text_input); +} diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index e0caa9494..c6d595098 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,6 +19,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadKb] = "Bad KB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", + [ArchiveTabSearch] = "Search", [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; @@ -35,6 +36,7 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeBadKb] = &I_badkb_10px, [ArchiveFileTypeU2f] = &I_u2f_10px, [ArchiveFileTypeApplication] = &I_Apps_10px, + [ArchiveFileTypeSearch] = &I_search_10px, [ArchiveFileTypeUpdateManifest] = &I_update_10px, [ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px, @@ -197,7 +199,8 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveFile_t* file = files_array_get( model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); file_type = file->type; - bool ext = model->tab_idx == ArchiveTabBrowser || model->tab_idx == ArchiveTabInternal; + bool ext = strncmp(archive_get_default_path(model->tab_idx), "/app:", 5) == 0 || + model->tab_idx == ArchiveTabBrowser || model->tab_idx == ArchiveTabInternal; if(file_type == ArchiveFileTypeApplication) { if(file->custom_icon_data) { custom_icon_data = file->custom_icon_data; @@ -261,6 +264,10 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m furi_assert(model); const char* tab_name = ArchiveTabNames[model->tab_idx]; + if(model->tab_idx == ArchiveTabSearch && + scene_manager_get_scene_state(model->archive->scene_manager, ArchiveAppSceneSearch)) { + tab_name = "Searching"; + } bool clip = model->clipboard != NULL; canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); @@ -354,6 +361,7 @@ static bool archive_view_input(InputEvent* event, void* context) { ArchiveBrowserView* browser = context; bool in_menu; + int32_t cur_item_idx; bool move_fav_mode; bool is_loading; with_view_model( @@ -361,6 +369,7 @@ static bool archive_view_input(InputEvent* event, void* context) { ArchiveBrowserViewModel * model, { in_menu = model->menu; + cur_item_idx = model->item_idx; move_fav_mode = model->move_fav; is_loading = model->folder_loading || model->list_loading; }, @@ -492,9 +501,12 @@ static bool archive_view_input(InputEvent* event, void* context) { archive_update_offset(browser); } - if(event->key == InputKeyOk) { - ArchiveFile_t* selected = archive_get_current_file(browser); - + ArchiveFile_t* selected = archive_get_current_file(browser); + if(selected && selected->type == ArchiveFileTypeSearch) { + if(event->key == InputKeyOk && event->type == InputTypeShort && cur_item_idx == 0) { + browser->callback(ArchiveBrowserEventSearch, browser->context); + } + } else if(event->key == InputKeyOk) { if(selected) { bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; bool folder = selected->type == ArchiveFileTypeFolder; diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 43322eaf8..f87d59a2b 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -30,6 +30,7 @@ typedef enum { ArchiveTabBadKb, ArchiveTabU2f, ArchiveTabApplications, + ArchiveTabSearch, ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, @@ -53,6 +54,8 @@ typedef enum { ArchiveBrowserEventEnterDir, + ArchiveBrowserEventSearch, + ArchiveBrowserEventFavMoveUp, ArchiveBrowserEventFavMoveDown, ArchiveBrowserEventEnterFavMove, @@ -90,6 +93,7 @@ struct ArchiveBrowserView { }; typedef struct { + ArchiveApp* archive; ArchiveTabEnum tab_idx; files_array_t files; diff --git a/assets/icons/Archive/search_10px.png b/assets/icons/Archive/search_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..6b521107454124c8299cd1f986bacd83f5303fa6 GIT binary patch literal 600 zcmV-e0;m0nP)EX>4Tx04R}tkv&MmKpe$i6Qv@R4t5X`$xxl_q9Ts93Pq?;YK2xEOfLO`CJjl7 zi=*ILaPVWX>fqw6tAnc`2!4RLx;QDiNQwVT3N2zhIPS;0dyl(!fY7Wm&FYB*nr@q! zWJ1d2SEb-9f)EkLC`6fA##}m`g75gcM}V()F`ngr?$0rx7Ayt?B;q(TOq+OvcxKZ! zIPVijSXowy&xt2Yx*+i**ACxG}2TR^4>lKOlcDPX000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0000( zNkl<1qlINi7Cq@!sM90000< literal 0 HcmV?d00001 diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 97fa8b27b..dc24b0d91 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -3466,6 +3466,7 @@ Variable,+,I_passport_DB,Icon, Variable,+,I_passport_bad_46x49,Icon, Variable,+,I_passport_happy_46x49,Icon, Variable,+,I_passport_okay_46x49,Icon, +Variable,+,I_search_10px,Icon, Variable,+,I_sub1_10px,Icon, Variable,+,I_subplaylist_10px,Icon, Variable,+,I_subrem_10px,Icon, From 1fab6080b78a4133634005484fec535695bed37b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:05:33 +0200 Subject: [PATCH 350/370] Dir walk add recurse filter support --- firmware/targets/f7/api_symbols.csv | 1 + lib/toolbox/dir_walk.c | 38 ++++++++++++++++++++++++----- lib/toolbox/dir_walk.h | 10 +++++++- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index dc24b0d91..88d52d6df 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -799,6 +799,7 @@ Function,+,dir_walk_get_error,FS_Error,DirWalk* Function,+,dir_walk_open,_Bool,"DirWalk*, const char*" Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" +Function,+,dir_walk_set_recurse_filter,void,"DirWalk*, const char**, size_t" Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" Function,-,div,div_t,"int, int" Function,+,dolphin_deed,void,DolphinDeed diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index 003863588..e309e1d90 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -13,6 +13,8 @@ struct DirWalk { bool recursive; DirWalkFilterCb filter_cb; void* filter_context; + const char** recurse_filter; + size_t recurse_filter_count; }; DirWalk* dir_walk_alloc(Storage* storage) { @@ -22,6 +24,8 @@ DirWalk* dir_walk_alloc(Storage* storage) { DirIndexList_init(dir_walk->index_list); dir_walk->recursive = true; dir_walk->filter_cb = NULL; + dir_walk->recurse_filter = NULL; + dir_walk->recurse_filter_count = 0; return dir_walk; } @@ -41,6 +45,11 @@ void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context dir_walk->filter_context = context; } +void dir_walk_set_recurse_filter(DirWalk* dir_walk, const char** array, size_t count) { + dir_walk->recurse_filter = array; + dir_walk->recurse_filter_count = count; +} + bool dir_walk_open(DirWalk* dir_walk, const char* path) { furi_string_set(dir_walk->path, path); dir_walk->current_index = 0; @@ -86,13 +95,30 @@ static DirWalkResult } if(file_info_is_dir(&info) && dir_walk->recursive) { - // step into - DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index); - dir_walk->current_index = 0; - storage_dir_close(dir_walk->file); - furi_string_cat_printf(dir_walk->path, "/%s", name); - storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path)); + + bool filter = false; + for(size_t i = 0; i < dir_walk->recurse_filter_count; i++) { + if(furi_string_start_with_str(dir_walk->path, dir_walk->recurse_filter[i])) { + filter = true; + break; + } + } + + if(filter) { + // reset path + size_t last_char = furi_string_search_rchar(dir_walk->path, '/'); + if(last_char != FURI_STRING_FAILURE) { + furi_string_left(dir_walk->path, last_char); + } + + } else { + // step into + DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index); + dir_walk->current_index = 0; + storage_dir_close(dir_walk->file); + storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path)); + } } } else if(storage_file_get_error(dir_walk->file) == FSE_NOT_EXIST) { if(DirIndexList_size(dir_walk->index_list) == 0) { diff --git a/lib/toolbox/dir_walk.h b/lib/toolbox/dir_walk.h index 535237fc6..67c364b47 100644 --- a/lib/toolbox/dir_walk.h +++ b/lib/toolbox/dir_walk.h @@ -43,6 +43,14 @@ void dir_walk_set_recursive(DirWalk* dir_walk, bool recursive); */ void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context); +/** + * Set recurse filtered paths + * @param dir_walk + * @param array + * @param count + */ +void dir_walk_set_recurse_filter(DirWalk* dir_walk, const char** array, size_t count); + /** * Open directory * @param dir_walk @@ -76,4 +84,4 @@ void dir_walk_close(DirWalk* dir_walk); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif From dd31294ad0d3158ada8973305f75b38d6693753a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:06:08 +0200 Subject: [PATCH 351/370] Expose base dolphin animation dir definition --- applications/services/desktop/animations/animation_storage.c | 1 - applications/services/desktop/animations/animation_storage.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 52927225a..9e0998214 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -13,7 +13,6 @@ #include #include #define ANIMATION_META_FILE "meta.txt" -#define BASE_ANIMATION_DIR EXT_PATH("dolphin") #define TAG "AnimationStorage" char ANIMATION_DIR[26 /*"/ext/dolphin_custom//Anims"*/ + XTREME_ASSETS_PACK_NAME_LEN + 1]; char ANIMATION_MANIFEST_FILE[sizeof(ANIMATION_DIR) + 13 /*"/manifest.txt"*/]; diff --git a/applications/services/desktop/animations/animation_storage.h b/applications/services/desktop/animations/animation_storage.h index 16c0feab4..1b9ce44e0 100644 --- a/applications/services/desktop/animations/animation_storage.h +++ b/applications/services/desktop/animations/animation_storage.h @@ -3,6 +3,8 @@ #include #include "views/bubble_animation_view.h" +#define BASE_ANIMATION_DIR EXT_PATH("dolphin") + /** Main structure to handle animation data. * Contains all, including animation playing data (BubbleAnimation), * data for random animation selection (StorageAnimationMeta) and From 685d0b4cd29e5ab1920beee98a40327553831b97 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:06:51 +0200 Subject: [PATCH 352/370] Speed up archive search by ignoring dolphin assets --- applications/main/archive/scenes/archive_scene_search.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/applications/main/archive/scenes/archive_scene_search.c b/applications/main/archive/scenes/archive_scene_search.c index 06f995038..643e6176f 100644 --- a/applications/main/archive/scenes/archive_scene_search.c +++ b/applications/main/archive/scenes/archive_scene_search.c @@ -5,6 +5,7 @@ #include "archive/views/archive_browser_view.h" #include "toolbox/path.h" #include +#include #define TAG "Archive" @@ -39,6 +40,11 @@ uint32_t archive_scene_search_dirwalk(void* context) { uint32_t count = 1; DirWalk* dir_walk = dir_walk_alloc(furi_record_open(RECORD_STORAGE)); + const char* ignore[] = { + XTREME_ASSETS_PATH, + BASE_ANIMATION_DIR, + }; + dir_walk_set_recurse_filter(dir_walk, ignore, COUNT_OF(ignore)); FuriString* path = furi_string_alloc(); FileInfo fileinfo; From 029ff8bcffc62d3f051dd6e032f16218461ebc3d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:13:56 +0200 Subject: [PATCH 353/370] Archive search show no results found --- applications/main/archive/scenes/archive_scene_search.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/applications/main/archive/scenes/archive_scene_search.c b/applications/main/archive/scenes/archive_scene_search.c index 643e6176f..624b9098b 100644 --- a/applications/main/archive/scenes/archive_scene_search.c +++ b/applications/main/archive/scenes/archive_scene_search.c @@ -57,6 +57,10 @@ uint32_t archive_scene_search_dirwalk(void* context) { break; } if(result == DirWalkLast) { + if(count == 1) { + archive_add_app_item(archive->browser, "/app:search/No results found!"); + archive_set_item_count(archive->browser, ++count); + } break; } if(!file_info_is_dir(&fileinfo) && From c7c066d033f652195b7d076099126921df1acd42 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:14:30 +0200 Subject: [PATCH 354/370] Archive search only in filename --- .../main/archive/scenes/archive_scene_search.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/applications/main/archive/scenes/archive_scene_search.c b/applications/main/archive/scenes/archive_scene_search.c index 624b9098b..aee9dd933 100644 --- a/applications/main/archive/scenes/archive_scene_search.c +++ b/applications/main/archive/scenes/archive_scene_search.c @@ -63,10 +63,12 @@ uint32_t archive_scene_search_dirwalk(void* context) { } break; } - if(!file_info_is_dir(&fileinfo) && - furi_string_search_str(path, archive->text_store) != FURI_STRING_FAILURE) { - archive_add_file_item(archive->browser, false, furi_string_get_cstr(path)); - archive_set_item_count(archive->browser, ++count); + if(!file_info_is_dir(&fileinfo)) { + furi_string_right(path, furi_string_search_rchar(path, '/') + 1); + if(furi_string_search_str(path, archive->text_store) != FURI_STRING_FAILURE) { + archive_add_file_item(archive->browser, false, furi_string_get_cstr(path)); + archive_set_item_count(archive->browser, ++count); + } } } } else { From 3c992d45822ebc068f8ab180779f22f062e960f7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:52:05 +0200 Subject: [PATCH 355/370] Fix search flename handling --- applications/main/archive/scenes/archive_scene_search.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/scenes/archive_scene_search.c b/applications/main/archive/scenes/archive_scene_search.c index aee9dd933..65d39e25a 100644 --- a/applications/main/archive/scenes/archive_scene_search.c +++ b/applications/main/archive/scenes/archive_scene_search.c @@ -46,6 +46,7 @@ uint32_t archive_scene_search_dirwalk(void* context) { }; dir_walk_set_recurse_filter(dir_walk, ignore, COUNT_OF(ignore)); FuriString* path = furi_string_alloc(); + FuriString* name = furi_string_alloc(); FileInfo fileinfo; if(dir_walk_open(dir_walk, STORAGE_EXT_PATH_PREFIX)) { @@ -64,8 +65,9 @@ uint32_t archive_scene_search_dirwalk(void* context) { break; } if(!file_info_is_dir(&fileinfo)) { - furi_string_right(path, furi_string_search_rchar(path, '/') + 1); - if(furi_string_search_str(path, archive->text_store) != FURI_STRING_FAILURE) { + furi_string_set( + name, furi_string_get_cstr(path) + furi_string_search_rchar(path, '/') + 1); + if(furi_string_search_str(name, archive->text_store) != FURI_STRING_FAILURE) { archive_add_file_item(archive->browser, false, furi_string_get_cstr(path)); archive_set_item_count(archive->browser, ++count); } @@ -79,6 +81,7 @@ uint32_t archive_scene_search_dirwalk(void* context) { archive_get_file_at(archive->browser, 0)->path, "/app:search/Search for files"); scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + furi_string_free(name); furi_string_free(path); dir_walk_free(dir_walk); return 0; From ba76722158d71c8d39e70dfc9c0728ba8b27039d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:53:26 +0200 Subject: [PATCH 356/370] Empty path macros cause double slashes lol --- applications/external/hex_editor/hex_editor.c | 2 +- applications/external/hex_viewer/hex_viewer.c | 2 +- applications/external/text_viewer/text_viewer.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/external/hex_editor/hex_editor.c b/applications/external/hex_editor/hex_editor.c index 8a4d86c93..fe8eb8b83 100644 --- a/applications/external/hex_editor/hex_editor.c +++ b/applications/external/hex_editor/hex_editor.c @@ -194,7 +194,7 @@ int32_t hex_editor_app(void* p) { if(p && strlen(p)) { furi_string_set(file_path, (const char*)p); } else { - furi_string_set(file_path, EXT_PATH("")); + furi_string_set(file_path, STORAGE_EXT_PATH_PREFIX); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options(&browser_options, "*", &I_edit_10px); diff --git a/applications/external/hex_viewer/hex_viewer.c b/applications/external/hex_viewer/hex_viewer.c index c4c7e75ae..054687744 100644 --- a/applications/external/hex_viewer/hex_viewer.c +++ b/applications/external/hex_viewer/hex_viewer.c @@ -13,7 +13,7 @@ #define TAG "HexViewer" -#define HEX_VIEWER_APP_PATH_FOLDER EXT_PATH("") +#define HEX_VIEWER_APP_PATH_FOLDER STORAGE_EXT_PATH_PREFIX #define HEX_VIEWER_APP_EXTENSION "*" #define HEX_VIEWER_BYTES_PER_LINE 4u diff --git a/applications/external/text_viewer/text_viewer.c b/applications/external/text_viewer/text_viewer.c index 7b742be7f..d1ac686ce 100644 --- a/applications/external/text_viewer/text_viewer.c +++ b/applications/external/text_viewer/text_viewer.c @@ -13,7 +13,7 @@ #define TAG "TextViewer" -#define TEXT_VIEWER_APP_PATH_FOLDER EXT_PATH("") +#define TEXT_VIEWER_APP_PATH_FOLDER STORAGE_EXT_PATH_PREFIX #define TEXT_VIEWER_APP_EXTENSION "*" #define TEXT_VIEWER_BYTES_PER_LINE 20u From c52bc49602f08e75e4c365c741543faf0b086c79 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 00:00:18 +0200 Subject: [PATCH 357/370] fUrI-ReCoRd_cLoSe --- applications/main/archive/scenes/archive_scene_search.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/archive/scenes/archive_scene_search.c b/applications/main/archive/scenes/archive_scene_search.c index 65d39e25a..458708bae 100644 --- a/applications/main/archive/scenes/archive_scene_search.c +++ b/applications/main/archive/scenes/archive_scene_search.c @@ -84,6 +84,7 @@ uint32_t archive_scene_search_dirwalk(void* context) { furi_string_free(name); furi_string_free(path); dir_walk_free(dir_walk); + furi_record_close(RECORD_STORAGE); return 0; } From 6cb7aa649af497e8febc0c750db7afe2f1b34c66 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 00:12:59 +0200 Subject: [PATCH 358/370] Fix u2f archive tab --- applications/main/archive/helpers/archive_apps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index 8fdb14c79..267525ca3 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -56,8 +56,8 @@ bool archive_app_read_dir(void* context, const char* path) { switch(app) { case ArchiveAppTypeU2f: - archive_add_app_item(browser, "/app:u2f/U2F Token"); archive_file_array_rm_all(browser); + archive_add_app_item(browser, "/app:u2f/U2F Token"); return true; case ArchiveAppTypeSearch: return true; From 9d4f38931185763c6fcb2f063deb0ed489edf338 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 00:42:26 +0200 Subject: [PATCH 359/370] Favorite move in actions menu --- .../main/archive/views/archive_browser_view.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index c6d595098..80a785dd0 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -85,12 +85,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveBrowserEventFileMenuNewDir); } if(selected) { - if(favorites) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - "Move", - ArchiveBrowserEventFileMenuRename); - } else if(!selected->is_app) { + if(!selected->is_app) { archive_menu_add_item( menu_array_push_raw(model->context_menu), "Rename", @@ -126,6 +121,12 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveBrowserEventFileMenuShow); } } + if(favorites) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + "Move", + ArchiveBrowserEventFileMenuRename); + } } } size_t size_menu = menu_array_size(model->context_menu); From 5cc3fc99ee6e7f20b2a13201a6d925f5ae5c13fb Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 00:44:19 +0200 Subject: [PATCH 360/370] Support adding search button to favorites --- .../main/archive/scenes/archive_scene_browser.c | 10 +++++++++- .../main/archive/views/archive_browser_view.c | 12 +++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index df9243f1d..e9da22484 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -59,7 +59,15 @@ static void const char* app_name = archive_get_flipper_app_name(selected->type); - if(app_name) { + if(selected->type == ArchiveFileTypeSearch) { + while(archive_get_tab(browser) != ArchiveTabSearch) { + archive_switch_tab(browser, TAB_LEFT); + } + ArchiveApp* archive; + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { archive = model->archive; }, false); + view_dispatcher_send_custom_event(archive->view_dispatcher, ArchiveBrowserEventSearch); + } else if(app_name) { if(selected->is_app) { char* param = strrchr(furi_string_get_cstr(selected->path), '/'); if(param != NULL) { diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 80a785dd0..835a28a68 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -503,13 +503,19 @@ static bool archive_view_input(InputEvent* event, void* context) { } ArchiveFile_t* selected = archive_get_current_file(browser); + bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; if(selected && selected->type == ArchiveFileTypeSearch) { - if(event->key == InputKeyOk && event->type == InputTypeShort && cur_item_idx == 0) { - browser->callback(ArchiveBrowserEventSearch, browser->context); + if((cur_item_idx == 0 || favorites) && event->key == InputKeyOk) { + if(event->type == InputTypeShort) { + browser->callback( + favorites ? ArchiveBrowserEventFileMenuRun : ArchiveBrowserEventSearch, + browser->context); + } else if(event->type == InputTypeLong) { + browser->callback(ArchiveBrowserEventFileMenuOpen, browser->context); + } } } else if(event->key == InputKeyOk) { if(selected) { - bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; bool folder = selected->type == ArchiveFileTypeFolder; if(event->type == InputTypeShort) { From e96f5b566faf09db9e42aa5d16a86d923210002a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 00:52:15 +0200 Subject: [PATCH 361/370] Test equal for recurse filter --- lib/toolbox/dir_walk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c index e309e1d90..144a3528f 100644 --- a/lib/toolbox/dir_walk.c +++ b/lib/toolbox/dir_walk.c @@ -99,7 +99,7 @@ static DirWalkResult bool filter = false; for(size_t i = 0; i < dir_walk->recurse_filter_count; i++) { - if(furi_string_start_with_str(dir_walk->path, dir_walk->recurse_filter[i])) { + if(furi_string_equal_str(dir_walk->path, dir_walk->recurse_filter[i])) { filter = true; break; } From bb169978097b13f30d93592d7d13903b747822bb Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Tue, 4 Jul 2023 12:38:47 +0300 Subject: [PATCH 362/370] [FL-3398] Desktop settings: show icon and name for external applications (#2837) --- .../scenes/desktop_settings_scene_favorite.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index 698cfae1b..c43f8e350 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -1,6 +1,7 @@ #include "../desktop_settings_app.h" #include "applications.h" #include "desktop_settings_scene.h" +#include #include #include @@ -13,16 +14,9 @@ static bool favorite_fap_selector_item_callback( uint8_t** icon_ptr, FuriString* item_name) { UNUSED(context); -#ifdef APP_FAP_LOADER Storage* storage = furi_record_open(RECORD_STORAGE); - bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); + bool success = flipper_application_load_name_and_icon(file_path, storage, icon_ptr, item_name); furi_record_close(RECORD_STORAGE); -#else - UNUSED(file_path); - UNUSED(icon_ptr); - UNUSED(item_name); - bool success = false; -#endif return success; } From 9ad02943a6b91d5d938f7ac7be59242575d37421 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 17:21:07 +0200 Subject: [PATCH 363/370] Fix favorite move callback --- .../main/archive/scenes/archive_scene_browser.c | 13 ++++--------- .../main/archive/views/archive_browser_view.c | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index e9da22484..285b1ebf9 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -287,15 +287,9 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { break; case ArchiveBrowserEventFileMenuRename: archive_show_file_menu(browser, false, false); - if(favorites) { - browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); - //} else if((archive_is_known_app(selected->type)) && (selected->is_app == false)) { - } else { - // Added ability to rename files and folders - scene_manager_set_scene_state( - archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); - scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); - } + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); + scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); consumed = true; break; case ArchiveBrowserEventFileMenuDelete: @@ -331,6 +325,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventEnterFavMove: + archive_show_file_menu(browser, false, false); furi_string_set(archive->fav_move_str, selected->path); archive_favorites_move_mode(archive->browser, true); consumed = true; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 835a28a68..039fb9591 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -125,7 +125,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { archive_menu_add_item( menu_array_push_raw(model->context_menu), "Move", - ArchiveBrowserEventFileMenuRename); + ArchiveBrowserEventEnterFavMove); } } } From 86d3eac2180f6127afce75983e075561d67dd82d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 18:04:11 +0200 Subject: [PATCH 364/370] =?UTF-8?q?Fix=20unitemp=20save=20(you=20don't=20s?= =?UTF-8?q?ave=20in=20GUI=20thread=20=F0=9F=A4=A1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- applications/external/unitemp/Sensors.c | 2 ++ applications/external/unitemp/unitemp.c | 2 ++ applications/external/unitemp/unitemp.h | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c index 11e5a602b..33dd3fa88 100644 --- a/applications/external/unitemp/Sensors.c +++ b/applications/external/unitemp/Sensors.c @@ -397,6 +397,8 @@ bool unitemp_sensors_save(void) { FuriString* filepath = furi_string_alloc(); //Составление пути к файлу furi_string_printf(filepath, "%s/%s", APP_PATH_FOLDER, APP_FILENAME_SENSORS); + //Создание папки плагина + storage_common_mkdir(app->storage, APP_PATH_FOLDER); //Открытие потока if(!file_stream_open( app->file_stream, furi_string_get_cstr(filepath), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { diff --git a/applications/external/unitemp/unitemp.c b/applications/external/unitemp/unitemp.c index 483ff9553..9f56d8eef 100644 --- a/applications/external/unitemp/unitemp.c +++ b/applications/external/unitemp/unitemp.c @@ -74,6 +74,8 @@ bool unitemp_saveSettings(void) { FuriString* filepath = furi_string_alloc(); //Составление пути к файлу furi_string_printf(filepath, "%s/%s", APP_PATH_FOLDER, APP_FILENAME_SETTINGS); + //Создание папки плагина + storage_common_mkdir(app->storage, APP_PATH_FOLDER); //Открытие потока if(!file_stream_open( app->file_stream, furi_string_get_cstr(filepath), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { diff --git a/applications/external/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h index d6d163eb8..320163126 100644 --- a/applications/external/unitemp/unitemp.h +++ b/applications/external/unitemp/unitemp.h @@ -42,7 +42,7 @@ //Версия приложения #define UNITEMP_APP_VER "1.4" //Путь хранения файлов плагина -#define APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX +#define APP_PATH_FOLDER EXT_PATH("apps_data/unitemp") //Имя файла с настройками #define APP_FILENAME_SETTINGS "settings.cfg" //Имя файла с датчиками From fd2c158ad2df0dcd1427a2357562079385d298e7 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 18:13:30 +0200 Subject: [PATCH 365/370] Move SWD path --- applications/external/swd_probe/swd_probe_app.c | 15 ++++++++------- .../{swd_scripts => apps_data/swd}/100us.swd | 0 .../swd}/call_test_1.swd | 0 .../swd}/call_test_2.swd | 0 .../swd}/dump_0x00000000_1k.swd | 2 +- .../swd}/dump_0x00000000_4b.swd | 0 .../{swd_scripts => apps_data/swd}/dump_STM32.swd | 0 .../{swd_scripts => apps_data/swd}/goto_test.swd | 0 .../{swd_scripts => apps_data/swd}/halt.swd | 0 .../{swd_scripts => apps_data/swd}/reset.swd | 0 .../{swd_scripts => apps_data/swd}/test_write.swd | 0 11 files changed, 9 insertions(+), 8 deletions(-) rename assets/resources/{swd_scripts => apps_data/swd}/100us.swd (100%) rename assets/resources/{swd_scripts => apps_data/swd}/call_test_1.swd (100%) rename assets/resources/{swd_scripts => apps_data/swd}/call_test_2.swd (100%) rename assets/resources/{swd_scripts => apps_data/swd}/dump_0x00000000_1k.swd (55%) rename assets/resources/{swd_scripts => apps_data/swd}/dump_0x00000000_4b.swd (100%) rename assets/resources/{swd_scripts => apps_data/swd}/dump_STM32.swd (100%) rename assets/resources/{swd_scripts => apps_data/swd}/goto_test.swd (100%) rename assets/resources/{swd_scripts => apps_data/swd}/halt.swd (100%) rename assets/resources/{swd_scripts => apps_data/swd}/reset.swd (100%) rename assets/resources/{swd_scripts => apps_data/swd}/test_write.swd (100%) diff --git a/applications/external/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c index c53500ae4..9795f12de 100644 --- a/applications/external/swd_probe/swd_probe_app.c +++ b/applications/external/swd_probe/swd_probe_app.c @@ -6,6 +6,8 @@ #include "jep106.h" #include "adi.h" +#define SWD_PATH EXT_PATH("apps_data/swd") + static void render_callback(Canvas* const canvas, void* cb_ctx); static bool swd_message_process(AppFSM* ctx); static uint8_t swd_transfer(AppFSM* const ctx, bool ap, bool write, uint8_t a23, uint32_t* data); @@ -2927,10 +2929,9 @@ static bool swd_message_process(AppFSM* ctx) { break; case ModePageScan: { - FuriString* result_path = furi_string_alloc_printf(EXT_PATH("swd_scripts")); + FuriString* result_path = furi_string_alloc_printf(SWD_PATH); FuriString* preselected = furi_string_alloc_printf( - (strlen(ctx->script_detected) > 0) ? ctx->script_detected : - EXT_PATH("swd_scripts")); + (strlen(ctx->script_detected) > 0) ? ctx->script_detected : SWD_PATH); DialogsFileBrowserOptions options; dialog_file_browser_set_basic_options(&options, "swd", &I_swd); @@ -3000,10 +3001,9 @@ static bool swd_message_process(AppFSM* ctx) { } } else if((ctx->mode_page == ModePageScan) || (ctx->mode_page == ModePageFound)) { uint32_t mode_page = ctx->mode_page; - FuriString* result_path = furi_string_alloc_printf(EXT_PATH("swd_scripts")); + FuriString* result_path = furi_string_alloc_printf(SWD_PATH); FuriString* preselected = furi_string_alloc_printf( - (strlen(ctx->script_detected) > 0) ? ctx->script_detected : - EXT_PATH("swd_scripts")); + (strlen(ctx->script_detected) > 0) ? ctx->script_detected : SWD_PATH); DialogsFileBrowserOptions options; dialog_file_browser_set_basic_options(&options, "swd", &I_swd); @@ -3102,6 +3102,7 @@ int32_t swd_probe_app_main(void* p) { app->gui = furi_record_open(RECORD_GUI); app->dialogs = furi_record_open(RECORD_DIALOGS); app->storage = furi_record_open(RECORD_STORAGE); + storage_common_migrate(app->storage, EXT_PATH("swd_scripts"), SWD_PATH); DBGS("furi_mutex_alloc"); app->swd_mutex = furi_mutex_alloc(FuriMutexTypeNormal); @@ -3129,7 +3130,7 @@ int32_t swd_probe_app_main(void* p) { notification_message(app->notification, &sequence_display_backlight_enforce_on); DBGS("swd_execute_script"); - swd_execute_script(app, EXT_PATH("swd_scripts/startup.swd")); + swd_execute_script(app, SWD_PATH "/startup.swd"); // dolphin_deed(DolphinDeedPluginGameStart); diff --git a/assets/resources/swd_scripts/100us.swd b/assets/resources/apps_data/swd/100us.swd similarity index 100% rename from assets/resources/swd_scripts/100us.swd rename to assets/resources/apps_data/swd/100us.swd diff --git a/assets/resources/swd_scripts/call_test_1.swd b/assets/resources/apps_data/swd/call_test_1.swd similarity index 100% rename from assets/resources/swd_scripts/call_test_1.swd rename to assets/resources/apps_data/swd/call_test_1.swd diff --git a/assets/resources/swd_scripts/call_test_2.swd b/assets/resources/apps_data/swd/call_test_2.swd similarity index 100% rename from assets/resources/swd_scripts/call_test_2.swd rename to assets/resources/apps_data/swd/call_test_2.swd diff --git a/assets/resources/swd_scripts/dump_0x00000000_1k.swd b/assets/resources/apps_data/swd/dump_0x00000000_1k.swd similarity index 55% rename from assets/resources/swd_scripts/dump_0x00000000_1k.swd rename to assets/resources/apps_data/swd/dump_0x00000000_1k.swd index a8870fe30..0c75d8e1b 100644 --- a/assets/resources/swd_scripts/dump_0x00000000_1k.swd +++ b/assets/resources/apps_data/swd/dump_0x00000000_1k.swd @@ -1,6 +1,6 @@ ap_select 0 max_tries 50 block_size 4 -mem_dump /ext/swd_scripts/flash.bin 0x00000000 0x100000 2 +mem_dump /ext/apps_data/swd/flash.bin 0x00000000 0x100000 2 beep 1 message 5 "Reading sucessful" diff --git a/assets/resources/swd_scripts/dump_0x00000000_4b.swd b/assets/resources/apps_data/swd/dump_0x00000000_4b.swd similarity index 100% rename from assets/resources/swd_scripts/dump_0x00000000_4b.swd rename to assets/resources/apps_data/swd/dump_0x00000000_4b.swd diff --git a/assets/resources/swd_scripts/dump_STM32.swd b/assets/resources/apps_data/swd/dump_STM32.swd similarity index 100% rename from assets/resources/swd_scripts/dump_STM32.swd rename to assets/resources/apps_data/swd/dump_STM32.swd diff --git a/assets/resources/swd_scripts/goto_test.swd b/assets/resources/apps_data/swd/goto_test.swd similarity index 100% rename from assets/resources/swd_scripts/goto_test.swd rename to assets/resources/apps_data/swd/goto_test.swd diff --git a/assets/resources/swd_scripts/halt.swd b/assets/resources/apps_data/swd/halt.swd similarity index 100% rename from assets/resources/swd_scripts/halt.swd rename to assets/resources/apps_data/swd/halt.swd diff --git a/assets/resources/swd_scripts/reset.swd b/assets/resources/apps_data/swd/reset.swd similarity index 100% rename from assets/resources/swd_scripts/reset.swd rename to assets/resources/apps_data/swd/reset.swd diff --git a/assets/resources/swd_scripts/test_write.swd b/assets/resources/apps_data/swd/test_write.swd similarity index 100% rename from assets/resources/swd_scripts/test_write.swd rename to assets/resources/apps_data/swd/test_write.swd From 1e700f19a2f90b66f815089b1fa0036b32982dde Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 19:02:08 +0200 Subject: [PATCH 366/370] Bump v49 --- fbt_options.py | 2 +- scripts/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index a1b6032b2..2cd82501d 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -19,7 +19,7 @@ DEBUG = 0 # If OS environment has DIST_SUFFIX set, it will be used instead # How about we add the timestamp automatically. Solves some problems -DIST_SUFFIX = f"XFW-0048_{datetime.datetime.today().strftime('%d%m%Y')}" +DIST_SUFFIX = f"XFW-0049_{datetime.datetime.today().strftime('%d%m%Y')}" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" diff --git a/scripts/version.py b/scripts/version.py index a3732f56b..987fd9b96 100755 --- a/scripts/version.py +++ b/scripts/version.py @@ -1,5 +1,5 @@ #!/usb/bin/env python3 -VERSION = "XFW-0048" +VERSION = "XFW-0049" import json import os From a4dd9f4d8a2a2889bb20718091ac1f29f5722121 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 20:09:30 +0200 Subject: [PATCH 367/370] Update apps (only manifest changes) --- applications/external/4inrow/application.fam | 4 ++++ applications/external/asteroids/application.fam | 9 +++++---- applications/external/barcode_gen/application.fam | 4 ++++ applications/external/bpmtapper/application.fam | 4 ++++ applications/external/brainfuck/application.fam | 4 ++++ applications/external/caesarcipher/application.fam | 4 ++++ applications/external/calculator/application.fam | 4 ++++ applications/external/cntdown_timer/application.fam | 4 ++++ applications/external/counter/application.fam | 4 ++++ applications/external/etch_a_sketch/application.fam | 4 ++++ applications/external/flashlight/application.fam | 4 ++++ applications/external/game_of_life/application.fam | 4 ++++ applications/external/geiger/application.fam | 4 ++++ applications/external/gpioreader_a/application.fam | 5 ++++- applications/external/hex_editor/application.fam | 4 ++++ applications/external/ir_remote/application.fam | 4 ++++ applications/external/mandelbrot/application.fam | 4 ++++ applications/external/mifare_fuzzer/application.fam | 4 ++++ applications/external/music_beeper/application.fam | 8 -------- applications/external/music_tracker/application.fam | 4 ++++ applications/external/nightstand/application.fam | 4 ++++ applications/external/ocarina/application.fam | 4 ++++ applications/external/paint/application.fam | 4 ++++ applications/external/pomodoro/application.fam | 4 ++++ applications/external/pong/application.fam | 4 ++++ applications/external/reversi/application.fam | 4 ++++ applications/external/rootoflife/application.fam | 4 ++++ .../external/rubiks_cube_scrambler/application.fam | 4 ++++ applications/external/scorched_tanks/application.fam | 4 ++++ applications/external/simonsays/application.fam | 2 +- applications/external/slots/application.fam | 4 ++++ applications/external/snake_2/application.fam | 4 ++++ applications/external/t_rex_runner/application.fam | 4 ++++ applications/external/text2sam/application.fam | 4 ++++ applications/external/timelapse/application.fam | 3 ++- applications/external/tuning_fork/application.fam | 4 ++++ applications/external/videopoker/application.fam | 4 ++++ applications/external/wifi_deauther/application.fam | 4 ++++ applications/external/wiiec/application.fam | 4 ++++ applications/external/yatzee/application.fam | 4 ++++ 40 files changed, 152 insertions(+), 15 deletions(-) diff --git a/applications/external/4inrow/application.fam b/applications/external/4inrow/application.fam index 01e2df644..9d0db09ca 100644 --- a/applications/external/4inrow/application.fam +++ b/applications/external/4inrow/application.fam @@ -10,4 +10,8 @@ App( order=90, fap_icon="4inrow_10px.png", fap_category="Games", + fap_author="leo-need-more-coffee", + fap_weburl="https://github.com/leo-need-more-coffee/flipperzero-4inrow", + fap_version="1.0", + fap_description="4 in row Game", ) diff --git a/applications/external/asteroids/application.fam b/applications/external/asteroids/application.fam index 5eb43a6e5..ca5f5d175 100644 --- a/applications/external/asteroids/application.fam +++ b/applications/external/asteroids/application.fam @@ -8,9 +8,10 @@ App( stack_size=8 * 1024, order=50, fap_icon="appicon.png", + fap_icon_assets="assets", fap_category="Games", - fap_icon_assets="assets", # Image assets to compile for this application - fap_description="An implementation of the classic arcade game Asteroids", - fap_author="antirez, SimplyMinimal", - fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Asteroids", + fap_author="@antirez & @SimplyMinimal", + fap_weburl="https://github.com/antirez/flipper-asteroids", + fap_version="1.0", + fap_description="Asteroids game", ) diff --git a/applications/external/barcode_gen/application.fam b/applications/external/barcode_gen/application.fam index 045db6cc7..9c0299617 100644 --- a/applications/external/barcode_gen/application.fam +++ b/applications/external/barcode_gen/application.fam @@ -9,4 +9,8 @@ App( fap_icon="images/barcode_10.png", fap_icon_assets="images", fap_icon_assets_symbol="barcode_app", + fap_author="@Kingal1337", + fap_weburl="https://github.com/Kingal1337/flipper-barcode-generator", + fap_version="1.0", + fap_description="App allows you to display various barcodes on flipper screen", ) diff --git a/applications/external/bpmtapper/application.fam b/applications/external/bpmtapper/application.fam index 8691a1e75..5e824813e 100644 --- a/applications/external/bpmtapper/application.fam +++ b/applications/external/bpmtapper/application.fam @@ -9,4 +9,8 @@ App( fap_icon="bpm_10px.png", fap_category="Media", order=15, + fap_author="@panki27", + fap_weburl="https://github.com/panki27/bpm-tapper", + fap_version="1.0", + fap_description="Tap center button to measure BPM", ) diff --git a/applications/external/brainfuck/application.fam b/applications/external/brainfuck/application.fam index b4cab7be2..9ee518fbe 100644 --- a/applications/external/brainfuck/application.fam +++ b/applications/external/brainfuck/application.fam @@ -11,4 +11,8 @@ App( fap_icon="bfico.png", fap_category="Misc", fap_icon_assets="icons", + fap_author="@nymda", + fap_weburl="https://github.com/nymda/FlipperZeroBrainfuck", + fap_version="1.0", + fap_description="Brainfuck language interpreter", ) diff --git a/applications/external/caesarcipher/application.fam b/applications/external/caesarcipher/application.fam index 487494640..7f973d54a 100644 --- a/applications/external/caesarcipher/application.fam +++ b/applications/external/caesarcipher/application.fam @@ -11,4 +11,8 @@ App( fap_icon="caesar_cipher_icon.png", fap_category="Misc", order=20, + fap_author="@panki27", + fap_weburl="https://github.com/panki27/caesar-cipher", + fap_version="1.0", + fap_description="Encrypt and decrypt text using Caesar Cipher", ) diff --git a/applications/external/calculator/application.fam b/applications/external/calculator/application.fam index 195a62143..1913e5503 100644 --- a/applications/external/calculator/application.fam +++ b/applications/external/calculator/application.fam @@ -9,4 +9,8 @@ App( order=45, fap_icon="calcIcon.png", fap_category="Misc", + fap_author="@n-o-T-I-n-s-a-n-e", + fap_weburl="https://github.com/n-o-T-I-n-s-a-n-e", + fap_version="1.0", + fap_description="Calculator, that can calculate simple expressions", ) diff --git a/applications/external/cntdown_timer/application.fam b/applications/external/cntdown_timer/application.fam index ba22fd4bd..10d610d0f 100644 --- a/applications/external/cntdown_timer/application.fam +++ b/applications/external/cntdown_timer/application.fam @@ -13,4 +13,8 @@ App( order=20, fap_icon="cntdown_timer.png", fap_category="Misc", + fap_author="@0w0mewo", + fap_weburl="https://github.com/0w0mewo/fpz_cntdown_timer", + fap_version="1.0", + fap_description="Simple count down timer", ) diff --git a/applications/external/counter/application.fam b/applications/external/counter/application.fam index 0db32b7ca..3678c644d 100644 --- a/applications/external/counter/application.fam +++ b/applications/external/counter/application.fam @@ -8,4 +8,8 @@ App( ], fap_category="Misc", fap_icon="counter_icon.png", + fap_author="@Krulknul", + fap_weburl="https://github.com/Krulknul/dolphin-counter", + fap_version="1.0", + fap_description="Simple counter", ) diff --git a/applications/external/etch_a_sketch/application.fam b/applications/external/etch_a_sketch/application.fam index 99e97e7d0..22144a685 100644 --- a/applications/external/etch_a_sketch/application.fam +++ b/applications/external/etch_a_sketch/application.fam @@ -9,4 +9,8 @@ App( order=175, fap_icon="etch-a-sketch-icon.png", fap_category="Media", + fap_author="@SimplyMinimal", + fap_weburl="https://github.com/SimplyMinimal/FlipperZero-Etch-A-Sketch", + fap_version="1.0", + fap_description="Turn the Flipper Zero into an Etch A Sketch", ) diff --git a/applications/external/flashlight/application.fam b/applications/external/flashlight/application.fam index ea8eb03d1..030c52990 100644 --- a/applications/external/flashlight/application.fam +++ b/applications/external/flashlight/application.fam @@ -11,4 +11,8 @@ App( order=20, fap_icon="flash10px.png", fap_category="GPIO", + fap_author="@xMasterX", + fap_weburl="https://github.com/xMasterX/flipper-flashlight", + fap_version="1.0", + fap_description="Enables 3.3v on pin 7/C3 when you press Ok and leaves it on when you exit app", ) diff --git a/applications/external/game_of_life/application.fam b/applications/external/game_of_life/application.fam index 6962bf23e..0037dbe4e 100644 --- a/applications/external/game_of_life/application.fam +++ b/applications/external/game_of_life/application.fam @@ -9,4 +9,8 @@ App( order=110, fap_icon="golIcon.png", fap_category="Games", + fap_author="@tgxn (original by @itsyourbedtime)", + fap_weburl="https://github.com/tgxn/flipperzero-firmware/blob/dev/applications/game_of_life/game_of_life.c", + fap_version="1.0", + fap_description="Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.", ) diff --git a/applications/external/geiger/application.fam b/applications/external/geiger/application.fam index 0f20bff78..eca3167e4 100644 --- a/applications/external/geiger/application.fam +++ b/applications/external/geiger/application.fam @@ -10,4 +10,8 @@ App( stack_size=1 * 1024, fap_icon="geiger.png", fap_category="GPIO", + fap_author="@nmrr", + fap_weburl="https://github.com/nmrr/flipperzero-geigercounter", + fap_version="1.0", + fap_description="Works with J305 Geiger tube on external board", ) diff --git a/applications/external/gpioreader_a/application.fam b/applications/external/gpioreader_a/application.fam index 2466ba6f8..c37346096 100644 --- a/applications/external/gpioreader_a/application.fam +++ b/applications/external/gpioreader_a/application.fam @@ -7,5 +7,8 @@ App( stack_size=1 * 1024, fap_category="GPIO", fap_icon="icon.png", - order=1, + fap_author="@aureli1c", + fap_weburl="https://github.com/aureli1c/flipperzero_GPIO_read", + fap_version="1.0", + fap_description="Read GPIO pins states, and display them on the screen", ) diff --git a/applications/external/hex_editor/application.fam b/applications/external/hex_editor/application.fam index cb7f878f1..4b187e469 100644 --- a/applications/external/hex_editor/application.fam +++ b/applications/external/hex_editor/application.fam @@ -13,4 +13,8 @@ App( fap_icon="icons/edit_10px.png", fap_category="Tools", fap_icon_assets="icons", + fap_author="@dunaevai135", + fap_weburl="https://github.com/dunaevai135/flipper-zero-hex_editor", + fap_version="1.0", + fap_description="Read any file line by line, and use Ok to edit char. Useful for NFC file - Edit Dump feature without PC/Phone.", ) diff --git a/applications/external/ir_remote/application.fam b/applications/external/ir_remote/application.fam index e0af447c5..5a98bd904 100644 --- a/applications/external/ir_remote/application.fam +++ b/applications/external/ir_remote/application.fam @@ -10,4 +10,8 @@ App( ], fap_category="Tools", fap_icon="ir_10px.png", + fap_author="@Hong5489 & @friebel & @d4ve10", + fap_weburl="https://github.com/Hong5489/ir_remote", + fap_version="1.0", + fap_description="Bind any IR remote button to each button on flipper d-pad, provides another way to use flipper as IR remote.", ) diff --git a/applications/external/mandelbrot/application.fam b/applications/external/mandelbrot/application.fam index d51058be2..b5a3f4ccc 100644 --- a/applications/external/mandelbrot/application.fam +++ b/applications/external/mandelbrot/application.fam @@ -9,4 +9,8 @@ App( order=130, fap_icon="Mandelbrot.png", fap_category="Media", + fap_author="@Possibly-Matt", + fap_weburl="https://github.com/Possibly-Matt", + fap_version="1.0", + fap_description="The Mandelbrot set is the set of all so-called (complex) numbers that meet Mandelbrots simple arithmetic criterion.", ) diff --git a/applications/external/mifare_fuzzer/application.fam b/applications/external/mifare_fuzzer/application.fam index a186559f2..8674505f7 100644 --- a/applications/external/mifare_fuzzer/application.fam +++ b/applications/external/mifare_fuzzer/application.fam @@ -11,4 +11,8 @@ App( order=30, fap_icon="mifare_fuzzer_10px.png", fap_category="NFC", + fap_author="@spheeere98", + fap_weburl="https://github.com/spheeere98/mifare_fuzzer", + fap_version="1.0", + fap_description="App emulates Mifare Classic cards with various UIDs to check how reader reacts on them", ) diff --git a/applications/external/music_beeper/application.fam b/applications/external/music_beeper/application.fam index a5af4f37c..404988ca6 100644 --- a/applications/external/music_beeper/application.fam +++ b/applications/external/music_beeper/application.fam @@ -14,11 +14,3 @@ App( fap_icon="music_10px.png", fap_category="Media", ) - -App( - appid="music_beeper_start", - apptype=FlipperAppType.STARTUP, - entry_point="music_beeper_on_system_start", - requires=["music_beeper"], - order=30, -) diff --git a/applications/external/music_tracker/application.fam b/applications/external/music_tracker/application.fam index bba3029db..23bcee079 100644 --- a/applications/external/music_tracker/application.fam +++ b/applications/external/music_tracker/application.fam @@ -10,4 +10,8 @@ App( order=20, fap_icon="zero_tracker.png", fap_category="Media", + fap_author="@DrZlo13", + fap_weburl="https://github.com/DrZlo13/flipper-zero-music-tracker", + fap_version="1.0", + fap_description="App plays hardcoded tracker song", ) diff --git a/applications/external/nightstand/application.fam b/applications/external/nightstand/application.fam index 1fb572054..28b8b8694 100644 --- a/applications/external/nightstand/application.fam +++ b/applications/external/nightstand/application.fam @@ -8,4 +8,8 @@ App( fap_icon="clock.png", fap_category="Misc", order=81, + fap_author="@nymda & @Willy-JL", + fap_weburl="https://github.com/nymda/FlipperNightStand", + fap_version="1.0", + fap_description="Clock with screen brightness controls", ) diff --git a/applications/external/ocarina/application.fam b/applications/external/ocarina/application.fam index e6ba5b787..686634023 100644 --- a/applications/external/ocarina/application.fam +++ b/applications/external/ocarina/application.fam @@ -9,4 +9,8 @@ App( order=30, fap_icon="music_10px.png", fap_category="Media", + fap_author="@invalidna-me", + fap_weburl="https://github.com/invalidna-me/flipperzero-ocarina", + fap_version="1.0", + fap_description="A basic Ocarina (of Time), Controls are the same as the N64 version of the Ocarina of Time", ) diff --git a/applications/external/paint/application.fam b/applications/external/paint/application.fam index ceef47ab5..a6f154bac 100644 --- a/applications/external/paint/application.fam +++ b/applications/external/paint/application.fam @@ -9,4 +9,8 @@ App( order=175, fap_icon="paintIcon.png", fap_category="Media", + fap_author="@n-o-T-I-n-s-a-n-e", + fap_weburl="https://github.com/n-o-T-I-n-s-a-n-e", + fap_version="1.0", + fap_description="A basic Paint app, Click Ok to draw dot, hold Ok to enable drawing continuously, hold Back to clear the screen", ) diff --git a/applications/external/pomodoro/application.fam b/applications/external/pomodoro/application.fam index b369db34f..e0d4e9ec0 100644 --- a/applications/external/pomodoro/application.fam +++ b/applications/external/pomodoro/application.fam @@ -8,4 +8,8 @@ App( fap_category="Misc", fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", + fap_author="@Th3Un1q3", + fap_weburl="https://github.com/Th3Un1q3/flipp_pomodoro", + fap_version="1.0", + fap_description="Boost Your Productivity with the Pomodoro Timer", ) diff --git a/applications/external/pong/application.fam b/applications/external/pong/application.fam index 95484b6e7..02dcfd675 100644 --- a/applications/external/pong/application.fam +++ b/applications/external/pong/application.fam @@ -10,4 +10,8 @@ App( stack_size=1 * 1024, fap_icon="pong.png", fap_category="Games", + fap_author="@nmrr & @SimplyMinimal", + fap_weburl="https://github.com/nmrr/flipperzero-pong", + fap_version="1.0", + fap_description="Simple pong game", ) diff --git a/applications/external/reversi/application.fam b/applications/external/reversi/application.fam index c625c1f9c..5401bc489 100644 --- a/applications/external/reversi/application.fam +++ b/applications/external/reversi/application.fam @@ -12,4 +12,8 @@ App( fap_icon="game_reversi.png", fap_category="Games", fap_icon_assets_symbol="game_reversi", + fap_author="@dimat", + fap_weburl="https://github.com/zyuhel/flipperzero-racegame", + fap_version="1.0", + fap_description="Reversi game, the game controls should be intuitive. Longs press on OK opens the menu to start a new game.", ) diff --git a/applications/external/rootoflife/application.fam b/applications/external/rootoflife/application.fam index 8dfe98599..aa3d68211 100644 --- a/applications/external/rootoflife/application.fam +++ b/applications/external/rootoflife/application.fam @@ -11,4 +11,8 @@ App( fap_category="Games", fap_icon_assets="images", fap_icon_assets_symbol="roots_of_life_game", + fap_author="@Xorboo", + fap_weburl="https://github.com/Xorboo/root-of-life", + fap_version="1.0", + fap_description="A zen-puzzle game for FlipperZero, puzzle made on GlobalGameJam23 (theme: Roots)", ) diff --git a/applications/external/rubiks_cube_scrambler/application.fam b/applications/external/rubiks_cube_scrambler/application.fam index 131da4de7..8dee8e952 100644 --- a/applications/external/rubiks_cube_scrambler/application.fam +++ b/applications/external/rubiks_cube_scrambler/application.fam @@ -17,4 +17,8 @@ App( stack_size=1 * 1024, fap_category="Misc", fap_icon="cube.png", + fap_author="@RaZeSloth", + fap_weburl="https://github.com/RaZeSloth/flipperzero-rubiks-cube-scrambler", + fap_version="1.0", + fap_description="App generates random moves to scramble a Rubik's cube.", ) diff --git a/applications/external/scorched_tanks/application.fam b/applications/external/scorched_tanks/application.fam index c32a07bf2..5bccead84 100644 --- a/applications/external/scorched_tanks/application.fam +++ b/applications/external/scorched_tanks/application.fam @@ -9,4 +9,8 @@ App( order=100, fap_icon="scorchedTanks_10px.png", fap_category="Games", + fap_author="@jasniec", + fap_weburl="https://github.com/jasniec/flipper-scorched-tanks-game", + fap_version="1.0", + fap_description="A flipper zero game inspired by scorched earth.", ) diff --git a/applications/external/simonsays/application.fam b/applications/external/simonsays/application.fam index 8165eb22b..d8df4793f 100644 --- a/applications/external/simonsays/application.fam +++ b/applications/external/simonsays/application.fam @@ -9,7 +9,7 @@ App( # fap_version=(0, 1), # (major, minor) fap_icon="simon_says.png", # 10x10 1-bit PNG fap_description="A Simon Says Game", - fap_author="SimplyMinimal,ShehabAttia96", + fap_author="@SimplyMinimal & @ShehabAttia96", fap_weburl="https://github.com/SimplyMinimal/FlipperZero-SimonSays", fap_icon_assets="images", # Image assets to compile for this application ) diff --git a/applications/external/slots/application.fam b/applications/external/slots/application.fam index 27337ca53..7821bdc91 100644 --- a/applications/external/slots/application.fam +++ b/applications/external/slots/application.fam @@ -10,4 +10,8 @@ App( order=30, fap_category="Games", fap_icon_assets="assets", + fap_author="@Daniel-dev-s", + fap_weburl="https://github.com/Daniel-dev-s/flipperzero-slots", + fap_version="1.0", + fap_description="Simple Slots simulator game", ) diff --git a/applications/external/snake_2/application.fam b/applications/external/snake_2/application.fam index f35642b7c..287522983 100644 --- a/applications/external/snake_2/application.fam +++ b/applications/external/snake_2/application.fam @@ -9,4 +9,8 @@ App( order=30, fap_icon="snake_10px.png", fap_category="Games", + fap_author="@Willzvul", + fap_weburl="https://github.com/Willzvul/Snake_2.0", + fap_version="2.0", + fap_description="Advanced Snake Game (Remake of original Snake)", ) diff --git a/applications/external/t_rex_runner/application.fam b/applications/external/t_rex_runner/application.fam index 88623b9c4..99cb0f19a 100644 --- a/applications/external/t_rex_runner/application.fam +++ b/applications/external/t_rex_runner/application.fam @@ -10,4 +10,8 @@ App( fap_icon="trexrunner_icon.png", fap_icon_assets="assets", order=36, + fap_author="@Rrycbarm", + fap_weburl="https://github.com/Rrycbarm/t-rex-runner", + fap_version="1.0", + fap_description="Port of Chrome browser running T-rex game", ) diff --git a/applications/external/text2sam/application.fam b/applications/external/text2sam/application.fam index d278ef8c3..d7d0e42eb 100644 --- a/applications/external/text2sam/application.fam +++ b/applications/external/text2sam/application.fam @@ -14,4 +14,8 @@ App( fap_icon="icon.png", fap_category="Media", order=20, + fap_author="@Round-Pi & (Fixes by @Willy-JL)", + fap_weburl="https://github.com/Round-Pi/flipperzero-text2sam", + fap_version="1.0", + fap_description="Enter text and hear it spoken by SAM (Software Automatic Mouth)", ) diff --git a/applications/external/timelapse/application.fam b/applications/external/timelapse/application.fam index 47a3e750e..a6dc6ad33 100644 --- a/applications/external/timelapse/application.fam +++ b/applications/external/timelapse/application.fam @@ -10,7 +10,8 @@ App( fap_icon_assets="icons", fap_icon="zeitraffer.png", fap_category="GPIO", - fap_description="Simple intervalometer app", + fap_version="1.0", + fap_description="Simple intervalometer app, works via GPIO pins.", fap_author="Aurelius Rosenbaum", fap_weburl="https://github.com/theageoflove/flipperzero-zeitraffer", ) diff --git a/applications/external/tuning_fork/application.fam b/applications/external/tuning_fork/application.fam index 852e0ab71..350eeb573 100644 --- a/applications/external/tuning_fork/application.fam +++ b/applications/external/tuning_fork/application.fam @@ -11,4 +11,8 @@ App( fap_category="Media", stack_size=2 * 1024, order=20, + fap_author="@besya & (Fixes by @Willy-JL)", + fap_weburl="https://github.com/besya/flipperzero-tuning-fork", + fap_version="1.0", + fap_description="Tuning fork for tuning musical instruments", ) diff --git a/applications/external/videopoker/application.fam b/applications/external/videopoker/application.fam index 31c74422a..129881648 100644 --- a/applications/external/videopoker/application.fam +++ b/applications/external/videopoker/application.fam @@ -9,4 +9,8 @@ App( order=270, fap_icon="pokerIcon.png", fap_category="Games", + fap_author="@PixlEmly", + fap_weburl="https://github.com/PixlEmly", + fap_version="1.0", + fap_description="Video poker is a casino game based on five-card draw poker", ) diff --git a/applications/external/wifi_deauther/application.fam b/applications/external/wifi_deauther/application.fam index 5b784bb10..17c6978b9 100644 --- a/applications/external/wifi_deauther/application.fam +++ b/applications/external/wifi_deauther/application.fam @@ -9,4 +9,8 @@ App( order=30, fap_icon="wifi_10px.png", fap_category="WiFi", + fap_author="@Timmotools & @xMasterX", + fap_weburl="https://github.com/Timmotools/flipperzero_esp8266_deautherv2", + fap_version="1.0", + fap_description="Works with ESP8266 Deauther v2 by @SpacehuhnTech (github)", ) diff --git a/applications/external/wiiec/application.fam b/applications/external/wiiec/application.fam index 16be18a2f..e84bba620 100644 --- a/applications/external/wiiec/application.fam +++ b/applications/external/wiiec/application.fam @@ -25,4 +25,8 @@ App( # fap_version=(1,0), fap_icon="WiiEC.png", fap_category="GPIO", + fap_author="@csBlueChip", + fap_weburl="https://github.com/csBlueChip/FlipperZero_WiiEC", + fap_version="1.0", + fap_description="Application to test Wii Extension Controllers.", ) diff --git a/applications/external/yatzee/application.fam b/applications/external/yatzee/application.fam index 45f0f181f..5885082f1 100644 --- a/applications/external/yatzee/application.fam +++ b/applications/external/yatzee/application.fam @@ -9,4 +9,8 @@ App( fap_icon="yatzee_icon_10px.png", fap_category="Games", fap_icon_assets="images", + fap_author="@emfleak", + fap_weburl="https://github.com/emfleak/flipperzero-yatzee", + fap_version="1.0", + fap_description="Yahtzee game", ) From 4556a5e47ab49667fcfc90808e7d838b95616913 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 23:16:01 +0200 Subject: [PATCH 368/370] Hide extensons in favs but show in search --- applications/main/archive/views/archive_browser_view.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 039fb9591..5aba05578 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -200,8 +200,8 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveFile_t* file = files_array_get( model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); file_type = file->type; - bool ext = strncmp(archive_get_default_path(model->tab_idx), "/app:", 5) == 0 || - model->tab_idx == ArchiveTabBrowser || model->tab_idx == ArchiveTabInternal; + bool ext = model->tab_idx == ArchiveTabBrowser || + model->tab_idx == ArchiveTabInternal || model->tab_idx == ArchiveTabSearch; if(file_type == ArchiveFileTypeApplication) { if(file->custom_icon_data) { custom_icon_data = file->custom_icon_data; From 02943dd1db46fdbb45e426de9eb23e320ed6ff78 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 23:30:32 +0200 Subject: [PATCH 369/370] Update subbrute --- .../helpers/subbrute_worker.c | 32 +++++++-- .../helpers/subbrute_worker.h | 8 ++- .../helpers/subbrute_worker_private.h | 1 + .../scenes/subbrute_scene_config.h | 1 + .../scenes/subbrute_scene_setup_attack.c | 2 + .../scenes/subbrute_scene_setup_extra.c | 66 +++++++++++++++++++ .../external/subghz_bruteforcer/subbrute.c | 6 ++ .../subbrute_custom_event.h | 1 + .../external/subghz_bruteforcer/subbrute_i.h | 2 +- .../views/subbrute_attack_view.c | 5 +- .../views/subbrute_main_view.c | 11 +++- 11 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c index 72f9f57b9..ef622482f 100644 --- a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c @@ -28,6 +28,7 @@ SubBruteWorker* subbrute_worker_alloc() { instance->context = NULL; instance->callback = NULL; + instance->tx_timeout_ms = SUBBRUTE_TX_TIMEOUT; instance->decoder_result = NULL; instance->transmitter = NULL; instance->environment = subghz_environment_alloc(); @@ -206,6 +207,7 @@ void subbrute_worker_stop(SubBruteWorker* instance) { furi_thread_join(instance->thread); furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_idle(); furi_hal_subghz_sleep(); } @@ -306,8 +308,9 @@ void subbrute_worker_set_callback( } void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* flipper_format) { + const uint8_t timeout = instance->tx_timeout_ms; while(instance->transmit_mode) { - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); + furi_delay_ms(timeout); } instance->transmit_mode = true; if(instance->transmitter != NULL) { @@ -318,17 +321,20 @@ void subbrute_worker_subghz_transmit(SubBruteWorker* instance, FlipperFormat* fl subghz_transmitter_alloc_init(instance->environment, instance->protocol_name); subghz_transmitter_deserialize(instance->transmitter, flipper_format); furi_hal_subghz_reset(); + furi_hal_subghz_idle(); furi_hal_subghz_load_preset(instance->preset); furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_start_async_tx(subghz_transmitter_yield, instance->transmitter); while(!furi_hal_subghz_is_async_tx_complete()) { - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); + furi_delay_ms(timeout); } furi_hal_subghz_stop_async_tx(); - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); + //furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_idle(); + //furi_hal_subghz_sleep(); + subghz_transmitter_stop(instance->transmitter); subghz_transmitter_free(instance->transmitter); instance->transmitter = NULL; @@ -420,7 +426,7 @@ int32_t subbrute_worker_thread(void* context) { instance->step++; // furi_string_free(payload); - furi_delay_ms(SUBBRUTE_TX_TIMEOUT); + furi_delay_ms(instance->tx_timeout_ms); } flipper_format_free(flipper_format); @@ -435,3 +441,19 @@ int32_t subbrute_worker_thread(void* context) { #endif return 0; } + +uint8_t subbrute_worker_get_timeout(SubBruteWorker* instance) { + return instance->tx_timeout_ms; +} + +void subbrute_worker_timeout_inc(SubBruteWorker* instance) { + if(instance->tx_timeout_ms < 255) { + instance->tx_timeout_ms++; + } +} + +void subbrute_worker_timeout_dec(SubBruteWorker* instance) { + if(instance->tx_timeout_ms > 0) { + instance->tx_timeout_ms--; + } +} \ No newline at end of file diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h index 4046f997c..f7c32dd4b 100644 --- a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h @@ -39,4 +39,10 @@ bool subbrute_worker_can_manual_transmit(SubBruteWorker* instance); void subbrute_worker_set_callback( SubBruteWorker* instance, SubBruteWorkerCallback callback, - void* context); \ No newline at end of file + void* context); + +uint8_t subbrute_worker_get_timeout(SubBruteWorker* instance); + +void subbrute_worker_timeout_inc(SubBruteWorker* instance); + +void subbrute_worker_timeout_dec(SubBruteWorker* instance); diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h b/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h index e38e77dc4..a660ca731 100644 --- a/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker_private.h @@ -21,6 +21,7 @@ struct SubBruteWorker { SubGhzEnvironment* environment; SubGhzTransmitter* transmitter; const char* protocol_name; + uint8_t tx_timeout_ms; // Initiated values SubBruteAttacks attack; // Attack state diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h index 3541df9ac..3c7a3bfda 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_config.h @@ -4,4 +4,5 @@ ADD_SCENE(subbrute, run_attack, RunAttack) ADD_SCENE(subbrute, save_name, SaveName) ADD_SCENE(subbrute, save_success, SaveSuccess) ADD_SCENE(subbrute, setup_attack, SetupAttack) +ADD_SCENE(subbrute, setup_extra, SetupExtra) ADD_SCENE(subbrute, start, Start) \ No newline at end of file diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c index c2877c7cb..5aa5e840a 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_attack.c @@ -81,6 +81,8 @@ bool subbrute_scene_setup_attack_on_event(void* context, SceneManagerEvent event false, instance->device->extra_repeats); scene_manager_next_scene(instance->scene_manager, SubBruteSceneSaveName); + } else if(event.event == SubBruteCustomEventTypeExtraSettings) { + scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupExtra); } else if(event.event == SubBruteCustomEventTypeBackPressed) { subbrute_attack_view_init_values( view, diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c new file mode 100644 index 000000000..c798e055b --- /dev/null +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c @@ -0,0 +1,66 @@ +#include "../subbrute_i.h" +#include "subbrute_scene.h" + +#define TAG "SubBruteSceneLoadFile" + +void setup_extra_widget_callback(GuiButtonType result, InputType type, void* context); + +static void setup_extra_widget_draw(void* context) { + furi_assert(context); + SubBruteState* instance = context; + + Widget* widget = instance->widget; + + widget_add_button_element( + widget, GuiButtonTypeLeft, "-TD", setup_extra_widget_callback, instance); + widget_add_button_element( + widget, GuiButtonTypeRight, "TD+", setup_extra_widget_callback, instance); + + char str[20]; + snprintf(&str[0], 20, "%d", subbrute_worker_get_timeout(instance->worker)); + + widget_add_string_element( + instance->widget, 64, 15, AlignCenter, AlignCenter, FontPrimary, "Time Delay"); + + widget_add_string_element( + instance->widget, 64, 32, AlignCenter, AlignCenter, FontBigNumbers, &str[0]); +} + +void setup_extra_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + SubBruteState* instance = context; + + if((result == GuiButtonTypeLeft) && ((type == InputTypeShort) || (type == InputTypeRepeat))) { + widget_reset(instance->widget); + subbrute_worker_timeout_dec(instance->worker); + setup_extra_widget_draw(instance); + } else if( + (result == GuiButtonTypeRight) && + ((type == InputTypeShort) || (type == InputTypeRepeat))) { + widget_reset(instance->widget); + subbrute_worker_timeout_inc(instance->worker); + setup_extra_widget_draw(instance); + } +} + +void subbrute_scene_setup_extra_on_enter(void* context) { + furi_assert(context); + SubBruteState* instance = context; + + setup_extra_widget_draw(instance); + + view_dispatcher_switch_to_view(instance->view_dispatcher, SubBruteViewWidget); +} + +void subbrute_scene_setup_extra_on_exit(void* context) { + furi_assert(context); + SubBruteState* instance = context; + + widget_reset(instance->widget); +} + +bool subbrute_scene_setup_extra_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} \ No newline at end of file diff --git a/applications/external/subghz_bruteforcer/subbrute.c b/applications/external/subghz_bruteforcer/subbrute.c index dbe83a5c4..f47c75943 100644 --- a/applications/external/subghz_bruteforcer/subbrute.c +++ b/applications/external/subghz_bruteforcer/subbrute.c @@ -94,12 +94,18 @@ SubBruteState* subbrute_alloc() { //instance->flipper_format = flipper_format_string_alloc(); //instance->environment = subghz_environment_alloc(); + // Uncomment to enable Debug pin output on PIN 17(1W) + //furi_hal_subghz_set_async_mirror_pin(&gpio_ibutton); + return instance; } void subbrute_free(SubBruteState* instance) { furi_assert(instance); + // Uncomment to enable Debug pin output on PIN 17(1W) + //furi_hal_subghz_set_async_mirror_pin(NULL); + // SubBruteWorker subbrute_worker_stop(instance->worker); subbrute_worker_free(instance->worker); diff --git a/applications/external/subghz_bruteforcer/subbrute_custom_event.h b/applications/external/subghz_bruteforcer/subbrute_custom_event.h index 2864f8934..4e0c1214d 100644 --- a/applications/external/subghz_bruteforcer/subbrute_custom_event.h +++ b/applications/external/subghz_bruteforcer/subbrute_custom_event.h @@ -12,6 +12,7 @@ typedef enum { SubBruteCustomEventTypeTransmitNotStarted, SubBruteCustomEventTypeTransmitCustom, SubBruteCustomEventTypeSaveFile, + SubBruteCustomEventTypeExtraSettings, SubBruteCustomEventTypeUpdateView, SubBruteCustomEventTypeChangeStepUp, SubBruteCustomEventTypeChangeStepDown, diff --git a/applications/external/subghz_bruteforcer/subbrute_i.h b/applications/external/subghz_bruteforcer/subbrute_i.h index 4ec8f6cd3..9f1bd57d4 100644 --- a/applications/external/subghz_bruteforcer/subbrute_i.h +++ b/applications/external/subghz_bruteforcer/subbrute_i.h @@ -29,7 +29,7 @@ #include "views/subbrute_attack_view.h" #include "views/subbrute_main_view.h" -#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.5" +#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.6" #ifdef FURI_DEBUG //#define SUBBRUTE_FAST_TRACK false diff --git a/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c index d7770bb44..c05fb8084 100644 --- a/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c +++ b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c @@ -72,9 +72,12 @@ bool subbrute_attack_view_input(InputEvent* event, void* context) { instance->is_attacking = true; instance->callback(SubBruteCustomEventTypeTransmitStarted, instance->context); update = true; - } else if(event->key == InputKeyUp) { + } else if(event->key == InputKeyUp && event->type == InputTypeShort) { instance->callback(SubBruteCustomEventTypeSaveFile, instance->context); update = true; + } else if(event->key == InputKeyUp && event->type == InputTypeLong) { + instance->callback(SubBruteCustomEventTypeExtraSettings, instance->context); + update = true; } else if(event->key == InputKeyDown) { instance->callback(SubBruteCustomEventTypeTransmitCustom, instance->context); update = true; diff --git a/applications/external/subghz_bruteforcer/views/subbrute_main_view.c b/applications/external/subghz_bruteforcer/views/subbrute_main_view.c index c21f2ea33..925188db1 100644 --- a/applications/external/subghz_bruteforcer/views/subbrute_main_view.c +++ b/applications/external/subghz_bruteforcer/views/subbrute_main_view.c @@ -167,7 +167,7 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { if(model->index == position) { canvas_draw_str_aligned( canvas, - 4, + 3, 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, AlignLeft, AlignCenter, @@ -181,9 +181,14 @@ void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { sizeof(buffer), "x%d", model->extra_repeats + subbrute_protocol_repeats_count(model->index)); + uint8_t temp_x_offset_repeats = 18; + if(model->extra_repeats + subbrute_protocol_repeats_count(model->index) < + 10) { + temp_x_offset_repeats = 15; + } canvas_draw_str_aligned( canvas, - screen_width - 15, + screen_width - temp_x_offset_repeats, 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, AlignLeft, AlignCenter, @@ -232,7 +237,7 @@ bool subbrute_main_view_input(InputEvent* event, void* context) { #endif const uint8_t min_value = 0; const uint8_t correct_total = SubBruteAttackTotalCount - 1; - uint8_t max_repeats = 9 - subbrute_protocol_repeats_count(instance->index); + uint8_t max_repeats = 14 - subbrute_protocol_repeats_count(instance->index); bool updated = false; bool consumed = false; From 2ada7b3bbb03c276a4c92283419145e764d37e16 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 4 Jul 2023 23:57:01 +0200 Subject: [PATCH 370/370] Add NFC Maker and Jetpack Joyride apps --- .../external/jetpack_joyride/application.fam | 15 + .../jetpack_joyride/assets/air_vent.png | Bin 0 -> 1838 bytes .../jetpack_joyride/assets/alert/frame_01.png | Bin 0 -> 1820 bytes .../jetpack_joyride/assets/alert/frame_02.png | Bin 0 -> 1807 bytes .../jetpack_joyride/assets/alert/frame_rate | 1 + .../jetpack_joyride/assets/barry/frame_01.png | Bin 0 -> 1932 bytes .../jetpack_joyride/assets/barry/frame_02.png | Bin 0 -> 1930 bytes .../jetpack_joyride/assets/barry/frame_03.png | Bin 0 -> 1929 bytes .../jetpack_joyride/assets/barry/frame_rate | 1 + .../jetpack_joyride/assets/barry_infill.png | Bin 0 -> 1614 bytes .../external/jetpack_joyride/assets/bg1.png | Bin 0 -> 835 bytes .../external/jetpack_joyride/assets/bg2.png | Bin 0 -> 968 bytes .../external/jetpack_joyride/assets/bg3.png | Bin 0 -> 886 bytes .../external/jetpack_joyride/assets/coin.png | Bin 0 -> 1842 bytes .../jetpack_joyride/assets/coin_infill.png | Bin 0 -> 1974 bytes .../jetpack_joyride/assets/dead_scientist.png | Bin 0 -> 1768 bytes .../assets/dead_scientist_infill.png | Bin 0 -> 1900 bytes .../external/jetpack_joyride/assets/door.png | Bin 0 -> 1961 bytes .../assets/missile/frame_01.png | Bin 0 -> 1898 bytes .../jetpack_joyride/assets/missile/frame_rate | 1 + .../jetpack_joyride/assets/missile_infill.png | Bin 0 -> 1591 bytes .../jetpack_joyride/assets/pillar.png | Bin 0 -> 1880 bytes .../jetpack_joyride/assets/scientist_left.png | Bin 0 -> 2056 bytes .../assets/scientist_left_infill.png | Bin 0 -> 2211 bytes .../assets/scientist_right.png | Bin 0 -> 1917 bytes .../assets/scientist_right_infill.png | Bin 0 -> 2210 bytes .../external/jetpack_joyride/icon.png | Bin 0 -> 1836 bytes .../includes/background_asset.c | 81 ++++ .../includes/background_assets.h | 34 ++ .../external/jetpack_joyride/includes/barry.c | 33 ++ .../external/jetpack_joyride/includes/barry.h | 23 ++ .../external/jetpack_joyride/includes/coin.c | 98 +++++ .../external/jetpack_joyride/includes/coin.h | 26 ++ .../jetpack_joyride/includes/game_sprites.h | 35 ++ .../jetpack_joyride/includes/game_state.c | 5 + .../jetpack_joyride/includes/game_state.h | 34 ++ .../jetpack_joyride/includes/missile.c | 86 ++++ .../jetpack_joyride/includes/missile.h | 24 ++ .../jetpack_joyride/includes/particle.c | 57 +++ .../jetpack_joyride/includes/particle.h | 21 + .../external/jetpack_joyride/includes/point.h | 14 + .../jetpack_joyride/includes/scientist.c | 77 ++++ .../jetpack_joyride/includes/scientist.h | 29 ++ .../jetpack_joyride/includes/states.h | 9 + .../external/jetpack_joyride/jetpack.c | 379 ++++++++++++++++++ .../external/nfc_maker/application.fam | 14 + applications/external/nfc_maker/nfc_maker.c | 81 ++++ applications/external/nfc_maker/nfc_maker.h | 59 +++ .../external/nfc_maker/nfc_maker_10px.png | Bin 0 -> 4142 bytes .../nfc_maker/scenes/nfc_maker_scene.c | 30 ++ .../nfc_maker/scenes/nfc_maker_scene.h | 29 ++ .../scenes/nfc_maker_scene_bluetooth.c | 57 +++ .../nfc_maker/scenes/nfc_maker_scene_config.h | 13 + .../nfc_maker/scenes/nfc_maker_scene_https.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_mail.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_menu.c | 61 +++ .../nfc_maker/scenes/nfc_maker_scene_name.c | 57 +++ .../nfc_maker/scenes/nfc_maker_scene_phone.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_result.c | 359 +++++++++++++++++ .../nfc_maker/scenes/nfc_maker_scene_text.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_url.c | 53 +++ .../nfc_maker/scenes/nfc_maker_scene_wifi.c | 55 +++ .../scenes/nfc_maker_scene_wifi_auth.c | 83 ++++ .../scenes/nfc_maker_scene_wifi_encr.c | 48 +++ .../scenes/nfc_maker_scene_wifi_pass.c | 53 +++ 65 files changed, 2347 insertions(+) create mode 100644 applications/external/jetpack_joyride/application.fam create mode 100644 applications/external/jetpack_joyride/assets/air_vent.png create mode 100644 applications/external/jetpack_joyride/assets/alert/frame_01.png create mode 100644 applications/external/jetpack_joyride/assets/alert/frame_02.png create mode 100644 applications/external/jetpack_joyride/assets/alert/frame_rate create mode 100644 applications/external/jetpack_joyride/assets/barry/frame_01.png create mode 100644 applications/external/jetpack_joyride/assets/barry/frame_02.png create mode 100644 applications/external/jetpack_joyride/assets/barry/frame_03.png create mode 100644 applications/external/jetpack_joyride/assets/barry/frame_rate create mode 100644 applications/external/jetpack_joyride/assets/barry_infill.png create mode 100644 applications/external/jetpack_joyride/assets/bg1.png create mode 100644 applications/external/jetpack_joyride/assets/bg2.png create mode 100644 applications/external/jetpack_joyride/assets/bg3.png create mode 100644 applications/external/jetpack_joyride/assets/coin.png create mode 100644 applications/external/jetpack_joyride/assets/coin_infill.png create mode 100644 applications/external/jetpack_joyride/assets/dead_scientist.png create mode 100644 applications/external/jetpack_joyride/assets/dead_scientist_infill.png create mode 100644 applications/external/jetpack_joyride/assets/door.png create mode 100644 applications/external/jetpack_joyride/assets/missile/frame_01.png create mode 100644 applications/external/jetpack_joyride/assets/missile/frame_rate create mode 100644 applications/external/jetpack_joyride/assets/missile_infill.png create mode 100644 applications/external/jetpack_joyride/assets/pillar.png create mode 100644 applications/external/jetpack_joyride/assets/scientist_left.png create mode 100644 applications/external/jetpack_joyride/assets/scientist_left_infill.png create mode 100644 applications/external/jetpack_joyride/assets/scientist_right.png create mode 100644 applications/external/jetpack_joyride/assets/scientist_right_infill.png create mode 100644 applications/external/jetpack_joyride/icon.png create mode 100644 applications/external/jetpack_joyride/includes/background_asset.c create mode 100644 applications/external/jetpack_joyride/includes/background_assets.h create mode 100644 applications/external/jetpack_joyride/includes/barry.c create mode 100644 applications/external/jetpack_joyride/includes/barry.h create mode 100644 applications/external/jetpack_joyride/includes/coin.c create mode 100644 applications/external/jetpack_joyride/includes/coin.h create mode 100644 applications/external/jetpack_joyride/includes/game_sprites.h create mode 100644 applications/external/jetpack_joyride/includes/game_state.c create mode 100644 applications/external/jetpack_joyride/includes/game_state.h create mode 100644 applications/external/jetpack_joyride/includes/missile.c create mode 100644 applications/external/jetpack_joyride/includes/missile.h create mode 100644 applications/external/jetpack_joyride/includes/particle.c create mode 100644 applications/external/jetpack_joyride/includes/particle.h create mode 100644 applications/external/jetpack_joyride/includes/point.h create mode 100644 applications/external/jetpack_joyride/includes/scientist.c create mode 100644 applications/external/jetpack_joyride/includes/scientist.h create mode 100644 applications/external/jetpack_joyride/includes/states.h create mode 100644 applications/external/jetpack_joyride/jetpack.c create mode 100644 applications/external/nfc_maker/application.fam create mode 100644 applications/external/nfc_maker/nfc_maker.c create mode 100644 applications/external/nfc_maker/nfc_maker.h create mode 100644 applications/external/nfc_maker/nfc_maker_10px.png create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene.h create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_config.h create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_https.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_name.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_result.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_text.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_url.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c create mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c diff --git a/applications/external/jetpack_joyride/application.fam b/applications/external/jetpack_joyride/application.fam new file mode 100644 index 000000000..1b98e11ce --- /dev/null +++ b/applications/external/jetpack_joyride/application.fam @@ -0,0 +1,15 @@ +# For details & more options, see documentation/AppManifests.md in firmware repo + +App( + appid="jetpack_joyride", + name="Jetpack Joyride", + apptype=FlipperAppType.EXTERNAL, + entry_point="jetpack_game_app", + cdefines=["APP_JETPACK_GAME"], + requires=["gui"], + stack_size=4 * 1024, + order=100, + fap_icon="icon.png", + fap_category="Games", + fap_icon_assets="assets", +) diff --git a/applications/external/jetpack_joyride/assets/air_vent.png b/applications/external/jetpack_joyride/assets/air_vent.png new file mode 100644 index 0000000000000000000000000000000000000000..a7fcf0b203a79875b27f4a90870bf66b8ec4e597 GIT binary patch literal 1838 zcma)7eNYr-7+>=vJkTK&D$80|6O6gry@NZr?G^^^PVR(b2#y2?!R79~yN$cuZFi45 z&{CWMzs4aHwWh|=88TCYMy9E3nlw#?$Q&RiDQiNQoW`Q2X0kz}clYo(B%S`)yM3SE z@A-V6=h+=4#RUsgOH>GgEU?=wrSLxiKJjr6!QXR9<8cTwH<@u(NEMDE(!~Wdw4191 zns9)JID(jR!#wS(0}@&bybMcWV;_Htp^Tft*6JOEgEs>oV`~&ZS!1!&)mY~;y0P3G zl_^X@0|6k>XgJ_!MKVlbid_=E%VHcu6^c|xVHJ)N)XWJ0)ob(`0?SdMCc*6?OD(G> zmEnoPe3HbII35axG@%R)CwOtK(P+d89j?=Md> z23=x2_o2(k3_7f}!|~uO0)c6Xh?2D)7Gkof(*nAix9Du(0bUA3E-1KwwH~n2a-|l~ z8)+5*EddLBC`?D_GSq}YO=LQ?*(6~g_4;%|M-Wl6gL5;UhW{jIky-;Jld){r9&TEq z{|`S}a8hPRf`YC2z=)=F%$%PSU~n*p3?9puJ!%(d1O$N(nzF@5hYG&P(+bADg!S? zPLZRZQ3eP_l{O-2m+YUyc!6^VUBIoB0Gf-SBIl7pv;gwFu!ATp&*NcWqzx#XO~%Ut z0o9?frf~45*$nvrw0O@&#&iol867T<_1*AH_>2Th3L$0>K)r|z?iaYI@aU$3hv=>Y zEbJlyZn7=y*P9Sz))of#!;C&akhHd7+ZToTd5gE@K24>aJ(QxZvN?|@q`x{eXVvq0 zme=ySc30Ol4UJYm(-FA*LfZDBKi3V`lze+An7G)Q7^qu6cx0r$Q?+K~@k?)VuVnm0 zi1SVyZr^j|muj2$cg77@v0x*Xzb|YR;KpecAZR7%{x7$ zxmi5OceL#5*q^fhz`)+((e~b#`wnE+zTVuh0BIF043jZSAqOWsOgK(ABspe6;G?kK0-vJ$vg$ z^O2=#DGArdzae&}ZrIpzyhy+KPTcAoEAD*%pqXdSd>$;{b@R-qL89KF)m3}^!&kyj ze*e{>z~&xx^-%! literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/alert/frame_01.png b/applications/external/jetpack_joyride/assets/alert/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..ac4cca1b1f85f1ac36afa559e601804c50074838 GIT binary patch literal 1820 zcma)7eM}o=7(Y-phI~v%Hc{iwBam&--nEP_Jz5I1eALmV<3q}#lcRmx9?-kn-IbQ= z;+Vpexf#s9m`W=8hcg5WI?b4ah$cqI2Gg*qTa1pGOcsNgK@*3;_pVUN>K6Za+rIDb z_x_%b_j#VaVzZW~Bt4RZAV`Y2!c+yn6W~ftjEA3D@jnIOejQz77wwix(!qL_l#^`) z%7B-HID+U50giGs0}*WmZko|!vu8#zly>T|x_k>^;f%mTR|I)b9kkXsg3S)C6EhSh z=>jAa@B)!S1Kt)!AOm_#)+OO@=@`dQnIbmpF}uZv8d)Bo`O17HffXj9I^O9bt4w8! zm*Gy2c|?&Taoq3sEB$#&mUrVStyYT@YFw>WK!!qSV?-*TV1!IbBCKHof`g|yk!BfG z(xe($pQy($h@(rMdAW!P_^MD5^4p%7&JetgTSdkSx>|L%z;FyabKrhFz!iC zkwzX+A~3n;quH=LoRmoY zAATg^q{I$81xxdu9xdN7vMnqRor5vt$<8iAW~0%@vo5*?VnS7U32H7iYE)W{Mxj=! zWUlmXq>T=M7Q2b|0!Dx}rpNN{!0Mwgm3kSJO;^Ltg8E@S>0wE;mvVzT+9`ULq?V5> z0ppg=7iDBXdYu-ZRNl1=4c{|tL5A$hm>$z=@F*+wF_6?Ss7@{;vVs;wmT!}f!d+Ai zc87Q2Z3`{84~oh~L{bjPz8>Rv*6DKqryL}d3!?(-68#hpiruh&^jNXWMMF>9P}rHY zn*ltkMqx%_+po~{djM3t;~ArB1z+?ImzsLXJslnsfKDbvO#yf>ds}<+8!SqTl$zhrZExa?(xSpkfAp&pSybc404nd`j6=%@@=qWbvKCg>9of!g1|= zM{Q49Mk7$MgDc%v`_v^xom=o9(Oeemp(Cb8h0Ff*<-+ z&$M^0|9MU6)X>Lxo?df zGNqI^+09Kf)u!F^YmN2f)i0knKbQL40^uVb^2hM8|78Ix{vHx*kX~4)s|_ z&Kx$J8@|5fTJ0@Qe>ZAATD(K3(r2rb!Ae)xr{6D(?wOj-?B(ZL@$YsePrtMAgg;~_ zUY&J(zNvTbT*iQUYJM*B;oIqFZBu`zem0V@;c~5FX!WrxlYhM1d5SX+b=6>$E73u#FpD{7M6IbM`l+2nbuHr;Y!^LaSsl=N1xj6#^4{)8m^*VK#V`e}7&46BmEU=$>0gKdwfQ{apHAAAQxUf^5WG LYBdd%H0=8q6;5LQ literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/alert/frame_02.png b/applications/external/jetpack_joyride/assets/alert/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..c3955f090f5c9e496e536ae34cb206384191a0bb GIT binary patch literal 1807 zcma)7ZA=?w94{km40+MXUQiP+M_A^dy=yzV^ypEb<)x02ju$D5PLB3zd(eB?-Ic!R z;@AQz8#9=_$1RBa;0ytSnP%W1qRA4612t^w7NcVyCX2z$;0K4n=dQez)h&Lw_MYeW zdw&0y=l}oT+m5R8w3KI3R4P@Pt-@Lj|C8X8n!E=7=B)W80@qK|wNA-tuOwU?qoLef z6VQYh9^xvM$sFP-R|}AkCg7!65}h3$MG@LfqV@T9+|FBokFJOape9mP>x#6vjBeCi zm|_YMP=Emvg@hPCD-t0RRdfmXE^lKfqEMt35_Q@gh=mgXlCR0v;AmkAViMdQqS{)v zco?oo)F(+ifnmX5P!rT^IKhi)jYcDe>oA>84H;@N%t};9&5GHwL{!5HM3+GG63wxQ ztVuO-0f|IWh$Bm$G5oSPD=tP4^MHjY9@A=YESAjsIEfQ|+#`-G!*K^eh+c_dl^Gd{ zqf4~s5p)@eqr+U=?T?RwVO9kqN~NtZ5sOJ(<xXm0Un; zghc?91g!8OQ5~++t8s%G&#%>K3EV*JFy`Vq9A75eIXCSI|0j7Tp~oSah-Jg_a8nZX zfB4G@CuDZiDOj41^=Rdch4XU)bPmR#SDameY!-_{;5@V+Vq$f931Ta?7_>%%L9Nqh z6|Q`5go6$Nztc)HfE8hlk!b!ySVIh^)vbVv=^EHsP(P|CUo2r`C@-j|-I8xfYGu0; zuwHq8QAPpe+wlkF@~&fPc%M-V3gleDNYrS+Vyra8L2|<&CZ&vs3R;vnA*^ggyQl{2 zi>{(eKdrP6f+$5qP%ha%iSh#H4!D3@2@=XhQIYdVK}rC{URXaQTI})A(9!Q!lw^!r}rjDTJ6Q0MAAD;Br1Bw$G#tH>*{O1-_L*cZfbe^MEkn@OFe&FzEJ-4yhrFAH*aluzb7N_?ATFj zT6we6)=X1jTeX@B)1rv6U}en|#_ezp!9V+|bxmlyvB9hN}2{;>Imq(^us0n60$R+F#Om@LxZYToM2P literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/alert/frame_rate b/applications/external/jetpack_joyride/assets/alert/frame_rate new file mode 100644 index 000000000..e440e5c84 --- /dev/null +++ b/applications/external/jetpack_joyride/assets/alert/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/applications/external/jetpack_joyride/assets/barry/frame_01.png b/applications/external/jetpack_joyride/assets/barry/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..8abdcaf6150ec448cba1279fd790c03985e4778e GIT binary patch literal 1932 zcma)7eM}Q~7(O>=80r*1rgJg59Y&_6z4rP+dbA+4d=xoWp=u#+di1XCNqcv@yULX@ z+{|!nGcr(v5dIKMoI|6iac*u7KjN4l(@X_JTy(l6nl0)aDC(H*cUL|IxBYSL{odz! zzn**Vy^mbEnG<7Y#X=A?(V6AQ1OMZ}H7Vvf@OyYglOKX!a4>F<IxHR;fVem*%>?rNv$gg`_0M zT4fvr2+Q(UOu9ZHYn434T;>u~<+YhGLioFf?L>l}K5`it$Q_XbcA}dIg4;7>htl}~Cvjqc;|D4l<}(0~aFiI|Gx8|N z2s)5&^b9h|k47elf2W5+!`cv~^wmI!K2e9Y(4RnbM|c{|qeU((cGP{UJ@({6VOoR0|t zOw7wngPrMiv)*DhYcOpzhoU3y43U01hi3g!03?sb9|V>$lxiR1pfnf^D-*JWWq>4@ znpbu43}QAU5krS~6^sgjhlUeix+zj39i#**#dMfKqcdqRy&J=D%zzt>3v?K+)2Ufh zK1VUW$bYewiH5C!PY`$(BTAeQQBBb?&!^vxo}$Mfqt3T7;R>JhD&5%-Uf`&(m!{Oz z>ijFH$oZraQlL})-~ro^RG*IlIz(Xb>M(wm7GMmlvwbUp-b-*#iA?S|k%)bcjoO3SXTQ#>% zOzJ7S-E&1e)U&)L6I<1><8G?C_R{gjtyogxo-g0x&*#2+U~E%!b#r1Z_ zecD-7TV6`1Z!WvaJ)U#AX6LD0d)J+vGVlDacPkGe?Ng_0Y)YUg&;CbG4(`biwzhf> z>toLPFO=OGcgZ~Mi=3|Czc-F=`&nLldbwrI!^z`P99QQT-+n%D<-M+*`(D0W@d}&c=wpaE;=gffOX7#% z6ZRsXRUm?|-c;op1JyUo1jdz4N2+_~=CCXEr6cf!DHoosshh3a@Z;{!3Ma?FUj?BD z8|O5{wfK52oVj_uzKcJC7RJik9+cOfc`fPaBiB**mAaa18HVE~$KSVpncrUDEN#0r myL(Gp`ofF%$nM_V-B6P&|8eVyd6Sfns53p+u{&+$+J6AnqoRNS literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/barry/frame_02.png b/applications/external/jetpack_joyride/assets/barry/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..5a4587ac8f56f2c9ee981d4811def57ed952329e GIT binary patch literal 1930 zcma)7du$VR96uDv81fhoAv4CyjZgvGYj0g=Z|&yR?ors)F$(Jlg16qU?QQMdd3R;k zNe~yth7w^sGC=--B@$kd0U`!P5Tlca5RsJ(2#7I4FagE}lSih%yY9(``p32R`+h#( z*XO?Xy;Vi-{LzWYi3oy>b`>~_;r}RjjZJtC{vKW27(kF0oUF$yd#OUw$A=9x!kl#xYc*$Q5?XOBJCGUIb{GA$kLoY=+nO5|il%j@+$8ZA4C$fUf@*Jwh zq|5k-Y{xK&qus%Q!uCbyr0({i5V(rqCW8_0PZxr`%u7LD=&fjgPcJ;kQD*(m$fF{I z=up0)Gbpq$6q%y_ogNMkXhV{7mp~!9MIF#WPXfsk6#!lgBt9bgK<*O2$tju=sxG91 z5Aot$I2yo?nflB{tO7z_CmRNw1Vx9~(_m?TFirF96)hf>;u#n;^(n~Za1@EWpAA7w zD$dVAUAYd6$!f9a2}3-Gsw3$N(*aNjxPTml$>Z_+fD)F`?EM_n215~bLgsNSl!Vao zYA)VB%#NiKct5YwAtCtCND59jL(8<2mSLrYkx11W)AWSNLl7j9P7-M|jRa{lYFRWs z&#?aJf3ekxMs1K!QDgxt$-EfVOz|N;>QrH&9^$?DxdbL-PtigHWPmhUr8gv-g~$(a)#+!V8~XUtwR>0!%np88|@ zSjzRJro#y{cO`y2uW3u{;E8+UmDV#KE;zF0{<rPuhbsi)WNIkk8H%CqCApZ~qB?g-X0Vf^|=9m9AJKKlFczC3Z;G4D}R z!r8ztOYe-hXqos$Vf&vynnyMNs#Kp|Xnp_TxDi>-YcnctKOelhynWAsmoC?QMBP*_ zG*)$#h;^6OyOfNY9oFE{4<6N&otyP|tL;tZ?Qd=+KX|Y`|7<$mg|Np1H%@IzuZ5qm zkwQVK1iyL{)N2^BW$P4ZT;y_Qd!}rRterFFxG^E;)3&Y>|EsURl6|9LV~Ke=Q#$Of z&h&Ob{9%5kA-h_<;j1!+qx{#+L$X=JEoesvS9^&X=LZPN1rrDZe42Z$kpB3 i)Uiwd`P3bMxW`_&Pj#6`)hz-_=Nw4ky_x+yV zefPWfoJDz);%CQ05H!h=Z!ZSF6TtOc+*t5^bXl_xg2vfdms@s|g@lI>>M5G9VDw5* z05}AhvlM~yR5CJL!T4Ctf^>a-9D!Ndf|RC_7%A8oKbv1IG9}eTE>Cr($3!Dpnek?Y z011MOOuh0)e7cRaN?`6g@BcP=m>2LNOf0aUJl`NnuW=6dfleswtv5?2P0QSwUub4pwtg z6?{mxAPB(WzT}L;4rb@1zVQJOsEVKlJ%$c-7yP`;OMYG$=xA8X06Zd4X1!0Tqaq{R z0ez!aP$*$EXOjGP_h4|?7?PZ`0ua$h>aY>|3rMc8z@Wv9#D_!=le2>1l#eN7IG^kX#iRKLfl`*%>_Y<70RtgbA&WQ`P=af9 zwGj6pWzS`K%=KVpuapWnRL(89hah0anwus&Lhx@~G2U5JBYWkcXkQ(whEN zRN}pI6(urOAGpC5#On33fQK*)9v#-lF(Qn^xE|A|4v(txGazICbOxgfHxQVCNW=Q3 zLr3jLQ{7YhiK73jAMjN6_Y;j8-p@$3`}#4X>K5py%Z!d7H0m&9ZGbtVHn5JsUPA{K z8#oNCMur3XPy~yw{FXpM(3sUM5bFq$LeMMqq54b7_Jz|=XQg=7KdQ?vbAOY%$D6w2 zOkh*}x`x!;`v1@mF+E;cvQrWg^ zW#_WWx}=FS0-=+(T`f0iPp?f{+}(64<799-F?(j_w0k#2>-CHU8)n>Z`^{Bs2B>i3R3L7uv zmr3BQH%-09Kuud`0pn7K-RhdPIpnlXK7qx}|M2l+JmZHiV?Irq5rgFGlcCIpwJBp4 z|8=bCg{4bEvCr0g-m+@usfSnS$sOY()^TfSx?tU$3lrO?-0gX(bZ6vFLHf_VYhLU= fEO*iJ`OTS~pE-LAw;pO#-=L11BKsHFZ>;_YytSIB literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/barry/frame_rate b/applications/external/jetpack_joyride/assets/barry/frame_rate new file mode 100644 index 000000000..e440e5c84 --- /dev/null +++ b/applications/external/jetpack_joyride/assets/barry/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/applications/external/jetpack_joyride/assets/barry_infill.png b/applications/external/jetpack_joyride/assets/barry_infill.png new file mode 100644 index 0000000000000000000000000000000000000000..9462801f02c43ffc7bc9fc70ab9b9ea902a908e4 GIT binary patch literal 1614 zcmcIlJ#W)M7mTfsOwFD?`=to}JGo6cz?MxqF`X>$&${zuekfo1U7P5(Hto zv|iYT-y87UoPd`wvpV@+5UzbAdPUmLu2a1R=K+`IdcvnYfQ@#51Iy zU%pBraSUlUW2&Z~$91ya4)9KUvuw8y?5ra#El%Yk9UQorSz_ciyikt}DfX+wf4;3q zVoYHNhEy@P#5@hKn2|HGDlJZlxxlIF+lAGRF{})!&X}(&O0(INn`xN_HATy2vxu+z72sbJP;$klZ(E+;*?hS&O*s>>D z;?<-MI89%IO8NX&K&zwySh&5mB9@Bzc`Z9XpF(mHgX^f5T&spRuvcSs2%h-w1G~hD z_XiAkfsI4nA&)%*lAt(lY*OiChO~f`0js^S5FDCrLU%hBvkDf2Op%JxDK(Qq3uRT) zRivlq9>H5x<0vtoI;7hA8=H5um;=6P>gyzAG-$9IzoRSE~ecXo+xjNX^56h(si-^4zdhvyQ=Iw>oXU zjG#JA&!jumDgQSgxRqDsgS0Y2I!fzvUA4#O6W@YA>XV@Vq|ZP;iPf+^qo?1=C#UeK z)IGkOGIGEPVPK4buE`#ZN0_rpZ_vV`H>$A*^ErS){ObPaA27tP`|Eol%>CQ^nGoK- zy9b+Msjyt0d}IH*cs_A_{!l%8a&r3d4os(eD;=bcIWPc58vBW{uwA0Hw&Lu Ho*w@ODWKxk literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/bg1.png b/applications/external/jetpack_joyride/assets/bg1.png new file mode 100644 index 0000000000000000000000000000000000000000..82d614e1cc45e9a74c6d44d4ae45b772807aae8d GIT binary patch literal 835 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|?*>baoCX4sv%=@N{)HGqf}_ z(>2mFWMI&kSUT~%wo9PMaeHGQWw$Aj8e;m1rY3H_Ia6k~Ofqqk%t@Q~bK$e&ulG(& zS)H?7W8=r{gYgH#BDGzYq@130!@%}=WtK{D-t&3q|K6+i`#z8RTt?Vg$*pxKo_1HI zm@GMS?3u`SVZB=)g8J>(N!|KzIL|r}qH=DQH)0x$M?uQAhUb;Rs zeAqd4QQF688^X^X5pVTxsofP5@Z;DeVKe*aIop0-PO_EaY~CCh;raj35&baZD!+(0 zzjVFbYyRCiUe&2SbN-wzHmPi|;UB7-Mv{pZT(m zywh3cInT9!w$@5i2=5KKX!U(TrmUa7R#cMoIkN>{YPx1s;*b3=De8Ak0{?)V>TD zL7AQ|jv*Cu-p)BViAj-%b-M2V|IMv0h^{JmuI#%rSa=m@{%nKHon~S}#Y*?=Ru=#TBivFikZ4_Zx%pk#fK!btGLf((FCW)LCj5w*^b@qH$g|R*3$>ZJA zpMTGrwIO`binj-UOzldv?mo@$XZ&-@1WBtft$*(x xShl0Tw<`T`xX?Esf3NW-AFq#6TnHEY&z`#_Q`kX&5-=thJYD@<);T3K0RWSjY2^R_ literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/bg2.png b/applications/external/jetpack_joyride/assets/bg2.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8590d3ada26094c39a3019b72084423c75ec41 GIT binary patch literal 968 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|?*>baoCX4sv%=@N{)HGqf}_ z(>2mFWMI&kSUT~%wo9PMaeHGQWw$Aj8e;m1rY3H_Ia6k~Ofqqk%t@Q~bK$e&ulG(& zS)H?7W8=r{gYgH#BDGzYq@130!@%}=WtK{D-t&3q|K6+i`#z8RTt?Vg$*pxKo_1HI zm@GMS?3u`SVZB=)g8J>(N!|KzIL|r}qH=DQH)0x$M?uQAhUb;Rs zeAqd4QQF688^X^X5pVTxsofP5@Z;DeVKe*aIop0-PO_EaY~CCh;raj35&baZD!+(0 zzjVFbYyRCiUe&2SbN-wzHmPi|;UB7-Mv{pZT(m zywh3cInT9!w$@5i2=5KKX!U(TrmUa7R#cMoIkN>{YPx1s;*b3=De8Ak0{?)V>TD zLGL_W978JRyq$BguSJ2!HF5L*|EJ#^PT^STk-f0G^UmiK9)qp!+I?*?UY;z+`K=l* zFJv$fVQ6DI5Wp}a-h1(!;u4uWoy`{Uf0`%W<=ojf^D6TS`)v{~fy z!Q9RB_XYBvb1(l>u6$l)iS@G=TsLffPu;z_)b5}4R%1@Vi?6;noZUP>NNXvNUenAU z%6+jery~U-q9+S^IbGfmwOJO_DO zEzBW0?p0a&^Nk5k9_x2n5%>BjQ(JEzy~+5G3!s-tD=)+hD+FX>9_pJov#eO^bb ztK~C9SA`apSFC#ExB8|IE0a_*E$7%bnb?*JlzA|Ls0@Y+se_JJX-n z)~kM~J+XY-6|Um#g+Ht%?bsVQ>-hrw|6gGErvB~I_9x$;{F=OoRY3F}^MOB%JgZEz TaygEGawvnRtDnm{r-UW|@zu8~ literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/bg3.png b/applications/external/jetpack_joyride/assets/bg3.png new file mode 100644 index 0000000000000000000000000000000000000000..ebb1dd66ba4e92493aacc09d90e44096d5035693 GIT binary patch literal 886 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!2~2@x4h6`U|?*>baoCX4sv%=@N{)HGqf}_ z(>2mFWMI&kSUT~%wo9PMaeHGQWw$Aj8e;m1rY3H_Ia6k~Ofqqk%t@Q~bK$e&ulG(& zS)H?7W8=r{gYgH#BDGzYq@130!@%}=WtK{D-t&3q|K6+i`#z8RTt?Vg$*pxKo_1HI zm@GMS?3u`SVZB=)g8J>(N!|KzIL|r}qH=DQH)0x$M?uQAhUb;Rs zeAqd4QQF688^X^X5pVTxsofP5@Z;DeVKe*aIop0-PO_EaY~CCh;raj35&baZD!+(0 zzjVFbYyRCiUe&2SbN-wzHmPi|;UB7-Mv{pZT(m zywh3cInT9!w$@5i2=5KKX!U(TrmUa7R#cMoIkN>{YPx1s;*b3=De8Ak0{?)V>TD zL32D^978JRyq$9}?~s82>+Dtk|DS%xc+%-eB>Vd5mIlQxKPpdNImmZaZtYh2hd-8b z|0ok-Sj-^7dO(BW+52;|`yPAv3BTDo>0x$|1|kfk%CpEu|~|2b{%-)Kk1G3C{h=b)tTch2l(m*?rUELq0F~>*cz6 z?r+ZMU#zT$jn6i%b5s4zx|gSBabj@WuD-~^3bp+!o_&`xJ6gKNcc*#1RgEr?KXqDa hP}l7MgmZqf2kd!Rba~T-7GUHtc)I$ztaD0e0sz#Bj4uEH literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/coin.png b/applications/external/jetpack_joyride/assets/coin.png new file mode 100644 index 0000000000000000000000000000000000000000..a2b5a409e43ce74ce6d84d83d8dda61c67b74d5b GIT binary patch literal 1842 zcma)6eM}Q)7(a20fx)N~U6hRT93aE?u7^~5_Q29sVbBIj8K7?K(Y|d@dLO$xXd#9v z_z~v(gDG*$uR*4Psl+U9#%#kab1c(se(Xct7KquhEsn70Viu=^y?5nP)a;M9*Z2AT zp3nDro|Y2l+QfuK2?&BD78clC@EfO|n3?eRwE4_x1c{x?x+|4RM=|9UI6dPNs)0Vj zi4aE+b6!Mbyfr{Ut3iO}Y3$Dr&R{6(qp^x?2jLK{AjlRpNT962>Gn3%cuhVmFE_y) zp`ZZ{C=43mLcB~xXiT$9!T+il$54%;)X-R^qXe}I5Dl(0( zu>Ob8Rb(0+*4p8CbQT;pB@tP%*TF)J7IjKMV|mN&dJ*6*APcpU7uf3nuPoJS0liUH z2`~zf3~B)#fheS@@cy=3G;$oWN+zWW*;qshS3<GVpTr7QKd8 zZFo>rYaxp9s{UzAlmuU`7x=Ugpt&e23w|ZcNWc<+-9uv*zn_JX)}wGP*#HkDltf8A zpanAF5#Nrw^+VCa;0;)>4(RUH@~&4DY*WRPGjmRIg7w9h`(<7YoBRHdzPog z-?qR1mu(w*yX;MkpKl2DU%Iz7Y1Ipx(|dlu5y?sJ?D=TmvZt@+RMpYp)2|%oGP*M_ zUc}5ty4ICOTsbFQZb$#Tq36k)sn?%m_NFbI-)p#SYdhp{R^0iHRyMThYekVuJ-qX@2e8;HO{BdAh1Jec!VOHEt*;&Lurxi#x9aVzbgH4b@b3zz7`H?FH*{Pp)8zrDDt=BChH`qS&reRAHQ zoc*{l_V}#t4*okRD{+^~@4QoV_1d->CSm=(m{t1%mxpt9!^c44ON!lg_#GJ;Nwa@} zo`{6uWv1eCmW_-cSI#9`RE}6sB_qh}v+9}AmN|Pd6fP*VS=>*wdqof9srAf>-;AL@ aZiz(}u8qk%++N2+22yBu+IsRmP5%LQYG}3q literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/coin_infill.png b/applications/external/jetpack_joyride/assets/coin_infill.png new file mode 100644 index 0000000000000000000000000000000000000000..ab37874ff8b25de5a5d955a376dff3534ee4ed41 GIT binary patch literal 1974 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 literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/dead_scientist_infill.png b/applications/external/jetpack_joyride/assets/dead_scientist_infill.png new file mode 100644 index 0000000000000000000000000000000000000000..6f036fde2f35d695c8eb2bb4ec27bb84f44de90c GIT binary patch literal 1900 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 literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/pillar.png b/applications/external/jetpack_joyride/assets/pillar.png new file mode 100644 index 0000000000000000000000000000000000000000..61979b393acb5f4397d25af4c2590f7f4b4bb1cc GIT binary patch literal 1880 zcma)74Qvxt950GE%J>Mv&?#~p$TpYWUAuLqXK$oy3oWd3jv^Zw8@KkgJ=Wfxch~h+ zl&^$OLk1fk8WBe_5&75*i%~I#5tNTPaPqO>bbjE7K!9mP#USdt>*zKch)rJG_kO?k z`+vOu|Gf>xMRP`L)3q9nX0*#`FM+>f;mA#S2!3~u{7uwo9+}8^N~KbFA?fA9813V# z049fdh-);KSu#(1tAT`6fdIo&Xy3<2P=xVO=t85LaPu}0WSlVp%#Rg$ys>Jp*@w=` z*IHx}3WR|~BXT&zilj`TsxArtE6X^Fs1&K1LQCDnh>a5fV#JIXf#z!wi{SH0wF&v91 znsgNxkth^}IC9UkFrN@-#s27F9&nlFaXm)hgUNi5lQ=QR-DgVzPTB}$W*~+kW)vie zE;0W5&=n+!4s-2xKRAkTct{|k}eZ z+5|vLzzzq68VEyyvyxgPyYk}#3EGqVW;K_tj-&d2y`??X14AekJ@hUMX-CHnvH z6A333cHAjgnh*47V2_OpaRPJ>#*m{rI{>+Cwqk+vGa-nHC36Z8m&2B)H|OQ)447W! zD(6NPGcpL3+LL}Xdo`AoGj@I(*dxM@kzmi*1&op zU;_%!ucHFW`Gg`$fiGYgc%pF=DpWdvQK&f&A7rL12~t`Hv8aVaIvG*ogj#he-beGn zOYvEJ8e-H2LJ+l#NZPB|r%+ztd=W43sZm0?I4W{}DM|~#8h{l5pz65vf%(s8;%qZzi6f%TA72O3T0mdKXR=M`8dteZ83$~@MXrYm=P_NQiV?jD}? zg4O<-wPjO9<*M$>70)zV&2+NzE}S68#LLdX7p*J&z|Bvx641QY#62e?u}Se2R9?5{V(sKH0_AC zZtQx|dA@1wwx;cA+jpF6D7w70{gsX#Gpk--Q@f$(5+#E!uZt`f*3yDd!f`oqs-PiM=fEE${hZ-P%dV zZeLlmXL4p*>W_CW5*ss?EL*$3(757m%Itgx?s^`n>bB2Xy^}n|b X8$D^dPo8AzRUSl_qsZP|uw?bWG&6$} literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/scientist_left.png b/applications/external/jetpack_joyride/assets/scientist_left.png new file mode 100644 index 0000000000000000000000000000000000000000..a9e880b6b2e1b8ee4a19c47c48b66bbcf88bb29c GIT binary patch literal 2056 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 B(K-MC literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/scientist_right.png b/applications/external/jetpack_joyride/assets/scientist_right.png new file mode 100644 index 0000000000000000000000000000000000000000..dc40b560de9f5d5212491fe62a09f849a9ba8b56 GIT binary patch literal 1917 zcma)7dr%a09AAwX#B2g28T)5j8u8KH-Y#&=Ew>QdAsn)VaN?n8F1LGkD|frs?&9sC zW9EdBj}RZ3jyd8Vj-e&K(k#Pp%6!kL;E;`GW20r`BMe4kq~GrH6s-Q(yZwDXpYQ9l z-~H|fMTNQJq9;Z}5H!w_XDUi>j5H%uutp^i z1ULj)GnF9iu4HAng7tDdiQN0D8G$*5L}sT_CMsxSeOz9($d*(WI^ETkZVQ8CW<*;R z0t5)KG7T#MKQ9pqiD+>NaIczC1lA~WC5gDGBG@K~ESzdgHJXr&XxJ(;9-`Pjz1JC> zNW>@0K>|fXp^!0@Y!pNO$BA zzaW-?(O^jg8!#7f3hQ^-xd6*cKy(tZJcX4F!mtz#?AJ^WN(l^znfw&wu-S@4!Nd6h zCKcyq!;T!A8MBzp2HdFUS7js|0ouzJu)J6Hfx7hg1Hf#KQSEw7npthnU!~5+ES>{` z;96P@at&Z4l7^#$vXqSofk#GAV73`rrtP#03dK!$vcZ&Uz%eI|6Ui8XrA;&8gvq33 z(f9(xdBXq2R_7VE0zO3%d7LB*VpucjLtetZt)KLxpVOvWoo|&-yVdSUBq$0@m78U> z)Y|l`s3dsg5G}GQ}5wnf~a|=5WW1bE~f`PCQlGJ~iLpJ?H*}rde(IF;m+2Pcj!=2zwSz z+AO7aExFruO*-0D-js_kY}@r9%e?8ziH7ZXdfMJ2^Me-)UpW-9qp@M!n-o>lnsIMm z*@L#q*2blG->R%ldLiCl)naShd8_8+dr74oTfR$g2`nTgPRNMsye($kNS{#`|ETpB zXMMAwE%8Q7)8VLTpGJQ;t7%KyftEkTpW4r^oO879uXW$sdC#1RO-fQEogFbV!Mh=* z`(e!5_PR|=tJ$25ORft~UOTgH_vtb&>Tl6#R?%;Uc(=(zJk%Gi@XE6dN6Ti$u}Qe>w6`qaw1&-tz`>)5^j<*PNT zsN2fr9n}wKi?vtRJCyXAofhA*cOTbOT*&`>tMv_L-Z#Hb?CfmFJ)ed?fw<$|TceZ8mW*G@S8qnqOke}oSv zG<_C(oIEmrv}M7WHIXyOWpks?mM`vFbn4qn=NzSz<1Zh))19ADzHMdmw%>4b5_e0m ch89C7N^}2Mv+B#a>Q~c|Q)vG@d%^O50K+qvi~s-t literal 0 HcmV?d00001 diff --git a/applications/external/jetpack_joyride/assets/scientist_right_infill.png b/applications/external/jetpack_joyride/assets/scientist_right_infill.png new file mode 100644 index 0000000000000000000000000000000000000000..e4bc7def8808e256ca957c218ce88ae9ab722036 GIT binary patch literal 2210 zcmcIlYj6`)6yCz<(3T=jd4srawba&RA0cV8BrR`o zr=<6~s7@)QPsP^!$}puk3qc+gIgeNIy64h9A!KA& zp&l4xArayTVk?S?jIR+WXgvsUVLSH6{sS1wv39K1>?Yh|DF|}qaS7DKD?Gk zMTKd$7!3(RKw;2Wh!4wj%#LZiH2kk_;~1)$D2;Z^>#jsg1qq;LquEGcg=wfwV*PZr zW6ppxT-mXpqKGt(N25_=)MON-08U!1R-B-4iZVb8gWMEWn3y3fXQ>oAhXcqyi4zr0 z2%{<|Q!hjmJBC3V9UuoW?k;;+9w;9QfvX5k8VNkvTnq||AO{68kX>h24{7SnDx3oN5B z4n~2AQUql(5M~3F?;!}~RCw-Fg(Y@};XIS?K^s(XU88+BH8$P_jHr7WgoWg^ob{F%?PdWXEbucCUcn0e5)%ST2~rQja{~-o$UzDlp3JXXCLOWRMc;!*H@pMz#3cY1iE9Le z;oTv@OJd>JhTjn+rImw|I&4A_BxikOeRrOtYTWT6Q`@AfEZ2n0BWWE~nNvabyF0kG zHyTZSS2k~X^5fh74g99wanD#<41cA6m)rPW+v|K?=G2>8f7#W^p6dOfZ{LOH?qAQ# zho)ELP3}53vXftu-f?HK+?T%R+>D0c&haxAKklq8TYc!++;evf7Sod3ouB2tbLZ@o z`sIsL7QJ=7ZF@u0rPNEiHhtM};|=lE4edwQ%v;lJ-nZ>~$F4F%i^ah~iBT{&D&sNnn7&zsHLoh>_Z zmp5Z6`0>*PkDbc3vo>+7C@@^v&m>9qO!nfb#gSP`ePk7PP0WbU1%DEmV zeE0YFXFE@zdt*_!7^zfO)b#fwXHRAnt2SbJos1yqnd*~*Y}q;qZsxcg#hy`}zEK&G zW#Y0&>c{quI+TJwckSTC!&eVp%xOQka`vHX3+>%;chDBm z=?DWh4dD+qokSL55FK$67bL+*2wM!Y4`jmP$7cVSae{6TH#al4`rZ`^SXunzKHm5D zdp_UidEPctNkN<BTHI^d)W8+cIGP*QGfsIh8nSnf_m zj+cZA+(4vIuiM24q*sf{dP(>%ZQ~d!Q^XoAR%tY$I+h1$hB8B`!geaq9NuOp&H7!7 z!f>UqISvK^$H3%*}H#Ar%ejEo zNF5I-5$NHg#RwIVo~Fu3Q)iSDYEqp+rf2U^5h_)PY-DY;-S=N)BAZOlf@E?f8aYX+F}UWf>joVtMEsj3Hfib{Q(t=}bIpr(F;e%mw*qkwKTK)?{X; z5lXepmClVc(O%%H)YERj2(ZSqm^v8sGH6)?lx>&6)`A9t3Z;`Ji`}u% z+Tbd)lU;!7f~dgSMGwV;JO`{GEtY4u)6i2N3VV}w zFn~u1lu)Xa&##v9I04kS`XEq54W!0H=218{r+C+ZG5ug)6MG*8_rhL7`Ao_7o#_*KRbW%^O38G z^^Va~y|>R)%|!iLJfmoTtn=L){pN*2&Fe{hJ*Ai30zvecr>-&cLkp??nbg;A?muN| z{ekqRHn)hEzgD!zl=j<<$FrKZ-uYkbd|Qk|Mc;MsE6El=CP;dXJU7> z?SJC_xrV9Tu@e)t@xxAT~bYY)eKQ<~L8+)X&yQet@- z8<~6I;E|N^X^=Ti4HO>QW+C-e$7XBW4&SIve3oYdfj_4+8(vjR*EE?P`m=GjX&|E8 z_m%G_PuHc$`Kx1=-gidMJRW(v=a-{<=al!Z_^*G_cW%%xK6&D1@r9``Q@V$)sQiCL z&-P4mG1Efx`wxB-ys@(%H%>>nZoN3%b@$T22tLU%H!=8oHoTHzxI&A7Ah8M36M>xQ z+5$IKMf$w*EuGfC1A*M+ + +#include "background_assets.h" + +static AssetProperties assetProperties[BG_ASSETS_MAX] = { + {.width = 27, .spawn_chance = 1, .x_offset = 24, .y_offset = 36, .sprite = &I_door}, + {.width = 12, .spawn_chance = 6, .x_offset = 33, .y_offset = 14, .sprite = &I_air_vent}}; + +void background_assets_tick(BackgroundAsset* const assets) { + // Move assets towards the player + for(int i = 0; i < BG_ASSETS_MAX; i++) { + if(assets[i].visible) { + assets[i].point.x -= 1; // move left by 2 units + if(assets[i].point.x <= + -assets[i].properties->width) { // if the asset is out of screen + assets[i].visible = false; // set asset x coordinate to 0 to mark it as "inactive" + } + } + } +} + +void spawn_random_background_asset(BackgroundAsset* const assets) { + // Calculate the total spawn chances for all assets + int total_spawn_chance = 0; + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + total_spawn_chance += assetProperties[i].spawn_chance; + } + + // Generate a random number between 0 and total_spawn_chance + int random_number = rand() % total_spawn_chance; + + // Select the asset based on the random number + int chosen_asset = -1; + int accumulated_chance = 0; + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + accumulated_chance += assetProperties[i].spawn_chance; + if(random_number < accumulated_chance) { + chosen_asset = i; + break; + } + } + + // If no asset is chosen, return + if(chosen_asset == -1) { + return; + } + + // Look for an available slot for the chosen asset + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + if(assets[i].visible == false) { + // Spawn the asset + assets[i].point.x = 127 + assetProperties[chosen_asset].x_offset; + assets[i].point.y = assetProperties[chosen_asset].y_offset; + assets[i].properties = &assetProperties[chosen_asset]; + assets[i].visible = true; + break; + } + } +} + +void draw_background_assets(const BackgroundAsset* assets, Canvas* const canvas, int distance) { + canvas_draw_box(canvas, 0, 6, 128, 1); + canvas_draw_box(canvas, 0, 56, 128, 2); + + // Calculate the pillar offset based on the traveled distance + int pillar_offset = distance % 64; + + // Draw pillars + for(int x = -pillar_offset; x < 128; x += 64) { + canvas_draw_icon(canvas, x, 6, &I_pillar); + } + + // Draw assets + for(int i = 0; i < BG_ASSETS_MAX; ++i) { + if(assets[i].visible) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon( + canvas, assets[i].point.x, assets[i].point.y, assets[i].properties->sprite); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/background_assets.h b/applications/external/jetpack_joyride/includes/background_assets.h new file mode 100644 index 000000000..d42fcfd71 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/background_assets.h @@ -0,0 +1,34 @@ +#ifndef BACKGROUND_ASSETS_H +#define BACKGROUND_ASSETS_H + +#include +#include + +#include + +#include "point.h" +#include "states.h" +#include "game_sprites.h" +#include + +#define BG_ASSETS_MAX 3 + +typedef struct { + int width; + int spawn_chance; + int x_offset; + int y_offset; + const Icon* sprite; +} AssetProperties; + +typedef struct { + POINT point; + AssetProperties* properties; + bool visible; +} BackgroundAsset; + +void background_assets_tick(BackgroundAsset* const assets); +void spawn_random_background_asset(BackgroundAsset* const assets); +void draw_background_assets(const BackgroundAsset* assets, Canvas* const canvas, int distance); + +#endif // BACKGROUND_ASSETS_H diff --git a/applications/external/jetpack_joyride/includes/barry.c b/applications/external/jetpack_joyride/includes/barry.c new file mode 100644 index 000000000..61d3a6fc4 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/barry.c @@ -0,0 +1,33 @@ +#include "barry.h" +#include "game_sprites.h" + +#include +#include + +void barry_tick(BARRY* const barry) { + // Do jetpack things + if(barry->isBoosting) { + barry->gravity += GRAVITY_BOOST; // Increase upward momentum + } else { + barry->gravity += GRAVITY_FALL; // Increase downward momentum faster + } + + barry->point.y += barry->gravity; + + // Constrain barry's height within sprite_height and 64 - sprite_height + if(barry->point.y > (64 - BARRY_HEIGHT)) { + barry->point.y = 64 - BARRY_HEIGHT; + barry->gravity = 0; // stop upward momentum + } else if(barry->point.y < 0) { + barry->point.y = 0; + barry->gravity = 0; // stop downward momentum + } +} + +void draw_barry(const BARRY* barry, Canvas* const canvas, const GameSprites* sprites) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon_animation(canvas, barry->point.x, barry->point.y, sprites->barry); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, barry->point.x, barry->point.y, sprites->barry_infill); +} \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/barry.h b/applications/external/jetpack_joyride/includes/barry.h new file mode 100644 index 000000000..494af434d --- /dev/null +++ b/applications/external/jetpack_joyride/includes/barry.h @@ -0,0 +1,23 @@ +#ifndef BARRY_H +#define BARRY_H + +#include + +#include +#include "point.h" +#include "game_sprites.h" + +#define GRAVITY_TICK 0.2 +#define GRAVITY_BOOST -0.4 +#define GRAVITY_FALL 0.3 + +typedef struct { + float gravity; + POINT point; + bool isBoosting; +} BARRY; + +void barry_tick(BARRY* const barry); +void draw_barry(const BARRY* barry, Canvas* const canvas, const GameSprites* sprites); + +#endif // BARRY_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/coin.c b/applications/external/jetpack_joyride/includes/coin.c new file mode 100644 index 000000000..7a3811a8c --- /dev/null +++ b/applications/external/jetpack_joyride/includes/coin.c @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +#include "coin.h" +#include "barry.h" + +#define PATTERN_MAX_HEIGHT 40 + +// Patterns +const COIN_PATTERN coin_patterns[] = { + {// Square pattern + .count = 9, + .coins = {{0, 0}, {8, 0}, {16, 0}, {0, 8}, {8, 8}, {16, 8}, {0, 16}, {8, 16}, {16, 16}}}, + {// Wavy pattern (approximate sine wave) + .count = 8, + .coins = {{0, 8}, {8, 16}, {16, 24}, {24, 16}, {32, 8}, {40, 0}, {48, 8}, {56, 16}}}, + {// Diagonal pattern + .count = 5, + .coins = {{0, 0}, {8, 8}, {16, 16}, {24, 24}, {32, 32}}}, + // Add more patterns here +}; + +void coin_tick(COIN* const coins, BARRY* const barry, int* const total_coins) { + // Move coins towards the player + for(int i = 0; i < COINS_MAX; i++) { + if(coin_colides(&coins[i], barry)) { + coins[i].point.x = 0; // Remove the coin + (*total_coins)++; + } + if(coins[i].point.x > 0) { + coins[i].point.x -= 1; // move left by 1 unit + if(coins[i].point.x < -COIN_WIDTH) { // if the coin is out of screen + coins[i].point.x = 0; // set coin x coordinate to 0 to mark it as "inactive" + } + } + } +} + +bool coin_colides(COIN* const coin, BARRY* const barry) { + return !( + barry->point.x > coin->point.x + COIN_WIDTH || // Barry is to the right of the coin + barry->point.x + BARRY_WIDTH < coin->point.x || // Barry is to the left of the coin + barry->point.y > coin->point.y + COIN_WIDTH || // Barry is below the coin + barry->point.y + BARRY_HEIGHT < coin->point.y); // Barry is above the coin +} + +void spawn_random_coin(COIN* const coins) { + // Select a random pattern + int pattern_index = rand() % (sizeof(coin_patterns) / sizeof(coin_patterns[0])); + const COIN_PATTERN* pattern = &coin_patterns[pattern_index]; + + // Count available slots for new coins + int available_slots = 0; + for(int i = 0; i < COINS_MAX; ++i) { + if(coins[i].point.x <= 0) { + ++available_slots; + } + } + + // If there aren't enough slots, return without spawning coins + if(available_slots < pattern->count) return; + + // Spawn coins according to the selected pattern + int coin_index = 0; + int random_offset = rand() % (SCREEN_HEIGHT - PATTERN_MAX_HEIGHT); + int random_offset_x = rand() % 16; + for(int i = 0; i < pattern->count; ++i) { + // Find an available slot for a new coin + while(coins[coin_index].point.x > 0 && coin_index < COINS_MAX) { + ++coin_index; + } + // If no slot is available, stop spawning coins + if(coin_index == COINS_MAX) break; + + // Spawn the coin + coins[coin_index].point.x = SCREEN_WIDTH - 1 + pattern->coins[i].x + random_offset_x; + coins[coin_index].point.y = + random_offset + + pattern->coins[i] + .y; // The pattern is spawned at a random y position, but not too close to the screen edge + } +} + +void draw_coins(const COIN* coins, Canvas* const canvas, const GameSprites* sprites) { + canvas_set_color(canvas, ColorBlack); + for(int i = 0; i < COINS_MAX; ++i) { + if(coins[i].point.x > 0) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, coins[i].point.x, coins[i].point.y, sprites->coin); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon(canvas, coins[i].point.x, coins[i].point.y, sprites->coin_infill); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/coin.h b/applications/external/jetpack_joyride/includes/coin.h new file mode 100644 index 000000000..41fd21ddc --- /dev/null +++ b/applications/external/jetpack_joyride/includes/coin.h @@ -0,0 +1,26 @@ +#ifndef COIN_H +#define COIN_H + +#include + +#include "point.h" +#include "barry.h" + +#define COINS_MAX 15 + +typedef struct { + float gravity; + POINT point; +} COIN; + +typedef struct { + int count; + POINT coins[COINS_MAX]; +} COIN_PATTERN; + +void coin_tick(COIN* const coins, BARRY* const barry, int* const poins); +void spawn_random_coin(COIN* const coins); +bool coin_colides(COIN* const coin, BARRY* const barry); +void draw_coins(const COIN* coins, Canvas* const canvas, const GameSprites* sprites); + +#endif // COIN_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/game_sprites.h b/applications/external/jetpack_joyride/includes/game_sprites.h new file mode 100644 index 000000000..d38494bf8 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_sprites.h @@ -0,0 +1,35 @@ +#ifndef GAME_SPRITES_H +#define GAME_SPRITES_H + +#include "point.h" +#include + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 + +#define BARRY_WIDTH 11 +#define BARRY_HEIGHT 15 + +#define MISSILE_WIDTH 26 +#define MISSILE_HEIGHT 12 + +#define SCIENTIST_WIDTH 9 +#define SCIENTIST_HEIGHT 14 + +#define COIN_WIDTH 7 + +typedef struct { + IconAnimation* barry; + const Icon* barry_infill; + const Icon* scientist_left; + const Icon* scientist_left_infill; + const Icon* scientist_right; + const Icon* scientist_right_infill; + const Icon* coin; + const Icon* coin_infill; + IconAnimation* missile; + IconAnimation* alert; + const Icon* missile_infill; +} GameSprites; + +#endif // GAME_SPRITES_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/game_state.c b/applications/external/jetpack_joyride/includes/game_state.c new file mode 100644 index 000000000..a8a9db618 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_state.c @@ -0,0 +1,5 @@ +#include "game_state.h" + +void game_state_tick(GameState* const game_state) { + game_state->distance++; +} diff --git a/applications/external/jetpack_joyride/includes/game_state.h b/applications/external/jetpack_joyride/includes/game_state.h new file mode 100644 index 000000000..1e97aaf18 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/game_state.h @@ -0,0 +1,34 @@ +#ifndef GAMESTATE_H +#define GAMESTATE_H + +#include +#include + +#include "barry.h" +#include "scientist.h" +#include "coin.h" +#include "particle.h" +#include "game_sprites.h" +#include "states.h" +#include "missile.h" +#include "background_assets.h" +typedef struct { + int total_coins; + int distance; + bool new_highscore; + BARRY barry; + COIN coins[COINS_MAX]; + PARTICLE particles[PARTICLES_MAX]; + SCIENTIST scientists[SCIENTISTS_MAX]; + MISSILE missiles[MISSILES_MAX]; + BackgroundAsset bg_assets[BG_ASSETS_MAX]; + State state; + GameSprites sprites; + FuriMutex* mutex; + FuriTimer* timer; + void (*death_handler)(); +} GameState; + +void game_state_tick(GameState* const game_state); + +#endif // GAMESTATE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/missile.c b/applications/external/jetpack_joyride/includes/missile.c new file mode 100644 index 000000000..af47e8478 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/missile.c @@ -0,0 +1,86 @@ +#include +#include + +#include +#include + +#include "states.h" +#include "game_sprites.h" +#include "missile.h" +#include "barry.h" + +void missile_tick(MISSILE* const missiles, BARRY* const barry, void (*death_handler)()) { + // Move missiles towards the player + for(int i = 0; i < MISSILES_MAX; i++) { + if(missiles[i].visible && missile_colides(&missiles[i], barry)) { + death_handler(); + } + if(missiles[i].visible) { + missiles[i].point.x -= 2; // move left by 2 units + if(missiles[i].point.x < -MISSILE_WIDTH) { // if the missile is out of screen + missiles[i].visible = false; // set missile as "inactive" + } + } + } +} + +void spawn_random_missile(MISSILE* const missiles) { + // Check for an available slot for a new missile + for(int i = 0; i < MISSILES_MAX; ++i) { + if(!missiles[i].visible) { + missiles[i].point.x = 2 * SCREEN_WIDTH; + missiles[i].point.y = rand() % (SCREEN_HEIGHT - MISSILE_HEIGHT); + missiles[i].visible = true; + break; + } + } +} + +void draw_missiles(const MISSILE* missiles, Canvas* const canvas, const GameSprites* sprites) { + for(int i = 0; i < MISSILES_MAX; ++i) { + if(missiles[i].visible) { + canvas_set_color(canvas, ColorBlack); + + if(missiles[i].point.x > 128) { + canvas_draw_icon_animation( + canvas, SCREEN_WIDTH - 7, missiles[i].point.y, sprites->alert); + } else { + canvas_draw_icon_animation( + canvas, missiles[i].point.x, missiles[i].point.y, sprites->missile); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, missiles[i].point.x, missiles[i].point.y, sprites->missile_infill); + } + } + } +} + +bool missile_colides(MISSILE* const missile, BARRY* const barry) { + return !( + barry->point.x > + missile->point.x + MISSILE_WIDTH - 14 || // Barry is to the right of the missile + barry->point.x + BARRY_WIDTH - 3 < + missile->point.x || // Barry is to the left of the missile + barry->point.y > missile->point.y + MISSILE_HEIGHT || // Barry is below the missile + barry->point.y + BARRY_HEIGHT < missile->point.y); // Barry is above the missile +} + +int get_rocket_spawn_distance(int player_distance) { + // Define the start and end points for rocket spawn distance + int start_distance = 256; + int end_distance = 24; + + // Define the maximum player distance at which the spawn distance should be at its minimum + int max_player_distance = 5000; // Adjust this value based on your game's difficulty curve + + if(player_distance >= max_player_distance) { + return end_distance; + } + + // Calculate the linear interpolation factor + float t = (float)player_distance / max_player_distance; + + // Interpolate the rocket spawn distance + return start_distance + t * (end_distance - start_distance); +} diff --git a/applications/external/jetpack_joyride/includes/missile.h b/applications/external/jetpack_joyride/includes/missile.h new file mode 100644 index 000000000..a5af4e885 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/missile.h @@ -0,0 +1,24 @@ +#ifndef MISSILE_H +#define MISSILE_H + +#include +#include "game_sprites.h" + +#include "states.h" +#include "point.h" +#include "barry.h" + +#define MISSILES_MAX 5 + +typedef struct { + POINT point; + bool visible; +} MISSILE; + +void missile_tick(MISSILE* const missiles, BARRY* const barry, void (*death_handler)()); +void spawn_random_missile(MISSILE* const MISSILEs); +bool missile_colides(MISSILE* const MISSILE, BARRY* const barry); +int get_rocket_spawn_distance(int player_distance); +void draw_missiles(const MISSILE* missiles, Canvas* const canvas, const GameSprites* sprites); + +#endif // MISSILE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/particle.c b/applications/external/jetpack_joyride/includes/particle.c new file mode 100644 index 000000000..cf8e6e0a6 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/particle.c @@ -0,0 +1,57 @@ +#include + +#include "particle.h" +#include "scientist.h" +#include "barry.h" + +void particle_tick(PARTICLE* const particles, SCIENTIST* const scientists) { + // Move particles + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y > 0) { + particles[i].point.y += PARTICLE_VELOCITY; + + // Check collision with scientists + for(int j = 0; j < SCIENTISTS_MAX; j++) { + if(scientists[j].state == ScientistStateAlive && scientists[j].point.x > 0) { + // Check whether the particle lies within the scientist's bounding box + if(!(particles[i].point.x > scientists[j].point.x + SCIENTIST_WIDTH || + particles[i].point.x < scientists[j].point.x || + particles[i].point.y > scientists[j].point.y + SCIENTIST_HEIGHT || + particles[i].point.y < scientists[j].point.y)) { + scientists[j].state = ScientistStateDead; + // (*points) += 2; // Increase the score by 2 + } + } + } + + if(particles[i].point.x < 0 || particles[i].point.x > SCREEN_WIDTH || + particles[i].point.y < 0 || particles[i].point.y > SCREEN_HEIGHT) { + particles[i].point.y = 0; + } + } + } +} + +void spawn_random_particles(PARTICLE* const particles, BARRY* const barry) { + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y <= 0) { + particles[i].point.x = barry->point.x + (rand() % 4); + particles[i].point.y = barry->point.y + 14; + break; + } + } +} + +void draw_particles(const PARTICLE* particles, Canvas* const canvas) { + canvas_set_color(canvas, ColorBlack); + for(int i = 0; i < PARTICLES_MAX; i++) { + if(particles[i].point.y > 0) { + canvas_draw_line( + canvas, + particles[i].point.x, + particles[i].point.y, + particles[i].point.x, + particles[i].point.y + 3); + } + } +} diff --git a/applications/external/jetpack_joyride/includes/particle.h b/applications/external/jetpack_joyride/includes/particle.h new file mode 100644 index 000000000..3442c9c4e --- /dev/null +++ b/applications/external/jetpack_joyride/includes/particle.h @@ -0,0 +1,21 @@ + + +#ifndef PARTICLE_H +#define PARTICLE_H + +#include "point.h" +#include "scientist.h" +#include "barry.h" + +#define PARTICLES_MAX 50 +#define PARTICLE_VELOCITY 2 + +typedef struct { + POINT point; +} PARTICLE; + +void particle_tick(PARTICLE* const particles, SCIENTIST* const scientists); +void spawn_random_particles(PARTICLE* const particles, BARRY* const barry); +void draw_particles(const PARTICLE* particles, Canvas* const canvas); + +#endif // PARTICLE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/point.h b/applications/external/jetpack_joyride/includes/point.h new file mode 100644 index 000000000..02c9a6ce4 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/point.h @@ -0,0 +1,14 @@ +#ifndef POINT_H +#define POINT_H + +typedef struct { + int x; + int y; +} POINT; + +typedef struct { + float x; + float y; +} POINTF; + +#endif // POINT_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/scientist.c b/applications/external/jetpack_joyride/includes/scientist.c new file mode 100644 index 000000000..b1a8a14c0 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/scientist.c @@ -0,0 +1,77 @@ +#include "scientist.h" +#include "game_sprites.h" + +#include +#include + +void scientist_tick(SCIENTIST* const scientists) { + for(int i = 0; i < SCIENTISTS_MAX; i++) { + if(scientists[i].visible) { + if(scientists[i].point.x < 64) scientists[i].velocity_x = 0.5f; + + scientists[i].point.x -= scientists[i].state == ScientistStateAlive ? + 1 - scientists[i].velocity_x : + 1; // move based on velocity_x + int width = (scientists[i].state == ScientistStateAlive) ? SCIENTIST_WIDTH : + SCIENTIST_HEIGHT; + if(scientists[i].point.x <= -width) { // if the scientist is out of screen + scientists[i].visible = false; + } + } + } +} + +void spawn_random_scientist(SCIENTIST* const scientists) { + float velocities[] = {-0.5f, 0.0f, 0.5f, -1.0f}; + // Check for an available slot for a new scientist + for(int i = 0; i < SCIENTISTS_MAX; ++i) { + if(!scientists[i].visible && + (rand() % 1000) < 10) { // Spawn rate is less frequent than coins + scientists[i].state = ScientistStateAlive; + scientists[i].point.x = 127; + scientists[i].point.y = 49; + scientists[i].velocity_x = velocities[rand() % 4]; + scientists[i].visible = true; + break; + } + } +} + +void draw_scientists(const SCIENTIST* scientists, Canvas* const canvas, const GameSprites* sprites) { + for(int i = 0; i < SCIENTISTS_MAX; ++i) { + if(scientists[i].visible) { + canvas_set_color(canvas, ColorBlack); + if(scientists[i].state == ScientistStateAlive) { + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y, + scientists[i].velocity_x >= 0 ? sprites->scientist_right : + sprites->scientist_left); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y, + scientists[i].velocity_x >= 0 ? sprites->scientist_right_infill : + sprites->scientist_left_infill); + + } else { + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y + 5, + &I_dead_scientist); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_icon( + canvas, + (int)scientists[i].point.x, + scientists[i].point.y + 5, + &I_dead_scientist_infill); + } + } + } +} \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/scientist.h b/applications/external/jetpack_joyride/includes/scientist.h new file mode 100644 index 000000000..a49e8028c --- /dev/null +++ b/applications/external/jetpack_joyride/includes/scientist.h @@ -0,0 +1,29 @@ +#ifndef SCIENTIST_H +#define SCIENTIST_H + +#include "point.h" +#include "game_sprites.h" +#include + +#define SCIENTIST_VELOCITY_MIN -0.5f +#define SCIENTIST_VELOCITY_MAX 0.5f + +#define SCIENTISTS_MAX 6 + +typedef enum { + ScientistStateAlive, + ScientistStateDead, +} ScientistState; + +typedef struct { + bool visible; + POINTF point; + float velocity_x; + ScientistState state; +} SCIENTIST; + +void scientist_tick(SCIENTIST* const scientist); +void spawn_random_scientist(SCIENTIST* const scientists); +void draw_scientists(const SCIENTIST* scientists, Canvas* const canvas, const GameSprites* sprites); + +#endif // SCIENTIST_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/includes/states.h b/applications/external/jetpack_joyride/includes/states.h new file mode 100644 index 000000000..d58e3e1f6 --- /dev/null +++ b/applications/external/jetpack_joyride/includes/states.h @@ -0,0 +1,9 @@ +#ifndef STATE_H +#define STATE_H + +typedef enum { + GameStateLife, + GameStateGameOver, +} State; + +#endif // STATE_H \ No newline at end of file diff --git a/applications/external/jetpack_joyride/jetpack.c b/applications/external/jetpack_joyride/jetpack.c new file mode 100644 index 000000000..c12f094c9 --- /dev/null +++ b/applications/external/jetpack_joyride/jetpack.c @@ -0,0 +1,379 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "includes/point.h" +#include "includes/barry.h" +#include "includes/scientist.h" +#include "includes/particle.h" +#include "includes/coin.h" +#include "includes/missile.h" +#include "includes/background_assets.h" + +#include "includes/game_state.h" + +#define TAG "Jetpack Joyride" +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/jetpack.save" +static GameState* global_state; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +typedef struct { + int max_distance; + int total_coins; +} SaveGame; + +static SaveGame save_game; + +static bool storage_game_state_load() { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) + bytes_readed = storage_file_read(file, &save_game, sizeof(SaveGame)); + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + return bytes_readed == sizeof(SaveGame); +} + +static void storage_game_state_save() { + Storage* storage = furi_record_open(RECORD_STORAGE); + + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { + return; + } + } + + File* file = storage_file_alloc(storage); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, &save_game, sizeof(SaveGame)); + } + storage_file_close(file); + storage_file_free(file); + furi_record_close(RECORD_STORAGE); +} + +void handle_death() { + global_state->state = GameStateGameOver; + global_state->new_highscore = global_state->distance > save_game.max_distance; + + if(global_state->distance > save_game.max_distance) { + save_game.max_distance = global_state->distance; + } + + save_game.total_coins += global_state->total_coins; + + storage_game_state_save(); +} + +static void jetpack_game_state_init(GameState* const game_state) { + UNUSED(game_state); + UNUSED(storage_game_state_save); + BARRY barry; + barry.gravity = 0; + barry.point.x = 32 + 5; + barry.point.y = 32; + barry.isBoosting = false; + + GameSprites sprites; + sprites.barry = icon_animation_alloc(&A_barry); + sprites.barry_infill = &I_barry_infill; + + sprites.scientist_left = (&I_scientist_left); + sprites.scientist_left_infill = (&I_scientist_left_infill); + sprites.scientist_right = (&I_scientist_right); + sprites.scientist_right_infill = (&I_scientist_right_infill); + + sprites.coin = (&I_coin); + sprites.coin_infill = (&I_coin_infill); + + sprites.missile = icon_animation_alloc(&A_missile); + sprites.missile_infill = &I_missile_infill; + + sprites.alert = icon_animation_alloc(&A_alert); + + icon_animation_start(sprites.barry); + icon_animation_start(sprites.missile); + icon_animation_start(sprites.alert); + + game_state->barry = barry; + game_state->total_coins = 0; + game_state->distance = 0; + game_state->new_highscore = false; + game_state->sprites = sprites; + game_state->state = GameStateLife; + game_state->death_handler = handle_death; + + memset(game_state->bg_assets, 0, sizeof(game_state->bg_assets)); + + memset(game_state->scientists, 0, sizeof(game_state->scientists)); + memset(game_state->coins, 0, sizeof(game_state->coins)); + memset(game_state->particles, 0, sizeof(game_state->particles)); + memset(game_state->missiles, 0, sizeof(game_state->missiles)); +} + +static void jetpack_game_state_free(GameState* const game_state) { + icon_animation_free(game_state->sprites.barry); + icon_animation_free(game_state->sprites.missile); + icon_animation_free(game_state->sprites.alert); + + free(game_state); +} + +static void jetpack_game_tick(GameState* const game_state) { + if(game_state->state == GameStateGameOver) return; + barry_tick(&game_state->barry); + game_state_tick(game_state); + coin_tick(game_state->coins, &game_state->barry, &game_state->total_coins); + particle_tick(game_state->particles, game_state->scientists); + scientist_tick(game_state->scientists); + missile_tick(game_state->missiles, &game_state->barry, game_state->death_handler); + + background_assets_tick(game_state->bg_assets); + + // generate background every 64px aka. ticks + if(game_state->distance % 64 == 0 && rand() % 3 == 0) { + spawn_random_background_asset(game_state->bg_assets); + } + + if(game_state->distance % 48 == 0 && rand() % 2 == 0) { + spawn_random_coin(game_state->coins); + } + + if(game_state->distance % get_rocket_spawn_distance(game_state->distance) == 0 && + rand() % 2 == 0) { + spawn_random_missile(game_state->missiles); + } + + spawn_random_scientist(game_state->scientists); + + if(game_state->barry.isBoosting) { + spawn_random_particles(game_state->particles, &game_state->barry); + } +} + +static void jetpack_game_render_callback(Canvas* const canvas, void* ctx) { + furi_assert(ctx); + const GameState* game_state = ctx; + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(game_state->state == GameStateLife) { + canvas_set_bitmap_mode(canvas, false); + + draw_background_assets(game_state->bg_assets, canvas, game_state->distance); + + canvas_set_bitmap_mode(canvas, true); + + draw_coins(game_state->coins, canvas, &game_state->sprites); + draw_scientists(game_state->scientists, canvas, &game_state->sprites); + draw_particles(game_state->particles, canvas); + draw_missiles(game_state->missiles, canvas, &game_state->sprites); + + draw_barry(&game_state->barry, canvas, &game_state->sprites); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + char buffer[12]; + snprintf(buffer, sizeof(buffer), "%u m", game_state->distance / 10); + canvas_draw_str_aligned(canvas, 123, 15, AlignRight, AlignBottom, buffer); + + snprintf(buffer, sizeof(buffer), "$%u", game_state->total_coins); + canvas_draw_str_aligned(canvas, 5, 15, AlignLeft, AlignBottom, buffer); + } + + if(game_state->state == GameStateGameOver) { + // Show highscore + char buffer[64]; + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignTop, "You flew"); + + snprintf( + buffer, + sizeof(buffer), + game_state->new_highscore ? "%u m (new best)" : "%u m", + game_state->distance / 10); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 16, AlignCenter, AlignTop, buffer); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, "and collected"); + + snprintf(buffer, sizeof(buffer), "$%u", game_state->total_coins); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignTop, buffer); + + snprintf( + buffer, + sizeof(buffer), + "Best: %u m, Tot: $%u", + save_game.max_distance / 10, + save_game.total_coins); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 63, AlignCenter, AlignBottom, buffer); + + canvas_draw_rframe(canvas, 0, 3, 128, 49, 5); + + // char buffer[12]; + // snprintf(buffer, sizeof(buffer), "Dist: %u", game_state->distance); + // canvas_draw_str_aligned(canvas, 123, 12, AlignRight, AlignBottom, buffer); + + // snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points); + // canvas_draw_str_aligned(canvas, 5, 12, AlignLeft, AlignBottom, buffer); + + // canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignCenter, "Highscore:"); + // snprintf(buffer, sizeof(buffer), "Dist: %u", save_game.max_distance); + // canvas_draw_str_aligned(canvas, 123, 50, AlignRight, AlignBottom, buffer); + + // snprintf(buffer, sizeof(buffer), "Score: %u", save_game.max_score); + // canvas_draw_str_aligned(canvas, 5, 50, AlignLeft, AlignBottom, buffer); + + // canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "boom."); + + // if(furi_timer_is_running(game_state->timer)) { + // furi_timer_start(game_state->timer, 0); + // } + } + + // canvas_draw_frame(canvas, 0, 0, 128, 64); + + furi_mutex_release(game_state->mutex); +} + +static void jetpack_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void jetpack_game_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t jetpack_game_app(void* p) { + UNUSED(p); + int32_t return_code = 0; + + if(!storage_game_state_load()) { + memset(&save_game, 0, sizeof(save_game)); + } + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + + global_state = game_state; + jetpack_game_state_init(game_state); + + game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(!game_state->mutex) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, jetpack_game_render_callback, game_state); + view_port_input_callback_set(view_port, jetpack_game_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(jetpack_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25); + + game_state->timer = timer; + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + GameEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + furi_mutex_acquire(game_state->mutex, FuriWaitForever); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeRelease && event.input.key == InputKeyOk) { + game_state->barry.isBoosting = false; + } + + // Reset highscore, for debug purposes + if(event.input.type == InputTypeLong && event.input.key == InputKeyLeft) { + save_game.max_distance = 0; + save_game.total_coins = 0; + storage_game_state_save(); + } + + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + break; + case InputKeyLeft: + break; + case InputKeyOk: + if(game_state->state == GameStateGameOver) { + jetpack_game_state_init(game_state); + } + + if(game_state->state == GameStateLife) { + // Do something + game_state->barry.isBoosting = true; + } + + break; + case InputKeyBack: + processing = false; + break; + default: + break; + } + } + } else if(event.type == EventTypeTick) { + jetpack_game_tick(game_state); + } + } + + view_port_update(view_port); + furi_mutex_release(game_state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_mutex_free(game_state->mutex); + +free_and_exit: + jetpack_game_state_free(game_state); + furi_message_queue_free(event_queue); + + return return_code; +} \ No newline at end of file diff --git a/applications/external/nfc_maker/application.fam b/applications/external/nfc_maker/application.fam new file mode 100644 index 000000000..89bbb202e --- /dev/null +++ b/applications/external/nfc_maker/application.fam @@ -0,0 +1,14 @@ +App( + appid="nfc_maker", + name="NFC Maker", + apptype=FlipperAppType.EXTERNAL, + entry_point="nfc_maker", + cdefines=["APP_NFC_MAKER"], + requires=[ + "storage", + "gui", + ], + stack_size=1 * 1024, + fap_icon="nfc_maker_10px.png", + fap_category="NFC", +) diff --git a/applications/external/nfc_maker/nfc_maker.c b/applications/external/nfc_maker/nfc_maker.c new file mode 100644 index 000000000..578054ade --- /dev/null +++ b/applications/external/nfc_maker/nfc_maker.c @@ -0,0 +1,81 @@ +#include "nfc_maker.h" + +static bool nfc_maker_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + NfcMaker* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool nfc_maker_back_event_callback(void* context) { + furi_assert(context); + NfcMaker* app = context; + + return scene_manager_handle_back_event(app->scene_manager); +} + +NfcMaker* nfc_maker_alloc() { + NfcMaker* app = malloc(sizeof(NfcMaker)); + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher and Scene Manager + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&nfc_maker_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, nfc_maker_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, nfc_maker_back_event_callback); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Gui Modules + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewSubmenu, submenu_get_view(app->submenu)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewTextInput, text_input_get_view(app->text_input)); + + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, NfcMakerViewByteInput, byte_input_get_view(app->byte_input)); + + app->popup = popup_alloc(); + view_dispatcher_add_view(app->view_dispatcher, NfcMakerViewPopup, popup_get_view(app->popup)); + + return app; +} + +void nfc_maker_free(NfcMaker* app) { + furi_assert(app); + + // Gui modules + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewSubmenu); + submenu_free(app->submenu); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewTextInput); + text_input_free(app->text_input); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewByteInput); + byte_input_free(app->byte_input); + view_dispatcher_remove_view(app->view_dispatcher, NfcMakerViewPopup); + popup_free(app->popup); + + // View Dispatcher and Scene Manager + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Records + furi_record_close(RECORD_GUI); + free(app); +} + +extern int32_t nfc_maker(void* p) { + UNUSED(p); + NfcMaker* app = nfc_maker_alloc(); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneMenu); + view_dispatcher_run(app->view_dispatcher); + nfc_maker_free(app); + return 0; +} diff --git a/applications/external/nfc_maker/nfc_maker.h b/applications/external/nfc_maker/nfc_maker.h new file mode 100644 index 000000000..388dc7196 --- /dev/null +++ b/applications/external/nfc_maker/nfc_maker.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/nfc_maker_scene.h" +#include +#include +#include +#include + +#define TEXT_INPUT_LEN 248 +#define WIFI_INPUT_LEN 90 + +typedef enum { + WifiAuthenticationOpen = 0x01, + WifiAuthenticationWpa2Personal = 0x20, + WifiAuthenticationWpa2Enterprise = 0x10, + WifiAuthenticationWpaPersonal = 0x02, + WifiAuthenticationWpaEnterprise = 0x08, + WifiAuthenticationShared = 0x04, +} WifiAuthentication; + +typedef enum { + WifiEncryptionAes = 0x08, + WifiEncryptionWep = 0x02, + WifiEncryptionTkip = 0x04, + WifiEncryptionNone = 0x01, +} WifiEncryption; + +typedef struct { + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + TextInput* text_input; + ByteInput* byte_input; + Popup* popup; + + uint8_t mac_buf[GAP_MAC_ADDR_SIZE]; + char text_buf[TEXT_INPUT_LEN]; + char pass_buf[WIFI_INPUT_LEN]; + char name_buf[TEXT_INPUT_LEN]; +} NfcMaker; + +typedef enum { + NfcMakerViewSubmenu, + NfcMakerViewTextInput, + NfcMakerViewByteInput, + NfcMakerViewPopup, +} NfcMakerView; diff --git a/applications/external/nfc_maker/nfc_maker_10px.png b/applications/external/nfc_maker/nfc_maker_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..a9e2443f1790f0b3a595767feb8622ff3cf7ab31 GIT binary patch literal 4142 zcmeH~d2ka|9LIxVTMCs!E&*YiGEkAt9@%WNYg4F6OCt@Gl4;8T%4YYa-D#2y$)-u` zw47xu$T0}YB`Bg*1cm`RGNYB5wt}_gK2$E{vgI0()=@?}_;z~_%s8Eq|C-6M``&jy z@Avz@-+Pl+nm%nxO!SCojYbn=OSNXeU*$SFsyF-||9T`1f2s=|*>VOKLVSM7CAtA3 z7x(}I!lFx~37_|*Ck{&dxC;rWUClX9DjCN+(S z-&lHktbO*>h|F`R&^6Z^M%Gu4+?E%2=*-Ol%h%5{CafRY{Em4{L~(G%t(w%!v2|HV z%%dBw!Tq^3$HeT))T*fOOGZVQ$9X24emLxR!`#^7+%*k!Egrc0L8EYC_RfkW8JWUZ zeqlxAwS_0p+`})EBktaw=cwOMJ@mnWvR9fS=O3H3`mdt%vPRP@PPACkZ5B&gJ}`?d zAFZi)vwHI2l0&=pE&rlqK}q(Gz4FwVvkp$^ofLa`$ei8#Y(s~=KYZ=P71D2;8>-x~ zQQs}DSX`d7y`)4Ob#Tk-+KT$Reh2Q1O{@K-D0Tl%U$gh|z6IcT#YtC5FhBdnO7dRe zi!lbZ|}mUg?=!zaGHZJ%;OnB zJ1H?bARSDF^xp zy@KVbdWFQRGx%y(bto4o(*q4daTXrlD68BWs|7KTo$8idH z;lH2|JS;VxS#%U0w4QTLonqCjpU_}^2=Ds%QfCD;n!baSPp?y#iXXwoNZDpjj;xOu zGz3MDUV0Bxj%PM&k|XM;xvOgWXz+ej*H1KO<&W|FaOK0e!F$~)OG{Ty9y#Kaqp_bg t_Wr$-tJBizZ`5U#ZOn{0H@d&CSIm|E10x2OB9No8B~P>Nd1Kz + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) NfcMakerScene##id, +typedef enum { +#include "nfc_maker_scene_config.h" + NfcMakerSceneNum, +} NfcMakerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers nfc_maker_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "nfc_maker_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c new file mode 100644 index 000000000..4e70a184d --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c @@ -0,0 +1,57 @@ +#include "../nfc_maker.h" + +enum ByteInputResult { + ByteInputResultOk, +}; + +static void nfc_maker_scene_bluetooth_byte_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, ByteInputResultOk); +} + +void nfc_maker_scene_bluetooth_on_enter(void* context) { + NfcMaker* app = context; + ByteInput* byte_input = app->byte_input; + + byte_input_set_header_text(byte_input, "Enter Bluetooth MAC:"); + + for(size_t i = 0; i < GAP_MAC_ADDR_SIZE; i++) { + app->mac_buf[i] = 0x69; + } + + byte_input_set_result_callback( + byte_input, + nfc_maker_scene_bluetooth_byte_input_callback, + NULL, + app, + app->mac_buf, + GAP_MAC_ADDR_SIZE); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewByteInput); +} + +bool nfc_maker_scene_bluetooth_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case ByteInputResultOk: + furi_hal_bt_reverse_mac_addr(app->mac_buf); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_bluetooth_on_exit(void* context) { + NfcMaker* app = context; + byte_input_set_result_callback(app->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(app->byte_input, ""); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h b/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h new file mode 100644 index 000000000..a89b4198c --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_config.h @@ -0,0 +1,13 @@ +ADD_SCENE(nfc_maker, menu, Menu) +ADD_SCENE(nfc_maker, bluetooth, Bluetooth) +ADD_SCENE(nfc_maker, https, Https) +ADD_SCENE(nfc_maker, mail, Mail) +ADD_SCENE(nfc_maker, phone, Phone) +ADD_SCENE(nfc_maker, text, Text) +ADD_SCENE(nfc_maker, url, Url) +ADD_SCENE(nfc_maker, wifi, Wifi) +ADD_SCENE(nfc_maker, wifi_auth, WifiAuth) +ADD_SCENE(nfc_maker, wifi_encr, WifiEncr) +ADD_SCENE(nfc_maker, wifi_pass, WifiPass) +ADD_SCENE(nfc_maker, name, Name) +ADD_SCENE(nfc_maker, result, Result) diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c new file mode 100644 index 000000000..9697c0d1c --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_https.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_https_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_https_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Https Link:"); + + strlcpy(app->text_buf, "flipper-xtre.me", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_https_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_https_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_https_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c new file mode 100644 index 000000000..5b76f56a2 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_mail_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_mail_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Mail Address:"); + + strlcpy(app->text_buf, "ben.dover@example.com", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_mail_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_mail_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_mail_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c new file mode 100644 index 000000000..4268e7e2f --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_menu.c @@ -0,0 +1,61 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_menu_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_menu_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "NFC Tag Maker:"); + + submenu_add_item( + submenu, + "Bluetooth MAC", + NfcMakerSceneBluetooth, + nfc_maker_scene_menu_submenu_callback, + app); + + submenu_add_item( + submenu, "HTTPS Link", NfcMakerSceneHttps, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Mail Address", NfcMakerSceneMail, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Phone Number", NfcMakerScenePhone, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Text Note", NfcMakerSceneText, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "Plain URL", NfcMakerSceneUrl, nfc_maker_scene_menu_submenu_callback, app); + + submenu_add_item( + submenu, "WiFi Login", NfcMakerSceneWifi, nfc_maker_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_menu_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneMenu, event.event); + consumed = true; + scene_manager_next_scene(app->scene_manager, event.event); + } + + return consumed; +} + +void nfc_maker_scene_menu_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c new file mode 100644 index 000000000..dd5170d94 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_name.c @@ -0,0 +1,57 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_name_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_name_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Name the NFC tag:"); + + set_random_name(app->name_buf, TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_name_text_input_callback, + app, + app->name_buf, + TEXT_INPUT_LEN, + true); + + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_name_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneResult); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_name_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c new file mode 100644 index 000000000..4e70bcb09 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_phone_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_phone_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Phone Number:"); + + strlcpy(app->text_buf, "+", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_phone_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_phone_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_phone_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c new file mode 100644 index 000000000..912bf3c9f --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_result.c @@ -0,0 +1,359 @@ +#include "../nfc_maker.h" + +enum PopupEvent { + PopupEventExit, +}; + +static void nfc_maker_scene_result_popup_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, PopupEventExit); +} + +void nfc_maker_scene_result_on_enter(void* context) { + NfcMaker* app = context; + Popup* popup = app->popup; + bool success = false; + + FlipperFormat* file = flipper_format_file_alloc(furi_record_open(RECORD_STORAGE)); + FuriString* path = furi_string_alloc(); + furi_string_printf(path, NFC_APP_FOLDER "/%s" NFC_APP_EXTENSION, app->name_buf); + do { + if(!flipper_format_file_open_new(file, furi_string_get_cstr(path))) break; + + uint32_t pages = 42; + size_t size = pages * 4; + uint8_t* buf = malloc(size); + + if(!flipper_format_write_header_cstr(file, "Flipper NFC device", 3)) break; + if(!flipper_format_write_string_cstr(file, "Device type", "NTAG203")) break; + + // Serial number + buf[0] = 0x04; + furi_hal_random_fill_buf(&buf[1], 8); + uint8_t uid[7]; + memcpy(&uid[0], &buf[0], 3); + memcpy(&uid[3], &buf[4], 4); + + if(!flipper_format_write_hex(file, "UID", uid, sizeof(uid))) break; + if(!flipper_format_write_string_cstr(file, "ATQA", "00 44")) break; + if(!flipper_format_write_string_cstr(file, "SAK", "00")) break; + // TODO: Maybe randomize? + if(!flipper_format_write_string_cstr( + file, + "Signature", + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00")) + break; + if(!flipper_format_write_string_cstr(file, "Mifare version", "00 00 00 00 00 00 00 00")) + break; + + if(!flipper_format_write_string_cstr(file, "Counter 0", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 0", "00")) break; + if(!flipper_format_write_string_cstr(file, "Counter 1", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 1", "00")) break; + if(!flipper_format_write_string_cstr(file, "Counter 2", "0")) break; + if(!flipper_format_write_string_cstr(file, "Tearing 2", "00")) break; + if(!flipper_format_write_uint32(file, "Pages total", &pages, 1)) break; + + // Static data + buf[9] = 0x48; // Internal + buf[10] = 0x00; // Lock bytes + buf[11] = 0x00; // ... + buf[12] = 0xE1; // Capability container + buf[13] = 0x10; // ... + buf[14] = 0x12; // ... + buf[15] = 0x00; // ... + buf[16] = 0x01; // ... + buf[17] = 0x03; // ... + buf[18] = 0xA0; // ... + buf[19] = 0x10; // ... + buf[20] = 0x44; // ... + buf[21] = 0x03; // Message flags + + size_t msg_len = 0; + switch(scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneMenu)) { + case NfcMakerSceneBluetooth: { + msg_len = 0x2B; + + buf[23] = 0xD2; + buf[24] = 0x20; + buf[25] = 0x08; + buf[26] = 0x61; + buf[27] = 0x70; + + buf[28] = 0x70; + buf[29] = 0x6C; + buf[30] = 0x69; + buf[31] = 0x63; + + buf[32] = 0x61; + buf[33] = 0x74; + buf[34] = 0x69; + buf[35] = 0x6F; + + buf[36] = 0x6E; + buf[37] = 0x2F; + buf[38] = 0x76; + buf[39] = 0x6E; + + buf[40] = 0x64; + buf[41] = 0x2E; + buf[42] = 0x62; + buf[43] = 0x6C; + + buf[44] = 0x75; + buf[45] = 0x65; + buf[46] = 0x74; + buf[47] = 0x6F; + + buf[48] = 0x6F; + buf[49] = 0x74; + buf[50] = 0x68; + buf[51] = 0x2E; + + buf[52] = 0x65; + buf[53] = 0x70; + buf[54] = 0x2E; + buf[55] = 0x6F; + + buf[56] = 0x6F; + buf[57] = 0x62; + buf[58] = 0x08; + buf[59] = 0x00; + + memcpy(&buf[60], app->mac_buf, GAP_MAC_ADDR_SIZE); + break; + } + case NfcMakerSceneHttps: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x04; // Prepend "https://" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneMail: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x06; // Prepend "mailto:" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerScenePhone: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x05; // Prepend "tel:" + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneText: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 7; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 3; + buf[26] = 0x54; + + buf[27] = 0x02; + buf[28] = 0x65; // e + buf[29] = 0x6E; // n + memcpy(&buf[30], app->text_buf, data_len); + break; + } + case NfcMakerSceneUrl: { + uint8_t data_len = strnlen(app->text_buf, TEXT_INPUT_LEN); + msg_len = data_len + 5; + + buf[23] = 0xD1; + buf[24] = 0x01; + buf[25] = data_len + 1; + buf[26] = 0x55; + + buf[27] = 0x00; // No prepend + memcpy(&buf[28], app->text_buf, data_len); + break; + } + case NfcMakerSceneWifi: { + uint8_t ssid_len = strnlen(app->text_buf, WIFI_INPUT_LEN); + uint8_t pass_len = strnlen(app->pass_buf, WIFI_INPUT_LEN); + uint8_t data_len = ssid_len + pass_len; + msg_len = data_len + 73; + + buf[23] = 0xD2; + buf[24] = 0x17; + buf[25] = data_len + 47; + buf[26] = 0x61; + buf[27] = 0x70; + + buf[28] = 0x70; + buf[29] = 0x6C; + buf[30] = 0x69; + buf[31] = 0x63; + + buf[32] = 0x61; + buf[33] = 0x74; + buf[34] = 0x69; + buf[35] = 0x6F; + + buf[36] = 0x6E; + buf[37] = 0x2F; + buf[38] = 0x76; + buf[39] = 0x6E; + + buf[40] = 0x64; + buf[41] = 0x2E; + buf[42] = 0x77; + buf[43] = 0x66; + + buf[44] = 0x61; + buf[45] = 0x2E; + buf[46] = 0x77; + buf[47] = 0x73; + + buf[48] = 0x63; + buf[49] = 0x10; + buf[50] = 0x0E; + buf[51] = 0x00; + + buf[52] = data_len + 43; + buf[53] = 0x10; + buf[54] = 0x26; + buf[55] = 0x00; + + buf[56] = 0x01; + buf[57] = 0x01; + buf[58] = 0x10; + buf[59] = 0x45; + + buf[60] = 0x00; + buf[61] = ssid_len; + memcpy(&buf[62], app->text_buf, ssid_len); + size_t ssid = 62 + ssid_len; + buf[ssid + 0] = 0x10; + buf[ssid + 1] = 0x03; + + buf[ssid + 2] = 0x00; + buf[ssid + 3] = 0x02; + buf[ssid + 4] = 0x00; + buf[ssid + 5] = + scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiAuth); + + buf[ssid + 6] = 0x10; + buf[ssid + 7] = 0x0F; + buf[ssid + 8] = 0x00; + buf[ssid + 9] = 0x02; + + buf[ssid + 10] = 0x00; + buf[ssid + 11] = + scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiEncr); + buf[ssid + 12] = 0x10; + buf[ssid + 13] = 0x27; + + buf[ssid + 14] = 0x00; + buf[ssid + 15] = pass_len; + memcpy(&buf[ssid + 16], app->pass_buf, pass_len); + size_t pass = ssid + 16 + pass_len; + buf[pass + 0] = 0x10; + buf[pass + 1] = 0x20; + + buf[pass + 2] = 0x00; + buf[pass + 3] = 0x06; + buf[pass + 4] = 0xFF; + buf[pass + 5] = 0xFF; + + buf[pass + 6] = 0xFF; + buf[pass + 7] = 0xFF; + buf[pass + 8] = 0xFF; + buf[pass + 9] = 0xFF; + + break; + } + default: + break; + } + + // Message length and terminator + buf[22] = msg_len; + size_t msg_end = 23 + msg_len; + buf[msg_end] = 0xFE; + + // Padding + for(size_t i = msg_end + 1; i < size; i++) { + buf[i] = 0x00; + } + + char str[16]; + bool ok = true; + for(size_t page = 0; page < pages; page++) { + snprintf(str, sizeof(str), "Page %u", page); + if(!flipper_format_write_hex(file, str, &buf[page * 4], 4)) { + ok = false; + break; + } + } + if(!ok) break; + + free(buf); + success = true; + + } while(false); + furi_string_free(path); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + if(success) { + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + } else { + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); + } + popup_set_timeout(popup, 1500); + popup_set_context(popup, app); + popup_set_callback(popup, nfc_maker_scene_result_popup_callback); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewPopup); +} + +bool nfc_maker_scene_result_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case PopupEventExit: + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, NfcMakerSceneMenu); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_result_on_exit(void* context) { + NfcMaker* app = context; + popup_reset(app->popup); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c new file mode 100644 index 000000000..17d84e0a9 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_text.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_text_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_text_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Text Note:"); + + strlcpy(app->text_buf, "Lorem ipsum", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_text_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_text_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_text_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c new file mode 100644 index 000000000..1250b46af --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_url.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_url_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_url_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter Plain URL:"); + + strlcpy(app->text_buf, "https://flipper-xtre.me", TEXT_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_url_text_input_callback, + app, + app->text_buf, + TEXT_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_url_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_url_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c new file mode 100644 index 000000000..68efaf7f6 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c @@ -0,0 +1,55 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_wifi_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_wifi_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter WiFi SSID:"); + + strlcpy(app->text_buf, "Bill Wi the Science Fi", WIFI_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_wifi_text_input_callback, + app, + app->text_buf, + WIFI_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_wifi_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiAuth, WifiAuthenticationWpa2Personal); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiAuth); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c new file mode 100644 index 000000000..4cb90dabe --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c @@ -0,0 +1,83 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_wifi_auth_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_wifi_auth_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Authentication Type:"); + + submenu_add_item( + submenu, "Open", WifiAuthenticationOpen, nfc_maker_scene_wifi_auth_submenu_callback, app); + + submenu_add_item( + submenu, + "WPA 2 Personal", + WifiAuthenticationWpa2Personal, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA 2 Enterprise", + WifiAuthenticationWpa2Enterprise, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA Personal", + WifiAuthenticationWpaPersonal, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "WPA Enterprise", + WifiAuthenticationWpaEnterprise, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_add_item( + submenu, + "Shared", + WifiAuthenticationShared, + nfc_maker_scene_wifi_auth_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiAuth)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_wifi_auth_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneWifiAuth, event.event); + consumed = true; + if(event.event == WifiAuthenticationOpen) { + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiEncr, WifiEncryptionNone); + strcpy(app->pass_buf, ""); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + } else { + scene_manager_set_scene_state( + app->scene_manager, NfcMakerSceneWifiEncr, WifiEncryptionAes); + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiEncr); + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_auth_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c new file mode 100644 index 000000000..d1a21f51a --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c @@ -0,0 +1,48 @@ +#include "../nfc_maker.h" + +void nfc_maker_scene_wifi_encr_submenu_callback(void* context, uint32_t index) { + NfcMaker* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void nfc_maker_scene_wifi_encr_on_enter(void* context) { + NfcMaker* app = context; + Submenu* submenu = app->submenu; + + submenu_set_header(submenu, "Encryption Type:"); + + submenu_add_item( + submenu, "AES", WifiEncryptionAes, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "WEP", WifiEncryptionWep, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "TKIP", WifiEncryptionTkip, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_add_item( + submenu, "None", WifiEncryptionNone, nfc_maker_scene_wifi_encr_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, NfcMakerSceneWifiEncr)); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewSubmenu); +} + +bool nfc_maker_scene_wifi_encr_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, NfcMakerSceneWifiEncr, event.event); + consumed = true; + scene_manager_next_scene(app->scene_manager, NfcMakerSceneWifiPass); + } + + return consumed; +} + +void nfc_maker_scene_wifi_encr_on_exit(void* context) { + NfcMaker* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c new file mode 100644 index 000000000..3f5b4bd76 --- /dev/null +++ b/applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c @@ -0,0 +1,53 @@ +#include "../nfc_maker.h" + +enum TextInputResult { + TextInputResultOk, +}; + +static void nfc_maker_scene_wifi_pass_text_input_callback(void* context) { + NfcMaker* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void nfc_maker_scene_wifi_pass_on_enter(void* context) { + NfcMaker* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Enter WiFi Password:"); + + strlcpy(app->pass_buf, "244466666", WIFI_INPUT_LEN); + + text_input_set_result_callback( + text_input, + nfc_maker_scene_wifi_pass_text_input_callback, + app, + app->pass_buf, + WIFI_INPUT_LEN, + true); + + view_dispatcher_switch_to_view(app->view_dispatcher, NfcMakerViewTextInput); +} + +bool nfc_maker_scene_wifi_pass_on_event(void* context, SceneManagerEvent event) { + NfcMaker* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_next_scene(app->scene_manager, NfcMakerSceneName); + break; + default: + break; + } + } + + return consumed; +} + +void nfc_maker_scene_wifi_pass_on_exit(void* context) { + NfcMaker* app = context; + text_input_reset(app->text_input); +}

Fc87HT~&L9^=BZz|H0U{wlGOFDf9!RT5 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..bedf366c6ccf988ca20807c9062e7e7f7b70d9b9 GIT binary patch literal 1305 zcmV+!1?KvRP)td%uS!`N3?sl70Tq=Y_f8vQs0;XV zED)XBPReiX#sDdJNwAypTe}2E!%Bi3mEYPWz|Z5!90#}7NcLlqyRyF$(j0f5e<};0 zCiXnf))PJj@VgcxQ^qg>IOU%TJa%s#gKO{I4ERen`2OlI;e-hACFOGq+UGdanpyhd zh!MrGLQwU>SqPTdX_8FqT&Rq|IwonSQ)8_nr<(vPcE43V*Un>>xuDj1kF+*XGW7m2 z5h_SfY5_)6{wn0bMdEf^hqv5|WpkfN@}%+Map534OM)^2-~o+*G)b*bD!9^Q!o?jG z4_K5>jj4(RWdwjhQ>Dxf&9qw0b0&(G7Mh#)i249W;)uM`O5n^)per5j2#Xbv$inqQ znRfg+C)4VBBFd}C2tIKf?8()m@+)>*2NgG1+?y5~M8@T^0I-u&^ZqFCqGD1=*@Nci zB^QBX5NUkFVgRXF(ywdzZ*?BO0@&PBB;b@yt)UiANG?MfqX#AeSo~X5281<_UjaBa zOagcf=j&Rfk?Myl2ub%vQ4#}S3*gPAx%+kjr4+7MBl&4g9(6sFt$0xSXc&CO*Q4@w z0ab~M#6q585J#rU&2b+MQvj7gnRH8V2Gi7NC2$mmJQ}u(tPy0S0zV50;42>ED&@EK zLthE2CMHD!jtD#M{Ekl)f>wYdffZW12>X#_Xl*oXO@Q6w2`UA&KvMqfj3MO$Mk(lo zN+S;Jv|UVtw!h?qVOR2=~qUvR(D`zla#?~1=3FjjX;$@uZ{~Oa}A+I^}UTn`J627{ZTOhg(`EX z`O_@4V2@LRXV+`W*W8i?TM=NCPpmX@aM3L!kTQm<{-N9P&UKqo@TyX%7ciLX?>S_M zEY%7;j(!ogX#iY9~!cz8q4sMj3sPQ2dGy5OmJhgzk@Z?a zfZ6HVXhc&v>`pjHY3}kukOyB=JVewIaLT0oz2*WUWEf6>(@eStmeMb7-&$@bOJ^Vv z3?V>N=_^a3Goc;)Sn~U-R50{-xCZY|_g2R&@+=j*S&+^LIw<8@&rmWXa1)?=1ibFXJXvWloWgxRI<@+<&e+sR~C zVdb1g^j)3>RPD0r-G4!&{Fg&#D&LE$O5>Ms9}oX=Z1u~b-raxAe;)q;>SwfYfJwge P00000NkvXXu0mjfKQdP` literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png new file mode 100644 index 0000000000000000000000000000000000000000..0731760dffbaa07d4e1f50c1fd65bd0eb1f6ef19 GIT binary patch literal 1677 zcmV;826Fj{P)Pk0p{ai`))_7(%Z%c13qA~ z?!K<;z5d{Rf4{x&Tl`!542k!Bulv6L%Aug2Yxj7;$MZEuWOG|s-&N3I`;Rtxx1%Lk z&hL^%|Guu=df)f`dfoT^Wyclk$>O;#+4(CS|F1!^rxjqc0O|C_N89<@m1A~)^m+nN z2Dcc2zJ^TcHO6Rj)9*9aM^c;`V7%9i!oROvPYVK+J|2d(Y7a|4UT@Ge>aqEUE5;T2 z9~oc(O}&&6Bo?s7NT1JY4Uf$~Qbo(7NUI67`?A+4R@5mSGEg-Z4JZj7n?JnrjyMVF z{M`&-9X)pz#Iw}PS%t!5^N%!fg#n@pR2ZNFZ_v#sx)V~KAK<&6zo++EHvdWi(H-j9 z!9RroqUdIZNPtQ~Ky`C5KxT(>=TW2Z0_7wI(CVHD=njnPjt+Rqemoe=8h{SS)Bx6^ zD2<^RATtKxd`Sns<7VDqyvj;F9{pHGAV zvhXrMrWhOJ#%n~lWB^pbH`C=5$7_HyKqLip1X(lz#|7? z7*Bm5t%2)mjX`52dyMsbs?PTy!T<g*NrmcQKtaJ zt{*Zad0rj8vbt-EEUZ%q{|>?c(vBKJrLpuoex*uBjLXx9lydf&f);WthOC<_u;w zwsd^>Ko31j0xZQURBo|H#9%XqLBYddU9vNHu4_(3JMx@F-&vaN*!dJc!ReZgkH;XM zDmpt>1H$=SzfLFcTpL+;CnHfv^}rKR(wbNsQz?)T&L8Ne{#}T!iKo`D-vwybF))iP z18^O7!K*{%7`p@StNb!3ijEY3+^s8j|4Lbi^!oMVQ0?lO&{8n76aEFN_5{POwDgq% zRtSrplV^>ii0o)VhLXwOBZ2Bdx@&-$GW<0JXy2cjAGyO2rw?tO6Knvf0jw0@{l3yG z=tf32t8;l$p$Mq~IK1a>$`CwF@MEB6EKAnzqu;E3UDXY-Hs~oJYjA|4I+{IQyAxGs zjJYanhSUJjB2Sw`l0VZ0y}P*_#JUjkSo2o`3VI6gwv}f^AT`>|fY#1Gt@C$c5PZf2 z)^&!+X<;}OT>v5H@}A$L^NsiPa}=`ngfM_-A<|PhdL8rdqtogXz;)?3vaZnC3rG#n zg=z{w$+u1zk-4(=jLM0~f_DPF5r`-pxm-GTCjV=Cqc_6JX9Y8q`v6*g)9%yG`Vu@H zIdm<-^{G71kkHMlQWuu~%~56msfnY3=T%Rms5=Ez9Fj=`QAFmKd@)N2%WD8DuU3%N z)iTa$$4CvH4W+HK<=+FKjJ6{MbR(j#=e|ggN@=*|-vb~jbE#ICJqxG=LbN`idhTF~ ztc|(8Wd@Lnz^db`6?|lT=P|Mq-Xf2KZzkxo09xQ7nkcIpL5PP?(RI%5_kQm^_wzbo zjDz+8q~fm120I!--IU(dw30R)b-X0ETmxwNc~?&C9y6OSTQ~7}yz=OLJT1)RT-To; zhGdi+f~?}qv&bEdAWnrDtSbB@r_#@@F?5`Ezxl%uj{~wh&l_Cyy>$wRfX|h2rdF(0 z0|U)UvtrRa1w^CFgRY&EnNu25rSV1Pwuv5187CM(s~0L zESuji*b7p0MxEZ-_wUBaz{!B?2de=r$L}cOcA?T)Tp=se2=ZX&_|uD^Q;=~cf^B{Q X1UzU}jMrLx00000NkvXXu0mjfn1~uQ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..898efdc4b8fe693c99eb9613e82bb5762b1d1645 GIT binary patch literal 1639 zcmV-t2AKJYP)OFJ=4bXJY&xJ?|e>!-lyPvB>sHvdZy8o=pu-&y$T)tzh#s*&p5yYF#~-|i5fwP5th5uK;OYkVpP zYOG~w{+VFsE0k%WHMxQeU9e>V=H`=#DuT_pjmc_=bFwHwmnW+=3Q1Z}6s|PJl)NiH2l+em#-{@b4L9=pb`XIDy4uJGysIFFVH za0`g>rKCx72L3E62MS3(ME9Bu+RqZ*R0PnNMiFr+kMS+ul*Sv9SdZ>KM**e#$r^t# zmpDWVcj~?N4Q?*wC5k*!i{>?cIipmYhZP{K0R7H_U zc}V5!j9?WGCeesgFS{%MKE^mx1)w1pP4rNnRwjKLZ;-1b0xaY4&w6HoCOZ<{><;cs zqN<=df=70)bHoN;D{Syk?h&3FPfJLu>s*hF8G3Kn!C8FAz+Warp*fGFK(53fPi*b`BpGl~78!dq2=i|2qP#i5V}{oPY~2EocYCK+?i3IT6`1xanA&Z^_>V zRyTrBctbYoFtjS!RUvPS5TP6(I#FWGQBlHG;tgT_Hh+%K2_{mFzr(I6R3Qse_3+a5 zCm_@ZSOYR0K&`B@&flBoptZJ+Xz{k)KSogwusZ@W&pD6Eo#-*J#QTlLj2z{k=)@gp z3)k9ZQngVbM}@OgI^7!`W{jd7fcO5rXa23^3^i~?*Y5-(Ilw16k-}?mjRtq3s8e_p zZaG1fQ+6F;S(H`*Xj|FJ@~f0yrEp0ePY1Q4nTFt6AHY*BNIN}4vjT7E4@3~@T=HkF zRRCwq$94AKy}RvX+1Kf8-s7_*2k^qm89WMlRK;j`BLy_@PiyKJ{=`{*;o!`t` zx!s~HuYYSuj6izHC0)nxC59)G!tEc1AUmw9R*o(n>1A}~0vbIoi&QB{^Rsdy^S7Nq ztF^n}ae_{-z7b8L=R1E-fXs=&a)Q(mFou2%RC%ulXsE4_FAMsD2z_VJ0r&{ez!M$q z9v_b)`n~{7rZT0g*MYR-$%+P@y@Bu%fC`q`VVPZ;-PzG>9SP@BRiO0?s#?XGE|2}c zff=0Y;Gy%QQ4_5a=)KA#N^De(;rnmnvI0xSzDt63Cc%o_6Ip}=&-v`Jrp+8IB z-hm8l{%+$0U(Y3U{({Q;LY;FKIu5$6B^002ovPDHLkV1lXu4UYf- literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png new file mode 100644 index 0000000000000000000000000000000000000000..39f5db8a0131b28046b4450ce84eda9d9f519ba9 GIT binary patch literal 1618 zcmV-Y2CeytP)d$G==M{J<5M|M zV+}*|tM~rxqHQ%gJMSy__qI3(&}N~8Sv&e(o7u=S6x0bg?-M}yQ&|x?mq&n|@I(QW zo<&oOB=j2XD((`0s~tl3O}%AymjsygLy?)Ot09daNi?;)N9PgU4I7&ckZ4H8=l3l+0RNmph7QtqhZD?rjQ1HQ(B4Z9u{;8B zhP;hOan<1bd+(yMs(?5c)hjn%CIZZ4S`q<45-mH zx*2|DIRF&~Wf*OP&H`&zc@-9k9@X&h=edoP>+rTEIe^z_)&R7yHp_%H{ua%dEL^Ug zkP^a540ETN%MB9897S*}YhI{<*cbXBU8mTr|-`dCoHF)3`ydk_a%2$FK2j2Ab?hbh9&f zWfD~d%@I7ZbDbkLc+bKH59J==x$(4wqh8dj2yEXhUAqvfTBn47u$?(%? zXtHoeogo_mRydER4lwXW1VQ%OXkSgyS;HgI$>QNP$Y{GG3am_G->C4jU4Yg{5`H|B z_WRzV&8%kNsVVSU!Ka0IB#Ec`=)M120%jdEUZ^<%7hW2W1;jwoz-MwIvSo16u{ht7 z-v*+}t%f&bqYguNCDtzFwg?f*0iqox#u~i4hgG;Cte@u3@mfio?&GD4<^g=&UzPMA zOPREF{RBkn0Iz6A&X^|`T5*ShI>PwA170Vn!fms^wW0_>4UHIH>OXUWRfQb1Eys<< zj4b7^Xvbfg2sBwW0{%IdtJ?1t)%ps+CklOFL6QWvD2oAvjhCh}O|7X=ZRkeISBJ<&wWEsRT-4 z{j@T@vu%YFX!KS(n^eZPkj?>UL5CK3w04rP+8_|zxUNdzI;vcrJV`i5ItTFV`>C^3 z%>u5>Y)(;S9DV}Fb#zOGu_OoZV8_yMdimi8NLF#HVj^VdEIAFV7*GYg>mlWzHO!|Z zm1T7R?T}|DZpa6s&Xz@#PC5sW4DLM(km#Y1q7U+Hg)H8_G#5JM1f_oupfUf1P-ww( zD$3V>bfm#RqNn& zfLrj&CV*O7bp5NsO-hw?=~+&zq5y&y&)|7A&vJ;U5s{gI(A5e+7NW(WNPBumlfB3U z)MP+tm~{Ya;8Oh`!KdtxJ>bT*qNyh_+@Y(tWjutr=Gob-HnR_x?v=7>(CBelq@9A) zzeV7*^RE% z{Il0_JZA zxyBgJaNv3VcE7G`JkRsfLR{DNtFQK)0yi&QpOTB4s{-8GU0AXHof%9W(irq%aD6#& z#>K6}Ddl+{))4!2!zv@7*4Jp?vTh3lWXD>JugVCl>%l}1 zek3DU#Q;!tuHD7}E4>pcey+7UBuLJ(iUH==a}B5yZQY}D0E^*K8<%L=vR-zt90kLF zuV4VnzFw09aP&s?(Y(NG3|Q%v;RViL!2lNdX1^^A5HTDsv7zSX8BDDX*FtFiT|*p9+&)i^ruIKvRyEKX8YFJ=Q~mQk>Psc4MXPt?Rbr z0F>}lc)(sC*(B`W#b91wMyk?16%Jub^<9<&a2w_XqUek|Ob@smz+zMydsSgq2%ht` zpwtH{9VdNN6mJ1{<-u!GimsOH)SQFD9q|$l5&Ft1 zi!(Ug&3$@qrJe0zyP#t7AW|6;OcebSgS)6;NN#^VIxqVAR92oA!*w zIl`N0+KNyP5FJP@j8T=`RN-3>K7&ukB3wae$3d(%$ejje3FR0+*T75#kgLQ|iANNb zhjtPxP3&T)k{m$G{-fWA{i+X5Q}}5HV!^f?0Km2cTZ)L^AYl zz~U8rNwq76XY~-t0nBv(2^C7CHlOuk>$!1{E|8_w=?Lz=Jdc zc<#>l-rW&zqbva1jx|@#BcVM9n8vc=@K4AGpmD#!2udq~-SK~}4M8a^szmQN%!Y3W zAD=O5ejWgRb{GQ2uF^8~EGx^(P?=@`>cBG;pP}9r^G0hNZ$at102~T-GC^f1SRu