diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37515be68..f118e279d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,7 +54,7 @@ jobs: run: bash .github/workflow_data/package.sh - name: Send devbuild webhook - if: "github.event_name == 'push' && github.ref_name == 'dev'" + if: "github.event_name == 'push' && github.ref_name == 'dev' && !contains(github.event.head_commit.message, '--nobuild')" env: NC_HOST: "https://cloud.cynthialabs.net/" NC_USER: "${{ secrets.NC_USER }}" diff --git a/.gitmodules b/.gitmodules index 671e7e2c4..7a280cb11 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,7 @@ [submodule "assets/protobuf"] path = assets/protobuf url = https://github.com/flipperdevices/flipperzero-protobuf.git + shallow = false [submodule "lib/libusb_stm32"] path = lib/libusb_stm32 url = https://github.com/flipperdevices/libusb_stm32.git @@ -40,3 +41,6 @@ [submodule "lib/stm32wb_copro"] path = lib/stm32wb_copro url = https://github.com/flipperdevices/stm32wb_copro.git +[submodule "applications/external/totp/lib/wolfssl"] + path = applications/external/totp/lib/wolfssl + url = https://github.com/wolfSSL/wolfssl.git diff --git a/.vscode/.gitignore b/.vscode/.gitignore index 670df7d48..481efcdef 100644 --- a/.vscode/.gitignore +++ b/.vscode/.gitignore @@ -1,4 +1,5 @@ -./c_cpp_properties.json -./launch.json -./settings.json -./tasks.json +/c_cpp_properties.json +/extensions.json +/launch.json +/settings.json +/tasks.json diff --git a/.vscode/example/clangd/extensions.json b/.vscode/example/clangd/extensions.json new file mode 100644 index 000000000..daab417cd --- /dev/null +++ b/.vscode/example/clangd/extensions.json @@ -0,0 +1,19 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "ms-python.black-formatter", + "llvm-vs-code-extensions.vscode-clangd", + "amiralizadeh9480.cpp-helper", + "marus25.cortex-debug", + "zxh404.vscode-proto3", + "augustocdias.tasks-shell-input" + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [ + "twxs.cmake", + "ms-vscode.cpptools", + "ms-vscode.cmake-tools" + ] +} diff --git a/.vscode/example/c_cpp_properties.json b/.vscode/example/cpptools/c_cpp_properties.json similarity index 100% rename from .vscode/example/c_cpp_properties.json rename to .vscode/example/cpptools/c_cpp_properties.json diff --git a/.vscode/extensions.json b/.vscode/example/cpptools/extensions.json similarity index 93% rename from .vscode/extensions.json rename to .vscode/example/cpptools/extensions.json index ead935b08..a8babee1c 100644 --- a/.vscode/extensions.json +++ b/.vscode/example/cpptools/extensions.json @@ -13,6 +13,7 @@ ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [ + "llvm-vs-code-extensions.vscode-clangd", "twxs.cmake", "ms-vscode.cmake-tools" ] diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json index 19a03b69d..efa08157b 100644 --- a/.vscode/example/settings.json +++ b/.vscode/example/settings.json @@ -21,5 +21,10 @@ "SConscript": "python", "SConstruct": "python", "*.fam": "python", - } -} + }, + "clangd.arguments": [ + // We might be able to tighten this a bit more to only include the correct toolchain. + "--query-driver=**", + "--compile-commands-dir=${workspaceFolder}/build/latest" + ] +} \ No newline at end of file diff --git a/.vscode/example/tasks.json b/.vscode/example/tasks.json index 1d2b55a2c..9861afa1e 100644 --- a/.vscode/example/tasks.json +++ b/.vscode/example/tasks.json @@ -28,29 +28,17 @@ "command": "./fbt -c" }, { - "label": "[Release] Flash (ST-Link)", + "label": "[Release] Flash (SWD)", "group": "build", "type": "shell", "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash" }, { - "label": "[Debug] Flash (ST-Link)", + "label": "[Debug] Flash (SWD)", "group": "build", "type": "shell", "command": "./fbt FORCE=1 flash" }, - { - "label": "[Release] Flash (blackmagic)", - "group": "build", - "type": "shell", - "command": "./fbt COMPACT=1 DEBUG=0 FORCE=1 flash_blackmagic" - }, - { - "label": "[Debug] Flash (blackmagic)", - "group": "build", - "type": "shell", - "command": "./fbt FORCE=1 flash_blackmagic" - }, { "label": "[Release] Flash (JLink)", "group": "build", diff --git a/SConstruct b/SConstruct index 3ea360979..8d389b70b 100644 --- a/SConstruct +++ b/SConstruct @@ -45,6 +45,7 @@ distenv = coreenv.Clone( ], ENV=os.environ, UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}", + VSCODE_LANG_SERVER=ARGUMENTS.get("LANG_SERVER", "cpptools"), ) firmware_env = distenv.AddFwProject( @@ -184,27 +185,15 @@ copro_dist = distenv.CoproBuilder( distenv.AlwaysBuild(copro_dist) distenv.Alias("copro_dist", copro_dist) -firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) + +firmware_flash = distenv.AddFwFlashTarget(firmware_env) distenv.Alias("flash", firmware_flash) +# To be implemented in fwflash.py firmware_jflash = distenv.AddJFlashTarget(firmware_env) distenv.Alias("jflash", firmware_jflash) -firmware_bm_flash = distenv.PhonyTarget( - "flash_blackmagic", - "$GDB $GDBOPTS $SOURCES $GDBFLASH", - source=firmware_env["FW_ELF"], - GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", - GDBREMOTE="${BLACKMAGIC_ADDR}", - GDBFLASH=[ - "-ex", - "load", - "-ex", - "quit", - ], -) - -gdb_backtrace_all_threads = distenv.PhonyTarget( +distenv.PhonyTarget( "gdb_trace_all", "$GDB $GDBOPTS $SOURCES $GDBFLASH", source=firmware_env["FW_ELF"], @@ -327,6 +316,9 @@ distenv.PhonyTarget( "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}" ) +# Update WiFi devboard firmware +distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") + # Find blackmagic probe distenv.PhonyTarget( @@ -345,7 +337,14 @@ distenv.PhonyTarget( ) # Prepare vscode environment -vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*")) +VSCODE_LANG_SERVER = cmd_environment["LANG_SERVER"] +vscode_dist = distenv.Install( + "#.vscode", + [ + distenv.Glob("#.vscode/example/*.json"), + distenv.Glob(f"#.vscode/example/{VSCODE_LANG_SERVER}/*.json"), + ], +) distenv.Precious(vscode_dist) distenv.NoClean(vscode_dist) distenv.Alias("vscode_dist", vscode_dist) diff --git a/applications/debug/accessor/helpers/wiegand.cpp b/applications/debug/accessor/helpers/wiegand.cpp index 5cb3a85f5..10b284eaa 100644 --- a/applications/debug/accessor/helpers/wiegand.cpp +++ b/applications/debug/accessor/helpers/wiegand.cpp @@ -174,7 +174,7 @@ bool WIEGAND::DoWiegandConversion() { return false; } - // TODO: Handle validation failure case! + // TODO FL-3490: Handle validation failure case! } else if(4 == _bitCount) { // 4-bit Wiegand codes have no data integrity check so we just // read the LOW nibble. diff --git a/applications/debug/subghz_test/views/subghz_test_packet.c b/applications/debug/subghz_test/views/subghz_test_packet.c index bab83ab5b..1f3458296 100644 --- a/applications/debug/subghz_test/views/subghz_test_packet.c +++ b/applications/debug/subghz_test/views/subghz_test_packet.c @@ -56,7 +56,6 @@ static void subghz_test_packet_rx_callback(bool level, uint32_t duration, void* subghz_decoder_princeton_for_testing_parse(instance->decoder, level, duration); } -//todo static void subghz_test_packet_rx_pt_callback(SubGhzDecoderPrinceton* parser, void* context) { UNUSED(parser); furi_assert(context); diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index 3e262a09d..8863a1a94 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -1,12 +1,11 @@ App( appid="uart_echo", - name="[GPIO] UART Echo", + name="UART Echo", apptype=FlipperAppType.DEBUG, entry_point="uart_echo_app", cdefines=["APP_UART_ECHO"], requires=["gui"], stack_size=2 * 1024, order=70, - fap_icon="uart_10px.png", fap_category="Debug", ) diff --git a/applications/debug/uart_echo/uart_10px.png b/applications/debug/uart_echo/uart_10px.png deleted file mode 100644 index 8420f5692..000000000 Binary files a/applications/debug/uart_echo/uart_10px.png and /dev/null differ diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam index 949bb3fc2..ad9a278f3 100644 --- a/applications/debug/unit_tests/application.fam +++ b/applications/debug/unit_tests/application.fam @@ -3,6 +3,7 @@ App( apptype=FlipperAppType.STARTUP, entry_point="unit_tests_on_system_start", cdefines=["APP_UNIT_TESTS"], + requires=["system_settings"], provides=["delay_test"], order=100, ) diff --git a/applications/debug/unit_tests/furi/furi_memmgr_test.c b/applications/debug/unit_tests/furi/furi_memmgr_test.c index f7bc234a0..05d967a99 100644 --- a/applications/debug/unit_tests/furi/furi_memmgr_test.c +++ b/applications/debug/unit_tests/furi/furi_memmgr_test.c @@ -26,7 +26,7 @@ void test_furi_memmgr() { mu_assert_int_eq(66, ((uint8_t*)ptr)[i]); } - // TODO: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized + // TODO FL-3492: fix realloc to copy only old size, and write testcase that leftover of reallocated memory is zero-initialized free(ptr); // allocate and zero-initialize array (calloc) diff --git a/applications/debug/unit_tests/furi/furi_string_test.c b/applications/debug/unit_tests/furi/furi_string_test.c index 3ea95b92b..6cbcc0dcc 100644 --- a/applications/debug/unit_tests/furi/furi_string_test.c +++ b/applications/debug/unit_tests/furi/furi_string_test.c @@ -69,7 +69,7 @@ MU_TEST(mu_test_furi_string_mem) { mu_check(string != NULL); mu_check(!furi_string_empty(string)); - // TODO: how to test furi_string_reserve? + // TODO FL-3493: how to test furi_string_reserve? // test furi_string_reset furi_string_reset(string); diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c new file mode 100644 index 000000000..b06d51130 --- /dev/null +++ b/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c @@ -0,0 +1,602 @@ +#include +#include +#include +#include "../minunit.h" + +static const uint8_t key_ctr_1[32] = { + 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, + 0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04, +}; +static const uint8_t iv_ctr_1[16] = { + 0x00, + 0x00, + 0x00, + 0x60, + 0xDB, + 0x56, + 0x72, + 0xC9, + 0x7A, + 0xA8, + 0xF0, + 0xB2, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_1[16] = { + 0x53, + 0x69, + 0x6E, + 0x67, + 0x6C, + 0x65, + 0x20, + 0x62, + 0x6C, + 0x6F, + 0x63, + 0x6B, + 0x20, + 0x6D, + 0x73, + 0x67, +}; +static const uint8_t tv_ctr_ct_1[16] = { + 0x14, + 0x5A, + 0xD0, + 0x1D, + 0xBF, + 0x82, + 0x4E, + 0xC7, + 0x56, + 0x08, + 0x63, + 0xDC, + 0x71, + 0xE3, + 0xE0, + 0xC0, +}; + +static const uint8_t key_ctr_2[32] = { + 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, + 0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04, +}; +static const uint8_t iv_ctr_2[16] = { + 0x00, + 0x00, + 0x00, + 0x60, + 0xDB, + 0x56, + 0x72, + 0xC9, + 0x7A, + 0xA8, + 0xF0, + 0xB2, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_2[0] = {}; +//static const uint8_t tv_ctr_ct_2[0] = {}; + +static const uint8_t key_ctr_3[32] = { + 0xF6, 0xD6, 0x6D, 0x6B, 0xD5, 0x2D, 0x59, 0xBB, 0x07, 0x96, 0x36, 0x58, 0x79, 0xEF, 0xF8, 0x86, + 0xC6, 0x6D, 0xD5, 0x1A, 0x5B, 0x6A, 0x99, 0x74, 0x4B, 0x50, 0x59, 0x0C, 0x87, 0xA2, 0x38, 0x84, +}; +static const uint8_t iv_ctr_3[16] = { + 0x00, + 0xFA, + 0xAC, + 0x24, + 0xC1, + 0x58, + 0x5E, + 0xF1, + 0x5A, + 0x43, + 0xD8, + 0x75, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_3[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, +}; +static const uint8_t tv_ctr_ct_3[32] = { + 0xF0, 0x5E, 0x23, 0x1B, 0x38, 0x94, 0x61, 0x2C, 0x49, 0xEE, 0x00, 0x0B, 0x80, 0x4E, 0xB2, 0xA9, + 0xB8, 0x30, 0x6B, 0x50, 0x8F, 0x83, 0x9D, 0x6A, 0x55, 0x30, 0x83, 0x1D, 0x93, 0x44, 0xAF, 0x1C, +}; + +static const uint8_t key_ctr_4[32] = { + 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2, + 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D, +}; +static const uint8_t iv_ctr_4[16] = { + 0x00, + 0x1C, + 0xC5, + 0xB7, + 0x51, + 0xA5, + 0x1D, + 0x70, + 0xA1, + 0xC1, + 0x11, + 0x48, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_4[36] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, +}; +static const uint8_t tv_ctr_ct_4[36] = { + 0xEB, 0x6C, 0x52, 0x82, 0x1D, 0x0B, 0xBB, 0xF7, 0xCE, 0x75, 0x94, 0x46, + 0x2A, 0xCA, 0x4F, 0xAA, 0xB4, 0x07, 0xDF, 0x86, 0x65, 0x69, 0xFD, 0x07, + 0xF4, 0x8C, 0xC0, 0xB5, 0x83, 0xD6, 0x07, 0x1F, 0x1E, 0xC0, 0xE6, 0xB8, +}; + +static const uint8_t key_ctr_5[32] = { + 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2, + 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D, +}; +static const uint8_t iv_ctr_5[16] = { + 0x00, + 0x1C, + 0xC5, + 0xB7, + 0x51, + 0xA5, + 0x1D, + 0x70, + 0xA1, + 0xC1, + 0x11, + 0x48, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_5[0] = {}; +//static const uint8_t tv_ctr_ct_5[0] = {}; + +static const uint8_t key_gcm_1[32] = { + 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, 0x00, +}; +static const uint8_t iv_gcm_1[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_1[0] = {}; +//static const uint8_t tv_gcm_ct_1[0] = {}; +static const uint8_t aad_gcm_1[0] = {}; +static const uint8_t tv_gcm_tag_1[16] = { + 0x53, + 0x0F, + 0x8A, + 0xFB, + 0xC7, + 0x45, + 0x36, + 0xB9, + 0xA9, + 0x63, + 0xB4, + 0xF1, + 0xC4, + 0xCB, + 0x73, + 0x8B, +}; + +static const uint8_t key_gcm_2[32] = { + 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, 0x00, +}; +static const uint8_t iv_gcm_2[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_2[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t tv_gcm_ct_2[16] = { + 0xCE, + 0xA7, + 0x40, + 0x3D, + 0x4D, + 0x60, + 0x6B, + 0x6E, + 0x07, + 0x4E, + 0xC5, + 0xD3, + 0xBA, + 0xF3, + 0x9D, + 0x18, +}; +static const uint8_t aad_gcm_2[0] = {}; +static const uint8_t tv_gcm_tag_2[16] = { + 0xD0, + 0xD1, + 0xC8, + 0xA7, + 0x99, + 0x99, + 0x6B, + 0xF0, + 0x26, + 0x5B, + 0x98, + 0xB5, + 0xD4, + 0x8A, + 0xB9, + 0x19, +}; + +static const uint8_t key_gcm_3[32] = { + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, +}; +static const uint8_t iv_gcm_3[16] = { + 0xCA, + 0xFE, + 0xBA, + 0xBE, + 0xFA, + 0xCE, + 0xDB, + 0xAD, + 0xDE, + 0xCA, + 0xF8, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_3[64] = { + 0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, 0x9A, + 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, + 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, 0xA6, 0xB5, 0x25, + 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, 0x1A, 0xAF, 0xD2, 0x55, +}; +static const uint8_t tv_gcm_ct_3[64] = { + 0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, 0x7D, + 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, 0xD1, 0xAA, + 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, 0x89, 0x80, 0x15, 0xAD, +}; +static const uint8_t aad_gcm_3[0] = {}; +static const uint8_t tv_gcm_tag_3[16] = { + 0xB0, + 0x94, + 0xDA, + 0xC5, + 0xD9, + 0x34, + 0x71, + 0xBD, + 0xEC, + 0x1A, + 0x50, + 0x22, + 0x70, + 0xE3, + 0xCC, + 0x6C, +}; + +static const uint8_t key_gcm_4[32] = { + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, +}; +static const uint8_t iv_gcm_4[16] = { + 0xCA, + 0xFE, + 0xBA, + 0xBE, + 0xFA, + 0xCE, + 0xDB, + 0xAD, + 0xDE, + 0xCA, + 0xF8, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_4[60] = { + 0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, + 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, + 0x8A, 0x72, 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, + 0xA6, 0xB5, 0x25, 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, +}; +static const uint8_t tv_gcm_ct_4[60] = { + 0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, + 0x7D, 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, + 0xD1, 0xAA, 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, + 0x82, 0x88, 0x38, 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, +}; +static const uint8_t aad_gcm_4[20] = { + 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED, + 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2, +}; +static const uint8_t tv_gcm_tag_4[16] = { + 0x76, + 0xFC, + 0x6E, + 0xCE, + 0x0F, + 0x4E, + 0x17, + 0x68, + 0xCD, + 0xDF, + 0x88, + 0x53, + 0xBB, + 0x2D, + 0x55, + 0x1B, +}; + +static void furi_hal_crypto_ctr_setup() { +} + +static void furi_hal_crypto_ctr_teardown() { +} + +static void furi_hal_crypto_gcm_setup() { +} + +static void furi_hal_crypto_gcm_teardown() { +} + +MU_TEST(furi_hal_crypto_ctr_1) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_1)]; + + ret = furi_hal_crypto_ctr(key_ctr_1, iv_ctr_1, pt_ctr_1, ct, sizeof(pt_ctr_1)); + mu_assert(ret, "CTR 1 failed"); + mu_assert_mem_eq(tv_ctr_ct_1, ct, sizeof(pt_ctr_1)); +} + +MU_TEST(furi_hal_crypto_ctr_2) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_2)]; + + ret = furi_hal_crypto_ctr(key_ctr_2, iv_ctr_2, pt_ctr_2, ct, sizeof(pt_ctr_2)); + mu_assert(ret, "CTR 2 failed"); + //mu_assert_mem_eq(tv_ctr_ct_2, ct, sizeof(pt_ctr_2)); +} + +MU_TEST(furi_hal_crypto_ctr_3) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_3)]; + + ret = furi_hal_crypto_ctr(key_ctr_3, iv_ctr_3, pt_ctr_3, ct, sizeof(pt_ctr_3)); + mu_assert(ret, "CTR 3 failed"); + mu_assert_mem_eq(tv_ctr_ct_3, ct, sizeof(pt_ctr_3)); +} + +MU_TEST(furi_hal_crypto_ctr_4) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_4)]; + + ret = furi_hal_crypto_ctr(key_ctr_4, iv_ctr_4, pt_ctr_4, ct, sizeof(pt_ctr_4)); + mu_assert(ret, "CTR 4 failed"); + mu_assert_mem_eq(tv_ctr_ct_4, ct, sizeof(pt_ctr_4)); +} + +MU_TEST(furi_hal_crypto_ctr_5) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_5)]; + + ret = furi_hal_crypto_ctr(key_ctr_5, iv_ctr_5, pt_ctr_5, ct, sizeof(pt_ctr_5)); + mu_assert(ret, "CTR 5 failed"); + //mu_assert_mem_eq(tv_ctr_ct_5, ct, sizeof(pt_ctr_5)); +} + +MU_TEST(furi_hal_crypto_gcm_1) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_1)]; + uint8_t ct[sizeof(pt_gcm_1)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_1, + iv_gcm_1, + aad_gcm_1, + sizeof(aad_gcm_1), + pt_gcm_1, + ct, + sizeof(pt_gcm_1), + tag_enc, + false); + mu_assert(ret, "GCM 1 encryption failed"); + //mu_assert_mem_eq(tv_gcm_ct_1, ct, sizeof(pt_gcm_1)); + mu_assert_mem_eq(tv_gcm_tag_1, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_1, iv_gcm_1, aad_gcm_1, sizeof(aad_gcm_1), ct, pt, sizeof(pt_gcm_1), tag_dec, true); + mu_assert(ret, "GCM 1 decryption failed"); + //mu_assert_mem_eq(pt_gcm_1, pt, sizeof(pt_gcm_1)); + mu_assert_mem_eq(tv_gcm_tag_1, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_2) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_2)]; + uint8_t ct[sizeof(pt_gcm_2)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_2, + iv_gcm_2, + aad_gcm_2, + sizeof(aad_gcm_2), + pt_gcm_2, + ct, + sizeof(pt_gcm_2), + tag_enc, + false); + mu_assert(ret, "GCM 2 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_2, ct, sizeof(pt_gcm_2)); + mu_assert_mem_eq(tv_gcm_tag_2, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_2, iv_gcm_2, aad_gcm_2, sizeof(aad_gcm_2), ct, pt, sizeof(pt_gcm_2), tag_dec, true); + mu_assert(ret, "GCM 2 decryption failed"); + mu_assert_mem_eq(pt_gcm_2, pt, sizeof(pt_gcm_2)); + mu_assert_mem_eq(tv_gcm_tag_2, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_3) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_3)]; + uint8_t ct[sizeof(pt_gcm_3)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_3, + iv_gcm_3, + aad_gcm_3, + sizeof(aad_gcm_3), + pt_gcm_3, + ct, + sizeof(pt_gcm_3), + tag_enc, + false); + mu_assert(ret, "GCM 3 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_3, ct, sizeof(pt_gcm_3)); + mu_assert_mem_eq(tv_gcm_tag_3, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_3, iv_gcm_3, aad_gcm_3, sizeof(aad_gcm_3), ct, pt, sizeof(pt_gcm_3), tag_dec, true); + mu_assert(ret, "GCM 3 decryption failed"); + mu_assert_mem_eq(pt_gcm_3, pt, sizeof(pt_gcm_3)); + mu_assert_mem_eq(tv_gcm_tag_3, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_4) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_4)]; + uint8_t ct[sizeof(pt_gcm_4)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_4, + iv_gcm_4, + aad_gcm_4, + sizeof(aad_gcm_4), + pt_gcm_4, + ct, + sizeof(pt_gcm_4), + tag_enc, + false); + mu_assert(ret, "GCM 4 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_4, ct, sizeof(pt_gcm_4)); + mu_assert_mem_eq(tv_gcm_tag_4, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_4, iv_gcm_4, aad_gcm_4, sizeof(aad_gcm_4), ct, pt, sizeof(pt_gcm_4), tag_dec, true); + mu_assert(ret, "GCM 4 decryption failed"); + mu_assert_mem_eq(pt_gcm_4, pt, sizeof(pt_gcm_4)); + mu_assert_mem_eq(tv_gcm_tag_4, tag_dec, 16); +} + +MU_TEST_SUITE(furi_hal_crypto_ctr_test) { + MU_SUITE_CONFIGURE(&furi_hal_crypto_ctr_setup, &furi_hal_crypto_ctr_teardown); + MU_RUN_TEST(furi_hal_crypto_ctr_1); + MU_RUN_TEST(furi_hal_crypto_ctr_2); + MU_RUN_TEST(furi_hal_crypto_ctr_3); + MU_RUN_TEST(furi_hal_crypto_ctr_4); + MU_RUN_TEST(furi_hal_crypto_ctr_5); +} + +MU_TEST_SUITE(furi_hal_crypto_gcm_test) { + MU_SUITE_CONFIGURE(&furi_hal_crypto_gcm_setup, &furi_hal_crypto_gcm_teardown); + MU_RUN_TEST(furi_hal_crypto_gcm_1); + MU_RUN_TEST(furi_hal_crypto_gcm_2); + MU_RUN_TEST(furi_hal_crypto_gcm_3); + MU_RUN_TEST(furi_hal_crypto_gcm_4); +} + +int run_minunit_test_furi_hal_crypto() { + MU_RUN_SUITE(furi_hal_crypto_ctr_test); + MU_RUN_SUITE(furi_hal_crypto_gcm_test); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/lfrfid/bit_lib_test.c b/applications/debug/unit_tests/lfrfid/bit_lib_test.c index 726615703..dcb69de7f 100644 --- a/applications/debug/unit_tests/lfrfid/bit_lib_test.c +++ b/applications/debug/unit_tests/lfrfid/bit_lib_test.c @@ -311,7 +311,7 @@ MU_TEST(test_bit_lib_test_parity) { } MU_TEST(test_bit_lib_remove_bit_every_nth) { - // TODO: more tests + // TODO FL-3494: more tests uint8_t data_i[1] = {0b00001111}; uint8_t data_o[1] = {0b00011111}; size_t length; diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index e01f56be1..caf740e9a 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -287,7 +287,8 @@ static void test_rpc_create_simple_message( PB_Main* message, uint16_t tag, const char* str, - uint32_t command_id) { + uint32_t command_id, + bool flag) { furi_check(message); char* str_copy = NULL; @@ -308,6 +309,7 @@ static void test_rpc_create_simple_message( break; case PB_Main_storage_list_request_tag: message->content.storage_list_request.path = str_copy; + message->content.storage_list_request.include_md5 = flag; break; case PB_Main_storage_mkdir_request_tag: message->content.storage_mkdir_request.path = str_copy; @@ -419,6 +421,7 @@ static void } mu_check(result_msg_file->size == expected_msg_file->size); mu_check(result_msg_file->type == expected_msg_file->type); + mu_assert_string_eq(expected_msg_file->md5sum, result_msg_file->md5sum); if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) { mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF??? @@ -430,10 +433,10 @@ static void } static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) { - mu_check(result->command_id == expected->command_id); - mu_check(result->command_status == expected->command_status); - mu_check(result->has_next == expected->has_next); - mu_check(result->which_content == expected->which_content); + mu_assert_int_eq(expected->command_id, result->command_id); + mu_assert_int_eq(expected->command_status, result->command_status); + mu_assert_int_eq(expected->has_next, result->has_next); + mu_assert_int_eq(expected->which_content, result->which_content); if(result->command_status != PB_CommandStatus_OK) { mu_check(result->which_content == PB_Main_empty_tag); } @@ -573,10 +576,15 @@ static void static void test_rpc_storage_list_create_expected_list( MsgList_t msg_list, const char* path, - uint32_t command_id) { + uint32_t command_id, + bool append_md5) { Storage* fs_api = furi_record_open(RECORD_STORAGE); File* dir = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + PB_Main response = { .command_id = command_id, .has_next = false, @@ -614,6 +622,17 @@ static void test_rpc_storage_list_create_expected_list( list->file[i].data = NULL; /* memory free inside rpc_encode_and_send() -> pb_release() */ list->file[i].name = name; + + if(append_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf(md5_path, "%s/%s", path, name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } } else { @@ -626,6 +645,10 @@ static void test_rpc_storage_list_create_expected_list( response.has_next = false; MsgList_push_back(msg_list, response); + furi_string_free(md5); + furi_string_free(md5_path); + storage_file_free(file); + storage_dir_close(dir); storage_file_free(dir); @@ -675,16 +698,17 @@ static void test_rpc_free_msg_list(MsgList_t msg_list) { MsgList_clear(msg_list); } -static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { +static void test_rpc_storage_list_run(const char* path, uint32_t command_id, bool md5) { PB_Main request; MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_list_request_tag, path, command_id, md5); if(!strcmp(path, "/")) { test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id); } else { - test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id); + test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id, md5); } test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -694,15 +718,25 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { } MU_TEST(test_storage_list) { - test_rpc_storage_list_run("/", ++command_id); - test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id); + test_rpc_storage_list_run("/", ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false); + test_rpc_storage_list_run("error_path", ++command_id, false); +} - test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id); - test_rpc_storage_list_run("error_path", ++command_id); +MU_TEST(test_storage_list_md5) { + test_rpc_storage_list_run("/", ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true); + test_rpc_storage_list_run("error_path", ++command_id, true); } static void @@ -770,7 +804,8 @@ static void test_storage_read_run(const char* path, uint32_t command_id) { MsgList_init(expected_msg_list); test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id); - test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_read_request_tag, path, command_id, false); test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -824,7 +859,8 @@ static void test_rpc_storage_info_run(const char* path, uint32_t command_id) { MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_info_request_tag, path, command_id, false); PB_Main* response = MsgList_push_new(expected_msg_list); response->command_id = command_id; @@ -856,7 +892,8 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) { MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_stat_request_tag, path, command_id, false); Storage* fs_api = furi_record_open(RECORD_STORAGE); FileInfo fileinfo; @@ -968,7 +1005,11 @@ static void test_storage_write_read_run( test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id); test_rpc_create_simple_message( - MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id); + MsgList_push_raw(input_msg_list), + PB_Main_storage_read_request_tag, + path, + ++*command_id, + false); test_rpc_add_read_or_write_to_list( expected_msg_list, READ_RESPONSE, @@ -1041,7 +1082,8 @@ MU_TEST(test_storage_interrupt_continuous_same_system) { MsgList_push_new(input_msg_list), PB_Main_storage_mkdir_request_tag, TEST_DIR "dir1", - command_id + 1); + command_id + 1, + false); test_rpc_add_read_or_write_to_list( input_msg_list, WRITE_REQUEST, @@ -1121,7 +1163,8 @@ static void test_storage_delete_run( MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_delete_request_tag, path, command_id, false); request.content.storage_delete_request.recursive = recursive; test_rpc_add_empty_to_list(expected_msg_list, status, command_id); @@ -1202,7 +1245,8 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_mkdir_request_tag, path, command_id, false); test_rpc_add_empty_to_list(expected_msg_list, status, command_id); test_rpc_encode_and_feed_one(&request, 0); @@ -1229,33 +1273,15 @@ MU_TEST(test_storage_mkdir) { static void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) { Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); - if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t once_read_size = 512; - const uint8_t hash_size = MD5SUM_SIZE; - uint8_t* data = malloc(once_read_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, once_read_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } - - free(hash); - free(data); + if(md5_string_calc_file(file, path, md5, NULL)) { + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); } else { furi_check(0); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); @@ -1271,11 +1297,12 @@ static void test_storage_md5sum_run( MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_md5sum_request_tag, path, command_id, false); if(status == PB_CommandStatus_OK) { PB_Main* response = MsgList_push_new(expected_msg_list); test_rpc_create_simple_message( - response, PB_Main_storage_md5sum_response_tag, md5sum, command_id); + response, PB_Main_storage_md5sum_response_tag, md5sum, command_id, false); response->command_status = status; } else { test_rpc_add_empty_to_list(expected_msg_list, status, command_id); @@ -1433,6 +1460,7 @@ MU_TEST_SUITE(test_rpc_storage) { MU_RUN_TEST(test_storage_info); MU_RUN_TEST(test_storage_stat); MU_RUN_TEST(test_storage_list); + MU_RUN_TEST(test_storage_list_md5); MU_RUN_TEST(test_storage_read); MU_RUN_TEST(test_storage_write_read); MU_RUN_TEST(test_storage_write); @@ -1731,7 +1759,8 @@ MU_TEST(test_rpc_multisession_storage) { MsgList_push_raw(input_0), PB_Main_storage_read_request_tag, TEST_DIR "file0.txt", - ++command_id); + ++command_id, + false); test_rpc_add_read_or_write_to_list( expected_0, READ_RESPONSE, TEST_DIR "file0.txt", pattern, sizeof(pattern), 1, command_id); @@ -1739,7 +1768,8 @@ MU_TEST(test_rpc_multisession_storage) { MsgList_push_raw(input_1), PB_Main_storage_read_request_tag, TEST_DIR "file1.txt", - ++command_id); + ++command_id, + false); test_rpc_add_read_or_write_to_list( expected_1, READ_RESPONSE, TEST_DIR "file1.txt", pattern, sizeof(pattern), 1, command_id); diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c index f0b45c598..13188e5e0 100644 --- a/applications/debug/unit_tests/storage/storage_test.c +++ b/applications/debug/unit_tests/storage/storage_test.c @@ -582,6 +582,49 @@ MU_TEST(test_storage_common_migrate) { furi_record_close(RECORD_STORAGE); } +#define MD5_HASH_SIZE (16) +#include + +MU_TEST(test_md5_calc) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + const char* path = UNIT_TESTS_PATH("storage/md5.txt"); + const char* md5_cstr = "2a456fa43e75088fdde41c93159d62a2"; + const uint8_t md5[MD5_HASH_SIZE] = { + 0x2a, + 0x45, + 0x6f, + 0xa4, + 0x3e, + 0x75, + 0x08, + 0x8f, + 0xdd, + 0xe4, + 0x1c, + 0x93, + 0x15, + 0x9d, + 0x62, + 0xa2, + }; + + uint8_t md5_output[MD5_HASH_SIZE]; + FuriString* md5_output_str = furi_string_alloc(); + memset(md5_output, 0, MD5_HASH_SIZE); + + mu_check(md5_calc_file(file, path, md5_output, NULL)); + mu_check(md5_string_calc_file(file, path, md5_output_str, NULL)); + + mu_assert_mem_eq(md5, md5_output, MD5_HASH_SIZE); + mu_assert_string_eq(md5_cstr, furi_string_get_cstr(md5_output_str)); + + storage_file_free(file); + furi_string_free(md5_output_str); + furi_record_close(RECORD_STORAGE); +} + MU_TEST_SUITE(test_data_path) { MU_RUN_TEST(test_storage_data_path); MU_RUN_TEST(test_storage_data_path_apps); @@ -591,11 +634,16 @@ MU_TEST_SUITE(test_storage_common) { MU_RUN_TEST(test_storage_common_migrate); } +MU_TEST_SUITE(test_md5_calc_suite) { + MU_RUN_TEST(test_md5_calc); +} + int run_minunit_test_storage() { MU_RUN_SUITE(storage_file); MU_RUN_SUITE(storage_dir); MU_RUN_SUITE(storage_rename); MU_RUN_SUITE(test_data_path); MU_RUN_SUITE(test_storage_common); + MU_RUN_SUITE(test_md5_calc_suite); return MU_EXIT_CODE; } diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 6bdaa641e..e32a57482 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -330,7 +330,12 @@ bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) { return false; } + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(30000000); + while(!furi_hal_subghz_is_async_tx_complete()) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } furi_delay_ms(10); } furi_hal_subghz_stop_async_tx(); diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index 9d7631bfe..edaf950c5 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -10,6 +10,7 @@ int run_minunit_test_furi(); int run_minunit_test_furi_hal(); +int run_minunit_test_furi_hal_crypto(); int run_minunit_test_furi_string(); int run_minunit_test_infrared(); int run_minunit_test_rpc(); @@ -39,6 +40,7 @@ typedef struct { const UnitTest unit_tests[] = { {.name = "furi", .entry = run_minunit_test_furi}, {.name = "furi_hal", .entry = run_minunit_test_furi_hal}, + {.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto}, {.name = "furi_string", .entry = run_minunit_test_furi_string}, {.name = "storage", .entry = run_minunit_test_storage}, {.name = "stream", .entry = run_minunit_test_stream}, @@ -88,7 +90,7 @@ void unit_tests_cli(Cli* cli, FuriString* args, void* context) { Loader* loader = furi_record_open(RECORD_LOADER); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - // TODO: lock device while test running + // TODO FL-3491: lock device while test running if(loader_is_locked(loader)) { printf("RPC: stop all applications to run tests\r\n"); notification_message(notification, &sequence_blink_magenta_100); diff --git a/applications/debug/usb_mouse/application.fam b/applications/debug/usb_mouse/application.fam index 55ba7d869..55cec85cc 100644 --- a/applications/debug/usb_mouse/application.fam +++ b/applications/debug/usb_mouse/application.fam @@ -7,6 +7,5 @@ App( requires=["gui"], stack_size=1 * 1024, order=60, - 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 deleted file mode 100644 index 94c3a7a14..000000000 Binary files a/applications/debug/usb_mouse/mouse_10px.png and /dev/null differ diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c index dc9588ed2..8683e7190 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.c +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -18,7 +19,10 @@ #define TAG "SubGhz_Device_CC1101_Ext" #define SUBGHZ_DEVICE_CC1101_EXT_TX_GPIO &gpio_ext_pb2 -#define SUBGHZ_DEVICE_CC1101_EXT_EXTENDED_RANGE false +#define SUBGHZ_DEVICE_CC1101_EXT_E07M20S_AMP_GPIO &gpio_ext_pc3 +#define SUBGHZ_DEVICE_CC1101_EXT_FORCE_EXTENDED_RANGE false + +#define SUBGHZ_DEVICE_CC1101_CONFIG_VER 1 /* DMA Channels definition */ #define SUBGHZ_DEVICE_CC1101_EXT_DMA DMA2 @@ -78,6 +82,8 @@ typedef struct { const GpioPin* g0_pin; SubGhzDeviceCC1101ExtAsyncTx async_tx; SubGhzDeviceCC1101ExtAsyncRx async_rx; + bool power_amp; + bool extended_range; } SubGhzDeviceCC1101Ext; static SubGhzDeviceCC1101Ext* subghz_device_cc1101_ext = NULL; @@ -187,25 +193,64 @@ static bool subghz_device_cc1101_ext_check_init() { return ret; } -bool subghz_device_cc1101_ext_alloc() { +bool subghz_device_cc1101_ext_alloc(SubGhzDeviceConf* conf) { 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->power_amp = false; + subghz_device_cc1101_ext->extended_range = false; + if(conf) { + if(conf->ver == SUBGHZ_DEVICE_CC1101_CONFIG_VER) { + subghz_device_cc1101_ext->power_amp = conf->power_amp; + subghz_device_cc1101_ext->extended_range = conf->extended_range; + } else { + FURI_LOG_E(TAG, "Config version mismatch"); + } + } subghz_device_cc1101_ext->async_rx.capture_delta_duration = 0; + subghz_device_cc1101_ext->spi_bus_handle = + (XTREME_SETTINGS()->spi_cc1101_handle == SpiDefault ? + &furi_hal_spi_bus_handle_external : + &furi_hal_spi_bus_handle_external_extra); + + // this is needed if multiple SPI devices are connected to the same bus but with different CS pins + if(XTREME_SETTINGS()->spi_cc1101_handle == SpiDefault && + !furi_hal_subghz_get_ext_power_amp()) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pc3, true); + } else if(XTREME_SETTINGS()->spi_cc1101_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pa4, true); + } + furi_hal_spi_bus_handle_init(subghz_device_cc1101_ext->spi_bus_handle); + if(subghz_device_cc1101_ext->power_amp) { + furi_hal_gpio_init_simple( + SUBGHZ_DEVICE_CC1101_EXT_E07M20S_AMP_GPIO, GpioModeOutputPushPull); + furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07M20S_AMP_GPIO, 0); + } + 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); + + // resetting the CS pins to floating + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault || subghz_device_cc1101_ext->power_amp) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog); + } + subghz_device_cc1101_ext = NULL; } @@ -221,7 +266,7 @@ bool subghz_device_cc1101_ext_is_connect() { bool ret = false; if(subghz_device_cc1101_ext == NULL) { // not initialized - ret = subghz_device_cc1101_ext_alloc(); + ret = subghz_device_cc1101_ext_alloc(NULL); subghz_device_cc1101_ext_free(); } else { // initialized furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle); @@ -333,7 +378,7 @@ bool subghz_device_cc1101_ext_rx_pipe_not_empty() { (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 + // TODO: Find reason why RXFIFO_OVERFLOW doesnt work correctly if(status->NUM_RXBYTES > 0) { return true; } else { @@ -381,12 +426,18 @@ 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); + if(subghz_device_cc1101_ext->power_amp) { + furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07M20S_AMP_GPIO, 0); + } } 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); + if(subghz_device_cc1101_ext->power_amp) { + furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07M20S_AMP_GPIO, 0); + } } bool subghz_device_cc1101_ext_tx() { @@ -394,6 +445,9 @@ bool subghz_device_cc1101_ext_tx() { 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); + if(subghz_device_cc1101_ext->power_amp) { + furi_hal_gpio_write(SUBGHZ_DEVICE_CC1101_EXT_E07M20S_AMP_GPIO, 1); + } return true; } @@ -432,14 +486,16 @@ bool subghz_device_cc1101_ext_is_frequency_valid(uint32_t value) { } bool subghz_device_cc1101_ext_is_tx_allowed(uint32_t value) { - if(!(SUBGHZ_DEVICE_CC1101_EXT_EXTENDED_RANGE) && + if(!(SUBGHZ_DEVICE_CC1101_EXT_FORCE_EXTENDED_RANGE || + subghz_device_cc1101_ext->extended_range) && !(value >= 299999755 && value <= 350000335) && // was increased from 348 to 350 !(value >= 386999938 && value <= 467750000) && // was increased from 464 to 467.75 !(value >= 778999847 && value <= 928000000)) { FURI_LOG_I(TAG, "Frequency blocked - outside default range"); return false; } else if( - (SUBGHZ_DEVICE_CC1101_EXT_EXTENDED_RANGE) && + (SUBGHZ_DEVICE_CC1101_EXT_FORCE_EXTENDED_RANGE || + subghz_device_cc1101_ext->extended_range) && !subghz_device_cc1101_ext_is_frequency_valid(value)) { FURI_LOG_I(TAG, "Frequency blocked - outside extended range"); return false; @@ -529,7 +585,7 @@ void subghz_device_cc1101_ext_start_async_rx( furi_hal_bus_enable(FuriHalBusTIM17); // Configure TIM - //Set the timer resolution to 2 µs + //Set the timer resolution to 2 us LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1); LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); LL_TIM_SetAutoReload(TIM17, 0xFFFF); @@ -710,7 +766,7 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb furi_hal_bus_enable(FuriHalBusTIM17); // Configure TIM - // Set the timer resolution to 2 µs + // Set the timer resolution to 2 us LL_TIM_SetPrescaler(TIM17, (64 << 1) - 1); LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); LL_TIM_SetAutoReload(TIM17, 0xFFFF); diff --git a/applications/drivers/subghz/cc1101_ext/cc1101_ext.h b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h index d972fcb66..388126726 100644 --- a/applications/drivers/subghz/cc1101_ext/cc1101_ext.h +++ b/applications/drivers/subghz/cc1101_ext/cc1101_ext.h @@ -5,11 +5,13 @@ #pragma once #include +#include #include #include #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -34,7 +36,7 @@ const GpioPin* subghz_device_cc1101_ext_get_data_gpio(); * * @return true if success */ -bool subghz_device_cc1101_ext_alloc(); +bool subghz_device_cc1101_ext_alloc(SubGhzDeviceConf* conf); /** Deinitialize device */ diff --git a/applications/external/4inrow/4inrow.c b/applications/external/4inrow/4inrow.c index 1f112a81a..da2d5a4cb 100644 --- a/applications/external/4inrow/4inrow.c +++ b/applications/external/4inrow/4inrow.c @@ -235,7 +235,7 @@ int32_t four_in_row_app(void* p) { return 255; } - // dolphin_deed(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); // Создаем новый view port ViewPort* view_port = view_port_alloc(); diff --git a/applications/external/4inrow/application.fam b/applications/external/4inrow/application.fam index 9d0db09ca..67a133992 100644 --- a/applications/external/4inrow/application.fam +++ b/applications/external/4inrow/application.fam @@ -7,7 +7,6 @@ App( "gui", ], stack_size=1 * 1024, - order=90, fap_icon="4inrow_10px.png", fap_category="Games", fap_author="leo-need-more-coffee", diff --git a/applications/external/advanced_wifisniff/application.fam b/applications/external/advanced_wifisniff/application.fam new file mode 100644 index 000000000..ba5e251e1 --- /dev/null +++ b/applications/external/advanced_wifisniff/application.fam @@ -0,0 +1,13 @@ +# For details & more options, see documentation/AppManifests.md in firmware repo + +App( + appid="wifisniffer", # Must be unique + name="[ESP32 GPS] Advanced Wifi Sniffer", # Displayed in menus + apptype=FlipperAppType.EXTERNAL, + entry_point="wifisniffer_app", + stack_size=2 * 1024, + fap_category="WiFi", + fap_icon="sniff.png", # 10x10 1-bit PNG + fap_icon_assets="assets", + fap_icon_assets_symbol="wifisniffer", +) diff --git a/applications/external/advanced_wifisniff/assets/down.png b/applications/external/advanced_wifisniff/assets/down.png new file mode 100644 index 000000000..d6cb77e69 Binary files /dev/null and b/applications/external/advanced_wifisniff/assets/down.png differ diff --git a/applications/external/advanced_wifisniff/assets/up.png b/applications/external/advanced_wifisniff/assets/up.png new file mode 100644 index 000000000..1b7d46904 Binary files /dev/null and b/applications/external/advanced_wifisniff/assets/up.png differ diff --git a/applications/external/advanced_wifisniff/helpers/minmea.c b/applications/external/advanced_wifisniff/helpers/minmea.c new file mode 100644 index 000000000..1b7a84b1c --- /dev/null +++ b/applications/external/advanced_wifisniff/helpers/minmea.c @@ -0,0 +1,640 @@ +/* + * Copyright © 2014 Kosma Moczek + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#include "minmea.h" + +#include +#include +#include + +#define boolstr(s) ((s) ? "true" : "false") + +static int hex2int(char c) { + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'A' && c <= 'F') return c - 'A' + 10; + if(c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; +} + +uint8_t minmea_checksum(const char* sentence) { + // Support senteces with or without the starting dollar sign. + if(*sentence == '$') sentence++; + + uint8_t checksum = 0x00; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while(*sentence && *sentence != '*') checksum ^= *sentence++; + + return checksum; +} + +bool minmea_check(const char* sentence, bool strict) { + uint8_t checksum = 0x00; + + // A valid sentence starts with "$". + if(*sentence++ != '$') return false; + + // The optional checksum is an XOR of all bytes between "$" and "*". + while(*sentence && *sentence != '*' && isprint((unsigned char)*sentence)) + checksum ^= *sentence++; + + // If checksum is present... + if(*sentence == '*') { + // Extract checksum. + sentence++; + int upper = hex2int(*sentence++); + if(upper == -1) return false; + int lower = hex2int(*sentence++); + if(lower == -1) return false; + int expected = upper << 4 | lower; + + // Check for checksum mismatch. + if(checksum != expected) return false; + } else if(strict) { + // Discard non-checksummed frames in strict mode. + return false; + } + + // The only stuff allowed at this point is a newline. + while(*sentence == '\r' || *sentence == '\n') { + sentence++; + } + + if(*sentence) { + return false; + } + + return true; +} + +bool minmea_scan(const char* sentence, const char* format, ...) { + bool result = false; + bool optional = false; + + if(sentence == NULL) return false; + + va_list ap; + va_start(ap, format); + + const char* field = sentence; +#define next_field() \ + do { \ + /* Progress to the next field. */ \ + while(minmea_isfield(*sentence)) sentence++; \ + /* Make sure there is a field there. */ \ + if(*sentence == ',') { \ + sentence++; \ + field = sentence; \ + } else { \ + field = NULL; \ + } \ + } while(0) + + while(*format) { + char type = *format++; + + if(type == ';') { + // All further fields are optional. + optional = true; + continue; + } + + if(!field && !optional) { + // Field requested but we ran out if input. Bail out. + goto parse_error; + } + + switch(type) { + case 'c': { // Single character field (char). + char value = '\0'; + + if(field && minmea_isfield(*field)) value = *field; + + *va_arg(ap, char*) = value; + } break; + + case 'd': { // Single character direction field (int). + int value = 0; + + if(field && minmea_isfield(*field)) { + switch(*field) { + case 'N': + case 'E': + value = 1; + break; + case 'S': + case 'W': + value = -1; + break; + default: + goto parse_error; + } + } + + *va_arg(ap, int*) = value; + } break; + + case 'f': { // Fractional value with scale (struct minmea_float). + int sign = 0; + int_least32_t value = -1; + int_least32_t scale = 0; + + if(field) { + while(minmea_isfield(*field)) { + if(*field == '+' && !sign && value == -1) { + sign = 1; + } else if(*field == '-' && !sign && value == -1) { + sign = -1; + } else if(isdigit((unsigned char)*field)) { + int digit = *field - '0'; + if(value == -1) value = 0; + if(value > (INT_LEAST32_MAX - digit) / 10) { + /* we ran out of bits, what do we do? */ + if(scale) { + /* truncate extra precision */ + break; + } else { + /* integer overflow. bail out. */ + goto parse_error; + } + } + value = (10 * value) + digit; + if(scale) scale *= 10; + } else if(*field == '.' && scale == 0) { + scale = 1; + } else if(*field == ' ') { + /* Allow spaces at the start of the field. Not NMEA + * conformant, but some modules do this. */ + if(sign != 0 || value != -1 || scale != 0) goto parse_error; + } else { + goto parse_error; + } + field++; + } + } + + if((sign || scale) && value == -1) goto parse_error; + + if(value == -1) { + /* No digits were scanned. */ + value = 0; + scale = 0; + } else if(scale == 0) { + /* No decimal point. */ + scale = 1; + } + if(sign) value *= sign; + + *va_arg(ap, struct minmea_float*) = (struct minmea_float){value, scale}; + } break; + + case 'i': { // Integer value, default 0 (int). + int value = 0; + + if(field) { + char* endptr; + value = strtol(field, &endptr, 10); + if(minmea_isfield(*endptr)) goto parse_error; + } + + *va_arg(ap, int*) = value; + } break; + + case 's': { // String value (char *). + char* buf = va_arg(ap, char*); + + if(field) { + while(minmea_isfield(*field)) *buf++ = *field++; + } + + *buf = '\0'; + } break; + + case 't': { // NMEA talker+sentence identifier (char *). + // This field is always mandatory. + if(!field) goto parse_error; + + if(field[0] != '$') goto parse_error; + for(int f = 0; f < 5; f++) + if(!minmea_isfield(field[1 + f])) goto parse_error; + + char* buf = va_arg(ap, char*); + memcpy(buf, field + 1, 5); + buf[5] = '\0'; + } break; + + case 'D': { // Date (int, int, int), -1 if empty. + struct minmea_date* date = va_arg(ap, struct minmea_date*); + + int d = -1, m = -1, y = -1; + + if(field && minmea_isfield(*field)) { + // Always six digits. + for(int f = 0; f < 6; f++) + if(!isdigit((unsigned char)field[f])) goto parse_error; + + char dArr[] = {field[0], field[1], '\0'}; + char mArr[] = {field[2], field[3], '\0'}; + char yArr[] = {field[4], field[5], '\0'}; + d = strtol(dArr, NULL, 10); + m = strtol(mArr, NULL, 10); + y = strtol(yArr, NULL, 10); + } + + date->day = d; + date->month = m; + date->year = y; + } break; + + case 'T': { // Time (int, int, int, int), -1 if empty. + struct minmea_time* time_ = va_arg(ap, struct minmea_time*); + + int h = -1, i = -1, s = -1, u = -1; + + if(field && minmea_isfield(*field)) { + // Minimum required: integer time. + for(int f = 0; f < 6; f++) + if(!isdigit((unsigned char)field[f])) goto parse_error; + + char hArr[] = {field[0], field[1], '\0'}; + char iArr[] = {field[2], field[3], '\0'}; + char sArr[] = {field[4], field[5], '\0'}; + h = strtol(hArr, NULL, 10); + i = strtol(iArr, NULL, 10); + s = strtol(sArr, NULL, 10); + field += 6; + + // Extra: fractional time. Saved as microseconds. + if(*field++ == '.') { + uint32_t value = 0; + uint32_t scale = 1000000LU; + while(isdigit((unsigned char)*field) && scale > 1) { + value = (value * 10) + (*field++ - '0'); + scale /= 10; + } + u = value * scale; + } else { + u = 0; + } + } + + time_->hours = h; + time_->minutes = i; + time_->seconds = s; + time_->microseconds = u; + } break; + + case '_': { // Ignore the field. + } break; + + default: { // Unknown. + goto parse_error; + } + } + + next_field(); + } + + result = true; + +parse_error: + va_end(ap); + return result; +} + +bool minmea_talker_id(char talker[3], const char* sentence) { + char type[6]; + if(!minmea_scan(sentence, "t", type)) return false; + + talker[0] = type[0]; + talker[1] = type[1]; + talker[2] = '\0'; + + return true; +} + +enum minmea_sentence_id minmea_sentence_id(const char* sentence, bool strict) { + if(!minmea_check(sentence, strict)) return MINMEA_INVALID; + + char type[6]; + if(!minmea_scan(sentence, "t", type)) return MINMEA_INVALID; + + if(!strcmp(type + 2, "GBS")) return MINMEA_SENTENCE_GBS; + if(!strcmp(type + 2, "GGA")) return MINMEA_SENTENCE_GGA; + if(!strcmp(type + 2, "GLL")) return MINMEA_SENTENCE_GLL; + if(!strcmp(type + 2, "GSA")) return MINMEA_SENTENCE_GSA; + if(!strcmp(type + 2, "GST")) return MINMEA_SENTENCE_GST; + if(!strcmp(type + 2, "GSV")) return MINMEA_SENTENCE_GSV; + if(!strcmp(type + 2, "RMC")) return MINMEA_SENTENCE_RMC; + if(!strcmp(type + 2, "VTG")) return MINMEA_SENTENCE_VTG; + if(!strcmp(type + 2, "ZDA")) return MINMEA_SENTENCE_ZDA; + + return MINMEA_UNKNOWN; +} + +bool minmea_parse_gbs(struct minmea_sentence_gbs* frame, const char* sentence) { + // $GNGBS,170556.00,3.0,2.9,8.3,,,,*5C + char type[6]; + if(!minmea_scan( + sentence, + "tTfffifff", + type, + &frame->time, + &frame->err_latitude, + &frame->err_longitude, + &frame->err_altitude, + &frame->svid, + &frame->prob, + &frame->bias, + &frame->stddev)) + return false; + if(strcmp(type + 2, "GBS")) return false; + + return true; +} + +bool minmea_parse_rmc(struct minmea_sentence_rmc* frame, const char* sentence) { + // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 + char type[6]; + char validity; + int latitude_direction; + int longitude_direction; + int variation_direction; + if(!minmea_scan( + sentence, + "tTcfdfdffDfd", + type, + &frame->time, + &validity, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->speed, + &frame->course, + &frame->date, + &frame->variation, + &variation_direction)) + return false; + if(strcmp(type + 2, "RMC")) return false; + + frame->valid = (validity == 'A'); + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + frame->variation.value *= variation_direction; + + return true; +} + +bool minmea_parse_gga(struct minmea_sentence_gga* frame, const char* sentence) { + // $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47 + char type[6]; + int latitude_direction; + int longitude_direction; + + if(!minmea_scan( + sentence, + "tTfdfdiiffcfcf_", + type, + &frame->time, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->fix_quality, + &frame->satellites_tracked, + &frame->hdop, + &frame->altitude, + &frame->altitude_units, + &frame->height, + &frame->height_units, + &frame->dgps_age)) + return false; + if(strcmp(type + 2, "GGA")) return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gsa(struct minmea_sentence_gsa* frame, const char* sentence) { + // $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39 + char type[6]; + + if(!minmea_scan( + sentence, + "tciiiiiiiiiiiiifff", + type, + &frame->mode, + &frame->fix_type, + &frame->sats[0], + &frame->sats[1], + &frame->sats[2], + &frame->sats[3], + &frame->sats[4], + &frame->sats[5], + &frame->sats[6], + &frame->sats[7], + &frame->sats[8], + &frame->sats[9], + &frame->sats[10], + &frame->sats[11], + &frame->pdop, + &frame->hdop, + &frame->vdop)) + return false; + if(strcmp(type + 2, "GSA")) return false; + + return true; +} + +bool minmea_parse_gll(struct minmea_sentence_gll* frame, const char* sentence) { + // $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41$; + char type[6]; + int latitude_direction; + int longitude_direction; + + if(!minmea_scan( + sentence, + "tfdfdTc;c", + type, + &frame->latitude, + &latitude_direction, + &frame->longitude, + &longitude_direction, + &frame->time, + &frame->status, + &frame->mode)) + return false; + if(strcmp(type + 2, "GLL")) return false; + + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + + return true; +} + +bool minmea_parse_gst(struct minmea_sentence_gst* frame, const char* sentence) { + // $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58 + char type[6]; + + if(!minmea_scan( + sentence, + "tTfffffff", + type, + &frame->time, + &frame->rms_deviation, + &frame->semi_major_deviation, + &frame->semi_minor_deviation, + &frame->semi_major_orientation, + &frame->latitude_error_deviation, + &frame->longitude_error_deviation, + &frame->altitude_error_deviation)) + return false; + if(strcmp(type + 2, "GST")) return false; + + return true; +} + +bool minmea_parse_gsv(struct minmea_sentence_gsv* frame, const char* sentence) { + // $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 + // $GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D + // $GPGSV,4,2,11,08,51,203,30,09,45,215,28*75 + // $GPGSV,4,4,13,39,31,170,27*40 + // $GPGSV,4,4,13*7B + char type[6]; + + if(!minmea_scan( + sentence, + "tiii;iiiiiiiiiiiiiiii", + type, + &frame->total_msgs, + &frame->msg_nr, + &frame->total_sats, + &frame->sats[0].nr, + &frame->sats[0].elevation, + &frame->sats[0].azimuth, + &frame->sats[0].snr, + &frame->sats[1].nr, + &frame->sats[1].elevation, + &frame->sats[1].azimuth, + &frame->sats[1].snr, + &frame->sats[2].nr, + &frame->sats[2].elevation, + &frame->sats[2].azimuth, + &frame->sats[2].snr, + &frame->sats[3].nr, + &frame->sats[3].elevation, + &frame->sats[3].azimuth, + &frame->sats[3].snr)) { + return false; + } + if(strcmp(type + 2, "GSV")) return false; + + return true; +} + +bool minmea_parse_vtg(struct minmea_sentence_vtg* frame, const char* sentence) { + // $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48 + // $GPVTG,156.1,T,140.9,M,0.0,N,0.0,K*41 + // $GPVTG,096.5,T,083.5,M,0.0,N,0.0,K,D*22 + // $GPVTG,188.36,T,,M,0.820,N,1.519,K,A*3F + char type[6]; + char c_true, c_magnetic, c_knots, c_kph, c_faa_mode; + + if(!minmea_scan( + sentence, + "t;fcfcfcfcc", + type, + &frame->true_track_degrees, + &c_true, + &frame->magnetic_track_degrees, + &c_magnetic, + &frame->speed_knots, + &c_knots, + &frame->speed_kph, + &c_kph, + &c_faa_mode)) + return false; + if(strcmp(type + 2, "VTG")) return false; + // values are only valid with the accompanying characters + if(c_true != 'T') frame->true_track_degrees.scale = 0; + if(c_magnetic != 'M') frame->magnetic_track_degrees.scale = 0; + if(c_knots != 'N') frame->speed_knots.scale = 0; + if(c_kph != 'K') frame->speed_kph.scale = 0; + frame->faa_mode = (enum minmea_faa_mode)c_faa_mode; + + return true; +} + +bool minmea_parse_zda(struct minmea_sentence_zda* frame, const char* sentence) { + // $GPZDA,201530.00,04,07,2002,00,00*60 + char type[6]; + + if(!minmea_scan( + sentence, + "tTiiiii", + type, + &frame->time, + &frame->date.day, + &frame->date.month, + &frame->date.year, + &frame->hour_offset, + &frame->minute_offset)) + return false; + if(strcmp(type + 2, "ZDA")) return false; + + // check offsets + if(abs(frame->hour_offset) > 13 || frame->minute_offset > 59 || frame->minute_offset < 0) + return false; + + return true; +} + +int minmea_getdatetime( + struct tm* tm, + const struct minmea_date* date, + const struct minmea_time* time_) { + if(date->year == -1 || time_->hours == -1) return -1; + + memset(tm, 0, sizeof(*tm)); + if(date->year < 80) { + tm->tm_year = 2000 + date->year - 1900; // 2000-2079 + } else if(date->year >= 1900) { + tm->tm_year = date->year - 1900; // 4 digit year, use directly + } else { + tm->tm_year = date->year; // 1980-1999 + } + tm->tm_mon = date->month - 1; + tm->tm_mday = date->day; + tm->tm_hour = time_->hours; + tm->tm_min = time_->minutes; + tm->tm_sec = time_->seconds; + + return 0; +} + +int minmea_gettime( + struct timespec* ts, + const struct minmea_date* date, + const struct minmea_time* time_) { + struct tm tm; + if(minmea_getdatetime(&tm, date, time_)) return -1; + + time_t timestamp = mktime(&tm); /* See README.md if your system lacks timegm(). */ + if(timestamp != (time_t)-1) { + ts->tv_sec = timestamp; + ts->tv_nsec = time_->microseconds * 1000; + return 0; + } else { + return -1; + } +} + +/* vim: set ts=4 sw=4 et: */ diff --git a/applications/external/advanced_wifisniff/helpers/minmea.h b/applications/external/advanced_wifisniff/helpers/minmea.h new file mode 100644 index 000000000..88eec4ae9 --- /dev/null +++ b/applications/external/advanced_wifisniff/helpers/minmea.h @@ -0,0 +1,295 @@ +/* + * Copyright © 2014 Kosma Moczek + * This program is free software. It comes without any warranty, to the extent + * permitted by applicable law. You can redistribute it and/or modify it under + * the terms of the Do What The Fuck You Want To Public License, Version 2, as + * published by Sam Hocevar. See the COPYING file for more details. + */ + +#ifndef MINMEA_H +#define MINMEA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#ifdef MINMEA_INCLUDE_COMPAT +#include +#endif + +#ifndef MINMEA_MAX_SENTENCE_LENGTH +#define MINMEA_MAX_SENTENCE_LENGTH 80 +#endif + +enum minmea_sentence_id { + MINMEA_INVALID = -1, + MINMEA_UNKNOWN = 0, + MINMEA_SENTENCE_GBS, + MINMEA_SENTENCE_GGA, + MINMEA_SENTENCE_GLL, + MINMEA_SENTENCE_GSA, + MINMEA_SENTENCE_GST, + MINMEA_SENTENCE_GSV, + MINMEA_SENTENCE_RMC, + MINMEA_SENTENCE_VTG, + MINMEA_SENTENCE_ZDA, +}; + +struct minmea_float { + int_least32_t value; + int_least32_t scale; +}; + +struct minmea_date { + int day; + int month; + int year; +}; + +struct minmea_time { + int hours; + int minutes; + int seconds; + int microseconds; +}; + +struct minmea_sentence_gbs { + struct minmea_time time; + struct minmea_float err_latitude; + struct minmea_float err_longitude; + struct minmea_float err_altitude; + int svid; + struct minmea_float prob; + struct minmea_float bias; + struct minmea_float stddev; +}; + +struct minmea_sentence_rmc { + struct minmea_time time; + bool valid; + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_float speed; + struct minmea_float course; + struct minmea_date date; + struct minmea_float variation; +}; + +struct minmea_sentence_gga { + struct minmea_time time; + struct minmea_float latitude; + struct minmea_float longitude; + int fix_quality; + int satellites_tracked; + struct minmea_float hdop; + struct minmea_float altitude; + char altitude_units; + struct minmea_float height; + char height_units; + struct minmea_float dgps_age; +}; + +enum minmea_gll_status { + MINMEA_GLL_STATUS_DATA_VALID = 'A', + MINMEA_GLL_STATUS_DATA_NOT_VALID = 'V', +}; + +// FAA mode added to some fields in NMEA 2.3. +enum minmea_faa_mode { + MINMEA_FAA_MODE_AUTONOMOUS = 'A', + MINMEA_FAA_MODE_DIFFERENTIAL = 'D', + MINMEA_FAA_MODE_ESTIMATED = 'E', + MINMEA_FAA_MODE_MANUAL = 'M', + MINMEA_FAA_MODE_SIMULATED = 'S', + MINMEA_FAA_MODE_NOT_VALID = 'N', + MINMEA_FAA_MODE_PRECISE = 'P', +}; + +struct minmea_sentence_gll { + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_time time; + char status; + char mode; +}; + +struct minmea_sentence_gst { + struct minmea_time time; + struct minmea_float rms_deviation; + struct minmea_float semi_major_deviation; + struct minmea_float semi_minor_deviation; + struct minmea_float semi_major_orientation; + struct minmea_float latitude_error_deviation; + struct minmea_float longitude_error_deviation; + struct minmea_float altitude_error_deviation; +}; + +enum minmea_gsa_mode { + MINMEA_GPGSA_MODE_AUTO = 'A', + MINMEA_GPGSA_MODE_FORCED = 'M', +}; + +enum minmea_gsa_fix_type { + MINMEA_GPGSA_FIX_NONE = 1, + MINMEA_GPGSA_FIX_2D = 2, + MINMEA_GPGSA_FIX_3D = 3, +}; + +struct minmea_sentence_gsa { + char mode; + int fix_type; + int sats[12]; + struct minmea_float pdop; + struct minmea_float hdop; + struct minmea_float vdop; +}; + +struct minmea_sat_info { + int nr; + int elevation; + int azimuth; + int snr; +}; + +struct minmea_sentence_gsv { + int total_msgs; + int msg_nr; + int total_sats; + struct minmea_sat_info sats[4]; +}; + +struct minmea_sentence_vtg { + struct minmea_float true_track_degrees; + struct minmea_float magnetic_track_degrees; + struct minmea_float speed_knots; + struct minmea_float speed_kph; + enum minmea_faa_mode faa_mode; +}; + +struct minmea_sentence_zda { + struct minmea_time time; + struct minmea_date date; + int hour_offset; + int minute_offset; +}; + +/** + * Calculate raw sentence checksum. Does not check sentence integrity. + */ +uint8_t minmea_checksum(const char* sentence); + +/** + * Check sentence validity and checksum. Returns true for valid sentences. + */ +bool minmea_check(const char* sentence, bool strict); + +/** + * Determine talker identifier. + */ +bool minmea_talker_id(char talker[3], const char* sentence); + +/** + * Determine sentence identifier. + */ +enum minmea_sentence_id minmea_sentence_id(const char* sentence, bool strict); + +/** + * Scanf-like processor for NMEA sentences. Supports the following formats: + * c - single character (char *) + * d - direction, returned as 1/-1, default 0 (int *) + * f - fractional, returned as value + scale (struct minmea_float *) + * i - decimal, default zero (int *) + * s - string (char *) + * t - talker identifier and type (char *) + * D - date (struct minmea_date *) + * T - time stamp (struct minmea_time *) + * _ - ignore this field + * ; - following fields are optional + * Returns true on success. See library source code for details. + */ +bool minmea_scan(const char* sentence, const char* format, ...); + +/* + * Parse a specific type of sentence. Return true on success. + */ +bool minmea_parse_gbs(struct minmea_sentence_gbs* frame, const char* sentence); +bool minmea_parse_rmc(struct minmea_sentence_rmc* frame, const char* sentence); +bool minmea_parse_gga(struct minmea_sentence_gga* frame, const char* sentence); +bool minmea_parse_gsa(struct minmea_sentence_gsa* frame, const char* sentence); +bool minmea_parse_gll(struct minmea_sentence_gll* frame, const char* sentence); +bool minmea_parse_gst(struct minmea_sentence_gst* frame, const char* sentence); +bool minmea_parse_gsv(struct minmea_sentence_gsv* frame, const char* sentence); +bool minmea_parse_vtg(struct minmea_sentence_vtg* frame, const char* sentence); +bool minmea_parse_zda(struct minmea_sentence_zda* frame, const char* sentence); + +/** + * Convert GPS UTC date/time representation to a UNIX calendar time. + */ +int minmea_getdatetime( + struct tm* tm, + const struct minmea_date* date, + const struct minmea_time* time_); + +/** + * Convert GPS UTC date/time representation to a UNIX timestamp. + */ +int minmea_gettime( + struct timespec* ts, + const struct minmea_date* date, + const struct minmea_time* time_); + +/** + * Rescale a fixed-point value to a different scale. Rounds towards zero. + */ +static inline int_least32_t minmea_rescale(const struct minmea_float* f, int_least32_t new_scale) { + if(f->scale == 0) return 0; + if(f->scale == new_scale) return f->value; + if(f->scale > new_scale) + return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale / new_scale / 2) / + (f->scale / new_scale); + else + return f->value * (new_scale / f->scale); +} + +/** + * Convert a fixed-point value to a floating-point value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tofloat(const struct minmea_float* f) { + if(f->scale == 0) return NAN; + return (float)f->value / (float)f->scale; +} + +/** + * Convert a raw coordinate to a floating point DD.DDD... value. + * Returns NaN for "unknown" values. + */ +static inline float minmea_tocoord(const struct minmea_float* f) { + if(f->scale == 0) return NAN; + if(f->scale > (INT_LEAST32_MAX / 100)) return NAN; + if(f->scale < (INT_LEAST32_MIN / 100)) return NAN; + int_least32_t degrees = f->value / (f->scale * 100); + int_least32_t minutes = f->value % (f->scale * 100); + return (float)degrees + (float)minutes / (60 * f->scale); +} + +/** + * Check whether a character belongs to the set of characters allowed in a + * sentence data field. + */ +static inline bool minmea_isfield(char c) { + return isprint((unsigned char)c) && c != ',' && c != '*'; +} + +#ifdef __cplusplus +} +#endif + +#endif /* MINMEA_H */ + +/* vim: set ts=4 sw=4 et: */ diff --git a/applications/external/advanced_wifisniff/sniff.png b/applications/external/advanced_wifisniff/sniff.png new file mode 100644 index 000000000..c74df7091 Binary files /dev/null and b/applications/external/advanced_wifisniff/sniff.png differ diff --git a/applications/external/advanced_wifisniff/sniffer.c b/applications/external/advanced_wifisniff/sniffer.c new file mode 100644 index 000000000..ce1a1b193 --- /dev/null +++ b/applications/external/advanced_wifisniff/sniffer.c @@ -0,0 +1,835 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "helpers/minmea.h" +#include "wifisniffer_icons.h" + +#define appname "ll-wifisniffer" + +#define RX_BUF_SIZE 2048 +#define MAX_ACCESS_POINTS 2048 // imagine getting this many access points + +#define MAX_SSID_LENGTH 32 +#define MAX_BSSID_LENGTH 18 + +#define UART_CH_ESP \ + (XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) + +#define UART_CH_GPS \ + (XTREME_SETTINGS()->uart_nmea_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) + +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), +} WorkerEvtFlags; + +typedef enum { + EventTypeKey, + EventTypeTick, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} Event; + +typedef struct { + char* recievedMac; + char* sentMac; +} Packet; + +typedef struct { + char* ssid; + char* bssid; + int8_t rssi; + uint8_t channel; + FuriHalRtcDateTime datetime; + uint16_t packetRxCount; + uint16_t packetTxCount; + float latitude; + float longitude; +} AccessPoint; + +typedef struct { + FuriMessageQueue* queue; + FuriMutex* mutex; + FuriString* buffer; + FuriString* buffer2; + NotificationApp* notifications; + FuriThread* thread_esp; + FuriStreamBuffer* rx_stream_esp; + uint8_t rx_buf_esp[2048]; + FuriThread* thread_gps; + FuriStreamBuffer* rx_stream_gps; + uint8_t rx_buf_gps[2048]; + File* file; + char* dataString; + uint16_t access_points_count; + AccessPoint access_points[MAX_ACCESS_POINTS]; + int16_t access_points_index; + AccessPoint active_access_point; + bool extra_info; + bool pressedButton; + float last_latitude; + float last_longitude; +} Context; + +static void tick_callback(void* ctx_q) { + furi_assert(ctx_q); + FuriMessageQueue* queue = ctx_q; + Event event = {.type = EventTypeTick}; + furi_message_queue_put(queue, &event, 0); +} + +static void input_callback(InputEvent* input_event, FuriMessageQueue* queue) { + furi_assert(queue); + Event event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(queue, &event, FuriWaitForever); +} + +static void show_access_point(Canvas* canvas, Context* context) { + Context* ctx = context; + + AccessPoint ap = ctx->active_access_point; + + canvas_draw_str_aligned(canvas, 62, 25, AlignCenter, AlignBottom, ap.ssid); + + canvas_set_font(canvas, FontSecondary); + + canvas_draw_str_aligned( + canvas, 38 + (ctx->access_points_count > 99 ? 5 : 0), 12, AlignLeft, AlignBottom, ap.bssid); + + furi_string_printf(ctx->buffer, "Signal strength: %ddBm", ap.rssi); + canvas_draw_str_aligned( + canvas, 3, 35, AlignLeft, AlignBottom, furi_string_get_cstr(ctx->buffer)); + + furi_string_printf(ctx->buffer, "CH: %d", ap.channel); + canvas_draw_str_aligned( + canvas, 3, 47, AlignLeft, AlignBottom, furi_string_get_cstr(ctx->buffer)); + + if(ap.latitude == 0 && ap.longitude == 0) { + canvas_draw_str_aligned(canvas, 29, 47, AlignLeft, AlignBottom, "X"); + } else { + canvas_draw_str_aligned(canvas, 29, 47, AlignLeft, AlignBottom, "O"); + } + + furi_string_printf(ctx->buffer, "%d", ap.packetRxCount); + canvas_draw_icon(canvas, 35, 39, &I_down); + canvas_draw_str_aligned( + canvas, 45, 47, AlignLeft, AlignBottom, furi_string_get_cstr(ctx->buffer)); + + furi_string_printf(ctx->buffer, "%d", ap.packetTxCount); + canvas_draw_icon(canvas, 85, 38, &I_up); + canvas_draw_str_aligned( + canvas, 95, 47, AlignLeft, AlignBottom, furi_string_get_cstr(ctx->buffer)); + + furi_string_printf( + ctx->buffer, + "Seen: %d:%d:%d (%lds ago)", + ap.datetime.hour, + ap.datetime.minute, + ap.datetime.second, + furi_hal_rtc_get_timestamp() - furi_hal_rtc_datetime_to_timestamp(&ap.datetime)); + canvas_draw_str_aligned( + canvas, 3, 59, AlignLeft, AlignBottom, furi_string_get_cstr(ctx->buffer)); +} + +static void render_callback(Canvas* canvas, void* context) { + Context* ctx = context; + + canvas_draw_frame(canvas, 0, 0, 128, 64); + + canvas_set_font(canvas, FontPrimary); + + if(ctx->access_points_count >= MAX_ACCESS_POINTS) { + canvas_draw_str(canvas, 118, 10, "!"); + } + + if(ctx->access_points_count == 0) { + canvas_draw_str(canvas, 80, 30, "No AP's"); + canvas_draw_str(canvas, 80, 40, "Found!"); + canvas_draw_icon(canvas, 1, 4, &I_DolphinWait_61x59); + } else { + canvas_draw_frame(canvas, 0, 0, 35 + (ctx->access_points_count > 99 ? 5 : 0), 15); + + furi_string_printf( + ctx->buffer, "%d/%d", ctx->access_points_index + 1, ctx->access_points_count); + + canvas_draw_str(canvas, 3, 12, furi_string_get_cstr(ctx->buffer)); + + show_access_point(canvas, ctx); + } + // canvas_clear(canvas); + furi_mutex_release(ctx->mutex); +} + +// order ctx->access_points by ssid alphabetically +static void sort_access_points(Context* ctx) { + for(int i = 0; i < ctx->access_points_count; i++) { + for(int j = i + 1; j < ctx->access_points_count; j++) { + if(strcmp(ctx->access_points[i].ssid, ctx->access_points[j].ssid) > 0) { + AccessPoint temp = ctx->access_points[i]; + ctx->access_points[i] = ctx->access_points[j]; + ctx->access_points[j] = temp; + } + } + } +} + +// set the index from the active access point +static void set_index_from_access_points(Context* ctx) { + for(int i = 0; i < ctx->access_points_count; i++) { + if(ctx->access_points[i].bssid == ctx->active_access_point.bssid) { + ctx->access_points_index = i; + break; + } + } +} + +static void removeSpaces(char* str) { + // Remove spaces from the beginning of the string + int i = 0; + while(isspace((unsigned char)str[i])) { + i++; + } + + // Move the remaining characters to the beginning of the string + int j = 0; + while(str[i] != '\0') { + str[j++] = str[i++]; + } + str[j] = '\0'; + + // Remove spaces from the end of the string + int len = strlen(str); + while(len > 0 && isspace((unsigned char)str[len - 1])) { + str[--len] = '\0'; + } +} + +static void parseLine(void* context, char* line) { + Context* ctx = context; + + AccessPoint ap = {.ssid = malloc(MAX_SSID_LENGTH + 1), .bssid = malloc(MAX_BSSID_LENGTH + 1)}; + + Packet pkt = {.recievedMac = malloc(18 + 1), .sentMac = malloc(18 + 1)}; + + char* token = strtok(line, ","); + int i = 0; + bool isAp = false; + bool isValid = true; + UNUSED(isValid); + while(token != NULL) { + switch(i) { + case 0: + if(strcmp(token, "AR") == 0) { + isAp = true; + isValid = true; + } else if(strcmp(token, "PK") == 0) { + isAp = false; + isValid = true; + } + break; + case 1: + if(isAp && isValid) { + removeSpaces(token); + strcpy(ap.ssid, token); + } else if(!isAp && isValid) { + strncpy(pkt.recievedMac, token, 18); + pkt.recievedMac[18] = '\0'; + } + break; + case 2: + if(isAp && isValid) { + strcpy(ap.bssid, token); + } else if(!isAp && isValid) { + strncpy(pkt.sentMac, token, 18); + pkt.sentMac[18] = '\0'; + } + break; + case 3: + if(isAp && isValid) { + ap.rssi = atoi(token); + } + break; + case 4: + if(isAp && isValid) { + ap.channel = atoi(token); + } + break; + } + + token = strtok(NULL, ","); + i++; + } + + if(isAp && isValid) { + // free the packet + free(pkt.recievedMac); + free(pkt.sentMac); + + // check if values are valid + // bssid needs an ":" + // rssi needs to be negative + // channel needs to be between 1 and 14 + // ssid needs to be at least 1 character long + if(ap.bssid[2] != ':' || ap.bssid[5] != ':' || ap.bssid[8] != ':' || ap.bssid[11] != ':' || + ap.bssid[14] != ':' || ap.rssi > 0 || ap.channel < 1 || ap.channel > 14 || + strlen(ap.ssid) < 1) { + free(ap.ssid); + free(ap.bssid); + return; + } + + furi_hal_light_set(LightBlue, 0); + furi_hal_light_set(LightGreen, 255); + + furi_hal_rtc_get_datetime(&ap.datetime); + + if(isnan(ctx->last_latitude) || isnan(ctx->last_longitude)) { + ctx->last_latitude = 0; + ctx->last_longitude = 0; + } else { + ap.latitude = ctx->last_latitude; + ap.longitude = ctx->last_longitude; + } + + // check if ap is already in the list otherwise add it but update the rssi + bool found = false; + for(size_t i = 0; i < ctx->access_points_count; i++) { + if(strcmp(ctx->access_points[i].bssid, ap.bssid) == 0) { + found = true; + //update rssi channel datetime + ctx->access_points[i].rssi = ap.rssi; + ctx->access_points[i].channel = ap.channel; + ctx->access_points[i].datetime = ap.datetime; + ctx->access_points[i].latitude = ap.latitude; + ctx->access_points[i].longitude = ap.longitude; + + if(strcmp(ctx->active_access_point.bssid, ap.bssid) == 0) { + ctx->active_access_point.rssi = ap.rssi; + ctx->active_access_point.channel = ap.channel; + ctx->active_access_point.datetime = ap.datetime; + ctx->active_access_point.latitude = ap.latitude; + ctx->active_access_point.longitude = ap.longitude; + } + + free(ap.ssid); + free(ap.bssid); + + break; + } + } + + if(!found) { + memcpy(&ctx->access_points[ctx->access_points_count], &ap, sizeof(AccessPoint)); + ctx->access_points_count++; + } + + sort_access_points(ctx); + set_index_from_access_points(ctx); + } else { + // it is a packet so screw the ap + free(ap.ssid); + free(ap.bssid); + + // check if values are valid + // mac needs to be 6 characters long + if(strlen(pkt.recievedMac) != 17 || strlen(pkt.sentMac) != 17 || + ctx->access_points_count == 0) { + free(pkt.recievedMac); + free(pkt.sentMac); + return; + } + + furi_hal_light_set(LightGreen, 0); + furi_hal_light_set(LightBlue, 255); + + for(size_t i = 0; i < ctx->access_points_count; i++) { + if(strcmp(ctx->access_points[i].bssid, pkt.recievedMac) == 0) { + ctx->access_points[i].packetRxCount++; + break; + } + } + + for(size_t i = 0; i < ctx->access_points_count; i++) { + if(strcmp(ctx->access_points[i].bssid, pkt.sentMac) == 0) { + ctx->access_points[i].packetTxCount++; + break; + } + } + + free(pkt.recievedMac); + free(pkt.sentMac); + } +} + +static void uart_cb_esp(UartIrqEvent ev, uint8_t data, void* context) { + Context* ctx = (Context*)context; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(ctx->rx_stream_esp, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(ctx->thread_esp), WorkerEvtRxDone); + } +} + +static int32_t uart_worker_esp(void* context) { + Context* ctx = (Context*)context; + + size_t rx_offset = 0; + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEvtStop) { + break; + } + + if(events & WorkerEvtRxDone) { + size_t len = 0; + do { + // receive serial bytes into rx_buf, starting at rx_offset from the start of the buffer + // the maximum we can receive is RX_BUF_SIZE - 1 - rx_offset + len = furi_stream_buffer_receive( + ctx->rx_stream_esp, + ctx->rx_buf_esp + rx_offset, + RX_BUF_SIZE - 1 - rx_offset, + 0); + + if(len > 0) { + // increase rx_offset by the number of bytes received, and null-terminate rx_buf + rx_offset += len; + ctx->rx_buf_esp[rx_offset] = '\0'; + + // look for strings ending in newlines, starting at the start of rx_buf + char* line_current = (char*)ctx->rx_buf_esp; + while(1) { + // skip null characters + while(*line_current == '\0' && + line_current < (char*)ctx->rx_buf_esp + rx_offset - 1) { + line_current++; + } + + // find the next newline + char* newline = strchr(line_current, '\n'); + if(newline) // newline found + { + // put a null terminator in place of the newline, to delimit the line string + *newline = '\0'; + + parseLine(ctx, line_current); + + // move the cursor to the character after the newline + line_current = newline + 1; + } else // no more newlines found + { + if(line_current > + (char*)ctx->rx_buf_esp) // at least one line was found + { + // clear parsed lines, and move any leftover bytes to the start of rx_buf + rx_offset = 0; + while( + *line_current) // stop when the original rx_offset terminator is reached + { + ctx->rx_buf_esp[rx_offset++] = *(line_current++); + } + } + break; // go back to receiving bytes from the serial stream + } + } + } + } while(len > 0); + } + } + + furi_hal_uart_set_irq_cb(UART_CH_ESP, NULL, NULL); + + furi_stream_buffer_free(ctx->rx_stream_esp); + + return 0; +} + +static void gps_uart_parse_nmea(Context* ctx, char* line) { + switch(minmea_sentence_id(line, false)) { + case MINMEA_SENTENCE_RMC: { + struct minmea_sentence_rmc frame; + if(minmea_parse_rmc(&frame, line)) { + ctx->last_latitude = minmea_tocoord(&frame.latitude); + ctx->last_longitude = minmea_tocoord(&frame.longitude); + } + } break; + + case MINMEA_SENTENCE_GGA: { + struct minmea_sentence_gga frame; + if(minmea_parse_gga(&frame, line)) { + ctx->last_latitude = minmea_tocoord(&frame.latitude); + ctx->last_longitude = minmea_tocoord(&frame.longitude); + } + } break; + + case MINMEA_SENTENCE_GLL: { + struct minmea_sentence_gll frame; + if(minmea_parse_gll(&frame, line)) { + ctx->last_latitude = minmea_tocoord(&frame.latitude); + ctx->last_longitude = minmea_tocoord(&frame.longitude); + } + } break; + + default: + break; + } +} + +static void uart_cb_gps(UartIrqEvent ev, uint8_t data, void* context) { + Context* ctx = (Context*)context; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(ctx->rx_stream_gps, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(ctx->thread_gps), WorkerEvtRxDone); + } +} + +static int32_t uart_worker_gps(void* context) { + Context* ctx = (Context*)context; + + size_t rx_offset = 0; + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEvtStop) { + break; + } + + if(events & WorkerEvtRxDone) { + size_t len = 0; + do { + // receive serial bytes into rx_buf, starting at rx_offset from the start of the buffer + // the maximum we can receive is RX_BUF_SIZE - 1 - rx_offset + len = furi_stream_buffer_receive( + ctx->rx_stream_gps, + ctx->rx_buf_gps + rx_offset, + RX_BUF_SIZE - 1 - rx_offset, + 0); + + if(len > 0) { + // increase rx_offset by the number of bytes received, and null-terminate rx_buf + rx_offset += len; + ctx->rx_buf_gps[rx_offset] = '\0'; + + // look for strings ending in newlines, starting at the start of rx_buf + char* line_current = (char*)ctx->rx_buf_gps; + while(1) { + // skip null characters + while(*line_current == '\0' && + line_current < (char*)ctx->rx_buf_gps + rx_offset - 1) { + line_current++; + } + + // find the next newline + char* newline = strchr(line_current, '\n'); + if(newline) // newline found + { + // put a null terminator in place of the newline, to delimit the line string + *newline = '\0'; + + // FURI_LOG_I(appname, "Received line: %s", line_current); + + gps_uart_parse_nmea(ctx, line_current); + + // move the cursor to the character after the newline + line_current = newline + 1; + } else // no more newlines found + { + if(line_current > + (char*)ctx->rx_buf_gps) // at least one line was found + { + // clear parsed lines, and move any leftover bytes to the start of rx_buf + rx_offset = 0; + while( + *line_current) // stop when the original rx_offset terminator is reached + { + ctx->rx_buf_gps[rx_offset++] = *(line_current++); + } + } + break; // go back to receiving bytes from the serial stream + } + } + } + } while(len > 0); + } + } + + furi_hal_uart_set_irq_cb(UART_CH_GPS, NULL, NULL); + + furi_stream_buffer_free(ctx->rx_stream_gps); + + return 0; +} + +int32_t wifisniffer_app(void* p) { + UNUSED(p); + + // if(UART_CH_ESP == UART_CH_GPS) { + // FURI_LOG_I(appname, "ESP and GPS uart can't be the same"); + // return -1; + // } + + // turn off 5v, so it gets reset on startup + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + + // Enable 5v on startup + 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); + + // alloc everything + Context* ctx = malloc(sizeof(Context)); + ctx->queue = furi_message_queue_alloc(8, sizeof(Event)); + ctx->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + ctx->buffer = furi_string_alloc(); + ctx->buffer2 = furi_string_alloc(); + ctx->notifications = furi_record_open(RECORD_NOTIFICATION); + + ctx->access_points_count = 0; + ctx->access_points_index = 0; + + ctx->pressedButton = false; + + //esp uart + ctx->rx_stream_esp = furi_stream_buffer_alloc(RX_BUF_SIZE * 5, 1); + + ctx->thread_esp = furi_thread_alloc(); + furi_thread_set_name(ctx->thread_esp, "LLwifiSnifferUartWorkerESP"); + furi_thread_set_stack_size(ctx->thread_esp, 2048); + furi_thread_set_context(ctx->thread_esp, ctx); + furi_thread_set_callback(ctx->thread_esp, uart_worker_esp); + + furi_thread_start(ctx->thread_esp); + + if(UART_CH_ESP == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH_ESP == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH_ESP, 115200); + } + furi_hal_uart_set_br(UART_CH_ESP, 115200); + furi_hal_uart_set_irq_cb(UART_CH_ESP, uart_cb_esp, ctx); + + furi_hal_uart_tx(UART_CH_ESP, (uint8_t*)"XFW#WIFISNIFF=1\r\n", strlen("XFW#WIFISNIFF=1\r\n")); + //end esp uart + + //gps uart + if(UART_CH_ESP != UART_CH_GPS) { + ctx->rx_stream_gps = furi_stream_buffer_alloc(RX_BUF_SIZE * 5, 1); + + ctx->thread_gps = furi_thread_alloc(); + furi_thread_set_name(ctx->thread_gps, "LLwifiSnifferUartWorkerGPS"); + furi_thread_set_stack_size(ctx->thread_gps, 2048); + furi_thread_set_context(ctx->thread_gps, ctx); + furi_thread_set_callback(ctx->thread_gps, uart_worker_gps); + + furi_thread_start(ctx->thread_gps); + + if(UART_CH_GPS == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH_GPS == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH_GPS, 9600); + } + furi_hal_uart_set_br(UART_CH_GPS, 9600); + furi_hal_uart_set_irq_cb(UART_CH_GPS, uart_cb_gps, ctx); + } + //end gps uart + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, ctx); + view_port_input_callback_set(view_port, input_callback, ctx->queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FuriTimer* timer = furi_timer_alloc(tick_callback, FuriTimerTypePeriodic, ctx->queue); + furi_timer_start(timer, 100); + + // application loop + Event event; + bool processing = true; + do { + if(furi_message_queue_get(ctx->queue, &event, FuriWaitForever) == FuriStatusOk) { + furi_mutex_acquire(ctx->mutex, FuriWaitForever); + switch(event.type) { + case EventTypeKey: + // applicatie verlaten + if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) { + processing = false; + } else if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { + processing = false; + } else if(event.input.type == InputTypeLong && event.input.key == InputKeyOk) { + // remove accespoint + if(ctx->access_points_count > 0) { + for(int i = ctx->access_points_index; i < ctx->access_points_count - 1; + i++) { + ctx->access_points[i] = ctx->access_points[i + 1]; + } + ctx->access_points_count--; + if(ctx->access_points_index >= ctx->access_points_count) { + ctx->access_points_index = ctx->access_points_count - 1; + } + } + } else if(event.input.type == InputTypePress && event.input.key == InputKeyDown) { + ctx->access_points_index--; + if(ctx->access_points_index < 0) { + ctx->access_points_index = ctx->access_points_count - 1; + } + ctx->active_access_point = ctx->access_points[ctx->access_points_index]; + } else if(event.input.type == InputTypePress && event.input.key == InputKeyUp) { + ctx->access_points_index++; + if(ctx->access_points_index >= ctx->access_points_count) { + ctx->access_points_index = 0; + } + ctx->active_access_point = ctx->access_points[ctx->access_points_index]; + } else if(event.input.type == InputTypePress && event.input.key == InputKeyLeft) { + } else if(event.input.type == InputTypePress && event.input.key == InputKeyRight) { + } + ctx->pressedButton = true; + break; + case EventTypeTick: + + // fix for the empty active access point when there was no interaction + if(!ctx->pressedButton) { + ctx->access_points_index = 0; + ctx->active_access_point = ctx->access_points[ctx->access_points_index]; + } + + break; + + default: + break; + } + + view_port_update(view_port); + } else { + processing = false; + } + } while(processing); + + // save the data to the file + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + + FuriString* filename = furi_string_alloc(); + furi_string_printf( + filename, + "%d_%d_%d_%d_%d_%d.txt", + datetime.year, + datetime.month, + datetime.day, + datetime.hour, + datetime.minute, + datetime.second); + + FuriString* path = furi_string_alloc(); + furi_string_printf(path, "/ext/apps_data/llsniffer/%s", furi_string_get_cstr(filename)); + + // open file + ctx->file = storage_file_alloc(storage); + + if(!storage_common_exists(storage, EXT_PATH("apps_data/llsniffer"))) { + storage_common_mkdir(storage, EXT_PATH("apps_data/llsniffer")); + } + + if(!storage_file_open(ctx->file, furi_string_get_cstr(path), FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + FURI_LOG_E(appname, "Failed to open file"); + } + + for(int i = 0; i < ctx->access_points_count; i++) { + AccessPoint ap = ctx->access_points[i]; + furi_string_printf( + ctx->buffer2, + "%s,%s,%s,%d,%d,%d,%d,%d,%d,%d,%d,%f,%f\r\n", + "Accesspoint", + ap.ssid, + ap.bssid, + ap.rssi, + ap.channel, + ap.datetime.year, + ap.datetime.month, + ap.datetime.day, + ap.datetime.hour, + ap.datetime.minute, + ap.datetime.second, + (double)ap.latitude, + (double)ap.longitude); + + if(!storage_file_write( + ctx->file, + furi_string_get_cstr(ctx->buffer2), + strlen(furi_string_get_cstr(ctx->buffer2)))) { + FURI_LOG_E(appname, "Failed to write AP to file"); + } + } + + // free everything + furi_record_close(RECORD_NOTIFICATION); + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_record_close(RECORD_GUI); + furi_message_queue_free(ctx->queue); + furi_mutex_free(ctx->mutex); + + furi_thread_flags_set(furi_thread_get_id(ctx->thread_esp), WorkerEvtStop); + furi_thread_join(ctx->thread_esp); + furi_thread_free(ctx->thread_esp); + + if(UART_CH_ESP != UART_CH_GPS) { + furi_thread_flags_set(furi_thread_get_id(ctx->thread_gps), WorkerEvtStop); + furi_thread_join(ctx->thread_gps); + furi_thread_free(ctx->thread_gps); + } + + storage_file_close(ctx->file); + storage_file_free(ctx->file); + furi_record_close(RECORD_STORAGE); + free(ctx); + + furi_hal_light_set(LightBlue, 0); + furi_hal_light_set(LightGreen, 0); + + if(UART_CH_ESP == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH_ESP); + } else if(UART_CH_ESP == FuriHalUartIdUSART1) { + furi_hal_console_enable(); + } + + if(UART_CH_GPS == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH_GPS); + } else if(UART_CH_GPS == FuriHalUartIdUSART1) { + furi_hal_console_enable(); + } + + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + + return 0; +} diff --git a/applications/external/airmouse/application.fam b/applications/external/airmouse/application.fam index dfb88da1e..b5498b8ce 100644 --- a/applications/external/airmouse/application.fam +++ b/applications/external/airmouse/application.fam @@ -6,6 +6,6 @@ App( stack_size=10 * 1024, fap_category="GPIO", fap_icon="mouse_10px.png", - fap_version="0.6", + fap_version="0.8", sources=["*.c", "*.cc"], ) diff --git a/applications/external/airmouse/tracking/main_loop.cc b/applications/external/airmouse/tracking/main_loop.cc index 2caffb717..a9aca0aab 100644 --- a/applications/external/airmouse/tracking/main_loop.cc +++ b/applications/external/airmouse/tracking/main_loop.cc @@ -12,178 +12,219 @@ static const float CURSOR_SPEED = 1024.0 / (M_PI / 4); static const float STABILIZE_BIAS = 16.0; -float g_yaw = 0; -float g_pitch = 0; -float g_dYaw = 0; -float g_dPitch = 0; -bool firstRead = true; -bool stabilize = true; -CalibrationData calibration; -cardboard::OrientationTracker tracker(10000000l); // 10 ms / 100 Hz -uint64_t ippms, ippms2; +class TrackingState { +private: + float yaw; + float pitch; + float dYaw; + float dPitch; + bool firstRead; + bool stabilize; + CalibrationData calibration; + cardboard::OrientationTracker tracker; + uint64_t ippus, ippus2; -static inline float clamp(float val) -{ - while (val <= -M_PI) { - val += 2 * M_PI; - } - while (val >= M_PI) { - val -= 2 * M_PI; - } - return val; -} - -static inline float highpass(float oldVal, float newVal) -{ - if (!stabilize) { - return newVal; - } - float delta = clamp(oldVal - newVal); - float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0)); - return newVal + alpha * delta; -} - -void sendCurrentState(MouseMoveCallback mouse_move, void *context) -{ - float dX = g_dYaw * CURSOR_SPEED; - float dY = g_dPitch * CURSOR_SPEED; - - // Scale the shift down to fit the protocol. - if (dX > 127) { - dY *= 127.0 / dX; - dX = 127; - } - if (dX < -127) { - dY *= -127.0 / dX; - dX = -127; - } - if (dY > 127) { - dX *= 127.0 / dY; - dY = 127; - } - if (dY < -127) { - dX *= -127.0 / dY; - dY = -127; +private: + float clamp(float val) { + while (val <= -M_PI) { + val += 2 * M_PI; + } + while (val >= M_PI) { + val -= 2 * M_PI; + } + return val; } - const int8_t x = (int8_t)std::floor(dX + 0.5); - const int8_t y = (int8_t)std::floor(dY + 0.5); - - mouse_move(x, y, context); - - // Only subtract the part of the error that was already sent. - if (x != 0) { - g_dYaw -= x / CURSOR_SPEED; - } - if (y != 0) { - g_dPitch -= y / CURSOR_SPEED; - } -} - -void onOrientation(cardboard::Vector4& quaternion) -{ - float q1 = quaternion[0]; // X * sin(T/2) - float q2 = quaternion[1]; // Y * sin(T/2) - float q3 = quaternion[2]; // Z * sin(T/2) - float q0 = quaternion[3]; // cos(T/2) - - float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3))); - float pitch = std::asin(2 * (q0 * q1 + q2 * q3)); - // float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2))); - - if (yaw == NAN || pitch == NAN) { - // NaN case, skip it - return; + float highpass(float oldVal, float newVal) { + if (!stabilize) { + return newVal; + } + float delta = clamp(oldVal - newVal); + float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0)); + return newVal + alpha * delta; } - if (firstRead) { - g_yaw = yaw; - g_pitch = pitch; - firstRead = false; - } else { - const float newYaw = highpass(g_yaw, yaw); - const float newPitch = highpass(g_pitch, pitch); + void sendCurrentState(MouseMoveCallback mouse_move, void *context) { + float dX = dYaw * CURSOR_SPEED; + float dY = dPitch * CURSOR_SPEED; - float dYaw = clamp(g_yaw - newYaw); - float dPitch = g_pitch - newPitch; - g_yaw = newYaw; - g_pitch = newPitch; + // Scale the shift down to fit the protocol. + if (dX > 127) { + dY *= 127.0 / dX; + dX = 127; + } + if (dX < -127) { + dY *= -127.0 / dX; + dX = -127; + } + if (dY > 127) { + dX *= 127.0 / dY; + dY = 127; + } + if (dY < -127) { + dX *= -127.0 / dY; + dY = -127; + } - // Accumulate the error locally. - g_dYaw += dYaw; - g_dPitch += dPitch; + const int8_t x = (int8_t)std::floor(dX + 0.5); + const int8_t y = (int8_t)std::floor(dY + 0.5); + + mouse_move(x, y, context); + + // Only subtract the part of the error that was already sent. + if (x != 0) { + dYaw -= x / CURSOR_SPEED; + } + if (y != 0) { + dPitch -= y / CURSOR_SPEED; + } } -} + + void onOrientation(cardboard::Vector4& quaternion) { + float q1 = quaternion[0]; // X * sin(T/2) + float q2 = quaternion[1]; // Y * sin(T/2) + float q3 = quaternion[2]; // Z * sin(T/2) + float q0 = quaternion[3]; // cos(T/2) + + float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3))); + float pitch = std::asin(2 * (q0 * q1 + q2 * q3)); + // float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2))); + + if (yaw == NAN || pitch == NAN) { + // NaN case, skip it + return; + } + + if (firstRead) { + this->yaw = yaw; + this->pitch = pitch; + firstRead = false; + } else { + const float newYaw = highpass(this->yaw, yaw); + const float newPitch = highpass(this->pitch, pitch); + + float dYaw = clamp(this->yaw - newYaw); + float dPitch = this->pitch - newPitch; + this->yaw = newYaw; + this->pitch = newPitch; + + // Accumulate the error locally. + this->dYaw += dYaw; + this->dPitch += dPitch; + } + } + +public: + TrackingState() + : yaw(0) + , pitch(0) + , dYaw(0) + , dPitch(0) + , firstRead(true) + , stabilize(true) + , tracker(10000000l) { // 10 ms / 100 Hz + ippus = furi_hal_cortex_instructions_per_microsecond(); + ippus2 = ippus / 2; + } + + void beginCalibration() { + calibration.reset(); + } + + bool stepCalibration() { + if (calibration.isComplete()) + return true; + + double vec[6]; + if (imu_read(vec) & GYR_DATA_READY) { + cardboard::Vector3 data(vec[3], vec[4], vec[5]); + furi_delay_ms(9); // Artificially limit to ~100Hz + return calibration.add(data); + } + + return false; + } + + void saveCalibration() { + CalibrationMedian store; + cardboard::Vector3 median = calibration.getMedian(); + store.x = median[0]; + store.y = median[1]; + store.z = median[2]; + CALIBRATION_DATA_SAVE(&store); + } + + void loadCalibration() { + CalibrationMedian store; + cardboard::Vector3 median = calibration.getMedian(); + if (CALIBRATION_DATA_LOAD(&store)) { + median[0] = store.x; + median[1] = store.y; + median[2] = store.z; + } + + tracker.SetCalibration(median); + } + + void beginTracking() { + loadCalibration(); + tracker.Resume(); + } + + void stepTracking(MouseMoveCallback mouse_move, void *context) { + double vec[6]; + int ret = imu_read(vec); + if (ret != 0) { + uint64_t t = (DWT->CYCCNT * 1000llu + ippus2) / ippus; + if (ret & ACC_DATA_READY) { + cardboard::AccelerometerData adata + = { .system_timestamp = t, .sensor_timestamp_ns = t, + .data = cardboard::Vector3(vec[0], vec[1], vec[2]) }; + tracker.OnAccelerometerData(adata); + } + if (ret & GYR_DATA_READY) { + cardboard::GyroscopeData gdata + = { .system_timestamp = t, .sensor_timestamp_ns = t, + .data = cardboard::Vector3(vec[3], vec[4], vec[5]) }; + cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata); + onOrientation(pose); + sendCurrentState(mouse_move, context); + } + } + } + + void stopTracking() { + tracker.Pause(); + } +}; + +static TrackingState g_state; extern "C" { void calibration_begin() { - calibration.reset(); + g_state.beginCalibration(); FURI_LOG_I(TAG, "Calibrating"); } bool calibration_step() { - if (calibration.isComplete()) - return true; - - double vec[6]; - if (imu_read(vec) & GYR_DATA_READY) { - cardboard::Vector3 data(vec[3], vec[4], vec[5]); - furi_delay_ms(9); // Artificially limit to ~100Hz - return calibration.add(data); - } - - return false; + return g_state.stepCalibration(); } void calibration_end() { - CalibrationMedian store; - cardboard::Vector3 median = calibration.getMedian(); - store.x = median[0]; - store.y = median[1]; - store.z = median[2]; - CALIBRATION_DATA_SAVE(&store); + g_state.saveCalibration(); } void tracking_begin() { - CalibrationMedian store; - cardboard::Vector3 median = calibration.getMedian(); - if (CALIBRATION_DATA_LOAD(&store)) { - median[0] = store.x; - median[1] = store.y; - median[2] = store.z; - } - - ippms = furi_hal_cortex_instructions_per_microsecond(); - ippms2 = ippms / 2; - tracker.SetCalibration(median); - tracker.Resume(); + g_state.beginTracking(); } void tracking_step(MouseMoveCallback mouse_move, void *context) { - double vec[6]; - int ret = imu_read(vec); - if (ret != 0) { - uint64_t t = (DWT->CYCCNT * 1000llu + ippms2) / ippms; - if (ret & ACC_DATA_READY) { - cardboard::AccelerometerData adata - = { .system_timestamp = t, .sensor_timestamp_ns = t, - .data = cardboard::Vector3(vec[0], vec[1], vec[2]) }; - tracker.OnAccelerometerData(adata); - } - if (ret & GYR_DATA_READY) { - cardboard::GyroscopeData gdata - = { .system_timestamp = t, .sensor_timestamp_ns = t, - .data = cardboard::Vector3(vec[3], vec[4], vec[5]) }; - cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata); - onOrientation(pose); - sendCurrentState(mouse_move, context); - } - } + g_state.stepTracking(mouse_move, context); } void tracking_end() { - tracker.Pause(); + g_state.stopTracking(); } } diff --git a/applications/external/airmouse/tracking/orientation_tracker.cc b/applications/external/airmouse/tracking/orientation_tracker.cc index ac20e9672..4f381c895 100644 --- a/applications/external/airmouse/tracking/orientation_tracker.cc +++ b/applications/external/airmouse/tracking/orientation_tracker.cc @@ -89,7 +89,7 @@ Vector4 OrientationTracker::OnGyroscopeData(const GyroscopeData& event) sensor_fusion_->ProcessGyroscopeSample(data); - return OrientationTracker::GetPose(data.sensor_timestamp_ns + sampling_period_ns_); + return GetPose(data.sensor_timestamp_ns + sampling_period_ns_); } } // namespace cardboard diff --git a/applications/external/airmouse/tracking/sensors/sensor_fusion_ekf.cc b/applications/external/airmouse/tracking/sensors/sensor_fusion_ekf.cc index 575dde6f0..dabd788ef 100644 --- a/applications/external/airmouse/tracking/sensors/sensor_fusion_ekf.cc +++ b/applications/external/airmouse/tracking/sensors/sensor_fusion_ekf.cc @@ -59,13 +59,14 @@ namespace { // angle = norm(a) // axis = a.normalized() // If norm(a) == 0, it returns an identity rotation. - static inline Rotation RotationFromVector(const Vector3& a) + static inline void RotationFromVector(const Vector3& a, Rotation& r) { const double norm_a = Length(a); if (norm_a < kEpsilon) { - return Rotation::Identity(); + r = Rotation::Identity(); + return; } - return Rotation::FromAxisAndAngle(a / norm_a, norm_a); + r = Rotation::FromAxisAndAngle(a / norm_a, norm_a); } } // namespace @@ -199,7 +200,8 @@ void SensorFusionEkf::ComputeMeasurementJacobian() Vector3 delta = Vector3::Zero(); delta[dof] = kFiniteDifferencingEpsilon; - const Rotation epsilon_rotation = RotationFromVector(delta); + Rotation epsilon_rotation; + RotationFromVector(delta, epsilon_rotation); const Vector3 delta_rotation = ComputeInnovation(epsilon_rotation * current_state_.sensor_from_start_rotation); @@ -263,7 +265,8 @@ void SensorFusionEkf::ProcessAccelerometerSample(const AccelerometerData& sample * state_covariance_; // Updates pose and associate covariance matrix. - const Rotation rotation_from_state_update = RotationFromVector(state_update_); + Rotation rotation_from_state_update; + RotationFromVector(state_update_, rotation_from_state_update); current_state_.sensor_from_start_rotation = rotation_from_state_update * current_state_.sensor_from_start_rotation; diff --git a/applications/external/airmouse/views/bt_mouse.c b/applications/external/airmouse/views/bt_mouse.c index 7d9c0e6db..664b25bfd 100644 --- a/applications/external/airmouse/views/bt_mouse.c +++ b/applications/external/airmouse/views/bt_mouse.c @@ -132,6 +132,11 @@ void bt_mouse_connection_status_changed_callback(BtStatus status, void* context) BtMouse* bt_mouse = context; bt_mouse->connected = (status == BtStatusConnected); + if(!bt_mouse->notifications) { + tracking_end(); + return; + } + if(bt_mouse->connected) { notification_internal_message(bt_mouse->notifications, &sequence_set_blue_255); tracking_begin(); @@ -140,9 +145,6 @@ void bt_mouse_connection_status_changed_callback(BtStatus status, void* context) tracking_end(); notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); } - - //with_view_model( - // bt_mouse->view, void * model, { model->connected = connected; }, true); } bool bt_mouse_move(int8_t dx, int8_t dy, void* context) { @@ -160,46 +162,6 @@ bool bt_mouse_move(int8_t dx, int8_t dy, void* context) { return true; } -void bt_mouse_enter_callback(void* context) { - furi_assert(context); - BtMouse* bt_mouse = context; - - bt_mouse->bt = furi_record_open(RECORD_BT); - bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION); - bt_set_status_changed_callback( - bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse); - furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard)); - furi_hal_bt_start_advertising(); -} - -bool bt_mouse_custom_callback(uint32_t event, void* context) { - UNUSED(event); - furi_assert(context); - BtMouse* bt_mouse = context; - - tracking_step(bt_mouse_move, context); - furi_delay_ms(3); // Magic! Removing this will break the buttons - - view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0); - return true; -} - -void bt_mouse_exit_callback(void* context) { - furi_assert(context); - BtMouse* bt_mouse = context; - - tracking_end(); - notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); - - furi_hal_bt_stop_advertising(); - bt_set_profile(bt_mouse->bt, BtProfileSerial); - - furi_record_close(RECORD_NOTIFICATION); - bt_mouse->notifications = NULL; - furi_record_close(RECORD_BT); - bt_mouse->bt = NULL; -} - static int8_t clamp(int t) { if(t < -128) { return -128; @@ -279,6 +241,50 @@ void bt_mouse_thread_stop(BtMouse* bt_mouse) { furi_thread_join(bt_mouse->thread); furi_thread_free(bt_mouse->thread); furi_mutex_free(bt_mouse->mutex); + bt_mouse->mutex = NULL; + bt_mouse->thread = NULL; +} + +void bt_mouse_enter_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + bt_mouse->bt = furi_record_open(RECORD_BT); + bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION); + bt_set_status_changed_callback( + bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse); + furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard)); + furi_hal_bt_start_advertising(); + bt_mouse_thread_start(bt_mouse); +} + +bool bt_mouse_custom_callback(uint32_t event, void* context) { + UNUSED(event); + furi_assert(context); + BtMouse* bt_mouse = context; + + tracking_step(bt_mouse_move, context); + furi_delay_ms(3); // Magic! Removing this will break the buttons + + view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0); + return true; +} + +void bt_mouse_exit_callback(void* context) { + furi_assert(context); + BtMouse* bt_mouse = context; + + bt_mouse_thread_stop(bt_mouse); + tracking_end(); + notification_internal_message(bt_mouse->notifications, &sequence_reset_blue); + + furi_hal_bt_stop_advertising(); + bt_set_profile(bt_mouse->bt, BtProfileSerial); + + furi_record_close(RECORD_NOTIFICATION); + bt_mouse->notifications = NULL; + furi_record_close(RECORD_BT); + bt_mouse->bt = NULL; } BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) { @@ -293,13 +299,11 @@ BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) { view_set_enter_callback(bt_mouse->view, bt_mouse_enter_callback); view_set_custom_callback(bt_mouse->view, bt_mouse_custom_callback); view_set_exit_callback(bt_mouse->view, bt_mouse_exit_callback); - bt_mouse_thread_start(bt_mouse); return bt_mouse; } void bt_mouse_free(BtMouse* bt_mouse) { furi_assert(bt_mouse); - bt_mouse_thread_stop(bt_mouse); view_free(bt_mouse->view); free(bt_mouse); } diff --git a/applications/external/arkanoid/application.fam b/applications/external/arkanoid/application.fam index b33b9acae..19e851514 100644 --- a/applications/external/arkanoid/application.fam +++ b/applications/external/arkanoid/application.fam @@ -5,7 +5,6 @@ App( entry_point="arkanoid_game_app", requires=["gui"], stack_size=1 * 1024, - order=20, fap_icon="arkanoid_10px.png", fap_category="Games", fap_author="@xMasterX & @gotnull", diff --git a/applications/external/arkanoid/arkanoid_game.c b/applications/external/arkanoid/arkanoid_game.c index 4fba7766e..2adf2e30b 100644 --- a/applications/external/arkanoid/arkanoid_game.c +++ b/applications/external/arkanoid/arkanoid_game.c @@ -5,6 +5,7 @@ #include #include #include +#include #define TAG "Arkanoid" @@ -398,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/asteroids/application.fam b/applications/external/asteroids/application.fam index ca5f5d175..608809c02 100644 --- a/applications/external/asteroids/application.fam +++ b/applications/external/asteroids/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_ASTEROIDS"], requires=["gui"], stack_size=8 * 1024, - order=50, fap_icon="appicon.png", fap_icon_assets="assets", fap_category="Games", diff --git a/applications/external/avr_isp_programmer/application.fam b/applications/external/avr_isp_programmer/application.fam index 3b396e83d..f355cfcb5 100644 --- a/applications/external/avr_isp_programmer/application.fam +++ b/applications/external/avr_isp_programmer/application.fam @@ -1,11 +1,12 @@ App( appid="avr_isp", - name="[AVR] Flasher", + name="[AVR] AVR Flasher", apptype=FlipperAppType.EXTERNAL, entry_point="avr_isp_app", requires=["gui"], stack_size=4 * 1024, - order=20, + fap_description="Application for flashing AVR microcontrollers", + fap_version="1.0", fap_icon="avr_app_icon_10x10.png", fap_category="GPIO", fap_icon_assets="images", 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 819e3f45f..8a7dbc4b8 100644 --- a/applications/external/avr_isp_programmer/avr_isp_app_i.h +++ b/applications/external/avr_isp_programmer/avr_isp_app_i.h @@ -1,7 +1,8 @@ #pragma once #include "helpers/avr_isp_types.h" -#include +#include "avr_isp_icons.h" +#include #include "scenes/avr_isp_scene.h" #include @@ -41,4 +42,4 @@ typedef struct { AvrIspError error; } AvrIspApp; -bool avr_isp_load_from_file(AvrIspApp* app); +bool avr_isp_load_from_file(AvrIspApp* app); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c index 1b9e2fd1f..283c17bfd 100644 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ b/applications/external/avr_isp_programmer/helpers/avr_isp.c @@ -174,7 +174,7 @@ static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) { while((furi_get_tick() - starttime) < 30) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { break; - } + }; } } } @@ -357,7 +357,7 @@ uint8_t avr_isp_read_lock_byte(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) { break; - } + }; data = 0x00; } return data; @@ -377,7 +377,7 @@ bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) { ret = true; break; - } + }; } } return ret; @@ -392,7 +392,7 @@ uint8_t avr_isp_read_fuse_low(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) { break; - } + }; data = 0x00; } return data; @@ -412,7 +412,7 @@ bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) { ret = true; break; - } + }; } } return ret; @@ -427,7 +427,7 @@ uint8_t avr_isp_read_fuse_high(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) { break; - } + }; data = 0x00; } return data; @@ -447,7 +447,7 @@ bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) { ret = true; break; - } + }; } } return ret; @@ -462,7 +462,7 @@ uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) { data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED); if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) { break; - } + }; data = 0x00; } return data; @@ -482,7 +482,7 @@ bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) { if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) { ret = true; break; - } + }; } } return ret; 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 121f08565..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 @@ -350,12 +350,12 @@ static void avr_isp_worker_rw_get_dump_flash(AvrIspWorkerRW* instance, const cha sizeof(data)); flipper_i32hex_file_bin_to_i32hex_set_data( flipper_hex_flash, data, avr_isp_chip_arr[instance->chip_arr_ind].pagesize); - //FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); + FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); instance->progress_flash = (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); } flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_flash); - //FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); + FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); flipper_i32hex_file_close(flipper_hex_flash); instance->progress_flash = 1.0f; } diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c index 051d97e9c..bbb6d4739 100644 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c @@ -111,7 +111,7 @@ static uint8_t avr_isp_prog_getch(AvrIspProg* instance) { uint8_t data[1] = {0}; while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) { if(instance->exit) break; - } + }; return data[0]; } @@ -196,7 +196,7 @@ static void avr_isp_prog_set_cfg(AvrIspProg* instance) { instance->cfg->lockbytes = instance->buff[6]; instance->cfg->fusebytes = instance->buff[7]; instance->cfg->flashpoll = instance->buff[8]; - // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as �flashpoll� + // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as “flashpoll” instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11]; instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13]; instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15]; @@ -348,7 +348,7 @@ static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t dat while((furi_get_tick() - starttime) < 30) { if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { break; - } + }; } } } diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c index fdcf71c36..b30d4e916 100644 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c @@ -1,5 +1,6 @@ #include "avr_isp_view_chip_detect.h" -#include +#include "avr_isp_icons.h" +#include #include #include "../helpers/avr_isp_worker_rw.h" diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c index 34e18770b..38b7394d6 100644 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c +++ b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c @@ -1,5 +1,6 @@ #include "avr_isp_view_programmer.h" -#include +#include "avr_isp_icons.h" +#include #include "../helpers/avr_isp_worker.h" #include diff --git a/applications/external/barcode_gen/barcode_app.c b/applications/external/barcode_gen/barcode_app.c index 99e5769d2..aff437f2e 100644 --- a/applications/external/barcode_gen/barcode_app.c +++ b/applications/external/barcode_gen/barcode_app.c @@ -1,6 +1,7 @@ #include "barcode_app.h" #include "barcode_app_icons.h" +#include /** * Opens a file browser dialog and returns the filepath of the selected file diff --git a/applications/external/blackjack/application.fam b/applications/external/blackjack/application.fam index 83b3c83a8..504989116 100644 --- a/applications/external/blackjack/application.fam +++ b/applications/external/blackjack/application.fam @@ -5,7 +5,6 @@ App( entry_point="blackjack_app", requires=["gui", "storage", "canvas"], stack_size=2 * 1024, - order=30, fap_icon="blackjack_10px.png", fap_category="Games", fap_icon_assets="assets", diff --git a/applications/external/blackjack/blackjack.c b/applications/external/blackjack/blackjack.c index 50d8e30f6..8cf698d85 100644 --- a/applications/external/blackjack/blackjack.c +++ b/applications/external/blackjack/blackjack.c @@ -15,6 +15,7 @@ #include "ui.h" #include "blackjack_icons.h" +#include #define DEALER_MAX 17 @@ -276,7 +277,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 +572,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/application.fam b/applications/external/bomberduck/application.fam index 39c680693..958ac7c02 100644 --- a/applications/external/bomberduck/application.fam +++ b/applications/external/bomberduck/application.fam @@ -7,7 +7,6 @@ App( "gui", ], stack_size=1 * 1024, - order=90, fap_icon="bomb.png", fap_category="Games", fap_icon_assets="assets", diff --git a/applications/external/bomberduck/bomberduck.c b/applications/external/bomberduck/bomberduck.c index 7cc7842c9..eddec089f 100644 --- a/applications/external/bomberduck/bomberduck.c +++ b/applications/external/bomberduck/bomberduck.c @@ -6,6 +6,8 @@ #include #include #include "bomberduck_icons.h" +#include +#include int max(int a, int b) { return (a > b) ? a : b; @@ -381,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 отриÑовки, без контекÑта @@ -455,9 +457,9 @@ int32_t bomberduck_app(void* p) { notification_message(notification, &end); world.running = 0; world.level += 1; - // if(world.level % 5 == 0) { - // dolphin_deed(DolphinDeedPluginGameWin); - // } + if(world.level % 5 == 0) { + dolphin_deed(DolphinDeedPluginGameWin); + } } for(int i = 0; i < world.bombs_count; i++) { if(furi_get_tick() - world.bombs[i].planted > diff --git a/applications/external/bpmtapper/application.fam b/applications/external/bpmtapper/application.fam index bf3f3c895..b25f61af3 100644 --- a/applications/external/bpmtapper/application.fam +++ b/applications/external/bpmtapper/application.fam @@ -9,7 +9,6 @@ App( fap_icon="bpm_10px.png", fap_category="Media", fap_icon_assets="icons", - order=15, fap_author="@panki27", fap_weburl="https://github.com/panki27/bpm-tapper", fap_version="1.0", diff --git a/applications/external/caesarcipher/application.fam b/applications/external/caesarcipher/application.fam index fdb1052b0..985487986 100644 --- a/applications/external/caesarcipher/application.fam +++ b/applications/external/caesarcipher/application.fam @@ -10,7 +10,6 @@ App( stack_size=2 * 1024, fap_icon="caesar_cipher_icon.png", fap_category="Tools", - order=20, fap_author="@panki27", fap_weburl="https://github.com/panki27/caesar-cipher", fap_version="1.0", diff --git a/applications/external/calculator/application.fam b/applications/external/calculator/application.fam index cba45e49b..cfd183ec3 100644 --- a/applications/external/calculator/application.fam +++ b/applications/external/calculator/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_CALCULATOR"], requires=["gui"], stack_size=1 * 1024, - order=45, fap_icon="calcIcon.png", fap_category="Tools", fap_author="@n-o-T-I-n-s-a-n-e", diff --git a/applications/external/camera_suite/application.fam b/applications/external/camera_suite/application.fam index 2e26a3a27..1c9f0cc6f 100644 --- a/applications/external/camera_suite/application.fam +++ b/applications/external/camera_suite/application.fam @@ -1,15 +1,14 @@ App( - appid="camerasuite", + appid="camera_suite", apptype=FlipperAppType.EXTERNAL, cdefines=["APP_CAMERA_SUITE"], entry_point="camera_suite_app", - fap_author="Cody Tolene", + fap_author="@CodyTolene @Z4urce @leedave", fap_category="GPIO", fap_description="A camera suite application for the Flipper Zero ESP32-CAM module.", fap_icon="icons/camera_suite.png", fap_weburl="https://github.com/CodyTolene/Flipper-Zero-Cam", name="[ESP32] Camera Suite", - order=1, requires=["gui", "storage"], stack_size=8 * 1024, ) diff --git a/applications/external/camera_suite/camera_suite.c b/applications/external/camera_suite/camera_suite.c index 13ee09c22..cbe7e3d62 100644 --- a/applications/external/camera_suite/camera_suite.c +++ b/applications/external/camera_suite/camera_suite.c @@ -13,7 +13,7 @@ void camera_suite_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } -//leave app if back button pressed +// Leave app if back button pressed. bool camera_suite_navigation_event_callback(void* context) { furi_assert(context); CameraSuite* app = context; @@ -25,10 +25,10 @@ CameraSuite* camera_suite_app_alloc() { 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 + // Turn backlight on. notification_message(app->notification, &sequence_display_backlight_on); - //Scene additions + // Scene additions app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -60,17 +60,11 @@ CameraSuite* camera_suite_app_alloc() { CameraSuiteViewIdStartscreen, camera_suite_view_start_get_view(app->camera_suite_view_start)); - app->camera_suite_view_style_1 = camera_suite_view_style_1_alloc(); + app->camera_suite_view_camera = camera_suite_view_camera_alloc(); view_dispatcher_add_view( app->view_dispatcher, - CameraSuiteViewIdScene1, - camera_suite_view_style_1_get_view(app->camera_suite_view_style_1)); - - app->camera_suite_view_style_2 = camera_suite_view_style_2_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - CameraSuiteViewIdScene2, - camera_suite_view_style_2_get_view(app->camera_suite_view_style_2)); + CameraSuiteViewIdCamera, + camera_suite_view_camera_get_view(app->camera_suite_view_camera)); app->camera_suite_view_guide = camera_suite_view_guide_alloc(); view_dispatcher_add_view( @@ -98,9 +92,9 @@ void camera_suite_app_free(CameraSuite* app) { scene_manager_free(app->scene_manager); // View Dispatcher + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdStartscreen); view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdMenu); - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdScene1); - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdScene2); + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdCamera); view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdGuide); view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdSettings); submenu_free(app->submenu); @@ -110,8 +104,7 @@ void camera_suite_app_free(CameraSuite* app) { // Free remaining resources camera_suite_view_start_free(app->camera_suite_view_start); - camera_suite_view_style_1_free(app->camera_suite_view_style_1); - camera_suite_view_style_2_free(app->camera_suite_view_style_2); + camera_suite_view_camera_free(app->camera_suite_view_camera); camera_suite_view_guide_free(app->camera_suite_view_guide); button_menu_free(app->button_menu); variable_item_list_free(app->variable_item_list); diff --git a/applications/external/camera_suite/camera_suite.h b/applications/external/camera_suite/camera_suite.h index 2b6a7748b..a8b9825be 100644 --- a/applications/external/camera_suite/camera_suite.h +++ b/applications/external/camera_suite/camera_suite.h @@ -4,9 +4,7 @@ #include "scenes/camera_suite_scene.h" #include "views/camera_suite_view_guide.h" #include "views/camera_suite_view_start.h" -#include "views/camera_suite_view_style_1.h" -#include "views/camera_suite_view_style_2.h" -#include +#include "views/camera_suite_view_camera.h" #include #include #include @@ -29,8 +27,7 @@ typedef struct { SceneManager* scene_manager; VariableItemList* variable_item_list; CameraSuiteViewStart* camera_suite_view_start; - CameraSuiteViewStyle1* camera_suite_view_style_1; - CameraSuiteViewStyle2* camera_suite_view_style_2; + CameraSuiteViewCamera* camera_suite_view_camera; CameraSuiteViewGuide* camera_suite_view_guide; uint32_t orientation; uint32_t haptic; @@ -42,8 +39,7 @@ typedef struct { typedef enum { CameraSuiteViewIdStartscreen, CameraSuiteViewIdMenu, - CameraSuiteViewIdScene1, - CameraSuiteViewIdScene2, + CameraSuiteViewIdCamera, CameraSuiteViewIdGuide, CameraSuiteViewIdSettings, } CameraSuiteViewId; diff --git a/applications/external/camera_suite/helpers/camera_suite_custom_event.h b/applications/external/camera_suite/helpers/camera_suite_custom_event.h index 7727a0de3..4d472d577 100644 --- a/applications/external/camera_suite/helpers/camera_suite_custom_event.h +++ b/applications/external/camera_suite/helpers/camera_suite_custom_event.h @@ -8,20 +8,13 @@ typedef enum { CameraSuiteCustomEventStartRight, CameraSuiteCustomEventStartOk, CameraSuiteCustomEventStartBack, - // Scene events: Camera style 1 - CameraSuiteCustomEventSceneStyle1Up, - CameraSuiteCustomEventSceneStyle1Down, - CameraSuiteCustomEventSceneStyle1Left, - CameraSuiteCustomEventSceneStyle1Right, - CameraSuiteCustomEventSceneStyle1Ok, - CameraSuiteCustomEventSceneStyle1Back, - // Scene events: Camera style 2 - CameraSuiteCustomEventSceneStyle2Up, - CameraSuiteCustomEventSceneStyle2Down, - CameraSuiteCustomEventSceneStyle2Left, - CameraSuiteCustomEventSceneStyle2Right, - CameraSuiteCustomEventSceneStyle2Ok, - CameraSuiteCustomEventSceneStyle2Back, + // Scene events: Camera + CameraSuiteCustomEventSceneCameraUp, + CameraSuiteCustomEventSceneCameraDown, + CameraSuiteCustomEventSceneCameraLeft, + CameraSuiteCustomEventSceneCameraRight, + CameraSuiteCustomEventSceneCameraOk, + CameraSuiteCustomEventSceneCameraBack, // Scene events: Guide CameraSuiteCustomEventSceneGuideUp, CameraSuiteCustomEventSceneGuideDown, diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_style_1.c b/applications/external/camera_suite/scenes/camera_suite_scene_camera.c similarity index 59% rename from applications/external/camera_suite/scenes/camera_suite_scene_style_1.c rename to applications/external/camera_suite/scenes/camera_suite_scene_camera.c index 1aeecec7a..809d9a5c1 100644 --- a/applications/external/camera_suite/scenes/camera_suite_scene_style_1.c +++ b/applications/external/camera_suite/scenes/camera_suite_scene_camera.c @@ -1,35 +1,35 @@ #include "../camera_suite.h" #include "../helpers/camera_suite_custom_event.h" -#include "../views/camera_suite_view_style_1.h" +#include "../views/camera_suite_view_camera.h" -static void camera_suite_view_style_1_callback(CameraSuiteCustomEvent event, void* context) { +void camera_suite_view_camera_callback(CameraSuiteCustomEvent event, void* context) { furi_assert(context); CameraSuite* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, event); } -void camera_suite_scene_style_1_on_enter(void* context) { +void camera_suite_scene_camera_on_enter(void* context) { furi_assert(context); CameraSuite* app = context; - camera_suite_view_style_1_set_callback( - app->camera_suite_view_style_1, camera_suite_view_style_1_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdScene1); + camera_suite_view_camera_set_callback( + app->camera_suite_view_camera, camera_suite_view_camera_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdCamera); } -bool camera_suite_scene_style_1_on_event(void* context, SceneManagerEvent event) { +bool camera_suite_scene_camera_on_event(void* context, SceneManagerEvent event) { CameraSuite* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case CameraSuiteCustomEventSceneStyle1Left: - case CameraSuiteCustomEventSceneStyle1Right: - case CameraSuiteCustomEventSceneStyle1Up: - case CameraSuiteCustomEventSceneStyle1Down: - case CameraSuiteCustomEventSceneStyle1Ok: + case CameraSuiteCustomEventSceneCameraLeft: + case CameraSuiteCustomEventSceneCameraRight: + case CameraSuiteCustomEventSceneCameraUp: + case CameraSuiteCustomEventSceneCameraDown: + case CameraSuiteCustomEventSceneCameraOk: // Do nothing. break; - case CameraSuiteCustomEventSceneStyle1Back: + case CameraSuiteCustomEventSceneCameraBack: notification_message(app->notification, &sequence_reset_red); notification_message(app->notification, &sequence_reset_green); notification_message(app->notification, &sequence_reset_blue); @@ -46,7 +46,7 @@ bool camera_suite_scene_style_1_on_event(void* context, SceneManagerEvent event) return consumed; } -void camera_suite_scene_style_1_on_exit(void* context) { +void camera_suite_scene_camera_on_exit(void* context) { CameraSuite* app = context; UNUSED(app); } diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_config.h b/applications/external/camera_suite/scenes/camera_suite_scene_config.h index fca73ae16..2cb9245ef 100644 --- a/applications/external/camera_suite/scenes/camera_suite_scene_config.h +++ b/applications/external/camera_suite/scenes/camera_suite_scene_config.h @@ -1,6 +1,5 @@ ADD_SCENE(camera_suite, start, Start) ADD_SCENE(camera_suite, menu, Menu) -ADD_SCENE(camera_suite, style_1, Style_1) -ADD_SCENE(camera_suite, style_2, Style_2) +ADD_SCENE(camera_suite, camera, Camera) ADD_SCENE(camera_suite, guide, Guide) ADD_SCENE(camera_suite, settings, Settings) \ No newline at end of file diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_menu.c b/applications/external/camera_suite/scenes/camera_suite_scene_menu.c index 09c4dade7..ae37e11b6 100644 --- a/applications/external/camera_suite/scenes/camera_suite_scene_menu.c +++ b/applications/external/camera_suite/scenes/camera_suite_scene_menu.c @@ -1,10 +1,8 @@ #include "../camera_suite.h" enum SubmenuIndex { - /** Atkinson Dithering Algorithm. */ - SubmenuIndexSceneStyle1 = 10, - /** Floyd-Steinberg Dithering Algorithm. */ - SubmenuIndexSceneStyle2, + /** Camera. */ + SubmenuIndexSceneCamera = 10, /** Guide/how-to. */ SubmenuIndexGuide, /** Settings menu. */ @@ -22,16 +20,9 @@ void camera_suite_scene_menu_on_enter(void* context) { submenu_add_item( app->submenu, "Open Camera", - SubmenuIndexSceneStyle1, + SubmenuIndexSceneCamera, camera_suite_scene_menu_submenu_callback, app); - // Staged view for the future. - // submenu_add_item( - // app->submenu, - // "Test", - // SubmenuIndexSceneStyle2, - // camera_suite_scene_menu_submenu_callback, - // app); submenu_add_item( app->submenu, "Guide", SubmenuIndexGuide, camera_suite_scene_menu_submenu_callback, app); submenu_add_item( @@ -56,15 +47,10 @@ bool camera_suite_scene_menu_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(app->view_dispatcher); return true; } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSceneStyle1) { + if(event.event == SubmenuIndexSceneCamera) { scene_manager_set_scene_state( - app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneStyle1); - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneStyle_1); - return true; - } else if(event.event == SubmenuIndexSceneStyle2) { - scene_manager_set_scene_state( - app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneStyle2); - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneStyle_2); + app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneCamera); + scene_manager_next_scene(app->scene_manager, CameraSuiteSceneCamera); return true; } else if(event.event == SubmenuIndexGuide) { scene_manager_set_scene_state( diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_start.c b/applications/external/camera_suite/scenes/camera_suite_scene_start.c index 12591760c..0dda05ede 100644 --- a/applications/external/camera_suite/scenes/camera_suite_scene_start.c +++ b/applications/external/camera_suite/scenes/camera_suite_scene_start.c @@ -24,9 +24,9 @@ bool camera_suite_scene_start_on_event(void* context, SceneManagerEvent event) { switch(event.event) { case CameraSuiteCustomEventStartLeft: case CameraSuiteCustomEventStartRight: - break; case CameraSuiteCustomEventStartUp: case CameraSuiteCustomEventStartDown: + // Do nothing. break; case CameraSuiteCustomEventStartOk: scene_manager_next_scene(app->scene_manager, CameraSuiteSceneMenu); diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_style_2.c b/applications/external/camera_suite/scenes/camera_suite_scene_style_2.c deleted file mode 100644 index b74d33784..000000000 --- a/applications/external/camera_suite/scenes/camera_suite_scene_style_2.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../camera_suite.h" -#include "../helpers/camera_suite_custom_event.h" -#include "../helpers/camera_suite_haptic.h" -#include "../helpers/camera_suite_led.h" -#include "../views/camera_suite_view_style_2.h" - -void camera_suite_view_style_2_callback(CameraSuiteCustomEvent event, void* context) { - furi_assert(context); - CameraSuite* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void camera_suite_scene_style_2_on_enter(void* context) { - furi_assert(context); - CameraSuite* app = context; - camera_suite_view_style_2_set_callback( - app->camera_suite_view_style_2, camera_suite_view_style_2_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdScene2); -} - -bool camera_suite_scene_style_2_on_event(void* context, SceneManagerEvent event) { - CameraSuite* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case CameraSuiteCustomEventSceneStyle2Left: - case CameraSuiteCustomEventSceneStyle2Right: - case CameraSuiteCustomEventSceneStyle2Up: - case CameraSuiteCustomEventSceneStyle2Down: - case CameraSuiteCustomEventSceneStyle2Ok: - // Do nothing. - break; - case CameraSuiteCustomEventSceneStyle2Back: - 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, CameraSuiteSceneMenu)) { - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); - } - consumed = true; - break; - } - } - - return consumed; -} - -void camera_suite_scene_style_2_on_exit(void* context) { - CameraSuite* app = context; - UNUSED(app); -} diff --git a/applications/external/camera_suite/views/camera_suite_view_style_1.c b/applications/external/camera_suite/views/camera_suite_view_camera.c similarity index 70% rename from applications/external/camera_suite/views/camera_suite_view_style_1.c rename to applications/external/camera_suite/views/camera_suite_view_camera.c index 6d16d0231..94b8a8639 100644 --- a/applications/external/camera_suite/views/camera_suite_view_style_1.c +++ b/applications/external/camera_suite/views/camera_suite_view_camera.c @@ -8,23 +8,19 @@ #include "../helpers/camera_suite_speaker.h" #include "../helpers/camera_suite_led.h" -static CameraSuiteViewStyle1* current_instance = NULL; -// Dithering type: -// 0 = Floyd Steinberg (default) -// 1 = Atkinson -static int current_dithering = 0; +static CameraSuiteViewCamera* current_instance = NULL; -struct CameraSuiteViewStyle1 { - CameraSuiteViewStyle1Callback callback; +struct CameraSuiteViewCamera { + CameraSuiteViewCameraCallback callback; FuriStreamBuffer* rx_stream; FuriThread* worker_thread; View* view; void* context; }; -void camera_suite_view_style_1_set_callback( - CameraSuiteViewStyle1* instance, - CameraSuiteViewStyle1Callback callback, +void camera_suite_view_camera_set_callback( + CameraSuiteViewCamera* instance, + CameraSuiteViewCameraCallback callback, void* context) { furi_assert(instance); furi_assert(callback); @@ -32,7 +28,29 @@ void camera_suite_view_style_1_set_callback( instance->context = context; } -static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model) { +// Function to draw pixels on the canvas based on camera orientation +static void draw_pixel_by_orientation(Canvas* canvas, uint8_t x, uint8_t y, uint8_t orientation) { + switch(orientation) { + case 0: // Camera rotated 0 degrees (right side up, default) + canvas_draw_dot(canvas, x, y); + break; + case 1: // Camera rotated 90 degrees + canvas_draw_dot(canvas, y, FRAME_WIDTH - 1 - x); + break; + case 2: // Camera rotated 180 degrees (upside down) + canvas_draw_dot(canvas, FRAME_WIDTH - 1 - x, FRAME_HEIGHT - 1 - y); + break; + case 3: // Camera rotated 270 degrees + canvas_draw_dot(canvas, FRAME_HEIGHT - 1 - y, x); + break; + default: + break; + } +} + +static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) { + UartDumpModel* model = _model; + // Clear the screen. canvas_set_color(canvas, ColorBlack); @@ -41,60 +59,17 @@ static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model) CameraSuite* app = current_instance->context; - // Draw the pixels with rotation. for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) { uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15 uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63 - // Apply rotation - int16_t rotated_x, rotated_y; - switch(app->orientation) { - case 1: // 90 degrees - rotated_x = y; - rotated_y = FRAME_WIDTH - 1 - x; - break; - case 2: // 180 degrees - rotated_x = FRAME_WIDTH - 1 - x; - rotated_y = FRAME_HEIGHT - 1 - y; - break; - case 3: // 270 degrees - rotated_x = FRAME_HEIGHT - 1 - y; - rotated_y = x; - break; - case 0: // 0 degrees - default: - rotated_x = x; - rotated_y = y; - break; - } - for(uint8_t i = 0; i < 8; ++i) { - if((model->pixels[p] & (1 << i)) != 0) { - // Adjust the coordinates based on the new screen dimensions - uint16_t screen_x, screen_y; - switch(app->orientation) { - case 1: // 90 degrees - screen_x = rotated_x; - screen_y = FRAME_HEIGHT - 8 + (rotated_y * 8) + i; - break; - case 2: // 180 degrees - screen_x = FRAME_WIDTH - 8 + (rotated_x * 8) + i; - screen_y = FRAME_HEIGHT - 1 - rotated_y; - break; - case 3: // 270 degrees - screen_x = FRAME_WIDTH - 1 - rotated_x; - screen_y = rotated_y * 8 + i; - break; - case 0: // 0 degrees - default: - screen_x = rotated_x * 8 + i; - screen_y = rotated_y; - break; - } - canvas_draw_dot(canvas, screen_x, screen_y); + if((model->pixels[p] & (1 << (7 - i))) != 0) { + draw_pixel_by_orientation(canvas, (x * 8) + i, y, app->orientation); } } } + // Draw the guide if the camera is not initialized. if(!model->initialized) { canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48); @@ -107,15 +82,15 @@ static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model) } } -static void camera_suite_view_style_1_model_init(UartDumpModel* const model) { +static void camera_suite_view_camera_model_init(UartDumpModel* const model) { for(size_t i = 0; i < FRAME_BUFFER_LENGTH; i++) { model->pixels[i] = 0; } } -static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { +static bool camera_suite_view_camera_input(InputEvent* event, void* context) { furi_assert(context); - CameraSuiteViewStyle1* instance = context; + CameraSuiteViewCamera* instance = context; if(event->type == InputTypeRelease) { switch(event->key) { default: // Stop all sounds, reset the LED. @@ -144,7 +119,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { UartDumpModel * model, { UNUSED(model); - instance->callback(CameraSuiteCustomEventSceneStyle1Back, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraBack, instance->context); }, true); break; @@ -159,7 +134,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Left, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraLeft, instance->context); }, true); break; @@ -174,7 +149,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Right, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraRight, instance->context); }, true); break; @@ -189,7 +164,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Up, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraUp, instance->context); }, true); break; @@ -204,18 +179,13 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Down, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraDown, instance->context); }, true); break; case InputKeyOk: - if(current_dithering == 0) { - data[0] = 'd'; // Update to Floyd Steinberg dithering. - current_dithering = 1; - } else { - data[0] = 'D'; // Update to Atkinson dithering. - current_dithering = 0; - } + // Switch dithering types. + data[0] = 'D'; with_view_model( instance->view, UartDumpModel * model, @@ -224,7 +194,7 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { camera_suite_play_happy_bump(instance->context); camera_suite_play_input_sound(instance->context); camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneStyle1Ok, instance->context); + instance->callback(CameraSuiteCustomEventSceneCameraOk, instance->context); }, true); break; @@ -232,21 +202,21 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { break; } // Send `data` to the ESP32-CAM - furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + furi_hal_uart_tx(UART_CH, data, 1); } return true; } -static void camera_suite_view_style_1_exit(void* context) { +static void camera_suite_view_camera_exit(void* context) { furi_assert(context); } -static void camera_suite_view_style_1_enter(void* context) { +static void camera_suite_view_camera_enter(void* context) { // Check `context` for null. If it is null, abort program, else continue. furi_assert(context); - // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`. - CameraSuiteViewStyle1* instance = (CameraSuiteViewStyle1*)context; + // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`. + CameraSuiteViewCamera* instance = (CameraSuiteViewCamera*)context; // Assign the current instance to the global variable current_instance = instance; @@ -254,12 +224,12 @@ static void camera_suite_view_style_1_enter(void* context) { uint8_t data[1]; data[0] = 'S'; // Uppercase `S` to start the camera // Send `data` to the ESP32-CAM - furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + furi_hal_uart_tx(UART_CH, data, 1); with_view_model( instance->view, UartDumpModel * model, - { camera_suite_view_style_1_model_init(model); }, + { camera_suite_view_camera_model_init(model); }, true); } @@ -267,8 +237,8 @@ static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* cont // Check `context` for null. If it is null, abort program, else continue. furi_assert(context); - // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`. - CameraSuiteViewStyle1* instance = context; + // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`. + CameraSuiteViewCamera* instance = context; // If `uartIrqEvent` is `UartIrqEventRXNE`, send the data to the // `rx_stream` and set the `WorkerEventRx` flag. @@ -319,7 +289,7 @@ static void process_ringbuffer(UartDumpModel* model, uint8_t byte) { static int32_t camera_worker(void* context) { furi_assert(context); - CameraSuiteViewStyle1* instance = context; + CameraSuiteViewCamera* instance = context; while(1) { uint32_t events = @@ -348,14 +318,17 @@ static int32_t camera_worker(void* context) { false); } } while(length > 0); + + with_view_model( + instance->view, UartDumpModel * model, { UNUSED(model); }, true); } } return 0; } -CameraSuiteViewStyle1* camera_suite_view_style_1_alloc() { - CameraSuiteViewStyle1* instance = malloc(sizeof(CameraSuiteViewStyle1)); +CameraSuiteViewCamera* camera_suite_view_camera_alloc() { + CameraSuiteViewCamera* instance = malloc(sizeof(CameraSuiteViewCamera)); instance->view = view_alloc(); @@ -364,38 +337,50 @@ CameraSuiteViewStyle1* camera_suite_view_style_1_alloc() { // Set up views view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel)); view_set_context(instance->view, instance); // furi_assert crashes in events without this - view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_style_1_draw); - view_set_input_callback(instance->view, camera_suite_view_style_1_input); - view_set_enter_callback(instance->view, camera_suite_view_style_1_enter); - view_set_exit_callback(instance->view, camera_suite_view_style_1_exit); + view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_camera_draw); + view_set_input_callback(instance->view, camera_suite_view_camera_input); + view_set_enter_callback(instance->view, camera_suite_view_camera_enter); + view_set_exit_callback(instance->view, camera_suite_view_camera_exit); with_view_model( instance->view, UartDumpModel * model, - { camera_suite_view_style_1_model_init(model); }, + { camera_suite_view_camera_model_init(model); }, true); instance->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance); furi_thread_start(instance->worker_thread); // Enable uart listener - furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, 230400); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, camera_on_irq_cb, instance); + if(UART_CH == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH, 230400); + } + furi_hal_uart_set_br(UART_CH, 230400); + furi_hal_uart_set_irq_cb(UART_CH, camera_on_irq_cb, instance); return instance; } -void camera_suite_view_style_1_free(CameraSuiteViewStyle1* instance) { +void camera_suite_view_camera_free(CameraSuiteViewCamera* instance) { furi_assert(instance); with_view_model( instance->view, UartDumpModel * model, { UNUSED(model); }, true); view_free(instance->view); free(instance); + + furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); + + if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH); + } else { + furi_hal_console_enable(); + } } -View* camera_suite_view_style_1_get_view(CameraSuiteViewStyle1* instance) { +View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* instance) { furi_assert(instance); return instance->view; } diff --git a/applications/external/camera_suite/views/camera_suite_view_style_1.h b/applications/external/camera_suite/views/camera_suite_view_camera.h similarity index 54% rename from applications/external/camera_suite/views/camera_suite_view_style_1.h rename to applications/external/camera_suite/views/camera_suite_view_camera.h index c460c94ba..4e2f29ddc 100644 --- a/applications/external/camera_suite/views/camera_suite_view_style_1.h +++ b/applications/external/camera_suite/views/camera_suite_view_camera.h @@ -14,16 +14,23 @@ #include #include +#include + +#define UART_CH \ + (XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) + #pragma once #define FRAME_WIDTH 128 #define FRAME_HEIGHT 64 #define FRAME_BIT_DEPTH 1 -#define FRAME_BUFFER_LENGTH \ - (FRAME_WIDTH * FRAME_HEIGHT * FRAME_BIT_DEPTH / 8) // 128*64*1 / 8 = 1024 -#define ROW_BUFFER_LENGTH (FRAME_WIDTH / 8) // 128/8 = 16 -#define RING_BUFFER_LENGTH (ROW_BUFFER_LENGTH + 3) // ROW_BUFFER_LENGTH + Header => 16 + 3 = 19 -#define LAST_ROW_INDEX (FRAME_BUFFER_LENGTH - ROW_BUFFER_LENGTH) // 1024 - 16 = 1008 +#define FRAME_BUFFER_LENGTH 1024 +#define ROW_BUFFER_LENGTH 16 +#define RING_BUFFER_LENGTH 19 +#define LAST_ROW_INDEX 1008 + +extern const Icon I_DolphinCommon_56x48; typedef struct UartDumpModel UartDumpModel; @@ -35,20 +42,20 @@ struct UartDumpModel { uint8_t row_ringbuffer[RING_BUFFER_LENGTH]; }; -typedef struct CameraSuiteViewStyle1 CameraSuiteViewStyle1; +typedef struct CameraSuiteViewCamera CameraSuiteViewCamera; -typedef void (*CameraSuiteViewStyle1Callback)(CameraSuiteCustomEvent event, void* context); +typedef void (*CameraSuiteViewCameraCallback)(CameraSuiteCustomEvent event, void* context); -void camera_suite_view_style_1_set_callback( - CameraSuiteViewStyle1* camera_suite_view_style_1, - CameraSuiteViewStyle1Callback callback, +void camera_suite_view_camera_set_callback( + CameraSuiteViewCamera* camera_suite_view_camera, + CameraSuiteViewCameraCallback callback, void* context); -CameraSuiteViewStyle1* camera_suite_view_style_1_alloc(); +CameraSuiteViewCamera* camera_suite_view_camera_alloc(); -void camera_suite_view_style_1_free(CameraSuiteViewStyle1* camera_suite_static); +void camera_suite_view_camera_free(CameraSuiteViewCamera* camera_suite_static); -View* camera_suite_view_style_1_get_view(CameraSuiteViewStyle1* camera_suite_static); +View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* camera_suite_static); typedef enum { // Reserved for StreamBuffer internal event diff --git a/applications/external/camera_suite/views/camera_suite_view_style_2.c b/applications/external/camera_suite/views/camera_suite_view_style_2.c deleted file mode 100644 index 0a7ac0b83..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_style_2.c +++ /dev/null @@ -1,249 +0,0 @@ -#include "../camera_suite.h" -#include -#include -#include -#include -#include -#include "../helpers/camera_suite_haptic.h" -#include "../helpers/camera_suite_speaker.h" -#include "../helpers/camera_suite_led.h" - -struct CameraSuiteViewStyle2 { - View* view; - CameraSuiteViewStyle2Callback callback; - void* context; -}; - -typedef struct { - int screen_text; -} CameraSuiteViewStyle2Model; - -char buttonText[11][14] = { - "", - "Press Up", - "Press Down", - "Press Left", - "Press Right", - "Press Ok", - "Release Up", - "Release Down", - "Release Left", - "Release Right", - "Release Ok", -}; - -void camera_suite_view_style_2_set_callback( - CameraSuiteViewStyle2* instance, - CameraSuiteViewStyle2Callback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - instance->callback = callback; - instance->context = context; -} - -void camera_suite_view_style_2_draw(Canvas* canvas, CameraSuiteViewStyle2Model* model) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, "Scene 2: Input Examples"); - canvas_set_font(canvas, FontSecondary); - char* strInput = malloc(15); - strcpy(strInput, buttonText[model->screen_text]); - canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, strInput); - free(strInput); -} - -static void camera_suite_view_style_2_model_init(CameraSuiteViewStyle2Model* const model) { - model->screen_text = 0; -} - -bool camera_suite_view_style_2_input(InputEvent* event, void* context) { - furi_assert(context); - CameraSuiteViewStyle2* instance = context; - if(event->type == InputTypeRelease) { - switch(event->key) { - case InputKeyBack: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - UNUSED(model); - camera_suite_stop_all_sound(instance->context); - instance->callback(CameraSuiteCustomEventSceneStyle2Back, instance->context); - camera_suite_play_long_bump(instance->context); - }, - true); - break; - case InputKeyUp: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 6; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 255, 0, 255); - }, - true); - break; - case InputKeyDown: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 7; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 255, 255, 0); - }, - true); - break; - case InputKeyLeft: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 8; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 0, 255, 255); - }, - true); - break; - case InputKeyRight: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 9; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 255, 0, 0); - }, - true); - break; - case InputKeyOk: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 10; - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 255, 255, 255); - }, - true); - break; - case InputKeyMAX: - break; - } - } else if(event->type == InputTypePress) { - switch(event->key) { - case InputKeyUp: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 1; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyDown: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 2; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyLeft: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 3; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyRight: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 4; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyOk: - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { - model->screen_text = 5; - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - }, - true); - break; - case InputKeyBack: - case InputKeyMAX: - break; - } - } - - return true; -} - -void camera_suite_view_style_2_exit(void* context) { - furi_assert(context); - CameraSuite* app = context; - camera_suite_stop_all_sound(app); - //camera_suite_led_reset(app); -} - -void camera_suite_view_style_2_enter(void* context) { - furi_assert(context); - dolphin_deed(DolphinDeedPluginStart); -} - -CameraSuiteViewStyle2* camera_suite_view_style_2_alloc() { - CameraSuiteViewStyle2* instance = malloc(sizeof(CameraSuiteViewStyle2)); - instance->view = view_alloc(); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(CameraSuiteViewStyle2Model)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_style_2_draw); - view_set_input_callback(instance->view, camera_suite_view_style_2_input); - //view_set_enter_callback(instance->view, camera_suite_view_style_2_enter); - view_set_exit_callback(instance->view, camera_suite_view_style_2_exit); - - with_view_model( - instance->view, - CameraSuiteViewStyle2Model * model, - { camera_suite_view_style_2_model_init(model); }, - true); - - return instance; -} - -void camera_suite_view_style_2_free(CameraSuiteViewStyle2* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* camera_suite_view_style_2_get_view(CameraSuiteViewStyle2* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/camera_suite/views/camera_suite_view_style_2.h b/applications/external/camera_suite/views/camera_suite_view_style_2.h deleted file mode 100644 index d24b895f8..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_style_2.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "../helpers/camera_suite_custom_event.h" - -typedef struct CameraSuiteViewStyle2 CameraSuiteViewStyle2; - -typedef void (*CameraSuiteViewStyle2Callback)(CameraSuiteCustomEvent event, void* context); - -void camera_suite_view_style_2_set_callback( - CameraSuiteViewStyle2* instance, - CameraSuiteViewStyle2Callback callback, - void* context); - -CameraSuiteViewStyle2* camera_suite_view_style_2_alloc(); - -void camera_suite_view_style_2_free(CameraSuiteViewStyle2* camera_suite_static); - -View* camera_suite_view_style_2_get_view(CameraSuiteViewStyle2* boilerpate_static); diff --git a/applications/external/text_viewer/LICENSE b/applications/external/chess/LICENSE similarity index 96% rename from applications/external/text_viewer/LICENSE rename to applications/external/chess/LICENSE index 69004dc62..61361ecc7 100644 --- a/applications/external/text_viewer/LICENSE +++ b/applications/external/chess/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Roman Shchekin +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 diff --git a/applications/external/chess/application.fam b/applications/external/chess/application.fam new file mode 100644 index 000000000..6f3d3321c --- /dev/null +++ b/applications/external/chess/application.fam @@ -0,0 +1,18 @@ +App( + appid="chess", + name="Chess", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipchess_app", + requires=[ + "gui", + ], + stack_size=4 * 1024, + fap_icon="flipchess_10px.png", + fap_icon_assets="icons", + fap_icon_assets_symbol="flipchess", + fap_category="Games", + fap_author="Struan Clark (xtruan)", + fap_weburl="https://github.com/xtruan/flipper-chess", + fap_version=(1, 9), + fap_description="Chess for Flipper", +) diff --git a/applications/external/chess/chess/smallchesslib.h b/applications/external/chess/chess/smallchesslib.h new file mode 100644 index 000000000..61b0aecfd --- /dev/null +++ b/applications/external/chess/chess/smallchesslib.h @@ -0,0 +1,3492 @@ +#ifndef SMALLCHESSLIB_H +#define SMALLCHESSLIB_H + +/** + @file smallchesslib.h + + Small and simple single header C99 public domain chess library and engine. + + author: Miloslav Ciz (drummyfish) + license: CC0 1.0 (public domain) + found at https://creativecommons.org/publicdomain/zero/1.0/ + + additional waiver of all IP + version: 0.8d + + Default notation format for this library is a coordinate one, i.e. + + squarefrom squareto [promotedpiece] + + e.g.: e2e4 or A2A1q + + This work's goal is to never be encumbered by any exclusive intellectual + property rights. The work is therefore provided under CC0 1.0 + additional + WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of + intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL + INTELLECTUAL PROPERTY RGHTS is as follows: + + Each contributor to this work agrees that they waive any exclusive rights, + including but not limited to copyright, patents, trademark, trade dress, + industrial design, plant varieties and trade secrets, to any and all ideas, + concepts, processes, discoveries, improvements and inventions conceived, + discovered, made, designed, researched or developed by the contributor either + solely or jointly with others, which relate to this work or result from this + work. Should any waiver of such right be judged legally invalid or + ineffective under applicable law, the contributor hereby grants to each + affected person a royalty-free, non transferable, non sublicensable, non + exclusive, irrevocable and unconditional license to this right. +*/ + +#include + +#ifndef SCL_DEBUG_AI +/** AI will print out a Newick-like tree of searched moves. */ +#define SCL_DEBUG_AI 0 +#endif + +/** + Maximum number of moves a chess piece can have (a queen in the middle of the + board). +*/ +#define SCL_CHESS_PIECE_MAX_MOVES 25 +#define SCL_BOARD_SQUARES 64 + +typedef uint8_t (*SCL_RandomFunction)(void); + +#if SCL_COUNT_EVALUATED_POSITIONS +uint32_t SCL_positionsEvaluated = 0; /**< If enabled by + SCL_COUNT_EVALUATED_POSITIONS, this + will increment with every + dynamically evaluated position (e.g. + when AI computes its move). */ +#endif + +#ifndef SCL_CALL_WDT_RESET +#define SCL_CALL_WDT_RESET \ + 0 /**< Option that should be enabled on some + Arduinos. If 1, call to watchdog timer + reset will be performed during dynamic + evaluation (without it if AI takes long the + program will reset). */ +#endif + +/** + Returns a pseudorandom byte. This function has a period 256 and returns each + possible byte value exactly once in the period. +*/ +uint8_t SCL_randomSimple(void); +void SCL_randomSimpleSeed(uint8_t seed); + +/** + Like SCL_randomSimple, but internally uses a 16 bit value, so the period is + 65536. +*/ +uint8_t SCL_randomBetter(void); +void SCL_randomBetterSeed(uint16_t seed); + +#ifndef SCL_EVALUATION_FUNCTION +/** + If defined, AI will always use the static evaluation function with this + name. This helps avoid pointers to functions and can be faster but the + function can't be changed at runtime. + */ +#define SCL_EVALUATION_FUNCTION +#undef SCL_EVALUATION_FUNCTION +#endif + +#ifndef SCL_960_CASTLING +/** + If set, chess 960 (Fisher random) castling will be considered by the library + rather than normal castling. 960 castling is slightly different (e.g. + requires the inital rook positions to be stored in board state). The + castling move is performed as "capturing own rook". + */ +#define SCL_960_CASTLING 0 +#endif + +#ifndef SCL_ALPHA_BETA +/** + Turns alpha-beta pruning (AI optimization) on or off. This can gain + performance and should normally be turned on. AI behavior should not + change at all. + */ +#define SCL_ALPHA_BETA 1 +#endif + +/** + A set of game squares as a bit array, each bit representing one game square. + Useful for representing e.g. possible moves. To easily iterate over the set + use provided macros (SCL_SQUARE_SET_ITERATE, ...). +*/ +typedef uint8_t SCL_SquareSet[8]; + +#define SCL_SQUARE_SET_EMPTY \ + { 0, 0, 0, 0, 0, 0, 0, 0 } + +void SCL_squareSetClear(SCL_SquareSet squareSet); +void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square); +uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square); +uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet); +uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet); + +/** + Returns a random square from a square set. +*/ +uint8_t SCL_squareSetGetRandom(const SCL_SquareSet squareSet, SCL_RandomFunction randFunc); + +#define SCL_SQUARE_SET_ITERATE_BEGIN(squareSet) \ + { \ + uint8_t iteratedSquare = 0; \ + uint8_t iterationEnd = 0; \ + for(int8_t _i = 0; _i < 8 && !iterationEnd; ++_i) { \ + uint8_t _row = squareSet[_i]; \ + if(_row == 0) { \ + iteratedSquare += 8; \ + continue; \ + } \ + \ + for(uint8_t _j = 0; _j < 8 && !iterationEnd; ++_j) { \ + if(_row & 0x01) { +/* + Between SCL_SQUARE_SET_ITERATE_BEGIN and _END iteratedSquare variable + represents the next square contained in the set. To break out of the + iteration set iterationEnd to 1. +*/ + +#define SCL_SQUARE_SET_ITERATE_END \ + } \ + _row >>= 1; \ + iteratedSquare++; \ + } \ + } /*for*/ \ + } + +#define SCL_SQUARE_SET_ITERATE(squareSet, command) \ + SCL_SQUARE_SET_ITERATE_BEGIN(squareSet){command} SCL_SQUARE_SET_ITERATE_END + +#define SCL_BOARD_STATE_SIZE 69 + +/** + Represents chess board state as a string in this format: + - First 64 characters represent the chess board (A1, B1, ... H8), each field + can be either a piece (PRNBKQprnbkq) or empty ('.'). I.e. the state looks + like this: + + 0 (A1) RNBQKBNR + PPPPPPPP + ........ + ........ + ........ + ........ + pppppppp + rnbqkbnr 63 (H8) + + - After this more bytes follow to represent global state, these are: + - 64: bits holding en-passant and castling related information: + - bits 0-3 (lsb): Column of the pawn that can, in current turn, be + taken by en-passant (0xF means no pawn can be taken this way). + - bit 4: Whether white is not prevented from short castling by previous + king or rook movement. + - bit 5: Same as 4, but for long castling. + - bit 6: Same as 4, but for black. + - bit 7: Same as 4, but for black and long castling. + - 65: Number saying the number of ply (half-moves) that have already been + played, also determining whose turn it currently is. + - 66: Move counter used in the 50 move rule, says the number of ply since + the last pawn move or capture. + - 67: Extra byte, left for storing additional info in variants. For normal + chess this byte should always be 0. + - 68: The last byte is always 0 to properly terminate the string in case + someone tries to print it. + - The state is designed so as to be simple and also print-friendly, i.e. you + can simply print it with line break after 8 characters to get a human + readable representation of the board. + + NOTE: there is a much more compact representation which however trades some + access speed which would affect the AI performance and isn't print friendly, + so we don't use it. In it each square takes 4 bits, using 15 out of 16 + possible values (empty square and W and B pieces including 2 types of pawns, + one "en-passant takeable"). Then only one extra byte needed is for castling + info (4 bits) and ply count (4 bits). +*/ +typedef char SCL_Board[SCL_BOARD_STATE_SIZE]; + +#define SCL_BOARD_ENPASSANT_CASTLE_BYTE 64 +#define SCL_BOARD_PLY_BYTE 65 +#define SCL_BOARD_MOVE_COUNT_BYTE 66 +#define SCL_BOARD_EXTRA_BYTE 67 + +#if SCL_960_CASTLING +#define _SCL_EXTRA_BYTE_VALUE (0 | (7 << 3)) // rooks on classic positions +#else +#define _SCL_EXTRA_BYTE_VALUE 0 +#endif + +#define SCL_BOARD_START_STATE \ + { \ + 82, 78, 66, 81, 75, 66, 78, 82, 80, 80, 80, 80, 80, 80, 80, 80, 46, 46, 46, 46, 46, 46, \ + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, \ + 46, 46, 46, 46, 46, 112, 112, 112, 112, 112, 112, 112, 112, 114, 110, 98, 113, 107, \ + 98, 110, 114, (char)0xff, 0, 0, _SCL_EXTRA_BYTE_VALUE, 0 \ + } + +#define SCL_FEN_START "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" + +#define SCL_FEN_HORDE "ppp2ppp/pppppppp/pppppppp/pppppppp/3pp3/8/PPPPPPPP/RNBQKBNR w KQ - 0 1" + +#define SCL_FEN_UPSIDE_DOWN "RNBKQBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbkqbnr w - - 0 1" + +#define SCL_FEN_PEASANT_REVOLT "1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - - 0 1" + +#define SCL_FEN_ENDGAME "4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1" + +#define SCL_FEN_KNIGHTS "N6n/1N4n1/2N2n2/3Nn3/k2nN2K/2n2N2/1n4N1/n6N w - - 0 1" + +/** + Holds an info required to undo a single move. +*/ +typedef struct { + uint8_t squareFrom; ///< start square + uint8_t squareTo; ///< target square + char enPassantCastle; ///< previous en passant/castle byte + char moveCount; ///< previous values of the move counter byte + uint8_t other; /**< lowest 7 bits: previous value of target square, + highest bit: if 1 then the move was promotion or + en passant */ +} SCL_MoveUndo; + +#define SCL_GAME_STATE_PLAYING 0x00 +#define SCL_GAME_STATE_WHITE_WIN 0x01 +#define SCL_GAME_STATE_BLACK_WIN 0x02 +#define SCL_GAME_STATE_DRAW 0x10 ///< further unspecified draw +#define SCL_GAME_STATE_DRAW_STALEMATE 0x11 ///< draw by stalemate +#define SCL_GAME_STATE_DRAW_REPETITION 0x12 ///< draw by repetition +#define SCL_GAME_STATE_DRAW_50 0x13 ///< draw by 50 move rule +#define SCL_GAME_STATE_DRAW_DEAD 0x14 ///< draw by dead position +#define SCL_GAME_STATE_END 0xff ///< end without known result + +/** + Converts square in common notation (e.g. 'c' 8) to square number. Only accepts + lowercase column. +*/ +#define SCL_SQUARE(colChar, rowInt) (((rowInt)-1) * 8 + ((colChar) - 'a')) +#define SCL_S(c, r) SCL_SQUARE(c, r) + +void SCL_boardInit(SCL_Board board); +void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo); + +/** + Initializes given chess 960 (Fisher random) position. If SCL_960_CASTLING + is not set, castling will be disabled by this function. +*/ +void SCL_boardInit960(SCL_Board board, uint16_t positionNumber); + +void SCL_boardDisableCastling(SCL_Board board); + +uint32_t SCL_boardHash32(const SCL_Board board); + +#define SCL_PHASE_OPENING 0 +#define SCL_PHASE_MIDGAME 1 +#define SCL_PHASE_ENDGAME 2 + +/** + Estimates the game phase: opening, midgame or endgame. +*/ +uint8_t SCL_boardEstimatePhase(SCL_Board board); + +/** + Sets the board position. The input string should be 64 characters long zero + terminated C string representing the board as squares A1, A2, ..., H8 with + each char being either a piece (RKBKQPrkbkqp) or an empty square ('.'). +*/ +void SCL_boardSetPosition( + SCL_Board board, + const char* pieces, + uint8_t castlingEnPassant, + uint8_t moveCount, + uint8_t ply); + +uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2); + +/** + Gets a random move on given board for the player whose move it is. +*/ +void SCL_boardRandomMove( + SCL_Board board, + SCL_RandomFunction randFunc, + uint8_t* squareFrom, + uint8_t* squareTo, + char* resultProm); + +#define SCL_FEN_MAX_LENGTH 90 + +/** + Converts a position to FEN (Forsyth–Edwards Notation) string. The string has + to have at least SCL_FEN_MAX_LENGTH bytes allocated to guarantee the + function won't write to unallocated memory. The string will be terminated by + 0 (this is included in SCL_FEN_MAX_LENGTH). The number of bytes written + (including the terminating 0) is returned. +*/ +uint8_t SCL_boardToFEN(SCL_Board board, char* string); + +/** + Loads a board from FEN (Forsyth–Edwards Notation) string. Returns 1 on + success, 0 otherwise. XFEN isn't supported fully but a start position in + chess960 can be loaded with this function. +*/ +uint8_t SCL_boardFromFEN(SCL_Board board, const char* string); + +/** + Returns an approximate/heuristic board rating as a number, 0 meaning equal + chances for both players, positive favoring white, negative favoring black. +*/ +typedef int16_t (*SCL_StaticEvaluationFunction)(SCL_Board); + +/* + NOTE: int8_t as a return value was tried for evaluation function, which would + be simpler, but it fails to capture important non-material position + differences counted in fractions of pawn values, hence we have to use int16_t. +*/ + +/** + Basic static evaluation function. WARNING: this function supposes a standard + chess game, for non-standard positions it may either not work well or even + crash the program. You should use a different function for non-standard games. +*/ +int16_t SCL_boardEvaluateStatic(SCL_Board board); + +/** + Dynamic evaluation function (search), i.e. unlike SCL_boardEvaluateStatic, + this one performs a recursive search for deeper positions to get a more + accurate score. Of course, this is much slower and hugely dependent on + baseDepth (you mostly want to keep this under 5). +*/ +int16_t SCL_boardEvaluateDynamic( + SCL_Board board, + uint8_t baseDepth, + uint8_t extensionExtraDepth, + SCL_StaticEvaluationFunction evalFunction); + +#define SCL_EVALUATION_MAX_SCORE 32600 // don't increase this, we need a margin + +/** + Checks if the board position is dead, i.e. mate is impossible (e.g. due to + insufficient material), which by the rules results in a draw. WARNING: This + function may fail to detect some dead positions as this is a non-trivial task. +*/ +uint8_t SCL_boardDead(SCL_Board board); + +/** + Tests whether given player is in check. +*/ +uint8_t SCL_boardCheck(SCL_Board board, uint8_t white); + +/** + Checks whether given move resets the move counter (used in the 50 move rule). +*/ +uint8_t SCL_boardMoveResetsCount(SCL_Board board, uint8_t squareFrom, uint8_t squareTo); + +uint8_t SCL_boardMate(SCL_Board board); + +/** + Performs a move on a board WITHOUT checking if the move is legal. Returns an + info with which the move can be undone. +*/ +SCL_MoveUndo + SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo, char promotePiece); + +void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo); + +/** + Checks if the game is over, i.e. the current player to move has no legal + moves, the game is in dead position etc. +*/ +uint8_t SCL_boardGameOver(SCL_Board board); + +/** + Checks if given move is legal. +*/ +uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom, uint8_t squareTo); + +/** + Checks if the player to move has at least one legal move. +*/ +uint8_t SCL_boardMovePossible(SCL_Board board); + +#define SCL_POSITION_NORMAL 0x00 +#define SCL_POSITION_CHECK 0x01 +#define SCL_POSITION_MATE 0x02 +#define SCL_POSITION_STALEMATE 0x03 +#define SCL_POSITION_DEAD 0x04 + +uint8_t SCL_boardGetPosition(SCL_Board board); + +/** + Returns 1 if the square is attacked by player of given color. This is used to + examine checks, so for performance reasons the functions only checks whether + or not the square is attacked (not the number of attackers). +*/ +uint8_t SCL_boardSquareAttacked(SCL_Board board, uint8_t square, uint8_t byWhite); + +/** + Gets pseudo moves of a piece: all possible moves WITHOUT eliminating moves + that lead to own check. To get only legal moves use SCL_boardGetMoves. +*/ +void SCL_boardGetPseudoMoves( + SCL_Board board, + uint8_t pieceSquare, + uint8_t checkCastling, + SCL_SquareSet result); + +/** + Gets all legal moves of given piece. +*/ +void SCL_boardGetMoves(SCL_Board board, uint8_t pieceSquare, SCL_SquareSet result); + +static inline uint8_t SCL_boardWhitesTurn(SCL_Board board); + +static inline uint8_t SCL_pieceIsWhite(char piece); +static inline uint8_t SCL_squareIsWhite(uint8_t square); +char SCL_pieceToColor(uint8_t piece, uint8_t toWhite); + +/** + Converts square coordinates to square number. Each coordinate must be a number + <1,8>. Validity of the coordinates is NOT checked. +*/ +static inline uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column); + +#ifndef SCL_VALUE_PAWN +#define SCL_VALUE_PAWN 256 +#endif + +#ifndef SCL_VALUE_KNIGHT +#define SCL_VALUE_KNIGHT 768 +#endif + +#ifndef SCL_VALUE_BISHOP +#define SCL_VALUE_BISHOP 800 +#endif + +#ifndef SCL_VALUE_ROOK +#define SCL_VALUE_ROOK 1280 +#endif + +#ifndef SCL_VALUE_QUEEN +#define SCL_VALUE_QUEEN 2304 +#endif + +#ifndef SCL_VALUE_KING +#define SCL_VALUE_KING 0 +#endif + +#define SCL_ENDGAME_MATERIAL_LIMIT \ + (2 * \ + (SCL_VALUE_PAWN * 4 + SCL_VALUE_QUEEN + SCL_VALUE_KING + SCL_VALUE_ROOK + SCL_VALUE_KNIGHT)) + +#define SCL_START_MATERIAL \ + (16 * SCL_VALUE_PAWN + 4 * SCL_VALUE_ROOK + 4 * SCL_VALUE_KNIGHT + 4 * SCL_VALUE_BISHOP + \ + 2 * SCL_VALUE_QUEEN + 2 * SCL_VALUE_KING) + +#ifndef SCL_RECORD_MAX_LENGTH +#define SCL_RECORD_MAX_LENGTH 256 +#endif + +#define SCL_RECORD_MAX_SIZE (SCL_RECORD_MAX_LENGTH * 2) + +/** + Records a single chess game. The format is following: + + Each record item consists of 2 bytes which record a single move (ply): + + abxxxxxx cdyyyyyy + + xxxxxx Start square of the move, counted as A0, A1, ... + yyyyyy End square of the move in the same format as the start square. + ab 00 means this move isn't the last move of the game, other possible + values are 01: white wins, 10: black wins, 11: draw or end for + other reasons. + cd In case of pawn promotion move this encodes the promoted piece as + 00: queen, 01: rook, 10: bishop, 11: knight (pawn isn't allowed by + chess rules). + + Every record should be ended by an ending move (ab != 00), empty record should + have one move where xxxxxx == yyyyyy == 0 and ab == 11. +*/ +typedef uint8_t SCL_Record[SCL_RECORD_MAX_SIZE]; + +#define SCL_RECORD_CONT 0x00 +#define SCL_RECORD_W_WIN 0x40 +#define SCL_RECORD_B_WIN 0x80 +#define SCL_RECORD_END 0xc0 + +#define SCL_RECORD_PROM_Q 0x00 +#define SCL_RECORD_PROM_R 0x40 +#define SCL_RECORD_PROM_B 0x80 +#define SCL_RECORD_PROM_N 0xc0 + +#define SCL_RECORD_ITEM(s0, s1, p, e) ((e) | (s0)), ((p) | (s1)) + +void SCL_recordInit(SCL_Record r); + +void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo); + +/** + Represents a complete game of chess (or a variant with different staring + position). This struct along with associated functions allows to easily + implement a chess game that allows undoing moves, detecting draws, recording + the moves etc. On platforms with extremely little RAM one can reduce + SCL_RECORD_MAX_LENGTH to reduce the size of this struct (which will however + possibly limit how many moves can be undone). +*/ +typedef struct { + SCL_Board board; + SCL_Record record; /**< Holds the game record. This record is here + firstly because games are usually recorded and + secondly this allows undoing moves up to the + beginning of the game. This infinite undoing will + only work as long as the record is able to hold + the whole game; if the record is full, undoing is + no longet possible. */ + uint16_t state; + uint16_t ply; ///< ply count (board ply counter is only 8 bit) + + uint32_t prevMoves[14]; ///< stores last moves, for repetition detection + + const char* startState; /**< Optional pointer to the starting board state. + If this is null, standard chess start position is + assumed. This is needed for undoing moves with + game record. */ +} SCL_Game; + +/** + Initializes a new chess game. The startState parameter is optional and allows + for setting up chess variants that differ by starting positions, setting this + to 0 will assume traditional starting position. WARNING: if startState is + provided, the pointed to board mustn't be deallocated afterwards, the string + is not internally copied (for memory saving reasons). +*/ +void SCL_gameInit(SCL_Game* game, const SCL_Board startState); + +void SCL_gameMakeMove(SCL_Game* game, uint8_t squareFrom, uint8_t squareTo, char promoteTo); + +uint8_t SCL_gameUndoMove(SCL_Game* game); + +/** + Gets a move which if played now would cause a draw by repetition. Returns 1 + if such move exists, 0 otherwise. The results parameters can be set to 0 in + which case they will be ignored and only the existence of a draw move will be + tested. +*/ +uint8_t SCL_gameGetRepetiotionMove(SCL_Game* game, uint8_t* squareFrom, uint8_t* squareTo); + +/** + Leads a game record from PGN string. The function will probably not strictly + adhere to the PGN input format, but should accept most sanely written PGN + strings. +*/ +void SCL_recordFromPGN(SCL_Record r, const char* pgn); + +uint16_t SCL_recordLength(const SCL_Record r); + +/** + Gets the move out of a game record, returns the end state of the move + (SCL_RECORD_CONT, SCL_RECORD_END etc.) +*/ +uint8_t SCL_recordGetMove( + const SCL_Record r, + uint16_t index, + uint8_t* squareFrom, + uint8_t* squareTo, + char* promotedPiece); + +/** + Adds another move to the game record. Terminating the record is handled so + that the last move is always marked with end flag, endState is here to only + indicate possible game result (otherwise pass SCL_RECORD_CONT). Returns 1 if + the item was added, otherwise 0 (replay was already of maximum size). +*/ +uint8_t SCL_recordAdd( + SCL_Record r, + uint8_t squareFrom, + uint8_t squareTo, + char promotePiece, + uint8_t endState); + +/** + Removes the last move from the record, returns 1 if the replay is non-empty + after the removal, otherwise 0. +*/ +uint8_t SCL_recordRemoveLast(SCL_Record r); + +/** + Applies given number of half-moves (ply) to a given board (the board is + automatically initialized at the beginning). +*/ +void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves); + +int16_t SCL_pieceValue(char piece); +int16_t SCL_pieceValuePositive(char piece); + +#define SCL_PRINT_FORMAT_NONE 0 +#define SCL_PRINT_FORMAT_NORMAL 1 +#define SCL_PRINT_FORMAT_COMPACT 2 +#define SCL_PRINT_FORMAT_UTF8 3 +#define SCL_PRINT_FORMAT_COMPACT_UTF8 4 + +/** + Gets the best move for the currently moving player as computed by AI. The + return value is the value of the move (with the same semantics as the value + of an evaluation function). baseDepth is depth in plys to which all moves will + be checked. If baseDepth 0 is passed, the function makes a random move and + returns the evaluation of the board. extensionExtraDepth is extra depth for + checking specific situations like exchanges and checks. endgameExtraDepth is + extra depth which is added to baseDepth in the endgame. If the randomness + function is 0, AI will always make the first best move it finds, if it is + not 0 and randomness is 0, AI will randomly pick between the equally best + moves, if it is not 0 and randomness is positive, AI will randomly choose + between best moves with some bias (may not pick the best rated move). +*/ +int16_t SCL_getAIMove( + SCL_Board board, + uint8_t baseDepth, + uint8_t extensionExtraDepth, + uint8_t endgameExtraDepth, + SCL_StaticEvaluationFunction evalFunc, + SCL_RandomFunction randFunc, + uint8_t randomness, + uint8_t repetitionMoveFrom, + uint8_t repetitionMoveTo, + uint8_t* resultFrom, + uint8_t* resultTo, + char* resultProm); + +/** + Function that prints out a single character. This is passed to printing + functions. +*/ +typedef void (*SCL_PutCharFunction)(char); + +/** + Prints given chessboard using given format and an abstract printing function. +*/ +void SCL_printBoard( + SCL_Board board, + SCL_PutCharFunction putCharFunc, + SCL_SquareSet highlightSquares, + uint8_t selectSquare, + uint8_t format, + uint8_t offset, + uint8_t labels, + uint8_t blackDown); + +void SCL_printBoardSimple( + SCL_Board board, + SCL_PutCharFunction putCharFunc, + uint8_t selectSquare, + uint8_t format); + +void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc); +void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc, SCL_Board initialState); + +/** + Reads a move from string (the notation format is described at the top of this + file). The function is safe as long as the string is 0 terminated. Returns 1 + on success or 0 on fail (invalid move string). +*/ +uint8_t SCL_stringToMove( + const char* moveString, + uint8_t* resultFrom, + uint8_t* resultTo, + char* resultPromotion); + +char* SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1, char promotion, char* string); + +/** + Function used in drawing, it is called to draw the next pixel. The first + parameter is the pixel color, the second one if the sequential number of the + pixel. +*/ +typedef void (*SCL_PutPixelFunction)(uint8_t, uint16_t); + +#define SCL_BOARD_PICTURE_WIDTH 64 + +/** + Draws a simple 1bit 64x64 pixels board using a provided abstract function for + drawing pixels. The function renders from top left to bottom right, i.e. no + frame buffer is required. +*/ +void SCL_drawBoard( + SCL_Board board, + SCL_PutPixelFunction putPixel, + uint8_t selectedSquare, + SCL_SquareSet highlightSquares, + uint8_t blackDown); + +/** + Converts square number to string representation (e.g. "d2"). This function + will modify exactly the first two bytes of the provided string. +*/ +static inline char* SCL_squareToString(uint8_t square, char* string); + +/** + Converts a string, such as "A1" or "b4", to square number. The string must + start with a letter (lower or upper case) and be followed by a number <1,8>. + Validity of the string is NOT checked. +*/ +uint8_t SCL_stringToSquare(const char* square); + +//============================================================================= +// privates: + +#define SCL_UNUSED(v) (void)(v) + +uint8_t SCL_currentRandom8 = 0; + +uint16_t SCL_currentRandom16 = 0; + +void SCL_randomSimpleSeed(uint8_t seed) { + SCL_currentRandom8 = seed; +} + +uint8_t SCL_randomSimple(void) { + SCL_currentRandom8 *= 13; + SCL_currentRandom8 += 7; + return SCL_currentRandom8; +} + +uint8_t SCL_randomBetter(void) { + SCL_currentRandom16 *= 13; + SCL_currentRandom16 += 7; + return (SCL_currentRandom16 % 256) ^ (SCL_currentRandom16 / 256); +} + +void SCL_randomBetterSeed(uint16_t seed) { + SCL_currentRandom16 = seed; +} + +void SCL_squareSetClear(SCL_SquareSet squareSet) { + for(uint8_t i = 0; i < 8; ++i) squareSet[i] = 0; +} + +uint8_t SCL_stringToSquare(const char* square) { + return (square[1] - '1') * 8 + + (square[0] - ((square[0] >= 'A' && square[0] <= 'Z') ? 'A' : 'a')); +} + +char* SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1, char promotion, char* string) { + char* result = string; + + SCL_squareToString(s0, string); + string += 2; + string = SCL_squareToString(s1, string); + string += 2; + + char c = board[s0]; + + if(c == 'p' || c == 'P') { + uint8_t rank = s1 / 8; + + if(rank == 0 || rank == 7) { + *string = promotion; + string++; + } + } + + *string = 0; + + return result; +} + +uint8_t SCL_boardWhitesTurn(SCL_Board board) { + return (board[SCL_BOARD_PLY_BYTE] % 2) == 0; +} + +uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column) { + return row * 8 + column; +} + +uint8_t SCL_pieceIsWhite(char piece) { + return piece < 'a'; +} + +char* SCL_squareToString(uint8_t square, char* string) { + string[0] = 'a' + square % 8; + string[1] = '1' + square / 8; + + return string; +} + +uint8_t SCL_squareIsWhite(uint8_t square) { + return (square % 2) != ((square / 8) % 2); +} + +char SCL_pieceToColor(uint8_t piece, uint8_t toWhite) { + return (SCL_pieceIsWhite(piece) == toWhite) ? piece : (piece + (toWhite ? -32 : 32)); +} + +/** + Records the rook starting positions in the board state. This is required in + chess 960 in order to be able to correctly perform castling (castling rights + knowledge isn't enough as one rook might have moved to the other side and we + wouldn't know which one can castle and which not). +*/ +void _SCL_board960RememberRookPositions(SCL_Board board) { + uint8_t pos = 0; + uint8_t rooks = 2; + + while(pos < 8 && rooks != 0) { + if(board[pos] == 'R') { + board[SCL_BOARD_EXTRA_BYTE] = rooks == 2 ? pos : + (board[SCL_BOARD_EXTRA_BYTE] | (pos << 3)); + + rooks--; + } + + pos++; + } +} + +void SCL_boardInit(SCL_Board board) { + /* + We might use SCL_BOARD_START_STATE and copy it to the board, but that might + waste RAM on Arduino, so we init the board by code. + */ + + char* b = board; + + *b = 'R'; + b++; + *b = 'N'; + b++; + *b = 'B'; + b++; + *b = 'Q'; + b++; + *b = 'K'; + b++; + *b = 'B'; + b++; + *b = 'N'; + b++; + *b = 'R'; + b++; + + char* b2 = board + 48; + + for(uint8_t i = 0; i < 8; ++i, b++, b2++) { + *b = 'P'; + *b2 = 'p'; + } + + for(uint8_t i = 0; i < 32; ++i, b++) *b = '.'; + + b += 8; + + *b = 'r'; + b++; + *b = 'n'; + b++; + *b = 'b'; + b++; + *b = 'q'; + b++; + *b = 'k'; + b++; + *b = 'b'; + b++; + *b = 'n'; + b++; + *b = 'r'; + b++; + + for(uint8_t i = 0; i < SCL_BOARD_STATE_SIZE - SCL_BOARD_SQUARES; ++i, ++b) *b = 0; + + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = (char)0xff; + +#if SCL_960_CASTLING + _SCL_board960RememberRookPositions(board); +#endif +} + +void _SCL_boardPlaceOnNthAvailable(SCL_Board board, uint8_t pos, char piece) { + char* c = board; + + while(1) { + if(*c == '.') { + if(pos == 0) break; + + pos--; + } + + c++; + } + + *c = piece; +} + +void SCL_boardInit960(SCL_Board board, uint16_t positionNumber) { + SCL_Board b; + + SCL_boardInit(b); + + for(uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i) + board[i] = ((i >= 8 && i < 56) || i >= 64) ? b[i] : '.'; + + uint8_t helper = positionNumber % 16; + + board[(helper / 4) * 2] = 'B'; + board[1 + (helper % 4) * 2] = 'B'; + + helper = positionNumber / 16; + + // maybe there's a simpler way :) + + _SCL_boardPlaceOnNthAvailable(board, helper % 6, 'Q'); + _SCL_boardPlaceOnNthAvailable(board, 0, helper <= 23 ? 'N' : 'R'); + + _SCL_boardPlaceOnNthAvailable( + board, 0, (helper >= 7 && helper <= 23) ? 'R' : (helper > 41 ? 'K' : 'N')); + + _SCL_boardPlaceOnNthAvailable( + board, + 0, + (helper <= 5 || helper >= 54) ? + 'R' : + (((helper >= 12 && helper <= 23) || (helper >= 30 && helper <= 41)) ? 'K' : 'N')); + + _SCL_boardPlaceOnNthAvailable( + board, + 0, + (helper <= 11 || (helper <= 29 && helper >= 24)) ? + 'K' : + (((helper >= 18 && helper <= 23) || (helper >= 36 && helper <= 41) || + (helper >= 48 && helper <= 53)) ? + 'R' : + 'N')); + + uint8_t rooks = 0; + + for(uint8_t i = 0; i < 8; ++i) + if(board[i] == 'R') rooks++; + + _SCL_boardPlaceOnNthAvailable(board, 0, rooks == 2 ? 'N' : 'R'); + + for(uint8_t i = 0; i < 8; ++i) board[56 + i] = SCL_pieceToColor(board[i], 0); + +#if SCL_960_CASTLING + _SCL_board960RememberRookPositions(board); +#else + SCL_boardDisableCastling(board); +#endif +} + +uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2) { + const char *p1 = b1, *p2 = b2; + + while(p1 < b1 + SCL_BOARD_STATE_SIZE) { + if(*p1 != *p2) return 1; + + p1++; + p2++; + } + + return 0; +} + +void SCL_recordInit(SCL_Record r) { + r[0] = 0 | SCL_RECORD_END; + r[1] = 0; +} + +void SCL_recordFromPGN(SCL_Record r, const char* pgn) { + SCL_Board board; + + SCL_boardInit(board); + + SCL_recordInit(r); + + uint8_t state = 0; + uint8_t evenMove = 0; + + while(*pgn != 0) { + switch(state) { + case 0: // skipping tags and spaces, outside [] + if(*pgn == '1') + state = 2; + else if(*pgn == '[') + state = 1; + + break; + + case 1: // skipping tags and spaces, inside [] + if(*pgn == ']') state = 0; + + break; + + case 2: // reading move number + if(*pgn == '{') + state = 3; + else if((*pgn >= 'a' && *pgn <= 'h') || (*pgn >= 'A' && *pgn <= 'Z')) { + state = 4; + pgn--; + } + + break; + + case 3: // initial comment + if(*pgn == '}') state = 2; + + break; + + case 4: // reading move + { + char piece = 'p'; + char promoteTo = 'q'; + uint8_t castle = 0; + uint8_t promotion = 0; + + int8_t coords[4]; + + uint8_t ranks = 0, files = 0; + + for(uint8_t i = 0; i < 4; ++i) coords[i] = -1; + + while(*pgn != ' ' && *pgn != '\n' && *pgn != '\t' && *pgn != '{' && *pgn != 0) { + if(*pgn == '=') promotion = 1; + if(*pgn == 'O' || *pgn == '0') castle++; + if(*pgn >= 'A' && *pgn <= 'Z') { + if(promotion) + promoteTo = *pgn; + else + piece = *pgn; + } else if(*pgn >= 'a' && *pgn <= 'h') { + coords[files * 2] = *pgn - 'a'; + files++; + } else if(*pgn >= '1' && *pgn <= '8') { + coords[1 + ranks * 2] = *pgn - '1'; + ranks++; + } + + pgn++; + } + + if(castle) { + piece = 'K'; + + coords[0] = 4; + coords[1] = 0; + coords[2] = castle < 3 ? 6 : 2; + coords[3] = 0; + + if(evenMove) { + coords[1] = 7; + coords[3] = 7; + } + } + + piece = SCL_pieceToColor(piece, evenMove == 0); + + if(coords[2] < 0) { + coords[2] = coords[0]; + coords[0] = -1; + } + + if(coords[3] < 0) { + coords[3] = coords[1]; + coords[1] = -1; + } + + uint8_t squareTo = coords[3] * 8 + coords[2]; + + if(coords[0] < 0 || coords[1] < 0) { + // without complete starting coords we have to find the piece + + for(int i = 0; i < SCL_BOARD_SQUARES; ++i) + if(board[i] == piece) { + SCL_SquareSet s; + + SCL_squareSetClear(s); + + SCL_boardGetMoves(board, i, s); + + if(SCL_squareSetContains(s, squareTo) && + (coords[0] < 0 || coords[0] == i % 8) && + (coords[1] < 0 || coords[1] == i / 8)) { + coords[0] = i % 8; + coords[1] = i / 8; + break; + } + } + } + + uint8_t squareFrom = coords[1] * 8 + coords[0]; + + SCL_boardMakeMove(board, squareFrom, squareTo, promoteTo); + + // for some reason tcc bugs here, the above line sets squareFrom to 0 lol + // can be fixed with doing "squareFrom = coords[1] * 8 + coords[0];" again + + SCL_recordAdd(r, squareFrom, squareTo, promoteTo, SCL_RECORD_CONT); + + while(*pgn == ' ' || *pgn == '\n' || *pgn == '\t' || *pgn == '{') { + if(*pgn == '{') + while(*pgn != '}') pgn++; + + pgn++; + } + + if(*pgn == 0) return; + + pgn--; + + if(evenMove) state = 2; + + evenMove = !evenMove; + + break; + } + + default: + break; + } + + pgn++; + } +} + +uint16_t SCL_recordLength(const SCL_Record r) { + if((r[0] & 0x3f) == (r[1] & 0x3f)) // empty record that's only terminator + return 0; + + uint16_t result = 0; + + while((r[result] & 0xc0) == 0) result += 2; + + return (result / 2) + 1; +} + +uint8_t SCL_recordGetMove( + const SCL_Record r, + uint16_t index, + uint8_t* squareFrom, + uint8_t* squareTo, + char* promotedPiece) { + index *= 2; + + uint8_t b = r[index]; + + *squareFrom = b & 0x3f; + uint8_t result = b & 0xc0; + + index++; + + b = r[index]; + + *squareTo = b & 0x3f; + + b &= 0xc0; + + switch(b) { + case SCL_RECORD_PROM_Q: + *promotedPiece = 'q'; + break; + case SCL_RECORD_PROM_R: + *promotedPiece = 'r'; + break; + case SCL_RECORD_PROM_B: + *promotedPiece = 'b'; + break; + case SCL_RECORD_PROM_N: + default: + *promotedPiece = 'n'; + break; + } + + return result; +} + +uint8_t SCL_recordAdd( + SCL_Record r, + uint8_t squareFrom, + uint8_t squareTo, + char promotePiece, + uint8_t endState) { + uint16_t l = SCL_recordLength(r); + + if(l >= SCL_RECORD_MAX_LENGTH) return 0; + + l *= 2; + + if(l != 0) r[l - 2] &= 0x3f; // remove the end flag from previous item + + if(endState == SCL_RECORD_CONT) endState = SCL_RECORD_END; + + r[l] = squareFrom | endState; + + uint8_t p; + + switch(promotePiece) { + case 'n': + case 'N': + p = SCL_RECORD_PROM_N; + break; + case 'b': + case 'B': + p = SCL_RECORD_PROM_B; + break; + case 'r': + case 'R': + p = SCL_RECORD_PROM_R; + break; + case 'q': + case 'Q': + default: + p = SCL_RECORD_PROM_Q; + break; + } + + l++; + + r[l] = squareTo | p; + + return 1; +} + +uint8_t SCL_recordRemoveLast(SCL_Record r) { + uint16_t l = SCL_recordLength(r); + + if(l == 0) return 0; + + if(l == 1) + SCL_recordInit(r); + else { + l = (l - 2) * 2; + + r[l] = (r[l] & 0x3f) | SCL_RECORD_END; + } + + return 1; +} + +void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves) { + SCL_boardInit(b); + + uint16_t l = SCL_recordLength(r); + + if(moves > l) moves = l; + + for(uint16_t i = 0; i < moves; ++i) { + uint8_t s0, s1; + char p; + + SCL_recordGetMove(r, i, &s0, &s1, &p); + SCL_boardMakeMove(b, s0, s1, p); + } +} + +void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo) { +#if SCL_960_CASTLING + char squareToNow = board[moveUndo.squareTo]; +#endif + + board[moveUndo.squareFrom] = board[moveUndo.squareTo]; + board[moveUndo.squareTo] = moveUndo.other & 0x7f; + board[SCL_BOARD_PLY_BYTE]--; + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = moveUndo.enPassantCastle; + board[SCL_BOARD_MOVE_COUNT_BYTE] = moveUndo.moveCount; + + if(moveUndo.other & 0x80) { + moveUndo.squareTo /= 8; + + if(moveUndo.squareTo == 0 || moveUndo.squareTo == 7) + board[moveUndo.squareFrom] = SCL_pieceIsWhite(board[moveUndo.squareFrom]) ? 'P' : 'p'; + // ^ was promotion + else + board[(moveUndo.squareFrom / 8) * 8 + (moveUndo.enPassantCastle & 0x0f)] = + (board[moveUndo.squareFrom] == 'P') ? 'p' : 'P'; // was en passant + } +#if !SCL_960_CASTLING + else if( + board[moveUndo.squareFrom] == 'k' && // black castling + moveUndo.squareFrom == 60) { + if(moveUndo.squareTo == 58) { + board[59] = '.'; + board[56] = 'r'; + } else if(moveUndo.squareTo == 62) { + board[61] = '.'; + board[63] = 'r'; + } + } else if( + board[moveUndo.squareFrom] == 'K' && // white castling + moveUndo.squareFrom == 4) { + if(moveUndo.squareTo == 2) { + board[3] = '.'; + board[0] = 'R'; + } else if(moveUndo.squareTo == 6) { + board[5] = '.'; + board[7] = 'R'; + } + } +#else // 960 castling + else if( + ((moveUndo.other & 0x7f) == 'r') && // black castling + (squareToNow == '.' || !SCL_pieceIsWhite(squareToNow))) { + board[moveUndo.squareTo < moveUndo.squareFrom ? 59 : 61] = '.'; + board[moveUndo.squareTo < moveUndo.squareFrom ? 58 : 62] = '.'; + + board[moveUndo.squareFrom] = 'k'; + board[moveUndo.squareTo] = 'r'; + } else if( + ((moveUndo.other & 0x7f) == 'R') && // white castling + (squareToNow == '.' || SCL_pieceIsWhite(squareToNow))) { + board[moveUndo.squareTo < moveUndo.squareFrom ? 3 : 5] = '.'; + board[moveUndo.squareTo < moveUndo.squareFrom ? 2 : 6] = '.'; + + board[moveUndo.squareFrom] = 'K'; + board[moveUndo.squareTo] = 'R'; + } +#endif +} + +/** + Potentially disables castling rights according to whether something moved from + or to a square with a rook. +*/ +void _SCL_handleRookActivity(SCL_Board board, uint8_t rookSquare) { +#if !SCL_960_CASTLING + switch(rookSquare) { + case 0: + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x20; + break; + case 7: + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x10; + break; + case 56: + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x80; + break; + case 63: + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x40; + break; + default: + break; + } +#else // 960 castling + if(rookSquare == (board[SCL_BOARD_EXTRA_BYTE] & 0x07)) + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x20; + else if(rookSquare == (board[SCL_BOARD_EXTRA_BYTE] >> 3)) + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x10; + else if(rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07)) + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x80; + else if(rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] >> 3)) + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x40; +#endif +} + +SCL_MoveUndo + SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo, char promotePiece) { + char s = board[squareFrom]; + + SCL_MoveUndo moveUndo; + + moveUndo.squareFrom = squareFrom; + moveUndo.squareTo = squareTo; + moveUndo.moveCount = board[SCL_BOARD_MOVE_COUNT_BYTE]; + moveUndo.enPassantCastle = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE]; + moveUndo.other = board[squareTo]; + + // reset the en-passant state + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] |= 0x0f; + + if(SCL_boardMoveResetsCount(board, squareFrom, squareTo)) + board[SCL_BOARD_MOVE_COUNT_BYTE] = 0; + else + board[SCL_BOARD_MOVE_COUNT_BYTE]++; + +#if SCL_960_CASTLING + uint8_t castled = 0; +#endif + + if((s == 'k') || (s == 'K')) { +#if !SCL_960_CASTLING + if((squareFrom == 4) || (squareFrom == 60)) // check castling + { + int8_t difference = squareTo - squareFrom; + + char rook = SCL_pieceToColor('r', SCL_pieceIsWhite(s)); + + if(difference == 2) // short + { + board[squareTo - 1] = rook; + board[squareTo + 1] = '.'; + } else if(difference == -2) // long + { + board[squareTo - 2] = '.'; + board[squareTo + 1] = rook; + } + } +#else // 960 castling + uint8_t isWhite = SCL_pieceIsWhite(s); + char rook = SCL_pieceToColor('r', isWhite); + + if(board[squareTo] == rook) { + castled = 1; + + board[squareFrom] = '.'; + board[squareTo] = '.'; + + if(squareTo > squareFrom) // short + { + board[isWhite ? 6 : (56 + 6)] = s; + board[isWhite ? 5 : (56 + 5)] = rook; + } else // long + { + board[isWhite ? 2 : (56 + 2)] = s; + board[isWhite ? 3 : (56 + 3)] = rook; + } + } +#endif + + // after king move disable castling + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= ~(0x03 << ((s == 'K') ? 4 : 6)); + } else if((s == 'p') || (s == 'P')) { + uint8_t row = squareTo / 8; + + int8_t rowDiff = squareFrom / 8 - row; + + if(rowDiff == 2 || rowDiff == -2) // record en passant column + { + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = + (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0) | (squareFrom % 8); + } + + if(row == 0 || row == 7) { + // promotion + s = SCL_pieceToColor(promotePiece, SCL_pieceIsWhite(s)); + + moveUndo.other |= 0x80; + } else { + // check en passant move + + int8_t columnDiff = (squareTo % 8) - (squareFrom % 8); + + if((columnDiff != 0) && (board[squareTo] == '.')) { + board[squareFrom + columnDiff] = '.'; + moveUndo.other |= 0x80; + } + } + } else if((s == 'r') || (s == 'R')) + _SCL_handleRookActivity(board, squareFrom); + + char taken = board[squareTo]; + + // taking a rook may also disable castling: + + if(taken == 'R' || taken == 'r') _SCL_handleRookActivity(board, squareTo); + +#if SCL_960_CASTLING + if(!castled) +#endif + { + board[squareTo] = s; + board[squareFrom] = '.'; + } + + board[SCL_BOARD_PLY_BYTE]++; // increase ply count + + return moveUndo; +} + +void SCL_boardSetPosition( + SCL_Board board, + const char* pieces, + uint8_t castlingEnPassant, + uint8_t moveCount, + uint8_t ply) { + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, pieces++) + if(*pieces != 0) + board[i] = *pieces; + else + break; + + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castlingEnPassant; + board[SCL_BOARD_PLY_BYTE] = ply; + board[SCL_BOARD_MOVE_COUNT_BYTE] = moveCount; + board[SCL_BOARD_STATE_SIZE - 1] = 0; +} + +void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square) { + squareSet[square / 8] |= 0x01 << (square % 8); +} + +uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square) { + return squareSet[square / 8] & (0x01 << (square % 8)); +} + +uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet) { + uint8_t result = 0; + + for(uint8_t i = 0; i < 8; ++i) { + uint8_t byte = squareSet[i]; + + for(uint8_t j = 0; j < 8; ++j) { + result += byte & 0x01; + byte >>= 1; + } + } + + return result; +} + +uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet) { + for(uint8_t i = 0; i < 8; ++i) + if(squareSet[i] != 0) return 0; + + return 1; +} + +uint8_t SCL_squareSetGetRandom(const SCL_SquareSet squareSet, SCL_RandomFunction randFunc) { + uint8_t size = SCL_squareSetSize(squareSet); + + if(size == 0) return 0; + + uint8_t n = (randFunc() % size) + 1; + uint8_t i = 0; + + while(i < SCL_BOARD_SQUARES) { + if(SCL_squareSetContains(squareSet, i)) { + n--; + + if(n == 0) break; + } + + ++i; + } + + return i; +} + +void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo) { + for(uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i) boardTo[i] = boardFrom[i]; +} + +uint8_t SCL_boardSquareAttacked(SCL_Board board, uint8_t square, uint8_t byWhite) { + const char* currentSquare = board; + + /* We need to place a temporary piece on the tested square in order to test if + the square is attacked (consider testing if attacked by a pawn). */ + + char previous = board[square]; + + board[square] = SCL_pieceToColor('r', !byWhite); + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++currentSquare) { + char s = *currentSquare; + + if((s == '.') || (SCL_pieceIsWhite(s) != byWhite)) continue; + + SCL_SquareSet moves; + SCL_boardGetPseudoMoves(board, i, 0, moves); + + if(SCL_squareSetContains(moves, square)) { + board[square] = previous; + return 1; + } + } + + board[square] = previous; + return 0; +} + +uint8_t SCL_boardCheck(SCL_Board board, uint8_t white) { + const char* square = board; + char kingChar = white ? 'K' : 'k'; + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++square) + if((*square == kingChar && SCL_boardSquareAttacked(board, i, !white))) return 1; + + return 0; +} + +uint8_t SCL_boardGameOver(SCL_Board board) { + uint8_t position = SCL_boardGetPosition(board); + + return (position == SCL_POSITION_MATE) || (position == SCL_POSITION_STALEMATE) || + (position == SCL_POSITION_DEAD); +} + +uint8_t SCL_boardMovePossible(SCL_Board board) { + uint8_t white = SCL_boardWhitesTurn(board); + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) { + char s = board[i]; + + if((s != '.') && (SCL_pieceIsWhite(s) == white)) { + SCL_SquareSet moves; + + SCL_boardGetMoves(board, i, moves); + + if(SCL_squareSetSize(moves) != 0) return 1; + } + } + + return 0; +} + +uint8_t SCL_boardMate(SCL_Board board) { + return SCL_boardGetPosition(board) == SCL_POSITION_MATE; +} + +void SCL_boardGetPseudoMoves( + SCL_Board board, + uint8_t pieceSquare, + uint8_t checkCastling, + SCL_SquareSet result) { + char piece = board[pieceSquare]; + + SCL_squareSetClear(result); + + uint8_t isWhite = SCL_pieceIsWhite(piece); + int8_t horizontalPosition = pieceSquare % 8; + int8_t pawnOffset = -8; + + switch(piece) { + case 'P': + pawnOffset = 8; + /* FALLTHROUGH */ + case 'p': { + uint8_t square = pieceSquare + pawnOffset; + uint8_t verticalPosition = pieceSquare / 8; + + if(board[square] == '.') // forward move + { + SCL_squareSetAdd(result, square); + + if(verticalPosition == (1 + (piece == 'p') * 5)) // start position? + { + uint8_t square2 = square + pawnOffset; + + if(board[square2] == '.') SCL_squareSetAdd(result, square2); + } + } + +#define checkDiagonal(hor, add) \ + if(horizontalPosition != hor) { \ + uint8_t square2 = square + add; \ + char c = board[square2]; \ + if(c != '.' && SCL_pieceIsWhite(c) != isWhite) SCL_squareSetAdd(result, square2); \ + } + + // diagonal moves + checkDiagonal(0, -1) checkDiagonal(7, 1) + + uint8_t enPassantRow = 4; + uint8_t enemyPawn = 'p'; + + if(piece == 'p') { + enPassantRow = 3; + enemyPawn = 'P'; + } + + // en-passant moves + if(verticalPosition == enPassantRow) { + uint8_t enPassantColumn = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f; + uint8_t column = pieceSquare % 8; + + for(int8_t offset = -1; offset < 2; offset += 2) + if((enPassantColumn == column + offset) && + (board[pieceSquare + offset] == enemyPawn)) { + SCL_squareSetAdd(result, pieceSquare + pawnOffset + offset); + break; + } + } + +#undef checkDiagonal + } break; + + case 'r': // rook + case 'R': + case 'b': // bishop + case 'B': + case 'q': // queen + case 'Q': { + const int8_t offsets[8] = {-8, 1, 8, -1, -7, 9, -9, 7}; + const int8_t columnDirs[8] = {0, 1, 0, -1, 1, 1, -1, -1}; + + uint8_t from = (piece == 'b' || piece == 'B') * 4; + uint8_t to = 4 + (piece != 'r' && piece != 'R') * 4; + + for(uint8_t i = from; i < to; ++i) { + int8_t offset = offsets[i]; + int8_t columnDir = columnDirs[i]; + int8_t square = pieceSquare; + int8_t col = horizontalPosition; + + while(1) { + square += offset; + col += columnDir; + + if(square < 0 || square > 63 || col < 0 || col > 7) break; + + char squareC = board[square]; + + if(squareC == '.') + SCL_squareSetAdd(result, square); + else { + if(SCL_pieceIsWhite(squareC) != isWhite) SCL_squareSetAdd(result, square); + + break; + } + } + } + } break; + + case 'n': // knight + case 'N': { + const int8_t offsets[4] = {6, 10, 15, 17}; + const int8_t columnsMinus[4] = {2, -2, 1, -1}; + const int8_t columnsPlus[4] = {-2, 2, -1, 1}; + const int8_t *off, *col; + +#define checkOffsets(op, comp, limit, dir) \ + off = offsets; \ + col = columns##dir; \ + for(uint8_t i = 0; i < 4; ++i, ++off, ++col) { \ + int8_t square = pieceSquare op(*off); \ + if(square comp limit) /* out of board? */ \ + break; \ + int8_t horizontalCheck = horizontalPosition + (*col); \ + if(horizontalCheck < 0 || horizontalCheck >= 8) continue; \ + char squareC = board[square]; \ + if((squareC == '.') || (SCL_pieceIsWhite(squareC) != isWhite)) \ + SCL_squareSetAdd(result, square); \ + } + + checkOffsets(-, <, 0, Minus) checkOffsets(+, >=, SCL_BOARD_SQUARES, Plus) + +#undef checkOffsets + } break; + + case 'k': // king + case 'K': { + uint8_t verticalPosition = pieceSquare / 8; + + uint8_t u = verticalPosition != 0, d = verticalPosition != 7, l = horizontalPosition != 0, + r = horizontalPosition != 7; + + uint8_t square2 = pieceSquare - 9; + +#define checkSquare(cond, add) \ + if(cond && ((board[square2] == '.') || (SCL_pieceIsWhite(board[square2])) != isWhite)) \ + SCL_squareSetAdd(result, square2); \ + square2 += add; + + checkSquare(l && u, 1) checkSquare(u, 1) checkSquare(r && u, 6) checkSquare(l, 2) + checkSquare(r, 6) checkSquare(l && d, 1) checkSquare(d, 1) checkSquare(r && d, 0) +#undef checkSquare + + // castling: + + if(checkCastling) { + uint8_t bitShift = 4 + 2 * (!isWhite); + + if((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x03 << bitShift)) && + !SCL_boardSquareAttacked(board, pieceSquare, !isWhite)) // no check? + { +#if !SCL_960_CASTLING + // short castle: + pieceSquare++; + + if((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x01 << bitShift)) && + (board[pieceSquare] == '.') && (board[pieceSquare + 1] == '.') && + (board[pieceSquare + 2] == SCL_pieceToColor('r', isWhite)) && + !SCL_boardSquareAttacked(board, pieceSquare, !isWhite)) + SCL_squareSetAdd(result, pieceSquare + 1); + + /* note: don't check the final square for check, it will potentially + be removed later (can't end up in check) */ + + // long castle: + pieceSquare -= 2; + + if((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x02 << bitShift)) && + (board[pieceSquare] == '.') && (board[pieceSquare - 1] == '.') && + (board[pieceSquare - 2] == '.') && + (board[pieceSquare - 3] == SCL_pieceToColor('r', isWhite)) && + !SCL_boardSquareAttacked(board, pieceSquare, !isWhite)) + SCL_squareSetAdd(result, pieceSquare - 1); +#else // 960 castling + for(int i = 0; i < 2; ++i) // short and long + if(board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & ((i + 1) << bitShift)) { + uint8_t rookPos = board[SCL_BOARD_EXTRA_BYTE] >> 3, targetPos = 5; + + if(i == 1) { + rookPos = board[SCL_BOARD_EXTRA_BYTE] & 0x07, targetPos = 3; + } + + if(!isWhite) { + rookPos += 56; + targetPos += 56; + } + + uint8_t ok = board[rookPos] == SCL_pieceToColor('r', isWhite); + + if(!ok) continue; + + int8_t inc = 1 - 2 * (targetPos > rookPos); + + while(targetPos != rookPos) // check vacant squares for the rook + { + if(board[targetPos] != '.' && targetPos != pieceSquare) { + ok = 0; + break; + } + + targetPos += inc; + } + + if(!ok) continue; + + targetPos = i == 0 ? 6 : 2; + + if(!isWhite) targetPos += 56; + + inc = 1 - 2 * (targetPos > pieceSquare); + + while(targetPos != pieceSquare) // check squares for the king + { + if((board[targetPos] != '.' && targetPos != rookPos) || + SCL_boardSquareAttacked(board, targetPos, !isWhite)) { + ok = 0; + break; + } + + targetPos += inc; + } + + if(ok) SCL_squareSetAdd(result, rookPos); + } +#endif + } + } + } break; + + default: + break; + } +} + +void SCL_printSquareSet(SCL_SquareSet set, SCL_PutCharFunction putCharFunc) { + uint8_t first = 1; + + putCharFunc('('); + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) { + if(!SCL_squareSetContains(set, i)) continue; + + if(!first) + putCharFunc(','); + else + first = 0; + + putCharFunc('A' + i % 8); + putCharFunc('1' + i / 8); + } + + putCharFunc(')'); +} + +void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc) { + uint32_t val = 0; + + switch(square) { + case 'r': + val = 0x9c99e200; + break; + case 'n': + val = 0x9e99e200; + break; + case 'b': + val = 0x9d99e200; + break; + case 'q': + val = 0x9b99e200; + break; + case 'k': + val = 0x9a99e200; + break; + case 'p': + val = 0x9f99e200; + break; + case 'R': + val = 0x9699e200; + break; + case 'N': + val = 0x9899e200; + break; + case 'B': + val = 0x9799e200; + break; + case 'Q': + val = 0x9599e200; + break; + case 'K': + val = 0x9499e200; + break; + case 'P': + val = 0x9999e200; + break; + case '.': + val = 0x9296e200; + break; + case ',': + val = 0x9196e200; + break; + default: + putCharFunc(square); + return; + break; + } + + uint8_t count = 4; + + while((val % 256 == 0) && (count > 0)) { + val /= 256; + count--; + } + + while(count > 0) { + putCharFunc(val % 256); + val /= 256; + count--; + } +} + +void SCL_boardGetMoves(SCL_Board board, uint8_t pieceSquare, SCL_SquareSet result) { + SCL_SquareSet allMoves; + + SCL_squareSetClear(allMoves); + + for(uint8_t i = 0; i < 8; ++i) result[i] = 0; + + SCL_boardGetPseudoMoves(board, pieceSquare, 1, allMoves); + + // Now only keep moves that don't lead to one's check: + + SCL_SQUARE_SET_ITERATE_BEGIN(allMoves) + + SCL_MoveUndo undo = SCL_boardMakeMove(board, pieceSquare, iteratedSquare, 'q'); + + if(!SCL_boardCheck(board, !SCL_boardWhitesTurn(board))) + SCL_squareSetAdd(result, iteratedSquare); + + SCL_boardUndoMove(board, undo); + + SCL_SQUARE_SET_ITERATE_END +} + +uint8_t SCL_boardDead(SCL_Board board) { + /* + This byte represents material by bits: + + MSB _ _ _ _ _ _ _ _ LSB + | | | | | \_ white knight + | | | | \__ white bishop on white + | | | \____ white bishop on black + | | \________ black knight + | \__________ black bishop on white + \____________ black bishop on black + */ + uint8_t material = 0; + + const char* p = board; + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) { + char c = *p; + + switch(c) { + case 'n': + material |= 0x01; + break; + case 'N': + material |= 0x10; + break; + case 'b': + material |= (0x02 << (!SCL_squareIsWhite(i))); + break; + case 'B': + material |= (0x20 << (!SCL_squareIsWhite(i))); + break; + case 'p': + case 'P': + case 'r': + case 'R': + case 'q': + case 'Q': + return 0; // REMOVE later if more complex check are performed + break; + + default: + break; + } + + p++; + } + + // TODO: add other checks than only insufficient material + + // possible combinations of insufficient material: + + return (material == 0x00) || // king vs king + (material == 0x01) || // king and knight vs king + (material == 0x10) || // king and knight vs king + (material == 0x02) || // king and bishop vs king + (material == 0x20) || // king and bishop vs king + (material == 0x04) || // king and bishop vs king + (material == 0x40) || // king and bishop vs king + (material == 0x22) || // king and bishop vs king and bishop (same color) + (material == 0x44); // king and bishop vs king and bishop (same color) +} + +uint8_t SCL_boardGetPosition(SCL_Board board) { + uint8_t check = SCL_boardCheck(board, SCL_boardWhitesTurn(board)); + uint8_t moves = SCL_boardMovePossible(board); + + if(check) + return moves ? SCL_POSITION_CHECK : SCL_POSITION_MATE; + else if(!moves) + return SCL_POSITION_STALEMATE; + + if(SCL_boardDead(board)) return SCL_POSITION_DEAD; + + return SCL_POSITION_NORMAL; +} + +uint8_t SCL_stringToMove( + const char* moveString, + uint8_t* resultFrom, + uint8_t* resultTo, + char* resultPromotion) { + char c; + + uint8_t* dst = resultFrom; + + for(uint8_t i = 0; i < 2; ++i) { + c = *moveString; + + *dst = (c >= 'a') ? (c - 'a') : (c - 'A'); + + if(*dst > 7) return 0; + + moveString++; + c = *moveString; + + *dst += 8 * (c - '1'); + + if(*dst > 63) return 0; + + moveString++; + + dst = resultTo; + } + + c = *moveString; + + if(c < 'A') c = c - 'A' + 'a'; + + switch(c) { + case 'N': + case 'n': + *resultPromotion = 'n'; + break; + case 'B': + case 'b': + *resultPromotion = 'b'; + break; + case 'R': + case 'r': + *resultPromotion = 'r'; + break; + case 'Q': + case 'q': + default: + *resultPromotion = 'q'; + break; + } + + return 1; +} + +void SCL_printBoard( + SCL_Board board, + SCL_PutCharFunction putCharFunc, + SCL_SquareSet highlightSquares, + uint8_t selectSquare, + uint8_t format, + uint8_t offset, + uint8_t labels, + uint8_t blackDown) { + if(labels) { + for(uint8_t i = 0; i < offset + 2; ++i) putCharFunc(' '); + + for(uint8_t i = 0; i < 8; ++i) { + if((format != SCL_PRINT_FORMAT_COMPACT) && (format != SCL_PRINT_FORMAT_COMPACT_UTF8)) + putCharFunc(' '); + + putCharFunc(blackDown ? ('H' - i) : ('A' + i)); + } + + putCharFunc('\n'); + } + + int8_t i = 7; + int8_t add = 1; + + if(!blackDown) { + i = 56; + add = -1; + } + + for(int8_t row = 0; row < 8; ++row) { + for(uint8_t j = 0; j < offset; ++j) putCharFunc(' '); + + if(labels) { + putCharFunc(!blackDown ? ('8' - row) : ('1' + row)); + putCharFunc(' '); + } + + const char* square = board + i; + + for(int8_t col = 0; col < 8; ++col) { + switch(format) { + case SCL_PRINT_FORMAT_COMPACT: + putCharFunc( + (*square == '.') ? + (((i != selectSquare) ? + (!SCL_squareSetContains(highlightSquares, i) ? *square : '*') : + '#')) : + *square); + break; + + case SCL_PRINT_FORMAT_UTF8: { + char squareChar = SCL_squareIsWhite(i) ? '.' : ','; + char pieceChar = (*square == '.') ? squareChar : *square; + + if(i == selectSquare) { + putCharFunc('('); + + if(*square == '.') + putCharFunc(')'); + else + SCL_printSquareUTF8(pieceChar, putCharFunc); + } else if(!SCL_squareSetContains(highlightSquares, i)) { + SCL_printSquareUTF8(squareChar, putCharFunc); + SCL_printSquareUTF8(pieceChar, putCharFunc); + } else { + putCharFunc('['); + + if(*square == '.') + putCharFunc(']'); + else + SCL_printSquareUTF8(*square, putCharFunc); + } + + break; + } + + case SCL_PRINT_FORMAT_COMPACT_UTF8: + SCL_printSquareUTF8( + (*square == '.') ? + (SCL_squareSetContains(highlightSquares, i) ? + '*' : + (i == selectSquare ? '#' : ((SCL_squareIsWhite(i) ? '.' : ',')))) : + *square, + putCharFunc); + break; + + case SCL_PRINT_FORMAT_NORMAL: + default: { + uint8_t c = *square; + + char squareColor = SCL_squareIsWhite(i) ? ' ' : ':'; + + putCharFunc( + (i != selectSquare) ? + (!SCL_squareSetContains(highlightSquares, i) ? squareColor : '#') : + '@'); + + putCharFunc(c == '.' ? squareColor : *square); + break; + } + } + + i -= add; + square -= add; + } + + putCharFunc('\n'); + + i += add * 16; + } // for rows +} + +int16_t SCL_pieceValuePositive(char piece) { + switch(piece) { + case 'p': + case 'P': + return SCL_VALUE_PAWN; + break; + case 'n': + case 'N': + return SCL_VALUE_KNIGHT; + break; + case 'b': + case 'B': + return SCL_VALUE_BISHOP; + break; + case 'r': + case 'R': + return SCL_VALUE_ROOK; + break; + case 'q': + case 'Q': + return SCL_VALUE_QUEEN; + break; + case 'k': + case 'K': + return SCL_VALUE_KING; + break; + default: + break; + } + + return 0; +} + +int16_t SCL_pieceValue(char piece) { + switch(piece) { + case 'P': + return SCL_VALUE_PAWN; + break; + case 'N': + return SCL_VALUE_KNIGHT; + break; + case 'B': + return SCL_VALUE_BISHOP; + break; + case 'R': + return SCL_VALUE_ROOK; + break; + case 'Q': + return SCL_VALUE_QUEEN; + break; + case 'K': + return SCL_VALUE_KING; + break; + case 'p': + return -1 * SCL_VALUE_PAWN; + break; + case 'n': + return -1 * SCL_VALUE_KNIGHT; + break; + case 'b': + return -1 * SCL_VALUE_BISHOP; + break; + case 'r': + return -1 * SCL_VALUE_ROOK; + break; + case 'q': + return -1 * SCL_VALUE_QUEEN; + break; + case 'k': + return -1 * SCL_VALUE_KING; + break; + default: + break; + } + + return 0; +} + +#define ATTACK_BONUS 3 +#define MOBILITY_BONUS 10 +#define CENTER_BONUS 7 +#define CHECK_BONUS 5 +#define KING_CASTLED_BONUS 30 +#define KING_BACK_BONUS 15 +#define KING_NOT_CENTER_BONUS 15 +#define PAWN_NON_DOUBLE_BONUS 3 +#define PAWN_PAIR_BONUS 3 +#define KING_CENTERNESS 10 + +int16_t _SCL_rateKingEndgamePosition(uint8_t position) { + int16_t result = 0; + uint8_t rank = position / 8; + position %= 8; + + if(position > 1 && position < 6) result += KING_CENTERNESS; + + if(rank > 1 && rank < 6) result += KING_CENTERNESS; + + return result; +} + +int16_t SCL_boardEvaluateStatic(SCL_Board board) { + uint8_t position = SCL_boardGetPosition(board); + + int16_t total = 0; + + switch(position) { + case SCL_POSITION_MATE: + return SCL_boardWhitesTurn(board) ? -1 * SCL_EVALUATION_MAX_SCORE : + SCL_EVALUATION_MAX_SCORE; + break; + + case SCL_POSITION_STALEMATE: + case SCL_POSITION_DEAD: + return 0; + break; + + /* + main points are assigned as follows: + - points for material as a sum of all material on board + - for playing side: if a piece attacks piece of greater value, a fraction + of the value difference is gained (we suppose exchange), this is only + gained once per every attacking piece (maximum gain is taken), we only + take fraction so that actually taking the piece is favored + - ATTACK_BONUS points for any attacked piece + + other points are assigned as follows (in total these shouldn't be more + than the value of one pawn) + - mobility: MOBILITY_BONUS points for each piece with at least 4 possible + moves + - center control: CENTER_BONUS points for a piece on a center square + - CHECK_BONUS points for check + - king: + - safety (non endgame): KING_BACK_BONUS points for king on staring rank, + additional KING_CASTLED_BONUS if the kind if on castled square or + closer to the edge, additional KING_NOT_CENTER_BONUS for king not on + its start neighbouring center square + - center closeness (endgame): up to 2 * KING_CENTERNESS points for + being closer to center + - non-doubled pawns: PAWN_NON_DOUBLE_BONUS points for each pawn without + same color pawn directly in front of it + - pawn structure: PAWN_PAIR_BONUS points for each pawn guarding own pawn + - advancing pawns: 1 point for each pawn's rank in its move + direction + */ + + case SCL_POSITION_CHECK: + total += SCL_boardWhitesTurn(board) ? -1 * CHECK_BONUS : CHECK_BONUS; + /* FALLTHROUGH */ + case SCL_POSITION_NORMAL: + default: { + SCL_SquareSet moves; + + const char* p = board; + + int16_t positiveMaterial = 0; + uint8_t endgame = 0; + + // first count material to see if this is endgame or not + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p) { + char s = *p; + + if(s != '.') { + positiveMaterial += SCL_pieceValuePositive(s); + total += SCL_pieceValue(s); + } + } + + endgame = positiveMaterial <= SCL_ENDGAME_MATERIAL_LIMIT; + + p = board; + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p) { + char s = *p; + + if(s != '.') { + uint8_t white = SCL_pieceIsWhite(s); + + switch(s) { + case 'k': // king safety + if(endgame) + total -= _SCL_rateKingEndgamePosition(i); + else if(i >= 56) { + total -= KING_BACK_BONUS; + + if(i != 59) { + total -= KING_NOT_CENTER_BONUS; + + if(i >= 62 || i <= 58) total -= KING_CASTLED_BONUS; + } + } + break; + + case 'K': + if(endgame) + total += _SCL_rateKingEndgamePosition(i); + else if(i <= 7) { + total += KING_BACK_BONUS; + + if(i != 3) { + total += KING_NOT_CENTER_BONUS; + + if(i <= 2 || i >= 6) total += KING_CASTLED_BONUS; + } + } + break; + + case 'P': // pawns + case 'p': { + int8_t rank = i / 8; + + if(rank != 0 && rank != 7) { + if(s == 'P') { + total += rank; + + char* tmp = board + i + 8; + + if(*tmp != 'P') total += PAWN_NON_DOUBLE_BONUS; + + if(i % 8 != 7) { + tmp++; + + if(*tmp == 'P') total += PAWN_PAIR_BONUS; + + if(*(tmp - 16) == 'P') total += PAWN_PAIR_BONUS; + } + } else { + total -= 7 - rank; + + char* tmp = board + i - 8; + + if(*tmp != 'p') total -= PAWN_NON_DOUBLE_BONUS; + + if(i % 8 != 7) { + tmp += 17; + + if(*tmp == 'p') total -= PAWN_PAIR_BONUS; + + if(*(tmp - 16) == 'p') total -= PAWN_PAIR_BONUS; + } + } + } + + break; + } + + default: + break; + } + + if(i >= 27 && i <= 36 && (i >= 35 || i <= 28)) // center control + total += white ? CENTER_BONUS : (-1 * CENTER_BONUS); + + // for performance we only take pseudo moves + SCL_boardGetPseudoMoves(board, i, 0, moves); + + if(SCL_squareSetSize(moves) >= 4) // mobility + total += white ? MOBILITY_BONUS : (-1 * MOBILITY_BONUS); + + int16_t exchangeBonus = 0; + + SCL_SQUARE_SET_ITERATE_BEGIN(moves) + + if(board[iteratedSquare] != '.') { + total += white ? ATTACK_BONUS : (-1 * ATTACK_BONUS); + + if(SCL_boardWhitesTurn(board) == white) { + int16_t valueDiff = SCL_pieceValuePositive(board[iteratedSquare]) - + SCL_pieceValuePositive(s); + + valueDiff /= 4; // only take a fraction to favor taking + + if(valueDiff > exchangeBonus) exchangeBonus = valueDiff; + } + } + + SCL_SQUARE_SET_ITERATE_END + + if(exchangeBonus != 0) total += white ? exchangeBonus : -1 * exchangeBonus; + } + } // for each square + + return total; + + break; + + } // normal position + } // switch + + return 0; +} + +#undef ATTACK_BONUS +#undef MOBILITY_BONUS +#undef CENTER_BONUS +#undef CHECK_BONUS +#undef KING_CASTLED_BONUS +#undef KING_BACK_BONUS +#undef PAWN_NON_DOUBLE_BONUS +#undef PAWN_PAIR_BONUS +#undef KING_CENTERNESS + +SCL_StaticEvaluationFunction _SCL_staticEvaluationFunction; +int16_t _SCL_currentEval; +int8_t _SCL_depthHardLimit; + +/** + Inner recursive function for SCL_boardEvaluateDynamic. It is passed a square + (or -1) at which last capture happened, to implement capture extension. +*/ +int16_t _SCL_boardEvaluateDynamic( + SCL_Board board, + int8_t depth, + int16_t alphaBeta, + int8_t takenSquare) { +#if SCL_COUNT_EVALUATED_POSITIONS + SCL_positionsEvaluated++; +#endif + +#if SCL_CALL_WDT_RESET + wdt_reset(); +#endif + + uint8_t whitesTurn = SCL_boardWhitesTurn(board); + int8_t valueMultiply = whitesTurn ? 1 : -1; + int16_t bestMoveValue = -1 * SCL_EVALUATION_MAX_SCORE; + uint8_t shouldCompute = depth > 0; + uint8_t extended = 0; + uint8_t positionType = SCL_boardGetPosition(board); + + if(!shouldCompute) { + /* here we do two extensions (deeper search): taking on a same square + (exchanges) and checks (good for mating and preventing mates): */ + extended = (depth > _SCL_depthHardLimit) && + (takenSquare >= 0 || (SCL_boardGetPosition(board) == SCL_POSITION_CHECK)); + + shouldCompute = extended; + } + +#if SCL_DEBUG_AI + char moveStr[8]; + uint8_t debugFirst = 1; +#endif + + if(shouldCompute && + (positionType == SCL_POSITION_NORMAL || positionType == SCL_POSITION_CHECK)) { +#if SCL_DEBUG_AI + putchar('('); +#endif + + alphaBeta *= valueMultiply; + uint8_t end = 0; + const char* b = board; + + depth--; + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b) { + char s = *b; + + if(s != '.' && SCL_pieceIsWhite(s) == whitesTurn) { + SCL_SquareSet moves; + + SCL_squareSetClear(moves); + + SCL_boardGetMoves(board, i, moves); + + if(!SCL_squareSetEmpty(moves)) { + SCL_SQUARE_SET_ITERATE_BEGIN(moves) + + int8_t captureExtension = -1; + + if(board[iteratedSquare] != '.' && // takes a piece + (takenSquare == -1 || // extend on first taken sq. + (extended && takenSquare != -1) || // ignore check extension + (iteratedSquare == takenSquare))) // extend on same sq. taken + captureExtension = iteratedSquare; + + SCL_MoveUndo undo = SCL_boardMakeMove(board, i, iteratedSquare, 'q'); + + uint8_t s0Dummy, s1Dummy; + char pDummy; + + SCL_UNUSED(s0Dummy); + SCL_UNUSED(s1Dummy); + SCL_UNUSED(pDummy); + +#if SCL_DEBUG_AI + if(debugFirst) + debugFirst = 0; + else + putchar(','); + + if(extended) putchar('*'); + + printf("%s ", SCL_moveToString(board, i, iteratedSquare, 'q', moveStr)); +#endif + + int16_t value = _SCL_boardEvaluateDynamic( + board, + depth, // this is depth - 1, we decremented it +#if SCL_ALPHA_BETA + valueMultiply * bestMoveValue, +#else + 0, +#endif + captureExtension) * + valueMultiply; + + SCL_boardUndoMove(board, undo); + + if(value > bestMoveValue) { + bestMoveValue = value; + +#if SCL_ALPHA_BETA + // alpha-beta pruning: + + if(value > alphaBeta) // no, >= can't be here + { + end = 1; + iterationEnd = 1; + } +#endif + } + + SCL_SQUARE_SET_ITERATE_END + } // !squre set empty? + } // valid piece? + + if(end) break; + + } // for each square + +#if SCL_DEBUG_AI + putchar(')'); +#endif + } else // don't dive recursively, evaluate statically + { + bestMoveValue = valueMultiply * +#ifndef SCL_EVALUATION_FUNCTION + _SCL_staticEvaluationFunction(board); +#else + SCL_EVALUATION_FUNCTION(board); +#endif + + /* For stalemate return the opposite value of the board, i.e. if the + position is good for white, then stalemate is good for black and vice + versa. */ + if(positionType == SCL_POSITION_STALEMATE) bestMoveValue *= -1; + } + + /* Here we either improve (if the move worsens the situation) or devalve (if + it improves the situation) the result: this needs to be done so that good + moves far away are seen as worse compared to equally good moves achieved + in fewer moves. Without this an AI in winning situation may just repeat + random moves and draw by repetition even if it has mate in 1 (it sees all + moves as leading to mate). */ + bestMoveValue += bestMoveValue > _SCL_currentEval * valueMultiply ? -1 : 1; + +#if SCL_DEBUG_AI + printf("%d", bestMoveValue * valueMultiply); +#endif + + return bestMoveValue * valueMultiply; +} + +int16_t SCL_boardEvaluateDynamic( + SCL_Board board, + uint8_t baseDepth, + uint8_t extensionExtraDepth, + SCL_StaticEvaluationFunction evalFunction) { + _SCL_staticEvaluationFunction = evalFunction; + _SCL_currentEval = evalFunction(board); + _SCL_depthHardLimit = 0; + _SCL_depthHardLimit -= extensionExtraDepth; + + return _SCL_boardEvaluateDynamic( + board, + baseDepth, + SCL_boardWhitesTurn(board) ? SCL_EVALUATION_MAX_SCORE : (-1 * SCL_EVALUATION_MAX_SCORE), + -1); +} + +void SCL_boardRandomMove( + SCL_Board board, + SCL_RandomFunction randFunc, + uint8_t* squareFrom, + uint8_t* squareTo, + char* resultProm) { + *resultProm = (randFunc() < 128) ? ((randFunc() < 128) ? 'r' : 'n') : + ((randFunc() < 128) ? 'b' : 'q'); + + SCL_SquareSet set; + uint8_t white = SCL_boardWhitesTurn(board); + const char* s = board; + + SCL_squareSetClear(set); + + // find squares with pieces that have legal moves + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++s) { + char c = *s; + + if(c != '.' && SCL_pieceIsWhite(c) == white) { + SCL_SquareSet moves; + + SCL_boardGetMoves(board, i, moves); + + if(SCL_squareSetSize(moves) != 0) SCL_squareSetAdd(set, i); + } + } + + *squareFrom = SCL_squareSetGetRandom(set, randFunc); + + SCL_boardGetMoves(board, *squareFrom, set); + + *squareTo = SCL_squareSetGetRandom(set, randFunc); +} + +void SCL_printBoardSimple( + SCL_Board board, + SCL_PutCharFunction putCharFunc, + uint8_t selectSquare, + uint8_t format) { + SCL_SquareSet s; + + SCL_squareSetClear(s); + + SCL_printBoard(board, putCharFunc, s, selectSquare, format, 1, 1, 0); +} + +int16_t SCL_getAIMove( + SCL_Board board, + uint8_t baseDepth, + uint8_t extensionExtraDepth, + uint8_t endgameExtraDepth, + SCL_StaticEvaluationFunction evalFunc, + SCL_RandomFunction randFunc, + uint8_t randomness, + uint8_t repetitionMoveFrom, + uint8_t repetitionMoveTo, + uint8_t* resultFrom, + uint8_t* resultTo, + char* resultProm) { +#if SCL_DEBUG_AI + puts("===== AI debug ====="); + putchar('('); + unsigned char debugFirst = 1; + char moveStr[8]; +#endif + + if(baseDepth == 0) { + SCL_boardRandomMove(board, randFunc, resultFrom, resultTo, resultProm); +#ifndef SCL_EVALUATION_FUNCTION + return evalFunc(board); +#else + return SCL_EVALUATION_FUNCTION(board); +#endif + } + + if(SCL_boardEstimatePhase(board) == SCL_PHASE_ENDGAME) baseDepth += endgameExtraDepth; + + *resultFrom = 0; + *resultTo = 0; + *resultProm = 'q'; + + int16_t bestScore = SCL_boardWhitesTurn(board) ? -1 * SCL_EVALUATION_MAX_SCORE - 1 : + (SCL_EVALUATION_MAX_SCORE + 1); + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) + if(board[i] != '.' && SCL_boardWhitesTurn(board) == SCL_pieceIsWhite(board[i])) { + SCL_SquareSet moves; + + SCL_squareSetClear(moves); + + SCL_boardGetMoves(board, i, moves); + + SCL_SQUARE_SET_ITERATE_BEGIN(moves) + + int16_t score = 0; + +#if SCL_DEBUG_AI + if(debugFirst) + debugFirst = 0; + else + putchar(','); + + printf("%s ", SCL_moveToString(board, i, iteratedSquare, 'q', moveStr)); + +#endif + + if(i != repetitionMoveFrom || iteratedSquare != repetitionMoveTo) { + SCL_MoveUndo undo = SCL_boardMakeMove(board, i, iteratedSquare, 'q'); + + score = + SCL_boardEvaluateDynamic(board, baseDepth - 1, extensionExtraDepth, evalFunc); + + SCL_boardUndoMove(board, undo); + } + + if(randFunc != 0 && randomness > 1 && score < 16000 && score > -16000) { + /*^ We limit randomizing by about half the max score for two reasons: + to prevent over/under flows and secondly we don't want to alter + the highest values for checkmate -- these are modified by tiny + values depending on their depth so as to prevent endless loops in + which most moves are winning, biasing such values would completely + kill that algorithm */ + + int16_t bias = randFunc(); + bias = (bias - 128) / 2; + bias *= randomness - 1; + score += bias; + } + + uint8_t comparison = score == bestScore; + + if((comparison != 1) && ((SCL_boardWhitesTurn(board) && score > bestScore) || + (!SCL_boardWhitesTurn(board) && score < bestScore))) + comparison = 2; + + uint8_t replace = 0; + + if(randFunc == 0) + replace = comparison == 2; + else + replace = + (comparison == 2) || + ((comparison == 1) && (randFunc() < 160)); // not uniform distr. but simple + + if(replace) { + *resultFrom = i; + *resultTo = iteratedSquare; + bestScore = score; + } + + SCL_SQUARE_SET_ITERATE_END + } + +#if SCL_DEBUG_AI + printf(")%d %s\n", bestScore, SCL_moveToString(board, *resultFrom, *resultTo, 'q', moveStr)); + puts("===== AI debug end ===== "); +#endif + + return bestScore; +} + +uint8_t SCL_boardToFEN(SCL_Board board, char* string) { + uint8_t square = 56; + uint8_t spaces = 0; + uint8_t result = 0; + +#define put(c) \ + { \ + *string = (c); \ + string++; \ + result++; \ + } + + while(1) // pieces + { + char s = board[square]; + + if(s == '.') { + spaces++; + } else { + if(spaces != 0) { + put('0' + spaces) spaces = 0; + } + + put(s) + } + + square++; + + if(square % 8 == 0) { + if(spaces != 0) { + put('0' + spaces) spaces = 0; + } + + if(square == 8) break; + + put('/'); + + square -= 16; + } + } + + put(' '); + put(SCL_boardWhitesTurn(board) ? 'w' : 'b'); + put(' '); + + uint8_t b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0; + + if(b != 0) // castling + { + if(b & 0x10) put('K'); + if(b & 0x20) put('Q'); + if(b & 0x40) put('k'); + if(b & 0x80) put('q'); + } else + put('-'); + + put(' '); + + b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f; + + if(b < 8) { + put('a' + b); + put(SCL_boardWhitesTurn(board) ? '6' : '3'); + } else + put('-'); + + for(uint8_t i = 0; i < 2; ++i) { + put(' '); + + uint8_t moves = i == 0 ? ((uint8_t)board[SCL_BOARD_MOVE_COUNT_BYTE]) : + (((uint8_t)board[SCL_BOARD_PLY_BYTE]) / 2 + 1); + + uint8_t hundreds = moves / 100; + uint8_t tens = (moves % 100) / 10; + + if(hundreds != 0) { + put('0' + hundreds); + put('0' + tens); + } else if(tens != 0) + put('0' + tens); + + put('0' + moves % 10); + } + + *string = 0; // terminate the string + + return result + 1; + +#undef put +} + +uint8_t SCL_boardFromFEN(SCL_Board board, const char* string) { + uint8_t square = 56; + + while(1) { + char c = *string; + + if(c == 0) return 0; + + if(c != '/' && c != ' ') // ignore line separators + { + if(c < '9') // empty square sequence + { + while(c > '0') { + board[square] = '.'; + square++; + c--; + } + } else // piece + { + board[square] = c; + square++; + } + } else { + if(square == 8) break; + + square -= 16; + } + + string++; + } + +#define nextChar \ + string++; \ + if(*string == 0) return 0; + + nextChar // space + + board[SCL_BOARD_PLY_BYTE] = *string == 'b'; + nextChar + + nextChar // space + + uint8_t castleEnPassant = 0x0; + + while(*string != ' ') { + switch(*string) { + case 'K': + castleEnPassant |= 0x10; + break; + case 'Q': + castleEnPassant |= 0x20; + break; + case 'k': + castleEnPassant |= 0x40; + break; + case 'q': + castleEnPassant |= 0x80; + break; + default: + castleEnPassant |= 0xf0; + break; // for partial XFEN compat. + } + + nextChar + } + + nextChar // space + + if(*string != '-') { + castleEnPassant |= *string - 'a'; + nextChar + } + else castleEnPassant |= 0x0f; + + nextChar + + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castleEnPassant; + + for(uint8_t i = 0; i < 2; ++i) { + nextChar // space + + uint8_t ply = 0; + + while(1) { + char c = *string; + + if(c < '0' || c > '9') break; + + ply = ply * 10 + (c - '0'); + + string++; + } + + if(i == 0 && *string == 0) return 0; + + if(i == 0) + board[SCL_BOARD_MOVE_COUNT_BYTE] = ply; + else + board[SCL_BOARD_PLY_BYTE] += (ply - 1) * 2; + } + +#if SCL_960_CASTLING + _SCL_board960RememberRookPositions(board); +#endif + + return 1; +#undef nextChar +} + +uint8_t SCL_boardEstimatePhase(SCL_Board board) { + uint16_t totalMaterial = 0; + + uint8_t ply = board[SCL_BOARD_PLY_BYTE]; + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) { + char s = *board; + + if(s != '.') { + int16_t v = SCL_pieceValue(s); + + if(!SCL_pieceIsWhite(s)) v *= -1; + + totalMaterial += v; + } + + board++; + } + + if(totalMaterial < SCL_ENDGAME_MATERIAL_LIMIT) return SCL_PHASE_ENDGAME; + + if(ply <= 10 && (totalMaterial >= SCL_START_MATERIAL - 3 * SCL_VALUE_PAWN)) + return SCL_PHASE_OPENING; + + return SCL_PHASE_MIDGAME; +} + +#define SCL_IMAGE_COUNT 12 + +static const uint8_t SCL_images[8 * SCL_IMAGE_COUNT] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xe7, 0xf7, + 0xf7, 0xaa, 0xff, 0xbd, 0xe7, 0xf7, 0xf7, 0xaa, 0xff, 0xc3, 0xc3, 0xe3, 0xc1, 0x80, + 0xff, 0x99, 0xdb, 0xeb, 0xc9, 0x94, 0xe7, 0xc3, 0x81, 0xc1, 0x94, 0x80, 0xe7, 0xdb, + 0xbd, 0xdd, 0xbe, 0xbe, 0xc3, 0xc3, 0x91, 0xe3, 0x80, 0x80, 0xdb, 0x99, 0x8d, 0xeb, + 0xaa, 0xbe, 0xc3, 0x81, 0xe1, 0xc1, 0xc1, 0xc1, 0xdb, 0xbd, 0xdd, 0xe3, 0xdd, 0xdd, + 0x81, 0x81, 0xc1, 0x9c, 0xc1, 0xc1, 0x81, 0x81, 0xc1, 0x9c, 0xc1, 0xc1}; + +void SCL_drawBoard( + SCL_Board board, + SCL_PutPixelFunction putPixel, + uint8_t selectedSquare, + SCL_SquareSet highlightSquares, + uint8_t blackDown) { + uint8_t row = 0; + uint8_t col = 0; + uint8_t x = 0; + uint8_t y = 0; + uint16_t n = 0; + uint8_t s = 0; + + uint8_t pictureLine = 0; + uint8_t loadLine = 1; + + while(row < 8) { + if(loadLine) { + s = blackDown ? (row * 8 + (7 - col)) : ((7 - row) * 8 + col); + + char piece = board[s]; + + if(piece == '.') + pictureLine = (y == 4) ? 0xef : 0xff; + else { + uint8_t offset = SCL_pieceIsWhite(piece) ? 6 : 0; + piece = SCL_pieceToColor(piece, 1); + + switch(piece) { + case 'R': + offset += 1; + break; + case 'N': + offset += 2; + break; + case 'B': + offset += 3; + break; + case 'K': + offset += 4; + break; + case 'Q': + offset += 5; + break; + default: + break; + } + + pictureLine = SCL_images[y * SCL_IMAGE_COUNT + offset]; + } + + if(SCL_squareSetContains(highlightSquares, s)) pictureLine &= (y % 2) ? 0xaa : 0x55; + + if(s == selectedSquare) pictureLine &= (y == 0 || y == 7) ? 0x00 : ~0x81; + + loadLine = 0; + } + + putPixel(pictureLine & 0x80, n); + pictureLine <<= 1; + + n++; + x++; + + if(x == 8) { + col++; + loadLine = 1; + x = 0; + } + + if(col == 8) { + y++; + col = 0; + x = 0; + } + + if(y == 8) { + row++; + y = 0; + } + } +} + +uint32_t SCL_boardHash32(const SCL_Board board) { + uint32_t result = (board[SCL_BOARD_PLY_BYTE] & 0x01) + + (((uint32_t)((uint8_t)board[SCL_BOARD_ENPASSANT_CASTLE_BYTE])) << 24) + + board[SCL_BOARD_MOVE_COUNT_BYTE]; + + const char* b = board; + + for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b) { + switch(*b) { +#define C(p, n) \ + case p: \ + result ^= (i + 1) * n; \ + break; + // the below number are primes + C('P', 4003) + C('R', 84673) + C('N', 93911) + C('B', 999331) + C('Q', 909091) + C('K', 2796203) + C('p', 4793) + C('r', 19391) + C('n', 391939) + C('b', 108301) + C('q', 174763) + C('k', 2474431) +#undef C + default: + break; + } + } + + // for extra spread of values we swap the low/high parts: + result = (result >> 16) | (result << 16); + + return result; +} + +void SCL_boardDisableCastling(SCL_Board board) { + board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= 0x0f; +} + +uint8_t SCL_boardMoveResetsCount(SCL_Board board, uint8_t squareFrom, uint8_t squareTo) { + return board[squareFrom] == 'P' || board[squareFrom] == 'p' || board[squareTo] != '.'; +} + +void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc, SCL_Board initialState) { + if(SCL_recordLength(r) == 0) return; + + uint16_t pos = 0; + + SCL_Board board; + + if(initialState != 0) + for(uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i) board[i] = initialState[i]; + else + SCL_boardInit(board); + + while(1) { + uint8_t s0, s1; + char p; + + uint8_t state = SCL_recordGetMove(r, pos, &s0, &s1, &p); + + pos++; + + if(pos % 2) { + uint8_t move = pos / 2 + 1; + + if(move / 100 != 0) putCharFunc('0' + move / 100); + + if(move / 10 != 0 || move / 100 != 0) putCharFunc('0' + (move % 100) / 10); + + putCharFunc('0' + move % 10); + + putCharFunc('.'); + putCharFunc(' '); + } + +#if !SCL_960_CASTLING + if((board[s0] == 'K' && s0 == 4 && (s1 == 2 || s1 == 6)) || + (board[s0] == 'k' && s0 == 60 && (s1 == 62 || s1 == 58))) +#else + if((board[s0] == 'K' && board[s1] == 'R') || (board[s0] == 'k' && board[s1] == 'r')) +#endif + { + putCharFunc('O'); + putCharFunc('-'); + putCharFunc('O'); + +#if !SCL_960_CASTLING + if(s1 == 58 || s1 == 2) +#else + if((s1 == (board[SCL_BOARD_EXTRA_BYTE] & 0x07)) || + (s1 == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07))) +#endif + { + putCharFunc('-'); + putCharFunc('O'); + } + } else { + uint8_t pawn = board[s0] == 'P' || board[s0] == 'p'; + + if(!pawn) { + putCharFunc(SCL_pieceToColor(board[s0], 1)); + + // disambiguation: + + uint8_t specify = 0; + + for(int i = 0; i < SCL_BOARD_SQUARES; ++i) + if(i != s0 && board[i] == board[s0]) { + SCL_SquareSet s; + + SCL_squareSetClear(s); + + SCL_boardGetMoves(board, i, s); + + if(SCL_squareSetContains(s, s1)) specify |= (s0 % 8 != s1 % 8) ? 1 : 2; + } + + if(specify & 0x01) putCharFunc('a' + s0 % 8); + + if(specify & 0x02) putCharFunc('1' + s0 / 8); + } + + if(board[s1] != '.' || (pawn && s0 % 8 != s1 % 8 && board[s1] == '.')) // capture? + { + if(pawn) putCharFunc('a' + s0 % 8); + + putCharFunc('x'); + } + + putCharFunc('a' + s1 % 8); + putCharFunc('1' + s1 / 8); + + if(pawn && (s1 >= 56 || s1 <= 7)) // promotion? + { + putCharFunc('='); + putCharFunc(SCL_pieceToColor(p, 1)); + } + } + + SCL_boardMakeMove(board, s0, s1, p); + + uint8_t position = SCL_boardGetPosition(board); + + if(position == SCL_POSITION_CHECK) putCharFunc('+'); + + if(position == SCL_POSITION_MATE) { + putCharFunc('#'); + break; + } else if(state != SCL_RECORD_CONT) { + putCharFunc('*'); + break; + } + + putCharFunc(' '); + } +} + +void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo) { + for(uint16_t i = 0; i < SCL_RECORD_MAX_SIZE; ++i) recordTo[i] = recordFrom[i]; +} + +void SCL_gameInit(SCL_Game* game, const SCL_Board startState) { + game->startState = startState; + + if(startState != 0) + SCL_boardCopy(startState, game->board); + else + SCL_boardInit(game->board); + + SCL_recordInit(game->record); + + for(uint8_t i = 0; i < 14; ++i) game->prevMoves[i] = 0; + + game->state = SCL_GAME_STATE_PLAYING; + game->ply = 0; + + SCL_recordInit(game->record); +} + +uint8_t SCL_gameGetRepetiotionMove(SCL_Game* game, uint8_t* squareFrom, uint8_t* squareTo) { + if(squareFrom != 0 && squareTo != 0) { + *squareFrom = 0; + *squareTo = 0; + } + + /* pos. 1st 2nd 3rd + | | | + v v v + 01 23 45 67 89 AB CD EF + move ab cd ba dc ab cd ba dc */ + + if(game->ply >= 7 && game->prevMoves[0] == game->prevMoves[5] && + game->prevMoves[0] == game->prevMoves[8] && game->prevMoves[0] == game->prevMoves[13] && + + game->prevMoves[1] == game->prevMoves[4] && game->prevMoves[1] == game->prevMoves[9] && + game->prevMoves[1] == game->prevMoves[12] && + + game->prevMoves[2] == game->prevMoves[7] && game->prevMoves[2] == game->prevMoves[10] && + + game->prevMoves[3] == game->prevMoves[6] && game->prevMoves[3] == game->prevMoves[11]) { + if(squareFrom != 0 && squareTo != 0) { + *squareFrom = game->prevMoves[3]; + *squareTo = game->prevMoves[2]; + } + + return 1; + } + + return 0; +} + +void SCL_gameMakeMove(SCL_Game* game, uint8_t squareFrom, uint8_t squareTo, char promoteTo) { + uint8_t repetitionS0, repetitionS1; + + SCL_gameGetRepetiotionMove(game, &repetitionS0, &repetitionS1); + SCL_boardMakeMove(game->board, squareFrom, squareTo, promoteTo); + SCL_recordAdd(game->record, squareFrom, squareTo, promoteTo, SCL_RECORD_CONT); + // ^ TODO: SCL_RECORD_CONT + + game->ply++; + + for(uint8_t i = 0; i < 14 - 2; ++i) game->prevMoves[i] = game->prevMoves[i + 2]; + + game->prevMoves[12] = squareFrom; + game->prevMoves[13] = squareTo; + + if(squareFrom == repetitionS0 && squareTo == repetitionS1) + game->state = SCL_GAME_STATE_DRAW_REPETITION; + else if(game->board[SCL_BOARD_MOVE_COUNT_BYTE] >= 50) + game->state = SCL_GAME_STATE_DRAW_50; + else { + uint8_t position = SCL_boardGetPosition(game->board); + + switch(position) { + case SCL_POSITION_MATE: + game->state = SCL_boardWhitesTurn(game->board) ? SCL_GAME_STATE_BLACK_WIN : + SCL_GAME_STATE_WHITE_WIN; + break; + + case SCL_POSITION_STALEMATE: + game->state = SCL_GAME_STATE_DRAW_STALEMATE; + break; + + case SCL_POSITION_DEAD: + game->state = SCL_GAME_STATE_DRAW_DEAD; + break; + + default: + break; + } + } +} + +uint8_t SCL_gameUndoMove(SCL_Game* game) { + if(game->ply == 0) return 0; + + if((game->ply - 1) > SCL_recordLength(game->record)) return 0; // can't undo, lacking record + + SCL_Record r; + + SCL_recordCopy(game->record, r); + + uint16_t applyMoves = game->ply - 1; + + SCL_gameInit(game, game->startState); + + for(uint16_t i = 0; i < applyMoves; ++i) { + uint8_t s0, s1; + char p; + + SCL_recordGetMove(r, i, &s0, &s1, &p); + SCL_gameMakeMove(game, s0, s1, p); + } + + return 1; +} + +uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom, uint8_t squareTo) { + if(squareFrom >= SCL_BOARD_SQUARES || squareTo >= SCL_BOARD_SQUARES) return 0; + + char piece = board[squareFrom]; + + if((piece == '.') || (SCL_boardWhitesTurn(board) != SCL_pieceIsWhite(piece))) return 0; + + SCL_SquareSet moves; + + SCL_boardGetMoves(board, squareFrom, moves); + + return SCL_squareSetContains(moves, squareTo); +} + +#endif // guard diff --git a/applications/external/chess/flipchess.c b/applications/external/chess/flipchess.c new file mode 100644 index 000000000..3a1ca8c79 --- /dev/null +++ b/applications/external/chess/flipchess.c @@ -0,0 +1,179 @@ +#include "flipchess.h" +#include "helpers/flipchess_haptic.h" + +bool flipchess_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + FlipChess* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +void flipchess_tick_event_callback(void* context) { + furi_assert(context); + FlipChess* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +//leave app if back button pressed +bool flipchess_navigation_event_callback(void* context) { + furi_assert(context); + FlipChess* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void text_input_callback(void* context) { + furi_assert(context); + FlipChess* app = context; + bool handled = false; + + // check that there is text in the input + if(strlen(app->input_text) > 0) { + if(app->input_state == FlipChessTextInputGame) { + if(app->import_game == 1) { + strncpy(app->import_game_text, app->input_text, TEXT_SIZE); + + uint8_t status = FlipChessStatusNone; + if(status == FlipChessStatusNone) { + //notification_message(app->notification, &sequence_blink_cyan_100); + flipchess_play_happy_bump(app); + } else { + //notification_message(app->notification, &sequence_blink_red_100); + flipchess_play_long_bump(app); + } + } + // reset input state + app->input_state = FlipChessTextInputDefault; + handled = true; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdMenu); + } + } + + if(!handled) { + // reset input state + app->input_state = FlipChessTextInputDefault; + view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdMenu); + } +} + +FlipChess* flipchess_app_alloc() { + FlipChess* app = malloc(sizeof(FlipChess)); + 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(&flipchess_scene_handlers, app); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, flipchess_navigation_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, flipchess_tick_event_callback, 100); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, flipchess_custom_event_callback); + app->submenu = submenu_alloc(); + + // Settings + app->haptic = FlipChessHapticOn; + app->white_mode = FlipChessPlayerHuman; + app->black_mode = FlipChessPlayerAI1; + + // Startscreen + app->sound = 0; + // Main menu + app->import_game = 0; + + // Text input + app->input_state = FlipChessTextInputDefault; + + view_dispatcher_add_view( + app->view_dispatcher, FlipChessViewIdMenu, submenu_get_view(app->submenu)); + app->flipchess_startscreen = flipchess_startscreen_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipChessViewIdStartscreen, + flipchess_startscreen_get_view(app->flipchess_startscreen)); + app->flipchess_scene_1 = flipchess_scene_1_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipChessViewIdScene1, + flipchess_scene_1_get_view(app->flipchess_scene_1)); + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipChessViewIdSettings, + 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, FlipChessViewIdTextInput, text_input_get_view(app->text_input)); + + //End Scene Additions + + return app; +} + +void flipchess_app_free(FlipChess* 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, FlipChessViewIdMenu); + view_dispatcher_remove_view(app->view_dispatcher, FlipChessViewIdScene1); + view_dispatcher_remove_view(app->view_dispatcher, FlipChessViewIdSettings); + view_dispatcher_remove_view(app->view_dispatcher, FlipChessViewIdTextInput); + 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(FlipChess)); + free(app); +} + +int32_t flipchess_app(void* p) { + UNUSED(p); + FlipChess* app = flipchess_app_alloc(); + + // Disabled because causes exit on custom firmwares such as RM + /*if(!furi_hal_region_is_provisioned()) { + flipchess_app_free(app); + return 1; + }*/ + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene( + app->scene_manager, FlipChessSceneStartscreen); //Start with start screen + //scene_manager_next_scene(app->scene_manager, FlipChessSceneMenu); //if you want to directly start with Menu + + furi_hal_random_init(); + // furi_hal_power_suppress_charge_enter(); + + view_dispatcher_run(app->view_dispatcher); + + // furi_hal_power_suppress_charge_exit(); + flipchess_app_free(app); + + return 0; +} diff --git a/applications/external/chess/flipchess.h b/applications/external/chess/flipchess.h new file mode 100644 index 000000000..2e6c2f124 --- /dev/null +++ b/applications/external/chess/flipchess.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/flipchess_scene.h" +#include "views/flipchess_startscreen.h" +#include "views/flipchess_scene_1.h" + +#define FLIPCHESS_VERSION "v1.9.0" + +#define TEXT_BUFFER_SIZE 96 +#define TEXT_SIZE (TEXT_BUFFER_SIZE - 1) + +typedef struct { + Gui* gui; + NotificationApp* notification; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + SceneManager* scene_manager; + VariableItemList* variable_item_list; + TextInput* text_input; + FlipChessStartscreen* flipchess_startscreen; + FlipChessScene1* flipchess_scene_1; + // Settings options + int haptic; + int white_mode; + int black_mode; + // Startscreen options + uint8_t sound; + // Main menu options + uint8_t import_game; + // Text input + uint8_t input_state; + char import_game_text[TEXT_BUFFER_SIZE]; + char input_text[TEXT_BUFFER_SIZE]; +} FlipChess; + +typedef enum { + FlipChessViewIdStartscreen, + FlipChessViewIdMenu, + FlipChessViewIdScene1, + FlipChessViewIdSettings, + FlipChessViewIdTextInput, +} FlipChessViewId; + +typedef enum { + FlipChessHapticOff, + FlipChessHapticOn, +} FlipChessHapticState; + +typedef enum { + FlipChessPlayerHuman = 0, + FlipChessPlayerAI1 = 1, + FlipChessPlayerAI2 = 2, + FlipChessPlayerAI3 = 3, +} FlipChessPlayerMode; + +typedef enum { FlipChessTextInputDefault, FlipChessTextInputGame } FlipChessTextInputState; + +typedef enum { + FlipChessStatusNone = 0, + FlipChessStatusMovePlayer = 1, + FlipChessStatusMoveAI = 2, + FlipChessStatusMoveUndo = 3, + FlipChessStatusReturn = 10, + FlipChessStatusLoadError = 11, + FlipChessStatusSaveError = 12, +} FlipChessStatus; diff --git a/applications/external/chess/flipchess_10px.png b/applications/external/chess/flipchess_10px.png new file mode 100644 index 000000000..c7b0779c1 Binary files /dev/null and b/applications/external/chess/flipchess_10px.png differ diff --git a/applications/external/chess/helpers/flipchess_custom_event.h b/applications/external/chess/helpers/flipchess_custom_event.h new file mode 100644 index 000000000..4391a2fa6 --- /dev/null +++ b/applications/external/chess/helpers/flipchess_custom_event.h @@ -0,0 +1,16 @@ +#pragma once + +typedef enum { + FlipChessCustomEventStartscreenUp, + FlipChessCustomEventStartscreenDown, + FlipChessCustomEventStartscreenLeft, + FlipChessCustomEventStartscreenRight, + FlipChessCustomEventStartscreenOk, + FlipChessCustomEventStartscreenBack, + FlipChessCustomEventScene1Up, + FlipChessCustomEventScene1Down, + FlipChessCustomEventScene1Left, + FlipChessCustomEventScene1Right, + FlipChessCustomEventScene1Ok, + FlipChessCustomEventScene1Back, +} FlipChessCustomEvent; \ No newline at end of file diff --git a/applications/external/chess/helpers/flipchess_file.c b/applications/external/chess/helpers/flipchess_file.c new file mode 100644 index 000000000..0a3cf5a4f --- /dev/null +++ b/applications/external/chess/helpers/flipchess_file.c @@ -0,0 +1,153 @@ +#include "flipchess_file.h" +#include +#include + +// #define FLIPCHESS_APP_BASE_FOLDER APP_BOARDA_PATH("flipchess") +#define FLIPCHESS_APP_BASE_FOLDER EXT_PATH("apps_data/flipchess") +#define FLIPCHESS_APP_BASE_FOLDER_PATH(path) FLIPCHESS_APP_BASE_FOLDER "/" path +#define FLIPCHESS_BOARD_FILE_NAME "board_fen.txt" +#define FLIPCHESS_BOARD_FILE_NAME_BAK "board_fen.bak" +#define FLIPCHESS_BOARD_PATH FLIPCHESS_APP_BASE_FOLDER_PATH(FLIPCHESS_BOARD_FILE_NAME) +#define FLIPCHESS_BOARD_PATH_BAK FLIPCHESS_APP_BASE_FOLDER_PATH(FLIPCHESS_BOARD_FILE_NAME_BAK) + +#define FILE_MAX_PATH_LEN 48 +#define FILE_MAX_CHARS 94 + +bool flipchess_has_file(const FlipChessFile file_type, const char* file_name, const bool remove) { + bool ret = false; + const char* path; + if(file_type == FlipChessFileBoard) { + path = FLIPCHESS_BOARD_PATH; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPCHESS_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 flipchess_load_file(char* contents, const FlipChessFile file_type, const char* file_name) { + bool ret = false; + const char* path; + if(file_type == FlipChessFileBoard) { + path = FLIPCHESS_BOARD_PATH; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPCHESS_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)) { + if(i < FILE_MAX_CHARS) { + contents[i] = chr; + } + i++; + } + ret = true; + } else { + contents[0] = '\0'; + ret = false; + } + storage_file_close(settings_file); + storage_file_free(settings_file); + furi_record_close(RECORD_STORAGE); + + if(strlen(contents) > 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) { + contents[0] = '\0'; + ret = false; + } + // if(layout_file_info.size != 256) { + // memzero(settings, strlen(settings)); + // settings[0] = '\0'; + // } + } + + return ret; +} + +bool flipchess_save_file( + const char* settings, + const FlipChessFile file_type, + const char* file_name, + const bool append, + const bool overwrite) { + bool ret = false; + const char* path; + const char* path_bak; + if(file_type == FlipChessFileBoard) { + path = FLIPCHESS_BOARD_PATH; + path_bak = FLIPCHESS_BOARD_PATH_BAK; + } else { + char path_buf[FILE_MAX_PATH_LEN] = {0}; + strcpy(path_buf, FLIPCHESS_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); + + // try to create the folder + storage_simply_mkdir(fs_api, FLIPCHESS_APP_BASE_FOLDER); + + if(overwrite) { + storage_simply_remove(fs_api, path); + } + + 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) { + if(overwrite) { + storage_simply_remove(fs_api, path_bak); + } + + 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; +} diff --git a/applications/external/chess/helpers/flipchess_file.h b/applications/external/chess/helpers/flipchess_file.h new file mode 100644 index 000000000..1550b2e2d --- /dev/null +++ b/applications/external/chess/helpers/flipchess_file.h @@ -0,0 +1,15 @@ +#include + +typedef enum { + FlipChessFileBoard, + FlipChessFileOther, +} FlipChessFile; + +bool flipchess_has_file(const FlipChessFile file_type, const char* file_name, const bool remove); +bool flipchess_load_file(char* contents, const FlipChessFile file_type, const char* file_name); +bool flipchess_save_file( + const char* contents, + const FlipChessFile file_type, + const char* file_name, + const bool append, + const bool overwrite); \ No newline at end of file diff --git a/applications/external/flipbip/helpers/flipbip_haptic.c b/applications/external/chess/helpers/flipchess_haptic.c similarity index 74% rename from applications/external/flipbip/helpers/flipbip_haptic.c rename to applications/external/chess/helpers/flipchess_haptic.c index c5608efa5..b07fd73df 100644 --- a/applications/external/flipbip/helpers/flipbip_haptic.c +++ b/applications/external/chess/helpers/flipchess_haptic.c @@ -1,8 +1,8 @@ -#include "flipbip_haptic.h" -#include "../flipbip.h" +#include "flipchess_haptic.h" +#include "../flipchess.h" -void flipbip_play_happy_bump(void* context) { - FlipBip* app = context; +void flipchess_play_happy_bump(void* context) { + FlipChess* app = context; if(app->haptic != 1) { return; } @@ -11,8 +11,8 @@ void flipbip_play_happy_bump(void* context) { notification_message(app->notification, &sequence_reset_vibro); } -void flipbip_play_bad_bump(void* context) { - FlipBip* app = context; +void flipchess_play_bad_bump(void* context) { + FlipChess* app = context; if(app->haptic != 1) { return; } @@ -21,8 +21,8 @@ void flipbip_play_bad_bump(void* context) { notification_message(app->notification, &sequence_reset_vibro); } -void flipbip_play_long_bump(void* context) { - FlipBip* app = context; +void flipchess_play_long_bump(void* context) { + FlipChess* app = context; if(app->haptic != 1) { return; } diff --git a/applications/external/chess/helpers/flipchess_haptic.h b/applications/external/chess/helpers/flipchess_haptic.h new file mode 100644 index 000000000..19c14a3e6 --- /dev/null +++ b/applications/external/chess/helpers/flipchess_haptic.h @@ -0,0 +1,7 @@ +#include + +void flipchess_play_happy_bump(void* context); + +void flipchess_play_bad_bump(void* context); + +void flipchess_play_long_bump(void* context); diff --git a/applications/external/chess/helpers/flipchess_voice.cpp b/applications/external/chess/helpers/flipchess_voice.cpp new file mode 100644 index 000000000..9e0c19908 --- /dev/null +++ b/applications/external/chess/helpers/flipchess_voice.cpp @@ -0,0 +1,37 @@ +#include "flipchess_voice.h" +#include +#include +#include "../sam/stm32_sam.h" +STM32SAM voice; + +void flipchess_voice_shall_we_play() { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say("SHAAL WE PLAY AY GAME?"); + furi_hal_speaker_release(); + } +} + +void flipchess_voice_which_side() { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say("WHICH SIDE DO YOU WANT?"); + furi_hal_speaker_release(); + } +} + +void flipchess_voice_how_about_chess() { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say("HOW ABOUT A NICE GAME OF CHESS?"); + furi_hal_speaker_release(); + } +} + +void flipchess_voice_a_strange_game() { + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) { + voice.begin(); + voice.say("A STRANGE GAME... THE ONLY WINNING MOVE IS NOT TO PLAY."); + furi_hal_speaker_release(); + } +} \ No newline at end of file diff --git a/applications/external/chess/helpers/flipchess_voice.h b/applications/external/chess/helpers/flipchess_voice.h new file mode 100644 index 000000000..3b1060118 --- /dev/null +++ b/applications/external/chess/helpers/flipchess_voice.h @@ -0,0 +1,12 @@ +#ifdef __cplusplus +#define EXTERNC extern "C" +#else +#define EXTERNC +#endif + +EXTERNC void flipchess_voice_shall_we_play(); +EXTERNC void flipchess_voice_which_side(); +EXTERNC void flipchess_voice_how_about_chess(); +EXTERNC void flipchess_voice_a_strange_game(); + +#undef EXTERNC diff --git a/applications/external/chess/icons/FLIPR_128x64.png b/applications/external/chess/icons/FLIPR_128x64.png new file mode 100644 index 000000000..48d3684a1 Binary files /dev/null and b/applications/external/chess/icons/FLIPR_128x64.png differ diff --git a/applications/external/chess/sam/stm32_sam.cpp b/applications/external/chess/sam/stm32_sam.cpp new file mode 100644 index 000000000..41053a5ee --- /dev/null +++ b/applications/external/chess/sam/stm32_sam.cpp @@ -0,0 +1,5703 @@ + +#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(i < 256) { + input[i] = phonetic ? '\x9b' : '['; + } + + if(!phonetic) { + if(!TextToPhonemes((unsigned char*)input)) { + return; + } + } + + 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/chess/sam/stm32_sam.h b/applications/external/chess/sam/stm32_sam.h new file mode 100644 index 000000000..910227ac3 --- /dev/null +++ b/applications/external/chess/sam/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 diff --git a/applications/external/chess/scenes/flipchess_scene.c b/applications/external/chess/scenes/flipchess_scene.c new file mode 100644 index 000000000..767afbeb2 --- /dev/null +++ b/applications/external/chess/scenes/flipchess_scene.c @@ -0,0 +1,30 @@ +#include "flipchess_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const flipchess_on_enter_handlers[])(void*) = { +#include "flipchess_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 flipchess_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "flipchess_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 flipchess_on_exit_handlers[])(void* context) = { +#include "flipchess_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers flipchess_scene_handlers = { + .on_enter_handlers = flipchess_on_enter_handlers, + .on_event_handlers = flipchess_on_event_handlers, + .on_exit_handlers = flipchess_on_exit_handlers, + .scene_num = FlipChessSceneNum, +}; diff --git a/applications/external/chess/scenes/flipchess_scene.h b/applications/external/chess/scenes/flipchess_scene.h new file mode 100644 index 000000000..c9912727d --- /dev/null +++ b/applications/external/chess/scenes/flipchess_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FlipChessScene##id, +typedef enum { +#include "flipchess_scene_config.h" + FlipChessSceneNum, +} FlipChessScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers flipchess_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "flipchess_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 "flipchess_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 "flipchess_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/chess/scenes/flipchess_scene_config.h b/applications/external/chess/scenes/flipchess_scene_config.h new file mode 100644 index 000000000..a1cc0d51d --- /dev/null +++ b/applications/external/chess/scenes/flipchess_scene_config.h @@ -0,0 +1,4 @@ +ADD_SCENE(flipchess, startscreen, Startscreen) +ADD_SCENE(flipchess, menu, Menu) +ADD_SCENE(flipchess, scene_1, Scene_1) +ADD_SCENE(flipchess, settings, Settings) \ No newline at end of file diff --git a/applications/external/chess/scenes/flipchess_scene_menu.c b/applications/external/chess/scenes/flipchess_scene_menu.c new file mode 100644 index 000000000..448bfc62e --- /dev/null +++ b/applications/external/chess/scenes/flipchess_scene_menu.c @@ -0,0 +1,91 @@ +#include "../flipchess.h" + +enum SubmenuIndex { + SubmenuIndexScene1New = 10, + SubmenuIndexScene1Resume, + SubmenuIndexScene1Import, + SubmenuIndexSettings, +}; + +void flipchess_scene_menu_submenu_callback(void* context, uint32_t index) { + FlipChess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void flipchess_scene_menu_on_enter(void* context) { + FlipChess* app = context; + + submenu_add_item( + app->submenu, + "New Game", + SubmenuIndexScene1New, + flipchess_scene_menu_submenu_callback, + app); + + if(app->import_game == 1) { + submenu_add_item( + app->submenu, + "Resume Game", + SubmenuIndexScene1Resume, + flipchess_scene_menu_submenu_callback, + app); + } + + // submenu_add_item( + // app->submenu, + // "Import Game", + // SubmenuIndexScene1Import, + // flipchess_scene_menu_submenu_callback, + // app); + + submenu_add_item( + app->submenu, "Settings", SubmenuIndexSettings, flipchess_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, FlipChessSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdMenu); +} + +bool flipchess_scene_menu_on_event(void* context, SceneManagerEvent event) { + FlipChess* 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 == SubmenuIndexScene1New) { + app->import_game = 0; + scene_manager_set_scene_state( + app->scene_manager, FlipChessSceneMenu, SubmenuIndexScene1New); + scene_manager_next_scene(app->scene_manager, FlipChessSceneScene_1); + return true; + } + if(event.event == SubmenuIndexScene1Resume) { + app->import_game = 1; + scene_manager_set_scene_state( + app->scene_manager, FlipChessSceneMenu, SubmenuIndexScene1Resume); + scene_manager_next_scene(app->scene_manager, FlipChessSceneScene_1); + return true; + } else if(event.event == SubmenuIndexScene1Import) { + app->import_game = 1; + app->input_state = FlipChessTextInputGame; + text_input_set_header_text(app->text_input, "Enter board FEN"); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdTextInput); + return true; + } else if(event.event == SubmenuIndexSettings) { + scene_manager_set_scene_state( + app->scene_manager, FlipChessSceneMenu, SubmenuIndexSettings); + scene_manager_next_scene(app->scene_manager, FlipChessSceneSettings); + return true; + } + } + return false; +} + +void flipchess_scene_menu_on_exit(void* context) { + FlipChess* app = context; + submenu_reset(app->submenu); +} \ No newline at end of file diff --git a/applications/external/chess/scenes/flipchess_scene_scene_1.c b/applications/external/chess/scenes/flipchess_scene_scene_1.c new file mode 100644 index 000000000..11e96b582 --- /dev/null +++ b/applications/external/chess/scenes/flipchess_scene_scene_1.c @@ -0,0 +1,55 @@ +#include "../flipchess.h" +#include "../helpers/flipchess_file.h" +#include "../helpers/flipchess_custom_event.h" +#include "../views/flipchess_scene_1.h" + +void flipchess_scene_1_callback(FlipChessCustomEvent event, void* context) { + furi_assert(context); + FlipChess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void flipchess_scene_scene_1_on_enter(void* context) { + furi_assert(context); + FlipChess* app = context; + + flipchess_scene_1_set_callback(app->flipchess_scene_1, flipchess_scene_1_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdScene1); +} + +bool flipchess_scene_scene_1_on_event(void* context, SceneManagerEvent event) { + FlipChess* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case FlipChessCustomEventScene1Left: + case FlipChessCustomEventScene1Right: + break; + case FlipChessCustomEventScene1Up: + case FlipChessCustomEventScene1Down: + break; + case FlipChessCustomEventScene1Back: + 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, FlipChessSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void flipchess_scene_scene_1_on_exit(void* context) { + FlipChess* app = context; + + if(app->import_game == 1 && strlen(app->import_game_text) > 0) { + flipchess_save_file(app->import_game_text, FlipChessFileBoard, NULL, false, true); + } +} \ No newline at end of file diff --git a/applications/external/chess/scenes/flipchess_scene_settings.c b/applications/external/chess/scenes/flipchess_scene_settings.c new file mode 100644 index 000000000..51833b99c --- /dev/null +++ b/applications/external/chess/scenes/flipchess_scene_settings.c @@ -0,0 +1,102 @@ +#include "../flipchess.h" +#include "../helpers/flipchess_voice.h" +#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] = { + FlipChessHapticOff, + FlipChessHapticOn, +}; + +const char* const player_mode_text[4] = { + "Human", + "CPU 1", + "CPU 2", + "CPU 3", +}; +const uint32_t player_mode_value[4] = { + FlipChessPlayerHuman, + FlipChessPlayerAI1, + FlipChessPlayerAI2, + FlipChessPlayerAI3, +}; + +static void flipchess_scene_settings_set_haptic(VariableItem* item) { + FlipChess* 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 flipchess_scene_settings_set_white_mode(VariableItem* item) { + FlipChess* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, player_mode_text[index]); + app->white_mode = player_mode_value[index]; +} + +static void flipchess_scene_settings_set_black_mode(VariableItem* item) { + FlipChess* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, player_mode_text[index]); + app->black_mode = player_mode_value[index]; +} + +void flipchess_scene_settings_submenu_callback(void* context, uint32_t index) { + FlipChess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void flipchess_scene_settings_on_enter(void* context) { + FlipChess* app = context; + VariableItem* item; + uint8_t value_index; + + if(app->sound == 1) { + flipchess_voice_which_side(); + } + + // White mode + item = variable_item_list_add( + app->variable_item_list, "White:", 4, flipchess_scene_settings_set_white_mode, app); + value_index = value_index_uint32(app->white_mode, player_mode_value, 4); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, player_mode_text[value_index]); + + // Black mode + item = variable_item_list_add( + app->variable_item_list, "Black:", 4, flipchess_scene_settings_set_black_mode, app); + value_index = value_index_uint32(app->black_mode, player_mode_value, 4); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, player_mode_text[value_index]); + + // Vibro on/off + item = variable_item_list_add( + app->variable_item_list, "Vibro/Haptic:", 2, flipchess_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]); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdSettings); +} + +bool flipchess_scene_settings_on_event(void* context, SceneManagerEvent event) { + FlipChess* app = context; + UNUSED(app); + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + } + return consumed; +} + +void flipchess_scene_settings_on_exit(void* context) { + FlipChess* 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/chess/scenes/flipchess_scene_startscreen.c b/applications/external/chess/scenes/flipchess_scene_startscreen.c new file mode 100644 index 000000000..f9dfde720 --- /dev/null +++ b/applications/external/chess/scenes/flipchess_scene_startscreen.c @@ -0,0 +1,67 @@ +#include "../flipchess.h" +#include "../helpers/flipchess_voice.h" +#include "../helpers/flipchess_file.h" +#include "../helpers/flipchess_custom_event.h" +#include "../views/flipchess_startscreen.h" + +void flipchess_scene_startscreen_callback(FlipChessCustomEvent event, void* context) { + furi_assert(context); + FlipChess* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void flipchess_scene_startscreen_on_enter(void* context) { + furi_assert(context); + FlipChess* app = context; + + if(flipchess_has_file(FlipChessFileBoard, NULL, false)) { + if(flipchess_load_file(app->import_game_text, FlipChessFileBoard, NULL)) { + app->import_game = 1; + } + } + + flipchess_startscreen_set_callback( + app->flipchess_startscreen, flipchess_scene_startscreen_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdStartscreen); +} + +bool flipchess_scene_startscreen_on_event(void* context, SceneManagerEvent event) { + FlipChess* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case FlipChessCustomEventStartscreenLeft: + case FlipChessCustomEventStartscreenRight: + break; + case FlipChessCustomEventStartscreenUp: + case FlipChessCustomEventStartscreenDown: + break; + case FlipChessCustomEventStartscreenOk: + scene_manager_next_scene(app->scene_manager, FlipChessSceneMenu); + consumed = true; + break; + case FlipChessCustomEventStartscreenBack: + 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, FlipChessSceneStartscreen)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void flipchess_scene_startscreen_on_exit(void* context) { + FlipChess* app = context; + + if(app->sound == 1) { + flipchess_voice_shall_we_play(); + } +} \ No newline at end of file diff --git a/applications/external/chess/views/flipchess_scene_1.c b/applications/external/chess/views/flipchess_scene_1.c new file mode 100644 index 000000000..82a0864da --- /dev/null +++ b/applications/external/chess/views/flipchess_scene_1.c @@ -0,0 +1,727 @@ +#include "../flipchess.h" +#include +// #include +// #include +#include +#include +//#include +#include +//#include "flipchess_icons.h" +#include "../helpers/flipchess_voice.h" +#include "../helpers/flipchess_haptic.h" + +#define SCL_960_CASTLING 0 // setting to 1 compiles a 960 version of smolchess +#define XBOARD_DEBUG 0 // will create files with xboard communication +#define SCL_EVALUATION_FUNCTION SCL_boardEvaluateStatic +#define SCL_DEBUG_AI 0 + +#include "../chess/smallchesslib.h" + +#define ENABLE_960 0 // setting to 1 enables 960 chess +#define MAX_TEXT_LEN 15 // 15 = max length of text +#define MAX_TEXT_BUF (MAX_TEXT_LEN + 1) // max length of text + null terminator +#define THREAD_WAIT_TIME 20 // time to wait for draw thread to finish + +struct FlipChessScene1 { + View* view; + FlipChessScene1Callback callback; + void* context; +}; +typedef struct { + uint8_t paramPlayerW; + uint8_t paramPlayerB; + + uint8_t paramAnalyze; // depth of analysis + uint8_t paramMoves; + uint8_t paramInfo; + uint8_t paramFlipBoard; + uint8_t paramExit; + uint16_t paramStep; + char* paramFEN; + char* paramPGN; + + int clockSeconds; + SCL_Game game; + SCL_Board startState; + +#if ENABLE_960 + int16_t random960PosNumber; +#endif + + //uint8_t picture[SCL_BOARD_PICTURE_WIDTH * SCL_BOARD_PICTURE_WIDTH]; + uint8_t squareSelected; + uint8_t squareSelectedLast; + + char* msg; + char* msg2; + char* msg3; + char moveString[MAX_TEXT_BUF]; + char moveString2[MAX_TEXT_BUF]; + char moveString3[MAX_TEXT_BUF]; + uint8_t thinking; + + SCL_SquareSet moveHighlight; + uint8_t squareFrom; + uint8_t squareTo; + uint8_t turnState; + +} FlipChessScene1Model; + +static uint8_t picture[SCL_BOARD_PICTURE_WIDTH * SCL_BOARD_PICTURE_WIDTH]; + +void flipchess_putImagePixel(uint8_t pixel, uint16_t index) { + picture[index] = pixel; +} + +uint8_t flipchess_stringsEqual(const char* s1, const char* s2, int max) { + for(int i = 0; i < max; ++i) { + if(*s1 != *s2) return 0; + + if(*s1 == 0) return 1; + + s1++; + s2++; + } + + return 1; +} + +int16_t flipchess_makeAIMove( + SCL_Board board, + uint8_t* s0, + uint8_t* s1, + char* prom, + FlipChessScene1Model* model) { + uint8_t level = SCL_boardWhitesTurn(board) ? model->paramPlayerW : model->paramPlayerB; + uint8_t depth = (level > 0) ? level : 1; + uint8_t extraDepth = 3; + uint8_t endgameDepth = 1; + uint8_t randomness = + model->game.ply < 2 ? 1 : 0; /* in first moves increase randomness for different + openings */ + uint8_t rs0, rs1; + + SCL_gameGetRepetiotionMove(&(model->game), &rs0, &rs1); + + if(model->clockSeconds >= 0) // when using clock, choose AI params accordingly + { + if(model->clockSeconds <= 5) { + depth = 1; + extraDepth = 2; + endgameDepth = 0; + } else if(model->clockSeconds < 15) { + depth = 2; + extraDepth = 2; + } else if(model->clockSeconds < 100) { + depth = 2; + } else if(model->clockSeconds < 5 * 60) { + depth = 3; + } else { + depth = 3; + extraDepth = 4; + } + } + + return SCL_getAIMove( + board, + depth, + extraDepth, + endgameDepth, + SCL_boardEvaluateStatic, + SCL_randomBetter, + randomness, + rs0, + rs1, + s0, + s1, + prom); +} + +bool flipchess_isPlayerTurn(FlipChessScene1Model* model) { + return (SCL_boardWhitesTurn(model->game.board) && model->paramPlayerW == 0) || + (!SCL_boardWhitesTurn(model->game.board) && model->paramPlayerB == 0); +} + +void flipchess_shiftMessages(FlipChessScene1Model* model) { + // shift messages + model->msg3 = model->msg2; + model->msg2 = model->msg; + strncpy(model->moveString3, model->moveString2, MAX_TEXT_LEN); + strncpy(model->moveString2, model->moveString, MAX_TEXT_LEN); +} + +void flipchess_drawBoard(FlipChessScene1Model* model) { + // draw chess board + SCL_drawBoard( + model->game.board, + flipchess_putImagePixel, + model->squareSelected, + model->moveHighlight, + model->paramFlipBoard); +} + +uint8_t flipchess_saveState(FlipChess* app, FlipChessScene1Model* model) { + for(uint8_t i = 0; i < SCL_FEN_MAX_LENGTH; i++) { + app->import_game_text[i] = '\0'; + } + const uint8_t res = SCL_boardToFEN(model->game.board, app->import_game_text); + if(res > 0) { + app->import_game = 1; + } + return res; +} + +uint8_t flipchess_turn(FlipChessScene1Model* model) { + // 0: none, 1: player, 2: AI, 3: undo + uint8_t moveType = FlipChessStatusNone; + + // if(model->paramInfo) { + + // if(model->random960PosNumber >= 0) + // printf("960 random position number: %d\n", model->random960PosNumber); + + // printf("ply number: %d\n", model->game.ply); + + // int16_t eval = SCL_boardEvaluateStatic(model->game.board); + // printf( + // "board static evaluation: %lf (%d)\n", + // ((double)eval) / ((double)SCL_VALUE_PAWN), + // eval); + // printf("board hash: %u\n", SCL_boardHash32(model->game.board)); + // printf("phase: "); + + // switch(SCL_boardEstimatePhase(model->game.board)) { + // case SCL_PHASE_OPENING: + // puts("opening"); + // break; + // case SCL_PHASE_ENDGAME: + // puts("endgame"); + // break; + // default: + // puts("midgame"); + // break; + // } + + // printf( + // "en passant: %d\n", + // ((model->game.board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f) + 1) % 16); + // printf( + // "50 move rule count: %d\n", model->game.board[SCL_BOARD_MOVE_COUNT_BYTE]); + + // if(model->paramFEN == NULL && model->paramPGN == NULL) { + // printf("PGN: "); + // SCL_printPGN(model->game.record, putCharacter, startState); + // putchar('\n'); + // } + // } + + if(model->game.state != SCL_GAME_STATE_PLAYING) { + model->paramExit = FlipChessStatusNone; + + } else { + char movePromote = 'q'; + + if(flipchess_isPlayerTurn(model)) { + // if(stringsEqual(string, "undo", 5)) + // moveType = FlipChessStatusMoveUndo; + // else if(stringsEqual(string, "quit", 5)) + // break; + + if(model->turnState == 0 && model->squareSelected != 255) { + model->squareFrom = model->squareSelected; + model->turnState = 1; + } else if(model->turnState == 1 && model->squareSelected != 255) { + model->squareTo = model->squareSelected; + model->turnState = 2; + model->squareSelectedLast = model->squareSelected; + //model->squareSelected = 255; + } + + if(model->turnState == 1 && model->squareFrom != 255) { + if((model->game.board[model->squareFrom] != '.') && + (SCL_pieceIsWhite(model->game.board[model->squareFrom]) == + SCL_boardWhitesTurn(model->game.board))) { + SCL_boardGetMoves(model->game.board, model->squareFrom, model->moveHighlight); + } + } else if(model->turnState == 2) { + if(SCL_squareSetContains(model->moveHighlight, model->squareTo)) { + moveType = FlipChessStatusMovePlayer; + } + model->turnState = 0; + SCL_squareSetClear(model->moveHighlight); + } + + } else { + model->squareSelected = 255; + flipchess_makeAIMove( + model->game.board, &(model->squareFrom), &(model->squareTo), &movePromote, model); + moveType = FlipChessStatusMoveAI; + model->turnState = 0; + } + + if(moveType == FlipChessStatusMovePlayer || moveType == FlipChessStatusMoveAI) { + flipchess_shiftMessages(model); + + SCL_moveToString( + model->game.board, + model->squareFrom, + model->squareTo, + movePromote, + model->moveString); + + SCL_gameMakeMove(&(model->game), model->squareFrom, model->squareTo, movePromote); + + SCL_squareSetClear(model->moveHighlight); + SCL_squareSetAdd(model->moveHighlight, model->squareFrom); + SCL_squareSetAdd(model->moveHighlight, model->squareTo); + } else if(moveType == FlipChessStatusMoveUndo) { + flipchess_shiftMessages(model); + + if(model->paramPlayerW != 0 || model->paramPlayerB != 0) + SCL_gameUndoMove(&(model->game)); + + SCL_gameUndoMove(&(model->game)); + SCL_squareSetClear(model->moveHighlight); + } + + switch(model->game.state) { + case SCL_GAME_STATE_WHITE_WIN: + model->msg = "white wins"; + model->paramExit = FlipChessStatusReturn; + break; + + case SCL_GAME_STATE_BLACK_WIN: + model->msg = "black wins"; + model->paramExit = FlipChessStatusReturn; + break; + + case SCL_GAME_STATE_DRAW_STALEMATE: + model->msg = "stalemate"; + model->paramExit = FlipChessStatusReturn; + break; + + case SCL_GAME_STATE_DRAW_REPETITION: + model->msg = "draw-repetition"; + model->paramExit = FlipChessStatusReturn; + break; + + case SCL_GAME_STATE_DRAW_DEAD: + model->msg = "draw-dead pos."; + model->paramExit = FlipChessStatusReturn; + break; + + case SCL_GAME_STATE_DRAW: + model->msg = "draw"; + model->paramExit = FlipChessStatusReturn; + break; + + case SCL_GAME_STATE_DRAW_50: + model->msg = "draw-50 moves"; + model->paramExit = FlipChessStatusReturn; + break; + + default: + if(model->game.ply > 0) { + const uint8_t whitesTurn = SCL_boardWhitesTurn(model->game.board); + + if(SCL_boardCheck(model->game.board, whitesTurn)) { + model->msg = (whitesTurn ? "black: check!" : "white: check!"); + } else { + model->msg = (whitesTurn ? "black played" : "white played"); + } + + uint8_t s0, s1; + char p; + + SCL_recordGetMove(model->game.record, model->game.ply - 1, &s0, &s1, &p); + SCL_moveToString(model->game.board, s0, s1, p, model->moveString); + } + break; + model->paramExit = moveType; + } + } + + model->thinking = 0; + return model->paramExit; +} + +void flipchess_scene_1_set_callback( + FlipChessScene1* instance, + FlipChessScene1Callback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void flipchess_scene_1_draw(Canvas* canvas, FlipChessScene1Model* model) { + //UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + //canvas_draw_icon(canvas, 0, 0, &I_FLIPR_128x64); + + // Frame + canvas_draw_frame(canvas, 0, 0, 66, 64); + + // Message + canvas_set_font(canvas, FontSecondary); + if(model->thinking) { + canvas_draw_str(canvas, 68, 10, "thinking..."); + } else { + canvas_draw_str(canvas, 68, 10, model->msg); + } + canvas_draw_str(canvas, 68, 19, model->moveString); + canvas_draw_str(canvas, 68, 31, model->msg2); + canvas_draw_str(canvas, 68, 40, model->moveString2); + canvas_draw_str(canvas, 68, 52, model->msg3); + canvas_draw_str(canvas, 68, 61, model->moveString3); + + // Board + for(uint16_t y = 0; y < SCL_BOARD_PICTURE_WIDTH; y++) { + for(uint16_t x = 0; x < SCL_BOARD_PICTURE_WIDTH; x++) { + if(!picture[x + (y * SCL_BOARD_PICTURE_WIDTH)]) { + canvas_draw_dot(canvas, x + 1, y); + } + } + } +} + +static int flipchess_scene_1_model_init( + FlipChessScene1Model* const model, + const int white_mode, + const int black_mode, + char* import_game_text) { + model->paramPlayerW = white_mode; + model->paramPlayerB = black_mode; + + model->paramAnalyze = 255; // depth of analysis + model->paramMoves = 0; + model->paramInfo = 1; + model->paramFlipBoard = 0; + model->paramExit = FlipChessStatusNone; + model->paramStep = 0; + model->paramFEN = import_game_text; + model->paramPGN = NULL; + model->clockSeconds = -1; + + SCL_Board emptyStartState = SCL_BOARD_START_STATE; + memcpy(model->startState, &emptyStartState, sizeof(SCL_Board)); + +#if ENABLE_960 + model->random960PosNumber = -1; +#endif + + model->squareSelected = 255; + model->squareSelectedLast = 28; // start selector near middle + + model->msg = "init"; + model->moveString[0] = '\0'; + model->msg2 = ""; + model->moveString2[0] = '\0'; + model->msg3 = ""; + model->moveString3[0] = '\0'; + model->thinking = 0; + + SCL_SquareSet emptySquareSet = SCL_SQUARE_SET_EMPTY; + memcpy(model->moveHighlight, &emptySquareSet, sizeof(SCL_SquareSet)); + model->squareFrom = 255; + model->squareTo = 255; + model->turnState = 0; + + SCL_randomBetterSeed(furi_hal_random_get()); + +#if ENABLE_960 +#if SCL_960_CASTLING + if(model->random960PosNumber < 0) model->random960PosNumber = SCL_randomBetter(); +#endif + if(model->random960PosNumber >= 0) model->random960PosNumber %= 960; +#endif + + if(model->paramFEN != NULL) + SCL_boardFromFEN(model->startState, model->paramFEN); + else if(model->paramPGN != NULL) { + SCL_Record record; + SCL_recordFromPGN(record, model->paramPGN); + SCL_boardInit(model->startState); + SCL_recordApply(record, model->startState, model->paramStep); + } + +#if ENABLE_960 +#if SCL_960_CASTLING + else + SCL_boardInit960(model->startState, model->random960PosNumber); +#endif +#endif + + SCL_gameInit(&(model->game), model->startState); + + if(model->paramAnalyze != 255) { + char p; + uint8_t move[] = {0, 0}; + + model->paramPlayerW = model->paramAnalyze; + model->paramPlayerB = model->paramAnalyze; + + int16_t evaluation = + flipchess_makeAIMove(model->game.board, &(move[0]), &(move[1]), &p, model); + + if(model->paramAnalyze == 0) evaluation = SCL_boardEvaluateStatic(model->game.board); + + char moveStr[5]; + moveStr[4] = 0; + + SCL_squareToString(move[0], moveStr); + SCL_squareToString(move[1], moveStr + 2); + + //printf("%lf (%d)\n", ((double)evaluation) / ((double)SCL_VALUE_PAWN), evaluation); + //puts(moveStr); + + return evaluation; + } + + if(model->paramMoves) { + char string[256]; + + for(int i = 0; i < 64; ++i) + if(model->game.board[i] != '.' && + SCL_pieceIsWhite(model->game.board[i]) == SCL_boardWhitesTurn(model->game.board)) { + SCL_SquareSet possibleMoves = SCL_SQUARE_SET_EMPTY; + + SCL_boardGetMoves(model->game.board, i, possibleMoves); + + SCL_SQUARE_SET_ITERATE_BEGIN(possibleMoves) + SCL_moveToString(model->game.board, i, iteratedSquare, 'q', string); + //printf("%s ", string); + SCL_SQUARE_SET_ITERATE_END + } + + return FlipChessStatusReturn; + } + + model->msg = (SCL_boardWhitesTurn(model->game.board) ? "white to move" : "black to move"); + + // 0 = success + return FlipChessStatusNone; +} + +bool flipchess_scene_1_input(InputEvent* event, void* context) { + furi_assert(context); + FlipChessScene1* instance = context; + FlipChess* app = instance->context; + + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + if(model->turnState == 1) { + model->turnState = 0; + SCL_squareSetClear(model->moveHighlight); + flipchess_drawBoard(model); + } else { + instance->callback(FlipChessCustomEventScene1Back, instance->context); + } + }, + true); + break; + case InputKeyRight: + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + if(model->squareSelectedLast != 255 && model->squareSelected == 255) { + model->squareSelected = model->squareSelectedLast; + } else { + model->squareSelected = (model->squareSelected + 1) % 64; + } + flipchess_drawBoard(model); + }, + true); + break; + case InputKeyDown: + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + if(model->squareSelectedLast != 255 && model->squareSelected == 255) { + model->squareSelected = model->squareSelectedLast; + } else { + model->squareSelected = (model->squareSelected + 56) % 64; + } + flipchess_drawBoard(model); + }, + true); + break; + case InputKeyLeft: + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + if(model->squareSelectedLast != 255 && model->squareSelected == 255) { + model->squareSelected = model->squareSelectedLast; + } else { + model->squareSelected = (model->squareSelected + 63) % 64; + } + flipchess_drawBoard(model); + }, + true); + break; + case InputKeyUp: + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + if(model->squareSelectedLast != 255 && model->squareSelected == 255) { + model->squareSelected = model->squareSelectedLast; + } else { + model->squareSelected = (model->squareSelected + 8) % 64; + } + flipchess_drawBoard(model); + }, + true); + break; + case InputKeyOk: + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + // if(model->paramExit == FlipChessStatusReturn) { + // instance->callback(FlipChessCustomEventScene1Back, instance->context); + // break; + // } + if(!flipchess_isPlayerTurn(model)) { + model->thinking = 1; + } + }, + true); + furi_thread_flags_wait(0, FuriFlagWaitAny, THREAD_WAIT_TIME); + + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + // first turn of round, probably player but could be AI + if(flipchess_turn(model) == FlipChessStatusReturn) { + if(app->sound == 1) flipchess_voice_a_strange_game(); + flipchess_play_long_bump(app); + } + flipchess_saveState(app, model); + flipchess_drawBoard(model); + }, + true); + + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + if(!flipchess_isPlayerTurn(model)) { + model->thinking = 1; + } + }, + true); + furi_thread_flags_wait(0, FuriFlagWaitAny, THREAD_WAIT_TIME); + + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + // if player played, let AI play + if(!flipchess_isPlayerTurn(model)) { + if(flipchess_turn(model) == FlipChessStatusReturn) { + if(app->sound == 1) flipchess_voice_a_strange_game(); + flipchess_play_long_bump(app); + } + flipchess_saveState(app, model); + flipchess_drawBoard(model); + } + }, + true); + break; + case InputKeyMAX: + break; + } + } + return true; +} + +void flipchess_scene_1_exit(void* context) { + furi_assert(context); + FlipChessScene1* instance = (FlipChessScene1*)context; + + with_view_model( + instance->view, FlipChessScene1Model * model, { model->paramExit = 0; }, true); +} + +void flipchess_scene_1_enter(void* context) { + furi_assert(context); + FlipChessScene1* instance = (FlipChessScene1*)context; + FlipChess* app = instance->context; + + flipchess_play_happy_bump(app); + + with_view_model( + instance->view, + FlipChessScene1Model * model, + { + // load imported game if applicable + char* import_game_text = NULL; + if(app->import_game == 1 && strlen(app->import_game_text) > 0) { + import_game_text = app->import_game_text; + } else { + if(app->sound == 1) flipchess_voice_how_about_chess(); + } + + int init = flipchess_scene_1_model_init( + model, app->white_mode, app->black_mode, import_game_text); + + if(init == FlipChessStatusNone) { + // perform initial turn, sets up and lets white + // AI play if applicable + const uint8_t turn = flipchess_turn(model); + if(turn == FlipChessStatusReturn) { + init = turn; + } else { + flipchess_saveState(app, model); + flipchess_drawBoard(model); + } + } + + // if return status, return from scene immediately + // if(init == FlipChessStatusReturn) { + // instance->callback(FlipChessCustomEventScene1Back, instance->context); + // } + }, + true); +} + +FlipChessScene1* flipchess_scene_1_alloc() { + FlipChessScene1* instance = malloc(sizeof(FlipChessScene1)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipChessScene1Model)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)flipchess_scene_1_draw); + view_set_input_callback(instance->view, flipchess_scene_1_input); + view_set_enter_callback(instance->view, flipchess_scene_1_enter); + view_set_exit_callback(instance->view, flipchess_scene_1_exit); + + return instance; +} + +void flipchess_scene_1_free(FlipChessScene1* instance) { + furi_assert(instance); + + with_view_model( + instance->view, FlipChessScene1Model * model, { UNUSED(model); }, true); + + view_free(instance->view); + free(instance); +} + +View* flipchess_scene_1_get_view(FlipChessScene1* instance) { + furi_assert(instance); + return instance->view; +} \ No newline at end of file diff --git a/applications/external/chess/views/flipchess_scene_1.h b/applications/external/chess/views/flipchess_scene_1.h new file mode 100644 index 000000000..a57a90781 --- /dev/null +++ b/applications/external/chess/views/flipchess_scene_1.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/flipchess_custom_event.h" + +typedef struct FlipChessScene1 FlipChessScene1; + +typedef void (*FlipChessScene1Callback)(FlipChessCustomEvent event, void* context); + +void flipchess_scene_1_set_callback( + FlipChessScene1* flipchess_scene_1, + FlipChessScene1Callback callback, + void* context); + +View* flipchess_scene_1_get_view(FlipChessScene1* flipchess_static); + +FlipChessScene1* flipchess_scene_1_alloc(); + +void flipchess_scene_1_free(FlipChessScene1* flipchess_static); \ No newline at end of file diff --git a/applications/external/chess/views/flipchess_startscreen.c b/applications/external/chess/views/flipchess_startscreen.c new file mode 100644 index 000000000..9a146674e --- /dev/null +++ b/applications/external/chess/views/flipchess_startscreen.c @@ -0,0 +1,164 @@ +#include "../flipchess.h" +#include +#include +#include +#include +#include "flipchess_icons.h" +#include + +struct FlipChessStartscreen { + View* view; + FlipChessStartscreenCallback callback; + void* context; +}; + +typedef struct { + int some_value; +} FlipChessStartscreenModel; + +void flipchess_startscreen_set_callback( + FlipChessStartscreen* instance, + FlipChessStartscreenCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void flipchess_startscreen_draw(Canvas* canvas, FlipChessStartscreenModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 0, &I_FLIPR_128x64); + +#ifdef CANVAS_HAS_FONT_SCUMM_ROMAN_OUTLINE + const uint8_t text_x_pos = 2; + const uint8_t text_y_pos = 12; + canvas_set_font(canvas, FontScummRomanOutline); +#else + const uint8_t text_x_pos = 4; + const uint8_t text_y_pos = 11; + canvas_set_font(canvas, FontPrimary); +#endif + canvas_draw_str(canvas, text_x_pos, text_y_pos, "Chess"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 62, text_y_pos, FLIPCHESS_VERSION); + + //canvas_set_font(canvas, FontSecondary); + //canvas_draw_str(canvas, 10, 11, "How about a nice game of..."); + //canvas_draw_str(canvas, 99, 40, FLIPCHESS_VERSION); + + //canvas_set_font(canvas, FontPrimary); + //canvas_draw_str(canvas, 10, 23, "Chess"); + //canvas_draw_icon(canvas, 0, 40, &I_Background_128x11); + //canvas_draw_str(canvas, 10, 61, "FLIPR"); + + elements_button_left(canvas, "Sound"); + elements_button_right(canvas, "Silent"); +} + +static void flipchess_startscreen_model_init(FlipChessStartscreenModel* const model) { + model->some_value = 1; +} + +bool flipchess_startscreen_input(InputEvent* event, void* context) { + furi_assert(context); + FlipChessStartscreen* instance = context; + FlipChess* app = instance->context; + + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + FlipChessStartscreenModel * model, + { + UNUSED(model); + instance->callback(FlipChessCustomEventStartscreenBack, instance->context); + }, + true); + break; + case InputKeyLeft: + // sound on, haptic off + app->sound = 1; + app->haptic = FlipChessHapticOff; + with_view_model( + instance->view, + FlipChessStartscreenModel * model, + { + UNUSED(model); + instance->callback(FlipChessCustomEventStartscreenOk, instance->context); + }, + true); + break; + case InputKeyRight: + // sound off, haptic on + app->sound = 0; + app->haptic = FlipChessHapticOn; + with_view_model( + instance->view, + FlipChessStartscreenModel * model, + { + UNUSED(model); + instance->callback(FlipChessCustomEventStartscreenOk, instance->context); + }, + true); + break; + case InputKeyUp: + case InputKeyDown: + case InputKeyOk: + case InputKeyMAX: + break; + } + } + return true; +} + +void flipchess_startscreen_exit(void* context) { + furi_assert(context); +} + +void flipchess_startscreen_enter(void* context) { + furi_assert(context); + FlipChessStartscreen* instance = (FlipChessStartscreen*)context; + with_view_model( + instance->view, + FlipChessStartscreenModel * model, + { flipchess_startscreen_model_init(model); }, + true); +} + +FlipChessStartscreen* flipchess_startscreen_alloc() { + FlipChessStartscreen* instance = malloc(sizeof(FlipChessStartscreen)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipChessStartscreenModel)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)flipchess_startscreen_draw); + view_set_input_callback(instance->view, flipchess_startscreen_input); + //view_set_enter_callback(instance->view, flipchess_startscreen_enter); + //view_set_exit_callback(instance->view, flipchess_startscreen_exit); + + with_view_model( + instance->view, + FlipChessStartscreenModel * model, + { flipchess_startscreen_model_init(model); }, + true); + + return instance; +} + +void flipchess_startscreen_free(FlipChessStartscreen* instance) { + furi_assert(instance); + + with_view_model( + instance->view, FlipChessStartscreenModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* flipchess_startscreen_get_view(FlipChessStartscreen* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/chess/views/flipchess_startscreen.h b/applications/external/chess/views/flipchess_startscreen.h new file mode 100644 index 000000000..7c6203569 --- /dev/null +++ b/applications/external/chess/views/flipchess_startscreen.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/flipchess_custom_event.h" + +typedef struct FlipChessStartscreen FlipChessStartscreen; + +typedef void (*FlipChessStartscreenCallback)(FlipChessCustomEvent event, void* context); + +void flipchess_startscreen_set_callback( + FlipChessStartscreen* flipchess_startscreen, + FlipChessStartscreenCallback callback, + void* context); + +View* flipchess_startscreen_get_view(FlipChessStartscreen* flipchess_static); + +FlipChessStartscreen* flipchess_startscreen_alloc(); + +void flipchess_startscreen_free(FlipChessStartscreen* flipchess_static); \ No newline at end of file diff --git a/applications/external/cntdown_timer/application.fam b/applications/external/cntdown_timer/application.fam index 054833eeb..79a08423e 100644 --- a/applications/external/cntdown_timer/application.fam +++ b/applications/external/cntdown_timer/application.fam @@ -10,7 +10,6 @@ App( "gui", ], stack_size=2 * 1024, - order=20, fap_icon="cntdown_timer.png", fap_category="Tools", fap_author="@0w0mewo", diff --git a/applications/external/dap_link/application.fam b/applications/external/dap_link/application.fam index e05a7e01f..8f25bef83 100644 --- a/applications/external/dap_link/application.fam +++ b/applications/external/dap_link/application.fam @@ -8,7 +8,8 @@ App( "dialogs", ], stack_size=4 * 1024, - order=20, + fap_description="Enables use of Flipper as a debug probe for ARM devices, implements the CMSIS-DAP protocol", + fap_version="1.0", fap_icon="dap_link.png", fap_category="GPIO", fap_private_libs=[ diff --git a/applications/external/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c index 52d222138..487c63c44 100644 --- a/applications/external/dap_link/dap_link.c +++ b/applications/external/dap_link/dap_link.c @@ -15,7 +15,7 @@ #include "usb/dap_v2_usb.h" #include #include "dap_link_icons.h" -#include "assets_icons.h" +#include /***************************************************************************/ /****************************** DAP COMMON *********************************/ @@ -525,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/gui/views/dap_main_view.c b/applications/external/dap_link/gui/views/dap_main_view.c index f54c5e3d5..15f285698 100644 --- a/applications/external/dap_link/gui/views/dap_main_view.c +++ b/applications/external/dap_link/gui/views/dap_main_view.c @@ -1,5 +1,6 @@ #include "dap_main_view.h" #include "dap_link_icons.h" +#include #include // extern const Icon I_ArrowDownEmpty_12x18; diff --git a/applications/external/doom/.gitignore b/applications/external/doom/.gitignore deleted file mode 100644 index e2a15a10a..000000000 --- a/applications/external/doom/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist/* -.vscode -.clang-format -.editorconfig \ No newline at end of file diff --git a/applications/external/doom/application.fam b/applications/external/doom/application.fam index 5ff6878ee..995286d74 100644 --- a/applications/external/doom/application.fam +++ b/applications/external/doom/application.fam @@ -8,7 +8,6 @@ App( "music_player", ], stack_size=4 * 1024, - order=75, fap_icon="doom_10px.png", fap_category="Games", fap_icon_assets="assets", diff --git a/applications/external/doom/display.h b/applications/external/doom/display.h index 89f821867..a949b14fa 100644 --- a/applications/external/doom/display.h +++ b/applications/external/doom/display.h @@ -1,7 +1,8 @@ #include #include #include "constants.h" -#include +#include "doom_icons.h" +#include #include "assets.h" #define CHECK_BIT(var, pos) ((var) & (1 << (pos))) diff --git a/applications/external/doom/doom.c b/applications/external/doom/doom.c index d9c9ab506..c2d9a6bf0 100644 --- a/applications/external/doom/doom.c +++ b/applications/external/doom/doom.c @@ -13,6 +13,7 @@ #include "level.h" #include #include +#include #define SOUND @@ -156,9 +157,6 @@ void spawnEntity(uint8_t type, uint8_t x, uint8_t y, PluginState* const plugin_s plugin_state->entity[plugin_state->num_entities] = create_medikit(x, y); plugin_state->num_entities++; break; - - default: - break; } } @@ -462,9 +460,6 @@ void updateEntities(const uint8_t level[], Canvas* const canvas, PluginState* co } break; } - - default: - break; } i++; @@ -994,7 +989,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/doom/doom_music_player_worker.c b/applications/external/doom/doom_music_player_worker.c index c8b9bb9db..e81549625 100644 --- a/applications/external/doom/doom_music_player_worker.c +++ b/applications/external/doom/doom_music_player_worker.c @@ -393,7 +393,7 @@ bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Unable to open file"); break; - } + }; uint16_t ret = 0; do { diff --git a/applications/external/dtmf_dolphin/application.fam b/applications/external/dtmf_dolphin/application.fam index 5f01bc9a0..11a72df12 100644 --- a/applications/external/dtmf_dolphin/application.fam +++ b/applications/external/dtmf_dolphin/application.fam @@ -10,9 +10,8 @@ App( ], fap_icon="phone.png", stack_size=8 * 1024, - order=20, fap_category="Tools", fap_author="@litui & @xMasterX", - fap_version="1.0", + fap_version="1.1", fap_description="DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.", ) diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin.c b/applications/external/dtmf_dolphin/dtmf_dolphin.c index 4447fb278..c1b10defa 100644 --- a/applications/external/dtmf_dolphin/dtmf_dolphin.c +++ b/applications/external/dtmf_dolphin/dtmf_dolphin.c @@ -2,7 +2,6 @@ #include #include -#include static bool dtmf_dolphin_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -83,9 +82,8 @@ int32_t dtmf_dolphin_app(void* p) { UNUSED(p); DTMFDolphinApp* app = app_alloc(); - dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(app->view_dispatcher); app_free(app); return 0; -} +} \ No newline at end of file diff --git a/applications/external/dtmf_dolphin/phone.png b/applications/external/dtmf_dolphin/phone.png index 443f847c3..9c4d04c02 100644 Binary files a/applications/external/dtmf_dolphin/phone.png and b/applications/external/dtmf_dolphin/phone.png differ diff --git a/applications/external/esp32cam_camera/application.fam b/applications/external/esp32cam_camera/application.fam index a0e0ede4c..e8d5c7158 100644 --- a/applications/external/esp32cam_camera/application.fam +++ b/applications/external/esp32cam_camera/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_CAMERA"], requires=["gui"], stack_size=8 * 1024, - order=1, fap_icon="icon.png", fap_category="GPIO", fap_description="ESP32-CAM live feed and photo capture, use left/right for orientation/mode, up/down for brightness and center for saving a screenshot. [Unplug the USB cable to test with Mayhem]", diff --git a/applications/external/esp32cam_camera/camera.c b/applications/external/esp32cam_camera/camera.c index c0631b8ef..cc740715f 100644 --- a/applications/external/esp32cam_camera/camera.c +++ b/applications/external/esp32cam_camera/camera.c @@ -42,7 +42,7 @@ void get_timefilename(FuriString* name) { furi_hal_rtc_get_datetime(&datetime); furi_string_printf( name, - EXT_PATH("DCIM/%.4d%.2d%.2d-%.2d%.2d%.2d.bmp"), + APP_DATA_PATH("%.4d%.2d%.2d-%.2d%.2d%.2d.bmp"), datetime.year, datetime.month, datetime.day, diff --git a/applications/external/esp32cam_evilportal/application.fam b/applications/external/esp32cam_evilportal/application.fam index 190ae711a..d842ddc79 100644 --- a/applications/external/esp32cam_evilportal/application.fam +++ b/applications/external/esp32cam_evilportal/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_EVIL_PORTAL"], requires=["gui"], stack_size=1 * 1024, - order=90, fap_icon="icons/evil_portal_10px.png", fap_category="GPIO", fap_description="ESP32-CAM evil portal. When users try to connect to this access point they will be served a fake login screen. User credentials are sent to the Flipper and logged on the SD card. [Unplug the USB cable to test with Mayhem]", diff --git a/applications/external/esp32cam_marauder_companion/application.fam b/applications/external/esp32cam_marauder_companion/application.fam index 800cb92be..891c0859d 100644 --- a/applications/external/esp32cam_marauder_companion/application.fam +++ b/applications/external/esp32cam_marauder_companion/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_WIFI_MARAUDER"], requires=["gui"], stack_size=4 * 1024, - order=2, fap_icon="wifi_10px.png", fap_category="GPIO", fap_description="ESP32-CAM version of Marauder. Includes all functionality from the original plus some options to trigger the camera and flashlight. [Unplug the USB cable to test with Mayhem]", diff --git a/applications/external/esp32cam_morseflasher/application.fam b/applications/external/esp32cam_morseflasher/application.fam index 037e63c81..4c40e19a9 100644 --- a/applications/external/esp32cam_morseflasher/application.fam +++ b/applications/external/esp32cam_morseflasher/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_UART_TERMINAL"], requires=["gui"], stack_size=1 * 1024, - order=90, fap_icon_assets="assets", fap_icon="icon.png", fap_category="GPIO", diff --git a/applications/external/esp32cam_motion_detection/application.fam b/applications/external/esp32cam_motion_detection/application.fam index 57a5396c5..b2fa8a467 100644 --- a/applications/external/esp32cam_motion_detection/application.fam +++ b/applications/external/esp32cam_motion_detection/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_QRCODE"], requires=["gui"], stack_size=8 * 1024, - order=1, fap_icon="icon.png", fap_category="GPIO", fap_description="ESP32-CAM Motion detection. It generates a beep when motion is detected. Can be extended to trigger more stuff in the code. [Unplug the USB cable to test with Mayhem]", diff --git a/applications/external/esp32cam_nannycam/application.fam b/applications/external/esp32cam_nannycam/application.fam index 75f2be783..bbb7022d1 100644 --- a/applications/external/esp32cam_nannycam/application.fam +++ b/applications/external/esp32cam_nannycam/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_QRCODE"], requires=["gui"], stack_size=8 * 1024, - order=1, fap_icon="icon.png", fap_category="GPIO", fap_description="ESP32-CAM simple app to start a remote camera. [Unplug the USB cable to test with Mayhem]", diff --git a/applications/external/esp32cam_qrcode/application.fam b/applications/external/esp32cam_qrcode/application.fam index 73c418510..b96942e9e 100644 --- a/applications/external/esp32cam_qrcode/application.fam +++ b/applications/external/esp32cam_qrcode/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_QRCODE"], requires=["gui"], stack_size=8 * 1024, - order=1, fap_icon="icon.png", fap_category="GPIO", fap_description="ESP32-CAM simple app to show a payload from QR codes. Can be extended to trigger more stuff in the code. [Unplug the USB cable to test with Mayhem]", diff --git a/applications/external/esp8266_deauth/application.fam b/applications/external/esp8266_deauth/application.fam index 9b176a3dd..f36d85d18 100644 --- a/applications/external/esp8266_deauth/application.fam +++ b/applications/external/esp8266_deauth/application.fam @@ -5,7 +5,6 @@ App( entry_point="esp8266_deauth_app", requires=["gui"], stack_size=2 * 1024, - order=100, fap_icon="wifi_10px.png", fap_category="WiFi", fap_author="@SequoiaSan & @xMasterX", diff --git a/applications/external/esp8266_deauth/esp8266_deauth.c b/applications/external/esp8266_deauth/esp8266_deauth.c index 76551f60c..41015c21b 100644 --- a/applications/external/esp8266_deauth/esp8266_deauth.c +++ b/applications/external/esp8266_deauth/esp8266_deauth.c @@ -6,14 +6,18 @@ #include #include #include -#include //#include //#include //#include //#include +#include #include "FlipperZeroWiFiDeauthModuleDefines.h" +#define UART_CH \ + (XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) + #define DEAUTH_APP_DEBUG 0 #if DEAUTH_APP_DEBUG @@ -321,7 +325,6 @@ int32_t esp8266_deauth_app(void* p) { SWiFiDeauthApp* app = malloc(sizeof(SWiFiDeauthApp)); - dolphin_deed(DolphinDeedPluginStart); esp8266_deauth_app_init(app); furi_hal_gpio_init_simple(app->m_GpioButtons.pinButtonUp, GpioModeOutputPushPull); @@ -392,6 +395,13 @@ int32_t esp8266_deauth_app(void* p) { #if DISABLE_CONSOLE furi_hal_console_disable(); #endif + + if(UART_CH == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH, FLIPPERZERO_SERIAL_BAUD); + } + furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app); DEAUTH_APP_LOG_I("UART Listener created"); @@ -511,6 +521,12 @@ int32_t esp8266_deauth_app(void* p) { furi_hal_console_enable(); #endif + if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH); + } else { + furi_hal_console_enable(); + } + //*app->m_originalBufferLocation = app->m_originalBuffer; view_port_enabled_set(view_port, false); diff --git a/applications/external/esp_flasher/application.fam b/applications/external/esp_flasher/application.fam index 8c56a34d6..1fd1c6e7a 100644 --- a/applications/external/esp_flasher/application.fam +++ b/applications/external/esp_flasher/application.fam @@ -1,13 +1,12 @@ App( appid="esp_flasher", - name="[ESP] Flasher", + name="[ESP] ESP Flasher", fap_version=(1, 1), apptype=FlipperAppType.EXTERNAL, entry_point="esp_flasher_app", requires=["gui"], stack_size=4 * 1024, - order=90, - fap_icon="wifi_10px.png", + fap_icon="update_10px.png", fap_category="GPIO", fap_private_libs=[ Lib( diff --git a/applications/external/esp_flasher/assets/KeyBackspaceSelected_16x9.png b/applications/external/esp_flasher/assets/KeyBackspaceSelected_16x9.png deleted file mode 100644 index 7cc0759a8..000000000 Binary files a/applications/external/esp_flasher/assets/KeyBackspaceSelected_16x9.png and /dev/null differ diff --git a/applications/external/esp_flasher/assets/KeyBackspace_16x9.png b/applications/external/esp_flasher/assets/KeyBackspace_16x9.png deleted file mode 100644 index 9946232d9..000000000 Binary files a/applications/external/esp_flasher/assets/KeyBackspace_16x9.png and /dev/null differ diff --git a/applications/external/esp_flasher/assets/KeySaveSelected_24x11.png b/applications/external/esp_flasher/assets/KeySaveSelected_24x11.png deleted file mode 100644 index eeb3569d3..000000000 Binary files a/applications/external/esp_flasher/assets/KeySaveSelected_24x11.png and /dev/null differ diff --git a/applications/external/esp_flasher/assets/KeySave_24x11.png b/applications/external/esp_flasher/assets/KeySave_24x11.png deleted file mode 100644 index e7dba987a..000000000 Binary files a/applications/external/esp_flasher/assets/KeySave_24x11.png and /dev/null differ diff --git a/applications/external/esp_flasher/esp_flasher_app.c b/applications/external/esp_flasher/esp_flasher_app.c index cf299c4c5..ce8fdf5a8 100644 --- a/applications/external/esp_flasher/esp_flasher_app.c +++ b/applications/external/esp_flasher/esp_flasher_app.c @@ -68,6 +68,7 @@ EspFlasherApp* esp_flasher_app_alloc() { app->reset = false; app->boot = false; + app->quickflash = false; scene_manager_next_scene(app->scene_manager, EspFlasherSceneStart); @@ -116,6 +117,7 @@ int32_t esp_flasher_app(void* p) { UNUSED(p); uint8_t attempts = 0; + bool otg_was_enabled = furi_hal_power_is_otg_enabled(); while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { furi_hal_power_enable_otg(); furi_delay_ms(10); @@ -132,7 +134,7 @@ int32_t esp_flasher_app(void* p) { esp_flasher_app_free(esp_flasher_app); - if(furi_hal_power_is_otg_enabled()) { + if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) { furi_hal_power_disable_otg(); } diff --git a/applications/external/esp_flasher/esp_flasher_app.h b/applications/external/esp_flasher/esp_flasher_app.h index 809fcc6fc..3a30d16c0 100644 --- a/applications/external/esp_flasher/esp_flasher_app.h +++ b/applications/external/esp_flasher/esp_flasher_app.h @@ -4,7 +4,7 @@ extern "C" { #endif -#define ESP_FLASHER_APP_VERSION "v1.1" +#define ESP_FLASHER_APP_VERSION "v1.2" typedef struct EspFlasherApp EspFlasherApp; diff --git a/applications/external/esp_flasher/esp_flasher_app_i.h b/applications/external/esp_flasher/esp_flasher_app_i.h index 8f533453f..e67f8a50f 100644 --- a/applications/external/esp_flasher/esp_flasher_app_i.h +++ b/applications/external/esp_flasher/esp_flasher_app_i.h @@ -34,11 +34,18 @@ typedef enum SelectedFlashOptions { SelectedFlashPart, SelectedFlashNvs, SelectedFlashBootApp0, - SelectedFlashApp, + SelectedFlashAppA, + SelectedFlashAppB, SelectedFlashCustom, NUM_FLASH_OPTIONS } SelectedFlashOptions; +typedef enum { + SwitchNotSet, + SwitchToFirmwareA, + SwitchToFirmwareB, +} SwitchFirmware; + struct EspFlasherApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -59,6 +66,9 @@ struct EspFlasherApp { bool reset; bool boot; + bool quickflash; + + SwitchFirmware switch_fw; bool selected_flash_options[NUM_FLASH_OPTIONS]; int num_selected_flash_options; @@ -66,7 +76,8 @@ struct EspFlasherApp { char bin_file_path_part[100]; char bin_file_path_nvs[100]; char bin_file_path_boot_app0[100]; - char bin_file_path_app[100]; + char bin_file_path_app_a[100]; + char bin_file_path_app_b[100]; char bin_file_path_custom[100]; FuriThread* flash_worker; bool flash_worker_busy; diff --git a/applications/external/esp_flasher/esp_flasher_uart.c b/applications/external/esp_flasher/esp_flasher_uart.c index 7dd2904b5..b4b7928b8 100644 --- a/applications/external/esp_flasher/esp_flasher_uart.c +++ b/applications/external/esp_flasher/esp_flasher_uart.c @@ -1,7 +1,10 @@ #include "esp_flasher_app_i.h" #include "esp_flasher_uart.h" +#include -#define UART_CH (FuriHalUartIdUSART1) +#define UART_CH \ + (XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) #define BAUDRATE (115200) struct EspFlasherUart { @@ -52,6 +55,7 @@ static int32_t uart_worker(void* context) { } } + furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); furi_stream_buffer_free(uart->rx_stream); return 0; @@ -96,7 +100,6 @@ void esp_flasher_uart_free(EspFlasherUart* uart) { furi_thread_join(uart->rx_thread); 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); } diff --git a/applications/external/esp_flasher/esp_flasher_worker.c b/applications/external/esp_flasher/esp_flasher_worker.c index ef7ed1806..be5da85db 100644 --- a/applications/external/esp_flasher/esp_flasher_worker.c +++ b/applications/external/esp_flasher/esp_flasher_worker.c @@ -74,6 +74,85 @@ static esp_loader_error_t _flash_file(EspFlasherApp* app, char* filepath, uint32 return ESP_LOADER_SUCCESS; } +// This in-app FW switch "exploits" the otadata (boot_app0) +// - the first four bytes of each array are the counter and the last four bytes are just a CRC of that counter +// - the bootloader will just boot whichever app has the highest counter in the otadata partition +// so we'll just pick 1 for A, and then B will use either 0 or 2 depending on whether it's the slot in use + +#define MAGIC_PAYLOAD_SIZE (32) + +const uint8_t magic_payload_app_a[MAGIC_PAYLOAD_SIZE] = {0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9a, 0x98, 0x43, 0x47}; + +const uint8_t magic_payload_app_b_unset[MAGIC_PAYLOAD_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +const uint8_t magic_payload_app_b_set[MAGIC_PAYLOAD_SIZE] = { + 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x74, 0x37, 0xf6, 0x55}; + +// return true if "switching" fw selected instead of flashing new fw +// (this does not indicate success) +static bool _switch_fw(EspFlasherApp* app) { + if(app->switch_fw == SwitchNotSet) { + return false; + } + + esp_loader_error_t err; + char user_msg[256]; + + loader_port_debug_print("Preparing to set flags for firmware A\n"); + err = esp_loader_flash_start( + ESP_ADDR_BOOT_APP0 + ESP_ADDR_OTADATA_OFFSET_APP_A, + MAGIC_PAYLOAD_SIZE, + MAGIC_PAYLOAD_SIZE); + if(err != ESP_LOADER_SUCCESS) { + snprintf(user_msg, sizeof(user_msg), "Erasing flash failed with error %d\n", err); + loader_port_debug_print(user_msg); + return true; + } + + loader_port_debug_print("Setting flags for firmware A\n"); + const uint8_t* which_payload_app_a = magic_payload_app_a; + err = esp_loader_flash_write((void*)which_payload_app_a, MAGIC_PAYLOAD_SIZE); + if(err != ESP_LOADER_SUCCESS) { + snprintf(user_msg, sizeof(user_msg), "Packet could not be written! Error: %u\n", err); + loader_port_debug_print(user_msg); + return true; + } + + loader_port_debug_print("Preparing to set flags for firmware B\n"); + err = esp_loader_flash_start( + ESP_ADDR_BOOT_APP0 + ESP_ADDR_OTADATA_OFFSET_APP_B, + MAGIC_PAYLOAD_SIZE, + MAGIC_PAYLOAD_SIZE); + if(err != ESP_LOADER_SUCCESS) { + snprintf(user_msg, sizeof(user_msg), "Erasing flash failed with error %d\n", err); + loader_port_debug_print(user_msg); + return true; + } + + loader_port_debug_print("Setting flags for firmware B\n"); + const uint8_t* which_payload_app_b = + (app->switch_fw == SwitchToFirmwareB ? magic_payload_app_b_set : + magic_payload_app_b_unset); + err = esp_loader_flash_write((void*)which_payload_app_b, MAGIC_PAYLOAD_SIZE); + if(err != ESP_LOADER_SUCCESS) { + snprintf(user_msg, sizeof(user_msg), "Packet could not be written! Error: %u\n", err); + loader_port_debug_print(user_msg); + return true; + } + + loader_port_debug_print("Finished programming\n"); + return true; +} + typedef struct { SelectedFlashOptions selected; const char* description; @@ -85,7 +164,7 @@ static void _flash_all_files(EspFlasherApp* app) { esp_loader_error_t err; const int num_steps = app->num_selected_flash_options; -#define NUM_FLASH_ITEMS 6 +#define NUM_FLASH_ITEMS 7 FlashItem items[NUM_FLASH_ITEMS] = { {SelectedFlashBoot, "bootloader", @@ -94,7 +173,8 @@ static void _flash_all_files(EspFlasherApp* app) { {SelectedFlashPart, "partition table", app->bin_file_path_part, ESP_ADDR_PART}, {SelectedFlashNvs, "NVS", app->bin_file_path_nvs, ESP_ADDR_NVS}, {SelectedFlashBootApp0, "boot_app0", app->bin_file_path_boot_app0, ESP_ADDR_BOOT_APP0}, - {SelectedFlashApp, "firmware", app->bin_file_path_app, ESP_ADDR_APP}, + {SelectedFlashAppA, "firmware A", app->bin_file_path_app_a, ESP_ADDR_APP_A}, + {SelectedFlashAppB, "firmware B", app->bin_file_path_app_b, ESP_ADDR_APP_B}, {SelectedFlashCustom, "custom data", app->bin_file_path_custom, 0x0}, /* if you add more entries, update NUM_FLASH_ITEMS above! */ }; @@ -167,7 +247,10 @@ static int32_t esp_flasher_flash_bin(void* context) { if(!err) { loader_port_debug_print("Connected\n"); - _flash_all_files(app); + if(!_switch_fw(app)) { + _flash_all_files(app); + } + app->switch_fw = SwitchNotSet; #if 0 loader_port_debug_print("Restoring transmission rate\n"); furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); @@ -189,6 +272,7 @@ static int32_t esp_flasher_flash_bin(void* context) { // done app->flash_worker_busy = false; + app->quickflash = false; // cleanup furi_stream_buffer_free(flash_rx_stream); @@ -236,6 +320,10 @@ static int32_t esp_flasher_reset(void* context) { app->reset = false; app->boot = false; + if(app->quickflash) { + esp_flasher_flash_bin(app); + } + return 0; } @@ -281,6 +369,8 @@ void loader_port_reset_target(void) { } void loader_port_enter_bootloader(void) { + // adapted from custom usb-jtag-serial reset in esptool + // (works on official wifi dev board) _setDTR(true); loader_port_delay_ms(SERIAL_FLASHER_RESET_HOLD_TIME_MS); _setRTS(true); @@ -324,4 +414,4 @@ void esp_flasher_worker_handle_rx_data_cb(uint8_t* buf, size_t len, void* contex // done flashing if(global_app) esp_flasher_console_output_handle_rx_data_cb(buf, len, global_app); } -} \ No newline at end of file +} diff --git a/applications/external/esp_flasher/esp_flasher_worker.h b/applications/external/esp_flasher/esp_flasher_worker.h index 44461e7d4..0dba16f0c 100644 --- a/applications/external/esp_flasher/esp_flasher_worker.h +++ b/applications/external/esp_flasher/esp_flasher_worker.h @@ -14,7 +14,11 @@ #define ESP_ADDR_PART 0x8000 #define ESP_ADDR_NVS 0x9000 #define ESP_ADDR_BOOT_APP0 0xE000 -#define ESP_ADDR_APP 0x10000 +#define ESP_ADDR_APP_A 0x10000 +#define ESP_ADDR_APP_B 0x150000 + +#define ESP_ADDR_OTADATA_OFFSET_APP_A 0x0 +#define ESP_ADDR_OTADATA_OFFSET_APP_B 0x1000 void esp_flasher_worker_start_thread(EspFlasherApp* app); void esp_flasher_worker_stop_thread(EspFlasherApp* app); diff --git a/applications/external/esp_flasher/scenes/esp_flasher_scene_browse.c b/applications/external/esp_flasher/scenes/esp_flasher_scene_browse.c index 88a9b25e4..8b223b29a 100644 --- a/applications/external/esp_flasher/scenes/esp_flasher_scene_browse.c +++ b/applications/external/esp_flasher/scenes/esp_flasher_scene_browse.c @@ -7,7 +7,8 @@ enum SubmenuIndex { SubmenuIndexPart, SubmenuIndexNvs, SubmenuIndexBootApp0, - SubmenuIndexApp, + SubmenuIndexAppA, + SubmenuIndexAppB, SubmenuIndexCustom, SubmenuIndexFlash, }; @@ -97,19 +98,35 @@ static void esp_flasher_scene_browse_callback(void* context, uint32_t index) { } view_dispatcher_send_custom_event(app->view_dispatcher, EspFlasherEventRefreshSubmenu); break; - case SubmenuIndexApp: - app->selected_flash_options[SelectedFlashApp] = - !app->selected_flash_options[SelectedFlashApp]; + case SubmenuIndexAppA: + app->selected_flash_options[SelectedFlashAppA] = + !app->selected_flash_options[SelectedFlashAppA]; if(dialog_file_browser_show( app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { strncpy( - app->bin_file_path_app, + app->bin_file_path_app_a, furi_string_get_cstr(selected_filepath), - sizeof(app->bin_file_path_app)); + sizeof(app->bin_file_path_app_a)); } - if(app->bin_file_path_app[0] == '\0') { + if(app->bin_file_path_app_a[0] == '\0') { // if user didn't select a file, leave unselected - app->selected_flash_options[SelectedFlashApp] = false; + app->selected_flash_options[SelectedFlashAppA] = false; + } + view_dispatcher_send_custom_event(app->view_dispatcher, EspFlasherEventRefreshSubmenu); + break; + case SubmenuIndexAppB: + app->selected_flash_options[SelectedFlashAppB] = + !app->selected_flash_options[SelectedFlashAppB]; + if(dialog_file_browser_show( + app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { + strncpy( + app->bin_file_path_app_b, + furi_string_get_cstr(selected_filepath), + sizeof(app->bin_file_path_app_b)); + } + if(app->bin_file_path_app_b[0] == '\0') { + // if user didn't select a file, leave unselected + app->selected_flash_options[SelectedFlashAppB] = false; } view_dispatcher_send_custom_event(app->view_dispatcher, EspFlasherEventRefreshSubmenu); break; @@ -157,7 +174,8 @@ static void esp_flasher_scene_browse_callback(void* context, uint32_t index) { #define STR_PART "Part Table (" TOSTRING(ESP_ADDR_PART) ")" #define STR_NVS "NVS (" TOSTRING(ESP_ADDR_NVS) ")" #define STR_BOOT_APP0 "boot_app0 (" TOSTRING(ESP_ADDR_BOOT_APP0) ")" -#define STR_APP "Firmware (" TOSTRING(ESP_ADDR_APP) ")" +#define STR_APP_A "FirmwareA(" TOSTRING(ESP_ADDR_APP_A) ")" +#define STR_APP_B "FirmwareB(" TOSTRING(ESP_ADDR_APP_B) ")" #define STR_CUSTOM "Custom" #define STR_FLASH_S3 "[>] FLASH (ESP32-S3)" #define STR_FLASH "[>] FLASH" @@ -213,9 +231,16 @@ static void _refresh_submenu(EspFlasherApp* app) { app); submenu_add_item( submenu, - app->selected_flash_options[SelectedFlashApp] ? STR_SELECT " " STR_APP : - STR_UNSELECT " " STR_APP, - SubmenuIndexApp, + app->selected_flash_options[SelectedFlashAppA] ? STR_SELECT " " STR_APP_A : + STR_UNSELECT " " STR_APP_A, + SubmenuIndexAppA, + esp_flasher_scene_browse_callback, + app); + submenu_add_item( + submenu, + app->selected_flash_options[SelectedFlashAppB] ? STR_SELECT " " STR_APP_B : + STR_UNSELECT " " STR_APP_B, + SubmenuIndexAppB, esp_flasher_scene_browse_callback, app); // TODO: custom addr @@ -241,7 +266,8 @@ void esp_flasher_scene_browse_on_enter(void* context) { app->bin_file_path_part[0] = '\0'; app->bin_file_path_nvs[0] = '\0'; app->bin_file_path_boot_app0[0] = '\0'; - app->bin_file_path_app[0] = '\0'; + app->bin_file_path_app_a[0] = '\0'; + app->bin_file_path_app_b[0] = '\0'; app->bin_file_path_custom[0] = '\0'; _refresh_submenu(app); diff --git a/applications/external/esp_flasher/scenes/esp_flasher_scene_config.h b/applications/external/esp_flasher/scenes/esp_flasher_scene_config.h index bf9c6e7b1..2f0a7272a 100644 --- a/applications/external/esp_flasher/scenes/esp_flasher_scene_config.h +++ b/applications/external/esp_flasher/scenes/esp_flasher_scene_config.h @@ -1,5 +1,5 @@ ADD_SCENE(esp_flasher, start, Start) -ADD_SCENE(esp_flasher, devboard, Devboard) +ADD_SCENE(esp_flasher, quick, Quick) ADD_SCENE(esp_flasher, console_output, ConsoleOutput) ADD_SCENE(esp_flasher, about, About) ADD_SCENE(esp_flasher, browse, Browse) diff --git a/applications/external/esp_flasher/scenes/esp_flasher_scene_devboard.c b/applications/external/esp_flasher/scenes/esp_flasher_scene_devboard.c deleted file mode 100644 index ccb8f527c..000000000 --- a/applications/external/esp_flasher/scenes/esp_flasher_scene_devboard.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "../esp_flasher_app_i.h" - -enum SubmenuIndex { - SubmenuIndexBlackMagic, - SubmenuIndexEvilPortal, - SubmenuIndexMarauder, -}; - -void esp_flasher_scene_devboard_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - EspFlasherApp* app = context; - - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void esp_flasher_scene_devboard_on_enter(void* context) { - furi_assert(context); - - EspFlasherApp* app = context; - Submenu* submenu = app->submenu; - submenu_set_header(submenu, "Select Firmware to flash"); - submenu_add_item( - submenu, - "Black Magic", - SubmenuIndexBlackMagic, - esp_flasher_scene_devboard_submenu_callback, - app); - submenu_add_item( - submenu, - "Evil Portal", - SubmenuIndexEvilPortal, - esp_flasher_scene_devboard_submenu_callback, - app); - submenu_add_item( - submenu, - "Marauder", - SubmenuIndexMarauder, - esp_flasher_scene_devboard_submenu_callback, - app); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, EspFlasherSceneDevboard)); - - view_dispatcher_switch_to_view(app->view_dispatcher, EspFlasherAppViewSubmenu); -} - -bool esp_flasher_scene_devboard_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - EspFlasherApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - const char* path = NULL; - consumed = true; - switch(event.event) { - case SubmenuIndexBlackMagic: - path = APP_DATA_PATH("Black_Magic.bin"); - break; - case SubmenuIndexEvilPortal: - path = APP_DATA_PATH("Evil_Portal.bin"); - break; - case SubmenuIndexMarauder: - path = APP_DATA_PATH("Marauder.bin"); - break; - default: - consumed = false; - break; - } - - if(consumed) { - app->selected_flash_options[SelectedFlashBoot] = true; - strncpy(app->bin_file_path_boot, path, sizeof(app->bin_file_path_boot)); - scene_manager_next_scene(app->scene_manager, EspFlasherSceneConsoleOutput); - } - scene_manager_set_scene_state(app->scene_manager, EspFlasherSceneDevboard, event.event); - } - - return consumed; -} - -void esp_flasher_scene_devboard_on_exit(void* context) { - furi_assert(context); - - EspFlasherApp* app = context; - submenu_reset(app->submenu); -} \ No newline at end of file diff --git a/applications/external/esp_flasher/scenes/esp_flasher_scene_quick.c b/applications/external/esp_flasher/scenes/esp_flasher_scene_quick.c new file mode 100644 index 000000000..7dd47c42b --- /dev/null +++ b/applications/external/esp_flasher/scenes/esp_flasher_scene_quick.c @@ -0,0 +1,186 @@ +#include "../esp_flasher_app_i.h" + +enum QuickState { + QuickStart, + QuickWifidevS2, + QuickWifidevS2Blackmagic, + QuickWifidevS2Dualboot, + QuickDevproWroom, + QuickDevproWroomEvilportal, + QuickDevproWroomMarauder, +}; + +void esp_flasher_scene_quick_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + EspFlasherApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void esp_flasher_scene_quick_on_enter(void* context) { + furi_assert(context); + EspFlasherApp* app = context; + Submenu* submenu = app->submenu; + uint32_t state = scene_manager_get_scene_state(app->scene_manager, EspFlasherSceneQuick); + + switch(state) { + case QuickStart: + case QuickWifidevS2: + case QuickDevproWroom: + submenu_set_header(submenu, "Choose Board:"); + submenu_add_item( + submenu, + "WiFi Dev (ESP32-S2)", + QuickWifidevS2, + esp_flasher_scene_quick_submenu_callback, + app); + submenu_add_item( + submenu, + "Dev Pro (ESP32-WROOM)", + QuickDevproWroom, + esp_flasher_scene_quick_submenu_callback, + app); + break; + case QuickWifidevS2Blackmagic: + case QuickWifidevS2Dualboot: + submenu_set_header(submenu, "Choose Firmware:"); + submenu_add_item( + submenu, + "Black Magic", + QuickWifidevS2Blackmagic, + esp_flasher_scene_quick_submenu_callback, + app); + submenu_add_item( + submenu, + "Evil Portal + Marauder", + QuickWifidevS2Dualboot, + esp_flasher_scene_quick_submenu_callback, + app); + break; + case QuickDevproWroomEvilportal: + case QuickDevproWroomMarauder: + submenu_set_header(submenu, "Choose Firmware:"); + submenu_add_item( + submenu, + "Evil Portal", + QuickDevproWroomEvilportal, + esp_flasher_scene_quick_submenu_callback, + app); + submenu_add_item( + submenu, + "Marauder", + QuickDevproWroomMarauder, + esp_flasher_scene_quick_submenu_callback, + app); + break; + default: + break; + } + + submenu_set_selected_item(submenu, state); + + view_dispatcher_switch_to_view(app->view_dispatcher, EspFlasherAppViewSubmenu); +} + +bool esp_flasher_scene_quick_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + + EspFlasherApp* app = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + + bool flash = true; + bool enter_bootloader = false; + const char* boot = NULL; // 0x1000 + const char* part = NULL; // 0x8000 + const char* app0 = NULL; // 0xE000 + const char* firm = NULL; // 0x10000 + + switch(event.event) { + case QuickWifidevS2: + case QuickDevproWroom: + scene_manager_set_scene_state( + app->scene_manager, EspFlasherSceneQuick, event.event + 1); + scene_manager_next_scene(app->scene_manager, EspFlasherSceneQuick); + flash = false; + break; + case QuickWifidevS2Blackmagic: + boot = APP_DATA_PATH("assets/wifidev-s2/blackmagic/bootloader.bin"); + part = APP_DATA_PATH("assets/wifidev-s2/blackmagic/partition-table.bin"); + firm = APP_DATA_PATH("assets/wifidev-s2/blackmagic/blackmagic.bin"); + enter_bootloader = true; + break; + case QuickWifidevS2Dualboot: + boot = APP_DATA_PATH("assets/wifidev-s2/dualboot.bin"); + enter_bootloader = true; + break; + case QuickDevproWroomEvilportal: + boot = APP_DATA_PATH("assets/devpro-wroom/evilportal/EvilPortal.ino.bootloader.bin"); + part = APP_DATA_PATH("assets/devpro-wroom/evilportal/EvilPortal.ino.partitions.bin"); + app0 = APP_DATA_PATH("assets/devpro-wroom/boot_app0.bin"); + firm = APP_DATA_PATH("assets/devpro-wroom/evilportal/EvilPortal.ino.bin"); + break; + case QuickDevproWroomMarauder: + boot = APP_DATA_PATH("assets/devpro-wroom/marauder/bootloader.bin"); + part = APP_DATA_PATH("assets/devpro-wroom/marauder/partitions.bin"); + app0 = APP_DATA_PATH("assets/devpro-wroom/boot_app0.bin"); + firm = APP_DATA_PATH("assets/devpro-wroom/marauder/marauder_dev_board_pro.bin"); + break; + default: + flash = false; + consumed = false; + break; + } + + if(flash) { + scene_manager_set_scene_state(app->scene_manager, EspFlasherSceneQuick, event.event); + memset(app->selected_flash_options, 0, sizeof(app->selected_flash_options)); + app->bin_file_path_boot[0] = '\0'; + app->bin_file_path_part[0] = '\0'; + app->bin_file_path_nvs[0] = '\0'; + app->bin_file_path_boot_app0[0] = '\0'; + app->bin_file_path_app_a[0] = '\0'; + app->bin_file_path_app_b[0] = '\0'; + app->bin_file_path_custom[0] = '\0'; + + if(boot) { + app->selected_flash_options[SelectedFlashBoot] = true; + strncpy(app->bin_file_path_boot, boot, sizeof(app->bin_file_path_boot)); + } + if(part) { + app->selected_flash_options[SelectedFlashPart] = true; + strncpy(app->bin_file_path_part, part, sizeof(app->bin_file_path_part)); + } + if(app0) { + app->selected_flash_options[SelectedFlashBootApp0] = true; + strncpy(app->bin_file_path_boot_app0, app0, sizeof(app->bin_file_path_boot_app0)); + } + if(firm) { + app->selected_flash_options[SelectedFlashAppA] = true; + strncpy(app->bin_file_path_app_a, firm, sizeof(app->bin_file_path_app_a)); + } + + app->reset = false; + app->quickflash = true; + app->boot = enter_bootloader; + scene_manager_next_scene(app->scene_manager, EspFlasherSceneConsoleOutput); + } + } else if(event.type == SceneManagerEventTypeBack) { + uint32_t state = scene_manager_get_scene_state(app->scene_manager, EspFlasherSceneQuick); + if(state > QuickDevproWroom) + state = QuickDevproWroom; + else if(state > QuickWifidevS2) + state = QuickWifidevS2; + scene_manager_set_scene_state(app->scene_manager, EspFlasherSceneQuick, state); + } + + return consumed; +} + +void esp_flasher_scene_quick_on_exit(void* context) { + furi_assert(context); + + EspFlasherApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/external/esp_flasher/scenes/esp_flasher_scene_start.c b/applications/external/esp_flasher/scenes/esp_flasher_scene_start.c index d531b884a..a7db0e99a 100644 --- a/applications/external/esp_flasher/scenes/esp_flasher_scene_start.c +++ b/applications/external/esp_flasher/scenes/esp_flasher_scene_start.c @@ -1,11 +1,13 @@ #include "../esp_flasher_app_i.h" enum SubmenuIndex { - SubmenuIndexEspFlasherDevboardFlash, - SubmenuIndexEspFlasherFlash, - SubmenuIndexEspFlasherAbout, + SubmenuIndexEspFlasherQuickFlash, + SubmenuIndexEspFlasherSwitchA, + SubmenuIndexEspFlasherSwitchB, + SubmenuIndexEspFlasherManualFlash, SubmenuIndexEspFlasherReset, SubmenuIndexEspFlasherBootloader, + SubmenuIndexEspFlasherAbout, }; void esp_flasher_scene_start_submenu_callback(void* context, uint32_t index) { @@ -20,16 +22,29 @@ void esp_flasher_scene_start_on_enter(void* context) { EspFlasherApp* app = context; Submenu* submenu = app->submenu; + submenu_set_header(submenu, "ESP Flasher"); submenu_add_item( submenu, - "Flash Wifi Devboard", - SubmenuIndexEspFlasherDevboardFlash, + "Quick Flash", + SubmenuIndexEspFlasherQuickFlash, esp_flasher_scene_start_submenu_callback, app); submenu_add_item( submenu, - "Flash Generic ESP", - SubmenuIndexEspFlasherFlash, + "Select Evil Portal (Fw A)", + SubmenuIndexEspFlasherSwitchA, + esp_flasher_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Select Marauder (Fw B)", + SubmenuIndexEspFlasherSwitchB, + esp_flasher_scene_start_submenu_callback, + app); + submenu_add_item( + submenu, + "Manual Flash", + SubmenuIndexEspFlasherManualFlash, esp_flasher_scene_start_submenu_callback, app); submenu_add_item( @@ -63,10 +78,22 @@ bool esp_flasher_scene_start_on_event(void* context, SceneManagerEvent event) { EspFlasherApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexEspFlasherDevboardFlash) { - scene_manager_next_scene(app->scene_manager, EspFlasherSceneDevboard); + if(event.event == SubmenuIndexEspFlasherQuickFlash) { + scene_manager_next_scene(app->scene_manager, EspFlasherSceneQuick); consumed = true; - } else if(event.event == SubmenuIndexEspFlasherFlash) { + } else if(event.event == SubmenuIndexEspFlasherSwitchA) { + app->boot = true; + app->quickflash = true; + app->switch_fw = SwitchToFirmwareA; + scene_manager_next_scene(app->scene_manager, EspFlasherSceneConsoleOutput); + consumed = true; + } else if(event.event == SubmenuIndexEspFlasherSwitchB) { + app->boot = true; + app->quickflash = true; + app->switch_fw = SwitchToFirmwareB; + scene_manager_next_scene(app->scene_manager, EspFlasherSceneConsoleOutput); + consumed = true; + } else if(event.event == SubmenuIndexEspFlasherManualFlash) { scene_manager_next_scene(app->scene_manager, EspFlasherSceneBrowse); consumed = true; } else if(event.event == SubmenuIndexEspFlasherReset) { diff --git a/applications/external/esp_flasher/update_10px.png b/applications/external/esp_flasher/update_10px.png new file mode 100644 index 000000000..5a97651c4 Binary files /dev/null and b/applications/external/esp_flasher/update_10px.png differ diff --git a/applications/external/esp_flasher/wifi_10px.png b/applications/external/esp_flasher/wifi_10px.png deleted file mode 100644 index c13534660..000000000 Binary files a/applications/external/esp_flasher/wifi_10px.png and /dev/null differ diff --git a/applications/external/esubghz_chat/LICENSE b/applications/external/esubghz_chat/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/applications/external/esubghz_chat/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/esubghz_chat/application.fam b/applications/external/esubghz_chat/application.fam new file mode 100644 index 000000000..70b8c6c70 --- /dev/null +++ b/applications/external/esubghz_chat/application.fam @@ -0,0 +1,15 @@ +App( + appid="esubghz_chat", + name="Enhanced Sub-Ghz Chat", + apptype=FlipperAppType.EXTERNAL, + entry_point="esubghz_chat", + requires=[ + "gui", + "subghz", + ], + stack_size=8 * 1024, + fap_category="Sub-GHz", + fap_icon="assets/chat_10px.png", + fap_icon_assets="assets", + fap_icon_assets_symbol="esubghz_chat", +) diff --git a/assets/icons/Infrared/TrackNext_hvr_25x27.png b/applications/external/esubghz_chat/assets/Loading_24.png similarity index 75% rename from assets/icons/Infrared/TrackNext_hvr_25x27.png rename to applications/external/esubghz_chat/assets/Loading_24.png index a4de4fc3c..93a59fe68 100644 Binary files a/assets/icons/Infrared/TrackNext_hvr_25x27.png and b/applications/external/esubghz_chat/assets/Loading_24.png differ diff --git a/applications/external/esubghz_chat/assets/Nfc_14px.png b/applications/external/esubghz_chat/assets/Nfc_14px.png new file mode 100644 index 000000000..9e43a3291 Binary files /dev/null and b/applications/external/esubghz_chat/assets/Nfc_14px.png differ diff --git a/applications/external/esubghz_chat/assets/chat_10px.png b/applications/external/esubghz_chat/assets/chat_10px.png new file mode 100644 index 000000000..9ee8343c1 Binary files /dev/null and b/applications/external/esubghz_chat/assets/chat_10px.png differ diff --git a/applications/external/esubghz_chat/assets/chat_14px.png b/applications/external/esubghz_chat/assets/chat_14px.png new file mode 100644 index 000000000..f949d0a79 Binary files /dev/null and b/applications/external/esubghz_chat/assets/chat_14px.png differ diff --git a/applications/external/esubghz_chat/assets/hex_14px.png b/applications/external/esubghz_chat/assets/hex_14px.png new file mode 100644 index 000000000..b2af9e204 Binary files /dev/null and b/applications/external/esubghz_chat/assets/hex_14px.png differ diff --git a/applications/external/esubghz_chat/assets/keyboard_14px.png b/applications/external/esubghz_chat/assets/keyboard_14px.png new file mode 100644 index 000000000..b84309c5a Binary files /dev/null and b/applications/external/esubghz_chat/assets/keyboard_14px.png differ diff --git a/applications/external/esubghz_chat/assets/u2f_14px.png b/applications/external/esubghz_chat/assets/u2f_14px.png new file mode 100644 index 000000000..d65bacea9 Binary files /dev/null and b/applications/external/esubghz_chat/assets/u2f_14px.png differ diff --git a/applications/external/esubghz_chat/crypto/aes.c b/applications/external/esubghz_chat/crypto/aes.c new file mode 100644 index 000000000..b2fe5fced --- /dev/null +++ b/applications/external/esubghz_chat/crypto/aes.c @@ -0,0 +1,440 @@ +/****************************************************************************** +* +* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL +* +* This is a simple and straightforward implementation of the AES Rijndael +* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus +* of this work was correctness & accuracy. It is written in 'C' without any +* particular focus upon optimization or speed. It should be endian (memory +* byte order) neutral since the few places that care are handled explicitly. +* +* This implementation of Rijndael was created by Steven M. Gibson of GRC.com. +* +* It is intended for general purpose use, but was written in support of GRC's +* reference implementation of the SQRL (Secure Quick Reliable Login) client. +* +* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html +* +* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE +* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. +* +*******************************************************************************/ + +#include "aes.h" + +static int aes_tables_inited = 0; // run-once flag for performing key + // expasion table generation (see below) +/* + * The following static local tables must be filled-in before the first use of + * the GCM or AES ciphers. They are used for the AES key expansion/scheduling + * and once built are read-only and thread safe. The "gcm_initialize" function + * must be called once during system initialization to populate these arrays + * for subsequent use by the AES key scheduler. If they have not been built + * before attempted use, an error will be returned to the caller. + * + * NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since + * GCM uses AES in counter-mode, where the AES cipher output is XORed with + * the GCM input, we ONLY NEED AES encryption. Thus, to save space AES + * decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h. + */ +// We always need our forward tables +static uchar FSb[256]; // Forward substitution box (FSb) +static uint32_t FT0[256]; // Forward key schedule assembly tables +static uint32_t FT1[256]; +static uint32_t FT2[256]; +static uint32_t FT3[256]; + +#if AES_DECRYPTION // We ONLY need reverse for decryption +static uchar RSb[256]; // Reverse substitution box (RSb) +static uint32_t RT0[256]; // Reverse key schedule assembly tables +static uint32_t RT1[256]; +static uint32_t RT2[256]; +static uint32_t RT3[256]; +#endif /* AES_DECRYPTION */ + +static uint32_t RCON[10]; // AES round constants + +/* + * Platform Endianness Neutralizing Load and Store Macro definitions + * AES wants platform-neutral Little Endian (LE) byte ordering + */ +#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); \ + } + +#define PUT_UINT32_LE(n, b, i) \ + { \ + (b)[(i)] = (uchar)((n)); \ + (b)[(i) + 1] = (uchar)((n) >> 8); \ + (b)[(i) + 2] = (uchar)((n) >> 16); \ + (b)[(i) + 3] = (uchar)((n) >> 24); \ + } + +/* + * AES forward and reverse encryption round processing macros + */ +#define AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \ + { \ + X0 = *RK++ ^ FT0[(Y0)&0xFF] ^ FT1[(Y1 >> 8) & 0xFF] ^ FT2[(Y2 >> 16) & 0xFF] ^ \ + FT3[(Y3 >> 24) & 0xFF]; \ + \ + X1 = *RK++ ^ FT0[(Y1)&0xFF] ^ FT1[(Y2 >> 8) & 0xFF] ^ FT2[(Y3 >> 16) & 0xFF] ^ \ + FT3[(Y0 >> 24) & 0xFF]; \ + \ + X2 = *RK++ ^ FT0[(Y2)&0xFF] ^ FT1[(Y3 >> 8) & 0xFF] ^ FT2[(Y0 >> 16) & 0xFF] ^ \ + FT3[(Y1 >> 24) & 0xFF]; \ + \ + X3 = *RK++ ^ FT0[(Y3)&0xFF] ^ FT1[(Y0 >> 8) & 0xFF] ^ FT2[(Y1 >> 16) & 0xFF] ^ \ + FT3[(Y2 >> 24) & 0xFF]; \ + } + +#define AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3) \ + { \ + X0 = *RK++ ^ RT0[(Y0)&0xFF] ^ RT1[(Y3 >> 8) & 0xFF] ^ RT2[(Y2 >> 16) & 0xFF] ^ \ + RT3[(Y1 >> 24) & 0xFF]; \ + \ + X1 = *RK++ ^ RT0[(Y1)&0xFF] ^ RT1[(Y0 >> 8) & 0xFF] ^ RT2[(Y3 >> 16) & 0xFF] ^ \ + RT3[(Y2 >> 24) & 0xFF]; \ + \ + X2 = *RK++ ^ RT0[(Y2)&0xFF] ^ RT1[(Y1 >> 8) & 0xFF] ^ RT2[(Y0 >> 16) & 0xFF] ^ \ + RT3[(Y3 >> 24) & 0xFF]; \ + \ + X3 = *RK++ ^ RT0[(Y3)&0xFF] ^ RT1[(Y2 >> 8) & 0xFF] ^ RT2[(Y1 >> 16) & 0xFF] ^ \ + RT3[(Y0 >> 24) & 0xFF]; \ + } + +/* + * These macros improve the readability of the key + * generation initialization code by collapsing + * repetitive common operations into logical pieces. + */ +#define ROTL8(x) ((x << 8) & 0xFFFFFFFF) | (x >> 24) +#define XTIME(x) ((x << 1) ^ ((x & 0x80) ? 0x1B : 0x00)) +#define MUL(x, y) ((x && y) ? pow[(log[x] + log[y]) % 255] : 0) +#define MIX(x, y) \ + { \ + y = ((y << 1) | (y >> 7)) & 0xFF; \ + x ^= y; \ + } +#define CPY128 \ + { \ + *RK++ = *SK++; \ + *RK++ = *SK++; \ + *RK++ = *SK++; \ + *RK++ = *SK++; \ + } + +/****************************************************************************** + * + * AES_INIT_KEYGEN_TABLES + * + * Fills the AES key expansion tables allocated above with their static + * data. This is not "per key" data, but static system-wide read-only + * table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once + * at system initialization to setup the tables for all subsequent use. + * + ******************************************************************************/ +void aes_init_keygen_tables(void) { + int i, x, y, z; // general purpose iteration and computation locals + int pow[256]; + int log[256]; + + if(aes_tables_inited) return; + + // fill the 'pow' and 'log' tables over GF(2^8) + for(i = 0, x = 1; i < 256; i++) { + pow[i] = x; + log[x] = i; + x = (x ^ XTIME(x)) & 0xFF; + } + // compute the round constants + for(i = 0, x = 1; i < 10; i++) { + RCON[i] = (uint32_t)x; + x = XTIME(x) & 0xFF; + } + // fill the forward and reverse substitution boxes + FSb[0x00] = 0x63; +#if AES_DECRYPTION // whether AES decryption is supported + RSb[0x63] = 0x00; +#endif /* AES_DECRYPTION */ + + for(i = 1; i < 256; i++) { + x = y = pow[255 - log[i]]; + MIX(x, y); + MIX(x, y); + MIX(x, y); + MIX(x, y); + FSb[i] = (uchar)(x ^= 0x63); +#if AES_DECRYPTION // whether AES decryption is supported + RSb[x] = (uchar)i; +#endif /* AES_DECRYPTION */ + } + // generate the forward and reverse key expansion tables + for(i = 0; i < 256; i++) { + x = FSb[i]; + y = XTIME(x) & 0xFF; + z = (y ^ x) & 0xFF; + + FT0[i] = ((uint32_t)y) ^ ((uint32_t)x << 8) ^ ((uint32_t)x << 16) ^ ((uint32_t)z << 24); + + FT1[i] = ROTL8(FT0[i]); + FT2[i] = ROTL8(FT1[i]); + FT3[i] = ROTL8(FT2[i]); + +#if AES_DECRYPTION // whether AES decryption is supported + x = RSb[i]; + + RT0[i] = ((uint32_t)MUL(0x0E, x)) ^ ((uint32_t)MUL(0x09, x) << 8) ^ + ((uint32_t)MUL(0x0D, x) << 16) ^ ((uint32_t)MUL(0x0B, x) << 24); + + RT1[i] = ROTL8(RT0[i]); + RT2[i] = ROTL8(RT1[i]); + RT3[i] = ROTL8(RT2[i]); +#endif /* AES_DECRYPTION */ + } + aes_tables_inited = 1; // flag that the tables have been generated +} // to permit subsequent use of the AES cipher + +/****************************************************************************** + * + * AES_SET_ENCRYPTION_KEY + * + * This is called by 'aes_setkey' when we're establishing a key for + * subsequent encryption. We give it a pointer to the encryption + * context, a pointer to the key, and the key's length in bytes. + * Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits). + * + ******************************************************************************/ +int aes_set_encryption_key(aes_context* ctx, const uchar* key, uint keysize) { + uint i; // general purpose iteration local + uint32_t* RK = ctx->rk; // initialize our RoundKey buffer pointer + + for(i = 0; i < (keysize >> 2); i++) { + GET_UINT32_LE(RK[i], key, i << 2); + } + + switch(ctx->rounds) { + case 10: + for(i = 0; i < 10; i++, RK += 4) { + RK[4] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[3] >> 8) & 0xFF]) ^ + ((uint32_t)FSb[(RK[3] >> 16) & 0xFF] << 8) ^ + ((uint32_t)FSb[(RK[3] >> 24) & 0xFF] << 16) ^ + ((uint32_t)FSb[(RK[3]) & 0xFF] << 24); + + RK[5] = RK[1] ^ RK[4]; + RK[6] = RK[2] ^ RK[5]; + RK[7] = RK[3] ^ RK[6]; + } + break; + + case 12: + for(i = 0; i < 8; i++, RK += 6) { + RK[6] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[5] >> 8) & 0xFF]) ^ + ((uint32_t)FSb[(RK[5] >> 16) & 0xFF] << 8) ^ + ((uint32_t)FSb[(RK[5] >> 24) & 0xFF] << 16) ^ + ((uint32_t)FSb[(RK[5]) & 0xFF] << 24); + + RK[7] = RK[1] ^ RK[6]; + RK[8] = RK[2] ^ RK[7]; + RK[9] = RK[3] ^ RK[8]; + RK[10] = RK[4] ^ RK[9]; + RK[11] = RK[5] ^ RK[10]; + } + break; + + case 14: + for(i = 0; i < 7; i++, RK += 8) { + RK[8] = RK[0] ^ RCON[i] ^ ((uint32_t)FSb[(RK[7] >> 8) & 0xFF]) ^ + ((uint32_t)FSb[(RK[7] >> 16) & 0xFF] << 8) ^ + ((uint32_t)FSb[(RK[7] >> 24) & 0xFF] << 16) ^ + ((uint32_t)FSb[(RK[7]) & 0xFF] << 24); + + RK[9] = RK[1] ^ RK[8]; + RK[10] = RK[2] ^ RK[9]; + RK[11] = RK[3] ^ RK[10]; + + RK[12] = RK[4] ^ ((uint32_t)FSb[(RK[11]) & 0xFF]) ^ + ((uint32_t)FSb[(RK[11] >> 8) & 0xFF] << 8) ^ + ((uint32_t)FSb[(RK[11] >> 16) & 0xFF] << 16) ^ + ((uint32_t)FSb[(RK[11] >> 24) & 0xFF] << 24); + + RK[13] = RK[5] ^ RK[12]; + RK[14] = RK[6] ^ RK[13]; + RK[15] = RK[7] ^ RK[14]; + } + break; + + default: + return -1; + } + return (0); +} + +#if AES_DECRYPTION // whether AES decryption is supported + +/****************************************************************************** + * + * AES_SET_DECRYPTION_KEY + * + * This is called by 'aes_setkey' when we're establishing a + * key for subsequent decryption. We give it a pointer to + * the encryption context, a pointer to the key, and the key's + * length in bits. Valid lengths are: 128, 192, or 256 bits. + * + ******************************************************************************/ +int aes_set_decryption_key(aes_context* ctx, const uchar* key, uint keysize) { + int i, j; + aes_context cty; // a calling aes context for set_encryption_key + uint32_t* RK = ctx->rk; // initialize our RoundKey buffer pointer + uint32_t* SK; + int ret; + + cty.rounds = ctx->rounds; // initialize our local aes context + cty.rk = cty.buf; // round count and key buf pointer + + if((ret = aes_set_encryption_key(&cty, key, keysize)) != 0) return (ret); + + SK = cty.rk + cty.rounds * 4; + + CPY128 // copy a 128-bit block from *SK to *RK + + for(i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8) { + for(j = 0; j < 4; j++, SK++) { + *RK++ = RT0[FSb[(*SK) & 0xFF]] ^ RT1[FSb[(*SK >> 8) & 0xFF]] ^ + RT2[FSb[(*SK >> 16) & 0xFF]] ^ RT3[FSb[(*SK >> 24) & 0xFF]]; + } + } + CPY128 // copy a 128-bit block from *SK to *RK + memset(&cty, 0, sizeof(aes_context)); // clear local aes context + return (0); +} + +#endif /* AES_DECRYPTION */ + +/****************************************************************************** + * + * AES_SETKEY + * + * Invoked to establish the key schedule for subsequent encryption/decryption + * + ******************************************************************************/ +int aes_setkey( + aes_context* ctx, // AES context provided by our caller + int mode, // ENCRYPT or DECRYPT flag + const uchar* key, // pointer to the key + uint keysize) // key length in bytes +{ + // since table initialization is not thread safe, we could either add + // system-specific mutexes and init the AES key generation tables on + // demand, or ask the developer to simply call "gcm_initialize" once during + // application startup before threading begins. That's what we choose. + if(!aes_tables_inited) return (-1); // fail the call when not inited. + + ctx->mode = mode; // capture the key type we're creating + ctx->rk = ctx->buf; // initialize our round key pointer + + switch(keysize) // set the rounds count based upon the keysize + { + case 16: + ctx->rounds = 10; + break; // 16-byte, 128-bit key + case 24: + ctx->rounds = 12; + break; // 24-byte, 192-bit key + case 32: + ctx->rounds = 14; + break; // 32-byte, 256-bit key + default: + return (-1); + } + +#if AES_DECRYPTION + if(mode == DECRYPT) // expand our key for encryption or decryption + return (aes_set_decryption_key(ctx, key, keysize)); + else /* ENCRYPT */ +#endif /* AES_DECRYPTION */ + return (aes_set_encryption_key(ctx, key, keysize)); +} + +/****************************************************************************** + * + * AES_CIPHER + * + * Perform AES encryption and decryption. + * The AES context will have been setup with the encryption mode + * and all keying information appropriate for the task. + * + ******************************************************************************/ +int aes_cipher(aes_context* ctx, const uchar input[16], uchar output[16]) { + int i; + uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals + + RK = ctx->rk; + + GET_UINT32_LE(X0, input, 0); + X0 ^= *RK++; // load our 128-bit + GET_UINT32_LE(X1, input, 4); + X1 ^= *RK++; // input buffer in a storage + GET_UINT32_LE(X2, input, 8); + X2 ^= *RK++; // memory endian-neutral way + GET_UINT32_LE(X3, input, 12); + X3 ^= *RK++; + +#if AES_DECRYPTION // whether AES decryption is supported + + if(ctx->mode == DECRYPT) { + for(i = (ctx->rounds >> 1) - 1; i > 0; i--) { + AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + } + + AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + + X0 = *RK++ ^ ((uint32_t)RSb[(Y0)&0xFF]) ^ ((uint32_t)RSb[(Y3 >> 8) & 0xFF] << 8) ^ + ((uint32_t)RSb[(Y2 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y1 >> 24) & 0xFF] << 24); + + X1 = *RK++ ^ ((uint32_t)RSb[(Y1)&0xFF]) ^ ((uint32_t)RSb[(Y0 >> 8) & 0xFF] << 8) ^ + ((uint32_t)RSb[(Y3 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y2 >> 24) & 0xFF] << 24); + + X2 = *RK++ ^ ((uint32_t)RSb[(Y2)&0xFF]) ^ ((uint32_t)RSb[(Y1 >> 8) & 0xFF] << 8) ^ + ((uint32_t)RSb[(Y0 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y3 >> 24) & 0xFF] << 24); + + X3 = *RK++ ^ ((uint32_t)RSb[(Y3)&0xFF]) ^ ((uint32_t)RSb[(Y2 >> 8) & 0xFF] << 8) ^ + ((uint32_t)RSb[(Y1 >> 16) & 0xFF] << 16) ^ ((uint32_t)RSb[(Y0 >> 24) & 0xFF] << 24); + } else /* ENCRYPT */ + { +#endif /* AES_DECRYPTION */ + + for(i = (ctx->rounds >> 1) - 1; i > 0; i--) { + AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); + } + + AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); + + X0 = *RK++ ^ ((uint32_t)FSb[(Y0)&0xFF]) ^ ((uint32_t)FSb[(Y1 >> 8) & 0xFF] << 8) ^ + ((uint32_t)FSb[(Y2 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y3 >> 24) & 0xFF] << 24); + + X1 = *RK++ ^ ((uint32_t)FSb[(Y1)&0xFF]) ^ ((uint32_t)FSb[(Y2 >> 8) & 0xFF] << 8) ^ + ((uint32_t)FSb[(Y3 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y0 >> 24) & 0xFF] << 24); + + X2 = *RK++ ^ ((uint32_t)FSb[(Y2)&0xFF]) ^ ((uint32_t)FSb[(Y3 >> 8) & 0xFF] << 8) ^ + ((uint32_t)FSb[(Y0 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y1 >> 24) & 0xFF] << 24); + + X3 = *RK++ ^ ((uint32_t)FSb[(Y3)&0xFF]) ^ ((uint32_t)FSb[(Y0 >> 8) & 0xFF] << 8) ^ + ((uint32_t)FSb[(Y1 >> 16) & 0xFF] << 16) ^ ((uint32_t)FSb[(Y2 >> 24) & 0xFF] << 24); + +#if AES_DECRYPTION // whether AES decryption is supported + } +#endif /* AES_DECRYPTION */ + + PUT_UINT32_LE(X0, output, 0); + PUT_UINT32_LE(X1, output, 4); + PUT_UINT32_LE(X2, output, 8); + PUT_UINT32_LE(X3, output, 12); + + return (0); +} +/* end of aes.c */ diff --git a/applications/external/esubghz_chat/crypto/aes.h b/applications/external/esubghz_chat/crypto/aes.h new file mode 100644 index 000000000..3ffaa065b --- /dev/null +++ b/applications/external/esubghz_chat/crypto/aes.h @@ -0,0 +1,80 @@ +/****************************************************************************** +* +* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL +* +* This is a simple and straightforward implementation of the AES Rijndael +* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus +* of this work was correctness & accuracy. It is written in 'C' without any +* particular focus upon optimization or speed. It should be endian (memory +* byte order) neutral since the few places that care are handled explicitly. +* +* This implementation of Rijndael was created by Steven M. Gibson of GRC.com. +* +* It is intended for general purpose use, but was written in support of GRC's +* reference implementation of the SQRL (Secure Quick Reliable Login) client. +* +* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html +* +* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE +* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. +* +*******************************************************************************/ + +#ifndef AES_HEADER +#define AES_HEADER + +/******************************************************************************/ +#define AES_DECRYPTION 0 // whether AES decryption is supported +/******************************************************************************/ + +#include + +#define ENCRYPT 1 // specify whether we're encrypting +#define DECRYPT 0 // or decrypting + +#if defined(_MSC_VER) +#include +typedef UINT32 uint32_t; +#else +#include +#endif + +typedef unsigned char uchar; // add some convienent shorter types +typedef unsigned int uint; + +/****************************************************************************** + * AES_INIT_KEYGEN_TABLES : MUST be called once before any AES use + ******************************************************************************/ +void aes_init_keygen_tables(void); + +/****************************************************************************** + * AES_CONTEXT : cipher context / holds inter-call data + ******************************************************************************/ +typedef struct { + int mode; // 1 for Encryption, 0 for Decryption + int rounds; // keysize-based rounds count + uint32_t* rk; // pointer to current round key + uint32_t buf[68]; // key expansion buffer +} aes_context; + +/****************************************************************************** + * AES_SETKEY : called to expand the key for encryption or decryption + ******************************************************************************/ +int aes_setkey( + aes_context* ctx, // pointer to context + int mode, // 1 or 0 for Encrypt/Decrypt + const uchar* key, // AES input key + uint keysize); // size in bytes (must be 16, 24, 32 for +// 128, 192 or 256-bit keys respectively) +// returns 0 for success + +/****************************************************************************** + * AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data + ******************************************************************************/ +int aes_cipher( + aes_context* ctx, // pointer to context + const uchar input[16], // 128-bit block to en/decipher + uchar output[16]); // 128-bit output result block +// returns 0 for success + +#endif /* AES_HEADER */ diff --git a/applications/external/esubghz_chat/crypto/gcm.c b/applications/external/esubghz_chat/crypto/gcm.c new file mode 100644 index 000000000..c5aad7aa4 --- /dev/null +++ b/applications/external/esubghz_chat/crypto/gcm.c @@ -0,0 +1,516 @@ +/****************************************************************************** +* +* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL +* +* This is a simple and straightforward implementation of AES-GCM authenticated +* encryption. The focus of this work was correctness & accuracy. It is written +* in straight 'C' without any particular focus upon optimization or speed. It +* should be endian (memory byte order) neutral since the few places that care +* are handled explicitly. +* +* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. +* +* It is intended for general purpose use, but was written in support of GRC's +* reference implementation of the SQRL (Secure Quick Reliable Login) client. +* +* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf +* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ +* gcm/gcm-revised-spec.pdf +* +* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE +* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. +* +*******************************************************************************/ + +#include "gcm.h" +#include "aes.h" + +/****************************************************************************** + * ==== IMPLEMENTATION WARNING ==== + * + * This code was developed for use within SQRL's fixed environmnent. Thus, it + * is somewhat less "general purpose" than it would be if it were designed as + * a general purpose AES-GCM library. Specifically, it bothers with almost NO + * error checking on parameter limits, buffer bounds, etc. It assumes that it + * is being invoked by its author or by someone who understands the values it + * expects to receive. Its behavior will be undefined otherwise. + * + * All functions that might fail are defined to return 'ints' to indicate a + * problem. Most do not do so now. But this allows for error propagation out + * of internal functions if robust error checking should ever be desired. + * + ******************************************************************************/ + +/* Calculating the "GHASH" + * + * There are many ways of calculating the so-called GHASH in software, each with + * a traditional size vs performance tradeoff. The GHASH (Galois field hash) is + * an intriguing construction which takes two 128-bit strings (also the cipher's + * block size and the fundamental operation size for the system) and hashes them + * into a third 128-bit result. + * + * Many implementation solutions have been worked out that use large precomputed + * table lookups in place of more time consuming bit fiddling, and this approach + * can be scaled easily upward or downward as needed to change the time/space + * tradeoff. It's been studied extensively and there's a solid body of theory and + * practice. For example, without using any lookup tables an implementation + * might obtain 119 cycles per byte throughput, whereas using a simple, though + * large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13 + * cycles per byte. + * + * And Intel's processors have, since 2010, included an instruction which does + * the entire 128x128->128 bit job in just several 64x64->128 bit pieces. + * + * Since SQRL is interactive, and only processing a few 128-bit blocks, I've + * settled upon a relatively slower but appealing small-table compromise which + * folds a bunch of not only time consuming but also bit twiddling into a simple + * 16-entry table which is attributed to Victor Shoup's 1996 work while at + * Bellcore: "On Fast and Provably Secure MessageAuthentication Based on + * Universal Hashing." See: http://www.shoup.net/papers/macs.pdf + * See, also section 4.1 of the "gcm-revised-spec" cited above. + */ + +/* + * This 16-entry table of pre-computed constants is used by the + * GHASH multiplier to improve over a strictly table-free but + * significantly slower 128x128 bit multiple within GF(2^128). + */ +static const uint64_t last4[16] = { + 0x0000, + 0x1c20, + 0x3840, + 0x2460, + 0x7080, + 0x6ca0, + 0x48c0, + 0x54e0, + 0xe100, + 0xfd20, + 0xd940, + 0xc560, + 0x9180, + 0x8da0, + 0xa9c0, + 0xb5e0}; + +/* + * Platform Endianness Neutralizing Load and Store Macro definitions + * GCM wants platform-neutral Big Endian (BE) byte ordering + */ +#define GET_UINT32_BE(n, b, i) \ + { \ + (n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | \ + ((uint32_t)(b)[(i) + 2] << 8) | ((uint32_t)(b)[(i) + 3]); \ + } + +#define PUT_UINT32_BE(n, b, i) \ + { \ + (b)[(i)] = (uchar)((n) >> 24); \ + (b)[(i) + 1] = (uchar)((n) >> 16); \ + (b)[(i) + 2] = (uchar)((n) >> 8); \ + (b)[(i) + 3] = (uchar)((n)); \ + } + +/****************************************************************************** + * + * GCM_INITIALIZE + * + * Must be called once to initialize the GCM library. + * + * At present, this only calls the AES keygen table generator, which expands + * the AES keying tables for use. This is NOT A THREAD-SAFE function, so it + * MUST be called during system initialization before a multi-threading + * environment is running. + * + ******************************************************************************/ +int gcm_initialize(void) { + aes_init_keygen_tables(); + return (0); +} + +/****************************************************************************** + * + * GCM_MULT + * + * Performs a GHASH operation on the 128-bit input vector 'x', setting + * the 128-bit output vector to 'x' times H using our precomputed tables. + * 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field. + * + ******************************************************************************/ +static void gcm_mult( + gcm_context* ctx, // pointer to established context + const uchar x[16], // pointer to 128-bit input vector + uchar output[16]) // pointer to 128-bit output vector +{ + int i; + uchar lo, hi, rem; + uint64_t zh, zl; + + lo = (uchar)(x[15] & 0x0f); + hi = (uchar)(x[15] >> 4); + zh = ctx->HH[lo]; + zl = ctx->HL[lo]; + + for(i = 15; i >= 0; i--) { + lo = (uchar)(x[i] & 0x0f); + hi = (uchar)(x[i] >> 4); + + if(i != 15) { + rem = (uchar)(zl & 0x0f); + zl = (zh << 60) | (zl >> 4); + zh = (zh >> 4); + zh ^= (uint64_t)last4[rem] << 48; + zh ^= ctx->HH[lo]; + zl ^= ctx->HL[lo]; + } + rem = (uchar)(zl & 0x0f); + zl = (zh << 60) | (zl >> 4); + zh = (zh >> 4); + zh ^= (uint64_t)last4[rem] << 48; + zh ^= ctx->HH[hi]; + zl ^= ctx->HL[hi]; + } + PUT_UINT32_BE(zh >> 32, output, 0); + PUT_UINT32_BE(zh, output, 4); + PUT_UINT32_BE(zl >> 32, output, 8); + PUT_UINT32_BE(zl, output, 12); +} + +/****************************************************************************** + * + * GCM_SETKEY + * + * This is called to set the AES-GCM key. It initializes the AES key + * and populates the gcm context's pre-calculated HTables. + * + ******************************************************************************/ +int gcm_setkey( + gcm_context* ctx, // pointer to caller-provided gcm context + const uchar* key, // pointer to the AES encryption key + const uint keysize) // size in bytes (must be 16, 24, 32 for +// 128, 192 or 256-bit keys respectively) +{ + int ret, i, j; + uint64_t hi, lo; + uint64_t vl, vh; + unsigned char h[16]; + + memset(ctx, 0, sizeof(gcm_context)); // zero caller-provided GCM context + memset(h, 0, 16); // initialize the block to encrypt + + // encrypt the null 128-bit block to generate a key-based value + // which is then used to initialize our GHASH lookup tables + if((ret = aes_setkey(&ctx->aes_ctx, ENCRYPT, key, keysize)) != 0) return (ret); + if((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0) return (ret); + + GET_UINT32_BE(hi, h, 0); // pack h as two 64-bit ints, big-endian + GET_UINT32_BE(lo, h, 4); + vh = (uint64_t)hi << 32 | lo; + + GET_UINT32_BE(hi, h, 8); + GET_UINT32_BE(lo, h, 12); + vl = (uint64_t)hi << 32 | lo; + + ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128) + ctx->HH[8] = vh; + ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128) + ctx->HL[0] = 0; + + for(i = 4; i > 0; i >>= 1) { + uint32_t T = (uint32_t)(vl & 1) * 0xe1000000U; + vl = (vh << 63) | (vl >> 1); + vh = (vh >> 1) ^ ((uint64_t)T << 32); + ctx->HL[i] = vl; + ctx->HH[i] = vh; + } + for(i = 2; i < 16; i <<= 1) { + uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i; + vh = *HiH; + vl = *HiL; + for(j = 1; j < i; j++) { + HiH[j] = vh ^ ctx->HH[j]; + HiL[j] = vl ^ ctx->HL[j]; + } + } + return (0); +} + +/****************************************************************************** + * + * GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH. + * + * SETKEY: + * + * START: Sets the Encryption/Decryption mode. + * Accepts the initialization vector and additional data. + * + * UPDATE: Encrypts or decrypts the plaintext or ciphertext. + * + * FINISH: Performs a final GHASH to generate the authentication tag. + * + ****************************************************************************** + * + * GCM_START + * + * Given a user-provided GCM context, this initializes it, sets the encryption + * mode, and preprocesses the initialization vector and additional AEAD data. + * + ******************************************************************************/ +int gcm_start( + gcm_context* ctx, // pointer to user-provided GCM context + int mode, // GCM_ENCRYPT or GCM_DECRYPT + const uchar* iv, // pointer to initialization vector + size_t iv_len, // IV length in bytes (should == 12) + const uchar* add, // ptr to additional AEAD data (NULL if none) + size_t add_len) // length of additional AEAD data (bytes) +{ + int ret; // our error return if the AES encrypt fails + uchar work_buf[16]; // XOR source built from provided IV if len != 16 + const uchar* p; // general purpose array pointer + size_t use_len; // byte count to process, up to 16 bytes + size_t i; // local loop iterator + + // since the context might be reused under the same key + // we zero the working buffers for this next new process + memset(ctx->y, 0x00, sizeof(ctx->y)); + memset(ctx->buf, 0x00, sizeof(ctx->buf)); + ctx->len = 0; + ctx->add_len = 0; + + ctx->mode = mode; // set the GCM encryption/decryption mode + ctx->aes_ctx.mode = ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode + + if(iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV + memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff + ctx->y[15] = 1; // start "counting" from 1 (not 0) + } else // if we don't have a 12-byte IV, we GHASH whatever we've been given + { + memset(work_buf, 0x00, 16); // clear the working buffer + PUT_UINT32_BE(iv_len * 8, work_buf, 12); // place the IV into buffer + + p = iv; + while(iv_len > 0) { + use_len = (iv_len < 16) ? iv_len : 16; + for(i = 0; i < use_len; i++) ctx->y[i] ^= p[i]; + gcm_mult(ctx, ctx->y, ctx->y); + iv_len -= use_len; + p += use_len; + } + for(i = 0; i < 16; i++) ctx->y[i] ^= work_buf[i]; + gcm_mult(ctx, ctx->y, ctx->y); + } + if((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ctx->base_ectr)) != 0) return (ret); + + ctx->add_len = add_len; + p = add; + while(add_len > 0) { + use_len = (add_len < 16) ? add_len : 16; + for(i = 0; i < use_len; i++) ctx->buf[i] ^= p[i]; + gcm_mult(ctx, ctx->buf, ctx->buf); + add_len -= use_len; + p += use_len; + } + return (0); +} + +/****************************************************************************** + * + * GCM_UPDATE + * + * This is called once or more to process bulk plaintext or ciphertext data. + * We give this some number of bytes of input and it returns the same number + * of output bytes. If called multiple times (which is fine) all but the final + * invocation MUST be called with length mod 16 == 0. (Only the final call can + * have a partial block length of < 128 bits.) + * + ******************************************************************************/ +int gcm_update( + gcm_context* ctx, // pointer to user-provided GCM context + size_t length, // length, in bytes, of data to process + const uchar* input, // pointer to source data + uchar* output) // pointer to destination data +{ + int ret; // our error return if the AES encrypt fails + uchar ectr[16]; // counter-mode cipher output for XORing + size_t use_len; // byte count to process, up to 16 bytes + size_t i; // local loop iterator + + ctx->len += length; // bump the GCM context's running length count + + while(length > 0) { + // clamp the length to process at 16 bytes + use_len = (length < 16) ? length : 16; + + // increment the context's 128-bit IV||Counter 'y' vector + for(i = 16; i > 12; i--) + if(++ctx->y[i - 1] != 0) break; + + // encrypt the context's 'y' vector under the established key + if((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ectr)) != 0) return (ret); + + // encrypt or decrypt the input to the output + if(ctx->mode == ENCRYPT) { + for(i = 0; i < use_len; i++) { + // XOR the cipher's ouptut vector (ectr) with our input + output[i] = (uchar)(ectr[i] ^ input[i]); + // now we mix in our data into the authentication hash. + // if we're ENcrypting we XOR in the post-XOR (output) + // results, but if we're DEcrypting we XOR in the input + // data + ctx->buf[i] ^= output[i]; + } + } else { + for(i = 0; i < use_len; i++) { + // but if we're DEcrypting we XOR in the input data first, + // i.e. before saving to ouput data, otherwise if the input + // and output buffer are the same (inplace decryption) we + // would not get the correct auth tag + + ctx->buf[i] ^= input[i]; + + // XOR the cipher's ouptut vector (ectr) with our input + output[i] = (uchar)(ectr[i] ^ input[i]); + } + } + gcm_mult(ctx, ctx->buf, ctx->buf); // perform a GHASH operation + + length -= use_len; // drop the remaining byte count to process + input += use_len; // bump our input pointer forward + output += use_len; // bump our output pointer forward + } + return (0); +} + +/****************************************************************************** + * + * GCM_FINISH + * + * This is called once after all calls to GCM_UPDATE to finalize the GCM. + * It performs the final GHASH to produce the resulting authentication TAG. + * + ******************************************************************************/ +int gcm_finish( + gcm_context* ctx, // pointer to user-provided GCM context + uchar* tag, // pointer to buffer which receives the tag + size_t tag_len) // length, in bytes, of the tag-receiving buf +{ + uchar work_buf[16]; + uint64_t orig_len = ctx->len * 8; + uint64_t orig_add_len = ctx->add_len * 8; + size_t i; + + if(tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len); + + if(orig_len || orig_add_len) { + memset(work_buf, 0x00, 16); + + PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0); + PUT_UINT32_BE((orig_add_len), work_buf, 4); + PUT_UINT32_BE((orig_len >> 32), work_buf, 8); + PUT_UINT32_BE((orig_len), work_buf, 12); + + for(i = 0; i < 16; i++) ctx->buf[i] ^= work_buf[i]; + gcm_mult(ctx, ctx->buf, ctx->buf); + for(i = 0; i < tag_len; i++) tag[i] ^= ctx->buf[i]; + } + return (0); +} + +/****************************************************************************** + * + * GCM_CRYPT_AND_TAG + * + * This either encrypts or decrypts the user-provided data and, either + * way, generates an authentication tag of the requested length. It must be + * called with a GCM context whose key has already been set with GCM_SETKEY. + * + * The user would typically call this explicitly to ENCRYPT a buffer of data + * and optional associated data, and produce its an authentication tag. + * + * To reverse the process the user would typically call the companion + * GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided + * authentication tag. The GCM_AUTH_DECRYPT function calls this function + * to perform its decryption and tag generation, which it then compares. + * + ******************************************************************************/ +int gcm_crypt_and_tag( + gcm_context* ctx, // gcm context with key already setup + int mode, // cipher direction: GCM_ENCRYPT or GCM_DECRYPT + const uchar* iv, // pointer to the 12-byte initialization vector + size_t iv_len, // byte length if the IV. should always be 12 + const uchar* add, // pointer to the non-ciphered additional data + size_t add_len, // byte length of the additional AEAD data + const uchar* input, // pointer to the cipher data source + uchar* output, // pointer to the cipher data destination + size_t length, // byte length of the cipher data + uchar* tag, // pointer to the tag to be generated + size_t tag_len) // byte length of the tag to be generated +{ /* + assuming that the caller has already invoked gcm_setkey to + prepare the gcm context with the keying material, we simply + invoke each of the three GCM sub-functions in turn... + */ + gcm_start(ctx, mode, iv, iv_len, add, add_len); + gcm_update(ctx, length, input, output); + gcm_finish(ctx, tag, tag_len); + return (0); +} + +/****************************************************************************** + * + * GCM_AUTH_DECRYPT + * + * This DECRYPTS a user-provided data buffer with optional associated data. + * It then verifies a user-supplied authentication tag against the tag just + * re-created during decryption to verify that the data has not been altered. + * + * This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption + * and authentication tag generation. + * + ******************************************************************************/ +int gcm_auth_decrypt( + gcm_context* ctx, // gcm context with key already setup + const uchar* iv, // pointer to the 12-byte initialization vector + size_t iv_len, // byte length if the IV. should always be 12 + const uchar* add, // pointer to the non-ciphered additional data + size_t add_len, // byte length of the additional AEAD data + const uchar* input, // pointer to the cipher data source + uchar* output, // pointer to the cipher data destination + size_t length, // byte length of the cipher data + const uchar* tag, // pointer to the tag to be authenticated + size_t tag_len) // byte length of the tag <= 16 +{ + uchar check_tag[16]; // the tag generated and returned by decryption + int diff; // an ORed flag to detect authentication errors + size_t i; // our local iterator + /* + we use GCM_DECRYPT_AND_TAG (above) to perform our decryption + (which is an identical XORing to reverse the previous one) + and also to re-generate the matching authentication tag + */ + gcm_crypt_and_tag( + ctx, DECRYPT, iv, iv_len, add, add_len, input, output, length, check_tag, tag_len); + + // now we verify the authentication tag in 'constant time' + for(diff = 0, i = 0; i < tag_len; i++) diff |= tag[i] ^ check_tag[i]; + + if(diff != 0) { // see whether any bits differed? + memset(output, 0, length); // if so... wipe the output data + return (GCM_AUTH_FAILURE); // return GCM_AUTH_FAILURE + } + return (0); +} + +/****************************************************************************** + * + * GCM_ZERO_CTX + * + * The GCM context contains both the GCM context and the AES context. + * This includes keying and key-related material which is security- + * sensitive, so it MUST be zeroed after use. This function does that. + * + ******************************************************************************/ +void gcm_zero_ctx(gcm_context* ctx) { + // zero the context originally provided to us + memset(ctx, 0, sizeof(gcm_context)); +} diff --git a/applications/external/esubghz_chat/crypto/gcm.h b/applications/external/esubghz_chat/crypto/gcm.h new file mode 100644 index 000000000..9a3e47052 --- /dev/null +++ b/applications/external/esubghz_chat/crypto/gcm.h @@ -0,0 +1,181 @@ +/****************************************************************************** +* +* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL +* +* This is a simple and straightforward implementation of AES-GCM authenticated +* encryption. The focus of this work was correctness & accuracy. It is written +* in straight 'C' without any particular focus upon optimization or speed. It +* should be endian (memory byte order) neutral since the few places that care +* are handled explicitly. +* +* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. +* +* It is intended for general purpose use, but was written in support of GRC's +* reference implementation of the SQRL (Secure Quick Reliable Login) client. +* +* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf +* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \ +* gcm/gcm-revised-spec.pdf +* +* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE +* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. +* +*******************************************************************************/ +#ifndef GCM_HEADER +#define GCM_HEADER + +#define GCM_AUTH_FAILURE 0x55555555 // authentication failure + +#include "aes.h" // gcm_context includes aes_context + +#if defined(_MSC_VER) +#include +typedef unsigned int size_t; // use the right type for length declarations +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; +#else +#include +#endif + +/****************************************************************************** + * GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx + ******************************************************************************/ +typedef struct { + int mode; // cipher direction: encrypt/decrypt + uint64_t len; // cipher data length processed so far + uint64_t add_len; // total add data length + uint64_t HL[16]; // precalculated lo-half HTable + uint64_t HH[16]; // precalculated hi-half HTable + uchar base_ectr[16]; // first counter-mode cipher output for tag + uchar y[16]; // the current cipher-input IV|Counter value + uchar buf[16]; // buf working value + aes_context aes_ctx; // cipher context used +} gcm_context; + +/****************************************************************************** + * GCM_CONTEXT : MUST be called once before ANY use of this library + ******************************************************************************/ +int gcm_initialize(void); + +/****************************************************************************** + * GCM_SETKEY : sets the GCM (and AES) keying material for use + ******************************************************************************/ +int gcm_setkey( + gcm_context* ctx, // caller-provided context ptr + const uchar* key, // pointer to cipher key + const uint keysize // size in bytes (must be 16, 24, 32 for + // 128, 192 or 256-bit keys respectively) +); // returns 0 for success + +/****************************************************************************** + * + * GCM_CRYPT_AND_TAG + * + * This either encrypts or decrypts the user-provided data and, either + * way, generates an authentication tag of the requested length. It must be + * called with a GCM context whose key has already been set with GCM_SETKEY. + * + * The user would typically call this explicitly to ENCRYPT a buffer of data + * and optional associated data, and produce its an authentication tag. + * + * To reverse the process the user would typically call the companion + * GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided + * authentication tag. The GCM_AUTH_DECRYPT function calls this function + * to perform its decryption and tag generation, which it then compares. + * + ******************************************************************************/ +int gcm_crypt_and_tag( + gcm_context* ctx, // gcm context with key already setup + int mode, // cipher direction: ENCRYPT (1) or DECRYPT (0) + const uchar* iv, // pointer to the 12-byte initialization vector + size_t iv_len, // byte length if the IV. should always be 12 + const uchar* add, // pointer to the non-ciphered additional data + size_t add_len, // byte length of the additional AEAD data + const uchar* input, // pointer to the cipher data source + uchar* output, // pointer to the cipher data destination + size_t length, // byte length of the cipher data + uchar* tag, // pointer to the tag to be generated + size_t tag_len); // byte length of the tag to be generated + +/****************************************************************************** + * + * GCM_AUTH_DECRYPT + * + * This DECRYPTS a user-provided data buffer with optional associated data. + * It then verifies a user-supplied authentication tag against the tag just + * re-created during decryption to verify that the data has not been altered. + * + * This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption + * and authentication tag generation. + * + ******************************************************************************/ +int gcm_auth_decrypt( + gcm_context* ctx, // gcm context with key already setup + const uchar* iv, // pointer to the 12-byte initialization vector + size_t iv_len, // byte length if the IV. should always be 12 + const uchar* add, // pointer to the non-ciphered additional data + size_t add_len, // byte length of the additional AEAD data + const uchar* input, // pointer to the cipher data source + uchar* output, // pointer to the cipher data destination + size_t length, // byte length of the cipher data + const uchar* tag, // pointer to the tag to be authenticated + size_t tag_len); // byte length of the tag <= 16 + +/****************************************************************************** + * + * GCM_START + * + * Given a user-provided GCM context, this initializes it, sets the encryption + * mode, and preprocesses the initialization vector and additional AEAD data. + * + ******************************************************************************/ +int gcm_start( + gcm_context* ctx, // pointer to user-provided GCM context + int mode, // ENCRYPT (1) or DECRYPT (0) + const uchar* iv, // pointer to initialization vector + size_t iv_len, // IV length in bytes (should == 12) + const uchar* add, // pointer to additional AEAD data (NULL if none) + size_t add_len); // length of additional AEAD data (bytes) + +/****************************************************************************** + * + * GCM_UPDATE + * + * This is called once or more to process bulk plaintext or ciphertext data. + * We give this some number of bytes of input and it returns the same number + * of output bytes. If called multiple times (which is fine) all but the final + * invocation MUST be called with length mod 16 == 0. (Only the final call can + * have a partial block length of < 128 bits.) + * + ******************************************************************************/ +int gcm_update( + gcm_context* ctx, // pointer to user-provided GCM context + size_t length, // length, in bytes, of data to process + const uchar* input, // pointer to source data + uchar* output); // pointer to destination data + +/****************************************************************************** + * + * GCM_FINISH + * + * This is called once after all calls to GCM_UPDATE to finalize the GCM. + * It performs the final GHASH to produce the resulting authentication TAG. + * + ******************************************************************************/ +int gcm_finish( + gcm_context* ctx, // pointer to user-provided GCM context + uchar* tag, // ptr to tag buffer - NULL if tag_len = 0 + size_t tag_len); // length, in bytes, of the tag-receiving buf + +/****************************************************************************** + * + * GCM_ZERO_CTX + * + * The GCM context contains both the GCM context and the AES context. + * This includes keying and key-related material which is security- + * sensitive, so it MUST be zeroed after use. This function does that. + * + ******************************************************************************/ +void gcm_zero_ctx(gcm_context* ctx); + +#endif /* GCM_HEADER */ diff --git a/applications/external/esubghz_chat/crypto_wrapper.c b/applications/external/esubghz_chat/crypto_wrapper.c new file mode 100644 index 000000000..5d72bd843 --- /dev/null +++ b/applications/external/esubghz_chat/crypto_wrapper.c @@ -0,0 +1,237 @@ +#include +#include +#include + +#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL +#include "crypto/gcm.h" +#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ + +#include "crypto_wrapper.h" + +DICT_DEF2(ESubGhzChatReplayDict, uint64_t, uint32_t) + +struct ESugGhzChatCryptoCtx { + uint8_t key[KEY_BITS / 8]; +#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL + gcm_context gcm_ctx; +#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ + ESubGhzChatReplayDict_t replay_dict; + uint64_t run_id; + uint32_t counter; +}; + +struct ESubGhzChatCryptoMsg { + uint64_t run_id; + uint32_t counter; + uint8_t iv[IV_BYTES]; + uint8_t tag[TAG_BYTES]; + uint8_t data[0]; +} __attribute__((packed)); + +void crypto_init(void) { +#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL + /* init the GCM and AES tables */ + gcm_initialize(); +#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ +} + +void crypto_explicit_bzero(void* s, size_t len) { + memset(s, 0, len); + asm volatile("" ::: "memory"); +} + +ESubGhzChatCryptoCtx* crypto_ctx_alloc(void) { + ESubGhzChatCryptoCtx* ret = malloc(sizeof(ESubGhzChatCryptoCtx)); + + if(ret != NULL) { + memset(ret, 0, sizeof(ESubGhzChatCryptoCtx)); + ESubGhzChatReplayDict_init(ret->replay_dict); + ret->run_id = 0; + ret->counter = 1; + } + + return ret; +} + +void crypto_ctx_free(ESubGhzChatCryptoCtx* ctx) { + crypto_ctx_clear(ctx); + ESubGhzChatReplayDict_clear(ctx->replay_dict); + free(ctx); +} + +void crypto_ctx_clear(ESubGhzChatCryptoCtx* ctx) { + crypto_explicit_bzero(ctx->key, sizeof(ctx->key)); +#ifndef FURI_HAL_CRYPTO_ADVANCED_AVAIL + crypto_explicit_bzero(&(ctx->gcm_ctx), sizeof(ctx->gcm_ctx)); +#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ + ESubGhzChatReplayDict_reset(ctx->replay_dict); + ctx->run_id = 0; + ctx->counter = 1; +} + +static uint64_t crypto_calc_run_id(FuriString* flipper_name, uint32_t tick) { + const char* fn = furi_string_get_cstr(flipper_name); + size_t fn_len = strlen(fn); + + uint8_t h_in[fn_len + sizeof(uint32_t)]; + memcpy(h_in, fn, fn_len); + memcpy(h_in + fn_len, &tick, sizeof(uint32_t)); + + uint8_t h_out[256]; + sha256(h_in, fn_len + sizeof(uint32_t), h_out); + + uint64_t run_id; + memcpy(&run_id, h_out, sizeof(uint64_t)); + + return run_id; +} + +bool crypto_ctx_set_key( + ESubGhzChatCryptoCtx* ctx, + const uint8_t* key, + FuriString* flipper_name, + uint32_t tick) { + ctx->run_id = crypto_calc_run_id(flipper_name, tick); + ctx->counter = 1; + + memcpy(ctx->key, key, KEY_BITS / 8); +#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL + return true; +#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ + return (gcm_setkey(&(ctx->gcm_ctx), key, KEY_BITS / 8) == 0); +#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ +} + +void crypto_ctx_get_key(ESubGhzChatCryptoCtx* ctx, uint8_t* key) { + memcpy(key, ctx->key, KEY_BITS / 8); +} + +bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out) { + if(in_len < MSG_OVERHEAD + 1) { + return false; + } + + struct ESubGhzChatCryptoMsg* msg = (struct ESubGhzChatCryptoMsg*)in; + + // check if message is stale, if yes, discard + uint32_t* counter = ESubGhzChatReplayDict_get(ctx->replay_dict, msg->run_id); + if(counter != NULL) { + if(*counter >= __ntohl(msg->counter)) { + return false; + } + } + + // decrypt and auth message +#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL + bool ret = + (furi_hal_crypto_gcm_decrypt_and_verify( + ctx->key, + msg->iv, + (uint8_t*)msg, + RUN_ID_BYTES + COUNTER_BYTES, + msg->data, + out, + in_len - MSG_OVERHEAD, + msg->tag) == FuriHalCryptoGCMStateOk); +#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ + bool ret = + (gcm_auth_decrypt( + &(ctx->gcm_ctx), + msg->iv, + IV_BYTES, + (uint8_t*)msg, + RUN_ID_BYTES + COUNTER_BYTES, + msg->data, + out, + in_len - MSG_OVERHEAD, + msg->tag, + TAG_BYTES) == 0); +#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ + + // if auth was successful update replay dict + if(ret) { + ESubGhzChatReplayDict_set_at(ctx->replay_dict, msg->run_id, __ntohl(msg->counter)); + } + + return ret; +} + +bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out) { + struct ESubGhzChatCryptoMsg* msg = (struct ESubGhzChatCryptoMsg*)out; + + // fill message header + msg->run_id = ctx->run_id; + msg->counter = __htonl(ctx->counter); + furi_hal_random_fill_buf(msg->iv, IV_BYTES); + + // encrypt message and store tag in header +#ifdef FURI_HAL_CRYPTO_ADVANCED_AVAIL + bool ret = + (furi_hal_crypto_gcm_encrypt_and_tag( + ctx->key, + msg->iv, + (uint8_t*)msg, + RUN_ID_BYTES + COUNTER_BYTES, + in, + msg->data, + in_len, + msg->tag) == FuriHalCryptoGCMStateOk); +#else /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ + bool ret = + (gcm_crypt_and_tag( + &(ctx->gcm_ctx), + ENCRYPT, + msg->iv, + IV_BYTES, + (uint8_t*)msg, + RUN_ID_BYTES + COUNTER_BYTES, + in, + msg->data, + in_len, + msg->tag, + TAG_BYTES) == 0); +#endif /* FURI_HAL_CRYPTO_ADVANCED_AVAIL */ + + // update replay dict and increase internal counter + if(ret) { + ESubGhzChatReplayDict_set_at(ctx->replay_dict, ctx->run_id, ctx->counter); + ctx->counter++; + } + + return ret; +} + +size_t crypto_ctx_dump_replay_dict( + ESubGhzChatCryptoCtx* ctx, + CryptoCtxReplayDictWriter writer, + void* writer_ctx) { + size_t ret = 0; + ESubGhzChatReplayDict_it_t i; + + for(ESubGhzChatReplayDict_it(i, ctx->replay_dict); !ESubGhzChatReplayDict_end_p(i); + ESubGhzChatReplayDict_next(i), ret++) { + ESubGhzChatReplayDict_itref_t* ref = ESubGhzChatReplayDict_ref(i); + if(!writer(ref->key, ref->value, writer_ctx)) { + break; + } + } + + return ret; +} + +size_t crypto_ctx_read_replay_dict( + ESubGhzChatCryptoCtx* ctx, + CryptoCtxReplayDictReader reader, + void* reader_ctx) { + size_t ret = 0; + + uint64_t run_id; + uint32_t counter; + + while(reader(&run_id, &counter, reader_ctx)) { + ESubGhzChatReplayDict_set_at(ctx->replay_dict, run_id, counter); + ret++; + } + + return ret; +} diff --git a/applications/external/esubghz_chat/crypto_wrapper.h b/applications/external/esubghz_chat/crypto_wrapper.h new file mode 100644 index 000000000..080a604d1 --- /dev/null +++ b/applications/external/esubghz_chat/crypto_wrapper.h @@ -0,0 +1,51 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define RUN_ID_BYTES (sizeof(uint64_t)) +#define COUNTER_BYTES (sizeof(uint32_t)) +#define KEY_BITS 256 +#define IV_BYTES 12 +#define TAG_BYTES 16 + +#define MSG_OVERHEAD (RUN_ID_BYTES + COUNTER_BYTES + IV_BYTES + TAG_BYTES) + +typedef struct ESugGhzChatCryptoCtx ESubGhzChatCryptoCtx; + +void crypto_init(void); + +/* Function to clear sensitive memory. */ +void crypto_explicit_bzero(void* s, size_t len); + +ESubGhzChatCryptoCtx* crypto_ctx_alloc(void); +void crypto_ctx_free(ESubGhzChatCryptoCtx* ctx); + +void crypto_ctx_clear(ESubGhzChatCryptoCtx* ctx); + +bool crypto_ctx_set_key( + ESubGhzChatCryptoCtx* ctx, + const uint8_t* key, + FuriString* flipper_name, + uint32_t tick); +void crypto_ctx_get_key(ESubGhzChatCryptoCtx* ctx, uint8_t* key); + +bool crypto_ctx_decrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out); +bool crypto_ctx_encrypt(ESubGhzChatCryptoCtx* ctx, uint8_t* in, size_t in_len, uint8_t* out); + +typedef bool (*CryptoCtxReplayDictWriter)(uint64_t run_id, uint32_t counter, void* context); +typedef bool (*CryptoCtxReplayDictReader)(uint64_t* run_id, uint32_t* counter, void* context); + +size_t crypto_ctx_dump_replay_dict( + ESubGhzChatCryptoCtx* ctx, + CryptoCtxReplayDictWriter writer, + void* writer_ctx); +size_t crypto_ctx_read_replay_dict( + ESubGhzChatCryptoCtx* ctx, + CryptoCtxReplayDictReader reader, + void* reader_ctx); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/esubghz_chat/esubghz_chat.c b/applications/external/esubghz_chat/esubghz_chat.c new file mode 100644 index 000000000..6a5778fd0 --- /dev/null +++ b/applications/external/esubghz_chat/esubghz_chat.c @@ -0,0 +1,703 @@ +#include +#include +#include + +#include "helpers/radio_device_loader.h" +#include "esubghz_chat_i.h" + +#define CHAT_LEAVE_DELAY 10 +#define TICK_INTERVAL 50 +#define MESSAGE_COMPLETION_TIMEOUT 500 + +#define KBD_UNLOCK_CNT 3 +#define KBD_UNLOCK_TIMEOUT 1000 + +/* Callback for RX events from the Sub-GHz worker. Records the current ticks as + * the time of the last reception. */ +static void have_read_cb(void* context) { + furi_assert(context); + ESubGhzChatState* state = context; + + state->last_time_rx_data = furi_get_tick(); +} + +/* Sets the header for the chat input field depending on whether or not a + * message preview exists. */ +void set_chat_input_header(ESubGhzChatState* state) { + if(strlen(state->msg_preview) == 0) { + text_input_set_header_text(state->text_input, "Message"); + } else { + text_input_set_header_text(state->text_input, state->msg_preview); + } +} + +/* Appends the latest message to the chat box and prepares the message preview. + */ +void append_msg(ESubGhzChatState* state, const char* msg) { + /* append message to text box */ + furi_string_cat_printf(state->chat_box_store, "\n%s", msg); + + /* prepare message preview */ + strncpy(state->msg_preview, msg, MSG_PREVIEW_SIZE); + state->msg_preview[MSG_PREVIEW_SIZE] = 0; + set_chat_input_header(state); + + /* reset text box contents and focus */ + text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store)); + text_box_set_focus(state->chat_box, TextBoxFocusEnd); +} + +/* Decrypts a message for post_rx(). */ +static bool post_rx_decrypt(ESubGhzChatState* state, size_t rx_size) { + bool ret = crypto_ctx_decrypt( + state->crypto_ctx, state->rx_buffer, rx_size, (uint8_t*)state->rx_str_buffer); + + if(ret) { + state->rx_str_buffer[rx_size - (MSG_OVERHEAD)] = 0; + } else { + state->rx_str_buffer[0] = 0; + } + + return ret; +} + +/* Post RX handler, decrypts received messages and calls append_msg(). */ +static void post_rx(ESubGhzChatState* state, size_t rx_size) { + furi_assert(state); + + if(rx_size == 0) { + return; + } + + furi_check(rx_size <= RX_TX_BUFFER_SIZE); + + /* decrypt if necessary */ + if(!state->encrypted) { + memcpy(state->rx_str_buffer, state->rx_buffer, rx_size); + state->rx_str_buffer[rx_size] = 0; + + /* remove trailing newline if it is there, for compat with CLI + * Sub-GHz chat */ + if(state->rx_str_buffer[rx_size - 1] == '\n') { + state->rx_str_buffer[rx_size - 1] = 0; + } + } else { + /* if decryption fails output an error message */ + if(!post_rx_decrypt(state, rx_size)) { + strcpy(state->rx_str_buffer, "ERR: Decryption failed!"); + } + } + + /* append message to text box and prepare message preview */ + append_msg(state, state->rx_str_buffer); + + /* send notification (make the flipper vibrate) */ + notification_message(state->notification, &sequence_single_vibro); +} + +/* Reads the message from msg_input, encrypts it if necessary and then + * transmits it. */ +void tx_msg_input(ESubGhzChatState* state) { + /* encrypt message if necessary */ + size_t msg_len = strlen(furi_string_get_cstr(state->msg_input)); + size_t tx_size = msg_len; + if(state->encrypted) { + tx_size += MSG_OVERHEAD; + furi_check(tx_size <= sizeof(state->tx_buffer)); + + crypto_ctx_encrypt( + state->crypto_ctx, + (uint8_t*)furi_string_get_cstr(state->msg_input), + msg_len, + state->tx_buffer); + } else { + tx_size += 2; + furi_check(tx_size <= sizeof(state->tx_buffer)); + memcpy(state->tx_buffer, furi_string_get_cstr(state->msg_input), msg_len); + + /* append \r\n for compat with Sub-GHz CLI chat */ + state->tx_buffer[msg_len] = '\r'; + state->tx_buffer[msg_len + 1] = '\n'; + } + + /* transmit */ + subghz_tx_rx_worker_write(state->subghz_worker, state->tx_buffer, tx_size); +} + +/* Displays information on frequency, encryption and radio type in the text + * box. Also clears the text input buffer to remove the password and starts the + * Sub-GHz worker. After starting the worker a join message is transmitted. */ +void enter_chat(ESubGhzChatState* state) { + furi_string_cat_printf(state->chat_box_store, "Frequency: %lu", state->frequency); + + furi_string_cat_printf( + state->chat_box_store, "\nEncrypted: %s", (state->encrypted ? "yes" : "no")); + + subghz_tx_rx_worker_start(state->subghz_worker, state->subghz_device, state->frequency); + + if(strcmp(state->subghz_device->name, "cc1101_ext") == 0) { + furi_string_cat_printf(state->chat_box_store, "\nRadio: External"); + } else { + furi_string_cat_printf(state->chat_box_store, "\nRadio: Internal"); + } + + /* concatenate the name prefix and join message */ + furi_string_set(state->msg_input, state->name_prefix); + furi_string_cat_str(state->msg_input, " joined chat."); + + /* encrypt and transmit message */ + tx_msg_input(state); + + /* clear message input buffer */ + furi_string_set_char(state->msg_input, 0, 0); +} + +/* Sends a leave message */ +void exit_chat(ESubGhzChatState* state) { + /* concatenate the name prefix and leave message */ + furi_string_set(state->msg_input, state->name_prefix); + furi_string_cat_str(state->msg_input, " left chat."); + + /* encrypt and transmit message */ + tx_msg_input(state); + + /* clear message input buffer */ + furi_string_set_char(state->msg_input, 0, 0); + + /* wait for leave message to be delivered */ + furi_delay_ms(CHAT_LEAVE_DELAY); +} + +/* Whether or not to display the locked message. */ +static bool kbd_lock_msg_display(ESubGhzChatState* state) { + return (state->kbd_lock_msg_ticks != 0); +} + +/* Whether or not to hide the locked message again. */ +static bool kbd_lock_msg_reset_timeout(ESubGhzChatState* state) { + if(state->kbd_lock_msg_ticks == 0) { + return false; + } + + if(furi_get_tick() - state->kbd_lock_msg_ticks > KBD_UNLOCK_TIMEOUT) { + return true; + } + + return false; +} + +/* Resets the timeout for the locked message and turns off the backlight if + * specified. */ +static void kbd_lock_msg_reset(ESubGhzChatState* state, bool backlight_off) { + state->kbd_lock_msg_ticks = 0; + state->kbd_lock_count = 0; + + if(backlight_off) { + notification_message(state->notification, &sequence_display_backlight_off); + } +} + +/* Locks the keyboard. */ +static void kbd_lock(ESubGhzChatState* state) { + state->kbd_locked = true; + kbd_lock_msg_reset(state, true); +} + +/* Unlocks the keyboard. */ +static void kbd_unlock(ESubGhzChatState* state) { + state->kbd_locked = false; + kbd_lock_msg_reset(state, false); +} + +/* Custom event callback for view dispatcher. Just calls scene manager. */ +static bool esubghz_chat_custom_event_callback(void* context, uint32_t event) { + FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_custom_event_callback"); + furi_assert(context); + ESubGhzChatState* state = context; + return scene_manager_handle_custom_event(state->scene_manager, event); +} + +/* Navigation event callback for view dispatcher. Just calls scene manager. */ +static bool esubghz_chat_navigation_event_callback(void* context) { + FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_navigation_event_callback"); + furi_assert(context); + ESubGhzChatState* state = context; + return scene_manager_handle_back_event(state->scene_manager); +} + +/* Tick event callback for view dispatcher. Called every TICK_INTERVAL. Resets + * the locked message if necessary. Retrieves a received message from the + * Sub-GHz worker and calls post_rx(). Then calls the scene manager. */ +static void esubghz_chat_tick_event_callback(void* context) { + FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_tick_event_callback"); + + furi_assert(context); + ESubGhzChatState* state = context; + + /* reset locked message if necessary */ + if(kbd_lock_msg_reset_timeout(state)) { + kbd_lock_msg_reset(state, true); + } + + /* if the maximum message size was reached or the + * MESSAGE_COMPLETION_TIMEOUT has expired, retrieve a message and call + * post_rx() */ + size_t avail = 0; + while((avail = subghz_tx_rx_worker_available(state->subghz_worker)) > 0) { + volatile uint32_t since_last_rx = furi_get_tick() - state->last_time_rx_data; + if(avail < RX_TX_BUFFER_SIZE && since_last_rx < MESSAGE_COMPLETION_TIMEOUT) { + break; + } + + size_t rx_size = + subghz_tx_rx_worker_read(state->subghz_worker, state->rx_buffer, RX_TX_BUFFER_SIZE); + post_rx(state, rx_size); + } + + /* call scene manager */ + scene_manager_handle_tick_event(state->scene_manager); +} + +/* Hooks into the view port's draw callback to overlay the keyboard locked + * message. */ +static void esubghz_hooked_draw_callback(Canvas* canvas, void* context) { + FURI_LOG_T(APPLICATION_NAME, "esubghz_hooked_draw_callback"); + + furi_assert(canvas); + + furi_assert(context); + ESubGhzChatState* state = context; + + /* call original callback */ + state->orig_draw_cb(canvas, state->view_dispatcher); + + /* display if the keyboard is locked */ + if(state->kbd_locked) { + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_framed(canvas, 42, 30, "Locked"); + } + + /* display the unlock message if necessary */ + if(kbd_lock_msg_display(state)) { + canvas_set_font(canvas, FontSecondary); + elements_bold_rounded_frame(canvas, 14, 8, 99, 48); + elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); + canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42); + } +} + +/* Hooks into the view port's input callback to handle the user locking the + * keyboard. */ +static void esubghz_hooked_input_callback(InputEvent* event, void* context) { + FURI_LOG_T(APPLICATION_NAME, "esubghz_hooked_input_callback"); + + furi_assert(event); + + furi_assert(context); + ESubGhzChatState* state = context; + + /* if the keyboard is locked no key presses are forwarded */ + if(state->kbd_locked) { + /* key has been pressed, display the unlock message and + * initiate the timer */ + if(state->kbd_lock_count == 0) { + state->kbd_lock_msg_ticks = furi_get_tick(); + } + + /* back button has been pressed, increase the lock counter */ + if(event->key == InputKeyBack && event->type == InputTypeShort) { + state->kbd_lock_count++; + } + + /* unlock the keyboard */ + if(state->kbd_lock_count >= KBD_UNLOCK_CNT) { + kbd_unlock(state); + } + + /* do not handle the event */ + return; + } + + if(event->key == InputKeyOk) { + /* if we are in the chat view and no input is ongoing, allow + * locking */ + if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) && + !(state->kbd_ok_input_ongoing)) { + /* lock keyboard upon long press of Ok button */ + if(event->type == InputTypeLong) { + kbd_lock(state); + } + + /* do not handle any Ok key events to prevent blocking + * of other keys */ + return; + } + + /* handle ongoing inputs when changing to chat view */ + if(event->type == InputTypePress) { + state->kbd_ok_input_ongoing = true; + } else if(event->type == InputTypeRelease) { + state->kbd_ok_input_ongoing = false; + } + } + + if(event->key == InputKeyLeft) { + /* if we are in the chat view and no input is ongoing, allow + * switching to msg input */ + if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) && + !(state->kbd_left_input_ongoing)) { + /* go to msg input upon short press of Left button */ + if(event->type == InputTypeShort) { + view_dispatcher_send_custom_event( + state->view_dispatcher, ESubGhzChatEvent_GotoMsgInput); + } + + /* do not handle any Left key events to prevent + * blocking of other keys */ + return; + } + + /* handle ongoing inputs when changing to chat view */ + if(event->type == InputTypePress) { + state->kbd_left_input_ongoing = true; + } else if(event->type == InputTypeRelease) { + state->kbd_left_input_ongoing = false; + } + } + + if(event->key == InputKeyRight) { + /* if we are in the chat view and no input is ongoing, allow + * switching to key display */ + if(state->view_dispatcher->current_view == text_box_get_view(state->chat_box) && + !(state->kbd_right_input_ongoing)) { + /* go to key display upon short press of Right button + */ + if(event->type == InputTypeShort) { + view_dispatcher_send_custom_event( + state->view_dispatcher, ESubGhzChatEvent_GotoKeyDisplay); + } + + /* do not handle any Right key events to prevent + * blocking of other keys */ + return; + } + + /* handle ongoing inputs when changing to chat view */ + if(event->type == InputTypePress) { + state->kbd_right_input_ongoing = true; + } else if(event->type == InputTypeRelease) { + state->kbd_right_input_ongoing = false; + } + } + + /* call original callback */ + state->orig_input_cb(event, state->view_dispatcher); +} + +static bool helper_strings_alloc(ESubGhzChatState* state) { + furi_assert(state); + + state->name_prefix = furi_string_alloc(); + if(state->name_prefix == NULL) { + return false; + } + + state->msg_input = furi_string_alloc(); + if(state->msg_input == NULL) { + furi_string_free(state->name_prefix); + return false; + } + + return true; +} + +static void helper_strings_free(ESubGhzChatState* state) { + furi_assert(state); + + furi_string_free(state->name_prefix); + furi_string_free(state->msg_input); +} + +static bool chat_box_alloc(ESubGhzChatState* state) { + furi_assert(state); + + state->chat_box = text_box_alloc(); + if(state->chat_box == NULL) { + return false; + } + + state->chat_box_store = furi_string_alloc(); + if(state->chat_box_store == NULL) { + text_box_free(state->chat_box); + return false; + } + + furi_string_reserve(state->chat_box_store, CHAT_BOX_STORE_SIZE); + furi_string_set_char(state->chat_box_store, 0, 0); + text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store)); + text_box_set_focus(state->chat_box, TextBoxFocusEnd); + + return true; +} + +static void chat_box_free(ESubGhzChatState* state) { + furi_assert(state); + + text_box_free(state->chat_box); + furi_string_free(state->chat_box_store); +} + +int32_t esubghz_chat(void) { + /* init the crypto system */ + crypto_init(); + + int32_t err = -1; + + FURI_LOG_I(APPLICATION_NAME, "Starting..."); + + /* allocate necessary structs and buffers */ + + ESubGhzChatState* state = malloc(sizeof(ESubGhzChatState)); + if(state == NULL) { + goto err_alloc; + } + memset(state, 0, sizeof(*state)); + + state->scene_manager = scene_manager_alloc(&esubghz_chat_scene_event_handlers, state); + if(state->scene_manager == NULL) { + goto err_alloc_sm; + } + + state->view_dispatcher = view_dispatcher_alloc(); + if(state->view_dispatcher == NULL) { + goto err_alloc_vd; + } + + if(!helper_strings_alloc(state)) { + goto err_alloc_hs; + } + + state->menu = menu_alloc(); + if(state->menu == NULL) { + goto err_alloc_menu; + } + + state->text_input = text_input_alloc(); + if(state->text_input == NULL) { + goto err_alloc_ti; + } + + state->hex_key_input = byte_input_alloc(); + if(state->hex_key_input == NULL) { + goto err_alloc_hki; + } + + if(!chat_box_alloc(state)) { + goto err_alloc_cb; + } + + state->key_display = dialog_ex_alloc(); + if(state->key_display == NULL) { + goto err_alloc_kd; + } + + state->nfc_popup = popup_alloc(); + if(state->nfc_popup == NULL) { + goto err_alloc_np; + } + + state->subghz_worker = subghz_tx_rx_worker_alloc(); + if(state->subghz_worker == NULL) { + goto err_alloc_worker; + } + + state->nfc_worker = nfc_worker_alloc(); + if(state->nfc_worker == NULL) { + goto err_alloc_nworker; + } + + state->nfc_dev_data = malloc(sizeof(NfcDeviceData)); + if(state->nfc_dev_data == NULL) { + goto err_alloc_ndevdata; + } + memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData)); + + state->crypto_ctx = crypto_ctx_alloc(); + if(state->crypto_ctx == NULL) { + goto err_alloc_crypto; + } + + /* set the default frequency */ + state->frequency = DEFAULT_FREQ; + + /* set the have_read callback of the Sub-GHz worker */ + subghz_tx_rx_worker_set_callback_have_read(state->subghz_worker, have_read_cb, state); + + /* enter suppress charge mode */ + furi_hal_power_suppress_charge_enter(); + + /* init internal device */ + subghz_devices_init(); + + state->subghz_device = + radio_device_loader_set(state->subghz_device, SubGhzRadioDeviceTypeExternalCC1101); + + subghz_devices_reset(state->subghz_device); + subghz_devices_idle(state->subghz_device); + + /* set chat name prefix */ + furi_string_printf(state->name_prefix, "%s", furi_hal_version_get_name_ptr()); + + /* get notification record, we use this to make the flipper vibrate */ + /* no error handling here, don't know how */ + state->notification = furi_record_open(RECORD_NOTIFICATION); + + /* hook into the view port's draw and input callbacks */ + state->orig_draw_cb = state->view_dispatcher->view_port->draw_callback; + state->orig_input_cb = state->view_dispatcher->view_port->input_callback; + view_port_draw_callback_set( + state->view_dispatcher->view_port, esubghz_hooked_draw_callback, state); + view_port_input_callback_set( + state->view_dispatcher->view_port, esubghz_hooked_input_callback, state); + + view_dispatcher_enable_queue(state->view_dispatcher); + + /* set callbacks for view dispatcher */ + view_dispatcher_set_event_callback_context(state->view_dispatcher, state); + view_dispatcher_set_custom_event_callback( + state->view_dispatcher, esubghz_chat_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + state->view_dispatcher, esubghz_chat_navigation_event_callback); + view_dispatcher_set_tick_event_callback( + state->view_dispatcher, esubghz_chat_tick_event_callback, TICK_INTERVAL); + + /* add our two views to the view dispatcher */ + view_dispatcher_add_view( + state->view_dispatcher, ESubGhzChatView_Menu, menu_get_view(state->menu)); + view_dispatcher_add_view( + state->view_dispatcher, ESubGhzChatView_Input, text_input_get_view(state->text_input)); + view_dispatcher_add_view( + state->view_dispatcher, + ESubGhzChatView_HexKeyInput, + byte_input_get_view(state->hex_key_input)); + view_dispatcher_add_view( + state->view_dispatcher, ESubGhzChatView_ChatBox, text_box_get_view(state->chat_box)); + view_dispatcher_add_view( + state->view_dispatcher, + ESubGhzChatView_KeyDisplay, + dialog_ex_get_view(state->key_display)); + view_dispatcher_add_view( + state->view_dispatcher, ESubGhzChatView_NfcPopup, popup_get_view(state->nfc_popup)); + + /* get the GUI record and attach the view dispatcher to the GUI */ + /* no error handling here, don't know how */ + Gui* gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen); + + /* switch to the key menu scene */ + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyMenu); + + /* run the view dispatcher, this call only returns when we close the + * application */ + view_dispatcher_run(state->view_dispatcher); + + /* if it is running, stop the Sub-GHz worker */ + if(subghz_tx_rx_worker_is_running(state->subghz_worker)) { + exit_chat(state); + subghz_tx_rx_worker_stop(state->subghz_worker); + } + + /* if it is running, stop the NFC worker */ + nfc_worker_stop(state->nfc_worker); + + err = 0; + + /* close GUI record */ + furi_record_close(RECORD_GUI); + + /* remove our two views from the view dispatcher */ + view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Menu); + view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Input); + view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_HexKeyInput); + view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_ChatBox); + view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_KeyDisplay); + view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_NfcPopup); + + /* close notification record */ + furi_record_close(RECORD_NOTIFICATION); + + /* clear the key and potential password */ + crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store)); + crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store)); + crypto_explicit_bzero(state->key_hex_str, sizeof(state->key_hex_str)); + crypto_ctx_clear(state->crypto_ctx); + + /* clear nfc data */ + if(state->nfc_dev_data->parsed_data != NULL) { + furi_string_free(state->nfc_dev_data->parsed_data); + } + crypto_explicit_bzero(state->nfc_dev_data, sizeof(NfcDeviceData)); + + /* deinit devices */ + radio_device_loader_end(state->subghz_device); + + subghz_devices_deinit(); + + /* exit suppress charge mode */ + furi_hal_power_suppress_charge_exit(); + + /* free everything we allocated */ + + crypto_ctx_free(state->crypto_ctx); + +err_alloc_crypto: + free(state->nfc_dev_data); + +err_alloc_ndevdata: + nfc_worker_free(state->nfc_worker); + +err_alloc_nworker: + subghz_tx_rx_worker_free(state->subghz_worker); + +err_alloc_worker: + popup_free(state->nfc_popup); + +err_alloc_np: + dialog_ex_free(state->key_display); + +err_alloc_kd: + chat_box_free(state); + +err_alloc_cb: + byte_input_free(state->hex_key_input); + +err_alloc_hki: + text_input_free(state->text_input); + +err_alloc_ti: + menu_free(state->menu); + +err_alloc_menu: + helper_strings_free(state); + +err_alloc_hs: + view_dispatcher_free(state->view_dispatcher); + +err_alloc_vd: + scene_manager_free(state->scene_manager); + +err_alloc_sm: + free(state); + +err_alloc: + if(err != 0) { + FURI_LOG_E(APPLICATION_NAME, "Failed to launch (alloc error)!"); + } else { + FURI_LOG_I(APPLICATION_NAME, "Clean exit."); + } + + return err; +} diff --git a/applications/external/esubghz_chat/esubghz_chat_i.h b/applications/external/esubghz_chat/esubghz_chat_i.h new file mode 100644 index 000000000..4c6458cec --- /dev/null +++ b/applications/external/esubghz_chat/esubghz_chat_i.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crypto_wrapper.h" +#include "scenes/esubghz_chat_scene.h" + +#include "esubghz_chat_icons.h" +#include + +#define APPLICATION_NAME "ESubGhzChat" + +#define DEFAULT_FREQ 433920000 + +#define KEY_READ_POPUP_MS 3000 + +#define RX_TX_BUFFER_SIZE 1024 + +#define CHAT_BOX_STORE_SIZE 4096 +#define TEXT_INPUT_STORE_SIZE 256 +#define MSG_PREVIEW_SIZE 32 + +#define KEY_HEX_STR_SIZE ((KEY_BITS / 8) * 3) + +typedef struct { + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + NotificationApp* notification; + + // UI elements + Menu* menu; + TextBox* chat_box; + FuriString* chat_box_store; + TextInput* text_input; + char text_input_store[TEXT_INPUT_STORE_SIZE + 1]; + ByteInput* hex_key_input; + uint8_t hex_key_input_store[KEY_BITS / 8]; + DialogEx* key_display; + char key_hex_str[KEY_HEX_STR_SIZE + 1]; + Popup* nfc_popup; + + // for Sub-GHz + uint32_t frequency; + SubGhzTxRxWorker* subghz_worker; + const SubGhzDevice* subghz_device; + + // for NFC + NfcWorker* nfc_worker; + NfcDeviceData* nfc_dev_data; + + // message assembly before TX + FuriString* name_prefix; + FuriString* msg_input; + + // message preview + char msg_preview[MSG_PREVIEW_SIZE + 1]; + + // encryption + bool encrypted; + ESubGhzChatCryptoCtx* crypto_ctx; + + // RX and TX buffers + uint8_t rx_buffer[RX_TX_BUFFER_SIZE]; + uint8_t tx_buffer[RX_TX_BUFFER_SIZE]; + char rx_str_buffer[RX_TX_BUFFER_SIZE + 1]; + volatile uint32_t last_time_rx_data; + + // for locking + ViewPortDrawCallback orig_draw_cb; + ViewPortInputCallback orig_input_cb; + bool kbd_locked; + uint32_t kbd_lock_msg_ticks; + uint8_t kbd_lock_count; + + // for ongoing inputs + bool kbd_ok_input_ongoing; + bool kbd_left_input_ongoing; + bool kbd_right_input_ongoing; +} ESubGhzChatState; + +typedef enum { + ESubGhzChatEvent_FreqEntered, + ESubGhzChatEvent_KeyMenuNoEncryption, + ESubGhzChatEvent_KeyMenuPassword, + ESubGhzChatEvent_KeyMenuHexKey, + ESubGhzChatEvent_KeyMenuGenKey, + ESubGhzChatEvent_KeyMenuReadKeyFromNfc, + ESubGhzChatEvent_KeyReadPopupFailed, + ESubGhzChatEvent_KeyReadPopupSucceeded, + ESubGhzChatEvent_PassEntered, + ESubGhzChatEvent_HexKeyEntered, + ESubGhzChatEvent_MsgEntered, + ESubGhzChatEvent_GotoMsgInput, + ESubGhzChatEvent_GotoKeyDisplay, + ESubGhzChatEvent_KeyDisplayBack, + ESubGhzChatEvent_KeyDisplayShare, +} ESubGhzChatEvent; + +typedef enum { + ESubGhzChatView_Menu, + ESubGhzChatView_Input, + ESubGhzChatView_HexKeyInput, + ESubGhzChatView_ChatBox, + ESubGhzChatView_KeyDisplay, + ESubGhzChatView_NfcPopup, +} ESubGhzChatView; + +void set_chat_input_header(ESubGhzChatState* state); +void append_msg(ESubGhzChatState* state, const char* msg); +void tx_msg_input(ESubGhzChatState* state); +void enter_chat(ESubGhzChatState* state); diff --git a/applications/external/esubghz_chat/helpers/nfc_helpers.h b/applications/external/esubghz_chat/helpers/nfc_helpers.h new file mode 100644 index 000000000..5e507e335 --- /dev/null +++ b/applications/external/esubghz_chat/helpers/nfc_helpers.h @@ -0,0 +1,25 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFC_MAX_BYTES 256 +#define NFC_CONFIG_PAGES 4 + +struct FreqNfcEntry { + uint32_t frequency; + uint32_t unused1; + uint32_t unused2; + uint32_t unused3; +} __attribute__((packed)); + +struct ReplayDictNfcEntry { + uint64_t run_id; + uint32_t counter; + uint32_t unused; +} __attribute__((packed)); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/subghz_bruteforcer/helpers/radio_device_loader.c b/applications/external/esubghz_chat/helpers/radio_device_loader.c similarity index 89% rename from applications/external/subghz_bruteforcer/helpers/radio_device_loader.c rename to applications/external/esubghz_chat/helpers/radio_device_loader.c index d2cffde58..91f006a44 100644 --- a/applications/external/subghz_bruteforcer/helpers/radio_device_loader.c +++ b/applications/external/esubghz_chat/helpers/radio_device_loader.c @@ -58,7 +58,8 @@ const SubGhzDevice* radio_device_loader_set( void radio_device_loader_end(const SubGhzDevice* radio_device) { furi_assert(radio_device); radio_device_loader_power_off(); - if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) { - subghz_devices_end(radio_device); - } + // Code below is not used (and will cause crash) since its called from tx_rx worker end! + //if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) { + // subghz_devices_end(radio_device); + //} } \ No newline at end of file diff --git a/applications/external/subghz_bruteforcer/helpers/radio_device_loader.h b/applications/external/esubghz_chat/helpers/radio_device_loader.h similarity index 100% rename from applications/external/subghz_bruteforcer/helpers/radio_device_loader.h rename to applications/external/esubghz_chat/helpers/radio_device_loader.h diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_chat_box.c b/applications/external/esubghz_chat/scenes/esubghz_chat_chat_box.c new file mode 100644 index 000000000..be84ced48 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_chat_box.c @@ -0,0 +1,59 @@ +#include "../esubghz_chat_i.h" + +/* Prepares the text box scene. */ +void scene_on_enter_chat_box(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_box"); + + furi_assert(context); + ESubGhzChatState* state = context; + + text_box_reset(state->chat_box); + text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store)); + text_box_set_focus(state->chat_box, TextBoxFocusEnd); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_ChatBox); +} + +/* Handles scene manager events for the text box scene. */ +bool scene_on_event_chat_box(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_box"); + + furi_assert(context); + ESubGhzChatState* state = context; + + bool consumed = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + /* switch to message input scene */ + case ESubGhzChatEvent_GotoMsgInput: + if(!scene_manager_previous_scene(state->scene_manager)) { + view_dispatcher_stop(state->view_dispatcher); + } + consumed = true; + break; + case ESubGhzChatEvent_GotoKeyDisplay: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyDisplay); + consumed = true; + break; + } + break; + + default: + consumed = false; + break; + } + + return consumed; +} + +/* Cleans up the text box scene. */ +void scene_on_exit_chat_box(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_box"); + + furi_assert(context); + ESubGhzChatState* state = context; + + text_box_reset(state->chat_box); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_chat_input.c b/applications/external/esubghz_chat/scenes/esubghz_chat_chat_input.c new file mode 100644 index 000000000..721fea676 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_chat_input.c @@ -0,0 +1,107 @@ +#include "../esubghz_chat_i.h" + +/* If no message was entred this simply emits a MsgEntered event to the scene + * manager to switch to the text box. If a message was entered it is appended + * to the name string. The result is encrypted, if encryption is enabled, and + * then copied into the TX buffer. The contents of the TX buffer are then + * transmitted. The sent message is appended to the text box and a MsgEntered + * event is sent to the scene manager to switch to the text box view. */ +static bool chat_input_validator(const char* text, FuriString* error, void* context) { + UNUSED(error); + + furi_assert(context); + ESubGhzChatState* state = context; + + /* no message, just switch to the text box view */ + if(strlen(text) == 0) { + view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_MsgEntered); + return true; + } + + /* concatenate the name prefix and the actual message */ + furi_string_set(state->msg_input, state->name_prefix); + furi_string_cat_str(state->msg_input, ": "); + furi_string_cat_str(state->msg_input, text); + + /* append the message to the chat box and prepare message preview */ + append_msg(state, furi_string_get_cstr(state->msg_input)); + + /* encrypt and transmit message */ + tx_msg_input(state); + + /* clear message input buffer */ + furi_string_set_char(state->msg_input, 0, 0); + + /* switch to text box view */ + view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_MsgEntered); + + return true; +} + +/* Prepares the message input scene. */ +void scene_on_enter_chat_input(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + state->text_input_store[0] = 0; + text_input_reset(state->text_input); + /* use validator for scene change to get around minimum length + * requirement */ + text_input_set_result_callback( + state->text_input, + NULL, + NULL, + state->text_input_store, + sizeof(state->text_input_store), + true); + text_input_set_validator(state->text_input, chat_input_validator, state); + set_chat_input_header(state); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input); +} + +/* Handles scene manager events for the message input scene. */ +bool scene_on_event_chat_input(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + bool consumed = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + /* switch to text box scene */ + case ESubGhzChatEvent_MsgEntered: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatBox); + consumed = true; + break; + } + break; + + case SceneManagerEventTypeBack: + /* stop the application if the user presses back here */ + view_dispatcher_stop(state->view_dispatcher); + consumed = true; + break; + + default: + consumed = false; + break; + } + + return consumed; +} + +/* Cleans up the password input scene. */ +void scene_on_exit_chat_input(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + text_input_reset(state->text_input); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_freq_input.c b/applications/external/esubghz_chat/scenes/esubghz_chat_freq_input.c new file mode 100644 index 000000000..3c74caec3 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_freq_input.c @@ -0,0 +1,108 @@ +#include "../esubghz_chat_i.h" + +/* Sends FreqEntered event to scene manager and enters the chat. */ +static void freq_input_cb(void* context) { + furi_assert(context); + ESubGhzChatState* state = context; + + enter_chat(state); + + view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_FreqEntered); +} + +/* Validates the entered frequency. */ +static bool freq_input_validator(const char* text, FuriString* error, void* context) { + furi_assert(text); + furi_assert(error); + + furi_assert(context); + ESubGhzChatState* state = context; + + int ret = sscanf(text, "%lu", &(state->frequency)); + if(ret != 1) { + furi_string_printf(error, "Please enter\nfrequency\nin Hz!"); + return false; + } + + if(!subghz_devices_is_frequency_valid(state->subghz_device, state->frequency)) { + furi_string_printf(error, "Frequency\n%lu\n is invalid!", state->frequency); + return false; + } + +#ifdef FW_ORIGIN_Official + if(!furi_hal_region_is_frequency_allowed(state->frequency)) { +#else /* FW_ORIGIN_Official */ + if(!furi_hal_subghz_is_tx_allowed(state->frequency)) { +#endif /* FW_ORIGIN_Official */ + furi_string_printf(error, "TX forbidden\non frequency\n%lu!", state->frequency); + return false; + } + + return true; +} + +/* Prepares the frequency input scene. */ +void scene_on_enter_freq_input(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_freq_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + snprintf(state->text_input_store, TEXT_INPUT_STORE_SIZE, "%lu", state->frequency); + text_input_reset(state->text_input); + text_input_set_result_callback( + state->text_input, + freq_input_cb, + state, + state->text_input_store, + sizeof(state->text_input_store), + true); + text_input_set_validator(state->text_input, freq_input_validator, state); + text_input_set_header_text(state->text_input, "Frequency"); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input); +} + +/* Handles scene manager events for the frequency input scene. */ +bool scene_on_event_freq_input(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_freq_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + bool consumed = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + /* switch to message input scene */ + case ESubGhzChatEvent_FreqEntered: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput); + consumed = true; + break; + } + break; + + case SceneManagerEventTypeBack: + /* stop the application if the user presses back here */ + view_dispatcher_stop(state->view_dispatcher); + consumed = true; + break; + + default: + consumed = false; + break; + } + + return consumed; +} + +/* Cleans up the frequency input scene. */ +void scene_on_exit_freq_input(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_freq_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + text_input_reset(state->text_input); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_hex_key_input.c b/applications/external/esubghz_chat/scenes/esubghz_chat_hex_key_input.c new file mode 100644 index 000000000..00d9e70f3 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_hex_key_input.c @@ -0,0 +1,80 @@ +#include "../esubghz_chat_i.h" + +/* Sets the entered bytes as the key and sends a HexKeyEntered event to the + * scene manager. */ +static void hex_key_input_cb(void* context) { + furi_assert(context); + ESubGhzChatState* state = context; + + /* initiate the crypto context */ + bool ret = crypto_ctx_set_key( + state->crypto_ctx, state->hex_key_input_store, state->name_prefix, furi_get_tick()); + + /* cleanup */ + crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store)); + + if(!ret) { + crypto_ctx_clear(state->crypto_ctx); + return; + } + + state->encrypted = true; + + view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_HexKeyEntered); +} + +/* Prepares the hex key input scene. */ +void scene_on_enter_hex_key_input(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_hex_key_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + byte_input_set_result_callback( + state->hex_key_input, + hex_key_input_cb, + NULL, + state, + state->hex_key_input_store, + sizeof(state->hex_key_input_store)); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_HexKeyInput); +} + +/* Handles scene manager events for the hex key input scene. */ +bool scene_on_event_hex_key_input(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_hex_key_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + bool consumed = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + /* switch to frequency input scene */ + case ESubGhzChatEvent_HexKeyEntered: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput); + consumed = true; + break; + } + break; + + default: + consumed = false; + break; + } + + return consumed; +} + +/* Cleans up the hex key input scene. */ +void scene_on_exit_hex_key_input(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_hex_key_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + crypto_explicit_bzero(state->hex_key_input_store, sizeof(state->hex_key_input_store)); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_key_display.c b/applications/external/esubghz_chat/scenes/esubghz_chat_key_display.c new file mode 100644 index 000000000..b682d7d87 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_key_display.c @@ -0,0 +1,145 @@ +#include "../esubghz_chat_i.h" + +void key_display_result_cb(DialogExResult result, void* context) { + furi_assert(context); + ESubGhzChatState* state = context; + + switch(result) { + case DialogExResultLeft: + view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyDisplayBack); + break; + + case DialogExResultCenter: + if(state->encrypted) { + view_dispatcher_send_custom_event( + state->view_dispatcher, ESubGhzChatEvent_KeyDisplayShare); + } + break; + + default: + break; + } +} + +/* Prepares the key display scene. */ +void scene_on_enter_key_display(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_display"); + + furi_assert(context); + ESubGhzChatState* state = context; + + if(state->encrypted) { + uint8_t key[KEY_BITS / 8]; + crypto_ctx_get_key(state->crypto_ctx, key); + snprintf( + state->key_hex_str, + KEY_HEX_STR_SIZE, + "%02hX%02hX%02hX%02hX" + "%02hX%02hX%02hX%02hX\n" + "%02hX%02hX%02hX%02hX" + "%02hX%02hX%02hX%02hX\n" + "%02hX%02hX%02hX%02hX" + "%02hX%02hX%02hX%02hX\n" + "%02hX%02hX%02hX%02hX" + "%02hX%02hX%02hX%02hX", + key[0], + key[1], + key[2], + key[3], + key[4], + key[5], + key[6], + key[7], + key[8], + key[9], + key[10], + key[11], + key[12], + key[13], + key[14], + key[15], + key[16], + key[17], + key[18], + key[19], + key[20], + key[21], + key[22], + key[23], + key[24], + key[25], + key[26], + key[27], + key[28], + key[29], + key[30], + key[31]); + crypto_explicit_bzero(key, sizeof(key)); + } else { + strcpy(state->key_hex_str, "No Key"); + } + + dialog_ex_reset(state->key_display); + + dialog_ex_set_text(state->key_display, state->key_hex_str, 64, 2, AlignCenter, AlignTop); + + dialog_ex_set_icon(state->key_display, 0, 0, NULL); + + dialog_ex_set_left_button_text(state->key_display, "Back"); + + if(state->encrypted) { + dialog_ex_set_center_button_text(state->key_display, "Share"); + } + + dialog_ex_set_result_callback(state->key_display, key_display_result_cb); + dialog_ex_set_context(state->key_display, state); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_KeyDisplay); +} + +/* Handles scene manager events for the key display scene. */ +bool scene_on_event_key_display(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_display"); + + furi_assert(context); + ESubGhzChatState* state = context; + + bool consumed = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + /* switch to message input scene */ + case ESubGhzChatEvent_KeyDisplayBack: + if(!scene_manager_previous_scene(state->scene_manager)) { + view_dispatcher_stop(state->view_dispatcher); + } + consumed = true; + break; + + /* open key sharing popup */ + case ESubGhzChatEvent_KeyDisplayShare: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeySharePopup); + consumed = true; + break; + } + break; + + default: + consumed = false; + break; + } + + return consumed; +} + +/* Cleans up the key display scene. */ +void scene_on_exit_key_display(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_display"); + + furi_assert(context); + ESubGhzChatState* state = context; + + dialog_ex_reset(state->key_display); + crypto_explicit_bzero(state->key_hex_str, sizeof(state->key_hex_str)); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_key_menu.c b/applications/external/esubghz_chat/scenes/esubghz_chat_key_menu.c new file mode 100644 index 000000000..cd1269ee1 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_key_menu.c @@ -0,0 +1,174 @@ +#include "../esubghz_chat_i.h" + +typedef enum { + ESubGhzChatKeyMenuItems_NoEncryption, + ESubGhzChatKeyMenuItems_Password, + ESubGhzChatKeyMenuItems_HexKey, + ESubGhzChatKeyMenuItems_GenKey, + ESubGhzChatKeyMenuItems_ReadKeyFromNfc, +} ESubGhzChatKeyMenuItems; + +static void key_menu_cb(void* context, uint32_t index) { + furi_assert(context); + ESubGhzChatState* state = context; + + uint8_t key[KEY_BITS / 8]; + + switch(index) { + case ESubGhzChatKeyMenuItems_NoEncryption: + state->encrypted = false; + + view_dispatcher_send_custom_event( + state->view_dispatcher, ESubGhzChatEvent_KeyMenuNoEncryption); + break; + + case ESubGhzChatKeyMenuItems_Password: + view_dispatcher_send_custom_event( + state->view_dispatcher, ESubGhzChatEvent_KeyMenuPassword); + break; + + case ESubGhzChatKeyMenuItems_HexKey: + view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyMenuHexKey); + break; + + case ESubGhzChatKeyMenuItems_GenKey: + /* generate a random key */ + furi_hal_random_fill_buf(key, KEY_BITS / 8); + + /* initiate the crypto context */ + bool ret = crypto_ctx_set_key(state->crypto_ctx, key, state->name_prefix, furi_get_tick()); + + /* cleanup */ + crypto_explicit_bzero(key, sizeof(key)); + + if(!ret) { + crypto_ctx_clear(state->crypto_ctx); + return; + } + + /* set encrypted flag and enter the chat */ + state->encrypted = true; + + view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_KeyMenuGenKey); + break; + + case ESubGhzChatKeyMenuItems_ReadKeyFromNfc: + view_dispatcher_send_custom_event( + state->view_dispatcher, ESubGhzChatEvent_KeyMenuReadKeyFromNfc); + break; + + default: + break; + } +} + +/* Prepares the key menu scene. */ +void scene_on_enter_key_menu(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_menu"); + + furi_assert(context); + ESubGhzChatState* state = context; + + menu_reset(state->menu); + + /* clear the crypto CTX in case we got back from password or hex key + * input */ + crypto_ctx_clear(state->crypto_ctx); + + menu_add_item( + state->menu, + "No encryption", + &I_chat_14px, + ESubGhzChatKeyMenuItems_NoEncryption, + key_menu_cb, + state); + menu_add_item( + state->menu, + "Password", + &I_keyboard_14px, + ESubGhzChatKeyMenuItems_Password, + key_menu_cb, + state); + menu_add_item( + state->menu, "Hex Key", &I_hex_14px, ESubGhzChatKeyMenuItems_HexKey, key_menu_cb, state); + menu_add_item( + state->menu, + "Generate Key", + &I_u2f_14px, + ESubGhzChatKeyMenuItems_GenKey, + key_menu_cb, + state); + menu_add_item( + state->menu, + "Read Key from NFC", + &I_Nfc_14px, + ESubGhzChatKeyMenuItems_ReadKeyFromNfc, + key_menu_cb, + state); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Menu); +} + +/* Handles scene manager events for the key menu scene. */ +bool scene_on_event_key_menu(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_menu"); + + furi_assert(context); + ESubGhzChatState* state = context; + + bool consumed = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + /* switch to frequency input scene */ + case ESubGhzChatEvent_KeyMenuNoEncryption: + case ESubGhzChatEvent_KeyMenuGenKey: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput); + consumed = true; + break; + + /* switch to password input scene */ + case ESubGhzChatEvent_KeyMenuPassword: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_PassInput); + consumed = true; + break; + + /* switch to hex key input scene */ + case ESubGhzChatEvent_KeyMenuHexKey: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_HexKeyInput); + consumed = true; + break; + + /* switch to hex key read scene */ + case ESubGhzChatEvent_KeyMenuReadKeyFromNfc: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_KeyReadPopup); + consumed = true; + break; + } + + break; + + case SceneManagerEventTypeBack: + /* stop the application if the user presses back here */ + view_dispatcher_stop(state->view_dispatcher); + consumed = true; + break; + + default: + consumed = false; + break; + } + + return consumed; +} + +/* Cleans up the key menu scene. */ +void scene_on_exit_key_menu(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_menu"); + + furi_assert(context); + ESubGhzChatState* state = context; + + menu_reset(state->menu); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_key_read_popup.c b/applications/external/esubghz_chat/scenes/esubghz_chat_key_read_popup.c new file mode 100644 index 000000000..db1f75491 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_key_read_popup.c @@ -0,0 +1,273 @@ +#include "../esubghz_chat_i.h" +#include "../helpers/nfc_helpers.h" + +typedef enum { + KeyReadPopupState_Idle, + KeyReadPopupState_Detecting, + KeyReadPopupState_Reading, + KeyReadPopupState_Fail, + KeyReadPopupState_Success, +} KeyReadPopupState; + +static bool read_worker_cb(NfcWorkerEvent event, void* context) { + furi_assert(context); + ESubGhzChatState* state = context; + + view_dispatcher_send_custom_event(state->view_dispatcher, event); + + return true; +} + +static void key_read_popup_timeout_cb(void* context) { + furi_assert(context); + ESubGhzChatState* state = context; + + uint32_t cur_state = + scene_manager_get_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup); + + /* done displaying our failure */ + if(cur_state == KeyReadPopupState_Fail) { + view_dispatcher_send_custom_event( + state->view_dispatcher, ESubGhzChatEvent_KeyReadPopupFailed); + /* done displaying our success */ + } else if(cur_state == KeyReadPopupState_Success) { + view_dispatcher_send_custom_event( + state->view_dispatcher, ESubGhzChatEvent_KeyReadPopupSucceeded); + } +} + +struct ReplayDictNfcReaderContext { + uint8_t* cur; + uint8_t* max; +}; + +static bool replay_dict_nfc_reader(uint64_t* run_id, uint32_t* counter, void* context) { + struct ReplayDictNfcReaderContext* ctx = (struct ReplayDictNfcReaderContext*)context; + + if(ctx->cur + sizeof(struct ReplayDictNfcEntry) > ctx->max) { + return false; + } + + struct ReplayDictNfcEntry* entry = (struct ReplayDictNfcEntry*)ctx->cur; + *run_id = entry->run_id; + *counter = __ntohl(entry->counter); + + ctx->cur += sizeof(struct ReplayDictNfcEntry); + + return true; +} + +static bool key_read_popup_handle_key_read(ESubGhzChatState* state) { + NfcDeviceData* dev_data = state->nfc_dev_data; + + /* check for config pages */ + if(dev_data->mf_ul_data.data_read < NFC_CONFIG_PAGES * 4) { + return false; + } + + size_t data_read = dev_data->mf_ul_data.data_read - (NFC_CONFIG_PAGES * 4); + + /* check if key was transmitted */ + if(data_read < KEY_BITS / 8) { + return false; + } + + /* initiate the crypto context */ + bool ret = crypto_ctx_set_key( + state->crypto_ctx, dev_data->mf_ul_data.data, state->name_prefix, furi_get_tick()); + + /* cleanup */ + crypto_explicit_bzero(dev_data->mf_ul_data.data, KEY_BITS / 8); + + if(!ret) { + crypto_ctx_clear(state->crypto_ctx); + return false; + } + + /* read the frequency */ + if(data_read >= (KEY_BITS / 8) + sizeof(struct FreqNfcEntry)) { + struct FreqNfcEntry* freq_entry = + (struct FreqNfcEntry*)(dev_data->mf_ul_data.data + (KEY_BITS / 8)); + state->frequency = __ntohl(freq_entry->frequency); + } + + /* read the replay dict */ + struct ReplayDictNfcReaderContext rd_ctx = { + .cur = dev_data->mf_ul_data.data + (KEY_BITS / 8) + sizeof(struct FreqNfcEntry), + .max = + dev_data->mf_ul_data.data + (data_read < NFC_MAX_BYTES ? data_read : NFC_MAX_BYTES)}; + + crypto_ctx_read_replay_dict(state->crypto_ctx, replay_dict_nfc_reader, &rd_ctx); + + /* set encrypted flag */ + state->encrypted = true; + + return true; +} + +static void key_read_popup_set_state(ESubGhzChatState* state, KeyReadPopupState new_state) { + uint32_t cur_state = + scene_manager_get_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup); + if(cur_state == new_state) { + return; + } + + if(new_state == KeyReadPopupState_Detecting) { + popup_reset(state->nfc_popup); + popup_disable_timeout(state->nfc_popup); + popup_set_text(state->nfc_popup, "Tap Flipper\n to sender", 97, 24, AlignCenter, AlignTop); + popup_set_icon(state->nfc_popup, 0, 8, &I_NFC_manual_60x50); + notification_message(state->notification, &sequence_blink_start_cyan); + } else if(new_state == KeyReadPopupState_Reading) { + popup_reset(state->nfc_popup); + popup_disable_timeout(state->nfc_popup); + popup_set_header( + state->nfc_popup, + "Reading key\nDon't " + "move...", + 85, + 24, + AlignCenter, + AlignTop); + popup_set_icon(state->nfc_popup, 12, 23, &I_Loading_24); + notification_message(state->notification, &sequence_blink_start_yellow); + } else if(new_state == KeyReadPopupState_Fail) { + nfc_worker_stop(state->nfc_worker); + + popup_reset(state->nfc_popup); + popup_set_header(state->nfc_popup, "Failure!", 64, 2, AlignCenter, AlignTop); + popup_set_text(state->nfc_popup, "Failed\nto read\nkey.", 78, 16, AlignLeft, AlignTop); + popup_set_icon(state->nfc_popup, 21, 13, &I_Cry_dolph_55x52); + + popup_set_timeout(state->nfc_popup, KEY_READ_POPUP_MS); + popup_set_context(state->nfc_popup, state); + popup_set_callback(state->nfc_popup, key_read_popup_timeout_cb); + popup_enable_timeout(state->nfc_popup); + + notification_message(state->notification, &sequence_blink_stop); + } else if(new_state == KeyReadPopupState_Success) { + nfc_worker_stop(state->nfc_worker); + + popup_reset(state->nfc_popup); + popup_set_header(state->nfc_popup, "Key\nread!", 13, 22, AlignLeft, AlignBottom); + popup_set_icon(state->nfc_popup, 32, 5, &I_DolphinNice_96x59); + + popup_set_timeout(state->nfc_popup, KEY_READ_POPUP_MS); + popup_set_context(state->nfc_popup, state); + popup_set_callback(state->nfc_popup, key_read_popup_timeout_cb); + popup_enable_timeout(state->nfc_popup); + + notification_message(state->notification, &sequence_success); + notification_message(state->notification, &sequence_blink_stop); + } + + scene_manager_set_scene_state(state->scene_manager, ESubGhzChatScene_KeyReadPopup, new_state); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_NfcPopup); +} + +/* Prepares the key share read scene. */ +void scene_on_enter_key_read_popup(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_read_popup"); + + furi_assert(context); + ESubGhzChatState* state = context; + + key_read_popup_set_state(state, KeyReadPopupState_Detecting); + + state->nfc_dev_data->parsed_data = furi_string_alloc(); + if(state->nfc_dev_data->parsed_data == NULL) { + /* can't do anything here, crash */ + furi_check(0); + } + + nfc_worker_start( + state->nfc_worker, NfcWorkerStateRead, state->nfc_dev_data, read_worker_cb, state); +} + +/* Handles scene manager events for the key read popup scene. */ +bool scene_on_event_key_read_popup(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_read_popup"); + + furi_assert(context); + ESubGhzChatState* state = context; + + bool consumed = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + /* card detected */ + case NfcWorkerEventCardDetected: + key_read_popup_set_state(state, KeyReadPopupState_Reading); + consumed = true; + break; + + /* no card detected */ + case NfcWorkerEventNoCardDetected: + key_read_popup_set_state(state, KeyReadPopupState_Detecting); + consumed = true; + break; + + /* key probably read */ + case NfcWorkerEventReadMfUltralight: + if(key_read_popup_handle_key_read(state)) { + key_read_popup_set_state(state, KeyReadPopupState_Success); + } else { + key_read_popup_set_state(state, KeyReadPopupState_Fail); + } + consumed = true; + break; + + /* close the popup and go back */ + case ESubGhzChatEvent_KeyReadPopupFailed: + if(!scene_manager_previous_scene(state->scene_manager)) { + view_dispatcher_stop(state->view_dispatcher); + } + consumed = true; + break; + + /* success, go to frequency input */ + case ESubGhzChatEvent_KeyReadPopupSucceeded: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput); + consumed = true; + break; + + /* something else happend, treat as failure */ + default: + key_read_popup_set_state(state, KeyReadPopupState_Fail); + consumed = true; + break; + } + + break; + + default: + consumed = false; + break; + } + + return consumed; +} + +/* Cleans up the key read popup scene. */ +void scene_on_exit_key_read_popup(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_read_popup"); + + furi_assert(context); + ESubGhzChatState* state = context; + + popup_reset(state->nfc_popup); + scene_manager_set_scene_state( + state->scene_manager, ESubGhzChatScene_KeyReadPopup, KeyReadPopupState_Idle); + + notification_message(state->notification, &sequence_blink_stop); + + nfc_worker_stop(state->nfc_worker); + + crypto_explicit_bzero(state->nfc_dev_data->mf_ul_data.data, KEY_BITS / 8); + if(state->nfc_dev_data->parsed_data != NULL) { + furi_string_free(state->nfc_dev_data->parsed_data); + } + memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData)); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_key_share_popup.c b/applications/external/esubghz_chat/scenes/esubghz_chat_key_share_popup.c new file mode 100644 index 000000000..07d85cb33 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_key_share_popup.c @@ -0,0 +1,124 @@ +#include "../esubghz_chat_i.h" +#include "../helpers/nfc_helpers.h" + +struct ReplayDictNfcWriterContext { + uint8_t* cur; + uint8_t* max; +}; + +static bool replay_dict_nfc_writer(uint64_t run_id, uint32_t counter, void* context) { + struct ReplayDictNfcWriterContext* ctx = (struct ReplayDictNfcWriterContext*)context; + + struct ReplayDictNfcEntry entry = {.run_id = run_id, .counter = __htonl(counter), .unused = 0}; + + if(ctx->cur + sizeof(entry) > ctx->max) { + return false; + } + + memcpy(ctx->cur, &entry, sizeof(entry)); + ctx->cur += sizeof(entry); + + return true; +} + +static void prepare_nfc_dev_data(ESubGhzChatState* state) { + NfcDeviceData* dev_data = state->nfc_dev_data; + + dev_data->protocol = NfcDeviceProtocolMifareUl; + furi_hal_random_fill_buf(dev_data->nfc_data.uid, 7); + dev_data->nfc_data.uid_len = 7; + dev_data->nfc_data.atqa[0] = 0x44; + dev_data->nfc_data.atqa[1] = 0x00; + dev_data->nfc_data.sak = 0x00; + + dev_data->mf_ul_data.type = MfUltralightTypeNTAG215; + dev_data->mf_ul_data.version.header = 0x00; + dev_data->mf_ul_data.version.vendor_id = 0x04; + dev_data->mf_ul_data.version.prod_type = 0x04; + dev_data->mf_ul_data.version.prod_subtype = 0x02; + dev_data->mf_ul_data.version.prod_ver_major = 0x01; + dev_data->mf_ul_data.version.prod_ver_minor = 0x00; + dev_data->mf_ul_data.version.storage_size = 0x11; + dev_data->mf_ul_data.version.protocol_type = 0x03; + + size_t data_written = 0; + + /* write key */ + crypto_ctx_get_key(state->crypto_ctx, dev_data->mf_ul_data.data); + data_written += (KEY_BITS / 8); + + /* write frequency */ + struct FreqNfcEntry* freq_entry = + (struct FreqNfcEntry*)(dev_data->mf_ul_data.data + data_written); + freq_entry->frequency = __htonl(state->frequency); + freq_entry->unused1 = 0; + freq_entry->unused2 = 0; + freq_entry->unused3 = 0; + data_written += sizeof(struct FreqNfcEntry); + + /* write the replay dict */ + struct ReplayDictNfcWriterContext wr_ctx = { + .cur = dev_data->mf_ul_data.data + data_written, + .max = dev_data->mf_ul_data.data + NFC_MAX_BYTES}; + + size_t n_entries = + crypto_ctx_dump_replay_dict(state->crypto_ctx, replay_dict_nfc_writer, &wr_ctx); + data_written += n_entries * sizeof(struct ReplayDictNfcEntry); + + /* calculate size of data, add 16 for config pages */ + dev_data->mf_ul_data.data_size = data_written + (NFC_CONFIG_PAGES * 4); +} + +/* Prepares the key share popup scene. */ +void scene_on_enter_key_share_popup(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_key_share_popup"); + + furi_assert(context); + ESubGhzChatState* state = context; + + popup_reset(state->nfc_popup); + + popup_disable_timeout(state->nfc_popup); + + popup_set_header(state->nfc_popup, "Sharing...", 67, 13, AlignLeft, AlignTop); + popup_set_icon(state->nfc_popup, 0, 3, &I_NFC_dolphin_emulation_47x61); + popup_set_text(state->nfc_popup, "Sharing\nKey via\nNFC", 90, 28, AlignCenter, AlignTop); + + prepare_nfc_dev_data(state); + nfc_worker_start( + state->nfc_worker, NfcWorkerStateMfUltralightEmulate, state->nfc_dev_data, NULL, NULL); + + notification_message(state->notification, &sequence_blink_start_magenta); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_NfcPopup); +} + +/* Handles scene manager events for the key share popup scene. */ +bool scene_on_event_key_share_popup(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_key_share_popup"); + + furi_assert(context); + ESubGhzChatState* state = context; + + UNUSED(state); + UNUSED(event); + + return false; +} + +/* Cleans up the key share popup scene. */ +void scene_on_exit_key_share_popup(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_key_share_popup"); + + furi_assert(context); + ESubGhzChatState* state = context; + + popup_reset(state->nfc_popup); + + notification_message(state->notification, &sequence_blink_stop); + + nfc_worker_stop(state->nfc_worker); + + crypto_explicit_bzero(state->nfc_dev_data->mf_ul_data.data, KEY_BITS / 8); + memset(state->nfc_dev_data, 0, sizeof(NfcDeviceData)); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_pass_input.c b/applications/external/esubghz_chat/scenes/esubghz_chat_pass_input.c new file mode 100644 index 000000000..f1cf4bfa7 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_pass_input.c @@ -0,0 +1,109 @@ +#include "../esubghz_chat_i.h" + +/* Sends PassEntered event to scene manager. */ +static void pass_input_cb(void* context) { + furi_assert(context); + ESubGhzChatState* state = context; + + crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store)); + + view_dispatcher_send_custom_event(state->view_dispatcher, ESubGhzChatEvent_PassEntered); +} + +/* If a password was entered this derives a key from the password using a + * single pass of SHA256 and initiates the AES-GCM context for encryption. If + * the initiation fails, the password is rejected. */ +static bool pass_input_validator(const char* text, FuriString* error, void* context) { + furi_assert(text); + furi_assert(error); + + furi_assert(context); + ESubGhzChatState* state = context; + + if(strlen(text) == 0) { + furi_string_printf(error, "Enter a\npassword!"); + return false; + } + + unsigned char key[KEY_BITS / 8]; + + /* derive a key from the password */ + sha256((unsigned char*)text, strlen(text), key); + + /* initiate the crypto context */ + bool ret = crypto_ctx_set_key(state->crypto_ctx, key, state->name_prefix, furi_get_tick()); + + /* cleanup */ + crypto_explicit_bzero(key, sizeof(key)); + + if(!ret) { + crypto_ctx_clear(state->crypto_ctx); + furi_string_printf(error, "Failed to\nset key!"); + return false; + } + + state->encrypted = true; + + return true; +} + +/* Prepares the password input scene. */ +void scene_on_enter_pass_input(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_pass_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + state->text_input_store[0] = 0; + text_input_reset(state->text_input); + text_input_set_result_callback( + state->text_input, + pass_input_cb, + state, + state->text_input_store, + sizeof(state->text_input_store), + true); + text_input_set_validator(state->text_input, pass_input_validator, state); + text_input_set_header_text(state->text_input, "Password"); + + view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input); +} + +/* Handles scene manager events for the password input scene. */ +bool scene_on_event_pass_input(void* context, SceneManagerEvent event) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_event_pass_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + bool consumed = false; + + switch(event.type) { + case SceneManagerEventTypeCustom: + switch(event.event) { + /* switch to frequency input scene */ + case ESubGhzChatEvent_PassEntered: + scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput); + consumed = true; + break; + } + break; + + default: + consumed = false; + break; + } + + return consumed; +} + +/* Cleans up the password input scene. */ +void scene_on_exit_pass_input(void* context) { + FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_pass_input"); + + furi_assert(context); + ESubGhzChatState* state = context; + + text_input_reset(state->text_input); + crypto_explicit_bzero(state->text_input_store, sizeof(state->text_input_store)); +} diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_scene.c b/applications/external/esubghz_chat/scenes/esubghz_chat_scene.c new file mode 100644 index 000000000..5efb8ea10 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_scene.c @@ -0,0 +1,30 @@ +#include "esubghz_chat_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) scene_on_enter_##name, +void (*const esubghz_chat_scene_on_enter_handlers[])(void*) = { +#include "esubghz_chat_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) scene_on_event_##name, +bool (*const esubghz_chat_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "esubghz_chat_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) scene_on_exit_##name, +void (*const esubghz_chat_scene_on_exit_handlers[])(void* context) = { +#include "esubghz_chat_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers esubghz_chat_scene_event_handlers = { + .on_enter_handlers = esubghz_chat_scene_on_enter_handlers, + .on_event_handlers = esubghz_chat_scene_on_event_handlers, + .on_exit_handlers = esubghz_chat_scene_on_exit_handlers, + .scene_num = ESubGhzChatScene_MAX, +}; diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_scene.h b/applications/external/esubghz_chat/scenes/esubghz_chat_scene.h new file mode 100644 index 000000000..3f57d2cc7 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) ESubGhzChatScene_##id, +typedef enum { +#include "esubghz_chat_scene_config.h" + ESubGhzChatScene_MAX +} ESubGhzChatScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers esubghz_chat_scene_event_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void scene_on_enter_##name(void*); +#include "esubghz_chat_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool scene_on_event_##name(void* context, SceneManagerEvent event); +#include "esubghz_chat_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void scene_on_exit_##name(void* context); +#include "esubghz_chat_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/esubghz_chat/scenes/esubghz_chat_scene_config.h b/applications/external/esubghz_chat/scenes/esubghz_chat_scene_config.h new file mode 100644 index 000000000..85981c898 --- /dev/null +++ b/applications/external/esubghz_chat/scenes/esubghz_chat_scene_config.h @@ -0,0 +1,9 @@ +ADD_SCENE(esubghz_chat, freq_input, FreqInput) +ADD_SCENE(esubghz_chat, key_menu, KeyMenu) +ADD_SCENE(esubghz_chat, pass_input, PassInput) +ADD_SCENE(esubghz_chat, hex_key_input, HexKeyInput) +ADD_SCENE(esubghz_chat, key_read_popup, KeyReadPopup) +ADD_SCENE(esubghz_chat, chat_input, ChatInput) +ADD_SCENE(esubghz_chat, chat_box, ChatBox) +ADD_SCENE(esubghz_chat, key_display, KeyDisplay) +ADD_SCENE(esubghz_chat, key_share_popup, KeySharePopup) diff --git a/applications/external/etch_a_sketch/application.fam b/applications/external/etch_a_sketch/application.fam index db1add1ab..2f6cd9136 100644 --- a/applications/external/etch_a_sketch/application.fam +++ b/applications/external/etch_a_sketch/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_ETCH_A_SKETCH"], requires=["gui"], stack_size=2 * 1024, - order=175, fap_icon="etch-a-sketch-icon.png", fap_category="Media", fap_icon_assets="assets", diff --git a/applications/external/evil_portal/LICENSE.txt b/applications/external/evil_portal/LICENSE.txt new file mode 100644 index 000000000..2ae63a7f6 --- /dev/null +++ b/applications/external/evil_portal/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 bigbrodude6119 + +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/evil_portal/application.fam b/applications/external/evil_portal/application.fam index 58c7d426c..6af0546c4 100644 --- a/applications/external/evil_portal/application.fam +++ b/applications/external/evil_portal/application.fam @@ -6,7 +6,9 @@ App( cdefines=["APP_EVIL_PORTAL"], requires=["gui"], stack_size=1 * 1024, - order=90, + fap_author="bigbrodude6119", + fap_description="Create an evil captive portal Wi-Fi access point", + fap_icon_assets="icons", fap_icon="icons/evil_portal_10px.png", fap_category="WiFi", ) diff --git a/applications/external/evil_portal/evil_portal_app.c b/applications/external/evil_portal/evil_portal_app.c index 4f985e72c..8e091da40 100644 --- a/applications/external/evil_portal/evil_portal_app.c +++ b/applications/external/evil_portal/evil_portal_app.c @@ -32,10 +32,15 @@ Evil_PortalApp* evil_portal_app_alloc() { app->command_index = 0; app->portal_logs = furi_string_alloc(); - app->gui = furi_record_open(RECORD_GUI); app->dialogs = furi_record_open(RECORD_DIALOGS); + app->file_path = furi_string_alloc(); + + app->gui = furi_record_open(RECORD_GUI); app->view_dispatcher = view_dispatcher_alloc(); + + app->loading = loading_alloc(); + app->scene_manager = scene_manager_alloc(&evil_portal_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); view_dispatcher_set_event_callback_context(app->view_dispatcher, app); @@ -49,12 +54,18 @@ Evil_PortalApp* evil_portal_app_alloc() { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + app->view_stack = view_stack_alloc(); + app->var_item_list = variable_item_list_alloc(); view_dispatcher_add_view( app->view_dispatcher, Evil_PortalAppViewVarItemList, variable_item_list_get_view(app->var_item_list)); + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, Evil_PortalAppViewTextInput, text_input_get_view(app->text_input)); + for(int i = 0; i < NUM_MENU_ITEMS; ++i) { app->selected_option_index[i] = 0; } @@ -89,6 +100,10 @@ void evil_portal_app_free(Evil_PortalApp* app) { text_box_free(app->text_box); furi_string_free(app->text_box_store); + text_input_free(app->text_input); + + view_stack_free(app->view_stack); + loading_free(app->loading); // View dispatcher view_dispatcher_free(app->view_dispatcher); @@ -98,31 +113,32 @@ void evil_portal_app_free(Evil_PortalApp* app) { // Close records furi_record_close(RECORD_GUI); + furi_record_close(RECORD_DIALOGS); + furi_string_free(app->file_path); free(app); } int32_t evil_portal_app(void* p) { UNUSED(p); + Evil_PortalApp* evil_portal_app = evil_portal_app_alloc(); - // Enable 5v on startup uint8_t attempts = 0; + bool otg_was_enabled = furi_hal_power_is_otg_enabled(); while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { furi_hal_power_enable_otg(); furi_delay_ms(10); } furi_delay_ms(200); - Evil_PortalApp* evil_portal_app = evil_portal_app_alloc(); - evil_portal_app->uart = evil_portal_uart_init(evil_portal_app); view_dispatcher_run(evil_portal_app->view_dispatcher); evil_portal_app_free(evil_portal_app); - if(furi_hal_power_is_otg_enabled()) { + if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) { furi_hal_power_disable_otg(); } diff --git a/applications/external/evil_portal/evil_portal_app.h b/applications/external/evil_portal/evil_portal_app.h index 65c047ea5..bb663ec22 100644 --- a/applications/external/evil_portal/evil_portal_app.h +++ b/applications/external/evil_portal/evil_portal_app.h @@ -8,4 +8,4 @@ typedef struct Evil_PortalApp Evil_PortalApp; #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/applications/external/evil_portal/evil_portal_app_i.h b/applications/external/evil_portal/evil_portal_app_i.h index eeda72b70..5fa6f62b8 100644 --- a/applications/external/evil_portal/evil_portal_app_i.h +++ b/applications/external/evil_portal/evil_portal_app_i.h @@ -4,33 +4,36 @@ #include "evil_portal_custom_event.h" #include "evil_portal_uart.h" #include "scenes/evil_portal_scene.h" +#include "evil_portal_icons.h" +#include #include +#include #include +#include #include #include #include - -#include +#include #include -#define NUM_MENU_ITEMS (4) +#include + +#define NUM_MENU_ITEMS (6) #define EVIL_PORTAL_TEXT_BOX_STORE_SIZE (4096) -#define UART_CH (FuriHalUartIdUSART1) +#define UART_CH \ + (XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) #define SET_HTML_CMD "sethtml" #define SET_AP_CMD "setap" #define RESET_CMD "reset" -#define EVIL_PORTAL_INDEX_EXTENSION ".html" -#define EVIL_PORTAL_BASE_FOLDER STORAGE_APP_DATA_PATH_PREFIX - struct Evil_PortalApp { Gui* gui; ViewDispatcher* view_dispatcher; SceneManager* scene_manager; - DialogsApp* dialogs; FuriString* portal_logs; const char* command_queue[1]; @@ -43,6 +46,11 @@ struct Evil_PortalApp { VariableItemList* var_item_list; Evil_PortalUart* uart; + TextInput* text_input; + DialogsApp* dialogs; + FuriString* file_path; + Loading* loading; + ViewStack* view_stack; int selected_menu_index; int selected_option_index[NUM_MENU_ITEMS]; @@ -55,6 +63,7 @@ struct Evil_PortalApp { bool sent_html; bool sent_reset; int BAUDRATE; + char text_store[2][128 + 1]; uint8_t* index_html; uint8_t* ap_name; @@ -64,4 +73,5 @@ typedef enum { Evil_PortalAppViewVarItemList, Evil_PortalAppViewConsoleOutput, Evil_PortalAppViewStartPortal, + Evil_PortalAppViewTextInput, } Evil_PortalAppView; diff --git a/applications/external/evil_portal/evil_portal_custom_event.h b/applications/external/evil_portal/evil_portal_custom_event.h index a566ca62e..a4609e00a 100644 --- a/applications/external/evil_portal/evil_portal_custom_event.h +++ b/applications/external/evil_portal/evil_portal_custom_event.h @@ -5,4 +5,5 @@ typedef enum { Evil_PortalEventStartConsole, Evil_PortalEventStartKeyboard, Evil_PortalEventStartPortal, + Evil_PortalEventTextInput, } Evil_PortalCustomEvent; diff --git a/applications/external/evil_portal/evil_portal_uart.c b/applications/external/evil_portal/evil_portal_uart.c index 2698a4410..8d88f7cf9 100644 --- a/applications/external/evil_portal/evil_portal_uart.c +++ b/applications/external/evil_portal/evil_portal_uart.c @@ -99,6 +99,7 @@ static int32_t uart_worker(void* context) { } } + furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); furi_stream_buffer_free(uart->rx_stream); return 0; @@ -121,13 +122,21 @@ Evil_PortalUart* evil_portal_uart_init(Evil_PortalApp* app) { furi_thread_start(uart->rx_thread); - furi_hal_console_disable(); + if(UART_CH == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH, app->BAUDRATE); + } + if(app->BAUDRATE == 0) { app->BAUDRATE = 115200; } + furi_hal_uart_set_br(UART_CH, app->BAUDRATE); furi_hal_uart_set_irq_cb(UART_CH, evil_portal_uart_on_irq_cb, uart); + evil_portal_uart_tx((uint8_t*)("XFW#EVILPORTAL=1\n"), strlen("XFW#EVILPORTAL=1\n")); + return uart; } @@ -138,8 +147,11 @@ void evil_portal_uart_free(Evil_PortalUart* uart) { furi_thread_join(uart->rx_thread); furi_thread_free(uart->rx_thread); - furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); - furi_hal_console_enable(); + if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH); + } else { + furi_hal_console_enable(); + } free(uart); } diff --git a/applications/external/evil_portal/helpers/evil_portal_storage.c b/applications/external/evil_portal/helpers/evil_portal_storage.c index aa9a50493..22dd11dfb 100644 --- a/applications/external/evil_portal/helpers/evil_portal_storage.c +++ b/applications/external/evil_portal/helpers/evil_portal_storage.c @@ -8,31 +8,21 @@ static void evil_portal_close_storage() { furi_record_close(RECORD_STORAGE); } -bool evil_portal_read_index_html(void* context) { - FuriString* file_path = furi_string_alloc_set(EVIL_PORTAL_BASE_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, - EVIL_PORTAL_INDEX_EXTENSION, - NULL); // TODO configure icon - browser_options.base_path = EVIL_PORTAL_BASE_FOLDER; - +void evil_portal_read_index_html(void* context) { Evil_PortalApp* app = context; - bool res = dialog_file_browser_show(app->dialogs, file_path, file_path, &browser_options); - - if(!res) { - furi_string_free(file_path); - return false; - } - Storage* storage = evil_portal_open_storage(); FileInfo fi; - if(storage_common_stat(storage, furi_string_get_cstr(file_path), &fi) == FSE_OK) { + if(!storage_common_exists(storage, EVIL_PORTAL_INDEX_SAVE_PATH)) { + FuriString* tmp = furi_string_alloc_set(EVIL_PORTAL_INDEX_DEFAULT_PATH); + evil_portal_replace_index_html(tmp); + furi_string_free(tmp); + } + + if(storage_common_stat(storage, EVIL_PORTAL_INDEX_SAVE_PATH, &fi) == FSE_OK) { File* index_html = storage_file_alloc(storage); if(storage_file_open( - index_html, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + index_html, EVIL_PORTAL_INDEX_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { app->index_html = malloc((size_t)fi.size); uint8_t* buf_ptr = app->index_html; size_t read = 0; @@ -45,7 +35,6 @@ bool evil_portal_read_index_html(void* context) { } free(buf_ptr); } - furi_string_free(file_path); storage_file_close(index_html); storage_file_free(index_html); } else { @@ -57,7 +46,33 @@ bool evil_portal_read_index_html(void* context) { } evil_portal_close_storage(); - return true; +} + +void evil_portal_replace_index_html(FuriString* path) { + Storage* storage = evil_portal_open_storage(); + FS_Error error; + error = storage_common_remove(storage, EVIL_PORTAL_INDEX_SAVE_PATH); + if(error != FSE_OK) { + FURI_LOG_D("EVIL PORTAL", "Error removing file"); + } else { + FURI_LOG_D("EVIL PORTAL", "Error removed file"); + } + error = storage_common_copy(storage, furi_string_get_cstr(path), EVIL_PORTAL_INDEX_SAVE_PATH); + if(error != FSE_OK) { + FURI_LOG_D("EVIL PORTAL", "Error copying file"); + } + evil_portal_close_storage(); +} + +void evil_portal_create_html_folder_if_not_exists() { + Storage* storage = evil_portal_open_storage(); + if(storage_common_stat(storage, HTML_FOLDER, NULL) == FSE_NOT_EXIST) { + FURI_LOG_D("Evil Portal", "Directory %s doesn't exist. Will create new.", HTML_FOLDER); + if(!storage_simply_mkdir(storage, HTML_FOLDER)) { + FURI_LOG_E("Evil Portal", "Error creating directory %s", HTML_FOLDER); + } + } + evil_portal_close_storage(); } void evil_portal_read_ap_name(void* context) { @@ -89,6 +104,19 @@ void evil_portal_read_ap_name(void* context) { evil_portal_close_storage(); } +void evil_portal_write_ap_name(void* context) { + Evil_PortalApp* app = context; + Storage* storage = evil_portal_open_storage(); + + File* ap_name = storage_file_alloc(storage); + if(storage_file_open(ap_name, EVIL_PORTAL_AP_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(ap_name, app->text_store[0], strlen(app->text_store[0])); + } + storage_file_close(ap_name); + storage_file_free(ap_name); + evil_portal_close_storage(); +} + char* sequential_file_resolve_path( Storage* storage, const char* dir, diff --git a/applications/external/evil_portal/helpers/evil_portal_storage.h b/applications/external/evil_portal/helpers/evil_portal_storage.h index d4ad33e1d..234b853e6 100644 --- a/applications/external/evil_portal/helpers/evil_portal_storage.h +++ b/applications/external/evil_portal/helpers/evil_portal_storage.h @@ -1,5 +1,4 @@ #include "../evil_portal_app_i.h" -#include #include #include #include @@ -7,12 +6,18 @@ #include #define PORTAL_FILE_DIRECTORY_PATH EXT_PATH("apps_data/evil_portal") +#define HTML_EXTENSION ".html" +#define HTML_FOLDER PORTAL_FILE_DIRECTORY_PATH "/html" #define EVIL_PORTAL_INDEX_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/index.html" +#define EVIL_PORTAL_INDEX_DEFAULT_PATH HTML_FOLDER "/xtreme.html" #define EVIL_PORTAL_AP_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/ap.config.txt" #define EVIL_PORTAL_LOG_SAVE_PATH PORTAL_FILE_DIRECTORY_PATH "/logs" -bool evil_portal_read_index_html(void* context); +void evil_portal_read_index_html(void* context); void evil_portal_read_ap_name(void* context); +void evil_portal_write_ap_name(void* context); +void evil_portal_replace_index_html(FuriString* path); +void evil_portal_create_html_folder_if_not_exists(); void write_logs(FuriString* portal_logs); char* sequential_file_resolve_path( Storage* storage, diff --git a/applications/external/evil_portal/icons/evil_portal_10px.png b/applications/external/evil_portal/icons/evil_portal_10px.png index c13534660..eed8789a4 100644 Binary files a/applications/external/evil_portal/icons/evil_portal_10px.png and b/applications/external/evil_portal/icons/evil_portal_10px.png differ diff --git a/applications/external/evil_portal/scenes/evil_portal_scene_config.h b/applications/external/evil_portal/scenes/evil_portal_scene_config.h index 94b09ae46..ffb958487 100644 --- a/applications/external/evil_portal/scenes/evil_portal_scene_config.h +++ b/applications/external/evil_portal/scenes/evil_portal_scene_config.h @@ -1,2 +1,4 @@ ADD_SCENE(evil_portal, start, Start) ADD_SCENE(evil_portal, console_output, ConsoleOutput) +ADD_SCENE(evil_portal, rename, Rename) +ADD_SCENE(evil_portal, select_html, SelectHtml) diff --git a/applications/external/evil_portal/scenes/evil_portal_scene_console_output.c b/applications/external/evil_portal/scenes/evil_portal_scene_console_output.c index 94f2cef4f..19b076845 100644 --- a/applications/external/evil_portal/scenes/evil_portal_scene_console_output.c +++ b/applications/external/evil_portal/scenes/evil_portal_scene_console_output.c @@ -22,8 +22,6 @@ void evil_portal_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, void void evil_portal_scene_console_output_on_enter(void* context) { Evil_PortalApp* app = context; - bool portal_file_set = false; - TextBox* text_box = app->text_box; text_box_reset(app->text_box); text_box_set_font(text_box, TextBoxFontText); @@ -64,25 +62,24 @@ void evil_portal_scene_console_output_on_enter(void* context) { } } - if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) { - portal_file_set = evil_portal_read_index_html(context); + if(0 == strncmp("setapname", app->selected_tx_string, strlen("setapname"))) { + scene_manager_next_scene(app->scene_manager, Evil_PortalSceneRename); + return; + } - if(portal_file_set) { - app->command_queue[0] = SET_AP_CMD; - app->has_command_queue = true; - app->command_index = 0; - if(app->show_stopscan_tip) { - const char* msg = "Starting portal\nIf no response press\nBACK to return\n"; - furi_string_cat_str(app->text_box_store, msg); - app->text_box_store_strlen += strlen(msg); - } - } else { - if(app->show_stopscan_tip) { - const char* msg = "No portal selected\nShowing current logs\nPress " - "BACK to return\n"; - furi_string_cat_str(app->text_box_store, msg); - app->text_box_store_strlen += strlen(msg); - } + if(0 == strncmp("selecthtml", app->selected_tx_string, strlen("selecthtml"))) { + scene_manager_next_scene(app->scene_manager, Evil_PortalSceneSelectHtml); + return; + } + + if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) { + app->command_queue[0] = SET_AP_CMD; + app->has_command_queue = true; + app->command_index = 0; + if(app->show_stopscan_tip) { + const char* msg = "Starting portal\nIf no response press\nBACK to return\n"; + furi_string_cat_str(app->text_box_store, msg); + app->text_box_store_strlen += strlen(msg); } } @@ -107,13 +104,7 @@ void evil_portal_scene_console_output_on_enter(void* context) { if(app->is_command && app->selected_tx_string) { if(0 == strncmp(SET_HTML_CMD, app->selected_tx_string, strlen(SET_HTML_CMD))) { - if(!portal_file_set) { - scene_manager_set_scene_state( - app->scene_manager, Evil_PortalSceneConsoleOutput, 0); - view_dispatcher_switch_to_view( - app->view_dispatcher, Evil_PortalAppViewConsoleOutput); - return; - } + evil_portal_read_index_html(context); FuriString* data = furi_string_alloc(); furi_string_cat(data, "sethtml="); diff --git a/applications/external/evil_portal/scenes/evil_portal_scene_rename.c b/applications/external/evil_portal/scenes/evil_portal_scene_rename.c new file mode 100644 index 000000000..72f3ef18c --- /dev/null +++ b/applications/external/evil_portal/scenes/evil_portal_scene_rename.c @@ -0,0 +1,42 @@ +#include "../evil_portal_app_i.h" +#include "../helpers/evil_portal_storage.h" + +void evil_portal_text_input_callback(void* context) { + furi_assert(context); + Evil_PortalApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, Evil_PortalEventTextInput); +} + +void evil_portal_scene_rename_on_enter(void* context) { + Evil_PortalApp* app = context; + TextInput* text_input = app->text_input; + size_t enter_name_length = 25; + evil_portal_read_ap_name(app); + text_input_set_header_text(text_input, "AP Name/SSID"); + strncpy(app->text_store[0], (char*)app->ap_name, enter_name_length); + text_input_set_result_callback( + text_input, + evil_portal_text_input_callback, + context, + app->text_store[0], + enter_name_length, + false); + view_dispatcher_switch_to_view(app->view_dispatcher, Evil_PortalAppViewTextInput); +} + +bool evil_portal_scene_rename_on_event(void* context, SceneManagerEvent event) { + Evil_PortalApp* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + evil_portal_write_ap_name(app); + scene_manager_search_and_switch_to_previous_scene(scene_manager, Evil_PortalSceneStart); + consumed = true; + } + return consumed; +} + +void evil_portal_scene_rename_on_exit(void* context) { + Evil_PortalApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/evil_portal/scenes/evil_portal_scene_select_html.c b/applications/external/evil_portal/scenes/evil_portal_scene_select_html.c new file mode 100644 index 000000000..1dbcdd54d --- /dev/null +++ b/applications/external/evil_portal/scenes/evil_portal_scene_select_html.c @@ -0,0 +1,54 @@ +#include "../evil_portal_app_i.h" +#include "../helpers/evil_portal_storage.h" + +void evil_portal_show_loading_popup(Evil_PortalApp* app, bool show) { + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + ViewStack* view_stack = app->view_stack; + Loading* loading = app->loading; + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_stack_add_view(view_stack, loading_get_view(loading)); + } else { + view_stack_remove_view(view_stack, loading_get_view(loading)); + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +void evil_portal_scene_select_html_on_enter(void* context) { + Evil_PortalApp* app = context; + DialogsFileBrowserOptions browser_options; + evil_portal_create_html_folder_if_not_exists(); + + dialog_file_browser_set_basic_options(&browser_options, HTML_EXTENSION, &I_evil_portal_10px); + browser_options.base_path = HTML_FOLDER; + + FuriString* path; + path = furi_string_alloc(); + + furi_string_set(path, HTML_FOLDER); + + bool success = dialog_file_browser_show(app->dialogs, app->file_path, path, &browser_options); + furi_string_free(path); + + if(success) { + //Replace HTML File + evil_portal_show_loading_popup(app, true); + evil_portal_replace_index_html(app->file_path); + evil_portal_show_loading_popup(app, false); + } + + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, Evil_PortalSceneStart); +} + +bool evil_portal_scene_select_html_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = true; + return consumed; +} + +void evil_portal_scene_select_html_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/external/evil_portal/scenes/evil_portal_scene_start.c b/applications/external/evil_portal/scenes/evil_portal_scene_start.c index 7f7200fcb..b06aedc51 100644 --- a/applications/external/evil_portal/scenes/evil_portal_scene_start.c +++ b/applications/external/evil_portal/scenes/evil_portal_scene_start.c @@ -33,6 +33,12 @@ const Evil_PortalItem items[NUM_MENU_ITEMS] = { // console {"Save logs", {""}, 1, {"savelogs"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, + // set AP name + {"Set AP name", {""}, 1, {"setapname"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, + + // select HTML Portal File + {"Select HTML", {""}, 1, {"selecthtml"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, + // help {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, }; diff --git a/applications/external/flappy_bird/application.fam b/applications/external/flappy_bird/application.fam index ad18e57e1..625c5f66c 100644 --- a/applications/external/flappy_bird/application.fam +++ b/applications/external/flappy_bird/application.fam @@ -5,7 +5,6 @@ App( entry_point="flappy_game_app", requires=["gui"], stack_size=4 * 1024, - order=100, fap_icon="flappy_10px.png", fap_category="Games", fap_icon_assets="assets", diff --git a/applications/external/flappy_bird/flappy_bird.c b/applications/external/flappy_bird/flappy_bird.c index b51efbb26..496ffcf56 100644 --- a/applications/external/flappy_bird/flappy_bird.c +++ b/applications/external/flappy_bird/flappy_bird.c @@ -1,9 +1,11 @@ #include -#include +#include "flappy_bird_icons.h" +#include #include #include #include +#include #define TAG "Flappy" #define DEBUG false @@ -312,7 +314,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/flashlight/application.fam b/applications/external/flashlight/application.fam index 030c52990..4ad9b6101 100644 --- a/applications/external/flashlight/application.fam +++ b/applications/external/flashlight/application.fam @@ -1,6 +1,6 @@ App( appid="flashlight", - name="[GPIO] Flashlight", + name="[LED] Flashlight", apptype=FlipperAppType.EXTERNAL, entry_point="flashlight_app", cdefines=["APP_FLASHLIGHT"], @@ -8,7 +8,6 @@ App( "gui", ], stack_size=2 * 1024, - order=20, fap_icon="flash10px.png", fap_category="GPIO", fap_author="@xMasterX", diff --git a/applications/external/flipbip/application.fam b/applications/external/flipbip/application.fam index 135b717e9..0831ff432 100644 --- a/applications/external/flipbip/application.fam +++ b/applications/external/flipbip/application.fam @@ -7,10 +7,7 @@ App( "gui", ], stack_size=3 * 1024, - order=10, fap_icon="flipbip_10px.png", - fap_icon_assets="icons", - fap_icon_assets_symbol="flipbip", fap_private_libs=[ Lib( name="crypto", @@ -19,6 +16,6 @@ App( fap_category="Tools", fap_author="Struan Clark (xtruan)", fap_weburl="https://github.com/xtruan/FlipBIP", - fap_version=(1, 11), - fap_description="Crypto wallet tools for Flipper", + fap_version=(1, 13), + fap_description="Crypto wallet for Flipper", ) diff --git a/applications/external/flipbip/flipbip.c b/applications/external/flipbip/flipbip.c index 1a5e5a983..7a7237639 100644 --- a/applications/external/flipbip/flipbip.c +++ b/applications/external/flipbip/flipbip.c @@ -1,12 +1,13 @@ -#pragma GCC optimize("-Os") - #include "flipbip.h" #include "helpers/flipbip_file.h" -#include "helpers/flipbip_haptic.h" // From: lib/crypto #include #include +#define MNEMONIC_MENU_DEFAULT "Import mnemonic seed" +#define MNEMONIC_MENU_SUCCESS "Import seed (success)" +#define MNEMONIC_MENU_FAILURE "Import seed (failed!)" + bool flipbip_custom_event_callback(void* context, uint32_t event) { furi_assert(context); FlipBip* app = context; @@ -42,6 +43,7 @@ static void text_input_callback(void* context) { // reset input state app->input_state = FlipBipTextInputDefault; handled = true; + // switch back to settings view view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdSettings); } else if(app->input_state == FlipBipTextInputMnemonic) { if(app->import_from_mnemonic == 1) { @@ -56,11 +58,13 @@ static void text_input_callback(void* context) { status = FlipBipStatusSaveError; // 12 = save error if(status == FlipBipStatusSuccess) { + app->mnemonic_menu_text = MNEMONIC_MENU_SUCCESS; //notification_message(app->notification, &sequence_blink_cyan_100); - flipbip_play_happy_bump(app); + //flipbip_play_happy_bump(app); } else { + app->mnemonic_menu_text = MNEMONIC_MENU_FAILURE; //notification_message(app->notification, &sequence_blink_red_100); - flipbip_play_long_bump(app); + //flipbip_play_long_bump(app); } memzero(app->import_mnemonic_text, TEXT_BUFFER_SIZE); @@ -70,7 +74,9 @@ static void text_input_callback(void* context) { // reset input state app->input_state = FlipBipTextInputDefault; handled = true; - view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); + // exit scene 1 instance that's being used for text input and go back to menu + scene_manager_previous_scene(app->scene_manager); + //view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); } } @@ -79,6 +85,7 @@ static void text_input_callback(void* context) { memzero(app->input_text, TEXT_BUFFER_SIZE); // reset input state app->input_state = FlipBipTextInputDefault; + // something went wrong, switch to menu view view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdMenu); } } @@ -86,12 +93,12 @@ static void text_input_callback(void* context) { FlipBip* flipbip_app_alloc() { FlipBip* app = malloc(sizeof(FlipBip)); app->gui = furi_record_open(RECORD_GUI); - app->notification = furi_record_open(RECORD_NOTIFICATION); + //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); + // Turn backlight on, believe me this makes testing your app easier + //notification_message(app->notification, &sequence_display_backlight_on); - //Scene additions + // Scene additions app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -105,8 +112,6 @@ FlipBip* flipbip_app_alloc() { app->submenu = submenu_alloc(); // Settings - app->haptic = FlipBipHapticOn; - app->led = FlipBipLedOn; app->bip39_strength = FlipBipStrength256; // 256 bits (24 words) app->passphrase = FlipBipPassphraseOff; @@ -114,17 +119,13 @@ FlipBip* flipbip_app_alloc() { app->bip44_coin = FlipBipCoinBTC0; // 0 (BTC) app->overwrite_saved_seed = 0; app->import_from_mnemonic = 0; + app->mnemonic_menu_text = MNEMONIC_MENU_DEFAULT; // 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)); @@ -141,13 +142,13 @@ FlipBip* flipbip_app_alloc() { (void*)app, app->input_text, TEXT_BUFFER_SIZE, - //clear default text + // clear default text true); - text_input_set_header_text(app->text_input, "Input"); + //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 + // End Scene Additions return app; } @@ -171,7 +172,7 @@ void flipbip_app_free(FlipBip* app) { furi_record_close(RECORD_GUI); app->gui = NULL; - app->notification = NULL; + //app->notification = NULL; //Remove whatever is left memzero(app, sizeof(FlipBip)); @@ -190,9 +191,7 @@ int32_t flipbip_app(void* p) { 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 + scene_manager_next_scene(app->scene_manager, FlipBipSceneMenu); //Start with menu furi_hal_power_suppress_charge_enter(); diff --git a/applications/external/flipbip/flipbip.h b/applications/external/flipbip/flipbip.h index 9df002aaa..9f5994b80 100644 --- a/applications/external/flipbip/flipbip.h +++ b/applications/external/flipbip/flipbip.h @@ -5,37 +5,35 @@ #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 "v1.11.0" +#define FLIPBIP_VERSION "v1.13" #define COIN_BTC 0 #define COIN_DOGE 3 #define COIN_ETH 60 +#define COIN_ZEC 133 #define TEXT_BUFFER_SIZE 256 typedef struct { Gui* gui; - NotificationApp* notification; + // NotificationApp* notification; ViewDispatcher* view_dispatcher; Submenu* submenu; SceneManager* scene_manager; VariableItemList* variable_item_list; TextInput* text_input; - FlipBipStartscreen* flipbip_startscreen; FlipBipScene1* flipbip_scene_1; + char* mnemonic_menu_text; // Settings options - int haptic; - int led; int bip39_strength; int passphrase; // Main menu options @@ -57,16 +55,6 @@ typedef enum { FlipBipViewIdTextInput, } FlipBipViewId; -typedef enum { - FlipBipHapticOff, - FlipBipHapticOn, -} FlipBipHapticState; - -typedef enum { - FlipBipLedOff, - FlipBipLedOn, -} FlipBipLedState; - typedef enum { FlipBipStrength128, FlipBipStrength192, @@ -82,6 +70,7 @@ typedef enum { FlipBipCoinBTC0, FlipBipCoinETH60, FlipBipCoinDOGE3, + FlipBipCoinZEC133, } FlipBipCoin; typedef enum { diff --git a/applications/external/flipbip/helpers/flipbip_custom_event.h b/applications/external/flipbip/helpers/flipbip_custom_event.h index 2dbaf5112..882c50439 100644 --- a/applications/external/flipbip/helpers/flipbip_custom_event.h +++ b/applications/external/flipbip/helpers/flipbip_custom_event.h @@ -1,12 +1,6 @@ #pragma once typedef enum { - FlipBipCustomEventStartscreenUp, - FlipBipCustomEventStartscreenDown, - FlipBipCustomEventStartscreenLeft, - FlipBipCustomEventStartscreenRight, - FlipBipCustomEventStartscreenOk, - FlipBipCustomEventStartscreenBack, FlipBipCustomEventScene1Up, FlipBipCustomEventScene1Down, FlipBipCustomEventScene1Left, diff --git a/applications/external/flipbip/helpers/flipbip_haptic.h b/applications/external/flipbip/helpers/flipbip_haptic.h deleted file mode 100644 index cab1d3a63..000000000 --- a/applications/external/flipbip/helpers/flipbip_haptic.h +++ /dev/null @@ -1,7 +0,0 @@ -#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 deleted file mode 100644 index 7a6fd1778..000000000 --- a/applications/external/flipbip/helpers/flipbip_led.c +++ /dev/null @@ -1,39 +0,0 @@ -#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 deleted file mode 100644 index bbacc976b..000000000 --- a/applications/external/flipbip/helpers/flipbip_led.h +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index f7ae2193b..000000000 --- a/applications/external/flipbip/helpers/flipbip_speaker.c +++ /dev/null @@ -1,27 +0,0 @@ -// #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 deleted file mode 100644 index 150ba9129..000000000 --- a/applications/external/flipbip/helpers/flipbip_speaker.h +++ /dev/null @@ -1,4 +0,0 @@ -// #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/lib/crypto/CONTRIBUTORS b/applications/external/flipbip/lib/crypto/CONTRIBUTORS index 300b9788d..1307453ad 100644 --- a/applications/external/flipbip/lib/crypto/CONTRIBUTORS +++ b/applications/external/flipbip/lib/crypto/CONTRIBUTORS @@ -14,3 +14,4 @@ Oleg Andreev mog John Dvorak Christian Reitter +Struan Clark \ No newline at end of file diff --git a/applications/external/flipbip/lib/crypto/bip32.c b/applications/external/flipbip/lib/crypto/bip32.c index 09f00d60e..efa511e6d 100644 --- a/applications/external/flipbip/lib/crypto/bip32.c +++ b/applications/external/flipbip/lib/crypto/bip32.c @@ -26,7 +26,9 @@ #include #include "address.h" +#if USE_NEM #include "aes/aes.h" +#endif #include "base58.h" #include "bignum.h" #include "bip32.h" diff --git a/applications/external/flipbip/lib/crypto/memzero.c b/applications/external/flipbip/lib/crypto/memzero.c index 64866ee56..234f7dd6c 100644 --- a/applications/external/flipbip/lib/crypto/memzero.c +++ b/applications/external/flipbip/lib/crypto/memzero.c @@ -50,6 +50,7 @@ void memzero(void* const pnt, const size_t len) { SecureZeroMemory(pnt, len); #elif defined(HAVE_MEMSET_S) memset_s(pnt, (rsize_t)len, 0, (rsize_t)len); +// REMOVED - Flipper Zero does not have this function // #elif defined(HAVE_EXPLICIT_BZERO) // explicit_bzero(pnt, len); #elif defined(HAVE_EXPLICIT_MEMSET) diff --git a/applications/external/flipbip/lib/crypto/options.h b/applications/external/flipbip/lib/crypto/options.h index f0edcc60f..8510cb3f5 100644 --- a/applications/external/flipbip/lib/crypto/options.h +++ b/applications/external/flipbip/lib/crypto/options.h @@ -86,9 +86,14 @@ #define USE_KECCAK 1 #endif -// add way how to mark confidential data +// add a way to mark confidential data #ifndef CONFIDENTIAL #define CONFIDENTIAL #endif +// use Flipper Zero hardware random number generator +#ifndef USE_FLIPPER_HAL_RANDOM +#define USE_FLIPPER_HAL_RANDOM 1 +#endif + #endif diff --git a/applications/external/flipbip/lib/crypto/rand.c b/applications/external/flipbip/lib/crypto/rand.c index a10858734..64ee3d7a1 100644 --- a/applications/external/flipbip/lib/crypto/rand.c +++ b/applications/external/flipbip/lib/crypto/rand.c @@ -21,11 +21,9 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#define FLIPPER_HAL_RANDOM - #include "rand.h" -#ifdef FLIPPER_HAL_RANDOM +#if USE_FLIPPER_HAL_RANDOM // NOTE: // random32() and random_buffer() have been replaced in this implementation @@ -67,6 +65,8 @@ void random_buffer(uint8_t* buf, size_t len) { // The following code is platform independent // +static uint32_t seed = 0; + uint32_t random32(void) { // Linear congruential generator from Numerical Recipes // https://en.wikipedia.org/wiki/Linear_congruential_generator @@ -74,7 +74,7 @@ uint32_t random32(void) { return seed; } -void __attribute__((weak)) random_buffer(uint8_t *buf, size_t len) { +void random_buffer(uint8_t *buf, size_t len) { uint32_t r = 0; for (size_t i = 0; i < len; i++) { if (i % 4 == 0) { @@ -84,7 +84,7 @@ void __attribute__((weak)) random_buffer(uint8_t *buf, size_t len) { } } -#endif /* FLIPPER_HAL_RANDOM */ +#endif /* USE_FLIPPER_HAL_RANDOM */ uint32_t random_uniform(uint32_t n) { uint32_t x = 0, max = 0xFFFFFFFF - (0xFFFFFFFF % n); diff --git a/applications/external/flipbip/lib/crypto/rand.h b/applications/external/flipbip/lib/crypto/rand.h index fa854890f..14c2bfcfc 100644 --- a/applications/external/flipbip/lib/crypto/rand.h +++ b/applications/external/flipbip/lib/crypto/rand.h @@ -26,6 +26,7 @@ #include #include +#include "options.h" void random_reseed(const uint32_t value); uint32_t random32(void); diff --git a/applications/external/flipbip/scenes/flipbip_scene_config.h b/applications/external/flipbip/scenes/flipbip_scene_config.h index a62832162..6468414ac 100644 --- a/applications/external/flipbip/scenes/flipbip_scene_config.h +++ b/applications/external/flipbip/scenes/flipbip_scene_config.h @@ -1,4 +1,3 @@ -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 index 04525909d..928a002f7 100644 --- a/applications/external/flipbip/scenes/flipbip_scene_menu.c +++ b/applications/external/flipbip/scenes/flipbip_scene_menu.c @@ -1,13 +1,17 @@ #include "../flipbip.h" #include "../helpers/flipbip_file.h" +#define FLIPBIP_SUBMENU_TEXT "** FlipBIP wallet " FLIPBIP_VERSION " **" + enum SubmenuIndex { SubmenuIndexScene1BTC = 10, SubmenuIndexScene1ETH, SubmenuIndexScene1DOGE, + SubmenuIndexScene1ZEC, SubmenuIndexScene1New, SubmenuIndexScene1Import, SubmenuIndexSettings, + SubmenuIndexNOP, }; void flipbip_scene_menu_submenu_callback(void* context, uint32_t index) { @@ -18,6 +22,14 @@ void flipbip_scene_menu_submenu_callback(void* context, uint32_t index) { void flipbip_scene_menu_on_enter(void* context) { FlipBip* app = context; + // FlipBIP header with version + submenu_add_item( + app->submenu, + FLIPBIP_SUBMENU_TEXT, + SubmenuIndexNOP, + flipbip_scene_menu_submenu_callback, + app); + if(flipbip_has_file(FlipBipFileKey, NULL, false) && flipbip_has_file(FlipBipFileDat, NULL, false)) { submenu_add_item( @@ -38,6 +50,12 @@ void flipbip_scene_menu_on_enter(void* context) { SubmenuIndexScene1DOGE, flipbip_scene_menu_submenu_callback, app); + submenu_add_item( + app->submenu, + "View ZEC (t-addr) wallet", + SubmenuIndexScene1ZEC, + flipbip_scene_menu_submenu_callback, + app); submenu_add_item( app->submenu, "Regenerate wallet", @@ -54,7 +72,7 @@ void flipbip_scene_menu_on_enter(void* context) { } submenu_add_item( app->submenu, - "Import from mnemonic", + app->mnemonic_menu_text, SubmenuIndexScene1Import, flipbip_scene_menu_submenu_callback, app); @@ -101,6 +119,14 @@ bool flipbip_scene_menu_on_event(void* context, SceneManagerEvent event) { app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1DOGE); scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); return true; + } else if(event.event == SubmenuIndexScene1ZEC) { + app->overwrite_saved_seed = 0; + app->import_from_mnemonic = 0; + app->bip44_coin = FlipBipCoinZEC133; + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1ZEC); + 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; @@ -110,15 +136,17 @@ bool flipbip_scene_menu_on_event(void* context, SceneManagerEvent event) { 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); + scene_manager_set_scene_state( + app->scene_manager, FlipBipSceneMenu, SubmenuIndexScene1Import); + scene_manager_next_scene(app->scene_manager, FlipBipSceneScene_1); 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; + } else if(event.event == SubmenuIndexNOP) { + return true; } } return false; diff --git a/applications/external/flipbip/scenes/flipbip_scene_scene_1.c b/applications/external/flipbip/scenes/flipbip_scene_scene_1.c index 6f4064cd4..3b0f1ff0e 100644 --- a/applications/external/flipbip/scenes/flipbip_scene_scene_1.c +++ b/applications/external/flipbip/scenes/flipbip_scene_scene_1.c @@ -11,8 +11,18 @@ void flipbip_scene_1_callback(FlipBipCustomEvent event, void* context) { 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); + + if(app->import_from_mnemonic == 1) { + // handle mnemonic seed import mode with text input, this only + // uses this scene to have a correct stack of scenes + app->input_state = FlipBipTextInputMnemonic; + text_input_set_header_text(app->text_input, "Enter mnemonic phrase"); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipBipViewIdTextInput); + } else { + // handle all other modes, these actually use this scene's logic + 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) { @@ -21,16 +31,16 @@ bool flipbip_scene_scene_1_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { switch(event.event) { - case FlipBipCustomEventScene1Left: - case FlipBipCustomEventScene1Right: - break; - case FlipBipCustomEventScene1Up: - case FlipBipCustomEventScene1Down: - break; + // 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); + //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); diff --git a/applications/external/flipbip/scenes/flipbip_scene_settings.c b/applications/external/flipbip/scenes/flipbip_scene_settings.c index c743c97b8..0e43033ed 100644 --- a/applications/external/flipbip/scenes/flipbip_scene_settings.c +++ b/applications/external/flipbip/scenes/flipbip_scene_settings.c @@ -6,24 +6,6 @@ #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", @@ -44,20 +26,6 @@ const uint32_t passphrase_value[2] = { 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); @@ -108,20 +76,6 @@ void flipbip_scene_settings_on_enter(void* context) { 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); } diff --git a/applications/external/flipbip/scenes/flipbip_scene_startscreen.c b/applications/external/flipbip/scenes/flipbip_scene_startscreen.c deleted file mode 100644 index a9cb8ba5f..000000000 --- a/applications/external/flipbip/scenes/flipbip_scene_startscreen.c +++ /dev/null @@ -1,55 +0,0 @@ -#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 index d36cad496..d3a5ee065 100644 --- a/applications/external/flipbip/views/flipbip_scene_1.c +++ b/applications/external/flipbip/views/flipbip_scene_1.c @@ -3,13 +3,9 @@ #include #include #include -//#include #include #include -#include "flipbip_icons.h" -#include -#include "../helpers/flipbip_haptic.h" -#include "../helpers/flipbip_led.h" +//#include "flipbip_icons.h" #include "../helpers/flipbip_string.h" #include "../helpers/flipbip_file.h" // From: /lib/crypto @@ -44,7 +40,7 @@ #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" +// #define TEXT_DEFAULT_DERIV "m/44'/X'/0'/0" const char* TEXT_INFO = "-Scroll pages with up/down-" "p1,2) BIP39 Mnemonic/Seed" "p3) BIP32 Root Key " @@ -56,16 +52,19 @@ const char* TEXT_INFO = "-Scroll pages with up/down-" #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] = { +const uint32_t COIN_INFO_ARRAY[4][6] = { {COIN_BTC, 0x0488ade4, 0x0488b21e, 0x00, 0x80, FlipBipCoinBTC0}, {COIN_ETH, 0x0488ade4, 0x0488b21e, 0x00, 0x80, FlipBipCoinETH60}, - {COIN_DOGE, 0x02fac398, 0x02facafd, 0x1e, 0x9e, FlipBipCoinBTC0}}; + {COIN_DOGE, 0x02fac398, 0x02facafd, 0x1e, 0x9e, FlipBipCoinBTC0}, + {COIN_ZEC, 0x0488ade4, 0x0488b21e, 0x1cb8, 0x80, FlipBipCoinZEC133}, +}; // coin_name, derivation_path -const char* COIN_TEXT_ARRAY[3][3] = { +const char* COIN_TEXT_ARRAY[4][3] = { {"BTC", "m/44'/0'/0'/0", "bitcoin:"}, {"ETH", "m/44'/60'/0'/0", "ethereum:"}, - {"DOGE", "m/44'/3'/0'/0", "dogecoin:"}}; + {"DOGE", "m/44'/3'/0'/0", "dogecoin:"}, + {"ZEC", "m/44'/133'/0'/0", "zcash:"}}; struct FlipBipScene1 { View* view; @@ -99,7 +98,7 @@ 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 const char* s_derivation_text = TEXT_DEFAULT_COIN; // TEXT_DEFAULT_DERIV; // Warning text static bool s_warn_insecure = false; #define WARN_INSECURE_TEXT_1 "Recommendation:" @@ -148,7 +147,6 @@ static void flipbip_scene_1_init_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 @@ -158,6 +156,12 @@ static void flipbip_scene_1_init_address( addr_text[1] = 'x'; // Convert the hash to a hex string flipbip_btox((uint8_t*)buf, 20, addr_text + 2); + + } else if(coin_info[5] == FlipBipCoinZEC133) { // ZEC + ecdsa_get_address( + s_addr_node->public_key, coin_info[3], HASHER_SHA2_RIPEMD, HASHER_SHA2D, buf, buflen); + addr_text[0] = 't'; + strcpy(addr_text, buf); } // Clear the address node @@ -312,7 +316,7 @@ void flipbip_scene_1_draw(Canvas* canvas, FlipBipScene1Model* model) { 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, 22, &I_Keychain_39x36); + // canvas_draw_icon(canvas, 86, 22, &I_Keychain_39x36); if(s_warn_insecure) { canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 50, WARN_INSECURE_TEXT_1); @@ -655,9 +659,12 @@ void flipbip_scene_1_enter(void* context) { s_derivation_text = TEXT_NEW_WALLET; } - flipbip_play_happy_bump(app); + // Wait a beat to allow the display time to update to the loading screen + furi_thread_flags_wait(0, FuriFlagWaitAny, 20); + + //flipbip_play_happy_bump(app); //notification_message(app->notification, &sequence_blink_cyan_100); - flipbip_led_set_rgb(app, 255, 0, 0); + //flipbip_led_set_rgb(app, 255, 0, 0); with_view_model( instance->view, @@ -670,7 +677,8 @@ void flipbip_scene_1_enter(void* context) { // nonzero status, free the mnemonic if(status != FlipBipStatusSuccess) { - memzero((void*)model->mnemonic, strlen(model->mnemonic)); + // calling strlen on mnemonic here can cause a crash, don't. + // it wasn't loaded properly anyways, no need to zero the memory free((void*)model->mnemonic); } @@ -678,15 +686,15 @@ void flipbip_scene_1_enter(void* context) { if(status == FlipBipStatusSaveError) { model->mnemonic = "ERROR:,Save error"; model->page = PAGE_MNEMONIC; - flipbip_play_long_bump(app); + //flipbip_play_long_bump(app); } else if(status == FlipBipStatusLoadError) { model->mnemonic = "ERROR:,Load error"; model->page = PAGE_MNEMONIC; - flipbip_play_long_bump(app); + //flipbip_play_long_bump(app); } else if(status == FlipBipStatusMnemonicCheckError) { model->mnemonic = "ERROR:,Mnemonic check error"; model->page = PAGE_MNEMONIC; - flipbip_play_long_bump(app); + //flipbip_play_long_bump(app); } // s_busy = false; diff --git a/applications/external/flipbip/views/flipbip_startscreen.c b/applications/external/flipbip/views/flipbip_startscreen.c deleted file mode 100644 index 0a4bebb57..000000000 --- a/applications/external/flipbip/views/flipbip_startscreen.c +++ /dev/null @@ -1,131 +0,0 @@ -#include "../flipbip.h" -#include -#include -#include -#include -#include "flipbip_icons.h" -#include - -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 deleted file mode 100644 index d6eb1fad8..000000000 --- a/applications/external/flipbip/views/flipbip_startscreen.h +++ /dev/null @@ -1,19 +0,0 @@ -#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 diff --git a/applications/external/flipper_i2ctools/application.fam b/applications/external/flipper_i2ctools/application.fam index 2d1d51316..5ed100750 100644 --- a/applications/external/flipper_i2ctools/application.fam +++ b/applications/external/flipper_i2ctools/application.fam @@ -1,11 +1,10 @@ App( appid="i2ctools", - name="[I2C] Tools", + name="[I2C] i2c Tools", apptype=FlipperAppType.EXTERNAL, entry_point="i2ctools_app", requires=["gui"], stack_size=2 * 1024, - order=175, fap_icon="i2ctools.png", fap_category="GPIO", fap_icon_assets="images", diff --git a/applications/external/flipper_i2ctools/images/Voltage_16x16.png b/applications/external/flipper_i2ctools/images/Voltage_16x16.png deleted file mode 100644 index 94e796872..000000000 Binary files a/applications/external/flipper_i2ctools/images/Voltage_16x16.png and /dev/null differ diff --git a/applications/external/flipper_i2ctools/views/infos_view.h b/applications/external/flipper_i2ctools/views/infos_view.h index 11b388c02..db7330483 100644 --- a/applications/external/flipper_i2ctools/views/infos_view.h +++ b/applications/external/flipper_i2ctools/views/infos_view.h @@ -1,7 +1,8 @@ #include #include #include -#include +#include "i2ctools_icons.h" +#include #define INFOS_TEXT "INFOS" diff --git a/applications/external/flipper_i2ctools/views/main_view.h b/applications/external/flipper_i2ctools/views/main_view.h index 6e476c6dd..878c3b2ea 100644 --- a/applications/external/flipper_i2ctools/views/main_view.h +++ b/applications/external/flipper_i2ctools/views/main_view.h @@ -1,7 +1,7 @@ #include #include #include -#include +#include "i2ctools_icons.h" #include #define APP_NAME "I2C Tools" @@ -41,4 +41,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); +void i2c_main_view_free(i2cMainView* main_view); \ No newline at end of file diff --git a/applications/external/flipper_i2ctools/views/scanner_view.h b/applications/external/flipper_i2ctools/views/scanner_view.h index 9b571ed9b..0ce780d5e 100644 --- a/applications/external/flipper_i2ctools/views/scanner_view.h +++ b/applications/external/flipper_i2ctools/views/scanner_view.h @@ -1,10 +1,10 @@ #include #include #include -#include +#include "i2ctools_icons.h" #include #include "../i2cscanner.h" #define SCAN_TEXT "SCAN" -void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner); +void draw_scanner_view(Canvas* canvas, i2cScanner* i2c_scanner); \ No newline at end of file diff --git a/applications/external/flipper_i2ctools/views/sender_view.h b/applications/external/flipper_i2ctools/views/sender_view.h index 3c2000703..a1148e1f8 100644 --- a/applications/external/flipper_i2ctools/views/sender_view.h +++ b/applications/external/flipper_i2ctools/views/sender_view.h @@ -1,10 +1,10 @@ #include #include #include -#include +#include "i2ctools_icons.h" #include #include "../i2csender.h" #define SEND_TEXT "SEND" -void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender); +void draw_sender_view(Canvas* canvas, i2cSender* i2c_sender); \ No newline at end of file diff --git a/applications/external/flipper_i2ctools/views/sniffer_view.h b/applications/external/flipper_i2ctools/views/sniffer_view.h index 1b586d568..fdb817ec3 100644 --- a/applications/external/flipper_i2ctools/views/sniffer_view.h +++ b/applications/external/flipper_i2ctools/views/sniffer_view.h @@ -1,10 +1,10 @@ #include #include #include -#include +#include "i2ctools_icons.h" #include #include "../i2csniffer.h" #define SNIFF_TEXT "SNIFF" -void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer); +void draw_sniffer_view(Canvas* canvas, i2cSniffer* i2c_sniffer); \ No newline at end of file diff --git a/applications/external/flizzer_tracker/application.fam b/applications/external/flizzer_tracker/application.fam index c5b2d2cee..481e41c40 100644 --- a/applications/external/flizzer_tracker/application.fam +++ b/applications/external/flizzer_tracker/application.fam @@ -5,7 +5,6 @@ App( 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", diff --git a/applications/external/flizzer_tracker/flizzer_tracker.c b/applications/external/flizzer_tracker/flizzer_tracker.c index 2c0729a43..63f22e32a 100644 --- a/applications/external/flizzer_tracker/flizzer_tracker.c +++ b/applications/external/flizzer_tracker/flizzer_tracker.c @@ -7,7 +7,8 @@ #include "view/pattern_editor.h" #include "font.h" -#include +#include "flizzer_tracker_icons.h" +#include void draw_callback(Canvas* canvas, void* ctx) { TrackerViewModel* model = (TrackerViewModel*)ctx; diff --git a/applications/external/flizzer_tracker/view/instrument_editor.c b/applications/external/flizzer_tracker/view/instrument_editor.c index b931a5de5..4d5463118 100644 --- a/applications/external/flizzer_tracker/view/instrument_editor.c +++ b/applications/external/flizzer_tracker/view/instrument_editor.c @@ -4,7 +4,8 @@ #include "../macros.h" #include "opcode_description.h" -#include +#include "flizzer_tracker_icons.h" +#include void draw_inst_flag( FlizzerTrackerApp* tracker, diff --git a/applications/external/flizzer_tracker/view/pattern_editor.c b/applications/external/flizzer_tracker/view/pattern_editor.c index 19c62d9f9..c4effed56 100644 --- a/applications/external/flizzer_tracker/view/pattern_editor.c +++ b/applications/external/flizzer_tracker/view/pattern_editor.c @@ -1,7 +1,8 @@ #include "pattern_editor.h" #include "../macros.h" -#include +#include "flizzer_tracker_icons.h" +#include #define PATTERN_EDITOR_Y ((tracker->focus == EDIT_PATTERN) ? 4 : (64 - (6 * 5) - 1)) diff --git a/applications/external/game15/application.fam b/applications/external/game15/application.fam index d6b1e10a0..36efe4d26 100644 --- a/applications/external/game15/application.fam +++ b/applications/external/game15/application.fam @@ -6,7 +6,6 @@ App( requires=["gui"], stack_size=1 * 1024, fap_icon="game15_10px.png", - order=30, fap_category="Games", fap_author="@x27", fap_version="1.0", diff --git a/applications/external/game15/game15.c b/applications/external/game15/game15.c index 32d47b944..3a8750d75 100644 --- a/applications/external/game15/game15.c +++ b/applications/external/game15/game15.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "sandbox.h" @@ -118,6 +119,7 @@ static bool storage_game_state_load() { 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)); @@ -457,7 +459,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/application.fam b/applications/external/game_2048/application.fam index 6be28fafd..d78cdfd27 100644 --- a/applications/external/game_2048/application.fam +++ b/applications/external/game_2048/application.fam @@ -1,4 +1,3 @@ -# PLUGIN BY eugene-kirzhanov App( appid="game_2048", name="2048", @@ -8,10 +7,9 @@ App( "gui", ], stack_size=1 * 1024, - order=90, fap_icon="game_2048.png", fap_category="Games", fap_author="@eugene-kirzhanov", - fap_version="1.0", - fap_description="2048 Game", + fap_version="1.1", + fap_description="Play the port of the 2048 game on Flipper Zero.", ) diff --git a/applications/external/game_2048/game_2048.c b/applications/external/game_2048/game_2048.c index 430bc5b5d..48d0200ec 100644 --- a/applications/external/game_2048/game_2048.c +++ b/applications/external/game_2048/game_2048.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "digits.h" #include "array_utils.h" @@ -394,7 +395,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/game_of_life/application.fam b/applications/external/game_of_life/application.fam index 0037dbe4e..a4de9ff40 100644 --- a/applications/external/game_of_life/application.fam +++ b/applications/external/game_of_life/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_GAMEOFLIFE_GAME"], requires=["gui"], stack_size=2 * 1024, - order=110, fap_icon="golIcon.png", fap_category="Games", fap_author="@tgxn (original by @itsyourbedtime)", diff --git a/applications/external/geiger/flipper_geiger.c b/applications/external/geiger/flipper_geiger.c index a9101a277..58ab08b0e 100644 --- a/applications/external/geiger/flipper_geiger.c +++ b/applications/external/geiger/flipper_geiger.c @@ -10,6 +10,7 @@ #include #include #include + #include #include @@ -35,48 +36,118 @@ typedef struct { typedef struct { FuriMutex* mutex; uint32_t cps, cpm; - uint32_t line[SCREEN_SIZE_X / 2]; + uint32_t line[SCREEN_SIZE_X]; float coef; uint8_t data; + uint8_t zoom; + uint8_t newLinePosition; + uint8_t version; } mutexStruct; static void draw_callback(Canvas* canvas, void* ctx) { furi_assert(ctx); - mutexStruct displayStruct; - mutexStruct* geigerMutex = ctx; - furi_mutex_acquire(geigerMutex->mutex, FuriWaitForever); - memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct)); - furi_mutex_release(geigerMutex->mutex); + mutexStruct* mutexVal = ctx; + mutexStruct mutexDraw; + furi_mutex_acquire(mutexVal->mutex, FuriWaitForever); + memcpy(&mutexDraw, mutexVal, sizeof(mutexStruct)); + furi_mutex_release(mutexVal->mutex); - char buffer[32]; - 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", - displayStruct.cps, - ((double)displayStruct.cpm * (double)CONVERSION_FACTOR)); - else - snprintf( - buffer, - sizeof(buffer), - "%ld cps - %.2f mSv/y", - displayStruct.cps, - (((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); + if(mutexDraw.version == 0) { + char buffer[32]; + if(mutexDraw.data == 0) + snprintf(buffer, sizeof(buffer), "%ld cps - %ld cpm", mutexDraw.cps, mutexDraw.cpm); + else if(mutexDraw.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); + else + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f uR/h", + mutexDraw.cps, + ((double)mutexDraw.cpm * (double)CONVERSION_FACTOR) * (double)100); - for(int i = 0; i < SCREEN_SIZE_X; i += 2) { - float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, buffer); - canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y); - canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y); + uint8_t linePosition = mutexDraw.newLinePosition; + + if(mutexDraw.zoom == 0) { + for(int i = 0; i < SCREEN_SIZE_X; i += 8) { + if(linePosition != 0) + linePosition--; + else + linePosition = SCREEN_SIZE_X - 1; + + float Y = SCREEN_SIZE_Y - (mutexDraw.line[linePosition] * mutexDraw.coef); + for(int j = 0; j < 8; j++) + canvas_draw_line(canvas, i + j, Y, i + j, SCREEN_SIZE_Y); + } + } else if(mutexDraw.zoom == 1) { + for(int i = 0; i < SCREEN_SIZE_X; i += 4) { + if(linePosition != 0) + linePosition--; + else + linePosition = SCREEN_SIZE_X - 1; + + float Y = SCREEN_SIZE_Y - (mutexDraw.line[linePosition] * mutexDraw.coef); + for(int j = 0; j < 4; j++) + canvas_draw_line(canvas, i + j, Y, i + j, SCREEN_SIZE_Y); + } + } else if(mutexDraw.zoom == 2) { + for(int i = 0; i < SCREEN_SIZE_X; i += 2) { + if(linePosition != 0) + linePosition--; + else + linePosition = SCREEN_SIZE_X - 1; + + float Y = SCREEN_SIZE_Y - (mutexDraw.line[linePosition] * mutexDraw.coef); + for(int j = 0; j < 2; j++) + canvas_draw_line(canvas, i + j, Y, i + j, SCREEN_SIZE_Y); + } + } else if(mutexDraw.zoom == 3) { + for(int i = 0; i < SCREEN_SIZE_X; i++) { + if(linePosition != 0) + linePosition--; + else + linePosition = SCREEN_SIZE_X - 1; + + float Y = SCREEN_SIZE_Y - (mutexDraw.line[linePosition] * mutexDraw.coef); + canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y); + } + } + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, "Geiger Counter"); + canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignBottom, "Version 20230806"); + canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignBottom, "github.com/nmrr"); } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, buffer); } static void input_callback(InputEvent* input_event, void* ctx) { @@ -107,8 +178,7 @@ static void gpiocallback(void* ctx) { furi_message_queue_put(queue, &event, 0); } -int32_t flipper_geiger_app(void* p) { - UNUSED(p); +int32_t flipper_geiger_app() { EventApp event; FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); @@ -118,9 +188,12 @@ int32_t flipper_geiger_app(void* p) { mutexStruct mutexVal; mutexVal.cps = 0; mutexVal.cpm = 0; - for(int i = 0; i < SCREEN_SIZE_X / 2; i++) mutexVal.line[i] = 0; + for(int i = 0; i < SCREEN_SIZE_X; i++) mutexVal.line[i] = 0; mutexVal.coef = 1; mutexVal.data = 0; + mutexVal.zoom = 2; + mutexVal.newLinePosition = 0; + mutexVal.version = 0; uint32_t counter = 0; @@ -131,7 +204,7 @@ int32_t flipper_geiger_app(void* p) { } ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, draw_callback, &mutexVal); + view_port_draw_callback_set(view_port, draw_callback, &mutexVal.mutex); view_port_input_callback_set(view_port, input_callback, event_queue); furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue); @@ -166,15 +239,16 @@ int32_t flipper_geiger_app(void* p) { if(event_status == FuriStatusOk) { if(event.type == EventTypeInput) { - if(event.input.key == InputKeyBack) { + if(event.input.key == InputKeyBack && event.input.type == InputTypeLong) { break; - } else if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) { + } else if(event.input.key == InputKeyOk && event.input.type == InputTypeLong) { counter = 0; furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); mutexVal.cps = 0; mutexVal.cpm = 0; - for(int i = 0; i < SCREEN_SIZE_X / 2; i++) mutexVal.line[i] = 0; + for(uint8_t i = 0; i < SCREEN_SIZE_X; i++) mutexVal.line[i] = 0; + mutexVal.newLinePosition = 0; screenRefresh = 1; furi_mutex_release(mutexVal.mutex); @@ -215,7 +289,7 @@ int32_t flipper_geiger_app(void* p) { if(mutexVal.data != 0) mutexVal.data--; else - mutexVal.data = 2; + mutexVal.data = 5; screenRefresh = 1; furi_mutex_release(mutexVal.mutex); @@ -223,35 +297,61 @@ int32_t flipper_geiger_app(void* p) { event.input.type == InputTypeShort)) { furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - if(mutexVal.data != 2) + if(mutexVal.data != 5) mutexVal.data++; else mutexVal.data = 0; + screenRefresh = 1; + furi_mutex_release(mutexVal.mutex); + } else if((event.input.key == InputKeyUp && event.input.type == InputTypeShort)) { + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); + if(mutexVal.zoom != 0) mutexVal.zoom--; + + screenRefresh = 1; + furi_mutex_release(mutexVal.mutex); + + } else if((event.input.key == InputKeyDown && + event.input.type == InputTypeShort)) { + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); + if(mutexVal.zoom != 3) mutexVal.zoom++; + + screenRefresh = 1; + furi_mutex_release(mutexVal.mutex); + } else if((event.input.key == InputKeyDown && event.input.type == InputTypeLong)) { + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); + if(mutexVal.version == 0) + mutexVal.version = 1; + else + mutexVal.version = 0; + screenRefresh = 1; furi_mutex_release(mutexVal.mutex); } } else if(event.type == ClockEventTypeTick) { - furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - if(recordData == 1) { furi_string_printf(dataString, "%lu,%lu\n", epoch++, counter); stream_write_string(file_stream, dataString); } - for(int i = 0; i < SCREEN_SIZE_X / 2 - 1; i++) - mutexVal.line[SCREEN_SIZE_X / 2 - 1 - i] = - mutexVal.line[SCREEN_SIZE_X / 2 - 2 - i]; + furi_mutex_acquire(mutexVal.mutex, FuriWaitForever); - mutexVal.line[0] = counter; + mutexVal.line[mutexVal.newLinePosition] = counter; mutexVal.cps = counter; counter = 0; - mutexVal.cpm = mutexVal.line[0]; - uint32_t max = mutexVal.line[0]; - for(int i = 1; i < SCREEN_SIZE_X / 2; i++) { - if(i < 60) mutexVal.cpm += mutexVal.line[i]; - if(mutexVal.line[i] > max) max = mutexVal.line[i]; + mutexVal.cpm = mutexVal.line[mutexVal.newLinePosition]; + uint32_t max = mutexVal.line[mutexVal.newLinePosition]; + uint8_t linePosition = mutexVal.newLinePosition; + + for(int i = 1; i < SCREEN_SIZE_X; i++) { + if(linePosition != 0) + linePosition--; + else + linePosition = SCREEN_SIZE_X - 1; + + if(i < 60) mutexVal.cpm += mutexVal.line[linePosition]; + if(mutexVal.line[linePosition] > max) max = mutexVal.line[linePosition]; } if(max > 0) @@ -259,6 +359,11 @@ int32_t flipper_geiger_app(void* p) { else mutexVal.coef = 1; + if(mutexVal.newLinePosition != SCREEN_SIZE_X - 1) + mutexVal.newLinePosition++; + else + mutexVal.newLinePosition = 0; + screenRefresh = 1; furi_mutex_release(mutexVal.mutex); } else if(event.type == EventGPIO) { diff --git a/applications/external/gpioreader_b/application.fam b/applications/external/gpioreader_b/application.fam index 29fe701b6..3e38301ba 100644 --- a/applications/external/gpioreader_b/application.fam +++ b/applications/external/gpioreader_b/application.fam @@ -5,7 +5,6 @@ App( entry_point="gpio_app", requires=["gui"], stack_size=1 * 1024, - order=50, fap_category="GPIO", fap_icon="icon.png", fap_icon_assets="icons", diff --git a/applications/external/gps_nmea_uart/application.fam b/applications/external/gps_nmea_uart/application.fam index c3897e38a..1bc46d535 100644 --- a/applications/external/gps_nmea_uart/application.fam +++ b/applications/external/gps_nmea_uart/application.fam @@ -5,7 +5,6 @@ App( entry_point="gps_app", requires=["gui"], stack_size=1 * 1024, - order=35, fap_icon="gps_10px.png", fap_category="GPIO", fap_author="@ezod & @xMasterX", diff --git a/applications/external/gps_nmea_uart/gps_uart.c b/applications/external/gps_nmea_uart/gps_uart.c index d44cf22ef..c7220da51 100644 --- a/applications/external/gps_nmea_uart/gps_uart.c +++ b/applications/external/gps_nmea_uart/gps_uart.c @@ -20,15 +20,24 @@ static void gps_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { } static void gps_uart_serial_init(GpsUart* gps_uart) { - furi_hal_console_disable(); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, gps_uart_on_irq_cb, gps_uart); - furi_hal_uart_set_br(FuriHalUartIdUSART1, gps_uart->baudrate); + if(UART_CH == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH, gps_uart->baudrate); + } + + furi_hal_uart_set_irq_cb(UART_CH, gps_uart_on_irq_cb, gps_uart); + furi_hal_uart_set_br(UART_CH, gps_uart->baudrate); } static void gps_uart_serial_deinit(GpsUart* gps_uart) { UNUSED(gps_uart); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); - furi_hal_console_enable(); + furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); + if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH); + } else { + furi_hal_console_enable(); + } } static void gps_uart_parse_nmea(GpsUart* gps_uart, char* line) { diff --git a/applications/external/gps_nmea_uart/gps_uart.h b/applications/external/gps_nmea_uart/gps_uart.h index 152f4cd7f..cddc37b17 100644 --- a/applications/external/gps_nmea_uart/gps_uart.h +++ b/applications/external/gps_nmea_uart/gps_uart.h @@ -2,11 +2,16 @@ #include #include +#include + +#define UART_CH \ + (XTREME_SETTINGS()->uart_nmea_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) #define RX_BUF_SIZE 1024 static const int gps_baudrates[5] = {9600, 19200, 38400, 57600, 115200}; -static int current_gps_baudrate = 3; +static int current_gps_baudrate = 0; typedef struct { bool valid; diff --git a/applications/external/hc_sr04/application.fam b/applications/external/hc_sr04/application.fam index 7bbbaa5be..cbe135194 100644 --- a/applications/external/hc_sr04/application.fam +++ b/applications/external/hc_sr04/application.fam @@ -7,7 +7,6 @@ App( "gui", ], stack_size=2 * 1024, - order=20, fap_icon="dist_sensor10px.png", fap_category="GPIO", fap_author="@xMasterX (first implementation by @Sanqui)", diff --git a/applications/external/heap_defence_game/heap_defence.c b/applications/external/heap_defence_game/heap_defence.c index c88c3e5cb..111c22dce 100644 --- a/applications/external/heap_defence_game/heap_defence.c +++ b/applications/external/heap_defence_game/heap_defence.c @@ -14,6 +14,7 @@ #include #include #include +#include #define Y_FIELD_SIZE 6 #define Y_LAST (Y_FIELD_SIZE - 1) @@ -533,7 +534,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/hex_editor/application.fam b/applications/external/hex_editor/application.fam index fa7c3f13e..8f8d8c26e 100644 --- a/applications/external/hex_editor/application.fam +++ b/applications/external/hex_editor/application.fam @@ -9,7 +9,6 @@ App( "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_viewer/application.fam b/applications/external/hex_viewer/application.fam index be8a7a0dd..821eead69 100644 --- a/applications/external/hex_viewer/application.fam +++ b/applications/external/hex_viewer/application.fam @@ -8,11 +8,10 @@ App( "dialogs", ], stack_size=2 * 1024, - order=20, fap_icon="icons/hex_10px.png", fap_category="Tools", fap_icon_assets="icons", fap_author="@QtRoS", - fap_version="1.0", + fap_version="1.1", fap_description="App allows to view various files as HEX.", ) diff --git a/applications/external/hex_viewer/hex_viewer.c b/applications/external/hex_viewer/hex_viewer.c index 58fedb341..b98f2d867 100644 --- a/applications/external/hex_viewer/hex_viewer.c +++ b/applications/external/hex_viewer/hex_viewer.c @@ -1,7 +1,8 @@ #include #include -#include +#include "hex_viewer_icons.h" +#include #include #include #include @@ -163,7 +164,7 @@ static bool hex_viewer_open_file(HexViewer* hex_viewer, const char* file_path) { FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); isOk = false; break; - } + }; hex_viewer->model->file_size = stream_size(hex_viewer->model->stream); } while(false); diff --git a/applications/external/hid_app/hid.c b/applications/external/hid_app/hid.c index 7b136e63f..b6a866718 100644 --- a/applications/external/hid_app/hid.c +++ b/applications/external/hid_app/hid.c @@ -1,7 +1,6 @@ #include "hid.h" #include "views.h" #include -#include #define TAG "HidApp" @@ -404,8 +403,6 @@ int32_t hid_usb_app(void* p) { bt_hid_connection_status_changed_callback(BtStatusConnected, app); - dolphin_deed(DolphinDeedPluginStart); - view_dispatcher_run(app->view_dispatcher); furi_hal_usb_set_config(usb_mode_prev, NULL); @@ -444,8 +441,6 @@ 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); - view_dispatcher_run(app->view_dispatcher); bt_set_status_changed_callback(app->bt, NULL, NULL); diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h index 2b5a8059b..c21d3e265 100644 --- a/applications/external/hid_app/hid.h +++ b/applications/external/hid_app/hid.h @@ -25,9 +25,6 @@ #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 1c1525ec9..c41c53797 100644 --- a/applications/external/hid_app/views/hid_keyboard.c +++ b/applications/external/hid_app/views/hid_keyboard.c @@ -3,6 +3,8 @@ #include #include #include "../hid.h" +#include "hid_icons.h" +#include #define TAG "HidKeyboard" diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c index e3d5f7a99..ce21bc56b 100644 --- a/applications/external/hid_app/views/hid_keynote.c +++ b/applications/external/hid_app/views/hid_keynote.c @@ -2,6 +2,9 @@ #include #include "../hid.h" +#include "hid_icons.h" +#include + #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 caf854eb1..037f251fe 100644 --- a/applications/external/hid_app/views/hid_media.c +++ b/applications/external/hid_app/views/hid_media.c @@ -5,6 +5,9 @@ #include #include "../hid.h" +#include "hid_icons.h" +#include + #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 4c4737155..3fd364ef9 100644 --- a/applications/external/hid_app/views/hid_mouse.c +++ b/applications/external/hid_app/views/hid_mouse.c @@ -2,6 +2,9 @@ #include #include "../hid.h" +#include "hid_icons.h" +#include + #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 6777034ff..b0269738e 100644 --- a/applications/external/hid_app/views/hid_mouse_clicker.c +++ b/applications/external/hid_app/views/hid_mouse_clicker.c @@ -2,6 +2,9 @@ #include #include "../hid.h" +#include "hid_icons.h" +#include + #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 c2ff93f60..bc71b7da6 100644 --- a/applications/external/hid_app/views/hid_mouse_jiggler.c +++ b/applications/external/hid_app/views/hid_mouse_jiggler.c @@ -2,6 +2,9 @@ #include #include "../hid.h" +#include "hid_icons.h" +#include + #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 b342bb38d..1d0c62769 100644 --- a/applications/external/hid_app/views/hid_numpad.c +++ b/applications/external/hid_app/views/hid_numpad.c @@ -3,6 +3,8 @@ #include #include #include "../hid.h" +#include "hid_icons.h" +#include #define TAG "HidNumpad" diff --git a/applications/external/hid_app/views/hid_tikshorts.c b/applications/external/hid_app/views/hid_tikshorts.c index 78e7488f3..6796b3ca6 100644 --- a/applications/external/hid_app/views/hid_tikshorts.c +++ b/applications/external/hid_app/views/hid_tikshorts.c @@ -2,6 +2,9 @@ #include "../hid.h" #include +#include "hid_icons.h" +#include + #define TAG "HidTikShorts" struct HidTikShorts { diff --git a/applications/external/ifttt/application.fam b/applications/external/ifttt/application.fam index 1a86000a4..2f8871e03 100644 --- a/applications/external/ifttt/application.fam +++ b/applications/external/ifttt/application.fam @@ -8,7 +8,6 @@ App( "gui", ], stack_size=2 * 1024, - order=20, fap_icon="icon.png", fap_category="WiFi", ) diff --git a/applications/external/ir_remote/infrared_remote_app.c b/applications/external/ir_remote/infrared_remote_app.c index c303ab2d3..bc27c2149 100644 --- a/applications/external/ir_remote/infrared_remote_app.c +++ b/applications/external/ir_remote/infrared_remote_app.c @@ -8,7 +8,6 @@ #include #include "ir_remote_icons.h" #include -#include #include #include @@ -113,7 +112,6 @@ static void app_input_callback(InputEvent* input_event, void* ctx) { int32_t infrared_remote_app(char* p) { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - dolphin_deed(DolphinDeedPluginStart); // App button string IRApp* app = malloc(sizeof(IRApp)); diff --git a/applications/external/ir_scope/application.fam b/applications/external/ir_scope/application.fam index 00e161d97..ceaa629f7 100644 --- a/applications/external/ir_scope/application.fam +++ b/applications/external/ir_scope/application.fam @@ -3,12 +3,11 @@ App( name="IR Scope", apptype=FlipperAppType.EXTERNAL, entry_point="ir_scope_app", - cdefines=["APP_IR_SCOPE"], requires=["gui"], stack_size=2 * 1024, fap_icon="ir_scope.png", fap_category="Infrared", fap_author="@kallanreed", - fap_version="1.0", + fap_version="1.2", fap_description="App allows to see incoming IR signals.", ) diff --git a/applications/external/jetpack_joyride/application.fam b/applications/external/jetpack_joyride/application.fam index 1b98e11ce..bb4eaa8fb 100644 --- a/applications/external/jetpack_joyride/application.fam +++ b/applications/external/jetpack_joyride/application.fam @@ -8,7 +8,6 @@ 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/includes/background_assets.h b/applications/external/jetpack_joyride/includes/background_assets.h index d42fcfd71..21721f03c 100644 --- a/applications/external/jetpack_joyride/includes/background_assets.h +++ b/applications/external/jetpack_joyride/includes/background_assets.h @@ -9,7 +9,8 @@ #include "point.h" #include "states.h" #include "game_sprites.h" -#include +#include "jetpack_joyride_icons.h" +#include #define BG_ASSETS_MAX 3 diff --git a/applications/external/jetpack_joyride/includes/coin.c b/applications/external/jetpack_joyride/includes/coin.c index 7a3811a8c..485e55f1b 100644 --- a/applications/external/jetpack_joyride/includes/coin.c +++ b/applications/external/jetpack_joyride/includes/coin.c @@ -1,7 +1,8 @@ #include #include -#include +#include "jetpack_joyride_icons.h" +#include #include #include "coin.h" diff --git a/applications/external/jetpack_joyride/includes/missile.c b/applications/external/jetpack_joyride/includes/missile.c index af47e8478..8302973a5 100644 --- a/applications/external/jetpack_joyride/includes/missile.c +++ b/applications/external/jetpack_joyride/includes/missile.c @@ -1,7 +1,8 @@ #include #include -#include +#include "jetpack_joyride_icons.h" +#include #include #include "states.h" diff --git a/applications/external/jetpack_joyride/includes/scientist.c b/applications/external/jetpack_joyride/includes/scientist.c index b1a8a14c0..d831b78db 100644 --- a/applications/external/jetpack_joyride/includes/scientist.c +++ b/applications/external/jetpack_joyride/includes/scientist.c @@ -1,7 +1,8 @@ #include "scientist.h" #include "game_sprites.h" -#include +#include "jetpack_joyride_icons.h" +#include #include void scientist_tick(SCIENTIST* const scientists) { diff --git a/applications/external/jetpack_joyride/jetpack.c b/applications/external/jetpack_joyride/jetpack.c index 7969300bd..37443ee90 100644 --- a/applications/external/jetpack_joyride/jetpack.c +++ b/applications/external/jetpack_joyride/jetpack.c @@ -1,6 +1,7 @@ #include -#include +#include "jetpack_joyride_icons.h" +#include #include #include #include @@ -370,4 +371,4 @@ free_and_exit: furi_message_queue_free(event_queue); return return_code; -} +} \ No newline at end of file diff --git a/applications/external/lightmeter/application.fam b/applications/external/lightmeter/application.fam index a1ad9fd0e..83e14b543 100644 --- a/applications/external/lightmeter/application.fam +++ b/applications/external/lightmeter/application.fam @@ -7,7 +7,6 @@ App( "gui", ], stack_size=4 * 1024, - order=90, fap_version=(1, 2), fap_icon="lightmeter.png", fap_category="GPIO", diff --git a/applications/external/lightmeter/docs/README.md b/applications/external/lightmeter/docs/README.md deleted file mode 100644 index e90630c03..000000000 --- a/applications/external/lightmeter/docs/README.md +++ /dev/null @@ -1,17 +0,0 @@ -## Lightmeter app for photography - -An application that suggests settings for your manual camera based on the reading of the ambient light sensor. Can also be used in a pure lux meter mode. - -## Supported sensors - -- BH1750 -- MAX44009 - -## Wiring - -| Sensor | Flipper Zero | -| ------ | ------------ | -| VCC | 3.3V | -| GND | GND | -| SCL | C0 | -| SDA | C1 | diff --git a/applications/external/lightmeter/docs/changelog.md b/applications/external/lightmeter/docs/changelog.md deleted file mode 100644 index a891921c7..000000000 --- a/applications/external/lightmeter/docs/changelog.md +++ /dev/null @@ -1,15 +0,0 @@ -## v1.2 - -* Lux only screen now has statistics -* Settings are now stored on SD card -* You can choose the resolution (BH1750 only) and address for sensor - -(thanks to @danielskowronski for contributing to this update) - -## v1.1 - -Added support for MAX44009 sensor (thanks to @wosk) - -## v1.0 - -Initial release for Flipper Application Catalog diff --git a/applications/external/magspoof/LICENSE b/applications/external/magspoof/LICENSE new file mode 100644 index 000000000..ac4d2d21a --- /dev/null +++ b/applications/external/magspoof/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Zachary Weiss + +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/magspoof/application.fam b/applications/external/magspoof/application.fam new file mode 100644 index 000000000..dd764c584 --- /dev/null +++ b/applications/external/magspoof/application.fam @@ -0,0 +1,20 @@ +App( + appid="magspoof", + name="[MAG] MagSpoof", + apptype=FlipperAppType.EXTERNAL, + entry_point="mag_app", + requires=[ + "gui", + "storage", + "notification", + "dialogs", + ], + stack_size=6 * 1024, + fap_icon="icons/mag_10px.png", + fap_category="GPIO", + fap_icon_assets="icons", + fap_version=(0, 5), # major, minor + fap_description="WIP MagSpoof port using the RFID subsystem", + fap_author="Zachary Weiss", + fap_weburl="https://github.com/zacharyweiss/magspoof_flipper", +) diff --git a/applications/external/magspoof/helpers/mag_helpers.c b/applications/external/magspoof/helpers/mag_helpers.c new file mode 100644 index 000000000..89c451f62 --- /dev/null +++ b/applications/external/magspoof/helpers/mag_helpers.c @@ -0,0 +1,479 @@ +#include "mag_helpers.h" + +#define TAG "MagHelpers" + +// Haviv Board - pins gpio_ext_pa7 & gpio_ext_pa6 was swapped. +#define GPIO_PIN_A &gpio_ext_pa7 +#define GPIO_PIN_B &gpio_ext_pa6 +#define GPIO_PIN_ENABLE &gpio_ext_pa4 +#define RFID_PIN_OUT &gpio_rfid_carrier_out + +#define ZERO_PREFIX 25 // n zeros prefix +#define ZERO_BETWEEN 53 // n zeros between tracks +#define ZERO_SUFFIX 25 // n zeros suffix + +// bits per char on a given track +const uint8_t bitlen[] = {7, 5, 5}; +// char offset by track +const int sublen[] = {32, 48, 48}; + +uint8_t last_value = 2; + +void play_halfbit(bool value, MagSetting* setting) { + switch(setting->tx) { + case MagTxStateRFID: + furi_hal_gpio_write(RFID_PIN_OUT, value); + /*furi_hal_gpio_write(RFID_PIN_OUT, !value); + furi_hal_gpio_write(RFID_PIN_OUT, value); + furi_hal_gpio_write(RFID_PIN_OUT, !value); + furi_hal_gpio_write(RFID_PIN_OUT, value);*/ + break; + case MagTxStateGPIO: + furi_hal_gpio_write(GPIO_PIN_A, value); + furi_hal_gpio_write(GPIO_PIN_B, !value); + break; + case MagTxStatePiezo: + furi_hal_gpio_write(&gpio_speaker, value); + /*furi_hal_gpio_write(&gpio_speaker, !value); + furi_hal_gpio_write(&gpio_speaker, value); + furi_hal_gpio_write(&gpio_speaker, !value); + furi_hal_gpio_write(&gpio_speaker, value);*/ + + break; + case MagTxStateLF_P: + furi_hal_gpio_write(RFID_PIN_OUT, value); + furi_hal_gpio_write(&gpio_speaker, value); + + /* // Weaker but cleaner signal + if(value) { + furi_hal_gpio_write(RFID_PIN_OUT, value); + furi_hal_gpio_write(&gpio_speaker, value); + furi_delay_us(10); + furi_hal_gpio_write(RFID_PIN_OUT, !value); + furi_hal_gpio_write(&gpio_speaker, !value); + } else { + furi_delay_us(10); + }*/ + + /*furi_hal_gpio_write(RFID_PIN_OUT, value); + furi_hal_gpio_write(&gpio_speaker, value); + furi_hal_gpio_write(RFID_PIN_OUT, !value); + furi_hal_gpio_write(&gpio_speaker, !value); + furi_hal_gpio_write(RFID_PIN_OUT, value); + furi_hal_gpio_write(&gpio_speaker, value);*/ + break; + case MagTxStateNFC: + // turn on for duration of half-bit? or "blip" the field on / off? + // getting nothing from the mag reader either way + //(value) ? furi_hal_nfc_ll_txrx_on() : furi_hal_nfc_ll_txrx_off(); + + if(last_value == 2 || value != (bool)last_value) { + furi_hal_nfc_ll_txrx_on(); + //furi_delay_us(64); + furi_hal_nfc_ll_txrx_off(); + } + break; + case MagTxCC1101_434: + case MagTxCC1101_868: + if(last_value == 2 || value != (bool)last_value) { + furi_hal_gpio_write(&gpio_cc1101_g0, true); + furi_delay_us(64); + furi_hal_gpio_write(&gpio_cc1101_g0, false); + } + break; + default: + break; + } + + last_value = value; +} + +void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting, bool reverse) { + for(uint16_t i = 0; i < n_bits; i++) { + uint16_t j = (reverse) ? (n_bits - i - 1) : i; + uint8_t byte = j / 8; + uint8_t bitmask = 1 << (7 - (j % 8)); + /* Bits are stored in their arrays like on a card (LSB first). This is not how usually bits are stored in a + * byte, with the MSB first. the var bitmask creates the pattern to iterate through each bit, LSB first, like so + * 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x80... masking bits one by one from the current byte + * + * I've chosen this LSB approach since bits and bytes are hard enough to visualize with the 5/8 and 7/8 encoding + * MSR uses. It's a biiit more complicated to process, but visualizing it with printf or a debugger is + * infinitely easier + * + * Encoding the following pairs of 5 bits as 5/8: A1234 B1234 C1234 D1234 + * using this LSB format looks like: A1234B12 34C1234D 12340000 + * using the MSB format, looks like: 21B4321A D4321C43 00004321 + * this means reading each byte backwards when printing/debugging, and the jumping 16 bits ahead, reading 8 more + * bits backward, jumping 16 more bits ahead. + * + * I find this much more convenient for debugging, with the tiny incovenience of reading the bits in reverse + * order. Thus, the reason for the bitmask above + */ + + bool bit = !!(bits_manchester[byte] & bitmask); + + // TODO: reimplement timing delays. Replace fixed furi_hal_cortex_delay_us to wait instead to a specific value + // for DWT->CYCCNT. Note timer is aliased to 64us as per + // #define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) | furi_hal_cortex.c + + play_halfbit(bit, setting); + furi_delay_us(setting->us_clock); + // if (i % 2 == 1) furi_delay_us(setting->us_interpacket); + } +} + +void tx_init_rfid() { + // initialize RFID system for TX + + furi_hal_ibutton_pin_configure(); + + // furi_hal_ibutton_start_drive(); + furi_hal_ibutton_pin_write(false); + + // Initializing at GpioSpeedLow seems sufficient for our needs; no improvements seen by increasing speed setting + + // this doesn't seem to make a difference, leaving it in + furi_hal_gpio_init(&gpio_rfid_data_in, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_rfid_data_in, false); + + // false->ground RFID antenna; true->don't ground + // skotopes (RFID dev) say normally you'd want RFID_PULL in high for signal forming, while modulating RFID_OUT + // dunaevai135 had it low in their old code. Leaving low, as it doesn't seem to make a difference on my janky antenna + furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_nfc_irq_rfid_pull, false); + + furi_hal_gpio_init(RFID_PIN_OUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + + furi_delay_ms(300); +} + +void tx_deinit_rfid() { + // reset RFID system + furi_hal_gpio_write(RFID_PIN_OUT, 0); + + furi_hal_rfid_pins_reset(); +} + +void tx_init_rf(int hz) { + // presets and frequency will need some experimenting + furi_hal_subghz_reset(); + // furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + // furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); + // furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); + // furi_hal_subghz_load_preset(FuriHalSubGhzPreset2FSKDev238Async); + // furi_hal_subghz_load_preset(FuriHalSubGhzPreset2FSKDev476Async); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_subghz_set_frequency_and_path(hz); + furi_hal_subghz_tx(); + furi_hal_gpio_write(&gpio_cc1101_g0, false); +} + +void tx_init_piezo() { + // TODO: some special mutex acquire procedure? c.f. furi_hal_speaker.c + furi_hal_gpio_init(&gpio_speaker, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); +} + +void tx_deinit_piezo() { + // TODO: some special mutex release procedure? + furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +bool tx_init(MagSetting* setting) { + // Initialize configured TX method + switch(setting->tx) { + case MagTxStateRFID: + tx_init_rfid(); + break; + case MagTxStateGPIO: + // gpio_item_configure_all_pins(GpioModeOutputPushPull); + furi_hal_gpio_init(GPIO_PIN_A, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(GPIO_PIN_B, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(GPIO_PIN_ENABLE, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + + furi_hal_gpio_write(GPIO_PIN_ENABLE, 1); + + // had some issues with ~300; bumped higher temporarily + furi_delay_ms(500); + break; + case MagTxStatePiezo: + tx_init_piezo(); + break; + case MagTxStateLF_P: + tx_init_piezo(); + tx_init_rfid(); + break; + case MagTxStateNFC: + furi_hal_nfc_exit_sleep(); + break; + case MagTxCC1101_434: + tx_init_rf(434000000); + break; + case MagTxCC1101_868: + tx_init_rf(868000000); + break; + default: + return false; + } + + return true; +} + +bool tx_deinit(MagSetting* setting) { + // Reset configured TX method + switch(setting->tx) { + case MagTxStateRFID: + tx_deinit_rfid(); + break; + case MagTxStateGPIO: + furi_hal_gpio_write(GPIO_PIN_A, 0); + furi_hal_gpio_write(GPIO_PIN_B, 0); + furi_hal_gpio_write(GPIO_PIN_ENABLE, 0); + + // set back to analog output mode? - YES + furi_hal_gpio_init(GPIO_PIN_A, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(GPIO_PIN_B, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(GPIO_PIN_ENABLE, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + //gpio_item_configure_all_pins(GpioModeAnalog); + break; + case MagTxStatePiezo: + tx_deinit_piezo(); + break; + case MagTxStateLF_P: + tx_deinit_piezo(); + tx_deinit_rfid(); + break; + case MagTxStateNFC: + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_start_sleep(); + break; + case MagTxCC1101_434: + case MagTxCC1101_868: + furi_hal_gpio_write(&gpio_cc1101_g0, false); + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + break; + default: + return false; + } + + return true; +} + +void mag_spoof(Mag* mag) { + MagSetting* setting = mag->setting; + + // TODO: cleanup this section. Possibly move precompute + tx_init to emulate_on_enter? + FuriString* ft1 = mag->mag_dev->dev_data.track[0].str; + FuriString* ft2 = mag->mag_dev->dev_data.track[1].str; + FuriString* ft3 = mag->mag_dev->dev_data.track[2].str; + + char *data1, *data2, *data3; + data1 = malloc(furi_string_size(ft1) + 1); + data2 = malloc(furi_string_size(ft2) + 1); + data3 = malloc(furi_string_size(ft3) + 1); + strncpy(data1, furi_string_get_cstr(ft1), furi_string_size(ft1)); + strncpy(data2, furi_string_get_cstr(ft2), furi_string_size(ft2)); + strncpy(data3, furi_string_get_cstr(ft3), furi_string_size(ft3)); + + if(furi_log_get_level() >= FuriLogLevelDebug) { + debug_mag_string(data1, bitlen[0], sublen[0]); + debug_mag_string(data2, bitlen[1], sublen[1]); + debug_mag_string(data3, bitlen[2], sublen[2]); + } + + uint8_t bits_t1_raw[64] = {0x00}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits + uint8_t bits_t1_manchester[128] = {0x00}; // twice the above + uint16_t bits_t1_count = mag_encode( + data1, (uint8_t*)bits_t1_manchester, (uint8_t*)bits_t1_raw, bitlen[0], sublen[0]); + uint8_t bits_t2_raw[64] = {0x00}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits + uint8_t bits_t2_manchester[128] = {0x00}; // twice the above + uint16_t bits_t2_count = mag_encode( + data2, (uint8_t*)bits_t2_manchester, (uint8_t*)bits_t2_raw, bitlen[1], sublen[1]); + uint8_t bits_t3_raw[64] = {0x00}; + uint8_t bits_t3_manchester[128] = {0x00}; + uint16_t bits_t3_count = mag_encode( + data3, (uint8_t*)bits_t3_manchester, (uint8_t*)bits_t3_raw, bitlen[2], sublen[2]); + + if(furi_log_get_level() >= FuriLogLevelDebug) { + printf( + "Manchester bitcount: T1: %d, T2: %d, T3: %d\r\n", + bits_t1_count, + bits_t2_count, + bits_t3_count); + printf("T1 raw: "); + for(int i = 0; i < bits_t1_count / 16; i++) printf("%02x ", bits_t1_raw[i]); + printf("\r\nT1 manchester: "); + for(int i = 0; i < bits_t1_count / 8; i++) printf("%02x ", bits_t1_manchester[i]); + printf("\r\nT2 raw: "); + for(int i = 0; i < bits_t2_count / 16; i++) printf("%02x ", bits_t2_raw[i]); + printf("\r\nT2 manchester: "); + for(int i = 0; i < bits_t2_count / 8; i++) printf("%02x ", bits_t2_manchester[i]); + printf("\r\nT3 raw: "); + for(int i = 0; i < bits_t3_count / 16; i++) printf("%02x ", bits_t3_raw[i]); + printf("\r\nT3 manchester: "); + for(int i = 0; i < bits_t3_count / 8; i++) printf("%02x ", bits_t3_manchester[i]); + printf("\r\nBitwise emulation done\r\n\r\n"); + } + + last_value = 2; + bool bit = false; + + if(!tx_init(setting)) return; + + FURI_CRITICAL_ENTER(); + for(uint16_t i = 0; i < (ZERO_PREFIX * 2); i++) { + // is this right? + if(!!(i % 2)) bit ^= 1; + play_halfbit(bit, setting); + furi_delay_us(setting->us_clock); + } + + if((setting->track == MagTrackStateOneAndTwo) || (setting->track == MagTrackStateOne)) + play_track((uint8_t*)bits_t1_manchester, bits_t1_count, setting, false); + + if((setting->track == MagTrackStateOneAndTwo)) + for(uint16_t i = 0; i < (ZERO_BETWEEN * 2); i++) { + if(!!(i % 2)) bit ^= 1; + play_halfbit(bit, setting); + furi_delay_us(setting->us_clock); + } + + if((setting->track == MagTrackStateOneAndTwo) || (setting->track == MagTrackStateTwo)) + play_track( + (uint8_t*)bits_t2_manchester, + bits_t2_count, + setting, + (setting->reverse == MagReverseStateOn)); + + if((setting->track == MagTrackStateThree)) + play_track((uint8_t*)bits_t3_manchester, bits_t3_count, setting, false); + + for(uint16_t i = 0; i < (ZERO_SUFFIX * 2); i++) { + if(!!(i % 2)) bit ^= 1; + play_halfbit(bit, setting); + furi_delay_us(setting->us_clock); + } + FURI_CRITICAL_EXIT(); + + free(data1); + free(data2); + free(data3); + tx_deinit(setting); +} + +uint16_t add_bit(bool value, uint8_t* out, uint16_t count) { + uint8_t bit = count % 8; + uint8_t byte = count / 8; + if(value) { + out[byte] |= 0x01; + } + if(bit < 7) out[byte] <<= 1; + return count + 1; +} + +uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count) { + static bool toggle = 0; + toggle ^= 0x01; + count = add_bit(toggle, out, count); + if(value) toggle ^= 0x01; + count = add_bit(toggle, out, count); + return count; +} + +uint16_t mag_encode( + char* data, + uint8_t* out_manchester, + uint8_t* out_raw, + uint8_t track_bits, + uint8_t track_ascii_offset) { + /* + * track_bits - the number of raw (data) bits on the track. on ISO cards, that's 7 for track 1, or 5 for 2/3 - this is samy's bitlen + * - this count includes the parity bit + * track_ascii_offset - how much the ascii values are offset. track 1 makes space (ascii 32) become data 0x00, + * - tracks 2/3 make ascii "0" become data 0x00 - this is samy's sublen + * + */ + + uint16_t raw_bits_count = 0; + uint16_t output_count = 0; + int tmp, crc, lrc = 0; + + /* // why are we adding zeros to the encoded string if we're also doing it while playing? + for(int i = 0; i < ZERO_PREFIX; i++) { + output_count = add_bit_manchester(0, out_manchester, output_count); + raw_bits_count = add_bit(0, out_raw, raw_bits_count); + }*/ + + for(int i = 0; *(data + i) != 0; i++) { + crc = 1; + tmp = *(data + i) - track_ascii_offset; + + for(int j = 0; j < track_bits - 1; j++) { + crc ^= tmp & 1; + lrc ^= (tmp & 1) << j; + raw_bits_count = add_bit(tmp & 0x01, out_raw, raw_bits_count); + output_count = add_bit_manchester(tmp & 0x01, out_manchester, output_count); + tmp >>= 1; + } + raw_bits_count = add_bit(crc, out_raw, raw_bits_count); + output_count = add_bit_manchester(crc, out_manchester, output_count); + } + + // LRC byte + tmp = lrc; + crc = 1; + for(int j = 0; j < track_bits - 1; j++) { + crc ^= tmp & 0x01; + raw_bits_count = add_bit(tmp & 0x01, out_raw, raw_bits_count); + output_count = add_bit_manchester(tmp & 0x01, out_manchester, output_count); + tmp >>= 1; + } + raw_bits_count = add_bit(crc, out_raw, raw_bits_count); + output_count = add_bit_manchester(crc, out_manchester, output_count); + + return output_count; +} + +void debug_mag_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset) { + uint8_t bits_raw[64] = {0}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits + uint8_t bits_manchester[128] = {0}; // twice the above + int numbits = 0; + + printf("Encoding [%s] with %d bits\r\n", data, track_bits); + numbits = mag_encode( + data, (uint8_t*)bits_manchester, (uint8_t*)bits_raw, track_bits, track_ascii_offset); + printf("Got %d bits\r\n", numbits); + printf("Raw byte stream: "); + for(int i = 0; i < numbits / 8 / 2; i++) { + printf("%02x", bits_raw[i]); + if(i % 4 == 3) printf(" "); + } + + printf("\r\n"); + + printf("Bits "); + int space_counter = 0; + for(int i = 0; i < numbits / 2; i++) { + /*if(i < ZERO_PREFIX) { + printf("X"); + continue; + } else if(i == ZERO_PREFIX) { + printf(" "); + space_counter = 0; + }*/ + printf("%01x", (bits_raw[i / 8] & (1 << (7 - (i % 8)))) != 0); + if((space_counter) % track_bits == track_bits - 1) printf(" "); + space_counter++; + } + + printf("\r\n"); + + printf("Manchester encoded, byte stream: "); + for(int i = 0; i < numbits / 8; i++) { + printf("%02x", bits_manchester[i]); + if(i % 4 == 3) printf(" "); + } + printf("\r\n\r\n"); +} diff --git a/applications/external/magspoof/helpers/mag_helpers.h b/applications/external/magspoof/helpers/mag_helpers.h new file mode 100644 index 000000000..a61f143b8 --- /dev/null +++ b/applications/external/magspoof/helpers/mag_helpers.h @@ -0,0 +1,25 @@ +#include "../mag_i.h" +#include +#include + +void play_halfbit(bool value, MagSetting* setting); +void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting, bool reverse); + +void tx_init_rf(int hz); +void tx_init_rfid(); +void tx_init_piezo(); +bool tx_init(MagSetting* setting); +void tx_deinit_piezo(); +void tx_deinit_rfid(); +bool tx_deinit(MagSetting* setting); + +uint16_t add_bit(bool value, uint8_t* out, uint16_t count); +uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count); +uint16_t mag_encode( + char* data, + uint8_t* out_manchester, + uint8_t* out_raw, + uint8_t track_bits, + uint8_t track_ascii_offset); +void debug_mag_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset); +void mag_spoof(Mag* mag); diff --git a/applications/external/magspoof/helpers/mag_types.h b/applications/external/magspoof/helpers/mag_types.h new file mode 100644 index 000000000..66a499559 --- /dev/null +++ b/applications/external/magspoof/helpers/mag_types.h @@ -0,0 +1,43 @@ +#pragma once + +#define MAG_VERSION_APP "0.05" +#define MAG_DEVELOPER "Zachary Weiss" +#define MAG_GITHUB "github.com/zacharyweiss/magspoof_flipper" + +typedef enum { + MagViewSubmenu, + MagViewDialogEx, + MagViewPopup, + MagViewLoading, + MagViewWidget, + MagViewVariableItemList, + MagViewTextInput, +} MagView; + +typedef enum { + MagReverseStateOff, + MagReverseStateOn, +} MagReverseState; + +typedef enum { + MagTrackStateOneAndTwo, + MagTrackStateOne, + MagTrackStateTwo, + MagTrackStateThree, +} MagTrackState; + +typedef enum { + MagTxStateRFID, + MagTxStateGPIO, + MagTxStatePiezo, + MagTxStateLF_P, // combo of RFID and Piezo + MagTxStateNFC, + MagTxCC1101_434, + MagTxCC1101_868, +} MagTxState; + +typedef enum { + UART_TerminalEventRefreshConsoleOutput = 0, + UART_TerminalEventStartConsole, + UART_TerminalEventStartKeyboard, +} UART_TerminalCustomEvent; diff --git a/applications/external/magspoof/icons/mag_10px.png b/applications/external/magspoof/icons/mag_10px.png new file mode 100644 index 000000000..5e4c15244 Binary files /dev/null and b/applications/external/magspoof/icons/mag_10px.png differ diff --git a/applications/external/magspoof/icons/mag_file_10px.png b/applications/external/magspoof/icons/mag_file_10px.png new file mode 100644 index 000000000..28d683d82 Binary files /dev/null and b/applications/external/magspoof/icons/mag_file_10px.png differ diff --git a/applications/external/magspoof/mag.c b/applications/external/magspoof/mag.c new file mode 100644 index 000000000..8c821b994 --- /dev/null +++ b/applications/external/magspoof/mag.c @@ -0,0 +1,248 @@ +#include "mag_i.h" + +#define TAG "Mag" + +#define SETTING_DEFAULT_REVERSE MagReverseStateOff +#define SETTING_DEFAULT_TRACK MagTrackStateOneAndTwo +#define SETTING_DEFAULT_TX_RFID MagTxStateGPIO +#define SETTING_DEFAULT_US_CLOCK 240 +#define SETTING_DEFAULT_US_INTERPACKET 10 + +static bool mag_debug_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + Mag* mag = context; + return scene_manager_handle_custom_event(mag->scene_manager, event); +} + +static bool mag_debug_back_event_callback(void* context) { + furi_assert(context); + Mag* mag = context; + return scene_manager_handle_back_event(mag->scene_manager); +} + +static MagSetting* mag_setting_alloc() { + // temp hardcoded defaults + MagSetting* setting = malloc(sizeof(MagSetting)); + setting->reverse = SETTING_DEFAULT_REVERSE; + setting->track = SETTING_DEFAULT_TRACK; + setting->tx = SETTING_DEFAULT_TX_RFID; + setting->us_clock = SETTING_DEFAULT_US_CLOCK; + setting->us_interpacket = SETTING_DEFAULT_US_INTERPACKET; + + return setting; +} + +static Mag* mag_alloc() { + Mag* mag = malloc(sizeof(Mag)); + + mag->storage = furi_record_open(RECORD_STORAGE); + mag->dialogs = furi_record_open(RECORD_DIALOGS); + + mag->file_name = furi_string_alloc(); + mag->file_path = furi_string_alloc_set(MAG_APP_FOLDER); + + mag->view_dispatcher = view_dispatcher_alloc(); + mag->scene_manager = scene_manager_alloc(&mag_scene_handlers, mag); + view_dispatcher_enable_queue(mag->view_dispatcher); + view_dispatcher_set_event_callback_context(mag->view_dispatcher, mag); + view_dispatcher_set_custom_event_callback( + mag->view_dispatcher, mag_debug_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + mag->view_dispatcher, mag_debug_back_event_callback); + + mag->mag_dev = mag_device_alloc(); + mag->setting = mag_setting_alloc(); + + // Open GUI record + mag->gui = furi_record_open(RECORD_GUI); + + // Open Notification record + mag->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Submenu + mag->submenu = submenu_alloc(); + view_dispatcher_add_view(mag->view_dispatcher, MagViewSubmenu, submenu_get_view(mag->submenu)); + + // Dialog + mag->dialog_ex = dialog_ex_alloc(); + view_dispatcher_add_view( + mag->view_dispatcher, MagViewDialogEx, dialog_ex_get_view(mag->dialog_ex)); + + // Popup + mag->popup = popup_alloc(); + view_dispatcher_add_view(mag->view_dispatcher, MagViewPopup, popup_get_view(mag->popup)); + + // Loading + mag->loading = loading_alloc(); + view_dispatcher_add_view(mag->view_dispatcher, MagViewLoading, loading_get_view(mag->loading)); + + // Widget + mag->widget = widget_alloc(); + view_dispatcher_add_view(mag->view_dispatcher, MagViewWidget, widget_get_view(mag->widget)); + + // Variable Item List + mag->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + mag->view_dispatcher, + MagViewVariableItemList, + variable_item_list_get_view(mag->variable_item_list)); + + // Text Input + mag->text_input = text_input_alloc(); + view_dispatcher_add_view( + mag->view_dispatcher, MagViewTextInput, text_input_get_view(mag->text_input)); + + return mag; +} + +static void mag_setting_free(MagSetting* setting) { + furi_assert(setting); + + free(setting); +} + +static void mag_free(Mag* mag) { + furi_assert(mag); + + furi_string_free(mag->file_name); + furi_string_free(mag->file_path); + + // Mag device + mag_device_free(mag->mag_dev); + mag->mag_dev = NULL; + + // Mag setting + mag_setting_free(mag->setting); + mag->setting = NULL; + + // Submenu + view_dispatcher_remove_view(mag->view_dispatcher, MagViewSubmenu); + submenu_free(mag->submenu); + + // DialogEx + view_dispatcher_remove_view(mag->view_dispatcher, MagViewDialogEx); + dialog_ex_free(mag->dialog_ex); + + // Popup + view_dispatcher_remove_view(mag->view_dispatcher, MagViewPopup); + popup_free(mag->popup); + + // Loading + view_dispatcher_remove_view(mag->view_dispatcher, MagViewLoading); + loading_free(mag->loading); + + // Widget + view_dispatcher_remove_view(mag->view_dispatcher, MagViewWidget); + widget_free(mag->widget); + + // Variable Item List + view_dispatcher_remove_view(mag->view_dispatcher, MagViewVariableItemList); + variable_item_list_free(mag->variable_item_list); + + // TextInput + view_dispatcher_remove_view(mag->view_dispatcher, MagViewTextInput); + text_input_free(mag->text_input); + + // View Dispatcher + view_dispatcher_free(mag->view_dispatcher); + + // Scene Manager + scene_manager_free(mag->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + mag->gui = NULL; + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + mag->notifications = NULL; + + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); + + free(mag); +} + +// entry point for app +int32_t mag_app(void* p) { + Mag* mag = mag_alloc(); + UNUSED(p); + + mag_make_app_folder(mag); + + // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering + uint8_t attempts = 0; + bool otg_was_enabled = furi_hal_power_is_otg_enabled(); + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + + view_dispatcher_attach_to_gui(mag->view_dispatcher, mag->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(mag->scene_manager, MagSceneStart); + + view_dispatcher_run(mag->view_dispatcher); + + // Disable 5v power + if(furi_hal_power_is_otg_enabled() && !otg_was_enabled) { + furi_hal_power_disable_otg(); + } + + mag_free(mag); + + return 0; +} + +void mag_make_app_folder(Mag* mag) { + furi_assert(mag); + + if(!storage_simply_mkdir(mag->storage, MAG_APP_FOLDER)) { + dialog_message_show_storage_error(mag->dialogs, "Cannot create\napp folder"); + } +} + +void mag_text_store_set(Mag* mag, const char* text, ...) { + furi_assert(mag); + va_list args; + va_start(args, text); + + vsnprintf(mag->text_store, MAG_TEXT_STORE_SIZE, text, args); + + va_end(args); +} + +void mag_text_store_clear(Mag* mag) { + furi_assert(mag); + memset(mag->text_store, 0, sizeof(mag->text_store)); +} + +void mag_popup_timeout_callback(void* context) { + Mag* mag = context; + view_dispatcher_send_custom_event(mag->view_dispatcher, MagEventPopupClosed); +} + +void mag_widget_callback(GuiButtonType result, InputType type, void* context) { + Mag* mag = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(mag->view_dispatcher, result); + } +} + +void mag_text_input_callback(void* context) { + Mag* mag = context; + view_dispatcher_send_custom_event(mag->view_dispatcher, MagEventNext); +} + +void mag_show_loading_popup(void* context, bool show) { + Mag* mag = context; + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} diff --git a/applications/external/magspoof/mag_device.c b/applications/external/magspoof/mag_device.c new file mode 100644 index 000000000..f3be3a5ff --- /dev/null +++ b/applications/external/magspoof/mag_device.c @@ -0,0 +1,311 @@ +#include "mag_device.h" + +#include +#include + +#define TAG "MagDevice" + +static const char* mag_file_header = "Flipper Mag device"; +static const uint32_t mag_file_version = 1; + +MagDevice* mag_device_alloc() { + MagDevice* mag_dev = malloc(sizeof(MagDevice)); + mag_dev->dev_data.track[0].str = furi_string_alloc(); + mag_dev->dev_data.track[1].str = furi_string_alloc(); + mag_dev->dev_data.track[2].str = furi_string_alloc(); + mag_dev->storage = furi_record_open(RECORD_STORAGE); + mag_dev->dialogs = furi_record_open(RECORD_DIALOGS); + mag_dev->load_path = furi_string_alloc(); + return mag_dev; +} + +void mag_device_data_clear(MagDeviceData* dev_data) { + furi_string_reset(dev_data->track[0].str); + furi_string_reset(dev_data->track[1].str); + furi_string_reset(dev_data->track[2].str); +} + +void mag_device_clear(MagDevice* mag_dev) { + furi_assert(mag_dev); + + mag_device_data_clear(&mag_dev->dev_data); + memset(&mag_dev->dev_data, 0, sizeof(mag_dev->dev_data)); + furi_string_reset(mag_dev->load_path); +} + +void mag_device_free(MagDevice* mag_dev) { + furi_assert(mag_dev); + + mag_device_clear(mag_dev); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); + furi_string_free(mag_dev->load_path); + + //furi_string_free(mag_dev->dev_data.track[0].str); + //furi_string_free(mag_dev->dev_data.track[1].str); + //furi_string_free(mag_dev->dev_data.track[2].str); + + free(mag_dev); +} + +void mag_device_set_name(MagDevice* mag_dev, const char* name) { + furi_assert(mag_dev); + + strlcpy(mag_dev->dev_name, name, MAG_DEV_NAME_MAX_LEN); +} + +static bool mag_device_save_file( + MagDevice* mag_dev, + const char* dev_name, + const char* folder, + const char* extension, + bool use_load_path) { + furi_assert(mag_dev); + + bool saved = false; + FlipperFormat* file = flipper_format_file_alloc(mag_dev->storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + + do { + if(use_load_path && !furi_string_empty(mag_dev->load_path)) { + // Get dir name + path_extract_dirname(furi_string_get_cstr(mag_dev->load_path), temp_str); + // Create mag directory if necessary + if(!storage_simply_mkdir((mag_dev->storage), furi_string_get_cstr(temp_str))) break; + // Make path to file to be saved + furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension); + } else { + // Create mag directory if necessary + if(!storage_simply_mkdir((mag_dev->storage), MAG_APP_FOLDER)) break; + // First remove mag device file if it was saved + furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); + } + // Open file + if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; + + // Write header + if(!flipper_format_write_header_cstr(file, mag_file_header, mag_file_version)) break; + + // Write comment + if(!flipper_format_write_comment_cstr(file, "Mag device track data")) break; + + // Write data + for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) { + furi_string_printf(temp_str, "Track %d", i + 1); + if(!flipper_format_write_string_cstr( + file, + furi_string_get_cstr(temp_str), + furi_string_get_cstr(mag_dev->dev_data.track[i].str))) + break; + } + + saved = true; + } while(0); + + if(!saved) { + dialog_message_show_storage_error(mag_dev->dialogs, "Cannot save\nfile"); + } + + furi_string_free(temp_str); + flipper_format_free(file); + + return saved; +} + +bool mag_device_save(MagDevice* mag_dev, const char* dev_name) { + // wrapping function in the event we have multiple formats + return mag_device_save_file(mag_dev, dev_name, MAG_APP_FOLDER, MAG_APP_EXTENSION, true); +} + +static bool mag_device_load_data(MagDevice* mag_dev, FuriString* path, bool show_dialog) { + bool parsed = false; + + FlipperFormat* file = flipper_format_file_alloc(mag_dev->storage); + FuriString* temp_str; + temp_str = furi_string_alloc(); + bool deprecated_version = false; + bool data_read = true; + + if(mag_dev->loading_cb) { + mag_dev->loading_cb(mag_dev->loading_cb_ctx, true); + } + + do { + if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break; + + // Read and verify header, check file version + uint32_t version; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(furi_string_cmp_str(temp_str, mag_file_header) || (version != mag_file_version)) { + deprecated_version = true; + break; + } + + // Parse data + for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) { + furi_string_printf(temp_str, "Track %d", i + 1); + if(!flipper_format_read_string( + file, furi_string_get_cstr(temp_str), mag_dev->dev_data.track[i].str)) { + FURI_LOG_D(TAG, "Could not read track %d data", i + 1); + + // TODO: smarter load handling now that it is acceptible for some tracks to be empty + data_read = false; + } + } + + parsed = true; + } while(false); + + if((!parsed) && (show_dialog)) { + if(deprecated_version) { + dialog_message_show_storage_error(mag_dev->dialogs, "File format\ndeprecated"); + } else if(!data_read) { + dialog_message_show_storage_error(mag_dev->dialogs, "Cannot read\ndata"); + } else { + dialog_message_show_storage_error(mag_dev->dialogs, "Cannot parse\nfile"); + } + } + + furi_string_free(temp_str); + flipper_format_free(file); + + return parsed; +} + +bool mag_file_select(MagDevice* mag_dev) { + furi_assert(mag_dev); + + // Input events and views are managed by file_browser + FuriString* mag_app_folder; + mag_app_folder = furi_string_alloc_set(MAG_APP_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, MAG_APP_EXTENSION, &I_mag_file_10px); + browser_options.base_path = MAG_APP_FOLDER; + + bool res = dialog_file_browser_show( + mag_dev->dialogs, mag_dev->load_path, mag_app_folder, &browser_options); + + furi_string_free(mag_app_folder); + if(res) { + FuriString* filename; + filename = furi_string_alloc(); + path_extract_filename(mag_dev->load_path, filename, true); + strncpy(mag_dev->dev_name, furi_string_get_cstr(filename), MAG_DEV_NAME_MAX_LEN); + res = mag_device_load_data(mag_dev, mag_dev->load_path, true); + if(res) { + mag_device_set_name(mag_dev, mag_dev->dev_name); + } + furi_string_free(filename); + } + + return res; +} + +bool mag_device_delete(MagDevice* mag_dev, bool use_load_path) { + furi_assert(mag_dev); + + bool deleted = false; + FuriString* file_path; + file_path = furi_string_alloc(); + + do { + // Delete original file + if(use_load_path && !furi_string_empty(mag_dev->load_path)) { + furi_string_set(file_path, mag_dev->load_path); + } else { + furi_string_printf( + file_path, "%s/%s%s", MAG_APP_FOLDER, mag_dev->dev_name, MAG_APP_EXTENSION); + } + if(!storage_simply_remove(mag_dev->storage, furi_string_get_cstr(file_path))) break; + deleted = true; + } while(false); + + if(!deleted) { + dialog_message_show_storage_error(mag_dev->dialogs, "Cannot remove\nfile"); + } + + furi_string_free(file_path); + return deleted; +} + +bool mag_device_parse_card_string(MagDevice* mag_dev, FuriString* f_card_str) { + furi_assert(mag_dev); + FURI_LOG_D(TAG, "mag_device_parse_card_string"); + + const char* card_str = furi_string_get_cstr(f_card_str); + + FURI_LOG_D(TAG, "Parsing card string: %s", card_str); + + // Track 1 + const char* track1_start = strchr(card_str, '%'); + if(!track1_start) { + FURI_LOG_D(TAG, "Could not find track 1 start"); + return false; + } + track1_start++; + const char* track1_end = strchr(track1_start, '?'); + if(!track1_end) { + FURI_LOG_D(TAG, "Could not find track 1 end"); + return false; + } + size_t track1_len = track1_end - track1_start; + + FURI_LOG_D(TAG, "Track 1: %.*s", track1_len, track1_start); + + mag_dev->dev_data.track[0].len = track1_len; + furi_string_printf(mag_dev->dev_data.track[0].str, "%%%.*s?", track1_len, track1_start); + + // Track 2 + const char* track2_start = strchr(track1_end, ';'); + if(!track2_start) { + FURI_LOG_D(TAG, "Could not find track 2 start"); + return true; + } + + track2_start++; + const char* track2_end = strchr(track2_start, '?'); + if(!track2_end) { + FURI_LOG_D(TAG, "Could not find track 2 end"); + return true; + } + size_t track2_len = track2_end - track2_start; + + FURI_LOG_D(TAG, "Track 2: %.*s", track2_len, track2_start); + + mag_dev->dev_data.track[1].len = track2_len; + furi_string_printf(mag_dev->dev_data.track[1].str, "%%%.*s?", track2_len, track2_start); + + // Track 3 + const char* track3_start = strchr(track2_end, ';'); + if(!track3_start) { + FURI_LOG_D(TAG, "Could not find track 3 start"); + return true; + } + + track3_start++; + const char* track3_end = strchr(track3_start, '?'); + if(!track3_end) { + FURI_LOG_D(TAG, "Could not find track 3 end"); + return true; + } + size_t track3_len = track3_end - track3_start; + + FURI_LOG_D(TAG, "Track 3: %.*s", track3_len, track3_start); + + mag_dev->dev_data.track[2].len = track3_len; + furi_string_printf(mag_dev->dev_data.track[2].str, "%%%.*s?", track3_len, track3_start); + + return true; +} + +void mag_device_set_loading_callback( + MagDevice* mag_dev, + MagLoadingCallback callback, + void* context) { + furi_assert(mag_dev); + + mag_dev->loading_cb = callback; + mag_dev->loading_cb_ctx = context; +} diff --git a/applications/external/magspoof/mag_device.h b/applications/external/magspoof/mag_device.h new file mode 100644 index 000000000..7009ffa3b --- /dev/null +++ b/applications/external/magspoof/mag_device.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include + +#include "magspoof_icons.h" +#include + +#define MAG_DEV_NAME_MAX_LEN 22 +#define MAG_DEV_TRACKS 3 + +#define MAG_APP_FOLDER STORAGE_APP_DATA_PATH_PREFIX +#define MAG_APP_EXTENSION ".mag" + +typedef void (*MagLoadingCallback)(void* context, bool state); + +typedef struct { + FuriString* str; + size_t len; +} MagTrack; + +typedef struct { + MagTrack track[MAG_DEV_TRACKS]; +} MagDeviceData; + +typedef struct { + Storage* storage; + DialogsApp* dialogs; + MagDeviceData dev_data; + char dev_name[MAG_DEV_NAME_MAX_LEN + 1]; + FuriString* load_path; + MagLoadingCallback loading_cb; + void* loading_cb_ctx; +} MagDevice; + +MagDevice* mag_device_alloc(); + +void mag_device_free(MagDevice* mag_dev); + +void mag_device_set_name(MagDevice* mag_dev, const char* name); + +bool mag_device_save(MagDevice* mag_dev, const char* dev_name); + +bool mag_file_select(MagDevice* mag_dev); + +void mag_device_data_clear(MagDeviceData* dev_data); + +void mag_device_clear(MagDevice* mag_dev); + +bool mag_device_delete(MagDevice* mag_dev, bool use_load_path); + +bool mag_device_parse_card_string(MagDevice* mag_dev, FuriString* card_str); + +void mag_device_set_loading_callback( + MagDevice* mag_dev, + MagLoadingCallback callback, + void* context); diff --git a/applications/external/magspoof/mag_i.h b/applications/external/magspoof/mag_i.h new file mode 100644 index 000000000..a57c26e15 --- /dev/null +++ b/applications/external/magspoof/mag_i.h @@ -0,0 +1,101 @@ +#pragma once + +#include "mag_device.h" +//#include "helpers/mag_helpers.h" +#include "helpers/mag_types.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "scenes/mag_scene.h" +#include "scenes/mag_scene_read.h" + +#define MAG_TEXT_STORE_SIZE 150 + +enum MagCustomEvent { + MagEventNext = 100, + MagEventExit, + MagEventPopupClosed, +}; + +typedef struct { + MagTxState tx; + MagTrackState track; + MagReverseState reverse; + uint32_t us_clock; + uint32_t us_interpacket; +} MagSetting; + +typedef struct { + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + Storage* storage; + DialogsApp* dialogs; + MagDevice* mag_dev; + + char text_store[MAG_TEXT_STORE_SIZE + 1]; + FuriString* file_path; + FuriString* file_name; + + MagSetting* setting; + + // Common views + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + Loading* loading; + TextInput* text_input; + Widget* widget; + VariableItemList* variable_item_list; + + // UART + FuriThread* uart_rx_thread; + FuriStreamBuffer* uart_rx_stream; + uint8_t uart_rx_buf[UART_RX_BUF_SIZE + 1]; + void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context); + + char uart_text_input_store[UART_TERMINAL_TEXT_INPUT_STORE_SIZE + 1]; + FuriString* uart_text_box_store; + size_t uart_text_box_store_strlen; + // UART_TextInput* text_input; +} Mag; + +void mag_text_store_set(Mag* mag, const char* text, ...); + +void mag_text_store_clear(Mag* mag); + +void mag_show_loading_popup(void* context, bool show); + +void mag_make_app_folder(Mag* mag); + +void mag_popup_timeout_callback(void* context); + +void mag_widget_callback(GuiButtonType result, InputType type, void* context); + +void mag_text_input_callback(void* context); diff --git a/applications/external/magspoof/scenes/mag_scene.c b/applications/external/magspoof/scenes/mag_scene.c new file mode 100644 index 000000000..61d847add --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene.c @@ -0,0 +1,30 @@ +#include "mag_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const mag_on_enter_handlers[])(void*) = { +#include "mag_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 mag_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "mag_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 mag_on_exit_handlers[])(void* context) = { +#include "mag_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers mag_scene_handlers = { + .on_enter_handlers = mag_on_enter_handlers, + .on_event_handlers = mag_on_event_handlers, + .on_exit_handlers = mag_on_exit_handlers, + .scene_num = MagSceneNum, +}; \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene.h b/applications/external/magspoof/scenes/mag_scene.h new file mode 100644 index 000000000..0f61112b9 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) MagScene##id, +typedef enum { +#include "mag_scene_config.h" + MagSceneNum, +} MagScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers mag_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "mag_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 "mag_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 "mag_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/magspoof/scenes/mag_scene_about.c b/applications/external/magspoof/scenes/mag_scene_about.c new file mode 100644 index 000000000..247c83199 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_about.c @@ -0,0 +1,40 @@ +#include "../mag_i.h" + +void mag_scene_about_on_enter(void* context) { + Mag* mag = context; + Widget* widget = mag->widget; + + FuriString* tmp_str; + tmp_str = furi_string_alloc(); + + furi_string_cat_printf(tmp_str, "Version: %s\n", MAG_VERSION_APP); + furi_string_cat_printf(tmp_str, "Developer: %s\n", MAG_DEVELOPER); + furi_string_cat_printf(tmp_str, "GitHub: %s\n\n", MAG_GITHUB); + + furi_string_cat_printf( + tmp_str, + "Unfinished port of Samy Kamkar's MagSpoof. Confer GitHub for updates; in the interim, use responsibly and at your own risk."); + + // TODO: Add credits + + widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(tmp_str)); + furi_string_free(tmp_str); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); +} + +bool mag_scene_about_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + SceneManager* scene_manager = mag->scene_manager; + bool consumed = false; + + UNUSED(event); + UNUSED(scene_manager); + + return consumed; +} + +void mag_scene_about_on_exit(void* context) { + Mag* mag = context; + widget_reset(mag->widget); +} \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene_config.h b/applications/external/magspoof/scenes/mag_scene_config.h new file mode 100644 index 000000000..7ab276e53 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_config.h @@ -0,0 +1,15 @@ +ADD_SCENE(mag, start, Start) +ADD_SCENE(mag, about, About) +ADD_SCENE(mag, emulate, Emulate) +ADD_SCENE(mag, emulate_config, EmulateConfig) +ADD_SCENE(mag, file_select, FileSelect) +ADD_SCENE(mag, saved_menu, SavedMenu) +ADD_SCENE(mag, saved_info, SavedInfo) +ADD_SCENE(mag, input_name, InputName) +ADD_SCENE(mag, input_value, InputValue) +ADD_SCENE(mag, save_success, SaveSuccess) +ADD_SCENE(mag, delete_success, DeleteSuccess) +ADD_SCENE(mag, delete_confirm, DeleteConfirm) +ADD_SCENE(mag, exit_confirm, ExitConfirm) +ADD_SCENE(mag, under_construction, UnderConstruction) +ADD_SCENE(mag, read, Read) \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene_delete_confirm.c b/applications/external/magspoof/scenes/mag_scene_delete_confirm.c new file mode 100644 index 000000000..b7349ba70 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_delete_confirm.c @@ -0,0 +1,49 @@ +#include "../mag_i.h" +#include "../mag_device.h" + +void mag_scene_delete_confirm_on_enter(void* context) { + Mag* mag = context; + Widget* widget = mag->widget; + MagDevice* mag_dev = mag->mag_dev; + + FuriString* tmp_str; + tmp_str = furi_string_alloc(); + + furi_string_printf(tmp_str, "\e#Delete %s?\e#", mag_dev->dev_name); + + //TODO: print concise summary of data on card? Would need to vary by card/track type + + widget_add_text_box_element( + widget, 0, 0, 128, 27, AlignCenter, AlignCenter, furi_string_get_cstr(tmp_str), true); + widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", mag_widget_callback, mag); + widget_add_button_element(widget, GuiButtonTypeRight, "Delete", mag_widget_callback, mag); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); + + furi_string_free(tmp_str); +} + +bool mag_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + SceneManager* scene_manager = mag->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + consumed = true; + if(mag_device_delete(mag->mag_dev, true)) { + scene_manager_next_scene(scene_manager, MagSceneDeleteSuccess); + } + } else if(event.event == GuiButtonTypeLeft) { + consumed = true; + scene_manager_previous_scene(scene_manager); + } + } + + return consumed; +} + +void mag_scene_delete_confirm_on_exit(void* context) { + Mag* mag = context; + widget_reset(mag->widget); +} diff --git a/applications/external/magspoof/scenes/mag_scene_delete_success.c b/applications/external/magspoof/scenes/mag_scene_delete_success.c new file mode 100644 index 000000000..ca7dbbbf7 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_delete_success.c @@ -0,0 +1,39 @@ +#include "../mag_i.h" + +void mag_scene_delete_success_on_enter(void* context) { + Mag* mag = context; + Popup* popup = mag->popup; + + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + + popup_set_callback(popup, mag_popup_timeout_callback); + popup_set_context(popup, mag); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewPopup); +} + +bool mag_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MagEventPopupClosed) { + consumed = true; + + scene_manager_search_and_switch_to_previous_scene( + mag->scene_manager, MagSceneFileSelect); + } + } + + return consumed; +} + +void mag_scene_delete_success_on_exit(void* context) { + Mag* mag = context; + Popup* popup = mag->popup; + + popup_reset(popup); +} \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene_emulate.c b/applications/external/magspoof/scenes/mag_scene_emulate.c new file mode 100644 index 000000000..e7e8737eb --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_emulate.c @@ -0,0 +1,93 @@ +#include "../mag_i.h" +#include "../helpers/mag_helpers.h" + +#define TAG "MagSceneEmulate" + +void cat_trackstr(FuriString* str, uint8_t calls, uint8_t i, FuriString* trackstr) { + furi_string_cat_printf( + str, + "%sTrack %d:%s%s\n", + (calls == 0) ? "" : "\n", // if first line, don't prepend a "\n" + (i + 1), + furi_string_empty(trackstr) ? " " : "\n", + furi_string_empty(trackstr) ? "< empty >" : furi_string_get_cstr(trackstr)); +} + +void mag_scene_emulate_on_enter(void* context) { + Mag* mag = context; + Widget* widget = mag->widget; + + FuriString* tmp_str; + tmp_str = furi_string_alloc(); + + // Use strlcpy instead perhaps, to truncate to screen width, then add ellipses if needed? + furi_string_printf(tmp_str, "%s\r\n", mag->mag_dev->dev_name); + + // TODO: Display other relevant config settings (namely RFID vs GPIO)? + + widget_add_icon_element(widget, 1, 1, &I_mag_file_10px); + widget_add_string_element( + widget, 13, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str)); + furi_string_reset(tmp_str); + + FURI_LOG_D(TAG, "%d", mag->setting->reverse); + + // print relevant data + uint8_t cat_count = 0; + for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) { + FuriString* trackstr = mag->mag_dev->dev_data.track[i].str; + + // still messy / dumb way to do this, but slightly cleaner than before. + // will clean up more later + switch(mag->setting->track) { + case MagTrackStateOne: + if(i == 0) cat_trackstr(tmp_str, cat_count++, i, trackstr); + break; + case MagTrackStateTwo: + if(i == 1) cat_trackstr(tmp_str, cat_count++, i, trackstr); + break; + case MagTrackStateThree: + if(i == 2) cat_trackstr(tmp_str, cat_count++, i, trackstr); + break; + case MagTrackStateOneAndTwo: + if((i == 0) | (i == 1)) cat_trackstr(tmp_str, cat_count++, i, trackstr); + break; + } + } + + widget_add_text_scroll_element(widget, 0, 15, 128, 49, furi_string_get_cstr(tmp_str)); + + widget_add_button_element(widget, GuiButtonTypeLeft, "Config", mag_widget_callback, mag); + widget_add_button_element(widget, GuiButtonTypeRight, "Send", mag_widget_callback, mag); + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); + furi_string_free(tmp_str); +} + +bool mag_scene_emulate_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + SceneManager* scene_manager = mag->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case GuiButtonTypeLeft: + consumed = true; + scene_manager_next_scene(scene_manager, MagSceneEmulateConfig); + break; + case GuiButtonTypeRight: + consumed = true; + notification_message(mag->notifications, &sequence_blink_start_cyan); + mag_spoof(mag); + notification_message(mag->notifications, &sequence_blink_stop); + break; + } + } + + return consumed; +} + +void mag_scene_emulate_on_exit(void* context) { + Mag* mag = context; + notification_message(mag->notifications, &sequence_blink_stop); + widget_reset(mag->widget); +} \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene_emulate_config.c b/applications/external/magspoof/scenes/mag_scene_emulate_config.c new file mode 100644 index 000000000..437f536a7 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_emulate_config.c @@ -0,0 +1,264 @@ +#include "../mag_i.h" + +#define TAG "MagSceneEmulateConfig" + +enum MagSettingIndex { + MagSettingIndexTx, + MagSettingIndexTrack, + MagSettingIndexReverse, + MagSettingIndexClock, + MagSettingIndexInterpacket, +}; + +#define TX_COUNT 7 +const char* const tx_text[TX_COUNT] = { + "RFID", + "GPIO", + "Piezo", + "LF + P", + "NFC", + "434MHz", + "868MHz", +}; +const uint32_t tx_value[TX_COUNT] = { + MagTxStateRFID, + MagTxStateGPIO, + MagTxStatePiezo, + MagTxStateLF_P, + MagTxStateNFC, + MagTxCC1101_434, + MagTxCC1101_868, +}; + +#define TRACK_COUNT 4 +const char* const track_text[TRACK_COUNT] = { + "1 + 2", + "1", + "2", + "3", +}; +const uint32_t track_value[TRACK_COUNT] = { + MagTrackStateOneAndTwo, + MagTrackStateOne, + MagTrackStateTwo, + MagTrackStateThree, +}; + +#define REVERSE_COUNT 2 +const char* const reverse_text[REVERSE_COUNT] = { + "OFF", + "ON", +}; +const uint32_t reverse_value[REVERSE_COUNT] = { + MagReverseStateOff, + MagReverseStateOn, +}; + +#define CLOCK_COUNT 15 +const char* const clock_text[CLOCK_COUNT] = { + "200us", + "220us", + "240us", + "250us", + "260us", + "280us", + "300us", + "325us", + "350us", + "375us", + "400us", + "450us", + "500us", + "600us", + "700us", +}; +const uint32_t clock_value[CLOCK_COUNT] = { + 200, + 220, + 240, + 250, + 260, + 280, + 300, + 325, + 350, + 375, + 400, + 450, + 500, + 600, + 700, +}; + +#define INTERPACKET_COUNT 13 +const char* const interpacket_text[INTERPACKET_COUNT] = { + "0us", + "2us", + "4us", + "6us", + "8us", + "10us", + "12us", + "14us", + "16us", + "18us", + "20us", + "25us", + "30us", +}; +const uint32_t interpacket_value[INTERPACKET_COUNT] = { + 0, + 2, + 4, + 6, + 8, + 10, + 12, + 14, + 16, + 18, + 20, + 25, + 30, +}; + +static void mag_scene_emulate_config_set_tx(VariableItem* item) { + Mag* mag = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, tx_text[index]); + + mag->setting->tx = tx_value[index]; +}; + +static void mag_scene_emulate_config_set_track(VariableItem* item) { + Mag* mag = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + if(mag->setting->reverse == MagReverseStateOff) { + variable_item_set_current_value_text(item, track_text[index]); + mag->setting->track = track_value[index]; + } else if(mag->setting->reverse == MagReverseStateOn) { + variable_item_set_current_value_index( + item, value_index_uint32(MagTrackStateOneAndTwo, track_value, TRACK_COUNT)); + } + + // TODO: Check there is data in selected track? + // Only display track options with data? +}; + +static void mag_scene_emulate_config_set_reverse(VariableItem* item) { + Mag* mag = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + if(mag->setting->track == MagTrackStateOneAndTwo) { + // only allow reverse track to be set when playing both 1 and 2 + variable_item_set_current_value_text(item, reverse_text[index]); + mag->setting->reverse = reverse_value[index]; + //FURI_LOG_D(TAG, "%s", reverse_text[index]); + //FURI_LOG_D(TAG, "%d", mag->setting->reverse); + } else { + variable_item_set_current_value_index( + item, value_index_uint32(MagReverseStateOff, reverse_value, REVERSE_COUNT)); + } +}; + +static void mag_scene_emulate_config_set_clock(VariableItem* item) { + Mag* mag = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, clock_text[index]); + + mag->setting->us_clock = clock_value[index]; +}; + +static void mag_scene_emulate_config_set_interpacket(VariableItem* item) { + Mag* mag = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, interpacket_text[index]); + + mag->setting->us_interpacket = interpacket_value[index]; +}; + +void mag_scene_emulate_config_on_enter(void* context) { + // TODO: retrieve current values from struct, rather than setting to default on setup + + Mag* mag = context; + VariableItem* item; + uint8_t value_index; + + // TX + item = variable_item_list_add( + mag->variable_item_list, "TX via:", TX_COUNT, mag_scene_emulate_config_set_tx, mag); + value_index = value_index_uint32(mag->setting->tx, tx_value, TX_COUNT); + scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, tx_text[value_index]); + + // Track + item = variable_item_list_add( + mag->variable_item_list, "Track:", TRACK_COUNT, mag_scene_emulate_config_set_track, mag); + value_index = value_index_uint32(mag->setting->track, track_value, TRACK_COUNT); + scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, track_text[value_index]); + + // Reverse + //FURI_LOG_D(TAG, "%d", mag->setting->reverse); + item = variable_item_list_add( + mag->variable_item_list, + "Reverse:", + REVERSE_COUNT, + mag_scene_emulate_config_set_reverse, + mag); + value_index = value_index_uint32(mag->setting->reverse, reverse_value, REVERSE_COUNT); + scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, reverse_text[value_index]); + + // Clock + item = variable_item_list_add( + mag->variable_item_list, "Clock:", CLOCK_COUNT, mag_scene_emulate_config_set_clock, mag); + value_index = value_index_uint32(mag->setting->us_clock, clock_value, CLOCK_COUNT); + scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, clock_text[value_index]); + + // Interpacket + /* + item = variable_item_list_add( + mag->variable_item_list, + "Interpacket:", + INTERPACKET_COUNT, + mag_scene_emulate_config_set_interpacket, + mag); + value_index = + value_index_uint32(mag->setting->us_interpacket, interpacket_value, INTERPACKET_COUNT); + scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, interpacket_text[value_index]);*/ + UNUSED(mag_scene_emulate_config_set_interpacket); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewVariableItemList); +} + +bool mag_scene_emulate_config_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + SceneManager* scene_manager = mag->scene_manager; + bool consumed = false; + + UNUSED(mag); + UNUSED(scene_manager); + UNUSED(event); + + return consumed; +} + +void mag_scene_emulate_config_on_exit(void* context) { + Mag* mag = context; + variable_item_list_set_selected_item(mag->variable_item_list, 0); + variable_item_list_reset(mag->variable_item_list); + // mag_last_settings_save? + // scene_manager_set_scene_state? Using subghz_scene_reciever_config as framework/inspo +} \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene_exit_confirm.c b/applications/external/magspoof/scenes/mag_scene_exit_confirm.c new file mode 100644 index 000000000..e26234fb8 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_exit_confirm.c @@ -0,0 +1,20 @@ +#include "../mag_i.h" + +void mag_scene_exit_confirm_on_enter(void* context) { + Mag* mag = context; + UNUSED(mag); +} + +bool mag_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + UNUSED(mag); + UNUSED(event); + bool consumed = false; + + return consumed; +} + +void mag_scene_exit_confirm_on_exit(void* context) { + Mag* mag = context; + UNUSED(mag); +} diff --git a/applications/external/magspoof/scenes/mag_scene_file_select.c b/applications/external/magspoof/scenes/mag_scene_file_select.c new file mode 100644 index 000000000..b759c4d18 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_file_select.c @@ -0,0 +1,24 @@ +#include "../mag_i.h" +#include "../mag_device.h" + +void mag_scene_file_select_on_enter(void* context) { + Mag* mag = context; + //UNUSED(mag); + mag_device_set_loading_callback(mag->mag_dev, mag_show_loading_popup, mag); + if(mag_file_select(mag->mag_dev)) { + scene_manager_next_scene(mag->scene_manager, MagSceneSavedMenu); + } else { + scene_manager_search_and_switch_to_previous_scene(mag->scene_manager, MagSceneStart); + } + mag_device_set_loading_callback(mag->mag_dev, NULL, mag); +} + +bool mag_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void mag_scene_file_select_on_exit(void* context) { + UNUSED(context); +} \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene_input_name.c b/applications/external/magspoof/scenes/mag_scene_input_name.c new file mode 100644 index 000000000..7368b4598 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_input_name.c @@ -0,0 +1,82 @@ +#include +#include "../mag_i.h" + +void mag_scene_input_name_on_enter(void* context) { + Mag* mag = context; + TextInput* text_input = mag->text_input; + FuriString* folder_path; + folder_path = furi_string_alloc(); + + //TODO: compatible types / etc + //bool name_is_empty = furi_string_empty(mag->mag_dev->dev_name); + bool name_is_empty = true; + + if(name_is_empty) { + furi_string_set(mag->file_path, MAG_APP_FOLDER); + set_random_name(mag->text_store, MAG_TEXT_STORE_SIZE); + furi_string_set(folder_path, MAG_APP_FOLDER); + } else { + // TODO: compatible types etc + //mag_text_store_set(mag, "%s", furi_string_get_cstr(mag->mag_dev->dev_name)); + path_extract_dirname(furi_string_get_cstr(mag->file_path), folder_path); + } + + text_input_set_header_text(text_input, "Name the card"); + text_input_set_result_callback( + text_input, + mag_text_input_callback, + mag, + mag->text_store, + MAG_DEV_NAME_MAX_LEN, + name_is_empty); + + FURI_LOG_I("", "%s %s", furi_string_get_cstr(folder_path), mag->text_store); + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + furi_string_get_cstr(folder_path), + MAG_APP_EXTENSION, + furi_string_get_cstr(mag->file_name)); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + furi_string_free(folder_path); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewTextInput); +} + +bool mag_scene_input_name_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + SceneManager* scene_manager = mag->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MagEventNext) { + consumed = true; + //if(!furi_string_empty(mag->file_name)) { + // mag_delete_key(mag); + //} + + furi_string_set(mag->file_name, mag->text_store); + + if(mag_device_save(mag->mag_dev, furi_string_get_cstr(mag->file_name))) { + scene_manager_next_scene(scene_manager, MagSceneSaveSuccess); + } else { + //scene_manager_search_and_switch_to_previous_scene( + // scene_manager, MagSceneReadKeyMenu); + // TODO: Replace with appropriate scene! No read scene prior if adding manually... + } + } + } + + return consumed; +} + +void mag_scene_input_name_on_exit(void* context) { + Mag* mag = context; + TextInput* text_input = mag->text_input; + + void* validator_context = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + validator_is_file_free((ValidatorIsFile*)validator_context); + + text_input_reset(text_input); +} diff --git a/applications/external/magspoof/scenes/mag_scene_input_value.c b/applications/external/magspoof/scenes/mag_scene_input_value.c new file mode 100644 index 000000000..77adcb306 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_input_value.c @@ -0,0 +1,39 @@ +#include "../mag_i.h" + +void mag_scene_input_value_on_enter(void* context) { + Mag* mag = context; + TextInput* text_input = mag->text_input; + + // TODO: retrieve stored/existing data if editing rather than adding anew? + mag_text_store_set(mag, furi_string_get_cstr(mag->mag_dev->dev_data.track[1].str)); + + text_input_set_header_text(text_input, "Enter track data (WIP)"); + text_input_set_result_callback( + text_input, mag_text_input_callback, mag, mag->text_store, MAG_TEXT_STORE_SIZE, true); + + text_input_add_illegal_symbols(text_input); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewTextInput); +} + +bool mag_scene_input_value_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + SceneManager* scene_manager = mag->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MagEventNext) { + consumed = true; + + furi_string_set(mag->mag_dev->dev_data.track[1].str, mag->text_store); + scene_manager_next_scene(scene_manager, MagSceneInputName); + } + } + + return consumed; +} + +void mag_scene_input_value_on_exit(void* context) { + Mag* mag = context; + UNUSED(mag); +} diff --git a/applications/external/magspoof/scenes/mag_scene_read.c b/applications/external/magspoof/scenes/mag_scene_read.c new file mode 100644 index 000000000..9b4246d38 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_read.c @@ -0,0 +1,185 @@ +// Creator: Hummus@FlipperGang + +#include "../mag_i.h" +#include "../helpers/mag_helpers.h" + +#include "mag_scene_read.h" + +#define TAG "MagSceneRead" + +void uart_callback(UartIrqEvent event, uint8_t data, void* context) { + Mag* mag = context; + if(event == UartIrqEventRXNE) { + furi_stream_buffer_send(mag->uart_rx_stream, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(mag->uart_rx_thread), WorkerEvtRxDone); + } +} + +static int32_t uart_worker(void* context) { + Mag* mag = context; + mag->uart_rx_stream = furi_stream_buffer_alloc(UART_RX_BUF_SIZE, 1); + mag->uart_text_box_store_strlen = 0; + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever); + // furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEvtStop) break; + if(events & WorkerEvtRxDone) { + FURI_LOG_D(TAG, "WorkerEvtRxDone"); + // notification_message(mag->notifications, &sequence_success); + size_t len = furi_stream_buffer_receive( + mag->uart_rx_stream, mag->uart_rx_buf, UART_RX_BUF_SIZE, 200); + FURI_LOG_D(TAG, "UART RX len: %d", len); + + if(len > 0) { + // If text box store gets too big, then truncate it + mag->uart_text_box_store_strlen += len; + + if(mag->uart_text_box_store_strlen >= UART_TERMINAL_TEXT_BOX_STORE_SIZE - 1) { + furi_string_right( + mag->uart_text_box_store, mag->uart_text_box_store_strlen / 2); + mag->uart_text_box_store_strlen = + furi_string_size(mag->uart_text_box_store) + len; + } + + // Add '\0' to the end of the string, and then add the new data + mag->uart_rx_buf[len] = '\0'; + furi_string_cat_printf(mag->uart_text_box_store, "%s", mag->uart_rx_buf); + + FURI_LOG_D(TAG, "UART RX buf: %*.s", len, mag->uart_rx_buf); + FURI_LOG_D( + TAG, "UART RX store: %s", furi_string_get_cstr(mag->uart_text_box_store)); + } + + FURI_LOG_D(TAG, "UARTEventRxData"); + + view_dispatcher_send_custom_event(mag->view_dispatcher, UARTEventRxData); + } + } + + furi_stream_buffer_free(mag->uart_rx_stream); + + return 0; +} + +void update_widgets(Mag* mag) { + // Clear widget from all elements + widget_reset(mag->widget); + + // Titlebar + widget_add_icon_element(mag->widget, 38, -1, &I_mag_file_10px); + widget_add_string_element(mag->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "READ"); + widget_add_icon_element(mag->widget, 81, -1, &I_mag_file_10px); + + // Text box + widget_add_text_scroll_element( + mag->widget, 0, 10, 128, 40, furi_string_get_cstr(mag->uart_text_box_store)); + + // Buttons + widget_add_button_element(mag->widget, GuiButtonTypeLeft, "Clear", mag_widget_callback, mag); + widget_add_button_element(mag->widget, GuiButtonTypeRight, "Parse", mag_widget_callback, mag); +} + +void mag_scene_read_on_enter(void* context) { + Mag* mag = context; + FuriString* message = furi_string_alloc(); + furi_string_printf(message, "Please swipe a card!\n"); + mag->uart_text_box_store = message; + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); + + update_widgets(mag); + + // Initialize UART + // furi_hal_console_disable(); + furi_hal_uart_deinit(FuriHalUartIdUSART1); + furi_hal_uart_init(FuriHalUartIdUSART1, 9600); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_callback, mag); + FURI_LOG_D(TAG, "UART initialized"); + + mag->uart_rx_thread = furi_thread_alloc(); + furi_thread_set_name(mag->uart_rx_thread, "UartRx"); + furi_thread_set_stack_size(mag->uart_rx_thread, 1024); + furi_thread_set_context(mag->uart_rx_thread, mag); + furi_thread_set_callback(mag->uart_rx_thread, uart_worker); + + furi_thread_start(mag->uart_rx_thread); + FURI_LOG_D(TAG, "UART worker started"); +} + +bool mag_scene_read_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + FURI_LOG_D(TAG, "Custom event: %ld", event.event); + + switch(event.event) { + case GuiButtonTypeLeft: // Clear + consumed = true; + // Clear text box store + furi_string_reset(mag->uart_text_box_store); + mag->uart_text_box_store_strlen = 0; + break; + + case GuiButtonTypeRight: // Parse + consumed = true; + FURI_LOG_D(TAG, "Trying to parse"); + MagDevice* mag_dev = mag->mag_dev; + + bool res = mag_device_parse_card_string(mag_dev, mag->uart_text_box_store); + furi_string_reset(mag->uart_text_box_store); + if(res) { + notification_message(mag->notifications, &sequence_success); + + furi_string_printf( + mag->uart_text_box_store, + "Track 1: %.*s\nTrack 2: %.*s\nTrack 3: %.*s", + mag_dev->dev_data.track[0].len, + furi_string_get_cstr(mag_dev->dev_data.track[0].str), + mag_dev->dev_data.track[1].len, + furi_string_get_cstr(mag_dev->dev_data.track[1].str), + mag_dev->dev_data.track[2].len, + furi_string_get_cstr(mag_dev->dev_data.track[2].str)); + + // Switch to saved menu scene + scene_manager_next_scene(mag->scene_manager, MagSceneSavedMenu); + + } else { + furi_string_printf(mag->uart_text_box_store, "Failed to parse! Try again\n"); + notification_message(mag->notifications, &sequence_error); + } + + break; + } + + update_widgets(mag); + } + + return consumed; +} + +void mag_scene_read_on_exit(void* context) { + Mag* mag = context; + // notification_message(mag->notifications, &sequence_blink_stop); + widget_reset(mag->widget); + // view_dispatcher_remove_view(mag->view_dispatcher, MagViewWidget); + + // Stop UART worker + FURI_LOG_D(TAG, "Stopping UART worker"); + furi_thread_flags_set(furi_thread_get_id(mag->uart_rx_thread), WorkerEvtStop); + furi_thread_join(mag->uart_rx_thread); + furi_thread_free(mag->uart_rx_thread); + FURI_LOG_D(TAG, "UART worker stopped"); + + furi_string_free(mag->uart_text_box_store); + + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL); + furi_hal_uart_deinit(FuriHalUartIdUSART1); + // furi_hal_console_enable(); + + notification_message(mag->notifications, &sequence_blink_stop); +} diff --git a/applications/external/magspoof/scenes/mag_scene_read.h b/applications/external/magspoof/scenes/mag_scene_read.h new file mode 100644 index 000000000..df8327877 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_read.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#define UART_RX_BUF_SIZE (320) +#define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096) +#define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512) +#define UART_CH (FuriHalUartIdUSART1) +#define UART_BAUDRATE (9600) + +typedef enum { + WorkerEvtStop = (1 << 0), + WorkerEvtRxDone = (1 << 1), +} WorkerEvtFlags; + +typedef enum { + UARTEventRxData = 100, +} UARTEvents; + +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) diff --git a/applications/external/magspoof/scenes/mag_scene_save_success.c b/applications/external/magspoof/scenes/mag_scene_save_success.c new file mode 100644 index 000000000..fe9b6b4af --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_save_success.c @@ -0,0 +1,43 @@ +#include "../mag_i.h" + +void mag_scene_save_success_on_enter(void* context) { + Mag* mag = context; + Popup* popup = mag->popup; + + // Clear state of data enter scene + //scene_manager_set_scene_state(mag->scene_manager, LfRfidSceneSaveData, 0); + mag_text_store_clear(mag); + + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + popup_set_context(popup, mag); + popup_set_callback(popup, mag_popup_timeout_callback); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewPopup); +} + +bool mag_scene_save_success_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + bool consumed = false; + + if((event.type == SceneManagerEventTypeBack) || + ((event.type == SceneManagerEventTypeCustom) && (event.event == MagEventPopupClosed))) { + bool result = + scene_manager_search_and_switch_to_previous_scene(mag->scene_manager, MagSceneStart); + if(!result) { + scene_manager_search_and_switch_to_another_scene( + mag->scene_manager, MagSceneFileSelect); + } + consumed = true; + } + + return consumed; +} + +void mag_scene_save_success_on_exit(void* context) { + Mag* mag = context; + + popup_reset(mag->popup); +} \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene_saved_info.c b/applications/external/magspoof/scenes/mag_scene_saved_info.c new file mode 100644 index 000000000..fd4e808b5 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_saved_info.c @@ -0,0 +1,50 @@ +#include "../mag_i.h" + +void mag_scene_saved_info_on_enter(void* context) { + Mag* mag = context; + Widget* widget = mag->widget; + + FuriString* tmp_str; + tmp_str = furi_string_alloc(); + + // Use strlcpy instead perhaps, to truncate to screen width, then add ellipses if needed? + furi_string_printf(tmp_str, "%s\r\n", mag->mag_dev->dev_name); + + widget_add_icon_element(widget, 1, 1, &I_mag_file_10px); + widget_add_string_element( + widget, 13, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str)); + furi_string_reset(tmp_str); + + for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) { + FuriString* trackstr = mag->mag_dev->dev_data.track[i].str; + + furi_string_cat_printf( + tmp_str, + "Track %d:%s%s%s", + (i + 1), + furi_string_empty(trackstr) ? " " : "\n", + furi_string_empty(trackstr) ? "< empty >" : furi_string_get_cstr(trackstr), + (i + 1 == MAG_DEV_TRACKS) ? "" : "\n\n"); + } + + widget_add_text_scroll_element(widget, 0, 15, 128, 49, furi_string_get_cstr(tmp_str)); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); + furi_string_free(tmp_str); +} + +bool mag_scene_saved_info_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + SceneManager* scene_manager = mag->scene_manager; + bool consumed = false; + + UNUSED(event); + UNUSED(scene_manager); + + return consumed; +} + +void mag_scene_saved_info_on_exit(void* context) { + Mag* mag = context; + widget_reset(mag->widget); +} diff --git a/applications/external/magspoof/scenes/mag_scene_saved_menu.c b/applications/external/magspoof/scenes/mag_scene_saved_menu.c new file mode 100644 index 000000000..f2b66de41 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_saved_menu.c @@ -0,0 +1,81 @@ +#include "../mag_i.h" + +enum SubmenuIndex { + SubmenuIndexEmulate, + //SubmenuIndexEdit, + SubmenuIndexDelete, + SubmenuIndexInfo, +}; + +void mag_scene_saved_menu_submenu_callback(void* context, uint32_t index) { + Mag* mag = context; + + view_dispatcher_send_custom_event(mag->view_dispatcher, index); +} + +void mag_scene_saved_menu_on_enter(void* context) { + Mag* mag = context; + Submenu* submenu = mag->submenu; + + // messy code to quickly check which tracks are available for emulation/display + // there's likely a better spot to do this, but the MagDevice functions don't have access to the full mag struct... + bool is_empty_t1 = furi_string_empty(mag->mag_dev->dev_data.track[0].str); + bool is_empty_t2 = furi_string_empty(mag->mag_dev->dev_data.track[1].str); + bool is_empty_t3 = furi_string_empty(mag->mag_dev->dev_data.track[2].str); + + if(!is_empty_t1 && !is_empty_t2) { + mag->setting->track = MagTrackStateOneAndTwo; + } else if(!is_empty_t1) { + mag->setting->track = MagTrackStateOne; + } else if(!is_empty_t2) { + mag->setting->track = MagTrackStateTwo; + } else if(!is_empty_t3) { + mag->setting->track = MagTrackStateThree; + } // TODO: what happens if no track data present? + + submenu_add_item( + submenu, "Emulate (WIP)", SubmenuIndexEmulate, mag_scene_saved_menu_submenu_callback, mag); + //submenu_add_item( + // submenu, "Edit (WIP)", SubmenuIndexEdit, mag_scene_saved_menu_submenu_callback, mag); + submenu_add_item( + submenu, "Delete", SubmenuIndexDelete, mag_scene_saved_menu_submenu_callback, mag); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, mag_scene_saved_menu_submenu_callback, mag); + + submenu_set_selected_item( + mag->submenu, scene_manager_get_scene_state(mag->scene_manager, MagSceneSavedMenu)); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewSubmenu); +} + +bool mag_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(mag->scene_manager, MagSceneSavedMenu, event.event); + + // TODO: replace with actual next scenes once built + if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(mag->scene_manager, MagSceneEmulate); + consumed = true; + //} else if(event.event == SubmenuIndexEdit) { + // scene_manager_next_scene(mag->scene_manager, MagSceneUnderConstruction); + // consumed = true; + } else if(event.event == SubmenuIndexDelete) { + scene_manager_next_scene(mag->scene_manager, MagSceneDeleteConfirm); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(mag->scene_manager, MagSceneSavedInfo); + consumed = true; + } + } + + return consumed; +} + +void mag_scene_saved_menu_on_exit(void* context) { + Mag* mag = context; + + submenu_reset(mag->submenu); +} diff --git a/applications/external/magspoof/scenes/mag_scene_start.c b/applications/external/magspoof/scenes/mag_scene_start.c new file mode 100644 index 000000000..538234189 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_start.c @@ -0,0 +1,71 @@ +#include "../mag_i.h" + +typedef enum { + SubmenuIndexSaved, + SubmenuIndexRead, + //SubmenuIndexAddManually, + SubmenuIndexAbout, +} SubmenuIndex; + +static void mag_scene_start_submenu_callback(void* context, uint32_t index) { + Mag* mag = context; + + view_dispatcher_send_custom_event(mag->view_dispatcher, index); +} + +void mag_scene_start_on_enter(void* context) { + Mag* mag = context; + Submenu* submenu = mag->submenu; + + submenu_add_item(submenu, "Saved", SubmenuIndexSaved, mag_scene_start_submenu_callback, mag); + submenu_add_item(submenu, "Read", SubmenuIndexRead, mag_scene_start_submenu_callback, mag); + //submenu_add_item( + // submenu, "Add Manually", SubmenuIndexAddManually, mag_scene_start_submenu_callback, mag); + submenu_add_item(submenu, "About", SubmenuIndexAbout, mag_scene_start_submenu_callback, mag); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(mag->scene_manager, MagSceneStart)); + + // clear key + furi_string_reset(mag->file_name); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewSubmenu); +} + +bool mag_scene_start_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case SubmenuIndexSaved: + furi_string_set(mag->file_path, MAG_APP_FOLDER); + scene_manager_next_scene(mag->scene_manager, MagSceneFileSelect); + consumed = true; + break; + + case SubmenuIndexRead: + scene_manager_next_scene(mag->scene_manager, MagSceneRead); + consumed = true; + break; + //case SubmenuIndexAddManually: + // scene_manager_next_scene(mag->scene_manager, MagSceneInputValue); + // consumed = true; + // break; + case SubmenuIndexAbout: + scene_manager_next_scene(mag->scene_manager, MagSceneAbout); + consumed = true; + break; + } + + scene_manager_set_scene_state(mag->scene_manager, MagSceneStart, event.event); + } + + return consumed; +} + +void mag_scene_start_on_exit(void* context) { + Mag* mag = context; + + submenu_reset(mag->submenu); +} \ No newline at end of file diff --git a/applications/external/magspoof/scenes/mag_scene_under_construction.c b/applications/external/magspoof/scenes/mag_scene_under_construction.c new file mode 100644 index 000000000..520f0a792 --- /dev/null +++ b/applications/external/magspoof/scenes/mag_scene_under_construction.c @@ -0,0 +1,40 @@ +#include "../mag_i.h" + +void mag_scene_under_construction_on_enter(void* context) { + Mag* mag = context; + Widget* widget = mag->widget; + + FuriString* tmp_str; + tmp_str = furi_string_alloc(); + + widget_add_button_element(widget, GuiButtonTypeLeft, "Back", mag_widget_callback, mag); + + furi_string_printf(tmp_str, "Under construction!"); + widget_add_string_element( + widget, 64, 4, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str)); + furi_string_reset(tmp_str); + + view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); + furi_string_free(tmp_str); +} + +bool mag_scene_under_construction_on_event(void* context, SceneManagerEvent event) { + Mag* mag = context; + SceneManager* scene_manager = mag->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = true; + + scene_manager_previous_scene(scene_manager); + } + } + + return consumed; +} + +void mag_scene_under_construction_on_exit(void* context) { + Mag* mag = context; + widget_reset(mag->widget); +} \ No newline at end of file diff --git a/applications/external/mandelbrot/application.fam b/applications/external/mandelbrot/application.fam index b5a3f4ccc..7c476ced8 100644 --- a/applications/external/mandelbrot/application.fam +++ b/applications/external/mandelbrot/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_MANDELBROT_GAME"], requires=["gui"], stack_size=1 * 1024, - order=130, fap_icon="Mandelbrot.png", fap_category="Media", fap_author="@Possibly-Matt", diff --git a/applications/external/mass_storage/application.fam b/applications/external/mass_storage/application.fam index 402affc9c..66d18422c 100644 --- a/applications/external/mass_storage/application.fam +++ b/applications/external/mass_storage/application.fam @@ -8,7 +8,8 @@ App( "dialogs", ], stack_size=2 * 1024, - order=20, + fap_description="Implements a mass storage device over USB for disk images", + fap_version="1.2", fap_icon="assets/mass_storage_10px.png", fap_icon_assets="assets", fap_category="USB", diff --git a/applications/external/mass_storage/helpers/mass_storage_scsi.c b/applications/external/mass_storage/helpers/mass_storage_scsi.c index c7419d31c..c1efacf8e 100644 --- a/applications/external/mass_storage/helpers/mass_storage_scsi.c +++ b/applications/external/mass_storage/helpers/mass_storage_scsi.c @@ -7,7 +7,8 @@ #define SCSI_TEST_UNIT_READY (0x00) #define SCSI_REQUEST_SENSE (0x03) #define SCSI_INQUIRY (0x12) -#define SCSI_READ_CAPACITY_6 (0x25) +#define SCSI_READ_FORMAT_CAPACITIES (0x23) +#define SCSI_READ_CAPACITY_10 (0x25) #define SCSI_MODE_SENSE_6 (0x1A) #define SCSI_READ_10 (0x28) #define SCSI_PREVENT_MEDIUM_REMOVAL (0x1E) @@ -20,7 +21,7 @@ bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) { scsi->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; return false; } - // FURI_LOG_I(TAG, "START %02x", cmd[0]); + FURI_LOG_T(TAG, "START %02X", cmd[0]); scsi->cmd = cmd; scsi->cmd_len = len; scsi->rx_done = false; @@ -30,14 +31,14 @@ bool scsi_cmd_start(SCSISession* scsi, uint8_t* cmd, uint8_t len) { 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); + FURI_LOG_D(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); + FURI_LOG_D(TAG, "SCSI_READ_10 %08lX %04X", scsi->read_10.lba, scsi->read_10.count); return true; }; break; } @@ -45,7 +46,7 @@ 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) { - // FURI_LOG_I(TAG, "RX %02x len %d", scsi->cmd[0], len); + FURI_LOG_T(TAG, "RX %02X len %lu", scsi->cmd[0], len); if(scsi->rx_done) return false; switch(scsi->cmd[0]) { case SCSI_WRITE_10: { @@ -61,7 +62,7 @@ bool scsi_cmd_rx_data(SCSISession* scsi, uint8_t* data, uint32_t len) { return result; }; break; default: { - FURI_LOG_W(TAG, "unexpected scsi rx data cmd=%02x", scsi->cmd[0]); + 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; @@ -70,11 +71,11 @@ 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) { - // FURI_LOG_I(TAG, "TX %02x cap %d", scsi->cmd[0], cap); + FURI_LOG_T(TAG, "TX %02X cap %lu", 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"); + FURI_LOG_D(TAG, "SCSI_REQUEST_SENSE"); if(cap < 18) return false; memset(data, 0, cap); data[0] = 0x70; // fixed format sense data @@ -102,31 +103,73 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t return true; }; break; case SCSI_INQUIRY: { - FURI_LOG_I(TAG, "SCSI_INQUIRY"); + FURI_LOG_D(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; + if(evpd == 0) { + if(page_code != 0) 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; + } else { + if(page_code != 0x80) { + FURI_LOG_W(TAG, "Unsupported VPD code %02X", page_code); + return false; + } + data[0] = 0x00; + data[1] = 0x80; + data[2] = 0x00; + data[3] = 0x01; // Serial len + data[4] = '0'; + *len = 5; + scsi->tx_done = true; + return true; + } + }; break; + case SCSI_READ_FORMAT_CAPACITIES: { + FURI_LOG_D(TAG, "SCSI_READ_FORMAT_CAPACITIES"); + if(cap < 12) { + return false; + } + uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx); + uint32_t block_size = SCSI_BLOCK_SIZE; + // Capacity List Header + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 8; + + // Capacity Descriptor + data[4] = (n_blocks - 1) >> 24; + data[5] = (n_blocks - 1) >> 16; + data[6] = (n_blocks - 1) >> 8; + data[7] = (n_blocks - 1) & 0xFF; + data[8] = 0x02; // Formatted media + data[9] = block_size >> 16; + data[10] = block_size >> 8; + data[11] = block_size & 0xFF; + *len = 12; scsi->tx_done = true; return true; }; break; - case SCSI_READ_CAPACITY_6: { - FURI_LOG_I(TAG, "SCSI_READ_CAPACITY_6"); + case SCSI_READ_CAPACITY_10: { + FURI_LOG_D(TAG, "SCSI_READ_CAPACITY_10"); if(cap < 8) return false; uint32_t n_blocks = scsi->fn.num_blocks(scsi->fn.ctx); uint32_t block_size = SCSI_BLOCK_SIZE; @@ -143,7 +186,7 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t return true; }; break; case SCSI_MODE_SENSE_6: { - FURI_LOG_I(TAG, "SCSI_MODE_SENSE_6 %lu", cap); + FURI_LOG_D(TAG, "SCSI_MODE_SENSE_6 %lu", cap); if(cap < 4) return false; data[0] = 3; // mode data length (len - 1) data[1] = 0; // medium type @@ -167,7 +210,7 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t return result; }; break; default: { - FURI_LOG_W(TAG, "unexpected scsi tx data cmd=%02x", scsi->cmd[0]); + 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; @@ -176,7 +219,7 @@ bool scsi_cmd_tx_data(SCSISession* scsi, uint8_t* data, uint32_t* len, uint32_t } bool scsi_cmd_end(SCSISession* scsi) { - // FURI_LOG_I(TAG, "END %02x", scsi->cmd[0]); + FURI_LOG_T(TAG, "END %02X", scsi->cmd[0]); uint8_t* cmd = scsi->cmd; uint8_t len = scsi->cmd_len; scsi->cmd = NULL; @@ -187,33 +230,34 @@ bool scsi_cmd_end(SCSISession* scsi) { case SCSI_REQUEST_SENSE: case SCSI_INQUIRY: - case SCSI_READ_CAPACITY_6: + case SCSI_READ_FORMAT_CAPACITIES: + case SCSI_READ_CAPACITY_10: 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"); + FURI_LOG_D(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); + FURI_LOG_D(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); + FURI_LOG_D(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]); + 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; diff --git a/applications/external/mass_storage/helpers/mass_storage_scsi.h b/applications/external/mass_storage/helpers/mass_storage_scsi.h index 24c24a69e..a35d6aff3 100644 --- a/applications/external/mass_storage/helpers/mass_storage_scsi.h +++ b/applications/external/mass_storage/helpers/mass_storage_scsi.h @@ -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); +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 index b0a55a592..f493203a6 100644 --- a/applications/external/mass_storage/helpers/mass_storage_usb.c +++ b/applications/external/mass_storage/helpers/mass_storage_usb.c @@ -44,6 +44,7 @@ typedef struct { uint8_t cmd_len; uint8_t cmd[16]; } __attribute__((packed)) CBW; + typedef struct { uint32_t sig; uint32_t tag; @@ -80,11 +81,11 @@ static int32_t mass_thread_worker(void* context) { while(true) { uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever); if(flags & EventExit) { - FURI_LOG_I(TAG, "exit"); + FURI_LOG_D(TAG, "exit"); break; } if(flags & EventReset) { - FURI_LOG_I(TAG, "reset"); + FURI_LOG_D(TAG, "reset"); scsi.sk = 0; scsi.asc = 0; memset(&cbw, 0, sizeof(cbw)); @@ -99,10 +100,10 @@ static int32_t mass_thread_worker(void* context) { if(flags & EventRxTx) do { switch(state) { case StateReadCBW: { - // FURI_LOG_I(TAG, "StateReadCBW"); + FURI_LOG_T(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"); + FURI_LOG_T(TAG, "cbw not ready"); break; } if(len != sizeof(cbw) || cbw.sig != CBW_SIG) { @@ -131,14 +132,14 @@ static int32_t mass_thread_worker(void* context) { continue; }; break; case StateReadData: { - // FURI_LOG_I(TAG, "StateReadData %d/%d", buf_len, cbw.len); + FURI_LOG_T(TAG, "StateReadData %lu/%lu", 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 %lu -> %lu", buf_cap, buf_clamp); + FURI_LOG_T(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp); if(buf) { free(buf); } @@ -149,10 +150,10 @@ static int32_t mass_thread_worker(void* context) { 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); + FURI_LOG_T(TAG, "rx not ready %ld", len); break; } - // FURI_LOG_I(TAG, "clamp %ld len %d", buf_clamp, len); + FURI_LOG_T(TAG, "clamp %lu len %ld", buf_clamp, len); buf_len += len; } if(buf_len == buf_clamp) { @@ -172,14 +173,14 @@ static int32_t mass_thread_worker(void* context) { continue; }; break; case StateWriteData: { - // FURI_LOG_I(TAG, "StateWriteData %d", cbw.len); + FURI_LOG_T(TAG, "StateWriteData %lu", 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 %lu -> %lu", buf_cap, buf_clamp); + FURI_LOG_T(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp); if(buf) { free(buf); } @@ -198,7 +199,7 @@ static int32_t mass_thread_worker(void* context) { 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); + FURI_LOG_T(TAG, "tx not ready %ld", len); break; } buf_sent += len; @@ -210,7 +211,7 @@ static int32_t mass_thread_worker(void* context) { continue; }; break; case StateBuildCSW: { - // FURI_LOG_I(TAG, "StateBuildCSW"); + FURI_LOG_T(TAG, "StateBuildCSW"); csw.sig = CSW_SIG; csw.tag = cbw.tag; if(scsi_cmd_end(&scsi)) { @@ -223,7 +224,7 @@ static int32_t mass_thread_worker(void* context) { continue; }; break; case StateWriteCSW: { - // FURI_LOG_I(TAG, "StateWriteCSW"); + FURI_LOG_T(TAG, "StateWriteCSW"); if(csw.status) { FURI_LOG_W( TAG, @@ -235,7 +236,7 @@ static int32_t mass_thread_worker(void* context) { } int32_t len = usbd_ep_write(dev, USB_MSC_TX_EP, &csw, sizeof(csw)); if(len < 0) { - // FURI_LOG_I(TAG, "csw not ready"); + FURI_LOG_T(TAG, "csw not ready"); break; } if(len != sizeof(csw)) { @@ -477,5 +478,4 @@ MassStorageUsb* mass_storage_usb_start(const char* filename, SCSIDeviceFunc fn) 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/mass_storage_app.c b/applications/external/mass_storage/mass_storage_app.c index 3612d8c07..ccb3369d9 100644 --- a/applications/external/mass_storage/mass_storage_app.c +++ b/applications/external/mass_storage/mass_storage_app.c @@ -21,6 +21,19 @@ static void mass_storage_app_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } +void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show) { + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + MassStorageApp* mass_storage_app_alloc(char* arg) { MassStorageApp* app = malloc(sizeof(MassStorageApp)); app->file_path = furi_string_alloc(); @@ -33,9 +46,27 @@ MassStorageApp* mass_storage_app_alloc(char* arg) { 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); + app->create_image_size = (uint8_t)-1; + SDInfo sd_info; + if(storage_sd_info(app->fs_api, &sd_info) == FSE_OK) { + switch(sd_info.fs_type) { + case FST_FAT12: + app->create_image_max = 16LL * 1024 * 1024; + break; + case FST_FAT16: + app->create_image_max = 2LL * 1024 * 1024 * 1024; + break; + case FST_FAT32: + app->create_image_max = 4LL * 1024 * 1024 * 1024; + break; + default: + app->create_image_max = 0; + break; + } + } + app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); @@ -55,12 +86,36 @@ MassStorageApp* mass_storage_app_alloc(char* arg) { MassStorageAppViewWork, mass_storage_get_view(app->mass_storage_view)); + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + MassStorageAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, MassStorageAppViewSubmenu, submenu_get_view(app->submenu)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, MassStorageAppViewTextInput, text_input_get_view(app->text_input)); + + app->popup = popup_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, MassStorageAppViewPopup, popup_get_view(app->popup)); + + app->loading = loading_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, MassStorageAppViewLoading, loading_get_view(app->loading)); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + scene_manager_set_scene_state( + app->scene_manager, MassStorageSceneStart, MassStorageSceneFileSelect); if(storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) { scene_manager_next_scene(app->scene_manager, MassStorageSceneWork); } else { - scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect); + scene_manager_next_scene(app->scene_manager, MassStorageSceneStart); } return app; @@ -71,7 +126,18 @@ void mass_storage_app_free(MassStorageApp* app) { // Views view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewWork); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewVarItemList); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewSubmenu); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewTextInput); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewPopup); + view_dispatcher_remove_view(app->view_dispatcher, MassStorageAppViewLoading); + mass_storage_free(app->mass_storage_view); + variable_item_list_free(app->var_item_list); + submenu_free(app->submenu); + text_input_free(app->text_input); + popup_free(app->popup); + loading_free(app->loading); // View dispatcher view_dispatcher_free(app->view_dispatcher); @@ -82,7 +148,6 @@ void mass_storage_app_free(MassStorageApp* app) { // Close records 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 5beebd3f3..fe8a2d944 100644 --- a/applications/external/mass_storage/mass_storage_app_i.h +++ b/applications/external/mass_storage/mass_storage_app_i.h @@ -8,13 +8,16 @@ #include #include #include -#include #include -#include #include -#include +#include +#include +#include +#include #include #include "views/mass_storage_view.h" +#include +#include #define MASS_STORAGE_APP_PATH_FOLDER STORAGE_APP_DATA_PATH_PREFIX #define MASS_STORAGE_APP_EXTENSION ".img" @@ -25,9 +28,12 @@ struct MassStorageApp { Storage* fs_api; ViewDispatcher* view_dispatcher; SceneManager* scene_manager; - NotificationApp* notifications; DialogsApp* dialogs; - Widget* widget; + VariableItemList* var_item_list; + Submenu* submenu; + TextInput* text_input; + Popup* popup; + Loading* loading; FuriString* file_path; File* file; @@ -35,9 +41,28 @@ struct MassStorageApp { FuriMutex* usb_mutex; MassStorageUsb* usb; + + uint64_t create_image_max; + uint8_t create_image_size; + char create_image_name[MASS_STORAGE_FILE_NAME_LEN]; + + uint32_t bytes_read, bytes_written; }; typedef enum { - MassStorageAppViewError, + MassStorageAppViewVarItemList, + MassStorageAppViewSubmenu, + MassStorageAppViewTextInput, + MassStorageAppViewPopup, + MassStorageAppViewLoading, MassStorageAppViewWork, } MassStorageAppView; + +enum MassStorageCustomEvent { + // Reserve first 100 events for button types and indexes, starting from 0 + MassStorageCustomEventReserved = 100, + + MassStorageCustomEventEject, +}; + +void mass_storage_app_show_loading_popup(MassStorageApp* app, bool show); diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_config.h b/applications/external/mass_storage/scenes/mass_storage_scene_config.h index 2ce2d41e9..ff0418fdf 100644 --- a/applications/external/mass_storage/scenes/mass_storage_scene_config.h +++ b/applications/external/mass_storage/scenes/mass_storage_scene_config.h @@ -1,2 +1,5 @@ +ADD_SCENE(mass_storage, start, Start) ADD_SCENE(mass_storage, file_select, FileSelect) ADD_SCENE(mass_storage, work, Work) +ADD_SCENE(mass_storage, create_image, CreateImage) +ADD_SCENE(mass_storage, create_image_name, CreateImageName) diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_create_image.c b/applications/external/mass_storage/scenes/mass_storage_scene_create_image.c new file mode 100644 index 000000000..a2ad46ef6 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_create_image.c @@ -0,0 +1,187 @@ +#include "../mass_storage_app_i.h" +#include + +enum VarItemListIndex { + VarItemListIndexImageSize, + VarItemListIndexImageName, + VarItemListIndexCreateImage, +}; + +void mass_storage_scene_create_image_var_item_list_callback(void* context, uint32_t index) { + MassStorageApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static const struct { + char* name; + uint64_t value; +} image_sizes[] = { + {"1MB", 1LL * 1024 * 1024}, + {"2MB", 2LL * 1024 * 1024}, + {"4MB", 4LL * 1024 * 1024}, + {"8MB", 8LL * 1024 * 1024}, + {"16MB", 16LL * 1024 * 1024}, + {"32MB", 32LL * 1024 * 1024}, + {"64MB", 64LL * 1024 * 1024}, + {"128MB", 128LL * 1024 * 1024}, + {"256MB", 256LL * 1024 * 1024}, + {"512MB", 512LL * 1024 * 1024}, + {"1GB", 1LL * 1024 * 1024 * 1024}, + {"2GB", 2LL * 1024 * 1024 * 1024}, + {"4GB", 4LL * 1024 * 1024 * 1024}, + {"8GB", 8LL * 1024 * 1024 * 1024}, + {"16GB", 16LL * 1024 * 1024 * 1024}, + {"32GB", 32LL * 1024 * 1024 * 1024}, + {"64GB", 64LL * 1024 * 1024 * 1024}, + {"128GB", 128LL * 1024 * 1024 * 1024}, + {"256GB", 256LL * 1024 * 1024 * 1024}, + {"512GB", 512LL * 1024 * 1024 * 1024}, +}; +static void mass_storage_scene_create_image_image_size_changed(VariableItem* item) { + MassStorageApp* app = variable_item_get_context(item); + app->create_image_size = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, image_sizes[app->create_image_size].name); +} + +void mass_storage_scene_create_image_on_enter(void* context) { + MassStorageApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + uint8_t size_count = COUNT_OF(image_sizes); + if(app->create_image_max) { + for(size_t i = 1; i < size_count; i++) { + if(image_sizes[i].value > app->create_image_max) { + size_count = i; + break; + } + } + } + if(app->create_image_size == (uint8_t)-1) { + app->create_image_size = CLAMP(7, size_count - 2, 0); // 7 = 128MB + } + item = variable_item_list_add( + var_item_list, + "Image Size", + size_count, + mass_storage_scene_create_image_image_size_changed, + app); + variable_item_set_current_value_index(item, app->create_image_size); + variable_item_set_current_value_text(item, image_sizes[app->create_image_size].name); + + item = variable_item_list_add(var_item_list, "Image Name", 0, NULL, app); + variable_item_set_current_value_text(item, app->create_image_name); + + variable_item_list_add(var_item_list, "Create Image", 0, NULL, app); + + variable_item_list_set_enter_callback( + var_item_list, mass_storage_scene_create_image_var_item_list_callback, app); + + variable_item_list_set_header(var_item_list, "Create Disk Image"); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, MassStorageSceneCreateImage)); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewVarItemList); +} + +static void popup_callback_ok(void* context) { + MassStorageApp* app = context; + scene_manager_set_scene_state( + app->scene_manager, MassStorageSceneStart, MassStorageSceneFileSelect); + scene_manager_previous_scene(app->scene_manager); + scene_manager_next_scene(app->scene_manager, MassStorageSceneFileSelect); +} + +static void popup_callback_error(void* context) { + MassStorageApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewVarItemList); +} + +bool mass_storage_scene_create_image_on_event(void* context, SceneManagerEvent event) { + MassStorageApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + app->scene_manager, MassStorageSceneCreateImage, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexImageName: + scene_manager_next_scene(app->scene_manager, MassStorageSceneCreateImageName); + break; + case VarItemListIndexCreateImage: { + mass_storage_app_show_loading_popup(app, true); + const char* name = strnlen(app->create_image_name, sizeof(app->create_image_name)) ? + app->create_image_name : + image_sizes[app->create_image_size].name; + furi_string_printf( + app->file_path, + "%s/%s%s", + MASS_STORAGE_APP_PATH_FOLDER, + name, + MASS_STORAGE_APP_EXTENSION); + + app->file = storage_file_alloc(app->fs_api); + const char* error = NULL; + bool success = false; + + size_t wipe_4k = 4096; + uint8_t* buffer = malloc(wipe_4k); + do { + if(!storage_file_open( + app->file, + furi_string_get_cstr(app->file_path), + FSAM_WRITE, + FSOM_CREATE_NEW)) + break; + + uint64_t size = image_sizes[app->create_image_size].value; + if(size == app->create_image_max) size--; + if(!storage_file_expand(app->file, size)) break; + + // Zero out first 4k - partition table and adjacent data + if(!storage_file_seek(app->file, 0, true)) break; + if(!storage_file_write(app->file, buffer, wipe_4k)) break; + + success = true; + } while(false); + free(buffer); + + if(!success) { + error = storage_file_get_error_desc(app->file); + storage_file_close(app->file); + storage_common_remove(app->fs_api, furi_string_get_cstr(app->file_path)); + } + storage_file_free(app->file); + mass_storage_app_show_loading_popup(app, false); + + if(error) { + popup_set_header( + app->popup, "Error Creating Image!", 64, 26, AlignCenter, AlignCenter); + popup_set_text(app->popup, error, 64, 40, AlignCenter, AlignCenter); + popup_set_callback(app->popup, popup_callback_error); + } else { + popup_set_header(app->popup, "Image Created!", 64, 32, AlignCenter, AlignCenter); + popup_set_text(app->popup, "", 0, 0, AlignLeft, AlignBottom); + popup_set_callback(app->popup, popup_callback_ok); + } + popup_set_context(app->popup, app); + popup_set_timeout(app->popup, 0); + popup_disable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewPopup); + break; + } + default: + break; + } + } + + return consumed; +} + +void mass_storage_scene_create_image_on_exit(void* context) { + MassStorageApp* app = context; + variable_item_list_reset(app->var_item_list); +} diff --git a/applications/external/mass_storage/scenes/mass_storage_scene_create_image_name.c b/applications/external/mass_storage/scenes/mass_storage_scene_create_image_name.c new file mode 100644 index 000000000..38efa7cb6 --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_create_image_name.c @@ -0,0 +1,52 @@ +#include "../mass_storage_app_i.h" + +enum TextInputIndex { + TextInputResultOk, +}; + +static void mass_storage_scene_create_image_name_text_input_callback(void* context) { + MassStorageApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); +} + +void mass_storage_scene_create_image_name_on_enter(void* context) { + MassStorageApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Image name, empty = default"); + + text_input_set_minimum_length(text_input, 0); + + text_input_set_result_callback( + text_input, + mass_storage_scene_create_image_name_text_input_callback, + app, + app->create_image_name, + sizeof(app->create_image_name), + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewTextInput); +} + +bool mass_storage_scene_create_image_name_on_event(void* context, SceneManagerEvent event) { + MassStorageApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case TextInputResultOk: + scene_manager_previous_scene(app->scene_manager); + break; + default: + break; + } + } + + return consumed; +} + +void mass_storage_scene_create_image_name_on_exit(void* context) { + MassStorageApp* app = context; + text_input_reset(app->text_input); +} 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 a3c002fb2..51d4f4dd2 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 @@ -1,14 +1,13 @@ #include "../mass_storage_app_i.h" #include "furi_hal_power.h" -#include static bool mass_storage_file_select(MassStorageApp* mass_storage) { furi_assert(mass_storage); DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, MASS_STORAGE_APP_EXTENSION, &I_mass_storage_10px); + dialog_file_browser_set_basic_options(&browser_options, "*", &I_mass_storage_10px); browser_options.base_path = MASS_STORAGE_APP_PATH_FOLDER; + browser_options.hide_ext = false; // Input events and views are managed by file_select bool res = dialog_file_browser_show( @@ -23,18 +22,15 @@ void mass_storage_scene_file_select_on_enter(void* context) { 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) { 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_start.c b/applications/external/mass_storage/scenes/mass_storage_scene_start.c new file mode 100644 index 000000000..c85d73dab --- /dev/null +++ b/applications/external/mass_storage/scenes/mass_storage_scene_start.c @@ -0,0 +1,49 @@ +#include "../mass_storage_app_i.h" + +static void mass_storage_scene_start_submenu_callback(void* context, uint32_t index) { + MassStorageApp* app = context; + scene_manager_set_scene_state(app->scene_manager, MassStorageSceneStart, index); + scene_manager_next_scene(app->scene_manager, index); +} + +void mass_storage_scene_start_on_enter(void* context) { + MassStorageApp* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Select Disk Image", + MassStorageSceneFileSelect, + mass_storage_scene_start_submenu_callback, + app); + + submenu_add_item( + submenu, + "Create Disk Image", + MassStorageSceneCreateImage, + mass_storage_scene_start_submenu_callback, + app); + scene_manager_set_scene_state(app->scene_manager, MassStorageSceneCreateImage, 0); + + submenu_set_header(submenu, "USB Mass Storage"); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, MassStorageSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewSubmenu); +} + +bool mass_storage_scene_start_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + } + + return consumed; +} + +void mass_storage_scene_start_on_exit(void* context) { + MassStorageApp* app = context; + submenu_reset(app->submenu); +} 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 9776e9f17..a2d533e4b 100644 --- a/applications/external/mass_storage/scenes/mass_storage_scene_work.c +++ b/applications/external/mass_storage/scenes/mass_storage_scene_work.c @@ -13,20 +13,21 @@ static bool file_read( 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); + FURI_LOG_T(TAG, "file_read lba=%08lX count=%04X out_cap=%08lX", 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); + FURI_LOG_T(TAG, "%lu/%lu", *out_len, count * SCSI_BLOCK_SIZE); + app->bytes_read += *out_len; 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); + FURI_LOG_T(TAG, "file_write lba=%08lX count=%04X len=%08lX", lba, count, len); if(len != count * SCSI_BLOCK_SIZE) { FURI_LOG_W(TAG, "bad write params count=%u len=%lu", count, len); return false; @@ -35,6 +36,7 @@ static bool file_write(void* ctx, uint32_t lba, uint16_t count, uint8_t* buf, ui FURI_LOG_W(TAG, "seek failed"); return false; } + app->bytes_written += len; return storage_file_write(app->file, buf, len) == len; } @@ -45,27 +47,46 @@ static uint32_t file_num_blocks(void* ctx) { static void file_eject(void* ctx) { MassStorageApp* app = ctx; - FURI_LOG_I(TAG, "EJECT"); - furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk); - mass_storage_usb_stop(app->usb); - app->usb = NULL; - furi_check(furi_mutex_release(app->usb_mutex) == FuriStatusOk); + FURI_LOG_D(TAG, "EJECT"); + view_dispatcher_send_custom_event(app->view_dispatcher, MassStorageCustomEventEject); } bool mass_storage_scene_work_on_event(void* context, SceneManagerEvent event) { MassStorageApp* app = context; - if(event.type == SceneManagerEventTypeTick) { - bool ejected; - furi_check(furi_mutex_acquire(app->usb_mutex, FuriWaitForever) == FuriStatusOk); - ejected = app->usb == NULL; - furi_check(furi_mutex_release(app->usb_mutex) == FuriStatusOk); - if(ejected) scene_manager_previous_scene(app->scene_manager); + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MassStorageCustomEventEject) { + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, MassStorageSceneFileSelect); + if(!consumed) { + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, MassStorageSceneStart); + } + } + } else if(event.type == SceneManagerEventTypeTick) { + mass_storage_set_stats(app->mass_storage_view, app->bytes_read, app->bytes_written); + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, MassStorageSceneFileSelect); + if(!consumed) { + consumed = scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, MassStorageSceneStart); + } } - return false; + return consumed; } void mass_storage_scene_work_on_enter(void* context) { MassStorageApp* app = context; + app->bytes_read = app->bytes_written = 0; + + if(!storage_file_exists(app->fs_api, furi_string_get_cstr(app->file_path))) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, MassStorageSceneStart); + return; + } + + mass_storage_app_show_loading_popup(app, true); app->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); @@ -92,13 +113,18 @@ void mass_storage_scene_work_on_enter(void* context) { furi_string_free(file_name); + mass_storage_app_show_loading_popup(app, false); view_dispatcher_switch_to_view(app->view_dispatcher, MassStorageAppViewWork); } void mass_storage_scene_work_on_exit(void* context) { MassStorageApp* app = context; + mass_storage_app_show_loading_popup(app, true); - furi_mutex_free(app->usb_mutex); + if(app->usb_mutex) { + furi_mutex_free(app->usb_mutex); + app->usb_mutex = NULL; + } if(app->usb) { mass_storage_usb_stop(app->usb); app->usb = NULL; @@ -107,4 +133,5 @@ void mass_storage_scene_work_on_exit(void* context) { storage_file_free(app->file); app->file = NULL; } + mass_storage_app_show_loading_popup(app, false); } diff --git a/applications/external/mass_storage/views/mass_storage_view.c b/applications/external/mass_storage/views/mass_storage_view.c index 97e80df71..25eeb5c19 100644 --- a/applications/external/mass_storage/views/mass_storage_view.c +++ b/applications/external/mass_storage/views/mass_storage_view.c @@ -1,16 +1,30 @@ #include "mass_storage_view.h" +#include "../mass_storage_app_i.h" #include -#include -#include struct MassStorage { View* view; }; typedef struct { - FuriString* file_name; + FuriString *file_name, *status_string; + uint32_t read_speed, write_speed; + uint32_t bytes_read, bytes_written; + uint32_t update_time; } MassStorageModel; +static void append_suffixed_byte_count(FuriString* string, uint32_t count) { + if(count < 1024) { + furi_string_cat_printf(string, "%luB", count); + } else if(count < 1024 * 1024) { + furi_string_cat_printf(string, "%luK", count / 1024); + } else if(count < 1024 * 1024 * 1024) { + furi_string_cat_printf(string, "%.1fM", (double)count / (1024 * 1024)); + } else { + furi_string_cat_printf(string, "%.1fG", (double)count / (1024 * 1024 * 1024)); + } +} + static void mass_storage_draw_callback(Canvas* canvas, void* _model) { MassStorageModel* model = _model; @@ -20,10 +34,28 @@ static void mass_storage_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned( canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "USB Mass Storage"); - elements_string_fit_width(canvas, model->file_name, 87 - 2); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 12, 25, "Disc image:"); - canvas_draw_str(canvas, 12, 40, furi_string_get_cstr(model->file_name)); + canvas_set_font(canvas, FontBatteryPercent); + elements_string_fit_width(canvas, model->file_name, 89 - 2); + canvas_draw_str_aligned( + canvas, 92, 24, AlignRight, AlignBottom, furi_string_get_cstr(model->file_name)); + + furi_string_set_str(model->status_string, "R:"); + append_suffixed_byte_count(model->status_string, model->bytes_read); + if(model->read_speed) { + furi_string_cat_str(model->status_string, "/"); + append_suffixed_byte_count(model->status_string, model->read_speed); + furi_string_cat_str(model->status_string, "s"); + } + canvas_draw_str(canvas, 14, 34, furi_string_get_cstr(model->status_string)); + + furi_string_set_str(model->status_string, "W:"); + append_suffixed_byte_count(model->status_string, model->bytes_written); + if(model->write_speed) { + furi_string_cat_str(model->status_string, "/"); + append_suffixed_byte_count(model->status_string, model->write_speed); + furi_string_cat_str(model->status_string, "s"); + } + canvas_draw_str(canvas, 14, 43, furi_string_get_cstr(model->status_string)); } MassStorage* mass_storage_alloc() { @@ -34,7 +66,10 @@ MassStorage* mass_storage_alloc() { with_view_model( mass_storage->view, MassStorageModel * model, - { model->file_name = furi_string_alloc(); }, + { + model->file_name = furi_string_alloc(); + model->status_string = furi_string_alloc(); + }, false); view_set_context(mass_storage->view, mass_storage); view_set_draw_callback(mass_storage->view, mass_storage_draw_callback); @@ -47,7 +82,10 @@ void mass_storage_free(MassStorage* mass_storage) { with_view_model( mass_storage->view, MassStorageModel * model, - { furi_string_free(model->file_name); }, + { + furi_string_free(model->file_name); + furi_string_free(model->status_string); + }, false); view_free(mass_storage->view); free(mass_storage); @@ -66,3 +104,19 @@ void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name) { { furi_string_set(model->file_name, name); }, true); } + +void mass_storage_set_stats(MassStorage* mass_storage, uint32_t read, uint32_t written) { + with_view_model( + mass_storage->view, + MassStorageModel * model, + { + uint32_t now = furi_get_tick(); + model->read_speed = (read - model->bytes_read) * 1000 / (now - model->update_time); + model->write_speed = + (written - model->bytes_written) * 1000 / (now - model->update_time); + model->bytes_read = read; + model->bytes_written = written; + model->update_time = now; + }, + true); +} diff --git a/applications/external/mass_storage/views/mass_storage_view.h b/applications/external/mass_storage/views/mass_storage_view.h index 96df01c8d..2edbf2a62 100644 --- a/applications/external/mass_storage/views/mass_storage_view.h +++ b/applications/external/mass_storage/views/mass_storage_view.h @@ -11,3 +11,5 @@ void mass_storage_free(MassStorage* mass_storage); View* mass_storage_get_view(MassStorage* mass_storage); void mass_storage_set_file_name(MassStorage* mass_storage, FuriString* name); + +void mass_storage_set_stats(MassStorage* mass_storage, uint32_t read, uint32_t written); diff --git a/applications/external/metronome/application.fam b/applications/external/metronome/application.fam index e6439db7b..b5a87a236 100644 --- a/applications/external/metronome/application.fam +++ b/applications/external/metronome/application.fam @@ -8,8 +8,8 @@ App( ], fap_icon="metronome_icon.png", fap_category="Media", + 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/metronome/gui_extensions.c b/applications/external/metronome/gui_extensions.c index 18494098e..4c7ad2252 100644 --- a/applications/external/metronome/gui_extensions.c +++ b/applications/external/metronome/gui_extensions.c @@ -1,5 +1,6 @@ #include #include +#include "metronome_icons.h" #include //lib can only do bottom left/right diff --git a/applications/external/metronome/metronome.c b/applications/external/metronome/metronome.c index 46231d66d..a01f4418d 100644 --- a/applications/external/metronome/metronome.c +++ b/applications/external/metronome/metronome.c @@ -178,7 +178,7 @@ static void timer_callback(void* ctx) { case Silent: break; } - } + }; // this is a bit of a kludge... if we are on vibro and unpronounced, stop vibro after half the usual duration switch(metronome_state->output_mode) { diff --git a/applications/external/mfkey32/application.fam b/applications/external/mfkey32/application.fam index 33d932ce9..4d8b3b062 100644 --- a/applications/external/mfkey32/application.fam +++ b/applications/external/mfkey32/application.fam @@ -9,9 +9,11 @@ App( "storage", ], stack_size=1 * 1024, - fap_icon="images/mfkey.png", + fap_description="Mf Classic key finder", + fap_version="1.0", + fap_icon="mfkey.png", fap_category="NFC", - fap_author="noproto", + fap_author="@noproto", fap_icon_assets="images", fap_weburl="https://github.com/noproto/FlipperMfkey", ) diff --git a/applications/external/mfkey32/mfkey.png b/applications/external/mfkey32/mfkey.png new file mode 100644 index 000000000..52ab29efb Binary files /dev/null and b/applications/external/mfkey32/mfkey.png differ diff --git a/applications/external/mfkey32/mfkey32.c b/applications/external/mfkey32/mfkey32.c index 5e790b01f..fc4c9db26 100644 --- a/applications/external/mfkey32/mfkey32.c +++ b/applications/external/mfkey32/mfkey32.c @@ -13,6 +13,7 @@ #include #include #include "mfkey32_icons.h" +#include #include #include #include diff --git a/applications/external/mifare_fuzzer/application.fam b/applications/external/mifare_fuzzer/application.fam index c90212286..5d02b9235 100644 --- a/applications/external/mifare_fuzzer/application.fam +++ b/applications/external/mifare_fuzzer/application.fam @@ -8,7 +8,6 @@ App( "gui", ], stack_size=4 * 1024, - order=30, fap_icon="images/mifare_fuzzer_10px.png", fap_category="NFC", fap_icon_assets="images", diff --git a/applications/external/mifare_nested/application.fam b/applications/external/mifare_nested/application.fam index b8e2aa0dd..a06f7d7ec 100644 --- a/applications/external/mifare_nested/application.fam +++ b/applications/external/mifare_nested/application.fam @@ -5,7 +5,6 @@ App( entry_point="mifare_nested_app", requires=["storage", "gui", "nfc"], stack_size=4 * 1024, - order=30, fap_icon="assets/icon.png", fap_category="NFC", fap_private_libs=[Lib(name="nested"), Lib(name="parity"), Lib(name="crypto1")], diff --git a/applications/external/mifare_nested/mifare_nested_i.h b/applications/external/mifare_nested/mifare_nested_i.h index b13a6c098..324328b44 100644 --- a/applications/external/mifare_nested/mifare_nested_i.h +++ b/applications/external/mifare_nested/mifare_nested_i.h @@ -20,6 +20,7 @@ #include #include #include "mifare_nested_icons.h" +#include #define NESTED_VERSION_APP "1.5.2" #define NESTED_GITHUB_LINK "https://github.com/AloneLiberty/FlipperNested" diff --git a/applications/external/minesweeper/application.fam b/applications/external/minesweeper/application.fam index 07b3e97bb..891bd7801 100644 --- a/applications/external/minesweeper/application.fam +++ b/applications/external/minesweeper/application.fam @@ -7,7 +7,6 @@ App( stack_size=8 * 1024, 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/minesweeper/minesweeper.c b/applications/external/minesweeper/minesweeper.c index 77d054384..4e92fba33 100644 --- a/applications/external/minesweeper/minesweeper.c +++ b/applications/external/minesweeper/minesweeper.c @@ -7,6 +7,8 @@ #include #include +#include + #include "assets.h" #define PLAYFIELD_WIDTH 16 @@ -237,7 +239,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); @@ -394,7 +396,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/morse_code/application.fam b/applications/external/morse_code/application.fam index d46b34628..d39f4738c 100644 --- a/applications/external/morse_code/application.fam +++ b/applications/external/morse_code/application.fam @@ -7,7 +7,6 @@ App( "gui", ], stack_size=1 * 1024, - order=20, fap_icon="morse_code_10px.png", fap_category="Media", fap_author="@wh00hw & @xMasterX", diff --git a/applications/external/multi_converter/application.fam b/applications/external/multi_converter/application.fam index 6844c73b3..51d1c0798 100644 --- a/applications/external/multi_converter/application.fam +++ b/applications/external/multi_converter/application.fam @@ -5,7 +5,6 @@ App( entry_point="multi_converter_app", requires=["gui"], stack_size=1 * 1024, - order=160, fap_icon="converter_10px.png", fap_category="Tools", fap_author="@theisolinearchip", diff --git a/applications/external/multi_dice/application.fam b/applications/external/multi_dice/application.fam index 15f5fe820..6bca3167a 100644 --- a/applications/external/multi_dice/application.fam +++ b/applications/external/multi_dice/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_DICE"], requires=["gui"], stack_size=2 * 1024, - order=70, fap_icon="dice.png", fap_category="Games", ) diff --git a/applications/external/multi_fuzzer/fuzzer.c b/applications/external/multi_fuzzer/fuzzer.c index bc9646873..5b85cc823 100644 --- a/applications/external/multi_fuzzer/fuzzer.c +++ b/applications/external/multi_fuzzer/fuzzer.c @@ -1,6 +1,5 @@ #include "fuzzer_i.h" #include "helpers/fuzzer_types.h" -#include static bool fuzzer_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -21,8 +20,6 @@ static void fuzzer_app_tick_event_callback(void* context) { } PacsFuzzerApp* fuzzer_app_alloc() { - dolphin_deed(DolphinDeedPluginStart); - PacsFuzzerApp* app = malloc(sizeof(PacsFuzzerApp)); app->fuzzer_state.menu_index = 0; diff --git a/applications/external/music_beeper/application.fam b/applications/external/music_beeper/application.fam index a15af7e32..24a592c4a 100644 --- a/applications/external/music_beeper/application.fam +++ b/applications/external/music_beeper/application.fam @@ -3,14 +3,11 @@ App( name="Music Beeper", apptype=FlipperAppType.EXTERNAL, entry_point="music_beeper_app", - cdefines=["APP_MUSIC_BEEPER"], requires=[ "gui", "dialogs", ], - provides=["music_beeper_start"], stack_size=2 * 1024, - order=45, fap_icon="music_10px.png", fap_icon_assets="icons", fap_category="Media", diff --git a/applications/external/music_player/application.fam b/applications/external/music_player/application.fam index 4d0858494..24ccb9a70 100644 --- a/applications/external/music_player/application.fam +++ b/applications/external/music_player/application.fam @@ -8,8 +8,8 @@ App( "dialogs", ], stack_size=2 * 1024, - order=45, fap_icon="music_10px.png", fap_category="Media", + fap_icon_assets="icons", fap_libs=["music_worker"], ) diff --git a/applications/external/music_player/music_player.c b/applications/external/music_player/music_player.c index 181eb60d6..aaec81346 100644 --- a/applications/external/music_player/music_player.c +++ b/applications/external/music_player/music_player.c @@ -3,6 +3,7 @@ #include #include +#include "music_player_icons.h" #include #include #include diff --git a/applications/external/music_tracker/application.fam b/applications/external/music_tracker/application.fam index 6c78a9bf6..5aba35cd3 100644 --- a/applications/external/music_tracker/application.fam +++ b/applications/external/music_tracker/application.fam @@ -7,7 +7,6 @@ App( "gui", ], stack_size=4 * 1024, - order=20, fap_icon="zero_tracker.png", fap_category="Media", fap_author="@DrZlo13", diff --git a/applications/external/nfc_magic/application.fam b/applications/external/nfc_magic/application.fam index 83a148f21..01c6dfaa8 100644 --- a/applications/external/nfc_magic/application.fam +++ b/applications/external/nfc_magic/application.fam @@ -9,7 +9,6 @@ App( "gui", ], stack_size=4 * 1024, - order=30, fap_icon="Nfc_10px.png", fap_category="NFC", fap_private_libs=[ diff --git a/applications/external/nfc_magic/nfc_magic.c b/applications/external/nfc_magic/nfc_magic.c index 0fce8bb66..68c9a65b5 100644 --- a/applications/external/nfc_magic/nfc_magic.c +++ b/applications/external/nfc_magic/nfc_magic.c @@ -1,5 +1,4 @@ #include "nfc_magic_i.h" -#include bool nfc_magic_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -174,7 +173,6 @@ int32_t nfc_magic_app(void* p) { UNUSED(p); NfcMagic* nfc_magic = nfc_magic_alloc(); - dolphin_deed(DolphinDeedPluginStart); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneStart); view_dispatcher_run(nfc_magic->view_dispatcher); diff --git a/applications/external/nfc_rfid_detector/application.fam b/applications/external/nfc_rfid_detector/application.fam index 70c91bc84..ffed6967a 100644 --- a/applications/external/nfc_rfid_detector/application.fam +++ b/applications/external/nfc_rfid_detector/application.fam @@ -6,8 +6,10 @@ App( entry_point="nfc_rfid_detector_app", requires=["gui"], stack_size=4 * 1024, - order=50, + fap_description="Identify the reader type: NFC (13 MHz) and/or RFID (125 KHz).", + fap_version="1.0", fap_icon="nfc_rfid_detector_10px.png", fap_category="Tools", fap_icon_assets="images", + fap_author="SkorP", ) 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 index faf253977..cf8ca936c 100644 --- 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 @@ -1,6 +1,6 @@ #include "nfc_rfid_detector_view_field_presence.h" #include "../nfc_rfid_detector_app_i.h" -#include +#include "nfc_rfid_detector_icons.h" #include #include diff --git a/applications/external/nightstand/application.fam b/applications/external/nightstand/application.fam index e94e24cf2..380c34b16 100644 --- a/applications/external/nightstand/application.fam +++ b/applications/external/nightstand/application.fam @@ -7,7 +7,6 @@ App( stack_size=2 * 1024, fap_icon="clock.png", fap_category="Tools", - order=81, fap_author="@nymda & @Willy-JL", fap_weburl="https://github.com/nymda/FlipperNightStand", fap_version="1.0", diff --git a/applications/external/nrf24batch/application.fam b/applications/external/nrf24batch/application.fam index ff8e5546e..aff3ffd30 100644 --- a/applications/external/nrf24batch/application.fam +++ b/applications/external/nrf24batch/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_NRF24BATCH"], requires=["gui"], stack_size=2 * 1024, - order=60, fap_icon="nrf24batch_10px.png", fap_category="GPIO", fap_private_libs=[ diff --git a/applications/external/nrf24batch/lib/nrf24/nrf24.c b/applications/external/nrf24batch/lib/nrf24/nrf24.c index 789002f80..e074860ed 100644 --- a/applications/external/nrf24batch/lib/nrf24/nrf24.c +++ b/applications/external/nrf24batch/lib/nrf24/nrf24.c @@ -8,6 +8,15 @@ #include void nrf24_init() { + // this is needed if multiple SPI devices are connected to the same bus but with different CS pins + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pc3, true); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pa4, true); + } + furi_hal_spi_bus_handle_init(nrf24_HANDLE); furi_hal_spi_acquire(nrf24_HANDLE); furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); @@ -19,13 +28,16 @@ void nrf24_deinit() { 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); + + // resetting the CS pins to floating + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog); + } } -void nrf24_spi_trx( - FuriHalSpiBusHandle* handle, - uint8_t* tx, - uint8_t* rx, - uint8_t size) { +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); @@ -38,7 +50,8 @@ uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) return buf[0]; } -uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { +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); @@ -57,7 +70,7 @@ uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, } uint8_t nrf24_read_register(FuriHalSpiBusHandle* handle, uint8_t reg) { - uint8_t buf[] = { R_REGISTER | (REGISTER_MASK & reg), 0 }; + uint8_t buf[] = {R_REGISTER | (REGISTER_MASK & reg), 0}; nrf24_spi_trx(handle, buf, buf, 2); return buf[1]; } @@ -190,7 +203,11 @@ uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { // 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 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 @@ -207,8 +224,9 @@ uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* re 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; + 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]; } @@ -241,7 +259,9 @@ uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t si 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); + 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); @@ -357,9 +377,8 @@ void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) { } } -uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen) -{ +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 + for(int i = 0; i < mlen; i++) addr[i] = mac[mlen - i - 1]; + return nrf24_write_buf_reg(nrf24_HANDLE, mac_addr, addr, mlen); +} diff --git a/applications/external/nrf24batch/lib/nrf24/nrf24.h b/applications/external/nrf24batch/lib/nrf24/nrf24.h index a05ddbebc..605513d4b 100644 --- a/applications/external/nrf24batch/lib/nrf24/nrf24.h +++ b/applications/external/nrf24batch/lib/nrf24/nrf24.h @@ -2,6 +2,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -47,14 +48,16 @@ extern "C" { #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 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 +#define nrf24_HANDLE \ + (XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault ? &furi_hal_spi_bus_handle_external : \ + &furi_hal_spi_bus_handle_external_extra) /* Low level API */ @@ -278,8 +281,11 @@ uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t siz * * @return device status */ -uint8_t - nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size_flag); +uint8_t nrf24_rxpacket( + FuriHalSpiBusHandle* handle, + uint8_t* packet, + uint8_t* ret_packetsize, + uint8_t packet_size_flag); /** Sends TX packet * @@ -315,7 +321,7 @@ void nrf24_configure( 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); +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. @@ -383,4 +389,4 @@ uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/external/nrf24channelscanner/application.fam b/applications/external/nrf24channelscanner/application.fam new file mode 100644 index 000000000..0e15185cd --- /dev/null +++ b/applications/external/nrf24channelscanner/application.fam @@ -0,0 +1,23 @@ +App( + appid="nrf24channelscanner", + name="[NRF24] Channel Scanner", + apptype=FlipperAppType.EXTERNAL, + entry_point="nrf24channelscanner_main", + fap_author="HTotoo", + fap_weburl="https://github.com/htotoo/NRF24ChannelScanner", + stack_size=2 * 1024, + requires=["gui"], + fap_category="GPIO", + fap_version=(1, 3), + fap_icon_assets="images", + fap_icon="fapicon.png", + fap_description="Scans 2.4Ghz frequency for usage data.", + fap_private_libs=[ + Lib( + name="nrf24", + sources=[ + "nrf24.c", + ], + ), + ], +) diff --git a/applications/external/nrf24channelscanner/fapicon.png b/applications/external/nrf24channelscanner/fapicon.png new file mode 100644 index 000000000..a77435427 Binary files /dev/null and b/applications/external/nrf24channelscanner/fapicon.png differ diff --git a/applications/external/nrf24channelscanner/lib/nrf24/nrf24.c b/applications/external/nrf24channelscanner/lib/nrf24/nrf24.c new file mode 100644 index 000000000..2307d6daf --- /dev/null +++ b/applications/external/nrf24channelscanner/lib/nrf24/nrf24.c @@ -0,0 +1,117 @@ +#include "nrf24.h" +#include +#include +#include +#include +#include + +void nrf24_init() { + // this is needed if multiple SPI devices are connected to the same bus but with different CS pins + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pc3, true); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pa4, true); + } + + 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); + + // resetting the CS pins to floating + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog); + } +} + +void nrf24_spi_trx( + FuriHalSpiBusHandle* handle, + uint8_t* tx, + uint8_t* rx, + uint8_t size, + uint32_t timeout) { + UNUSED(timeout); + 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 tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data}; + uint8_t rx[2] = {0}; + nrf24_spi_trx(handle, tx, rx, 2, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { + uint8_t tx[size + 1]; + uint8_t rx[size + 1]; + memset(rx, 0, size + 1); + tx[0] = R_REGISTER | (REGISTER_MASK & reg); + memset(&tx[1], 0, size); + nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT); + memcpy(data, &rx[1], size); + return rx[0]; +} + +uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) { + uint8_t tx[] = {FLUSH_RX}; + uint8_t rx[] = {0}; + nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT); + return rx[0]; +} + +uint8_t nrf24_get_rdp(FuriHalSpiBusHandle* handle) { + uint8_t rdp; + nrf24_read_reg(handle, REG_RDP, &rdp, 1); + return rdp; +} + +uint8_t nrf24_status(FuriHalSpiBusHandle* handle) { + uint8_t status; + uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)}; + nrf24_spi_trx(handle, tx, &status, 1, nrf24_TIMEOUT); + 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, bool nodelay) { + uint8_t status = 0; + uint8_t cfg = 0; + nrf24_read_reg(handle, REG_CONFIG, &cfg, 1); + cfg |= 0x03; // PWR_UP, and PRIM_RX + status = nrf24_write_reg(handle, REG_CONFIG, cfg); + furi_hal_gpio_write(nrf24_CE_PIN, true); + if(!nodelay) furi_delay_ms(2000); + return status; +} + +bool nrf24_check_connected(FuriHalSpiBusHandle* handle) { + uint8_t status = nrf24_status(handle); + + if(status != 0x00) { + return true; + } else { + return false; + } +} diff --git a/applications/external/nrf24channelscanner/lib/nrf24/nrf24.h b/applications/external/nrf24channelscanner/lib/nrf24/nrf24.h new file mode 100644 index 000000000..fdbdb0e3f --- /dev/null +++ b/applications/external/nrf24channelscanner/lib/nrf24/nrf24.h @@ -0,0 +1,129 @@ +#pragma once +#include +#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_RDP 0x09 +#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_RF_CH 0x05 +#define REG_TX_ADDR 0x10 + +#define RX_PW_P0 0x11 +#define TX_DS 0x20 +#define MAX_RT 0x10 + +#define nrf24_TIMEOUT 500 +#define nrf24_CE_PIN &gpio_ext_pb2 +#define nrf24_HANDLE \ + (XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault ? &furi_hal_spi_bus_handle_external : \ + &furi_hal_spi_bus_handle_external_extra) + +/* 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); + +/** 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); + +/** 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, bool nodelay); + +/*=============================================================================================================*/ + +/* 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); + +/** Gets RDP from register 0x09 + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return RDP from register 0x09 + */ +uint8_t nrf24_get_rdp(FuriHalSpiBusHandle* handle); + +/** Gets the current status flags from the STATUS register + * + * @param handle - pointer to FuriHalSpiHandle + * + * @return status flags + */ +uint8_t nrf24_status(FuriHalSpiBusHandle* handle); + +bool nrf24_check_connected(FuriHalSpiBusHandle* handle); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/nrf24channelscanner/nrf24channelscanner.c b/applications/external/nrf24channelscanner/nrf24channelscanner.c new file mode 100644 index 000000000..d7363a024 --- /dev/null +++ b/applications/external/nrf24channelscanner/nrf24channelscanner.c @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "nrf24channelscanner_icons.h" +#include + +const uint8_t num_channels = 128; +static uint8_t nrf24values[128] = {0}; //to store channel data + +bool ifNotFoundNrf = false; //to show error message +bool szuz = true; //to show welcome screen +static bool isScanning = false; //to track the progress +static bool stopNrfScan = false; //to exit thread + +static bool isInfiniteScan = false; //to prevent stop scan when OK long pressed + +static bool threadStoppedsoFree = false; //indicate if I can free the thread from ram. +static uint8_t currCh = 0; //for the progress bar or the channel selector + +static int delayPerChan = 150; //can set via up / down. + +bool showFreq = true; + +FuriThread* thread; + +typedef enum { + EventTypeKey, + EventTypeTick, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} Event; + +static void draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + + canvas_clear(canvas); + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 100, 0, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 112, 8, "Exit"); + canvas_draw_icon(canvas, 1, 0, &I_Ok_btn_9x9); + canvas_set_font(canvas, FontSecondary); + if(isScanning) { + canvas_draw_str(canvas, 12, 8, "Stop"); + } else { + canvas_draw_str(canvas, 12, 8, "Scan"); + } + canvas_draw_line(canvas, 0, 11, 127, 11); + + if(ifNotFoundNrf) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 23, 35, "NRF24 not found!"); + return; + } + + canvas_draw_line(canvas, currCh, 12, currCh, 13); //draw the current channel + + //draw hello mesage + if(szuz) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 1, 22, "OK: scan / stop. Long: infinite."); + canvas_draw_str(canvas, 1, 33, "Up / Down to change channel time."); + canvas_draw_str(canvas, 1, 44, "Left / Right to select channel"); + canvas_draw_str(canvas, 1, 56, " to get it's frequency"); + } + + //draw freq ir the progress + canvas_set_font(canvas, FontSecondary); + if(isScanning) { + if(isInfiniteScan) + canvas_draw_str(canvas, 37, 8, "scanning..."); + else + canvas_draw_str(canvas, 37, 8, "scanning"); + + } else { + if(showFreq) { + int freq = 2400 + currCh; + char strfreq[10] = {0}; + snprintf(strfreq, sizeof(strfreq), "%d MHZ", freq); + canvas_draw_str(canvas, 40, 8, strfreq); + } else { + //show delay + int dly = delayPerChan; + char strdel[10] = {0}; + snprintf(strdel, sizeof(strdel), "%d us", dly); + canvas_draw_str(canvas, 40, 8, strdel); + } + } + + //draw the chart + for(int i = 0; i < num_channels; ++i) { + int h = 64 - nrf24values[i]; + if(h < 11) h = 12; + canvas_draw_line(canvas, i, h, i, 64); + } +} + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + Event event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static int32_t scanner(void* context) { + UNUSED(context); + isScanning = true; + stopNrfScan = false; + threadStoppedsoFree = false; + uint8_t tmp = 0; + currCh = 0; + nrf24_set_rx_mode(nrf24_HANDLE, false); + nrf24_write_reg(nrf24_HANDLE, REG_EN_AA, 0x0); + nrf24_write_reg(nrf24_HANDLE, REG_RF_SETUP, 0x0f); + while(true) { //scan until stopped somehow + if(stopNrfScan) break; + for(uint8_t i = 0; i < num_channels; i++) { + if(stopNrfScan) break; + currCh = i; + nrf24_write_reg(nrf24_HANDLE, REG_RF_CH, i); + nrf24_set_rx_mode(nrf24_HANDLE, true); + for(uint8_t ii = 0; ii < 3; ++ii) { + nrf24_flush_rx(nrf24_HANDLE); + furi_delay_us(delayPerChan); + tmp = nrf24_get_rdp(nrf24_HANDLE); + if(tmp > 0 && nrf24values[i] < 65) { + nrf24values[i]++; //don't overrun it + } + if(nrf24values[i] > 50 && !isInfiniteScan) { + stopNrfScan = true; //stop, bc maxed, but only when not infinite + } + } + } + furi_delay_ms(1); + //for screen refresh. + } + //cleanup + nrf24_set_idle(nrf24_HANDLE); + isScanning = false; + threadStoppedsoFree = true; + currCh = 0; + return 0; +} + +void ChangeFreq(int delta) { + currCh += delta; + if(currCh > num_channels) currCh = 0; + showFreq = true; +} + +void ChangeDelay(int delta) { + delayPerChan += delta; + if(delayPerChan > 4000) delayPerChan = 4000; + if(delayPerChan < 120) delayPerChan = 120; + if(delayPerChan == 170) delayPerChan = 150; //rounding for the next + showFreq = false; +} + +// Main entry of the application +int32_t nrf24channelscanner_main(void* p) { + UNUSED(p); + + Event event; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Event)); + + //turn on 5v for some modules + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + + nrf24_init(); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, NULL); + 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); + + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + + while(true) { + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + + if(event.type == EventTypeKey) { + szuz = false; //hit any button, so hide welcome screen + if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { + if(isScanning) { + stopNrfScan = true; //if running, stop it. + notification_message(notification, &sequence_blink_yellow_100); + furi_thread_join(thread); + furi_thread_free(thread); + } + break; + } + //isInfiniteScan + if((event.input.type == InputTypeShort || event.input.type == InputTypeLong) && + event.input.key == InputKeyOk) { + if(isScanning) { + notification_message(notification, &sequence_blink_yellow_100); + stopNrfScan = true; + furi_thread_join(thread); + furi_thread_free(thread); + threadStoppedsoFree = false; //to prevent double free + continue; + } + memset(nrf24values, 0, sizeof(nrf24values)); + if(nrf24_check_connected(nrf24_HANDLE)) { + threadStoppedsoFree = false; + ifNotFoundNrf = false; + notification_message(notification, &sequence_blink_green_100); + isInfiniteScan = (event.input.type == InputTypeLong); + thread = furi_thread_alloc(); + furi_thread_set_name(thread, "nrfscannerth"); + furi_thread_set_stack_size(thread, 1024); + furi_thread_set_callback(thread, scanner); + furi_thread_start(thread); + } else { + ifNotFoundNrf = true; + notification_message(notification, &sequence_error); + } + } + //change the delay + if(event.input.type == InputTypeShort && event.input.key == InputKeyUp) { + ChangeDelay(50); + } + if(event.input.type == InputTypeShort && event.input.key == InputKeyDown) { + ChangeDelay(-50); + } + + if(!isScanning) { + if(event.input.type == InputTypeLong && event.input.key == InputKeyLeft) + ChangeFreq(-10); + if(event.input.type == InputTypeShort && event.input.key == InputKeyLeft) + ChangeFreq(-1); + if(event.input.type == InputTypeLong && event.input.key == InputKeyRight) + ChangeFreq(10); + if(event.input.type == InputTypeShort && event.input.key == InputKeyRight) + ChangeFreq(1); + } + } + if(threadStoppedsoFree) { + threadStoppedsoFree = false; + furi_thread_join(thread); + furi_thread_free(thread); + } + } + nrf24_deinit(); + furi_message_queue_free(event_queue); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_record_close(RECORD_GUI); + //turn off 5v + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + return 0; +} \ No newline at end of file diff --git a/applications/external/nrf24mousejacker/application.fam b/applications/external/nrf24mousejacker/application.fam index f5a9b9dbc..30de60b5d 100644 --- a/applications/external/nrf24mousejacker/application.fam +++ b/applications/external/nrf24mousejacker/application.fam @@ -8,12 +8,12 @@ App( "dialogs", ], stack_size=2 * 1024, - 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( name="nrf24", diff --git a/applications/external/nrf24mousejacker/lib/nrf24/nrf24.c b/applications/external/nrf24mousejacker/lib/nrf24/nrf24.c index 67c5dde1c..d85196e86 100644 --- a/applications/external/nrf24mousejacker/lib/nrf24/nrf24.c +++ b/applications/external/nrf24mousejacker/lib/nrf24/nrf24.c @@ -6,6 +6,15 @@ #include void nrf24_init() { + // this is needed if multiple SPI devices are connected to the same bus but with different CS pins + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pc3, true); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pa4, true); + } + furi_hal_spi_bus_handle_init(nrf24_HANDLE); furi_hal_spi_acquire(nrf24_HANDLE); furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); @@ -17,6 +26,13 @@ void nrf24_deinit() { 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); + + // resetting the CS pins to floating + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog); + } } void nrf24_spi_trx( diff --git a/applications/external/nrf24mousejacker/lib/nrf24/nrf24.h b/applications/external/nrf24mousejacker/lib/nrf24/nrf24.h index 046784bc7..58dbad0c8 100644 --- a/applications/external/nrf24mousejacker/lib/nrf24/nrf24.h +++ b/applications/external/nrf24mousejacker/lib/nrf24/nrf24.h @@ -2,6 +2,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -40,7 +41,9 @@ extern "C" { #define nrf24_TIMEOUT 500 #define nrf24_CE_PIN &gpio_ext_pb2 -#define nrf24_HANDLE &furi_hal_spi_bus_handle_external +#define nrf24_HANDLE \ + (XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault ? &furi_hal_spi_bus_handle_external : \ + &furi_hal_spi_bus_handle_external_extra) /* Low level API */ @@ -370,4 +373,4 @@ bool nrf24_check_connected(FuriHalSpiBusHandle* handle); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/external/nrf24mousejacker/mousejacker.c b/applications/external/nrf24mousejacker/mousejacker.c index 6b9396a63..02d91f788 100644 --- a/applications/external/nrf24mousejacker/mousejacker.c +++ b/applications/external/nrf24mousejacker/mousejacker.c @@ -11,8 +11,8 @@ #include #include #include "mousejacker_ducky.h" +#include "nrf24mousejacker_icons.h" #include -#include #define TAG "mousejacker" #define LOGITECH_MAX_CHANNEL 85 @@ -279,7 +279,6 @@ static int32_t mj_worker_thread(void* ctx) { int32_t mousejacker_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - dolphin_deed(DolphinDeedPluginStart); PluginState* plugin_state = malloc(sizeof(PluginState)); mousejacker_state_init(plugin_state); @@ -291,6 +290,12 @@ int32_t mousejacker_app(void* p) { return 255; } + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); // Set system callbacks @@ -394,5 +399,9 @@ int32_t mousejacker_app(void* p) { furi_mutex_free(plugin_state->mutex); free(plugin_state); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + return 0; } diff --git a/applications/external/nrf24scan/application.fam b/applications/external/nrf24scan/application.fam index 1be252f7c..5ad617ad4 100644 --- a/applications/external/nrf24scan/application.fam +++ b/applications/external/nrf24scan/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_NRF24SCAN"], requires=["gui"], stack_size=2 * 1024, - order=60, fap_icon="nrf24scan_10px.png", fap_category="GPIO", fap_private_libs=[ diff --git a/applications/external/nrf24scan/lib/nrf24/nrf24.c b/applications/external/nrf24scan/lib/nrf24/nrf24.c index 83f0613a1..acd0130c7 100644 --- a/applications/external/nrf24scan/lib/nrf24/nrf24.c +++ b/applications/external/nrf24scan/lib/nrf24/nrf24.c @@ -8,6 +8,15 @@ #include void nrf24_init() { + // this is needed if multiple SPI devices are connected to the same bus but with different CS pins + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pc3, true); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pa4, true); + } + furi_hal_spi_bus_handle_init(nrf24_HANDLE); furi_hal_spi_acquire(nrf24_HANDLE); furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); @@ -19,6 +28,13 @@ void nrf24_deinit() { 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); + + // resetting the CS pins to floating + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog); + } } void nrf24_spi_trx( @@ -41,7 +57,8 @@ uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) return rx[0]; } -uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { +uint8_t + nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) { uint8_t tx[size + 1]; uint8_t rx[size + 1]; memset(rx, 0, size + 1); @@ -190,7 +207,11 @@ uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) { return status; } -uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size) { +uint8_t nrf24_rxpacket( + FuriHalSpiBusHandle* handle, + uint8_t* packet, + uint8_t* ret_packetsize, + uint8_t packet_size) { uint8_t status = 0; uint8_t tx_cmd[33] = {0}; // 32 max payload size + 1 for command uint8_t tmp_packet[33] = {0}; @@ -204,13 +225,15 @@ uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* re if(status & RX_DR) { if(packet_size == 1) packet_size = nrf24_get_packetlen(handle, (status >> 1) & 7); - else if(packet_size == 0){ - tx_cmd[0] = R_RX_PL_WID; tx_cmd[1] = 0; + else if(packet_size == 0) { + tx_cmd[0] = R_RX_PL_WID; + tx_cmd[1] = 0; nrf24_spi_trx(handle, tx_cmd, tmp_packet, 2, nrf24_TIMEOUT); packet_size = tmp_packet[1]; } if(packet_size > 32 || packet_size == 0) packet_size = 32; - tx_cmd[0] = R_RX_PAYLOAD; tx_cmd[1] = 0; + tx_cmd[0] = R_RX_PAYLOAD; + tx_cmd[1] = 0; nrf24_spi_trx(handle, tx_cmd, tmp_packet, packet_size + 1, nrf24_TIMEOUT); memcpy(packet, &tmp_packet[1], packet_size); nrf24_write_reg(handle, REG_STATUS, RX_DR); // clear RX_DR @@ -240,7 +263,8 @@ uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t si nrf24_set_tx_mode(handle); uint32_t start_time = furi_get_tick(); - while(!(status & (TX_DS | MAX_RT)) && furi_get_tick() - start_time < 2000UL) status = nrf24_status(handle); + while(!(status & (TX_DS | MAX_RT)) && furi_get_tick() - start_time < 2000UL) + status = nrf24_status(handle); if(status & MAX_RT) nrf24_flush_tx(handle); @@ -525,9 +549,8 @@ uint8_t nrf24_find_channel( return ch; } -uint8_t nrf24_set_mac(uint8_t mac_addr, uint8_t *mac, uint8_t mlen) -{ +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 + for(int i = 0; i < mlen; i++) addr[i] = mac[mlen - i - 1]; + return nrf24_write_buf_reg(nrf24_HANDLE, mac_addr, addr, mlen); +} diff --git a/applications/external/nrf24scan/lib/nrf24/nrf24.h b/applications/external/nrf24scan/lib/nrf24/nrf24.h index cd994dc40..d5fdced3b 100644 --- a/applications/external/nrf24scan/lib/nrf24/nrf24.h +++ b/applications/external/nrf24scan/lib/nrf24/nrf24.h @@ -2,6 +2,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -46,13 +47,15 @@ extern "C" { #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 RX_DR 0x40 +#define TX_DS 0x20 +#define MAX_RT 0x10 #define nrf24_TIMEOUT 500 #define nrf24_CE_PIN &gpio_ext_pb2 -#define nrf24_HANDLE &furi_hal_spi_bus_handle_external +#define nrf24_HANDLE \ + (XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault ? &furi_hal_spi_bus_handle_external : \ + &furi_hal_spi_bus_handle_external_extra) /* Low level API */ @@ -273,8 +276,11 @@ uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t siz * * @return device status */ -uint8_t - nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* ret_packetsize, uint8_t packet_size_flag); +uint8_t nrf24_rxpacket( + FuriHalSpiBusHandle* handle, + uint8_t* packet, + uint8_t* ret_packetsize, + uint8_t packet_size_flag); /** Sends TX packet * @@ -310,7 +316,7 @@ void nrf24_configure( 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); +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. @@ -378,4 +384,4 @@ uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/external/nrf24scan/nrf24scan.c b/applications/external/nrf24scan/nrf24scan.c index 0bd4b6b3b..1a60ce36d 100644 --- a/applications/external/nrf24scan/nrf24scan.c +++ b/applications/external/nrf24scan/nrf24scan.c @@ -12,7 +12,6 @@ #include #include #include -#include #define TAG "nrf24scan" #define VERSION "2.2" @@ -1332,7 +1331,6 @@ static void render_callback(Canvas* const canvas, void* ctx) { int32_t nrf24scan_app(void* p) { UNUSED(p); APP = malloc(sizeof(Nrf24Scan)); - 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/nrf24sniff/application.fam b/applications/external/nrf24sniff/application.fam index 244677e38..98be7d06e 100644 --- a/applications/external/nrf24sniff/application.fam +++ b/applications/external/nrf24sniff/application.fam @@ -5,7 +5,6 @@ App( entry_point="nrfsniff_app", requires=["gui"], stack_size=2 * 1024, - order=60, fap_icon="nrfsniff_10px.png", fap_category="GPIO", fap_author="@mothball187 & @xMasterX", diff --git a/applications/external/nrf24sniff/lib/nrf24/nrf24.c b/applications/external/nrf24sniff/lib/nrf24/nrf24.c index 67c5dde1c..8b39cf5eb 100644 --- a/applications/external/nrf24sniff/lib/nrf24/nrf24.c +++ b/applications/external/nrf24sniff/lib/nrf24/nrf24.c @@ -6,6 +6,15 @@ #include void nrf24_init() { + // this is needed if multiple SPI devices are connected to the same bus but with different CS pins + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pc3, true); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull); + furi_hal_gpio_write(&gpio_ext_pa4, true); + } + furi_hal_spi_bus_handle_init(nrf24_HANDLE); furi_hal_spi_acquire(nrf24_HANDLE); furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); @@ -17,6 +26,13 @@ void nrf24_deinit() { 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); + + // resetting the CS pins to floating + if(XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault) { + furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog); + } else if(XTREME_SETTINGS()->spi_nrf24_handle == SpiExtra) { + furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeAnalog); + } } void nrf24_spi_trx( @@ -527,4 +543,4 @@ bool nrf24_check_connected(FuriHalSpiBusHandle* handle) { } else { return false; } -} \ No newline at end of file +} diff --git a/applications/external/nrf24sniff/lib/nrf24/nrf24.h b/applications/external/nrf24sniff/lib/nrf24/nrf24.h index 046784bc7..58dbad0c8 100644 --- a/applications/external/nrf24sniff/lib/nrf24/nrf24.h +++ b/applications/external/nrf24sniff/lib/nrf24/nrf24.h @@ -2,6 +2,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -40,7 +41,9 @@ extern "C" { #define nrf24_TIMEOUT 500 #define nrf24_CE_PIN &gpio_ext_pb2 -#define nrf24_HANDLE &furi_hal_spi_bus_handle_external +#define nrf24_HANDLE \ + (XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault ? &furi_hal_spi_bus_handle_external : \ + &furi_hal_spi_bus_handle_external_extra) /* Low level API */ @@ -370,4 +373,4 @@ bool nrf24_check_connected(FuriHalSpiBusHandle* handle); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/applications/external/nrf24sniff/nrfsniff.c b/applications/external/nrf24sniff/nrfsniff.c index cad16f349..bc58602ff 100644 --- a/applications/external/nrf24sniff/nrfsniff.c +++ b/applications/external/nrf24sniff/nrfsniff.c @@ -4,14 +4,13 @@ #include #include #include -#include #include #include #define LOGITECH_MAX_CHANNEL 85 #define COUNT_THRESHOLD 2 -#define DEFAULT_SAMPLE_TIME 4000 +#define DEFAULT_SAMPLE_TIME 8000 #define MAX_ADDRS 100 #define MAX_CONFIRMED 32 @@ -315,7 +314,6 @@ static void start_sniffing() { int32_t nrfsniff_app(void* p) { UNUSED(p); - dolphin_deed(DolphinDeedPluginStart); uint8_t address[5] = {0}; uint32_t start = 0; hexlify(address, 5, top_address); @@ -329,6 +327,12 @@ int32_t nrfsniff_app(void* p) { return 255; } + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + nrf24_init(); // Set system callbacks @@ -346,10 +350,6 @@ int32_t nrfsniff_app(void* p) { storage_common_migrate(storage, EXT_PATH("nrfsniff"), NRFSNIFF_APP_PATH_FOLDER); storage_common_mkdir(storage, NRFSNIFF_APP_PATH_FOLDER); - while(!furi_hal_speaker_acquire(100)) { - furi_delay_ms(100); - } - PluginEvent event; for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); @@ -455,7 +455,6 @@ int32_t nrfsniff_app(void* p) { target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps) sniffing_state = false; nrf24_deinit(); - furi_hal_speaker_release(); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close(RECORD_GUI); @@ -466,5 +465,9 @@ int32_t nrfsniff_app(void* p) { furi_mutex_free(plugin_state->mutex); free(plugin_state); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } + return 0; } diff --git a/applications/external/ocarina/application.fam b/applications/external/ocarina/application.fam index 552adfdeb..572aa1679 100644 --- a/applications/external/ocarina/application.fam +++ b/applications/external/ocarina/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_OCARINA"], requires=["gui"], stack_size=1 * 1024, - order=30, fap_icon="music_10px.png", fap_category="Media", fap_icon_assets="icons", diff --git a/applications/external/orgasmotron/application.fam b/applications/external/orgasmotron/application.fam index b4c191a8a..f58a4a734 100644 --- a/applications/external/orgasmotron/application.fam +++ b/applications/external/orgasmotron/application.fam @@ -6,7 +6,6 @@ App( cdefines=["ORGASMOTRON"], requires=["gui"], stack_size=1 * 1024, - order=20, fap_icon="orgasmotron_10px.png", fap_category="Tools", ) diff --git a/applications/external/paint/application.fam b/applications/external/paint/application.fam index a6f154bac..e5dec6040 100644 --- a/applications/external/paint/application.fam +++ b/applications/external/paint/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_PAINT"], requires=["gui"], stack_size=2 * 1024, - order=175, fap_icon="paintIcon.png", fap_category="Media", fap_author="@n-o-T-I-n-s-a-n-e", diff --git a/applications/external/picopass/application.fam b/applications/external/picopass/application.fam index b14427f2e..896d3cf74 100644 --- a/applications/external/picopass/application.fam +++ b/applications/external/picopass/application.fam @@ -1,6 +1,6 @@ App( appid="picopass", - name="PicoPass", + name="[iClass] PicoPass", apptype=FlipperAppType.EXTERNAL, targets=["f7"], entry_point="picopass_app", @@ -9,7 +9,8 @@ App( "gui", ], stack_size=4 * 1024, - order=30, + fap_description="App to communicate with NFC tags using the PicoPass(iClass) format", + fap_version="1.2", fap_icon="125_10px.png", fap_category="NFC", fap_libs=["mbedtls"], @@ -18,4 +19,6 @@ App( name="loclass", ), ], + fap_icon_assets="icons", + fap_file_assets="files", ) diff --git a/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt b/applications/external/picopass/files/iclass_elite_dict.txt similarity index 100% rename from assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt rename to applications/external/picopass/files/iclass_elite_dict.txt diff --git a/assets/resources/apps_data/picopass/assets/iclass_standard_dict.txt b/applications/external/picopass/files/iclass_standard_dict.txt similarity index 100% rename from assets/resources/apps_data/picopass/assets/iclass_standard_dict.txt rename to applications/external/picopass/files/iclass_standard_dict.txt diff --git a/applications/external/picopass/helpers/iclass_elite_dict.c b/applications/external/picopass/helpers/iclass_elite_dict.c index f92dce0aa..5f0f41f85 100644 --- a/applications/external/picopass/helpers/iclass_elite_dict.c +++ b/applications/external/picopass/helpers/iclass_elite_dict.c @@ -3,9 +3,9 @@ #include #include -#define ICLASS_ELITE_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_elite_dict.txt") +#define ICLASS_ELITE_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_elite_dict.txt") +#define ICLASS_STANDARD_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_standard_dict.txt") #define ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt") -#define ICLASS_STANDARD_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_standard_dict.txt") #define TAG "IclassEliteDict" diff --git a/applications/external/picopass/picopass.c b/applications/external/picopass/picopass.c index 52ceab08a..13f6ae5fb 100644 --- a/applications/external/picopass/picopass.c +++ b/applications/external/picopass/picopass.c @@ -1,5 +1,4 @@ #include "picopass_i.h" -#include #define TAG "PicoPass" @@ -69,6 +68,13 @@ Picopass* picopass_alloc() { PicopassViewTextInput, text_input_get_view(picopass->text_input)); + // Byte Input + picopass->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + picopass->view_dispatcher, + PicopassViewByteInput, + byte_input_get_view(picopass->byte_input)); + // Custom Widget picopass->widget = widget_alloc(); view_dispatcher_add_view( @@ -110,6 +116,10 @@ void picopass_free(Picopass* picopass) { view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewTextInput); text_input_free(picopass->text_input); + // ByteInput + view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewByteInput); + byte_input_free(picopass->byte_input); + // Custom Widget view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget); widget_free(picopass->widget); @@ -219,7 +229,6 @@ int32_t picopass_app(void* p) { UNUSED(p); picopass_migrate_from_old_folder(); - dolphin_deed(DolphinDeedPluginStart); Picopass* picopass = picopass_alloc(); scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart); diff --git a/applications/external/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c index 3502f253e..dbb9b9e1b 100644 --- a/applications/external/picopass/picopass_device.c +++ b/applications/external/picopass/picopass_device.c @@ -2,6 +2,7 @@ #include #include +#include "picopass_icons.h" #include #define TAG "PicopassDevice" @@ -356,8 +357,8 @@ ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pa return ERR_NONE; } -ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) { - uint32_t* halves = (uint32_t*)data; +ReturnCode picopass_device_parse_wiegand(uint8_t* credential, PicopassWiegandRecord* record) { + uint32_t* halves = (uint32_t*)credential; if(halves[0] == 0) { uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); record->bitLength = 31 - leading0s; @@ -367,8 +368,16 @@ ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* r } FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); + // Remove sentinel bit from credential. Byteswapping to handle array of bytes vs 64bit value + uint64_t sentinel = __builtin_bswap64(1ULL << record->bitLength); + uint64_t swapped = 0; + memcpy(&swapped, credential, sizeof(uint64_t)); + swapped = swapped ^ sentinel; + memcpy(credential, &swapped, sizeof(uint64_t)); + FURI_LOG_D(TAG, "PACS: (%d) %016llx", record->bitLength, swapped); + if(record->bitLength == 26) { - uint8_t* v4 = data + 4; + uint8_t* v4 = credential + 4; uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); record->CardNumber = (bot >> 1) & 0xFFFF; diff --git a/applications/external/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h index 88c2140ee..0fc928610 100644 --- a/applications/external/picopass/picopass_i.h +++ b/applications/external/picopass/picopass_i.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ #include #include +#include "picopass_icons.h" #include #define PICOPASS_TEXT_STORE_SIZE 128 @@ -60,12 +62,14 @@ struct Picopass { char text_store[PICOPASS_TEXT_STORE_SIZE + 1]; FuriString* text_box_store; + uint8_t byte_input_store[RFAL_PICOPASS_BLOCK_LEN]; // Common Views Submenu* submenu; Popup* popup; Loading* loading; TextInput* text_input; + ByteInput* byte_input; Widget* widget; DictAttack* dict_attack; Loclass* loclass; @@ -76,6 +80,7 @@ typedef enum { PicopassViewPopup, PicopassViewLoading, PicopassViewTextInput, + PicopassViewByteInput, PicopassViewWidget, PicopassViewDictAttack, PicopassViewLoclass, diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index 25ba7ebba..a0aac6231 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -21,8 +21,8 @@ static const uint8_t loclass_csns[LOCLASS_NUM_CSNS][RFAL_PICOPASS_BLOCK_LEN] = { }; static void picopass_worker_enable_field() { - furi_hal_nfc_ll_txrx_on(); furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); furi_hal_nfc_ll_poll(); } @@ -130,7 +130,7 @@ ReturnCode picopass_detect_card(int timeout) { err = rfalPicoPassPollerCheckPresence(); if(err != ERR_RF_COLLISION) { - //FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err); + FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err); return err; } @@ -1057,7 +1057,9 @@ static void picopass_emu_handle_packet( 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"); +#ifndef PICOPASS_DEBUG_IGNORE_BAD_RMAC return; +#endif } // CHIPRESPONSE(4) @@ -1196,6 +1198,8 @@ static void picopass_emu_handle_packet( } void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode) { + furi_hal_nfc_exit_sleep(); + FuriHalNfcTxRxContext tx_rx = {}; PicopassEmulatorCtx emu_ctx = { .state = PicopassEmulatorStateIdle, diff --git a/applications/external/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c index 983a11eab..1d45a48dc 100644 --- a/applications/external/picopass/rfal_picopass.c +++ b/applications/external/picopass/rfal_picopass.c @@ -47,7 +47,7 @@ FuriHalNfcReturn rfalPicoPassPollerInitialize(void) { FuriHalNfcModePollPicopass, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48); if(ret != FuriHalNfcReturnOk) { return ret; - } + }; furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_PICOPASS); diff --git a/applications/external/picopass/scenes/picopass_scene_card_menu.c b/applications/external/picopass/scenes/picopass_scene_card_menu.c index fe63f7c86..fa4515db3 100644 --- a/applications/external/picopass/scenes/picopass_scene_card_menu.c +++ b/applications/external/picopass/scenes/picopass_scene_card_menu.c @@ -4,6 +4,8 @@ enum SubmenuIndex { SubmenuIndexSave, SubmenuIndexSaveAsLF, SubmenuIndexChangeKey, + SubmenuIndexWrite, + SubmenuIndexEmulate, }; void picopass_scene_card_menu_submenu_callback(void* context, uint32_t index) { @@ -26,6 +28,14 @@ void picopass_scene_card_menu_on_enter(void* context) { picopass_scene_card_menu_submenu_callback, picopass); } + submenu_add_item( + submenu, "Write", SubmenuIndexWrite, picopass_scene_card_menu_submenu_callback, picopass); + submenu_add_item( + submenu, + "Emulate", + SubmenuIndexEmulate, + picopass_scene_card_menu_submenu_callback, + picopass); submenu_add_item( submenu, "Change Key", @@ -57,6 +67,12 @@ bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) { picopass->dev->format = PicopassDeviceSaveFormatLF; scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); consumed = true; + } 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; } else if(event.event == SubmenuIndexChangeKey) { scene_manager_set_scene_state( picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexChangeKey); diff --git a/applications/external/picopass/scenes/picopass_scene_config.h b/applications/external/picopass/scenes/picopass_scene_config.h index 6156ed689..3241c2344 100644 --- a/applications/external/picopass/scenes/picopass_scene_config.h +++ b/applications/external/picopass/scenes/picopass_scene_config.h @@ -11,9 +11,11 @@ ADD_SCENE(picopass, delete, Delete) ADD_SCENE(picopass, delete_success, DeleteSuccess) ADD_SCENE(picopass, write_card, WriteCard) ADD_SCENE(picopass, write_card_success, WriteCardSuccess) +ADD_SCENE(picopass, write_card_failure, WriteCardFailure) 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) +ADD_SCENE(picopass, key_input, KeyInput) diff --git a/applications/external/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c index 41d0bad81..60ed6ed94 100644 --- a/applications/external/picopass/scenes/picopass_scene_device_info.c +++ b/applications/external/picopass/scenes/picopass_scene_device_info.c @@ -17,7 +17,6 @@ void picopass_scene_device_info_on_enter(void* context) { FuriString* csn_str = furi_string_alloc_set("CSN:"); FuriString* credential_str = furi_string_alloc(); FuriString* wiegand_str = furi_string_alloc(); - FuriString* sio_str = furi_string_alloc(); dolphin_deed(DolphinDeedNfcReadSuccess); @@ -43,7 +42,7 @@ void picopass_scene_device_info_on_enter(void* context) { } furi_string_set(credential_str, ""); 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]); + furi_string_cat_printf(credential_str, "%02X", pacs->credential[i]); } if(pacs->record.valid) { @@ -54,7 +53,7 @@ void picopass_scene_device_info_on_enter(void* context) { } if(pacs->sio) { - furi_string_cat_printf(sio_str, "+SIO"); + furi_string_cat_printf(credential_str, " +SIO"); } } @@ -70,13 +69,10 @@ void picopass_scene_device_info_on_enter(void* context) { AlignCenter, FontSecondary, furi_string_get_cstr(credential_str)); - widget_add_string_element( - widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); furi_string_free(csn_str); furi_string_free(credential_str); furi_string_free(wiegand_str); - furi_string_free(sio_str); widget_add_button_element( picopass->widget, diff --git a/applications/external/picopass/scenes/picopass_scene_key_input.c b/applications/external/picopass/scenes/picopass_scene_key_input.c new file mode 100644 index 000000000..97db46653 --- /dev/null +++ b/applications/external/picopass/scenes/picopass_scene_key_input.c @@ -0,0 +1,48 @@ +#include "../picopass_i.h" +#include +#include +#include + +void picopass_scene_key_input_text_input_callback(void* context) { + Picopass* picopass = context; + + picopass->dev->dev_data.pacs.elite_kdf = true; + memcpy(picopass->dev->dev_data.pacs.key, picopass->byte_input_store, RFAL_PICOPASS_BLOCK_LEN); + view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventByteInputDone); +} + +void picopass_scene_key_input_on_enter(void* context) { + Picopass* picopass = context; + + ByteInput* byte_input = picopass->byte_input; + byte_input_set_header_text(byte_input, "Enter The Key In Hex"); + byte_input_set_result_callback( + byte_input, + picopass_scene_key_input_text_input_callback, + NULL, + picopass, + picopass->byte_input_store, + RFAL_PICOPASS_BLOCK_LEN); + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewByteInput); +} + +bool picopass_scene_key_input_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == PicopassCustomEventByteInputDone) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); + consumed = true; + } + } + return consumed; +} + +void picopass_scene_key_input_on_exit(void* context) { + Picopass* picopass = context; + + // Clear view + byte_input_set_result_callback(picopass->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(picopass->byte_input, ""); +} diff --git a/applications/external/picopass/scenes/picopass_scene_key_menu.c b/applications/external/picopass/scenes/picopass_scene_key_menu.c index 08adffed2..21d8a526d 100644 --- a/applications/external/picopass/scenes/picopass_scene_key_menu.c +++ b/applications/external/picopass/scenes/picopass_scene_key_menu.c @@ -6,7 +6,7 @@ enum SubmenuIndex { SubmenuIndexWriteiCE, SubmenuIndexWriteiCL, SubmenuIndexWriteiCS, - SubmenuIndexWriteCustom, //TODO: user input of key + SubmenuIndexWriteCustom, }; void picopass_scene_key_menu_submenu_callback(void* context, uint32_t index) { @@ -43,6 +43,12 @@ void picopass_scene_key_menu_on_enter(void* context) { SubmenuIndexWriteiCS, picopass_scene_key_menu_submenu_callback, picopass); + submenu_add_item( + submenu, + "Write Elite", + SubmenuIndexWriteCustom, + picopass_scene_key_menu_submenu_callback, + picopass); submenu_set_selected_item( picopass->submenu, @@ -84,6 +90,12 @@ bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) { picopass->dev->dev_data.pacs.elite_kdf = false; scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); consumed = true; + } else if(event.event == SubmenuIndexWriteCustom) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteCustom); + // Key and elite_kdf = true are both set in key_input scene + scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyInput); + consumed = true; } } else if(event.type == SceneManagerEventTypeBack) { consumed = scene_manager_search_and_switch_to_previous_scene( 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 2f80cd7b9..adcdaded3 100644 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_read_card_success.c @@ -19,7 +19,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { FuriString* csn_str = furi_string_alloc_set("CSN:"); FuriString* credential_str = furi_string_alloc(); FuriString* wiegand_str = furi_string_alloc(); - FuriString* sio_str = furi_string_alloc(); + FuriString* key_str = furi_string_alloc(); dolphin_deed(DolphinDeedNfcReadSuccess); @@ -79,7 +79,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { size_t bytesLength = 1 + pacs->record.bitLength / 8; furi_string_set(credential_str, ""); 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]); + furi_string_cat_printf(credential_str, "%02X", pacs->credential[i]); } if(pacs->record.valid) { @@ -90,19 +90,16 @@ void picopass_scene_read_card_success_on_enter(void* context) { } if(pacs->sio) { - furi_string_cat_printf(sio_str, "+SIO"); + furi_string_cat_printf(credential_str, " +SIO"); } if(pacs->key) { - if(pacs->sio) { - furi_string_cat_printf(sio_str, " "); - } - furi_string_cat_printf(sio_str, "Key: "); + furi_string_cat_printf(key_str, "Key: "); 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]); + furi_string_cat_printf(key_str, "%02X", key[i]); } } @@ -134,12 +131,12 @@ void picopass_scene_read_card_success_on_enter(void* context) { FontSecondary, furi_string_get_cstr(credential_str)); widget_add_string_element( - widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); + widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(key_str)); furi_string_free(csn_str); furi_string_free(credential_str); furi_string_free(wiegand_str); - furi_string_free(sio_str); + furi_string_free(key_str); view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); } diff --git a/applications/external/picopass/scenes/picopass_scene_save_name.c b/applications/external/picopass/scenes/picopass_scene_save_name.c index baf882b80..a80932f08 100644 --- a/applications/external/picopass/scenes/picopass_scene_save_name.c +++ b/applications/external/picopass/scenes/picopass_scene_save_name.c @@ -52,8 +52,9 @@ bool picopass_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == PicopassCustomEventTextInputDone) { + // Delete old file if renaming if(strcmp(picopass->dev->dev_name, "") != 0) { - // picopass_device_delete(picopass->dev, true); + picopass_device_delete(picopass->dev, true); } strlcpy( picopass->dev->dev_name, picopass->text_store, strlen(picopass->text_store) + 1); diff --git a/applications/external/picopass/scenes/picopass_scene_saved_menu.c b/applications/external/picopass/scenes/picopass_scene_saved_menu.c index 401f43f9b..0283906f2 100644 --- a/applications/external/picopass/scenes/picopass_scene_saved_menu.c +++ b/applications/external/picopass/scenes/picopass_scene_saved_menu.c @@ -5,6 +5,7 @@ enum SubmenuIndex { SubmenuIndexInfo, SubmenuIndexWrite, SubmenuIndexEmulate, + SubmenuIndexRename, }; void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -17,12 +18,6 @@ void picopass_scene_saved_menu_on_enter(void* context) { Picopass* picopass = context; Submenu* submenu = picopass->submenu; - submenu_add_item( - submenu, - "Delete", - SubmenuIndexDelete, - picopass_scene_saved_menu_submenu_callback, - picopass); submenu_add_item( submenu, "Info", SubmenuIndexInfo, picopass_scene_saved_menu_submenu_callback, picopass); submenu_add_item( @@ -33,6 +28,18 @@ void picopass_scene_saved_menu_on_enter(void* context) { SubmenuIndexEmulate, picopass_scene_saved_menu_submenu_callback, picopass); + submenu_add_item( + submenu, + "Rename", + SubmenuIndexRename, + picopass_scene_saved_menu_submenu_callback, + picopass); + submenu_add_item( + submenu, + "Delete", + SubmenuIndexDelete, + picopass_scene_saved_menu_submenu_callback, + picopass); submenu_set_selected_item( picopass->submenu, @@ -61,6 +68,9 @@ bool picopass_scene_saved_menu_on_event(void* context, SceneManagerEvent event) } else if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneEmulate); consumed = true; + } else if(event.event == SubmenuIndexRename) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); + consumed = true; } } diff --git a/applications/external/picopass/scenes/picopass_scene_start.c b/applications/external/picopass/scenes/picopass_scene_start.c index cfd758ed5..d6b394b3f 100644 --- a/applications/external/picopass/scenes/picopass_scene_start.c +++ b/applications/external/picopass/scenes/picopass_scene_start.c @@ -58,7 +58,7 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == SubmenuIndexLoclass) { scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneLoclass, PicopassSceneLoclass); + picopass->scene_manager, PicopassSceneStart, PicopassSceneLoclass); scene_manager_next_scene(picopass->scene_manager, PicopassSceneLoclass); consumed = true; } diff --git a/applications/external/picopass/scenes/picopass_scene_write_card.c b/applications/external/picopass/scenes/picopass_scene_write_card.c index ce396fc10..3c6eae296 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_card.c +++ b/applications/external/picopass/scenes/picopass_scene_write_card.c @@ -4,7 +4,7 @@ void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* context) { UNUSED(event); Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); + view_dispatcher_send_custom_event(picopass->view_dispatcher, event); } void picopass_scene_write_card_on_enter(void* context) { @@ -33,7 +33,10 @@ bool picopass_scene_write_card_on_event(void* context, SceneManagerEvent event) bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventWorkerExit) { + if(event.event == PicopassWorkerEventFail) { + scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardFailure); + consumed = true; + } else if(event.event == PicopassWorkerEventSuccess) { scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess); consumed = true; } diff --git a/applications/external/picopass/scenes/picopass_scene_write_card_failure.c b/applications/external/picopass/scenes/picopass_scene_write_card_failure.c new file mode 100644 index 000000000..4aae21996 --- /dev/null +++ b/applications/external/picopass/scenes/picopass_scene_write_card_failure.c @@ -0,0 +1,65 @@ +#include "../picopass_i.h" +#include + +void picopass_scene_write_card_failure_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Picopass* picopass = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(picopass->view_dispatcher, result); + } +} + +void picopass_scene_write_card_failure_on_enter(void* context) { + Picopass* picopass = context; + Widget* widget = picopass->widget; + FuriString* str = furi_string_alloc_set("Write Failure!"); + + widget_add_button_element( + widget, + GuiButtonTypeLeft, + "Retry", + picopass_scene_write_card_failure_widget_callback, + picopass); + + widget_add_button_element( + widget, + GuiButtonTypeRight, + "Menu", + picopass_scene_write_card_failure_widget_callback, + picopass); + + widget_add_string_element( + widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(str)); + + furi_string_free(str); + + view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); +} + +bool picopass_scene_write_card_failure_on_event(void* context, SceneManagerEvent event) { + Picopass* picopass = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + consumed = scene_manager_previous_scene(picopass->scene_manager); + } else if(event.event == GuiButtonTypeRight) { + // Clear device name + picopass_device_set_name(picopass->dev, ""); + consumed = scene_manager_search_and_switch_to_previous_scene( + picopass->scene_manager, PicopassSceneStart); + } + } + return consumed; +} + +void picopass_scene_write_card_failure_on_exit(void* context) { + Picopass* picopass = context; + + // Clear view + widget_reset(picopass->widget); +} 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 cd760272f..52b403cfe 100644 --- a/applications/external/picopass/scenes/picopass_scene_write_card_success.c +++ b/applications/external/picopass/scenes/picopass_scene_write_card_success.c @@ -55,8 +55,8 @@ bool picopass_scene_write_card_success_on_event(void* context, SceneManagerEvent } else if(event.event == GuiButtonTypeRight) { // Clear device name picopass_device_set_name(picopass->dev, ""); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu); - consumed = true; + consumed = scene_manager_search_and_switch_to_previous_scene( + picopass->scene_manager, PicopassSceneStart); } } return consumed; diff --git a/applications/external/pocsag_pager/application.fam b/applications/external/pocsag_pager/application.fam index cb893f66f..04634c81f 100644 --- a/applications/external/pocsag_pager/application.fam +++ b/applications/external/pocsag_pager/application.fam @@ -5,7 +5,6 @@ App( entry_point="pocsag_pager_app", requires=["gui"], stack_size=4 * 1024, - order=50, fap_icon="pocsag_pager_10px.png", fap_category="Sub-GHz", fap_icon_assets="images", diff --git a/applications/external/pocsag_pager/pocsag_pager_app.c b/applications/external/pocsag_pager/pocsag_pager_app.c index a381cf7fd..70ff49f1a 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app.c +++ b/applications/external/pocsag_pager/pocsag_pager_app.c @@ -4,7 +4,6 @@ #include #include #include "protocols/protocol_items.h" -#include static bool pocsag_pager_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -199,7 +198,6 @@ int32_t pocsag_pager_app(void* p) { UNUSED(p); POCSAGPagerApp* pocsag_pager_app = pocsag_pager_app_alloc(); - dolphin_deed(DolphinDeedPluginStart); view_dispatcher_run(pocsag_pager_app->view_dispatcher); pocsag_pager_app_free(pocsag_pager_app); diff --git a/applications/external/pocsag_pager/pocsag_pager_app_i.c b/applications/external/pocsag_pager/pocsag_pager_app_i.c index 88c3a8c30..8dda1d8b6 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app_i.c +++ b/applications/external/pocsag_pager/pocsag_pager_app_i.c @@ -136,7 +136,7 @@ void pcsg_hopper_update(POCSAGPagerApp* app) { if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); - } + }; if(app->txrx->txrx_state == PCSGTxRxStateIDLE) { subghz_receiver_reset(app->txrx->receiver); app->txrx->preset->frequency = diff --git a/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c b/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c index 58af9a64a..cc2abd7e0 100644 --- a/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c +++ b/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c @@ -135,7 +135,7 @@ void pocsag_pager_scene_receiver_on_enter(void* context) { if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); - } + }; if((app->txrx->txrx_state == PCSGTxRxStateIDLE) || (app->txrx->txrx_state == PCSGTxRxStateSleep)) { // Start RX @@ -161,7 +161,7 @@ bool pocsag_pager_scene_receiver_on_event(void* context, SceneManagerEvent event if(app->txrx->txrx_state == PCSGTxRxStateRx) { pcsg_rx_end(app); pcsg_idle(app); - } + }; app->txrx->hopper_state = PCSGHopperStateOFF; app->txrx->idx_menu_chosen = 0; subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); diff --git a/applications/external/pocsag_pager/views/pocsag_pager_receiver.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c index 755102682..629c3894e 100644 --- a/applications/external/pocsag_pager/views/pocsag_pager_receiver.c +++ b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c @@ -1,6 +1,6 @@ #include "pocsag_pager_receiver.h" #include "../pocsag_pager_app_i.h" -#include +#include "pocsag_pager_icons.h" #include #include diff --git a/applications/external/pomodoro/flipp_pomodoro_app.c b/applications/external/pomodoro/flipp_pomodoro_app.c index 1e6499db9..c91edc93c 100644 --- a/applications/external/pomodoro/flipp_pomodoro_app.c +++ b/applications/external/pomodoro/flipp_pomodoro_app.c @@ -33,7 +33,7 @@ static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) case FlippPomodoroAppCustomEventStageComplete: if(flipp_pomodoro__get_stage(app->state) == FlippPomodoroStageFocus) { // REGISTER a deed on work stage complete to get an acheivement - // dolphin_deed(DolphinDeedPluginGameWin); + dolphin_deed(DolphinDeedPluginGameWin); FURI_LOG_I(TAG, "Focus stage reward added"); flipp_pomodoro_statistics__increase_focus_stages_completed(app->statistics); @@ -111,11 +111,11 @@ int32_t flipp_pomodoro_app(void* p) { FlippPomodoroApp* app = flipp_pomodoro_app_alloc(); FURI_LOG_I(TAG, "Run deed added"); - // dolphin_deed(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); view_dispatcher_run(app->view_dispatcher); flipp_pomodoro_app_free(app); return 0; -}; +}; \ No newline at end of file diff --git a/applications/external/protoview/app.c b/applications/external/protoview/app.c index 656c35373..868ec8f16 100644 --- a/applications/external/protoview/app.c +++ b/applications/external/protoview/app.c @@ -2,7 +2,6 @@ * See the LICENSE file for information about the license. */ #include "app.h" -#include RawSamplesBuffer *RawSamples, *DetectedSamples; extern const SubGhzProtocolRegistry protoview_protocol_registry; @@ -259,7 +258,6 @@ 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); /* Create a timer. We do data analysis in the callback. */ FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); diff --git a/applications/external/protoview/application.fam b/applications/external/protoview/application.fam index 7dbb7ea31..75084b852 100644 --- a/applications/external/protoview/application.fam +++ b/applications/external/protoview/application.fam @@ -5,7 +5,6 @@ App( entry_point="protoview_app_entry", requires=["gui"], stack_size=8 * 1024, - order=50, fap_icon="appicon.png", fap_category="Sub-GHz", fap_author="@antirez & (fixes by @xMasterX)", diff --git a/applications/external/protoview/signal_file.c b/applications/external/protoview/signal_file.c index e7934d04b..8f325ab52 100644 --- a/applications/external/protoview/signal_file.c +++ b/applications/external/protoview/signal_file.c @@ -45,14 +45,21 @@ bool save_signal(ProtoViewApp* app, const char* filename) { custom, "Custom_preset_module: CC1101\n" "Custom_preset_data: "); - for(int j = 0; regs[j]; j += 2) { + + /* We will know the size of the preset data once we reach the end + * of the registers (null address). For now it's INT_MAX. */ + int preset_data_size = INT_MAX; + bool patable_reached = false; + for(int j = 0; j <= preset_data_size; j += 2) { + // End reached, set the size to write the remaining 8 bytes (PATABLE) + if(!patable_reached && regs[j] == 0) { + preset_data_size = j + 8; + patable_reached = true; + } 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"); + size_t len = furi_string_size(custom); + furi_string_set_char(custom, len - 1, '\n'); furi_string_cat(file_content, custom); furi_string_free(custom); } diff --git a/applications/external/rc2014_coleco/application.fam b/applications/external/rc2014_coleco/application.fam index fe3ee028b..d9b3aa727 100644 --- a/applications/external/rc2014_coleco/application.fam +++ b/applications/external/rc2014_coleco/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_COLECO"], requires=["gui"], stack_size=1 * 1024, - order=35, fap_icon="coleco_10px.png", fap_icon_assets="icons", fap_category="GPIO", diff --git a/applications/external/reversi/application.fam b/applications/external/reversi/application.fam index 25a087c0c..87091e91a 100644 --- a/applications/external/reversi/application.fam +++ b/applications/external/reversi/application.fam @@ -8,7 +8,6 @@ App( "gui", ], stack_size=1 * 1024, - order=90, fap_icon="game_reversi.png", fap_category="Games", fap_icon_assets_symbol="game_reversi", diff --git a/applications/external/rootoflife/application.fam b/applications/external/rootoflife/application.fam index aa3d68211..c335f77aa 100644 --- a/applications/external/rootoflife/application.fam +++ b/applications/external/rootoflife/application.fam @@ -6,7 +6,6 @@ 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", diff --git a/applications/external/sam/application.fam b/applications/external/sam/application.fam index 8a6d70c07..e123f455b 100644 --- a/applications/external/sam/application.fam +++ b/applications/external/sam/application.fam @@ -8,7 +8,6 @@ App( "dialogs", ], stack_size=4 * 1024, - order=20, fap_icon="music_10px.png", fap_category="Media", ) @@ -22,7 +21,6 @@ App( "dialogs", ], stack_size=4 * 1024, - order=20, fap_icon="music_10px.png", fap_category="Media", ) @@ -36,7 +34,6 @@ App( "dialogs", ], stack_size=4 * 1024, - order=20, fap_icon="music_10px.png", fap_category="Media", ) @@ -50,7 +47,6 @@ App( "dialogs", ], stack_size=4 * 1024, - order=20, fap_icon="music_10px.png", fap_category="Media", ) diff --git a/applications/external/scorched_tanks/application.fam b/applications/external/scorched_tanks/application.fam index 4d74c5ced..6256a3c47 100644 --- a/applications/external/scorched_tanks/application.fam +++ b/applications/external/scorched_tanks/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_SCORCHED_TANKS_GAME"], requires=["gui"], stack_size=1 * 1024, - order=100, fap_icon="scorchedTanks_10px.png", fap_category="Games", fap_author="@jasniec", diff --git a/applications/external/sentry_safe/application.fam b/applications/external/sentry_safe/application.fam index 73264cf01..7f17ca5f6 100644 --- a/applications/external/sentry_safe/application.fam +++ b/applications/external/sentry_safe/application.fam @@ -5,7 +5,6 @@ App( entry_point="sentry_safe_app", requires=["gui"], stack_size=1 * 1024, - order=40, fap_icon="safe_10px.png", fap_category="GPIO", fap_author="@H4ckd4ddy & @xMasterX (ported to latest firmware)", diff --git a/applications/external/sentry_safe/sentry_safe.c b/applications/external/sentry_safe/sentry_safe.c index 9c7445463..789b43f2c 100644 --- a/applications/external/sentry_safe/sentry_safe.c +++ b/applications/external/sentry_safe/sentry_safe.c @@ -2,7 +2,6 @@ #include #include #include -#include #include @@ -85,7 +84,6 @@ int32_t sentry_safe_app(void* p) { UNUSED(p); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Event)); - dolphin_deed(DolphinDeedPluginStart); SentryState* sentry_state = malloc(sizeof(SentryState)); @@ -168,4 +166,4 @@ int32_t sentry_safe_app(void* p) { free(sentry_state); return 0; -} +} \ No newline at end of file diff --git a/applications/external/signal_generator/application.fam b/applications/external/signal_generator/application.fam index a7b93b817..4a5877a7c 100644 --- a/applications/external/signal_generator/application.fam +++ b/applications/external/signal_generator/application.fam @@ -5,7 +5,9 @@ App( entry_point="signal_gen_app", requires=["gui"], stack_size=1 * 1024, - order=50, + fap_description="Control GPIO pins to generate digital signals", + fap_version="1.0", fap_icon="signal_gen_10px.png", fap_category="GPIO", + fap_icon_assets="icons", ) diff --git a/applications/external/signal_generator/views/signal_gen_pwm.c b/applications/external/signal_generator/views/signal_gen_pwm.c index 84dc63f92..63669a937 100644 --- a/applications/external/signal_generator/views/signal_gen_pwm.c +++ b/applications/external/signal_generator/views/signal_gen_pwm.c @@ -1,6 +1,7 @@ #include "../signal_gen_app_i.h" #include #include +#include "signal_generator_icons.h" #include typedef enum { diff --git a/applications/external/slots/application.fam b/applications/external/slots/application.fam index 190841543..9a2e244d8 100644 --- a/applications/external/slots/application.fam +++ b/applications/external/slots/application.fam @@ -7,7 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, fap_icon="ddgame_icon.png", - order=30, fap_category="Games", fap_icon_assets="assets", fap_author="@Daniel-dev-s", diff --git a/applications/external/snake_2/application.fam b/applications/external/snake_2/application.fam index 287522983..f05f76441 100644 --- a/applications/external/snake_2/application.fam +++ b/applications/external/snake_2/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_SNAKE_20"], requires=["gui"], stack_size=1 * 1024, - order=30, fap_icon="snake_10px.png", fap_category="Games", fap_author="@Willzvul", diff --git a/applications/external/snake_2/snake_20.c b/applications/external/snake_2/snake_20.c index e56f38b71..c45c9796c 100644 --- a/applications/external/snake_2/snake_20.c +++ b/applications/external/snake_2/snake_20.c @@ -400,7 +400,7 @@ int32_t snake_20_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/external/solitaire/LICENSE b/applications/external/solitaire/LICENSE new file mode 100644 index 000000000..37558e682 --- /dev/null +++ b/applications/external/solitaire/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Tibor Tálosi + +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/solitaire/application.fam b/applications/external/solitaire/application.fam index 99067e1b9..9394dfc8a 100644 --- a/applications/external/solitaire/application.fam +++ b/applications/external/solitaire/application.fam @@ -5,7 +5,6 @@ App( entry_point="solitaire_app", requires=["gui", "storage", "canvas"], stack_size=2 * 1024, - order=30, fap_icon="solitaire_10px.png", fap_category="Games", fap_icon_assets="assets", diff --git a/applications/external/solitaire/defines.h b/applications/external/solitaire/defines.h index 8734395e8..a225b7c83 100644 --- a/applications/external/solitaire/defines.h +++ b/applications/external/solitaire/defines.h @@ -44,6 +44,7 @@ typedef struct { InputKey input; bool started; + bool had_change; bool processing; bool longPress; PlayState state; diff --git a/applications/external/solitaire/solitaire.c b/applications/external/solitaire/solitaire.c index 0dcaa91c3..9b9c8ce74 100644 --- a/applications/external/solitaire/solitaire.c +++ b/applications/external/solitaire/solitaire.c @@ -5,9 +5,11 @@ #include "defines.h" #include "common/ui.h" #include "solitaire_icons.h" +#include #include #include void init(GameState* game_state); + const NotificationSequence sequence_fail = { &message_vibro_on, &message_note_c4, @@ -34,13 +36,6 @@ int8_t columns[7][3] = { }; bool can_place_card(Card where, Card what) { - FURI_LOG_D( - APP_NAME, - "TESTING pip %i, letter %i with pip %i, letter %i", - where.pip, - where.character, - what.pip, - what.character); bool a_black = where.pip == 0 || where.pip == 3; bool b_black = what.pip == 0 || what.pip == 3; if(a_black == b_black) return false; @@ -54,86 +49,97 @@ bool can_place_card(Card where, Card what) { } static void draw_scene(Canvas* const canvas, const GameState* game_state) { - int deckIndex = game_state->deck.index; - if(game_state->dragging_deck) deckIndex--; + if(game_state->had_change) { + int deckIndex = game_state->deck.index; + if(game_state->dragging_deck) deckIndex--; - if((game_state->deck.index < (game_state->deck.card_count - 1) || - game_state->deck.index == -1) && - game_state->deck.card_count > 0) { - draw_card_back_at(columns[0][0], columns[0][1], canvas); - if(game_state->selectRow == 0 && game_state->selectColumn == 0) { - draw_rounded_box( - canvas, - columns[0][0] + 1, - columns[0][1] + 1, - CARD_WIDTH - 2, - CARD_HEIGHT - 2, - Inverse); - } - } else - draw_card_space( - columns[0][0], - columns[0][1], - game_state->selectRow == 0 && game_state->selectColumn == 0, - canvas); - //deck side - if(deckIndex >= 0) { - Card c = game_state->deck.cards[deckIndex]; - draw_card_at_colored( - columns[1][0], - columns[1][1], - c.pip, - c.character, - game_state->selectRow == 0 && game_state->selectColumn == 1, - canvas); - } else - draw_card_space( - columns[1][0], - columns[1][1], - game_state->selectRow == 0 && game_state->selectColumn == 1, - canvas); - - for(uint8_t i = 0; i < 4; i++) { - Card current = game_state->top_cards[i]; - bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3); - if(current.disabled) { - draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas); - } else { - draw_card_at( - columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas); - if(selected) { + if((game_state->deck.index < (game_state->deck.card_count - 1) || + game_state->deck.index == -1) && + game_state->deck.card_count > 0) { + draw_card_back_at(columns[0][0], columns[0][1], canvas); + if(game_state->selectRow == 0 && game_state->selectColumn == 0) { draw_rounded_box( - canvas, columns[i + 3][0], columns[i + 3][1], CARD_WIDTH, CARD_HEIGHT, Inverse); + canvas, + columns[0][0] + 1, + columns[0][1] + 1, + CARD_WIDTH - 2, + CARD_HEIGHT - 2, + Inverse); + } + } else + draw_card_space( + columns[0][0], + columns[0][1], + game_state->selectRow == 0 && game_state->selectColumn == 0, + canvas); + //deck side + if(deckIndex >= 0) { + Card c = game_state->deck.cards[deckIndex]; + draw_card_at_colored( + columns[1][0], + columns[1][1], + c.pip, + c.character, + game_state->selectRow == 0 && game_state->selectColumn == 1, + canvas); + } else + draw_card_space( + columns[1][0], + columns[1][1], + game_state->selectRow == 0 && game_state->selectColumn == 1, + canvas); + + for(uint8_t i = 0; i < 4; i++) { + Card current = game_state->top_cards[i]; + bool selected = game_state->selectRow == 0 && game_state->selectColumn == (i + 3); + if(current.disabled) { + draw_card_space(columns[i + 3][0], columns[i + 3][1], selected, canvas); + } else { + draw_card_at( + columns[i + 3][0], columns[i + 3][1], current.pip, current.character, canvas); + if(selected) { + draw_rounded_box( + canvas, + columns[i + 3][0], + columns[i + 3][1], + CARD_WIDTH, + CARD_HEIGHT, + Inverse); + } } } - } - for(uint8_t i = 0; i < 7; i++) { - bool selected = game_state->selectRow == 1 && game_state->selectColumn == i; - int8_t index = (game_state->bottom_columns[i].index - 1 - game_state->selected_card); - if(index < 0) index = 0; - draw_hand_column( - game_state->bottom_columns[i], - columns[i][0], - columns[i][2], - selected ? index : -1, - canvas); - } + for(uint8_t i = 0; i < 7; i++) { + bool selected = game_state->selectRow == 1 && game_state->selectColumn == i; + int8_t index = (game_state->bottom_columns[i].index - 1 - game_state->selected_card); + if(index < 0) index = 0; + draw_hand_column( + game_state->bottom_columns[i], + columns[i][0], + columns[i][2], + selected ? index : -1, + canvas); + } - int8_t pos[2] = { - columns[game_state->selectColumn][0], - columns[game_state->selectColumn][game_state->selectRow + 1]}; + int8_t pos[2] = { + columns[game_state->selectColumn][0], + columns[game_state->selectColumn][game_state->selectRow + 1]}; - /* draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5, - Filled);*/ + /* draw_icon_clip(canvas, &I_card_graphics, pos[0] + CARD_HALF_WIDTH, pos[1] + CARD_HALF_HEIGHT, 30, 5, 5, 5, + Filled);*/ - if(game_state->dragging_hand.index > 0) { - draw_hand_column( - game_state->dragging_hand, - pos[0] + CARD_HALF_WIDTH + 3, - pos[1] + CARD_HALF_HEIGHT + 3, - -1, - canvas); + if(game_state->dragging_hand.index > 0) { + draw_hand_column( + game_state->dragging_hand, + pos[0] + CARD_HALF_WIDTH + 3, + pos[1] + CARD_HALF_HEIGHT + 3, + -1, + canvas); + } + + clone_buffer(get_buffer(canvas), game_state->animation.buffer); + } else { + clone_buffer(game_state->animation.buffer, get_buffer(canvas)); } } @@ -155,9 +161,11 @@ static void draw_animation(Canvas* const canvas, const GameState* game_state) { } static void render_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); const GameState* game_state = ctx; - furi_mutex_acquire(game_state->mutex, FuriWaitForever); + furi_mutex_acquire(game_state->mutex, 25); + if(game_state == NULL) { + return; + } switch(game_state->state) { case GameStateAnimate: @@ -254,7 +262,6 @@ bool place_on_top(Card* where, Card what) { int8_t b_letter = (int8_t)what.character; if(a_letter == 12) a_letter = -1; if(b_letter == 12) b_letter = -1; - if(where->disabled && b_letter != -1) return false; if((a_letter + 1) == b_letter) { @@ -276,8 +283,10 @@ 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); game_state->state = GameStateAnimate; + game_state->had_change = true; + dolphin_deed(DolphinDeedPluginGameWin); + return; } } @@ -408,6 +417,7 @@ void tick(GameState* game_state, NotificationApp* notification) { } void init(GameState* game_state) { + dolphin_deed(DolphinDeedPluginGameStart); game_state->selectColumn = 0; game_state->selected_card = 0; game_state->selectRow = 0; @@ -486,19 +496,17 @@ int32_t solitaire_app(void* p) { FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 30); - Gui* gui = furi_record_open(RECORD_GUI); + Gui* gui = furi_record_open("gui"); gui_add_view_port(gui, view_port, GuiLayerFullscreen); AppEvent event; - - // Call Dolphin deed on game start - // dolphin_deed(DolphinDeedPluginGameStart); - for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 150); furi_mutex_acquire(game_state->mutex, FuriWaitForever); + game_state->had_change = false; if(event_status == FuriStatusOk) { if(event.type == EventTypeKey) { + game_state->had_change = true; if(event.input.type == InputTypeLong) { game_state->longPress = true; switch(event.input.key) { @@ -545,6 +553,7 @@ int32_t solitaire_app(void* p) { game_state->input = InputKeyMAX; } } + view_port_update(view_port); furi_mutex_release(game_state->mutex); } @@ -566,5 +575,6 @@ free_and_exit: free(game_state->deck.cards); free(game_state); furi_message_queue_free(event_queue); + return return_code; } \ No newline at end of file diff --git a/applications/external/spectrum_analyzer/application.fam b/applications/external/spectrum_analyzer/application.fam index 27cc286d9..13f5d49f9 100644 --- a/applications/external/spectrum_analyzer/application.fam +++ b/applications/external/spectrum_analyzer/application.fam @@ -5,7 +5,6 @@ App( entry_point="spectrum_analyzer_app", requires=["gui"], stack_size=2 * 1024, - order=12, fap_icon="spectrum_10px.png", fap_category="Sub-GHz", fap_author="@xMasterX & @theY4Kman & @ALEEF02 (original by @jolcese)", diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c index 0ff38fc4b..345dd008b 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/external/spectrum_analyzer/spectrum_analyzer.c @@ -5,7 +5,6 @@ #include #include #include "spectrum_analyzer.h" -#include #include #include "spectrum_analyzer_worker.h" @@ -441,7 +440,6 @@ void spectrum_analyzer_free(SpectrumAnalyzer* instance) { int32_t spectrum_analyzer_app(void* p) { UNUSED(p); - dolphin_deed(DolphinDeedPluginStart); SpectrumAnalyzer* spectrum_analyzer = spectrum_analyzer_alloc(); InputEvent input; @@ -610,4 +608,4 @@ int32_t spectrum_analyzer_app(void* p) { spectrum_analyzer_free(spectrum_analyzer); return 0; -} +} \ No newline at end of file diff --git a/applications/external/spi_mem_manager/application.fam b/applications/external/spi_mem_manager/application.fam index 21e70980a..8d1100cf9 100644 --- a/applications/external/spi_mem_manager/application.fam +++ b/applications/external/spi_mem_manager/application.fam @@ -1,11 +1,12 @@ App( appid="spi_mem_manager", - name="[SPI] Mem Manager", + name="[SPI] SPI Mem Manager", apptype=FlipperAppType.EXTERNAL, entry_point="spi_mem_app", requires=["gui"], stack_size=1 * 2048, - order=30, + fap_description="Application for reading and writing 25-series SPI memory chips", + fap_version="1.0", fap_icon="images/Dip8_10px.png", fap_category="GPIO", fap_icon_assets="images", diff --git a/applications/external/spi_mem_manager/views/spi_mem_view_detect.c b/applications/external/spi_mem_manager/views/spi_mem_view_detect.c index eddf36e49..473d54ae2 100644 --- a/applications/external/spi_mem_manager/views/spi_mem_view_detect.c +++ b/applications/external/spi_mem_manager/views/spi_mem_view_detect.c @@ -1,5 +1,6 @@ #include "spi_mem_view_detect.h" #include "spi_mem_manager_icons.h" +#include #include struct SPIMemDetectView { diff --git a/applications/external/subghz_bruteforcer/LICENSE b/applications/external/subghz_bruteforcer/LICENSE index 06dcf7e87..8fb0d1388 100644 --- a/applications/external/subghz_bruteforcer/LICENSE +++ b/applications/external/subghz_bruteforcer/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Der Skythe +Copyright (c) 2023 DerSkythe Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/applications/external/subghz_bruteforcer/application.fam b/applications/external/subghz_bruteforcer/application.fam index 713520a2c..bb9cd6d24 100644 --- a/applications/external/subghz_bruteforcer/application.fam +++ b/applications/external/subghz_bruteforcer/application.fam @@ -5,7 +5,6 @@ App( entry_point="subbrute_app", requires=["gui", "dialogs"], stack_size=2 * 1024, - order=11, fap_icon="subbrute_10px.png", fap_category="Sub-GHz", fap_icon_assets="images", diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_radio_device_loader.c b/applications/external/subghz_bruteforcer/helpers/subbrute_radio_device_loader.c new file mode 100644 index 000000000..f09bc7136 --- /dev/null +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_radio_device_loader.c @@ -0,0 +1,83 @@ +//#include "../../../lib/subghz/protocols/protocol_items.h" +//#include "../../../firmware//targets/f7/furi_hal/furi_hal_subghz.h" +#include +//#include "../../../furi/core/check.h" +// furi/core/check.h +#include +#include +#include +#include +#include "subbrute_radio_device_loader.h" + +#define TAG "SubBruteRadioDeviceLoader" + +static void subbrute_radio_device_loader_power_on() { + uint8_t attempts = 5; + while(--attempts > 0) { + if(furi_hal_power_enable_otg()) { + break; + } + } + if(attempts == 0) { + if(furi_hal_power_get_usb_voltage() < 4.5f) { + FURI_LOG_E( + TAG, + "Error power otg enable. BQ2589 check otg fault = %d", + furi_hal_power_check_otg_fault() ? 1 : 0); + } + } +} + +static void subbrute_radio_device_loader_power_off() { + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } +} + +bool subbrute_radio_device_loader_is_connect_external(const char* name) { + bool is_connect = false; + bool is_otg_enabled = furi_hal_power_is_otg_enabled(); + + if(!is_otg_enabled) { + subbrute_radio_device_loader_power_on(); + } + + const SubGhzDevice* device = subghz_devices_get_by_name(name); + if(device) { + is_connect = subghz_devices_is_connect(device); + } + + if(!is_otg_enabled) { + subbrute_radio_device_loader_power_off(); + } + return is_connect; +} + +const SubGhzDevice* subbrute_radio_device_loader_set( + const SubGhzDevice* current_radio_device, + SubGhzRadioDeviceType radio_device_type) { + const SubGhzDevice* radio_device; + + if(radio_device_type == SubGhzRadioDeviceTypeExternalCC1101 && + subbrute_radio_device_loader_is_connect_external(SUBGHZ_DEVICE_CC1101_EXT_NAME)) { + subbrute_radio_device_loader_power_on(); + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_EXT_NAME); + subghz_devices_begin(radio_device); + } else if(current_radio_device == NULL) { + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + } else { + subbrute_radio_device_loader_end(current_radio_device); + radio_device = subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME); + } + + return radio_device; +} + +void subbrute_radio_device_loader_end(const SubGhzDevice* radio_device) { + furi_assert(radio_device); + + subbrute_radio_device_loader_power_off(); + if(radio_device != subghz_devices_get_by_name(SUBGHZ_DEVICE_CC1101_INT_NAME)) { + subghz_devices_end(radio_device); + } +} diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_radio_device_loader.h b/applications/external/subghz_bruteforcer/helpers/subbrute_radio_device_loader.h new file mode 100644 index 000000000..664d9ac36 --- /dev/null +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_radio_device_loader.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include "subghz/helpers/subghz_types.h" + +const SubGhzDevice* subbrute_radio_device_loader_set( + const SubGhzDevice* current_radio_device, + SubGhzRadioDeviceType radio_device_type); + +void subbrute_radio_device_loader_end(const SubGhzDevice* radio_device); diff --git a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c index a2f3d8d05..2061f7c81 100644 --- a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.c @@ -59,7 +59,7 @@ void subbrute_worker_free(SubBruteWorker* instance) { furi_thread_free(instance->thread); subghz_devices_sleep(instance->radio_device); - radio_device_loader_end(instance->radio_device); + subbrute_radio_device_loader_end(instance->radio_device); free(instance); } @@ -85,7 +85,7 @@ bool subbrute_worker_init_default_attack( SubBruteAttacks attack_type, uint64_t step, const SubBruteProtocol* protocol, - uint8_t extra_repeats) { + uint8_t repeats) { furi_assert(instance); if(instance->worker_running) { @@ -100,7 +100,7 @@ bool subbrute_worker_init_default_attack( instance->step = step; instance->bits = protocol->bits; instance->te = protocol->te; - instance->repeat = protocol->repeat + extra_repeats; + instance->repeat = repeats; instance->load_index = 0; instance->file_key = 0; instance->two_bytes = false; @@ -133,7 +133,7 @@ bool subbrute_worker_init_file_attack( uint8_t load_index, uint64_t file_key, SubBruteProtocol* protocol, - uint8_t extra_repeats, + uint8_t repeats, bool two_bytes) { furi_assert(instance); @@ -150,7 +150,7 @@ bool subbrute_worker_init_file_attack( instance->bits = protocol->bits; instance->te = protocol->te; instance->load_index = load_index; - instance->repeat = protocol->repeat + extra_repeats; + instance->repeat = repeats; instance->file_key = file_key; instance->two_bytes = two_bytes; @@ -490,6 +490,7 @@ bool subbrute_worker_is_tx_allowed(SubBruteWorker* instance, uint32_t value) { bool res = false; if(!subghz_devices_is_frequency_valid(instance->radio_device, value)) { + return false; } else { subghz_devices_set_frequency(instance->radio_device, value); res = subghz_devices_set_tx(instance->radio_device); @@ -497,4 +498,4 @@ bool subbrute_worker_is_tx_allowed(SubBruteWorker* instance, uint32_t value) { } return res; -} \ 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 c5b04f991..38caf5f6d 100644 --- a/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h +++ b/applications/external/subghz_bruteforcer/helpers/subbrute_worker.h @@ -1,7 +1,7 @@ #pragma once #include "../subbrute_protocols.h" -#include "radio_device_loader.h" +#include "subbrute_radio_device_loader.h" typedef enum { SubBruteWorkerStateIDLE, @@ -24,14 +24,14 @@ bool subbrute_worker_init_default_attack( SubBruteAttacks attack_type, uint64_t step, const SubBruteProtocol* protocol, - uint8_t extra_repeats); + uint8_t repeats); bool subbrute_worker_init_file_attack( SubBruteWorker* instance, uint64_t step, uint8_t load_index, uint64_t file_key, SubBruteProtocol* protocol, - uint8_t extra_repeats, + uint8_t repeats, bool two_bytes); bool subbrute_worker_start(SubBruteWorker* instance); void subbrute_worker_stop(SubBruteWorker* instance); @@ -53,4 +53,4 @@ void subbrute_worker_set_te(SubBruteWorker* instance, uint32_t te); // void subbrute_worker_timeout_dec(SubBruteWorker* instance); -bool subbrute_worker_is_tx_allowed(SubBruteWorker* instance, uint32_t value); \ No newline at end of file +bool subbrute_worker_is_tx_allowed(SubBruteWorker* instance, uint32_t value); 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 13fc85909..7cd318ed5 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_file.c @@ -36,10 +36,13 @@ void subbrute_scene_load_file_on_enter(void* context) { load_result = subbrute_device_load_from_file(instance->device, furi_string_get_cstr(load_path)); if(load_result == SubBruteFileResultOk) { - uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); + instance->settings->last_index = SubBruteAttackLoadFile; + subbrute_settings_set_repeats( + instance->settings, subbrute_main_view_get_extra_repeats(instance->view_main)); + uint8_t extra_repeats = subbrute_settings_get_current_repeats(instance->settings); load_result = subbrute_device_attack_set( - instance->device, SubBruteAttackLoadFile, extra_repeats); + instance->device, instance->settings->last_index, extra_repeats); if(load_result == SubBruteFileResultOk) { if(!subbrute_worker_init_file_attack( instance->worker, @@ -58,6 +61,7 @@ void subbrute_scene_load_file_on_enter(void* context) { } if(load_result == SubBruteFileResultOk) { + subbrute_settings_save(instance->settings); scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadSelect); } else { FURI_LOG_E(TAG, "Returned error: %d", load_result); diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_select.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_select.c index d018e8b4d..5d2af28ed 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_select.c +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_load_select.c @@ -21,7 +21,12 @@ void subbrute_scene_load_select_on_enter(void* context) { instance->current_view = SubBruteViewMain; subbrute_main_view_set_callback(view, subbrute_scene_load_select_callback, instance); subbrute_main_view_set_index( - view, 7, true, instance->device->two_bytes, instance->device->key_from_file); + view, + 7, + instance->settings->repeat_values, + true, + instance->device->two_bytes, + instance->device->key_from_file); view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); } @@ -46,7 +51,12 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) instance->device->current_step = 0; instance->device->bit_index = subbrute_main_view_get_index(instance->view_main); instance->device->two_bytes = subbrute_main_view_get_two_bytes(instance->view_main); - uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); + + instance->settings->last_index = instance->device->attack; + subbrute_settings_set_repeats( + instance->settings, subbrute_main_view_get_extra_repeats(instance->view_main)); + uint8_t total_repeats = subbrute_settings_get_current_repeats(instance->settings); + instance->device->max_value = subbrute_protocol_calc_max_value( instance->device->attack, instance->device->bit_index, @@ -58,10 +68,11 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) instance->device->bit_index, instance->device->key_from_file, instance->device->file_protocol_info, - extra_repeats, + total_repeats, instance->device->two_bytes)) { furi_crash("Invalid attack set!"); } + subbrute_settings_save(instance->settings); scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); /*#endif*/ consumed = true; @@ -79,4 +90,4 @@ bool subbrute_scene_load_select_on_event(void* context, SceneManagerEvent event) } return consumed; -} \ No newline at end of file +} diff --git a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c index a4f14aa26..cbb19fb26 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_setup_extra.c @@ -12,7 +12,7 @@ enum SubBruteVarListIndex { SubBruteVarListIndexTimeDelay, - SubBruteVarListIndexRepeat_or_OnExtra, + SubBruteVarListIndexRepeatOrOnExtra, SubBruteVarListIndexTe, }; @@ -255,7 +255,7 @@ static void setup_extra_enter_callback(void* context, uint32_t index) { furi_assert(context); SubBruteState* instance = context; - if(index == SubBruteVarListIndexRepeat_or_OnExtra) { + if(index == SubBruteVarListIndexRepeatOrOnExtra) { subbrute_scene_setup_extra_init_var_list(instance, true); } } @@ -278,4 +278,4 @@ 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/scenes/subbrute_scene_start.c b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_start.c index 256762d92..da8814a22 100644 --- a/applications/external/subghz_bruteforcer/scenes/subbrute_scene_start.c +++ b/applications/external/subghz_bruteforcer/scenes/subbrute_scene_start.c @@ -7,9 +7,6 @@ void subbrute_scene_start_callback(SubBruteCustomEvent event, void* context) { furi_assert(context); SubBruteState* instance = (SubBruteState*)context; -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_scene_start_callback"); -#endif view_dispatcher_send_custom_event(instance->view_dispatcher, event); } @@ -23,22 +20,22 @@ void subbrute_scene_start_on_enter(void* context) { instance->current_view = SubBruteViewMain; subbrute_main_view_set_callback(view, subbrute_scene_start_callback, instance); + + instance->device->attack = instance->settings->last_index; + subbrute_main_view_set_index( - view, instance->device->attack, false, instance->device->two_bytes, 0); + view, + instance->settings->last_index, + instance->settings->repeat_values, + false, + instance->device->two_bytes, + 0); view_dispatcher_switch_to_view(instance->view_dispatcher, instance->current_view); - - // TODO: DELETE IT -#ifdef SUBBRUTE_FAST_TRACK - scene_manager_next_scene(instance->scene_manager, SubBruteSceneLoadFile); -#endif } void subbrute_scene_start_on_exit(void* context) { - UNUSED(context); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "subbrute_scene_start_on_exit"); -#endif + furi_assert(context); } bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) { @@ -55,19 +52,23 @@ bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) { event.event == SubBruteCustomEventTypeLoadFile ? "true" : "false"); #endif if(event.event == SubBruteCustomEventTypeMenuSelected) { - SubBruteAttacks attack = subbrute_main_view_get_index(instance->view_main); - uint8_t extra_repeats = subbrute_main_view_get_extra_repeats(instance->view_main); + instance->settings->last_index = subbrute_main_view_get_index(instance->view_main); + subbrute_settings_set_repeats( + instance->settings, subbrute_main_view_get_extra_repeats(instance->view_main)); + uint8_t total_repeats = subbrute_settings_get_current_repeats(instance->settings); - if((subbrute_device_attack_set(instance->device, attack, extra_repeats) != + if((subbrute_device_attack_set( + instance->device, instance->settings->last_index, total_repeats) != SubBruteFileResultOk) || (!subbrute_worker_init_default_attack( instance->worker, - attack, + instance->settings->last_index, instance->device->current_step, instance->device->protocol_info, instance->device->extra_repeats))) { furi_crash("Invalid attack set!"); } + subbrute_settings_save(instance->settings); scene_manager_next_scene(instance->scene_manager, SubBruteSceneSetupAttack); consumed = true; @@ -80,10 +81,15 @@ bool subbrute_scene_start_on_event(void* context, SceneManagerEvent event) { } } else if(event.type == SceneManagerEventTypeBack) { //exit app + instance->settings->last_index = subbrute_main_view_get_index(instance->view_main); + subbrute_settings_set_repeats( + instance->settings, subbrute_main_view_get_extra_repeats(instance->view_main)); + subbrute_settings_save(instance->settings); + scene_manager_stop(instance->scene_manager); view_dispatcher_stop(instance->view_dispatcher); consumed = true; } return consumed; -} \ No newline at end of file +} diff --git a/applications/external/subghz_bruteforcer/subbrute.c b/applications/external/subghz_bruteforcer/subbrute.c index 65e3616ce..c115bd4e0 100644 --- a/applications/external/subghz_bruteforcer/subbrute.c +++ b/applications/external/subghz_bruteforcer/subbrute.c @@ -1,7 +1,5 @@ #include "subbrute_i.h" -#include "subbrute_custom_event.h" #include "scenes/subbrute_scene.h" -#include #define TAG "SubBruteApp" @@ -52,8 +50,8 @@ SubBruteState* subbrute_alloc() { subghz_devices_init(); // init radio device - instance->radio_device = - radio_device_loader_set(instance->radio_device, SubGhzRadioDeviceTypeExternalCC1101); + instance->radio_device = subbrute_radio_device_loader_set( + instance->radio_device, SubGhzRadioDeviceTypeExternalCC1101); subghz_devices_reset(instance->radio_device); subghz_devices_idle(instance->radio_device); @@ -107,6 +105,8 @@ SubBruteState* subbrute_alloc() { SubBruteViewAttack, subbrute_attack_view_get_view(instance->view_attack)); + instance->settings = subbrute_settings_alloc(); + subbrute_settings_load(instance->settings); //instance->flipper_format = flipper_format_string_alloc(); //instance->environment = subghz_environment_alloc(); @@ -128,9 +128,11 @@ void subbrute_free(SubBruteState* instance) { // SubBruteDevice subbrute_device_free(instance->device); - subghz_devices_deinit(); + //subbrute_settings_save(instance->settings); + subbrute_settings_free(instance->settings); + // Notifications notification_message(instance->notifications, &sequence_blink_stop); furi_record_close(RECORD_NOTIFICATION); @@ -203,7 +205,6 @@ int32_t subbrute_app(void* p) { UNUSED(p); furi_hal_power_suppress_charge_enter(); - dolphin_deed(DolphinDeedPluginStart); SubBruteState* instance = subbrute_alloc(); view_dispatcher_attach_to_gui( instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); diff --git a/applications/external/subghz_bruteforcer/subbrute_device.c b/applications/external/subghz_bruteforcer/subbrute_device.c index 48d4e4bc4..d39624d6b 100644 --- a/applications/external/subghz_bruteforcer/subbrute_device.c +++ b/applications/external/subghz_bruteforcer/subbrute_device.c @@ -1,9 +1,6 @@ #include "subbrute_device.h" -#include #include -#include -#include #include #include @@ -24,11 +21,11 @@ SubBruteDevice* subbrute_device_alloc(const SubGhzDevice* radio_device) { instance->radio_device = radio_device; -#ifdef FURI_DEBUG - subbrute_device_attack_set_default_values(instance, SubBruteAttackLoadFile); -#else + //#ifdef FURI_DEBUG + // subbrute_device_attack_set_default_values(instance, SubBruteAttackLoadFile); + //#else subbrute_device_attack_set_default_values(instance, SubBruteAttackCAME12bit433); -#endif + //#endif return instance; } @@ -386,6 +383,7 @@ uint8_t subbrute_device_load_from_file(SubBruteDevice* instance, const char* fil FURI_LOG_D(TAG, "Loaded successfully"); #endif } else { + FURI_LOG_E(TAG, "Load failed!"); subbrute_device_free_protocol_info(instance); } @@ -468,4 +466,4 @@ void subbrute_device_free_protocol_info(SubBruteDevice* instance) { free(instance->file_protocol_info); } instance->file_protocol_info = NULL; -} \ No newline at end of file +} diff --git a/applications/external/subghz_bruteforcer/subbrute_device.h b/applications/external/subghz_bruteforcer/subbrute_device.h index 8b91222ee..8ee1e3324 100644 --- a/applications/external/subghz_bruteforcer/subbrute_device.h +++ b/applications/external/subghz_bruteforcer/subbrute_device.h @@ -5,16 +5,12 @@ #include #include #include -#include "helpers/radio_device_loader.h" - -#define SUBBRUTE_TEXT_STORE_SIZE 256 +#include "helpers/subbrute_radio_device_loader.h" #define SUBBRUTE_MAX_LEN_NAME 64 #define SUBBRUTE_PATH EXT_PATH("subghz") #define SUBBRUTE_FILE_EXT ".sub" -#define SUBBRUTE_PAYLOAD_SIZE 16 - typedef enum { SubBruteFileResultUnknown, SubBruteFileResultOk, @@ -54,11 +50,12 @@ typedef struct { uint64_t key_from_file; uint64_t current_key_from_file; bool two_bytes; + // Index of group to bruteforce in loaded file uint8_t bit_index; } SubBruteDevice; -SubBruteDevice* subbrute_device_alloc(const SubGhzDevice* radio_device;); +SubBruteDevice* subbrute_device_alloc(const SubGhzDevice* radio_device); void subbrute_device_free(SubBruteDevice* instance); bool subbrute_device_save_file(SubBruteDevice* instance, const char* key_name); @@ -74,4 +71,4 @@ uint64_t subbrute_device_add_step(SubBruteDevice* instance, int8_t step); void subbrute_device_free_protocol_info(SubBruteDevice* instance); void subbrute_device_attack_set_default_values( SubBruteDevice* context, - SubBruteAttacks default_attack); \ No newline at end of file + SubBruteAttacks default_attack); diff --git a/applications/external/subghz_bruteforcer/subbrute_i.h b/applications/external/subghz_bruteforcer/subbrute_i.h index bab48dc20..2478586ba 100644 --- a/applications/external/subghz_bruteforcer/subbrute_i.h +++ b/applications/external/subghz_bruteforcer/subbrute_i.h @@ -27,11 +27,12 @@ #include "subbrute.h" #include "subbrute_device.h" +#include "subbrute_settings.h" #include "helpers/subbrute_worker.h" #include "views/subbrute_attack_view.h" #include "views/subbrute_main_view.h" -#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.7" +#define SUBBRUTEFORCER_VER "Sub-GHz BruteForcer 3.9" #ifdef FURI_DEBUG //#define SUBBRUTE_FAST_TRACK false @@ -78,6 +79,8 @@ struct SubBruteState { SubBruteDevice* device; // SubBruteWorker SubBruteWorker* worker; + // Last used settings + SubBruteSettings* settings; }; void subbrute_show_loading_popup(void* context, bool show); diff --git a/applications/external/subghz_bruteforcer/subbrute_protocols.c b/applications/external/subghz_bruteforcer/subbrute_protocols.c index 1f3e45129..4956d3e4c 100644 --- a/applications/external/subghz_bruteforcer/subbrute_protocols.c +++ b/applications/external/subghz_bruteforcer/subbrute_protocols.c @@ -1,6 +1,5 @@ #include "subbrute_protocols.h" #include "math.h" -#include #define TAG "SubBruteProtocols" @@ -873,4 +872,4 @@ uint64_t } return max_value; -} \ No newline at end of file +} diff --git a/applications/external/subghz_bruteforcer/subbrute_protocols.h b/applications/external/subghz_bruteforcer/subbrute_protocols.h index 066040b10..009c22b4c 100644 --- a/applications/external/subghz_bruteforcer/subbrute_protocols.h +++ b/applications/external/subghz_bruteforcer/subbrute_protocols.h @@ -5,6 +5,8 @@ #include #include +#define SUBBRUTE_PROTOCOL_MAX_REPEATS 9 + typedef enum { CAMEFileProtocol, NICEFileProtocol, @@ -123,4 +125,4 @@ void subbrute_protocol_file_generate_file( uint64_t file_key, bool two_bytes); uint64_t - subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes); \ No newline at end of file + subbrute_protocol_calc_max_value(SubBruteAttacks attack_type, uint8_t bits, bool two_bytes); diff --git a/applications/external/subghz_bruteforcer/subbrute_settings.c b/applications/external/subghz_bruteforcer/subbrute_settings.c new file mode 100644 index 000000000..3487f8d69 --- /dev/null +++ b/applications/external/subghz_bruteforcer/subbrute_settings.c @@ -0,0 +1,147 @@ +#include "subbrute_settings.h" +#include "subbrute_i.h" + +#define TAG "SubBruteSettings" + +#define SUBBRUTE_SETTINGS_FILE_TYPE "Sub-GHz BruteForcer Settings File" +#define SUBBRUTE_SETTINGS_FILE_VERSION 1 +#define SUBBRUTE_SETTINGS_PATH EXT_PATH("subghz-bruteforcer.settings") + +#define SUBBRUTE_FIELD_LAST_INDEX "LastIndex" +#define SUBBRUTE_FIELD_REPEAT_VALUES "RepeatValue" + +SubBruteSettings* subbrute_settings_alloc(void) { + SubBruteSettings* instance = malloc(sizeof(SubBruteSettings)); + return instance; +} + +void subbrute_settings_free(SubBruteSettings* instance) { + furi_assert(instance); + free(instance); +} + +void subbrute_settings_load(SubBruteSettings* instance) { + furi_assert(instance); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + uint32_t temp_last_index = 0; + uint8_t temp_repeat_values[SubBruteAttackTotalCount] = {0}; + bool was_read_last_index = false; + bool was_read_repeat_values = false; + + if(FSE_OK == storage_sd_status(storage) && SUBBRUTE_SETTINGS_PATH && + flipper_format_file_open_existing(fff_data_file, SUBBRUTE_SETTINGS_PATH)) { + was_read_last_index = flipper_format_read_uint32( + fff_data_file, SUBBRUTE_FIELD_LAST_INDEX, (uint32_t*)&temp_last_index, 1); + was_read_repeat_values = flipper_format_read_hex( + fff_data_file, + SUBBRUTE_FIELD_REPEAT_VALUES, + temp_repeat_values, + SubBruteAttackTotalCount); + } else { + FURI_LOG_E(TAG, "Error open file %s", SUBBRUTE_SETTINGS_PATH); + } + + if(was_read_last_index && temp_last_index < SubBruteAttackTotalCount) { + instance->last_index = temp_last_index; + } else { + FURI_LOG_W(TAG, "Last used index not found or can't be used!"); + instance->last_index = (uint32_t)SubBruteAttackCAME12bit433; + } + if(was_read_repeat_values) { + for(size_t i = 0; i < SubBruteAttackTotalCount; i++) { + uint8_t protocol_count = subbrute_protocol_repeats_count(i); + uint8_t max_protocol_count = protocol_count * 3; + if(temp_repeat_values[i] < protocol_count) { + instance->repeat_values[i] = protocol_count; + } else if(temp_repeat_values[i] > max_protocol_count) { + instance->repeat_values[i] = max_protocol_count; + } else { + instance->repeat_values[i] = temp_repeat_values[i]; + } + } + } else { + FURI_LOG_W(TAG, "Last used repeat values can't be used!"); + for(size_t i = 0; i < SubBruteAttackTotalCount; i++) { + instance->repeat_values[i] = subbrute_protocol_repeats_count(i); + } + } + + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); +} + +bool subbrute_settings_save(SubBruteSettings* instance) { + furi_assert(instance); + + bool saved = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(FSE_OK != storage_sd_status(storage)) { + break; + } + // Open file + if(!flipper_format_file_open_always(file, SUBBRUTE_SETTINGS_PATH)) { + break; + } + // Write header + if(!flipper_format_write_header_cstr( + file, SUBBRUTE_SETTINGS_FILE_TYPE, SUBBRUTE_SETTINGS_FILE_VERSION)) { + break; + } + if(!flipper_format_insert_or_update_uint32( + file, SUBBRUTE_FIELD_LAST_INDEX, &instance->last_index, 1)) { + break; + } + + if(!flipper_format_insert_or_update_hex( + file, + SUBBRUTE_FIELD_REPEAT_VALUES, + instance->repeat_values, + SubBruteAttackTotalCount)) { + break; + } + saved = true; + break; + } while(true); + + if(!saved) { + FURI_LOG_E(TAG, "Error save file %s", SUBBRUTE_SETTINGS_PATH); + } + + flipper_format_file_close(file); + flipper_format_free(file); + furi_record_close(RECORD_STORAGE); + + return saved; +} + +void subbrute_settings_set_value(SubBruteSettings* instance, SubBruteAttacks index, uint8_t value) { + furi_assert(instance); + + instance->repeat_values[index] = value; +} +uint8_t subbrute_settings_get_value(SubBruteSettings* instance, SubBruteAttacks index) { + furi_assert(instance); + + return instance->repeat_values[index]; +} + +void subbrute_settings_set_repeats(SubBruteSettings* instance, const uint8_t* repeated_values) { + furi_assert(instance); + + for(size_t i = 0; i < SubBruteAttackTotalCount; i++) { + instance->repeat_values[i] = repeated_values[i]; + } +} + +uint8_t subbrute_settings_get_current_repeats(SubBruteSettings* instance) { + furi_assert(instance); + + return instance->repeat_values[instance->last_index]; +} diff --git a/applications/external/subghz_bruteforcer/subbrute_settings.h b/applications/external/subghz_bruteforcer/subbrute_settings.h new file mode 100644 index 000000000..961ff0973 --- /dev/null +++ b/applications/external/subghz_bruteforcer/subbrute_settings.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include +#include "subbrute_protocols.h" + +typedef struct { + uint8_t repeat_values[SubBruteAttackTotalCount]; + uint32_t last_index; +} SubBruteSettings; + +SubBruteSettings* subbrute_settings_alloc(void); +void subbrute_settings_free(SubBruteSettings* instance); +void subbrute_settings_load(SubBruteSettings* instance); +bool subbrute_settings_save(SubBruteSettings* instance); +void subbrute_settings_set_value(SubBruteSettings* instance, SubBruteAttacks index, uint8_t value); +uint8_t subbrute_settings_get_value(SubBruteSettings* instance, SubBruteAttacks index); +void subbrute_settings_set_repeats(SubBruteSettings* instance, const uint8_t* repeated_values); +uint8_t subbrute_settings_get_current_repeats(SubBruteSettings* instance); diff --git a/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c index 2e5a73de7..1a22ff56b 100644 --- a/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c +++ b/applications/external/subghz_bruteforcer/views/subbrute_attack_view.c @@ -1,13 +1,12 @@ #include "subbrute_attack_view.h" #include "../subbrute_i.h" -#include "../subbrute_protocols.h" #include "../helpers/gui_top_buttons.h" #include #include -#include #include -#include +#include "subghz_bruteforcer_icons.h" +#include #define TAG "SubBruteAttackView" @@ -26,7 +25,7 @@ typedef struct { SubBruteAttacks attack_type; uint64_t max_value; uint64_t current_step; - uint8_t extra_repeats; + uint8_t repeat_count; bool is_attacking; IconAnimation* icon; } SubBruteAttackViewModel; @@ -126,10 +125,10 @@ bool subbrute_attack_view_input(InputEvent* event, void* context) { model->current_step = instance->current_step; model->is_attacking = instance->is_attacking; }, - true); + update); } - return true; + return update; } SubBruteAttackView* subbrute_attack_view_alloc() { @@ -236,7 +235,7 @@ void subbrute_attack_view_init_values( model->attack_type = index; model->current_step = current_step; model->is_attacking = is_attacking; - model->extra_repeats = extra_repeats; + model->repeat_count = extra_repeats; if(is_attacking) { icon_animation_start(model->icon); } else { @@ -308,7 +307,7 @@ void subbrute_attack_view_draw(Canvas* canvas, void* context) { buffer, sizeof(buffer), "x%d", - model->extra_repeats); // + subbrute_protocol_repeats_count(model->attack_type)); + model->repeat_count); // + subbrute_protocol_repeats_count(model->attack_type)); canvas_draw_str_aligned(canvas, 60, 6, AlignCenter, AlignCenter, buffer); elements_button_left(canvas, "-1"); @@ -335,7 +334,7 @@ void subbrute_attack_view_draw(Canvas* canvas, void* context) { buffer, sizeof(buffer), "x%d", - model->extra_repeats); // + subbrute_protocol_repeats_count(model->attack_type)); + model->repeat_count); // + subbrute_protocol_repeats_count(model->attack_type)); canvas_draw_str(canvas, 4, y - 8, buffer); canvas_draw_str(canvas, 4, y - 1, "repeats"); diff --git a/applications/external/subghz_bruteforcer/views/subbrute_main_view.c b/applications/external/subghz_bruteforcer/views/subbrute_main_view.c index 921ed6b6e..2358f2375 100644 --- a/applications/external/subghz_bruteforcer/views/subbrute_main_view.c +++ b/applications/external/subghz_bruteforcer/views/subbrute_main_view.c @@ -1,11 +1,9 @@ #include "subbrute_main_view.h" #include "../subbrute_i.h" -#include "../subbrute_protocols.h" #include "../helpers/gui_top_buttons.h" #include #include -#include #define STATUS_BAR_Y_SHIFT 14 #define TAG "SubBruteMainView" @@ -29,13 +27,13 @@ struct SubBruteMainView { bool is_select_byte; bool two_bytes; uint64_t key_from_file; - uint8_t extra_repeats; + uint8_t repeat_values[SubBruteAttackTotalCount]; uint8_t window_position; }; typedef struct { uint8_t index; - uint8_t extra_repeats; + uint8_t repeat_values[SubBruteAttackTotalCount]; uint8_t window_position; bool is_select_byte; bool two_bytes; @@ -110,227 +108,252 @@ void subbrute_main_view_center_displayed_key( canvas_set_color(canvas, ColorBlack); } -void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { +void subbrute_main_view_draw_is_byte_selected(Canvas* canvas, SubBruteMainViewModel* model) { +#ifdef FURI_DEBUG + //FURI_LOG_D(TAG, "key_from_file: %s", model->key_from_file); +#endif + //char msg_index[18]; + //snprintf(msg_index, sizeof(msg_index), "Field index: %d", model->index); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 17, AlignCenter, AlignTop, "Please select values to calc:"); + + subbrute_main_view_center_displayed_key( + canvas, model->key_from_file, model->index, model->two_bytes); + //const char* line = furi_string_get_cstr(menu_items); + //canvas_set_font(canvas, FontSecondary); + //canvas_draw_str_aligned( + // canvas, 64, 37, AlignCenter, AlignTop, furi_string_get_cstr(menu_items)); + + elements_button_center(canvas, "Select"); + if(model->index > 0) { + elements_button_left(canvas, " "); + } + if(model->index < 7) { + elements_button_right(canvas, " "); + } + // Switch to another mode + if(model->two_bytes) { + elements_button_top_left(canvas, "One byte"); + } else { + elements_button_top_left(canvas, "Two bytes"); + } +} + +void subbrute_main_view_draw_is_ordinary_selected(Canvas* canvas, SubBruteMainViewModel* model) { uint16_t screen_width = canvas_width(canvas); uint16_t screen_height = canvas_height(canvas); - if(model->is_select_byte) { -#ifdef FURI_DEBUG - //FURI_LOG_D(TAG, "key_from_file: %s", model->key_from_file); -#endif - //char msg_index[18]; - //snprintf(msg_index, sizeof(msg_index), "Field index: %d", model->index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 17, AlignCenter, AlignTop, "Please select values to calc:"); + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT); + canvas_invert_color(canvas); + canvas_draw_str_aligned(canvas, 64, 3, AlignCenter, AlignTop, SUBBRUTEFORCER_VER); + canvas_invert_color(canvas); - subbrute_main_view_center_displayed_key( - canvas, model->key_from_file, model->index, model->two_bytes); - //const char* line = furi_string_get_cstr(menu_items); - //canvas_set_font(canvas, FontSecondary); - //canvas_draw_str_aligned( - // canvas, 64, 37, AlignCenter, AlignTop, furi_string_get_cstr(menu_items)); - - elements_button_center(canvas, "Select"); - if(model->index > 0) { - elements_button_left(canvas, " "); - } - if(model->index < 7) { - elements_button_right(canvas, " "); - } - // Switch to another mode - if(model->two_bytes) { - elements_button_top_left(canvas, "One byte"); - } else { - elements_button_top_left(canvas, "Two bytes"); - } - } else { - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_box(canvas, 0, 0, canvas_width(canvas), STATUS_BAR_Y_SHIFT); - canvas_invert_color(canvas); - canvas_draw_str_aligned(canvas, 64, 3, AlignCenter, AlignTop, SUBBRUTEFORCER_VER); - canvas_invert_color(canvas); - - // Menu - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - const uint8_t item_height = 16; + // Menu + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + const uint8_t item_height = 16; + const uint8_t string_height_offset = 9; #ifdef FURI_DEBUG - //FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, model->index); + //FURI_LOG_D(TAG, "window_position: %d, index: %d", model->window_position, model->index); #endif - for(uint8_t position = 0; position < SubBruteAttackTotalCount; ++position) { - uint8_t item_position = position - model->window_position; + for(size_t position = 0; position < SubBruteAttackTotalCount; ++position) { + uint8_t item_position = position - model->window_position; - if(item_position < ITEMS_ON_SCREEN) { - if(model->index == position) { - canvas_draw_str_aligned( - canvas, - 3, - 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, - AlignLeft, - AlignCenter, - subbrute_protocol_name(position)); + if(item_position < ITEMS_ON_SCREEN) { + if(model->index == position) { + canvas_draw_str_aligned( + canvas, + 3, + string_height_offset + (item_position * item_height) + STATUS_BAR_Y_SHIFT, + AlignLeft, + AlignCenter, + subbrute_protocol_name(position)); - if(model->extra_repeats > 0) { + elements_frame( + canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15); + } else { + canvas_draw_str_aligned( + canvas, + 4, + string_height_offset + (item_position * item_height) + STATUS_BAR_Y_SHIFT, + AlignLeft, + AlignCenter, + subbrute_protocol_name(position)); + } + + uint8_t current_repeat_count = model->repeat_values[position]; + uint8_t min_repeat_count = subbrute_protocol_repeats_count(position); + + if(current_repeat_count > min_repeat_count) { #ifdef FW_ORIGIN_Official - canvas_set_font(canvas, FontSecondary); + canvas_set_font(canvas, FontSecondary); #else - canvas_set_font(canvas, FontBatteryPercent); + canvas_set_font(canvas, FontBatteryPercent); #endif - char buffer[10]; - snprintf( - buffer, - 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 - temp_x_offset_repeats, - 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, - AlignLeft, - AlignCenter, - buffer); - canvas_set_font(canvas, FontSecondary); - } + char buffer[10]; + snprintf(buffer, sizeof(buffer), "x%d", current_repeat_count); + uint8_t temp_x_offset_repeats = + current_repeat_count <= SUBBRUTE_PROTOCOL_MAX_REPEATS ? 15 : 18; - elements_frame( - canvas, 1, 1 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, 124, 15); - } else { - canvas_draw_str_aligned( - canvas, - 4, - 9 + (item_position * item_height) + STATUS_BAR_Y_SHIFT, - AlignLeft, - AlignCenter, - subbrute_protocol_name(position)); - } + canvas_draw_str_aligned( + canvas, + screen_width - temp_x_offset_repeats, + string_height_offset + (item_position * item_height) + STATUS_BAR_Y_SHIFT, + AlignLeft, + AlignCenter, + buffer); + canvas_set_font(canvas, FontSecondary); } } - - elements_scrollbar_pos( - canvas, - screen_width, - STATUS_BAR_Y_SHIFT + 2, - screen_height - STATUS_BAR_Y_SHIFT, - model->index, - SubBruteAttackTotalCount); } + + elements_scrollbar_pos( + canvas, + screen_width, + STATUS_BAR_Y_SHIFT + 2, + screen_height - STATUS_BAR_Y_SHIFT, + model->index, + SubBruteAttackTotalCount); +} + +void subbrute_main_view_draw(Canvas* canvas, SubBruteMainViewModel* model) { + if(model->is_select_byte) { + subbrute_main_view_draw_is_byte_selected(canvas, model); + } else { + subbrute_main_view_draw_is_ordinary_selected(canvas, model); + } +} + +bool subbrute_main_view_input_file_protocol(InputEvent* event, SubBruteMainView* instance) { + bool updated = false; + if(event->key == InputKeyLeft) { + if((instance->index > 0 && !instance->two_bytes) || + (instance->two_bytes && instance->index > 1)) { + instance->index--; + } + updated = true; + } else if(event->key == InputKeyRight) { + if(instance->index < 7) { + instance->index++; + } + updated = true; + } else if(event->key == InputKeyUp) { + instance->two_bytes = !instance->two_bytes; + // Because index is changing + if(instance->two_bytes && instance->index < 7) { + instance->index++; + } + // instance->callback( + // instance->two_bytes ? SubBruteCustomEventTypeChangeStepUp : + // SubBruteCustomEventTypeChangeStepDown, + // instance->context); + + updated = true; + } else if(event->key == InputKeyOk) { + instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context); + updated = true; + } + return updated; +} + +bool subbrute_main_view_input_ordinary_protocol( + InputEvent* event, + SubBruteMainView* instance, + bool is_short) { + const uint8_t min_value = 0; + const uint8_t correct_total = SubBruteAttackTotalCount - 1; + uint8_t index = instance->index; + uint8_t min_repeats = subbrute_protocol_repeats_count(index); + uint8_t max_repeats = min_repeats * 3; + uint8_t current_repeats = instance->repeat_values[index]; + + bool updated = false; + if(event->key == InputKeyUp && is_short) { + if(index == min_value) { + instance->index = correct_total; + } else { + instance->index = CLAMP(index - 1, correct_total, min_value); + } + //instance->repeat_values = 0; + updated = true; + } else if(event->key == InputKeyDown && is_short) { + if(index == correct_total) { + instance->index = min_value; + } else { + instance->index = CLAMP(index + 1, correct_total, min_value); + } + //instance->repeat_values = 0; + updated = true; + } else if(event->key == InputKeyLeft && is_short) { + instance->repeat_values[index] = CLAMP(current_repeats - 1, max_repeats, min_repeats); + + updated = true; + } else if(event->key == InputKeyRight && is_short) { + instance->repeat_values[index] = CLAMP(current_repeats + 1, max_repeats, min_repeats); + + updated = true; + } else if(event->key == InputKeyOk && is_short) { + if(index == SubBruteAttackLoadFile) { + instance->callback(SubBruteCustomEventTypeLoadFile, instance->context); + } else { + instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context); + } + updated = true; + } + + if(updated) { + instance->window_position = instance->index; + if(instance->window_position > 0) { + instance->window_position -= 1; + } + + if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) { + instance->window_position = 0; + } else { + if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) { + instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN); + } + } + } + + return updated; } bool subbrute_main_view_input(InputEvent* event, void* context) { furi_assert(event); furi_assert(context); + SubBruteMainView* instance = context; + if(event->key == InputKeyBack && event->type == InputTypeShort) { #ifdef FURI_DEBUG FURI_LOG_I(TAG, "InputKey: BACK"); #endif + instance->callback(SubBruteCustomEventTypeBackPressed, instance->context); return false; } - SubBruteMainView* instance = context; #ifdef FURI_DEBUG - FURI_LOG_D(TAG, "InputKey: %d, extra_repeats: %d", event->key, instance->extra_repeats); + FURI_LOG_D( + TAG, + "InputKey: %d, extra_repeats: %d", + event->key, + instance->repeat_values[instance->index]); #endif - const uint8_t min_value = 0; - const uint8_t correct_total = SubBruteAttackTotalCount - 1; - uint8_t max_repeats = 14 - subbrute_protocol_repeats_count(instance->index); bool updated = false; - bool consumed = false; bool is_short = (event->type == InputTypeShort) || (event->type == InputTypeRepeat); - if(!instance->is_select_byte) { - if(event->key == InputKeyUp && is_short) { - if(instance->index == min_value) { - instance->index = correct_total; - } else { - instance->index = CLAMP(instance->index - 1, correct_total, min_value); - } - instance->extra_repeats = 0; - updated = true; - consumed = true; - } else if(event->key == InputKeyDown && is_short) { - if(instance->index == correct_total) { - instance->index = min_value; - } else { - instance->index = CLAMP(instance->index + 1, correct_total, min_value); - } - instance->extra_repeats = 0; - updated = true; - consumed = true; - } else if(event->key == InputKeyLeft && is_short) { - instance->extra_repeats = CLAMP(instance->extra_repeats - 1, max_repeats, 0); - - updated = true; - consumed = true; - } else if(event->key == InputKeyRight && is_short) { - instance->extra_repeats = CLAMP(instance->extra_repeats + 1, max_repeats, 0); - - updated = true; - consumed = true; - } else if(event->key == InputKeyOk && is_short) { - if(instance->index == SubBruteAttackLoadFile) { - instance->callback(SubBruteCustomEventTypeLoadFile, instance->context); - } else { - instance->callback(SubBruteCustomEventTypeMenuSelected, instance->context); - } - consumed = true; - updated = true; - } - if(updated) { - instance->window_position = instance->index; - if(instance->window_position > 0) { - instance->window_position -= 1; - } - - if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) { - instance->window_position = 0; - } else { - if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) { - instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN); - } - } - } - } else if(is_short) { - if(event->key == InputKeyLeft) { - if((instance->index > 0 && !instance->two_bytes) || - (instance->two_bytes && instance->index > 1)) { - instance->index--; - } - updated = true; - consumed = true; - } else if(event->key == InputKeyRight) { - if(instance->index < 7) { - instance->index++; - } - updated = true; - consumed = true; - } else if(event->key == InputKeyUp) { - instance->two_bytes = !instance->two_bytes; - // Because index is changing - if(instance->two_bytes && instance->index < 7) { - instance->index++; - } - // instance->callback( - // instance->two_bytes ? SubBruteCustomEventTypeChangeStepUp : - // SubBruteCustomEventTypeChangeStepDown, - // instance->context); - - updated = true; - consumed = true; - } else if(event->key == InputKeyOk) { - instance->callback(SubBruteCustomEventTypeIndexSelected, instance->context); - consumed = true; - updated = true; + if(instance->is_select_byte) { + if(is_short) { + updated = subbrute_main_view_input_file_protocol(event, instance); } + } else { + updated = subbrute_main_view_input_ordinary_protocol(event, instance, is_short); } if(updated) { @@ -343,28 +366,20 @@ bool subbrute_main_view_input(InputEvent* event, void* context) { model->key_from_file = instance->key_from_file; model->is_select_byte = instance->is_select_byte; model->two_bytes = instance->two_bytes; - model->extra_repeats = instance->extra_repeats; + model->repeat_values[model->index] = instance->repeat_values[instance->index]; }, true); } - return consumed; + return updated; } void subbrute_main_view_enter(void* context) { furi_assert(context); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_main_view_enter"); -#endif } void subbrute_main_view_exit(void* context) { furi_assert(context); - -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "subbrute_main_view_exit"); -#endif } SubBruteMainView* subbrute_main_view_alloc() { @@ -382,7 +397,7 @@ SubBruteMainView* subbrute_main_view_alloc() { instance->key_from_file = 0; instance->is_select_byte = false; instance->two_bytes = false; - instance->extra_repeats = 0; + with_view_model( instance->view, SubBruteMainViewModel * model, @@ -392,7 +407,6 @@ SubBruteMainView* subbrute_main_view_alloc() { model->key_from_file = instance->key_from_file; model->is_select_byte = instance->is_select_byte; model->two_bytes = instance->two_bytes; - model->extra_repeats = instance->extra_repeats; }, true); @@ -414,6 +428,7 @@ View* subbrute_main_view_get_view(SubBruteMainView* instance) { void subbrute_main_view_set_index( SubBruteMainView* instance, uint8_t idx, + const uint8_t* repeats, bool is_select_byte, bool two_bytes, uint64_t key_from_file) { @@ -422,6 +437,9 @@ void subbrute_main_view_set_index( #ifdef FURI_DEBUG FURI_LOG_I(TAG, "Set index: %d, is_select_byte: %d", idx, is_select_byte); #endif + for(size_t i = 0; i < SubBruteAttackTotalCount; i++) { + instance->repeat_values[i] = repeats[i]; + } instance->is_select_byte = is_select_byte; instance->two_bytes = two_bytes; instance->key_from_file = key_from_file; @@ -433,12 +451,8 @@ void subbrute_main_view_set_index( instance->window_position -= 1; } - if(SubBruteAttackTotalCount <= ITEMS_ON_SCREEN) { - instance->window_position = 0; - } else { - if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) { - instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN); - } + if(instance->window_position >= (SubBruteAttackTotalCount - ITEMS_ON_SCREEN)) { + instance->window_position = (SubBruteAttackTotalCount - ITEMS_ON_SCREEN); } } @@ -451,7 +465,9 @@ void subbrute_main_view_set_index( model->key_from_file = instance->key_from_file; model->is_select_byte = instance->is_select_byte; model->two_bytes = instance->two_bytes; - model->extra_repeats = instance->extra_repeats; + for(size_t i = 0; i < SubBruteAttackTotalCount; i++) { + model->repeat_values[i] = repeats[i]; + } }, true); } @@ -461,12 +477,12 @@ SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance) { return instance->index; } -uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance) { +const uint8_t* subbrute_main_view_get_extra_repeats(SubBruteMainView* instance) { furi_assert(instance); - return instance->extra_repeats; + return instance->repeat_values; } bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance) { furi_assert(instance); return instance->two_bytes; -} \ No newline at end of file +} diff --git a/applications/external/subghz_bruteforcer/views/subbrute_main_view.h b/applications/external/subghz_bruteforcer/views/subbrute_main_view.h index 003cd9817..4aa8ddd7f 100644 --- a/applications/external/subghz_bruteforcer/views/subbrute_main_view.h +++ b/applications/external/subghz_bruteforcer/views/subbrute_main_view.h @@ -20,13 +20,14 @@ View* subbrute_main_view_get_view(SubBruteMainView* instance); void subbrute_main_view_set_index( SubBruteMainView* instance, uint8_t idx, + const uint8_t* repeats, bool is_select_byte, bool two_bytes, - uint64_t file_key); + uint64_t key_from_file); SubBruteAttacks subbrute_main_view_get_index(SubBruteMainView* instance); -uint8_t subbrute_main_view_get_extra_repeats(SubBruteMainView* instance); +const uint8_t* subbrute_main_view_get_extra_repeats(SubBruteMainView* instance); bool subbrute_main_view_get_two_bytes(SubBruteMainView* instance); void subbrute_attack_view_enter(void* context); void subbrute_attack_view_exit(void* context); bool subbrute_attack_view_input(InputEvent* event, void* context); -void subbrute_attack_view_draw(Canvas* canvas, void* context); \ No newline at end of file +void subbrute_attack_view_draw(Canvas* canvas, void* context); diff --git a/applications/external/subghz_playlist/application.fam b/applications/external/subghz_playlist/application.fam index 8fe9ccbaf..5050197f2 100644 --- a/applications/external/subghz_playlist/application.fam +++ b/applications/external/subghz_playlist/application.fam @@ -5,9 +5,9 @@ App( entry_point="playlist_app", requires=["storage", "gui", "dialogs", "subghz"], stack_size=2 * 1024, - order=14, fap_icon="subplaylist_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/subghz_playlist/playlist.c b/applications/external/subghz_playlist/playlist.c index 424bfe678..85b5109b8 100644 --- a/applications/external/subghz_playlist/playlist.c +++ b/applications/external/subghz_playlist/playlist.c @@ -6,6 +6,7 @@ #include #include +#include "subghz_playlist_icons.h" #include #include @@ -18,7 +19,6 @@ #include #include -#include #include "playlist_file.h" #include "canvas_helper.h" @@ -721,8 +721,6 @@ void playlist_free(Playlist* app) { } int32_t playlist_app(char* p) { - dolphin_deed(DolphinDeedPluginStart); - // create playlist folder { Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/applications/external/subghz_remote/application.fam b/applications/external/subghz_remote/application.fam index 7eee1369f..1a99a2025 100644 --- a/applications/external/subghz_remote/application.fam +++ b/applications/external/subghz_remote/application.fam @@ -4,7 +4,6 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="subghz_remote_app", stack_size=2 * 1024, - order=11, fap_icon="icon.png", fap_description="SubGhz Remote, uses up to 5 .sub files", fap_category="Sub-GHz", diff --git a/applications/external/subghz_remote/helpers/subrem_presets.c b/applications/external/subghz_remote/helpers/subrem_presets.c index 75ced8e00..ca91ccbaf 100644 --- a/applications/external/subghz_remote/helpers/subrem_presets.c +++ b/applications/external/subghz_remote/helpers/subrem_presets.c @@ -84,7 +84,7 @@ SubRemLoadSubState subrem_sub_preset_load( if(!flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { FURI_LOG_W(TAG, "Cannot read frequency. Set default frequency"); sub_preset->freq_preset.frequency = subghz_setting_get_default_frequency(setting); - } else if(!subghz_txrx_radio_device_is_frequecy_valid(txrx, temp_data32)) { + } else if(!subghz_txrx_radio_device_is_frequency_valid(txrx, temp_data32)) { FURI_LOG_E(TAG, "Frequency not supported on chosen radio module"); break; } diff --git a/applications/external/subghz_remote/helpers/txrx/subghz_txrx.c b/applications/external/subghz_remote/helpers/txrx/subghz_txrx.c index 8def9cf01..aa713c7a8 100644 --- a/applications/external/subghz_remote/helpers/txrx/subghz_txrx.c +++ b/applications/external/subghz_remote/helpers/txrx/subghz_txrx.c @@ -629,12 +629,12 @@ const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) { return subghz_devices_get_name(instance->radio_device); } -bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) { +bool subghz_txrx_radio_device_is_frequency_valid(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); return subghz_devices_is_frequency_valid(instance->radio_device, frequency); } -bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t frequency) { +bool subghz_txrx_radio_device_is_tx_allowed(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); diff --git a/applications/external/subghz_remote/helpers/txrx/subghz_txrx.h b/applications/external/subghz_remote/helpers/txrx/subghz_txrx.h index 8bb7f2aee..4593ea20c 100644 --- a/applications/external/subghz_remote/helpers/txrx/subghz_txrx.h +++ b/applications/external/subghz_remote/helpers/txrx/subghz_txrx.h @@ -363,9 +363,9 @@ const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance); * @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); +bool subghz_txrx_radio_device_is_frequency_valid(SubGhzTxRx* instance, uint32_t frequency); -bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t frequency); +bool subghz_txrx_radio_device_is_tx_allowed(SubGhzTxRx* instance, uint32_t frequency); void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state); bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance); diff --git a/applications/external/swd_probe/application.fam b/applications/external/swd_probe/application.fam index c14ae1890..998dcc7b6 100644 --- a/applications/external/swd_probe/application.fam +++ b/applications/external/swd_probe/application.fam @@ -1,11 +1,10 @@ App( appid="swd_probe", - name="[SWD] Probe", + name="[SWD] SWD Probe", apptype=FlipperAppType.EXTERNAL, entry_point="swd_probe_app_main", requires=["notification", "gui", "storage", "dialogs", "cli"], stack_size=2 * 1024, - order=10, fap_icon="icons/app.png", fap_category="GPIO", fap_icon_assets="icons", diff --git a/applications/external/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c index 04a0165b0..b43b9c1c8 100644 --- a/applications/external/swd_probe/swd_probe_app.c +++ b/applications/external/swd_probe/swd_probe_app.c @@ -3137,7 +3137,7 @@ int32_t swd_probe_app_main(void* p) { DBGS("swd_execute_script"); swd_execute_script(app, SWD_PATH "/startup.swd"); - // dolphin_deed(DolphinDeedPluginGameStart); + dolphin_deed(DolphinDeedPluginGameStart); DBGS("processing"); for(bool processing = true; processing;) { diff --git a/applications/external/swd_probe/swd_probe_app.h b/applications/external/swd_probe/swd_probe_app.h index b60617e98..5a45a4fd9 100644 --- a/applications/external/swd_probe/swd_probe_app.h +++ b/applications/external/swd_probe/swd_probe_app.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -240,4 +241,4 @@ typedef struct { uint8_t swd_read_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t* data); -#endif +#endif \ No newline at end of file diff --git a/applications/external/t_rex_runner/application.fam b/applications/external/t_rex_runner/application.fam index 843ea307d..739a5bd07 100644 --- a/applications/external/t_rex_runner/application.fam +++ b/applications/external/t_rex_runner/application.fam @@ -9,7 +9,6 @@ App( fap_category="Games", 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.2", diff --git a/applications/external/t_rex_runner/trexrunner.c b/applications/external/t_rex_runner/trexrunner.c index a1351a4ce..c676c407e 100644 --- a/applications/external/t_rex_runner/trexrunner.c +++ b/applications/external/t_rex_runner/trexrunner.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -22,7 +23,8 @@ #define CACTUS_W 10 #define CACTUS_H 10 -#define START_x_speed 25 +#define START_x_speed 35 +#define X_INCREASE 3 #define BACKGROUND_W 128 #define BACKGROUND_H 12 @@ -101,16 +103,23 @@ static void timer_callback(void* ctx) { // Update Cactus state if(game_state->has_cactus) { game_state->cactus_position = - game_state->cactus_position - game_state->x_speed * delta_time_ms / 1000; + game_state->cactus_position - (game_state->x_speed - 15) * delta_time_ms / 1000; if(game_state->cactus_position <= 0) { game_state->has_cactus = 0; game_state->score = game_state->score + 1; + + // Increase speed + game_state->x_speed = game_state->x_speed + X_INCREASE; } } - // Create cactus (not random) + // Create cactus (random frame in 1.5s) else { - game_state->has_cactus = 1; - game_state->cactus_position = 120; + uint8_t randomuint8[1]; + furi_hal_random_fill_buf(randomuint8, 1); + if(randomuint8[0] % 30 == 0) { + game_state->has_cactus = 1; + game_state->cactus_position = 120; + } } // Move horizontal line @@ -120,9 +129,9 @@ static void timer_callback(void* ctx) { 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))) + if(game_state->has_cactus && ((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); diff --git a/applications/external/tama_p1/application.fam b/applications/external/tama_p1/application.fam index e51281ff7..4152b054c 100644 --- a/applications/external/tama_p1/application.fam +++ b/applications/external/tama_p1/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_TAMA_P1"], requires=["gui", "storage"], stack_size=2 * 1024, - order=215, fap_icon="tamaIcon.png", fap_category="Games", ) diff --git a/applications/external/tanksgame/application.fam b/applications/external/tanksgame/application.fam index f4fe49723..bf6bd1b4a 100644 --- a/applications/external/tanksgame/application.fam +++ b/applications/external/tanksgame/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_TANKS_GAME"], requires=["gui", "subghz"], stack_size=4 * 1024, - order=730, fap_icon="tanksIcon.png", fap_category="Games", fap_icon_assets="images", diff --git a/applications/external/tetris_game/application.fam b/applications/external/tetris_game/application.fam index eb6ab5d5e..0766daf1e 100644 --- a/applications/external/tetris_game/application.fam +++ b/applications/external/tetris_game/application.fam @@ -5,7 +5,6 @@ App( entry_point="tetris_game_app", requires=["gui"], stack_size=2 * 1024, - order=240, fap_icon="tetris_10px.png", fap_category="Games", fap_author="@xMasterX & @jeffplang", diff --git a/applications/external/tetris_game/tetris_game.c b/applications/external/tetris_game/tetris_game.c index bd16cddfc..07534d725 100644 --- a/applications/external/tetris_game/tetris_game.c +++ b/applications/external/tetris_game/tetris_game.c @@ -6,6 +6,7 @@ #include #include #include +#include #define BORDER_OFFSET 1 #define MARGIN_OFFSET 3 @@ -388,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 @@ -476,4 +477,4 @@ int32_t tetris_game_app() { free(tetris_state); return 0; -} +} \ No newline at end of file diff --git a/applications/external/text2sam/application.fam b/applications/external/text2sam/application.fam index 5598df644..3a0765680 100644 --- a/applications/external/text2sam/application.fam +++ b/applications/external/text2sam/application.fam @@ -13,7 +13,6 @@ App( # stack_size=2 * 1024, 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.1", diff --git a/applications/external/text_viewer/application.fam b/applications/external/text_viewer/application.fam index 6222dc643..6bdfe7c89 100644 --- a/applications/external/text_viewer/application.fam +++ b/applications/external/text_viewer/application.fam @@ -2,17 +2,16 @@ App( appid="text_viewer", name="Text Viewer", apptype=FlipperAppType.EXTERNAL, - entry_point="text_viewer_app", + entry_point="text_viewer", requires=[ "gui", "dialogs", ], - stack_size=2 * 1024, - order=20, + stack_size=10 * 1024, fap_icon="icons/text_10px.png", fap_category="Tools", fap_icon_assets="icons", - fap_author="@kowalski7cc & @kyhwana", + fap_author="@Willy-JL", # Original by @kowalski7cc & @kyhwana, new has code borrowed from archive > show fap_version="1.0", fap_description="Text viewer application", ) diff --git a/applications/external/text_viewer/scenes/text_viewer_scene.c b/applications/external/text_viewer/scenes/text_viewer_scene.c new file mode 100644 index 000000000..a507da4af --- /dev/null +++ b/applications/external/text_viewer/scenes/text_viewer_scene.c @@ -0,0 +1,30 @@ +#include "text_viewer_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const text_viewer_on_enter_handlers[])(void*) = { +#include "text_viewer_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 text_viewer_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "text_viewer_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 text_viewer_on_exit_handlers[])(void* context) = { +#include "text_viewer_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers text_viewer_scene_handlers = { + .on_enter_handlers = text_viewer_on_enter_handlers, + .on_event_handlers = text_viewer_on_event_handlers, + .on_exit_handlers = text_viewer_on_exit_handlers, + .scene_num = TextViewerSceneNum, +}; diff --git a/applications/external/text_viewer/scenes/text_viewer_scene.h b/applications/external/text_viewer/scenes/text_viewer_scene.h new file mode 100644 index 000000000..a8770db3b --- /dev/null +++ b/applications/external/text_viewer/scenes/text_viewer_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) TextViewerScene##id, +typedef enum { +#include "text_viewer_scene_config.h" + TextViewerSceneNum, +} TextViewerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers text_viewer_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "text_viewer_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 "text_viewer_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 "text_viewer_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/text_viewer/scenes/text_viewer_scene_config.h b/applications/external/text_viewer/scenes/text_viewer_scene_config.h new file mode 100644 index 000000000..86c69441e --- /dev/null +++ b/applications/external/text_viewer/scenes/text_viewer_scene_config.h @@ -0,0 +1 @@ +ADD_SCENE(text_viewer, show, Show) diff --git a/applications/main/archive/scenes/archive_scene_show.c b/applications/external/text_viewer/scenes/text_viewer_scene_show.c similarity index 62% rename from applications/main/archive/scenes/archive_scene_show.c rename to applications/external/text_viewer/scenes/text_viewer_scene_show.c index 09e3efa2f..3d5aa077a 100644 --- a/applications/main/archive/scenes/archive_scene_show.c +++ b/applications/external/text_viewer/scenes/text_viewer_scene_show.c @@ -1,14 +1,10 @@ -#include "../archive_i.h" -#include "../helpers/archive_browser.h" -#include +#include "../text_viewer.h" -#define TAG "Archive" +#define SHOW_MAX_FILE_SIZE 8000 -#define SHOW_MAX_FILE_SIZE 5000 - -void archive_scene_show_widget_callback(GuiButtonType result, InputType type, void* context) { +void text_viewer_scene_show_widget_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); - ArchiveApp* app = (ArchiveApp*)context; + TextViewer* app = (TextViewer*)context; if(type == InputTypeShort) { view_dispatcher_send_custom_event(app->view_dispatcher, result); } @@ -30,26 +26,22 @@ static bool text_show_read_lines(File* file, FuriString* str_result) { return true; } -void archive_scene_show_on_enter(void* context) { +void text_viewer_scene_show_on_enter(void* context) { furi_assert(context); - ArchiveApp* instance = context; - - FuriString* filename; - filename = furi_string_alloc(); + TextViewer* app = context; FuriString* buffer; buffer = furi_string_alloc(); - ArchiveFile_t* current = archive_get_current_file(instance->browser); - Storage* fs_api = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(fs_api); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); FileInfo fileinfo; - FS_Error error = storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo); + FS_Error error = storage_common_stat(storage, furi_string_get_cstr(app->path), &fileinfo); if(error == FSE_OK) { if((fileinfo.size < SHOW_MAX_FILE_SIZE) && (fileinfo.size > 2)) { bool ok = storage_file_open( - file, furi_string_get_cstr(current->path), FSAM_READ, FSOM_OPEN_EXISTING); + file, furi_string_get_cstr(app->path), FSAM_READ, FSOM_OPEN_EXISTING); if(ok) { if(!text_show_read_lines(file, buffer)) { goto text_file_read_err; @@ -61,12 +53,12 @@ void archive_scene_show_on_enter(void* context) { storage_file_seek(file, 0, true); widget_add_text_scroll_element( - instance->widget, 0, 0, 128, 64, furi_string_get_cstr(buffer)); + app->widget, 0, 0, 128, 64, furi_string_get_cstr(buffer)); } else { text_file_read_err: widget_add_text_box_element( - instance->widget, + app->widget, 0, 0, 128, @@ -79,7 +71,7 @@ void archive_scene_show_on_enter(void* context) { storage_file_close(file); } else if(fileinfo.size < 2) { widget_add_text_box_element( - instance->widget, + app->widget, 0, 0, 128, @@ -90,7 +82,7 @@ void archive_scene_show_on_enter(void* context) { false); } else { widget_add_text_box_element( - instance->widget, + app->widget, 0, 0, 128, @@ -102,7 +94,7 @@ void archive_scene_show_on_enter(void* context) { } } else { widget_add_text_box_element( - instance->widget, + app->widget, 0, 0, 128, @@ -112,25 +104,18 @@ void archive_scene_show_on_enter(void* context) { "\e#Error:\nFile system error\e#", false); } - path_extract_filename(current->path, filename, false); - - // This one to return and cursor select this file - path_extract_filename_no_ext(furi_string_get_cstr(current->path), filename); - strlcpy(instance->text_store, furi_string_get_cstr(filename), MAX_NAME_LEN); furi_string_free(buffer); storage_file_free(file); furi_record_close(RECORD_STORAGE); - furi_string_free(filename); - - view_dispatcher_switch_to_view(instance->view_dispatcher, ArchiveViewWidget); + view_dispatcher_switch_to_view(app->view_dispatcher, TextViewerViewWidget); } -bool archive_scene_show_on_event(void* context, SceneManagerEvent event) { +bool text_viewer_scene_show_on_event(void* context, SceneManagerEvent event) { furi_assert(context); - ArchiveApp* app = (ArchiveApp*)context; + TextViewer* app = (TextViewer*)context; if(event.type == SceneManagerEventTypeCustom) { scene_manager_previous_scene(app->scene_manager); @@ -139,9 +124,9 @@ bool archive_scene_show_on_event(void* context, SceneManagerEvent event) { return false; } -void archive_scene_show_on_exit(void* context) { +void text_viewer_scene_show_on_exit(void* context) { furi_assert(context); - ArchiveApp* app = (ArchiveApp*)context; + TextViewer* app = (TextViewer*)context; widget_reset(app->widget); } diff --git a/applications/external/text_viewer/text_viewer.c b/applications/external/text_viewer/text_viewer.c index 6313175c2..782b44c6f 100644 --- a/applications/external/text_viewer/text_viewer.c +++ b/applications/external/text_viewer/text_viewer.c @@ -1,287 +1,84 @@ -#include -#include +#include "text_viewer.h" -#include -#include -#include -#include - -#include -#include -#include -#include - -#define TAG "TextViewer" - -#define TEXT_VIEWER_APP_PATH_FOLDER STORAGE_EXT_PATH_PREFIX -#define TEXT_VIEWER_APP_EXTENSION "*" - -#define TEXT_VIEWER_BYTES_PER_LINE 20u -#define TEXT_VIEWER_LINES_ON_SCREEN 5u -#define TEXT_VIEWER_BUF_SIZE (TEXT_VIEWER_LINES_ON_SCREEN * TEXT_VIEWER_BYTES_PER_LINE) - -typedef struct { - uint8_t file_bytes[TEXT_VIEWER_LINES_ON_SCREEN][TEXT_VIEWER_BYTES_PER_LINE]; - uint32_t file_offset; - uint32_t file_read_bytes; - uint32_t file_size; - Stream* stream; - bool mode; // Print address or content -} TextViewerModel; - -typedef struct { - TextViewerModel* model; - FuriMutex** mutex; - - FuriMessageQueue* input_queue; - - ViewPort* view_port; - Gui* gui; - Storage* storage; -} TextViewer; - -static void render_callback(Canvas* canvas, void* ctx) { - TextViewer* text_viewer = ctx; - furi_check(furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk); - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - //elements_button_left(canvas, text_viewer->model->mode ? "Addr" : "Text"); - text_viewer->model->mode = 1; //text mode - //elements_button_right(canvas, "Info"); - - int ROW_HEIGHT = 12; - int TOP_OFFSET = 10; - int LEFT_OFFSET = 3; - - uint32_t line_count = text_viewer->model->file_size / TEXT_VIEWER_BYTES_PER_LINE; - if(text_viewer->model->file_size % TEXT_VIEWER_BYTES_PER_LINE != 0) line_count += 1; - uint32_t first_line_on_screen = text_viewer->model->file_offset / TEXT_VIEWER_BYTES_PER_LINE; - if(line_count > TEXT_VIEWER_LINES_ON_SCREEN) { - uint8_t width = canvas_width(canvas); - elements_scrollbar_pos( - canvas, - width, - 0, - ROW_HEIGHT * TEXT_VIEWER_LINES_ON_SCREEN, - first_line_on_screen, // TODO - line_count - (TEXT_VIEWER_LINES_ON_SCREEN - 1)); - } - - char temp_buf[32]; - uint32_t row_iters = text_viewer->model->file_read_bytes / TEXT_VIEWER_BYTES_PER_LINE; - if(text_viewer->model->file_read_bytes % TEXT_VIEWER_BYTES_PER_LINE != 0) row_iters += 1; - - for(uint32_t i = 0; i < row_iters; ++i) { - uint32_t bytes_left_per_row = - text_viewer->model->file_read_bytes - i * TEXT_VIEWER_BYTES_PER_LINE; - bytes_left_per_row = MIN(bytes_left_per_row, TEXT_VIEWER_BYTES_PER_LINE); - - if(text_viewer->model->mode) { - memcpy(temp_buf, text_viewer->model->file_bytes[i], bytes_left_per_row); - temp_buf[bytes_left_per_row] = '\0'; - for(uint32_t j = 0; j < bytes_left_per_row; ++j) - if(!isprint((int)temp_buf[j])) temp_buf[j] = ' '; - - canvas_set_font(canvas, FontKeyboard); - canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); - } else { - uint32_t addr = text_viewer->model->file_offset + i * TEXT_VIEWER_BYTES_PER_LINE; - snprintf(temp_buf, 32, "%04lX", addr); - - canvas_set_font(canvas, FontKeyboard); - canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); - } - } - - furi_mutex_release(text_viewer->mutex); +static bool text_viewer_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + TextViewer* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); } -static void input_callback(InputEvent* input_event, void* ctx) { - TextViewer* text_viewer = ctx; - if(input_event->type == InputTypeShort || input_event->type == InputTypeRepeat) { - furi_message_queue_put(text_viewer->input_queue, input_event, 0); - } +static bool text_viewer_back_event_callback(void* context) { + furi_assert(context); + TextViewer* app = context; + return scene_manager_handle_back_event(app->scene_manager); } -static TextViewer* text_viewer_alloc() { - TextViewer* instance = malloc(sizeof(TextViewer)); +TextViewer* text_viewer_alloc() { + TextViewer* app = malloc(sizeof(TextViewer)); + app->gui = furi_record_open(RECORD_GUI); - instance->model = malloc(sizeof(TextViewerModel)); - memset(instance->model, 0x0, sizeof(TextViewerModel)); + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&text_viewer_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, text_viewer_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, text_viewer_back_event_callback); - instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - instance->view_port = view_port_alloc(); - view_port_draw_callback_set(instance->view_port, render_callback, instance); - view_port_input_callback_set(instance->view_port, input_callback, instance); + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, TextViewerViewWidget, widget_get_view(app->widget)); - instance->gui = furi_record_open(RECORD_GUI); - gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + app->path = furi_string_alloc(); - instance->storage = furi_record_open(RECORD_STORAGE); - - return instance; + return app; } -static void text_viewer_free(TextViewer* instance) { - furi_record_close(RECORD_STORAGE); +void text_viewer_free(TextViewer* app) { + furi_assert(app); + + view_dispatcher_remove_view(app->view_dispatcher, TextViewerViewWidget); + widget_free(app->widget); + + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + furi_string_free(app->path); - 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); - - free(instance->model); - free(instance); + free(app); } -static bool text_viewer_open_file(TextViewer* text_viewer, const char* file_path) { - furi_assert(text_viewer); - furi_assert(file_path); - - text_viewer->model->stream = buffered_file_stream_alloc(text_viewer->storage); - bool isOk = true; - - do { - if(!buffered_file_stream_open( - text_viewer->model->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { - FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); - isOk = false; - break; - } - - text_viewer->model->file_size = stream_size(text_viewer->model->stream); - } while(false); - - return isOk; -} - -static bool text_viewer_read_file(TextViewer* text_viewer) { - furi_assert(text_viewer); - furi_assert(text_viewer->model->stream); - furi_assert(text_viewer->model->file_offset % TEXT_VIEWER_BYTES_PER_LINE == 0); - - memset(text_viewer->model->file_bytes, 0x0, TEXT_VIEWER_BUF_SIZE); - bool isOk = true; - - do { - uint32_t offset = text_viewer->model->file_offset; - if(!stream_seek(text_viewer->model->stream, offset, true)) { - FURI_LOG_E(TAG, "Unable to seek stream"); - isOk = false; - break; - } - - text_viewer->model->file_read_bytes = stream_read( - text_viewer->model->stream, - (uint8_t*)text_viewer->model->file_bytes, - TEXT_VIEWER_BUF_SIZE); - } while(false); - - return isOk; -} - -int32_t text_viewer_app(void* p) { - TextViewer* text_viewer = text_viewer_alloc(); - - FuriString* file_path; - file_path = furi_string_alloc(); +extern int32_t text_viewer(void* p) { + TextViewer* app = text_viewer_alloc(); do { if(p && strlen(p)) { - furi_string_set(file_path, (const char*)p); + furi_string_set(app->path, (const char*)p); } else { - furi_string_set(file_path, TEXT_VIEWER_APP_PATH_FOLDER); + furi_string_set(app->path, TEXT_VIEWER_PATH); DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( - &browser_options, TEXT_VIEWER_APP_EXTENSION, &I_text_10px); + &browser_options, TEXT_VIEWER_EXTENSION, &I_text_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); + bool res = dialog_file_browser_show(dialogs, app->path, app->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(!text_viewer_open_file(text_viewer, furi_string_get_cstr(file_path))) break; - text_viewer_read_file(text_viewer); - - InputEvent input; - while(1) { - if(furi_message_queue_get(text_viewer->input_queue, &input, 100) == FuriStatusOk) { - if(input.key == InputKeyBack) { - break; - } else if(input.key == InputKeyUp) { - furi_check( - furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk); - if(text_viewer->model->file_offset > 0) { - text_viewer->model->file_offset -= TEXT_VIEWER_BYTES_PER_LINE; - if(!text_viewer_read_file(text_viewer)) break; - } - furi_mutex_release(text_viewer->mutex); - } else if(input.key == InputKeyDown) { - furi_check( - furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk); - uint32_t last_byte_on_screen = - text_viewer->model->file_offset + text_viewer->model->file_read_bytes; - - if(text_viewer->model->file_size > last_byte_on_screen) { - text_viewer->model->file_offset += TEXT_VIEWER_BYTES_PER_LINE; - if(!text_viewer_read_file(text_viewer)) break; - } - furi_mutex_release(text_viewer->mutex); - } else if(input.key == InputKeyLeft) { - furi_check( - furi_mutex_acquire(text_viewer->mutex, FuriWaitForever) == FuriStatusOk); - text_viewer->model->mode = !text_viewer->model->mode; - furi_mutex_release(text_viewer->mutex); - } else if(input.key == InputKeyRight) { - FuriString* buffer; - buffer = furi_string_alloc(); - furi_string_printf( - buffer, - "File path: %s\nFile size: %lu (0x%lX)", - furi_string_get_cstr(file_path), - text_viewer->model->file_size, - text_viewer->model->file_size); - - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header( - message, "Text Viewer v1.1", 16, 2, AlignLeft, AlignTop); - dialog_message_set_icon(message, &I_text_10px, 3, 2); - dialog_message_set_text( - message, furi_string_get_cstr(buffer), 3, 16, AlignLeft, AlignTop); - dialog_message_set_buttons(message, NULL, NULL, "Back"); - dialog_message_show(dialogs, message); - - furi_string_free(buffer); - dialog_message_free(message); - furi_record_close(RECORD_DIALOGS); - } - } - view_port_update(text_viewer->view_port); - } + scene_manager_next_scene(app->scene_manager, TextViewerSceneShow); + view_dispatcher_run(app->view_dispatcher); } while(false); - furi_string_free(file_path); - text_viewer_free(text_viewer); - + text_viewer_free(app); return 0; } diff --git a/applications/external/text_viewer/text_viewer.h b/applications/external/text_viewer/text_viewer.h new file mode 100644 index 000000000..f0427ca28 --- /dev/null +++ b/applications/external/text_viewer/text_viewer.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "text_viewer_icons.h" +#include "scenes/text_viewer_scene.h" + +#define TEXT_VIEWER_PATH STORAGE_EXT_PATH_PREFIX +#define TEXT_VIEWER_EXTENSION "*" + +typedef struct { + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Widget* widget; + + FuriString* path; +} TextViewer; + +typedef enum { + TextViewerViewWidget, +} TextViewerView; diff --git a/applications/external/tictactoe_game/application.fam b/applications/external/tictactoe_game/application.fam index 3def1fa85..48d904d0e 100644 --- a/applications/external/tictactoe_game/application.fam +++ b/applications/external/tictactoe_game/application.fam @@ -5,7 +5,6 @@ App( entry_point="tictactoe_game_app", requires=["gui"], stack_size=1 * 1024, - order=250, fap_icon="tictactoe_10px.png", fap_category="Games", fap_author="@xMasterX & @gotnull", diff --git a/applications/external/tictactoe_game/tictactoe_game.c b/applications/external/tictactoe_game/tictactoe_game.c index 5113fcf84..6ad076a4a 100644 --- a/applications/external/tictactoe_game/tictactoe_game.c +++ b/applications/external/tictactoe_game/tictactoe_game.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "TicTacToe" @@ -332,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;) { @@ -383,4 +384,4 @@ int32_t tictactoe_game_app(void* p) { free(tictactoe_state); return 0; -} +} \ No newline at end of file diff --git a/applications/external/timelapse/application.fam b/applications/external/timelapse/application.fam index 6dc6a3bc4..e59dc9722 100644 --- a/applications/external/timelapse/application.fam +++ b/applications/external/timelapse/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_ZEITRAFFER"], requires=["gui", "input", "notification", "gpio"], stack_size=2 * 1024, - order=90, fap_icon_assets="icons", fap_icon="zeitraffer.png", fap_category="GPIO", diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index 8debade2c..8e4eaa519 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -6,8 +6,7 @@ App( cdefines=["APP_TOTP"], requires=["gui", "cli", "dialogs", "storage", "input", "notification", "bt"], stack_size=2 * 1024, - order=20, - fap_version="2.3", + fap_version="4.03", fap_author="Alexander Kopachov (@akopachov)", fap_description="Software-based TOTP authenticator for Flipper Zero device", fap_weburl="https://github.com/akopachov/flipper-zero_authenticator", @@ -33,5 +32,19 @@ App( Lib( name="fonts", ), + Lib( + name="wolfssl", + sources=[ + "wolfcrypt/src/pwdbased.c", + "wolfcrypt/src/hmac.c", + "wolfcrypt/src/hash.c", + "wolfcrypt/src/sha.c", + "wolfcrypt/src/sha256.c", + "wolfcrypt/src/sha512.c", + ], + cflags=["-Wno-error"], + cdefines=["HAVE_CONFIG_H"], + cincludes=["config/wolfssl"], + ), ], ) diff --git a/applications/external/totp/cli/cli.c b/applications/external/totp/cli/cli.c index c860b5a36..a556c31ac 100644 --- a/applications/external/totp/cli/cli.c +++ b/applications/external/totp/cli/cli.c @@ -16,6 +16,10 @@ #include "commands/automation/automation.h" #include "commands/details/details.h" +struct TotpCliContext { + PluginState* plugin_state; +}; + static void totp_cli_print_unknown_command(const FuriString* unknown_command) { TOTP_CLI_PRINTF_ERROR( "Command \"%s\" is unknown. Use \"" TOTP_CLI_COMMAND_HELP @@ -43,7 +47,7 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { } else if( furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_LIST) == 0 || furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_LIST_ALT) == 0) { - totp_cli_command_list_handle(plugin_state, cli); + totp_cli_command_list_handle(plugin_state, args, cli); } else if( furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_DELETE) == 0 || furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_DELETE_ALT) == 0) { @@ -63,7 +67,7 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_AUTOMATION) == 0) { totp_cli_command_automation_handle(plugin_state, args, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_RESET) == 0) { - totp_cli_command_reset_handle(plugin_state, cli, cli_context->event_queue); + totp_cli_command_reset_handle(plugin_state, cli); } else if(furi_string_cmp_str(cmd, TOTP_CLI_COMMAND_UPDATE) == 0) { totp_cli_command_update_handle(plugin_state, args, cli); } else if( @@ -77,13 +81,11 @@ static void totp_cli_handler(Cli* cli, FuriString* args, void* context) { furi_string_free(cmd); } -TotpCliContext* - totp_cli_register_command_handler(PluginState* plugin_state, FuriMessageQueue* event_queue) { +TotpCliContext* totp_cli_register_command_handler(PluginState* plugin_state) { Cli* cli = furi_record_open(RECORD_CLI); TotpCliContext* context = malloc(sizeof(TotpCliContext)); furi_check(context != NULL); context->plugin_state = plugin_state; - context->event_queue = event_queue; cli_add_command( cli, TOTP_CLI_COMMAND_NAME, CliCommandFlagParallelSafe, totp_cli_handler, context); furi_record_close(RECORD_CLI); diff --git a/applications/external/totp/cli/cli.h b/applications/external/totp/cli/cli.h index 2e4b92db8..13afef78f 100644 --- a/applications/external/totp/cli/cli.h +++ b/applications/external/totp/cli/cli.h @@ -3,11 +3,17 @@ #include #include "../types/plugin_state.h" -typedef struct { - PluginState* plugin_state; - FuriMessageQueue* event_queue; -} TotpCliContext; +typedef struct TotpCliContext TotpCliContext; -TotpCliContext* - totp_cli_register_command_handler(PluginState* plugin_state, FuriMessageQueue* event_queue); +/** + * @brief Registers TOTP CLI handler + * @param plugin_state application state + * @return TOTP CLI context + */ +TotpCliContext* totp_cli_register_command_handler(PluginState* plugin_state); + +/** + * @brief Unregisters TOTP CLI handler + * @param context application state + */ void totp_cli_unregister_command_handler(TotpCliContext* context); \ No newline at end of file diff --git a/applications/external/totp/cli/cli_helpers.h b/applications/external/totp/cli/cli_helpers.h index b8f4f236a..9c40bff13 100644 --- a/applications/external/totp/cli/cli_helpers.h +++ b/applications/external/totp/cli/cli_helpers.h @@ -33,12 +33,14 @@ extern const char* TOTP_CLI_COLOR_INFO; #define TOTP_CLI_PRINTF_INFO(format, ...) \ TOTP_CLI_PRINTF_COLORFUL(TOTP_CLI_COLOR_INFO, format, ##__VA_ARGS__) -#define TOTP_CLI_LOCK_UI(plugin_state) \ - Scene __previous_scene = plugin_state->current_scene; \ - totp_scene_director_activate_scene(plugin_state, TotpSceneStandby) +#define TOTP_CLI_LOCK_UI(plugin_state) \ + Scene __previous_scene = plugin_state->current_scene; \ + totp_scene_director_activate_scene(plugin_state, TotpSceneStandby); \ + totp_scene_director_force_redraw(plugin_state) -#define TOTP_CLI_UNLOCK_UI(plugin_state) \ - totp_scene_director_activate_scene(plugin_state, __previous_scene) +#define TOTP_CLI_UNLOCK_UI(plugin_state) \ + totp_scene_director_activate_scene(plugin_state, __previous_scene); \ + totp_scene_director_force_redraw(plugin_state) /** * @brief Checks whether user is authenticated and entered correct PIN. @@ -109,6 +111,6 @@ void totp_cli_print_error_updating_config_file(); void totp_cli_print_error_loading_token_info(); /** - * @brief Prints message to let user knwo that command is processing now + * @brief Prints message to let user know that command is processing now */ void totp_cli_print_processing(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c index fa64bd41c..5e5435eec 100644 --- a/applications/external/totp/cli/commands/add/add.c +++ b/applications/external/totp/cli/commands/add/add.c @@ -11,7 +11,7 @@ struct TotpAddContext { FuriString* args; Cli* cli; - uint8_t* iv; + const CryptoSettings* crypto_settings; }; enum TotpIteratorUpdateTokenResultsEx { @@ -54,7 +54,7 @@ static TotpIteratorUpdateTokenResult // Reading token secret furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]:\r\n"); if(!totp_cli_read_line(context_t->cli, temp_str, mask_user_input)) { totp_cli_delete_last_line(); furi_string_secure_free(temp_str); @@ -68,7 +68,7 @@ static TotpIteratorUpdateTokenResult furi_string_get_cstr(temp_str), furi_string_size(temp_str), token_secret_encoding, - context_t->iv); + context_t->crypto_settings); furi_string_secure_free(temp_str); @@ -79,6 +79,7 @@ static TotpIteratorUpdateTokenResult return TotpIteratorUpdateTokenResultSuccess; } +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_add_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT ", " TOTP_CLI_COMMAND_ADD_ALT2 " Add new token\r\n"); @@ -155,6 +156,7 @@ void totp_cli_command_add_docopt_options() { TOTP_CLI_PRINTF(" # " TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME " - Type slower\r\n"); } +#endif void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { @@ -166,7 +168,8 @@ void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cl TOTP_CLI_LOCK_UI(plugin_state); - struct TotpAddContext add_context = {.args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + struct TotpAddContext add_context = { + .args = args, .cli = cli, .crypto_settings = &plugin_state->crypto_settings}; TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token(iterator_context, &add_token_handler, &add_context); diff --git a/applications/external/totp/cli/commands/add/add.h b/applications/external/totp/cli/commands/add/add.h index 001dc9e80..ac2006c12 100644 --- a/applications/external/totp/cli/commands/add/add.h +++ b/applications/external/totp/cli/commands/add/add.h @@ -1,6 +1,7 @@ #pragma once #include +#include "../../../config/app/config.h" #include "../../../types/plugin_state.h" #define TOTP_CLI_COMMAND_ADD "add" @@ -8,7 +9,9 @@ #define TOTP_CLI_COMMAND_ADD_ALT2 "new" void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_add_docopt_commands(); void totp_cli_command_add_docopt_usage(); void totp_cli_command_add_docopt_arguments(); -void totp_cli_command_add_docopt_options(); \ No newline at end of file +void totp_cli_command_add_docopt_options(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/commands/automation/automation.c b/applications/external/totp/cli/commands/automation/automation.c index c9f6ac34b..113393130 100644 --- a/applications/external/totp/cli/commands/automation/automation.c +++ b/applications/external/totp/cli/commands/automation/automation.c @@ -7,17 +7,24 @@ #define TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD "automation" #define TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE "none" #define TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "usb" -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED #define TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT "bt" #endif +#define TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY "QWERTY" +#define TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY "AZERTY" +#define TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX "-k" +#define TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT "layout" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_automation_docopt_commands() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation method\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation settings\r\n"); } void totp_cli_command_automation_docopt_usage() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL( - DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n"); + TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_AUTOMATION " " DOCOPT_OPTIONAL(DOCOPT_OPTION( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT))) " " DOCOPT_OPTIONAL(DOCOPT_MULTIPLE(DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD))) "\r\n"); } void totp_cli_command_automation_docopt_arguments() { @@ -25,24 +32,34 @@ void totp_cli_command_automation_docopt_arguments() { " " TOTP_CLI_COMMAND_AUTOMATION_ARG_METHOD " Automation method to be set. Must be one of: " TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED ", " TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT #endif "\r\n"); } -static void totp_cli_command_automation_print_method(AutomationMethod method, const char* color) { -#ifdef TOTP_BADBT_TYPE_ENABLED +void totp_cli_command_automation_docopt_options() { + TOTP_CLI_PRINTF(" " DOCOPT_OPTION( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT)) " Automation keyboard layout. Must be one of: " TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY + ", " TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY + "\r\n"); +} +#endif + +static void print_method(AutomationMethod method, const char* color) { +#ifdef TOTP_BADBT_AUTOMATION_ENABLED bool has_previous_method = false; #endif if(method & AutomationMethodBadUsb) { TOTP_CLI_PRINTF_COLORFUL(color, "\"" TOTP_CLI_COMMAND_AUTOMATION_METHOD_USB "\""); -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED has_previous_method = true; #endif } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(method & AutomationMethodBadBt) { if(has_previous_method) { TOTP_CLI_PRINTF_COLORFUL(color, " and "); @@ -57,6 +74,37 @@ static void totp_cli_command_automation_print_method(AutomationMethod method, co } } +static void print_kb_layout(AutomationKeyboardLayout layout, const char* color) { + char* layoutToPrint; + switch(layout) { + case AutomationKeyboardLayoutQWERTY: + layoutToPrint = TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY; + break; + case AutomationKeyboardLayoutAZERTY: + layoutToPrint = TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY; + break; + default: + furi_crash("Unknown automation keyboard layout"); + break; + } + + TOTP_CLI_PRINTF_COLORFUL(color, "%s", layoutToPrint); +} + +static bool + parse_automation_keyboard_layout(const FuriString* str, AutomationKeyboardLayout* out) { + bool result = true; + if(furi_string_cmpi_str(str, TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_QWERTY) == 0) { + *out = AutomationKeyboardLayoutQWERTY; + } else if(furi_string_cmpi_str(str, TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY) == 0) { + *out = AutomationKeyboardLayoutAZERTY; + } else { + result = false; + } + + return result; +} + void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; @@ -65,6 +113,7 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a FuriString* temp_str = furi_string_alloc(); bool new_method_provided = false; AutomationMethod new_method = AutomationMethodNone; + AutomationKeyboardLayout new_kb_layout = plugin_state->automation_kb_layout; bool args_valid = true; while(args_read_string_and_trim(args, temp_str)) { if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_NONE) == 0) { @@ -74,13 +123,19 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a new_method_provided = true; new_method |= AutomationMethodBadUsb; } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_METHOD_BT) == 0) { new_method_provided = true; new_method |= AutomationMethodBadBt; } #endif - else { + else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX) == 0) { + if(!args_read_string_and_trim(args, temp_str) || + !parse_automation_keyboard_layout(temp_str, &new_kb_layout)) { + args_valid = false; + break; + } + } else { args_valid = false; break; } @@ -96,15 +151,19 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a TOTP_CLI_LOCK_UI(plugin_state); plugin_state->automation_method = new_method; + plugin_state->automation_kb_layout = new_kb_layout; if(totp_config_file_update_automation_method(plugin_state)) { TOTP_CLI_PRINTF_SUCCESS("Automation method is set to "); - totp_cli_command_automation_print_method(new_method, TOTP_CLI_COLOR_SUCCESS); + print_method(new_method, TOTP_CLI_COLOR_SUCCESS); + TOTP_CLI_PRINTF_SUCCESS(" ("); + print_kb_layout(plugin_state->automation_kb_layout, TOTP_CLI_COLOR_SUCCESS); + TOTP_CLI_PRINTF_SUCCESS(")"); cli_nl(); } else { totp_cli_print_error_updating_config_file(); } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(!(new_method & AutomationMethodBadBt) && plugin_state->bt_type_code_worker_context != NULL) { totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); @@ -115,8 +174,10 @@ void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* a TOTP_CLI_UNLOCK_UI(plugin_state); } else { TOTP_CLI_PRINTF_INFO("Current automation method is "); - totp_cli_command_automation_print_method( - plugin_state->automation_method, TOTP_CLI_COLOR_INFO); + print_method(plugin_state->automation_method, TOTP_CLI_COLOR_INFO); + TOTP_CLI_PRINTF_INFO(" ("); + print_kb_layout(plugin_state->automation_kb_layout, TOTP_CLI_COLOR_INFO); + TOTP_CLI_PRINTF_INFO(")"); cli_nl(); } } while(false); diff --git a/applications/external/totp/cli/commands/automation/automation.h b/applications/external/totp/cli/commands/automation/automation.h index fb62e638e..522bfc560 100644 --- a/applications/external/totp/cli/commands/automation/automation.h +++ b/applications/external/totp/cli/commands/automation/automation.h @@ -2,10 +2,14 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_AUTOMATION "automation" void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_automation_docopt_commands(); void totp_cli_command_automation_docopt_usage(); -void totp_cli_command_automation_docopt_arguments(); \ No newline at end of file +void totp_cli_command_automation_docopt_arguments(); +void totp_cli_command_automation_docopt_options(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c index 9a084b23f..d9cea4803 100644 --- a/applications/external/totp/cli/commands/delete/delete.c +++ b/applications/external/totp/cli/commands/delete/delete.c @@ -10,6 +10,7 @@ #define TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX "-f" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_delete_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE ", " TOTP_CLI_COMMAND_DELETE_ALT " Delete existing token\r\n"); @@ -30,6 +31,7 @@ void totp_cli_command_delete_docopt_options() { TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX) " Force command to do not ask user for interactive confirmation\r\n"); } +#endif void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/delete/delete.h b/applications/external/totp/cli/commands/delete/delete.h index deb7f74fe..98b15e595 100644 --- a/applications/external/totp/cli/commands/delete/delete.h +++ b/applications/external/totp/cli/commands/delete/delete.h @@ -2,12 +2,15 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_DELETE "delete" #define TOTP_CLI_COMMAND_DELETE_ALT "rm" void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_delete_docopt_commands(); void totp_cli_command_delete_docopt_usage(); void totp_cli_command_delete_docopt_arguments(); -void totp_cli_command_delete_docopt_options(); \ No newline at end of file +void totp_cli_command_delete_docopt_options(); +#endif diff --git a/applications/external/totp/cli/commands/details/details.c b/applications/external/totp/cli/commands/details/details.c index 1677121b1..cc4c9eeff 100644 --- a/applications/external/totp/cli/commands/details/details.c +++ b/applications/external/totp/cli/commands/details/details.c @@ -7,36 +7,72 @@ #include "../../../ui/scene_director.h" #include "../../cli_helpers.h" #include "../../common_command_arguments.h" +#include "formatters/table/details_output_formatter_table.h" +#include "formatters/tsv/details_output_formatter_tsv.h" -#define TOTP_CLI_PRINTF_AUTOMATION_FEATURE(description, header_printed) \ - do { \ - TOTP_CLI_PRINTF( \ - "| %-20s | %-28.28s |\r\n", \ - header_printed ? "" : "Automation features", \ - description); \ - header_printed = true; \ - } while(false) +typedef void (*TOTP_CLI_DETAILS_HEADER_FORMATTER)(); +typedef void (*TOTP_CLI_DETAILS_FOOTER_FORMATTER)(); +typedef void (*TOTP_CLI_DETAILS_AUTOMATION_FEATURE_ITEM_FORMATTER)( + const char* key, + const char* feature, + bool* header_printed); +typedef void (*TOTP_CLI_DETAILS_CSTR_FORMATTER)(const char* key, const char* value); +typedef void (*TOTP_CLI_DETAILS_UINT8T_FORMATTER)(const char* key, uint8_t value); +typedef void (*TOTP_CLI_DETAILS_SIZET_FORMATTER)(const char* key, size_t value); -static void print_automation_features(const TokenInfo* token_info) { +typedef struct { + const TOTP_CLI_DETAILS_HEADER_FORMATTER header_formatter; + const TOTP_CLI_DETAILS_FOOTER_FORMATTER footer_formatter; + const TOTP_CLI_DETAILS_AUTOMATION_FEATURE_ITEM_FORMATTER automation_feature_item_formatter; + const TOTP_CLI_DETAILS_CSTR_FORMATTER cstr_formatter; + const TOTP_CLI_DETAILS_UINT8T_FORMATTER uint8t_formatter; + const TOTP_CLI_DETAILS_SIZET_FORMATTER sizet_formatter; +} TotpCliDetailsFormatter; + +static const TotpCliDetailsFormatter available_formatters[] = { + {.header_formatter = &details_output_formatter_print_header_table, + .footer_formatter = &details_output_formatter_print_footer_table, + .automation_feature_item_formatter = &details_output_formatter_print_automation_feature_table, + .cstr_formatter = &details_output_formatter_print_cstr_table, + .uint8t_formatter = &details_output_formatter_print_uint8t_table, + .sizet_formatter = &details_output_formatter_print_sizet_table}, + + {.header_formatter = &details_output_formatter_print_header_tsv, + .footer_formatter = &details_output_formatter_print_footer_tsv, + .automation_feature_item_formatter = &details_output_formatter_print_automation_feature_tsv, + .cstr_formatter = &details_output_formatter_print_cstr_tsv, + .uint8t_formatter = &details_output_formatter_print_uint8t_tsv, + .sizet_formatter = &details_output_formatter_print_sizet_tsv}, +}; + +static void print_automation_features( + const TokenInfo* token_info, + const TotpCliDetailsFormatter* formatter) { + bool header_printed = false; + const char* AUTOMATION_FEATURES_PRINT_KEY = "Automation features"; if(token_info->automation_features == TokenAutomationFeatureNone) { - TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", "Automation features", "None"); + (*formatter->automation_feature_item_formatter)( + AUTOMATION_FEATURES_PRINT_KEY, "None", &header_printed); return; } - bool header_printed = false; if(token_info->automation_features & TokenAutomationFeatureEnterAtTheEnd) { - TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type key at the end", header_printed); + (*formatter->automation_feature_item_formatter)( + AUTOMATION_FEATURES_PRINT_KEY, "Type key at the end", &header_printed); } if(token_info->automation_features & TokenAutomationFeatureTabAtTheEnd) { - TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type key at the end", header_printed); + (*formatter->automation_feature_item_formatter)( + AUTOMATION_FEATURES_PRINT_KEY, "Type key at the end", &header_printed); } if(token_info->automation_features & TokenAutomationFeatureTypeSlower) { - TOTP_CLI_PRINTF_AUTOMATION_FEATURE("Type slower", header_printed); + (*formatter->automation_feature_item_formatter)( + AUTOMATION_FEATURES_PRINT_KEY, "Type slower", &header_printed); } } +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_details_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DETAILS ", " TOTP_CLI_COMMAND_DETAILS_ALT " Displays token details\r\n"); @@ -47,6 +83,7 @@ void totp_cli_command_details_docopt_usage() { TOTP_CLI_COMMAND_DETAILS " | " TOTP_CLI_COMMAND_DETAILS_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) "\r\n"); } +#endif void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { @@ -62,6 +99,14 @@ void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args return; } + const TotpCliDetailsFormatter* formatter = &available_formatters[0]; + FuriString* arg = furi_string_alloc(); + if(args_read_string_and_trim(args, arg) && furi_string_cmpi_str(arg, "--tsv") == 0) { + formatter = &available_formatters[1]; + } + + furi_string_free(arg); + TOTP_CLI_LOCK_UI(plugin_state); size_t original_token_index = @@ -69,19 +114,14 @@ void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args if(totp_token_info_iterator_go_to(iterator_context, token_number - 1)) { const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); - TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); - TOTP_CLI_PRINTF("| %-20s | %-28d |\r\n", "Index", token_number); - TOTP_CLI_PRINTF( - "| %-20s | %-28.28s |\r\n", "Name", furi_string_get_cstr(token_info->name)); - TOTP_CLI_PRINTF( - "| %-20s | %-28s |\r\n", "Hashing algorithm", token_info_get_algo_as_cstr(token_info)); - TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", "Number of digits", token_info->digits); - TOTP_CLI_PRINTF( - "| %-20s | %" PRIu8 " sec.%-21s |\r\n", "Token lifetime", token_info->duration, " "); - print_automation_features(token_info); - TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + (*formatter->header_formatter)(); + (*formatter->sizet_formatter)("Index", token_number); + (*formatter->cstr_formatter)("Name", furi_string_get_cstr(token_info->name)); + (*formatter->cstr_formatter)("Hashing algorithm", token_info_get_algo_as_cstr(token_info)); + (*formatter->uint8t_formatter)("Number of digits", token_info->digits); + (*formatter->uint8t_formatter)("Token lifetime", token_info->duration); + print_automation_features(token_info, formatter); + (*formatter->footer_formatter)(); } else { totp_cli_print_error_loading_token_info(); } diff --git a/applications/external/totp/cli/commands/details/details.h b/applications/external/totp/cli/commands/details/details.h index d141705a5..aec1a0184 100644 --- a/applications/external/totp/cli/commands/details/details.h +++ b/applications/external/totp/cli/commands/details/details.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_DETAILS "lsattr" #define TOTP_CLI_COMMAND_DETAILS_ALT "cat" void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_details_docopt_commands(); void totp_cli_command_details_docopt_usage(); +#endif diff --git a/applications/external/totp/cli/commands/details/formatters/table/details_output_formatter_table.c b/applications/external/totp/cli/commands/details/formatters/table/details_output_formatter_table.c new file mode 100644 index 000000000..44290db69 --- /dev/null +++ b/applications/external/totp/cli/commands/details/formatters/table/details_output_formatter_table.c @@ -0,0 +1,32 @@ +#include "details_output_formatter_table.h" +#include "../../../../cli_helpers.h" + +void details_output_formatter_print_header_table() { + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); + TOTP_CLI_PRINTF("| %-20s | %-28s |\r\n", "Property", "Value"); + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); +} + +void details_output_formatter_print_footer_table() { + TOTP_CLI_PRINTF("+----------------------+------------------------------+\r\n"); +} + +void details_output_formatter_print_automation_feature_table( + const char* key, + const char* feature, + bool* header_printed) { + TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", *header_printed ? "" : key, feature); + *header_printed = true; +} + +void details_output_formatter_print_cstr_table(const char* key, const char* value) { + TOTP_CLI_PRINTF("| %-20s | %-28.28s |\r\n", key, value); +} + +void details_output_formatter_print_uint8t_table(const char* key, uint8_t value) { + TOTP_CLI_PRINTF("| %-20s | %-28" PRIu8 " |\r\n", key, value); +} + +void details_output_formatter_print_sizet_table(const char* key, size_t value) { + TOTP_CLI_PRINTF("| %-20s | %-28" PRIu16 " |\r\n", key, value); +} diff --git a/applications/external/totp/cli/commands/details/formatters/table/details_output_formatter_table.h b/applications/external/totp/cli/commands/details/formatters/table/details_output_formatter_table.h new file mode 100644 index 000000000..bdb6bf115 --- /dev/null +++ b/applications/external/totp/cli/commands/details/formatters/table/details_output_formatter_table.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +void details_output_formatter_print_header_table(); +void details_output_formatter_print_footer_table(); +void details_output_formatter_print_automation_feature_table( + const char* key, + const char* feature, + bool* header_printed); +void details_output_formatter_print_cstr_table(const char* key, const char* value); +void details_output_formatter_print_uint8t_table(const char* key, uint8_t value); +void details_output_formatter_print_sizet_table(const char* key, size_t value); diff --git a/applications/external/totp/cli/commands/details/formatters/tsv/details_output_formatter_tsv.c b/applications/external/totp/cli/commands/details/formatters/tsv/details_output_formatter_tsv.c new file mode 100644 index 000000000..39e61342c --- /dev/null +++ b/applications/external/totp/cli/commands/details/formatters/tsv/details_output_formatter_tsv.c @@ -0,0 +1,29 @@ +#include "details_output_formatter_tsv.h" +#include "../../../../cli_helpers.h" + +void details_output_formatter_print_header_tsv() { + TOTP_CLI_PRINTF("%s\t%s\r\n", "Property", "Value"); +} + +void details_output_formatter_print_footer_tsv() { +} + +void details_output_formatter_print_automation_feature_tsv( + const char* key, + const char* feature, + bool* header_printed) { + TOTP_CLI_PRINTF("%s\t%s\r\n", *header_printed ? "" : key, feature); + *header_printed = true; +} + +void details_output_formatter_print_cstr_tsv(const char* key, const char* value) { + TOTP_CLI_PRINTF("%s\t%s\r\n", key, value); +} + +void details_output_formatter_print_uint8t_tsv(const char* key, uint8_t value) { + TOTP_CLI_PRINTF("%s\t%" PRIu8 "\r\n", key, value); +} + +void details_output_formatter_print_sizet_tsv(const char* key, size_t value) { + TOTP_CLI_PRINTF("%s\t%" PRIu16 "\r\n", key, value); +} diff --git a/applications/external/totp/cli/commands/details/formatters/tsv/details_output_formatter_tsv.h b/applications/external/totp/cli/commands/details/formatters/tsv/details_output_formatter_tsv.h new file mode 100644 index 000000000..15b20ffcb --- /dev/null +++ b/applications/external/totp/cli/commands/details/formatters/tsv/details_output_formatter_tsv.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +void details_output_formatter_print_header_tsv(); +void details_output_formatter_print_footer_tsv(); +void details_output_formatter_print_automation_feature_tsv( + const char* key, + const char* feature, + bool* header_printed); +void details_output_formatter_print_cstr_tsv(const char* key, const char* value); +void details_output_formatter_print_uint8t_tsv(const char* key, uint8_t value); +void details_output_formatter_print_sizet_tsv(const char* key, size_t value); diff --git a/applications/external/totp/cli/commands/help/help.c b/applications/external/totp/cli/commands/help/help.c index cc47db11f..e5519d043 100644 --- a/applications/external/totp/cli/commands/help/help.c +++ b/applications/external/totp/cli/commands/help/help.c @@ -12,6 +12,7 @@ #include "../automation/automation.h" #include "../details/details.h" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_help_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT ", " TOTP_CLI_COMMAND_HELP_ALT2 " Show command usage help\r\n"); @@ -22,8 +23,10 @@ void totp_cli_command_help_docopt_usage() { TOTP_CLI_COMMAND_HELP " | " TOTP_CLI_COMMAND_HELP_ALT " | " TOTP_CLI_COMMAND_HELP_ALT2) "\r\n"); } +#endif void totp_cli_command_help_handle() { +#ifdef TOTP_CLI_RICH_HELP_ENABLED TOTP_CLI_PRINTF("Usage:\r\n"); totp_cli_command_help_docopt_usage(); totp_cli_command_list_docopt_usage(); @@ -64,4 +67,10 @@ void totp_cli_command_help_handle() { totp_cli_command_add_docopt_options(); totp_cli_command_update_docopt_options(); totp_cli_command_delete_docopt_options(); + totp_cli_command_pin_docopt_options(); + totp_cli_command_automation_docopt_options(); +#else + TOTP_CLI_PRINTF( + "All the TOTP CLI commands, their arguments, options and usage can be found here https://t.ly/_6pJG"); +#endif } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/help/help.h b/applications/external/totp/cli/commands/help/help.h index 4268b8bc5..da7c2fd62 100644 --- a/applications/external/totp/cli/commands/help/help.h +++ b/applications/external/totp/cli/commands/help/help.h @@ -1,5 +1,6 @@ #pragma once +#include "../../../config/app/config.h" #include #define TOTP_CLI_COMMAND_HELP "help" @@ -7,5 +8,7 @@ #define TOTP_CLI_COMMAND_HELP_ALT2 "?" void totp_cli_command_help_handle(); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_help_docopt_commands(); -void totp_cli_command_help_docopt_usage(); \ No newline at end of file +void totp_cli_command_help_docopt_usage(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/formatters/table/list_output_formatter_table.c b/applications/external/totp/cli/commands/list/formatters/table/list_output_formatter_table.c new file mode 100644 index 000000000..8774a6edc --- /dev/null +++ b/applications/external/totp/cli/commands/list/formatters/table/list_output_formatter_table.c @@ -0,0 +1,22 @@ +#include "list_output_formatter_table.h" +#include "../../../../cli_helpers.h" + +void list_output_formatter_print_header_table() { + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); + TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur"); + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); +} + +void list_output_formatter_print_body_item_table(size_t index, const TokenInfo* token_info) { + TOTP_CLI_PRINTF( + "| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n", + index + 1, + furi_string_get_cstr(token_info->name), + token_info_get_algo_as_cstr(token_info), + token_info->digits, + token_info->duration); +} + +void list_output_formatter_print_footer_table() { + TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); +} \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/formatters/table/list_output_formatter_table.h b/applications/external/totp/cli/commands/list/formatters/table/list_output_formatter_table.h new file mode 100644 index 000000000..e2ed9c4dc --- /dev/null +++ b/applications/external/totp/cli/commands/list/formatters/table/list_output_formatter_table.h @@ -0,0 +1,9 @@ +#pragma once + +#include "../../../../../types/token_info.h" + +void list_output_formatter_print_header_table(); + +void list_output_formatter_print_body_item_table(size_t index, const TokenInfo* token_info); + +void list_output_formatter_print_footer_table(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/formatters/tsv/list_output_formatter_tsv.c b/applications/external/totp/cli/commands/list/formatters/tsv/list_output_formatter_tsv.c new file mode 100644 index 000000000..2bd455397 --- /dev/null +++ b/applications/external/totp/cli/commands/list/formatters/tsv/list_output_formatter_tsv.c @@ -0,0 +1,19 @@ +#include "list_output_formatter_tsv.h" +#include "../../../../cli_helpers.h" + +void list_output_formatter_print_header_tsv() { + TOTP_CLI_PRINTF("%s\t%s\t%s\t%s\t%s\r\n", "#", "Name", "Algo", "Ln", "Dur"); +} + +void list_output_formatter_print_body_item_tsv(size_t index, const TokenInfo* token_info) { + TOTP_CLI_PRINTF( + "%" PRIu16 "\t%s\t%s\t%" PRIu8 "\t%" PRIu8 "\r\n", + index + 1, + furi_string_get_cstr(token_info->name), + token_info_get_algo_as_cstr(token_info), + token_info->digits, + token_info->duration); +} + +void list_output_formatter_print_footer_tsv() { +} \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/formatters/tsv/list_output_formatter_tsv.h b/applications/external/totp/cli/commands/list/formatters/tsv/list_output_formatter_tsv.h new file mode 100644 index 000000000..e01c95d2e --- /dev/null +++ b/applications/external/totp/cli/commands/list/formatters/tsv/list_output_formatter_tsv.h @@ -0,0 +1,9 @@ +#pragma once + +#include "../../../../../types/token_info.h" + +void list_output_formatter_print_header_tsv(); + +void list_output_formatter_print_body_item_tsv(size_t index, const TokenInfo* token_info); + +void list_output_formatter_print_footer_tsv(); \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/list.c b/applications/external/totp/cli/commands/list/list.c index bf5a8da52..1c39f3203 100644 --- a/applications/external/totp/cli/commands/list/list.c +++ b/applications/external/totp/cli/commands/list/list.c @@ -1,11 +1,34 @@ #include "list.h" #include +#include #include "../../../types/token_info.h" #include "../../../services/config/constants.h" #include "../../../services/config/config.h" #include "../../../ui/scene_director.h" #include "../../cli_helpers.h" +#include "formatters/table/list_output_formatter_table.h" +#include "formatters/tsv/list_output_formatter_tsv.h" +typedef void (*TOTP_CLI_LIST_HEADER_FORMATTER)(); +typedef void (*TOTP_CLI_LIST_FOOTER_FORMATTER)(); +typedef void (*TOTP_CLI_LIST_BODY_ITEM_FORMATTER)(size_t index, const TokenInfo* token_info); + +typedef struct { + const TOTP_CLI_LIST_HEADER_FORMATTER header_formatter; + const TOTP_CLI_LIST_FOOTER_FORMATTER footer_formatter; + const TOTP_CLI_LIST_BODY_ITEM_FORMATTER body_item_formatter; +} TotpCliListFormatter; + +static const TotpCliListFormatter available_formatters[] = { + {.header_formatter = &list_output_formatter_print_header_table, + .body_item_formatter = &list_output_formatter_print_body_item_table, + .footer_formatter = &list_output_formatter_print_footer_table}, + + {.header_formatter = &list_output_formatter_print_header_tsv, + .body_item_formatter = &list_output_formatter_print_body_item_tsv, + .footer_formatter = &list_output_formatter_print_footer_tsv}}; + +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_list_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_LIST ", " TOTP_CLI_COMMAND_LIST_ALT " List all available tokens\r\n"); @@ -15,8 +38,9 @@ void totp_cli_command_list_docopt_usage() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " DOCOPT_REQUIRED( TOTP_CLI_COMMAND_LIST " | " TOTP_CLI_COMMAND_LIST_ALT) "\r\n"); } +#endif -void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { +void totp_cli_command_list_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { return; } @@ -29,26 +53,26 @@ void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { return; } + const TotpCliListFormatter* formatter = &available_formatters[0]; + FuriString* arg = furi_string_alloc(); + if(args_read_string_and_trim(args, arg) && furi_string_cmpi_str(arg, "--tsv") == 0) { + formatter = &available_formatters[1]; + } + + furi_string_free(arg); + TOTP_CLI_LOCK_UI(plugin_state); size_t original_index = totp_token_info_iterator_get_current_token_index(iterator_context); - TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); - TOTP_CLI_PRINTF("| %-3s | %-25s | %-6s | %-s | %-s |\r\n", "#", "Name", "Algo", "Ln", "Dur"); - TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); + (*formatter->header_formatter)(); for(size_t i = 0; i < total_count; i++) { totp_token_info_iterator_go_to(iterator_context, i); const TokenInfo* token_info = totp_token_info_iterator_get_current_token(iterator_context); - TOTP_CLI_PRINTF( - "| %-3" PRIu16 " | %-25.25s | %-6s | %-2" PRIu8 " | %-3" PRIu8 " |\r\n", - i + 1, - furi_string_get_cstr(token_info->name), - token_info_get_algo_as_cstr(token_info), - token_info->digits, - token_info->duration); + (*formatter->body_item_formatter)(i, token_info); } - TOTP_CLI_PRINTF("+-----+---------------------------+--------+----+-----+\r\n"); + (*formatter->footer_formatter)(); totp_token_info_iterator_go_to(iterator_context, original_index); diff --git a/applications/external/totp/cli/commands/list/list.h b/applications/external/totp/cli/commands/list/list.h index 7d6de5874..0527e8c5f 100644 --- a/applications/external/totp/cli/commands/list/list.h +++ b/applications/external/totp/cli/commands/list/list.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_LIST "list" #define TOTP_CLI_COMMAND_LIST_ALT "ls" -void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli); +void totp_cli_command_list_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_list_docopt_commands(); void totp_cli_command_list_docopt_usage(); +#endif diff --git a/applications/external/totp/cli/commands/move/move.c b/applications/external/totp/cli/commands/move/move.c index f26cda46e..fbf82a3bb 100644 --- a/applications/external/totp/cli/commands/move/move.c +++ b/applications/external/totp/cli/commands/move/move.c @@ -10,6 +10,7 @@ #define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX "new_index" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_move_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE ", " TOTP_CLI_COMMAND_MOVE_ALT " Move token\r\n"); @@ -26,6 +27,7 @@ void totp_cli_command_move_docopt_arguments() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX " New token index in the list\r\n"); } +#endif void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/move/move.h b/applications/external/totp/cli/commands/move/move.h index da84f2a62..0dfd90f6b 100644 --- a/applications/external/totp/cli/commands/move/move.h +++ b/applications/external/totp/cli/commands/move/move.h @@ -2,11 +2,14 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_MOVE "move" #define TOTP_CLI_COMMAND_MOVE_ALT "mv" void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_move_docopt_commands(); void totp_cli_command_move_docopt_usage(); -void totp_cli_command_move_docopt_arguments(); \ No newline at end of file +void totp_cli_command_move_docopt_arguments(); +#endif diff --git a/applications/external/totp/cli/commands/notification/notification.c b/applications/external/totp/cli/commands/notification/notification.c index f91b1b982..d1814fb26 100644 --- a/applications/external/totp/cli/commands/notification/notification.c +++ b/applications/external/totp/cli/commands/notification/notification.c @@ -9,6 +9,7 @@ #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "sound" #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "vibro" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_notification_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NOTIFICATION " Get or set notification method\r\n"); @@ -27,6 +28,7 @@ void totp_cli_command_notification_docopt_arguments() { ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\r\n"); } +#endif static void totp_cli_command_notification_print_method(NotificationMethod method, const char* color) { diff --git a/applications/external/totp/cli/commands/notification/notification.h b/applications/external/totp/cli/commands/notification/notification.h index c349f9620..e4cdb651d 100644 --- a/applications/external/totp/cli/commands/notification/notification.h +++ b/applications/external/totp/cli/commands/notification/notification.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_NOTIFICATION "notify" void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_notification_docopt_commands(); void totp_cli_command_notification_docopt_usage(); -void totp_cli_command_notification_docopt_arguments(); \ No newline at end of file +void totp_cli_command_notification_docopt_arguments(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index 62531b96a..d31790950 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -7,21 +7,39 @@ #include "../../../services/config/config.h" #include "../../cli_helpers.h" #include -#include "../../../services/crypto/crypto.h" +#include "../../../services/crypto/crypto_facade.h" #include "../../../ui/scene_director.h" #define TOTP_CLI_COMMAND_PIN_COMMAND_SET "set" #define TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE "remove" +#define TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX "-c" +#define TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT "slot" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_pin_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_PIN " Set\\change\\remove PIN\r\n"); } void totp_cli_command_pin_docopt_usage() { - TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_PIN " " DOCOPT_REQUIRED( - TOTP_CLI_COMMAND_PIN_COMMAND_SET " | " TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) "\r\n"); + TOTP_CLI_PRINTF( + " " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_PIN + " " DOCOPT_REQUIRED(TOTP_CLI_COMMAND_PIN_COMMAND_SET " | " TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) " " DOCOPT_OPTIONAL( + DOCOPT_OPTION( + TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX, + DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT))) "\r\n"); } +void totp_cli_command_pin_docopt_options() { + TOTP_CLI_PRINTF( + " " DOCOPT_OPTION( + TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX, + DOCOPT_ARGUMENT( + TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT)) " New crypto key slot. Must be between %d and %d\r\n", + ACCEPTABLE_CRYPTO_KEY_SLOT_START, + ACCEPTABLE_CRYPTO_KEY_SLOT_END); +} +#endif + static inline uint8_t totp_cli_key_to_pin_code(uint8_t key) { uint8_t code = 0; switch(key) { @@ -89,35 +107,49 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl bool do_change = false; bool do_remove = false; - UNUSED(do_remove); - if(args_read_string_and_trim(args, temp_str)) { + uint8_t crypto_key_slot = plugin_state->crypto_settings.crypto_key_slot; + + bool arguments_parsed = true; + while(args_read_string_and_trim(args, temp_str)) { if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_SET) == 0) { do_change = true; } else if(furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_COMMAND_REMOVE) == 0) { do_remove = true; + } else if( + furi_string_cmpi_str(temp_str, TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX) == + 0) { + if(!args_read_uint8_and_trim(args, &crypto_key_slot) || + !totp_crypto_check_key_slot(crypto_key_slot)) { + TOTP_CLI_PRINTF_ERROR("Slot \"%" PRIu8 "\" can not be used\r\n", crypto_key_slot); + arguments_parsed = false; + break; + } } else { totp_cli_print_invalid_arguments(); + arguments_parsed = false; + break; } - } else { - totp_cli_print_invalid_arguments(); } - if((do_change || do_remove) && totp_cli_ensure_authenticated(plugin_state, cli)) { + if(!(do_change || do_remove) || (do_change && do_remove)) { + totp_cli_print_invalid_arguments(); + arguments_parsed = false; + } + + if(arguments_parsed && totp_cli_ensure_authenticated(plugin_state, cli)) { TOTP_CLI_LOCK_UI(plugin_state); do { - uint8_t old_iv[TOTP_IV_SIZE]; - memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); - uint8_t new_pin[TOTP_IV_SIZE]; - memset(&new_pin[0], 0, TOTP_IV_SIZE); + uint8_t new_pin[CRYPTO_IV_LENGTH]; + memset(&new_pin[0], 0, CRYPTO_IV_LENGTH); uint8_t new_pin_length = 0; if(do_change) { if(!totp_cli_read_pin(cli, &new_pin[0], &new_pin_length)) { - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + memset_s(&new_pin[0], CRYPTO_IV_LENGTH, 0, CRYPTO_IV_LENGTH); break; } } else if(do_remove) { new_pin_length = 0; - memset(&new_pin[0], 0, TOTP_IV_SIZE); + memset(&new_pin[0], 0, CRYPTO_IV_LENGTH); } char* backup_path = totp_config_file_backup(plugin_state); @@ -127,7 +159,7 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl "Once you make sure everything is fine and works as expected, please delete this backup file\r\n"); free(backup_path); } else { - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + memset_s(&new_pin[0], CRYPTO_IV_LENGTH, 0, CRYPTO_IV_LENGTH); TOTP_CLI_PRINTF_ERROR( "An error has occurred during taking backup of config file\r\n"); break; @@ -135,10 +167,10 @@ void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cl TOTP_CLI_PRINTF("Encrypting...\r\n"); - bool update_result = - totp_config_file_update_encryption(plugin_state, new_pin, new_pin_length); + bool update_result = totp_config_file_update_encryption( + plugin_state, crypto_key_slot, new_pin, new_pin_length); - memset_s(&new_pin[0], TOTP_IV_SIZE, 0, TOTP_IV_SIZE); + memset_s(&new_pin[0], CRYPTO_IV_LENGTH, 0, CRYPTO_IV_LENGTH); totp_cli_delete_last_line(); diff --git a/applications/external/totp/cli/commands/pin/pin.h b/applications/external/totp/cli/commands/pin/pin.h index 1308ae736..708353565 100644 --- a/applications/external/totp/cli/commands/pin/pin.h +++ b/applications/external/totp/cli/commands/pin/pin.h @@ -2,9 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_PIN "pin" void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_pin_docopt_commands(); -void totp_cli_command_pin_docopt_usage(); \ No newline at end of file +void totp_cli_command_pin_docopt_usage(); +void totp_cli_command_pin_docopt_options(); +#endif diff --git a/applications/external/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c index 96b2cc56a..a02bc0eb0 100644 --- a/applications/external/totp/cli/commands/reset/reset.c +++ b/applications/external/totp/cli/commands/reset/reset.c @@ -8,6 +8,7 @@ #define TOTP_CLI_RESET_CONFIRMATION_KEYWORD "YES" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_reset_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_RESET " Reset application to default settings\r\n"); @@ -16,11 +17,9 @@ void totp_cli_command_reset_docopt_commands() { void totp_cli_command_reset_docopt_usage() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_RESET "\r\n"); } +#endif -void totp_cli_command_reset_handle( - PluginState* plugin_state, - Cli* cli, - FuriMessageQueue* event_queue) { +void totp_cli_command_reset_handle(PluginState* plugin_state, Cli* cli) { TOTP_CLI_LOCK_UI(plugin_state); TOTP_CLI_PRINTF_WARNING( "As a result of reset all the settings and tokens will be permanently lost.\r\n"); @@ -35,7 +34,7 @@ void totp_cli_command_reset_handle( totp_config_file_reset(plugin_state); TOTP_CLI_PRINTF_SUCCESS("Application has been successfully reset to default.\r\n"); TOTP_CLI_PRINTF_SUCCESS("Now application will be closed to apply all the changes.\r\n"); - totp_cli_force_close_app(event_queue); + totp_cli_force_close_app(plugin_state->event_queue); } else { TOTP_CLI_PRINTF_INFO("Action was not confirmed by user\r\n"); TOTP_CLI_UNLOCK_UI(plugin_state); diff --git a/applications/external/totp/cli/commands/reset/reset.h b/applications/external/totp/cli/commands/reset/reset.h index 3a4675587..7f48aa10b 100644 --- a/applications/external/totp/cli/commands/reset/reset.h +++ b/applications/external/totp/cli/commands/reset/reset.h @@ -2,12 +2,12 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_RESET "reset" -void totp_cli_command_reset_handle( - PluginState* plugin_state, - Cli* cli, - FuriMessageQueue* event_queue); +void totp_cli_command_reset_handle(PluginState* plugin_state, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_reset_docopt_commands(); -void totp_cli_command_reset_docopt_usage(); \ No newline at end of file +void totp_cli_command_reset_docopt_usage(); +#endif diff --git a/applications/external/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c index 0f6bc5a76..af5db6e6e 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.c +++ b/applications/external/totp/cli/commands/timezone/timezone.c @@ -6,6 +6,7 @@ #define TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE "timezone" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_timezone_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE ", " TOTP_CLI_COMMAND_TIMEZONE_ALT " Get or set current timezone\r\n"); @@ -22,6 +23,7 @@ void totp_cli_command_timezone_docopt_arguments() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE " Timezone offset in hours to be set\r\n"); } +#endif void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/timezone/timezone.h b/applications/external/totp/cli/commands/timezone/timezone.h index 9823cf0ee..1a697f067 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.h +++ b/applications/external/totp/cli/commands/timezone/timezone.h @@ -2,11 +2,14 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_TIMEZONE "timezone" #define TOTP_CLI_COMMAND_TIMEZONE_ALT "tz" void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_timezone_docopt_commands(); void totp_cli_command_timezone_docopt_usage(); -void totp_cli_command_timezone_docopt_arguments(); \ No newline at end of file +void totp_cli_command_timezone_docopt_arguments(); +#endif diff --git a/applications/external/totp/cli/commands/update/update.c b/applications/external/totp/cli/commands/update/update.c index 49beb7b6d..5c1553d87 100644 --- a/applications/external/totp/cli/commands/update/update.c +++ b/applications/external/totp/cli/commands/update/update.c @@ -13,7 +13,7 @@ struct TotpUpdateContext { FuriString* args; Cli* cli; - uint8_t* iv; + const CryptoSettings* crypto_settings; }; enum TotpIteratorUpdateTokenResultsEx { @@ -83,7 +83,7 @@ static TotpIteratorUpdateTokenResult if(update_token_secret) { // Reading token secret furi_string_reset(temp_str); - TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]\r\n"); + TOTP_CLI_PRINTF("Enter token secret and confirm with [ENTER]:\r\n"); bool token_secret_read = totp_cli_read_line(context_t->cli, temp_str, mask_user_input); totp_cli_delete_last_line(); if(!token_secret_read) { @@ -96,7 +96,7 @@ static TotpIteratorUpdateTokenResult furi_string_get_cstr(temp_str), furi_string_size(temp_str), token_secret_encoding, - context_t->iv)) { + context_t->crypto_settings)) { furi_string_secure_free(temp_str); return TotpIteratorUpdateTokenResultInvalidSecret; } @@ -107,6 +107,7 @@ static TotpIteratorUpdateTokenResult return TotpIteratorUpdateTokenResultSuccess; } +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_update_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n"); } @@ -129,6 +130,7 @@ void totp_cli_command_update_docopt_options() { TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\r\n"); } +#endif void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { @@ -151,7 +153,7 @@ void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, totp_token_info_iterator_go_to(iterator_context, token_number - 1); struct TotpUpdateContext update_context = { - .args = args, .cli = cli, .iv = &plugin_state->iv[0]}; + .args = args, .cli = cli, .crypto_settings = &plugin_state->crypto_settings}; TotpIteratorUpdateTokenResult update_result = totp_token_info_iterator_update_current_token( iterator_context, &update_token_handler, &update_context); diff --git a/applications/external/totp/cli/commands/update/update.h b/applications/external/totp/cli/commands/update/update.h index 3b5b442e8..6100f4b38 100644 --- a/applications/external/totp/cli/commands/update/update.h +++ b/applications/external/totp/cli/commands/update/update.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_UPDATE "update" void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_update_docopt_commands(); void totp_cli_command_update_docopt_usage(); -void totp_cli_command_update_docopt_options(); \ No newline at end of file +void totp_cli_command_update_docopt_options(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/common_command_arguments.h b/applications/external/totp/cli/common_command_arguments.h index be1c0bdfe..0bf834225 100644 --- a/applications/external/totp/cli/common_command_arguments.h +++ b/applications/external/totp/cli/common_command_arguments.h @@ -18,32 +18,87 @@ #define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING_PREFIX "-e" #define TOTP_CLI_COMMAND_ARG_SECRET_ENCODING "encoding" +/** + * @brief Prints information about unknown argument + * @param arg + */ void totp_cli_printf_unknown_argument(const FuriString* arg); +/** + * @brief Prints information about missed required argument + * @param arg + */ void totp_cli_printf_missed_argument_value(char* arg); +/** + * @brief Tries to read token hashing algo + * @param token_info token info to set parsed algo to if successfully read and parsed + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if token hashing algo sucecssfully read and parsed; \c false otherwise + * @return \c true if \c arg represents token hashing algo argument; \c false otherwise + */ bool totp_cli_try_read_algo(TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); +/** + * @brief Tries to read token digits count + * @param token_info token info to set parsed digits count to if successfully read and parsed + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if token digits count sucecssfully read and parsed; \c false otherwise + * @return \c true if \c arg represents token digits count argument; \c false otherwise + */ bool totp_cli_try_read_digits( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); +/** + * @brief Tries to read token duration + * @param token_info token info to set parsed duration to if successfully read and parsed + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if token duration sucecssfully read and parsed; \c false otherwise + * @return \c true if \c arg represents token duration argument; \c false otherwise + */ bool totp_cli_try_read_duration( TokenInfo* token_info, const FuriString* arg, FuriString* args, bool* parsed); +/** + * @brief Tries to read token automation features + * @param token_info token info to set parsed automation features to if successfully read and parsed + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if token automation features sucecssfully read and parsed; \c false otherwise + * @return \c true if \c arg represents token automation features argument; \c false otherwise + */ bool totp_cli_try_read_automation_features( TokenInfo* token_info, FuriString* arg, FuriString* args, bool* parsed); +/** + * @brief Tries to read unsecure flag + * @param arg argument to parse + * @param[out] parsed will be set to \c true if unsecure flag sucecssfully read and parsed; \c false otherwise + * @param[out] unsecure_flag will be set to parsed unsecure flag state if read and parsed successfully + * @return \c true if \c arg represents unsecure flag argument; \c false otherwise + */ bool totp_cli_try_read_unsecure_flag(const FuriString* arg, bool* parsed, bool* unsecure_flag); +/** + * @brief Tries to read plain token secret encoding + * @param arg argument to parse + * @param args rest of arguments + * @param[out] parsed will be set to \c true if plain token secret encoding sucecssfully read and parsed; \c false otherwise + * @param[out] secret_encoding will be set to parsed plain token secret encoding if read and parsed successfully + * @return \c true if \c arg represents plain token secret encoding argument; \c false otherwise + */ bool totp_cli_try_read_plain_token_secret_encoding( FuriString* arg, FuriString* args, diff --git a/applications/external/totp/config/app/config.h b/applications/external/totp/config/app/config.h new file mode 100644 index 000000000..8c5fc1314 --- /dev/null +++ b/applications/external/totp/config/app/config.h @@ -0,0 +1,42 @@ +// Application automatic lock timeout if user IDLE. (ticks) +#ifndef TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC +#define TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC (60) +#endif + +// Enables\disables Bluetooth token input automation +#ifndef TOTP_NO_BADBT_AUTOMATION +#define TOTP_BADBT_AUTOMATION_ENABLED +#endif + +// Enables\disables backward compatibility with crypto algorithms v1 +#ifndef TOTP_NO_OBSOLETE_CRYPTO_V1_COMPATIBILITY +#define TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED +#endif + +// Enables\disables backward compatibility with crypto algorithms v2 +#ifndef TOTP_NO_OBSOLETE_CRYPTO_V2_COMPATIBILITY +#define TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED +#endif + +// Enables\disables userfriendly TOTP CLI help text +// If disabled, it will print a link to a wiki page +#ifndef TOTP_CLI_NO_RICH_HELP +#define TOTP_CLI_RICH_HELP_ENABLED +#endif + +// Enables\disables "Add new token" UI +// If disabled it will print a link to wiki page +#ifndef TOTP_UI_NO_ADD_NEW_TOKEN +#define TOTP_UI_ADD_NEW_TOKEN_ENABLED +#endif + +// List of compatible firmwares +#define TOTP_FIRMWARE_OFFICIAL_STABLE (1) +#define TOTP_FIRMWARE_OFFICIAL_DEV (2) +#define TOTP_FIRMWARE_XTREME_UL (3) +// End of list + +// Target firmware +#ifndef TOTP_TARGET_FIRMWARE +#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME_UL +#endif diff --git a/applications/external/totp/config/wolfssl/config.h b/applications/external/totp/config/wolfssl/config.h new file mode 100644 index 000000000..6500d6422 --- /dev/null +++ b/applications/external/totp/config/wolfssl/config.h @@ -0,0 +1,34 @@ +#pragma once + +#define NO_OLD_SHA_NAMES +#define WOLFCRYPT_ONLY +#define NO_SIG_WRAPPER +#define NO_AES +#define NO_AES_CBC +#define NO_DES3 +#define NO_DSA +#define NO_RSA +#define NO_DH +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_PKCS12 +#define NO_PKCS8 +#define WC_NO_RNG +#define NO_FILESYSTEM +#define NO_WRITEV +#define NO_MAIN_DRIVER +#define NO_DEV_RANDOM +#define WOLFSSL_SHA512 +#define WOLFSSL_NOSHA512_224 +#define WOLFSSL_NOSHA512_256 +#define USE_SLOW_SHA512 +#define USE_SLOW_SHA256 +#define USE_SLOW_SHA +#define NO_CERTS +#define NO_WOLFSSL_MEMORY +#define WOLFSSL_NO_PEM +#define NO_PSK +#define NO_ERROR_STRINGS +#define NO_OLD_TLS +#define SINGLE_THREADED diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h deleted file mode 100644 index 54bbaba94..000000000 --- a/applications/external/totp/features_config.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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 -#ifndef TOTP_NO_BADBT_TYPE -#define TOTP_BADBT_TYPE_ENABLED -#endif - -// Include token input automation icons on the main screen -#ifndef TOTP_NO_AUTOMATION_ICONS -#define TOTP_AUTOMATION_ICONS_ENABLED -#endif - -// List of compatible firmwares -#define TOTP_FIRMWARE_OFFICIAL_STABLE (1) -#define TOTP_FIRMWARE_OFFICIAL_DEV (2) -#define TOTP_FIRMWARE_XTREME_UL (3) -// End of list - -#ifndef TOTP_TARGET_FIRMWARE -#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME_UL -#endif diff --git a/applications/external/totp/images/totp_arrow_bottom_10x5.png b/applications/external/totp/images/totp_arrow_bottom_10x5.png deleted file mode 100644 index 54e22f5ef..000000000 Binary files a/applications/external/totp/images/totp_arrow_bottom_10x5.png and /dev/null differ diff --git a/applications/external/totp/lib/roll_value/roll_value.c b/applications/external/totp/lib/roll_value/roll_value.c index 563429d0d..326c7846a 100644 --- a/applications/external/totp/lib/roll_value/roll_value.c +++ b/applications/external/totp/lib/roll_value/roll_value.c @@ -25,4 +25,4 @@ TOTP_ROLL_VALUE_FN(int8_t, int8_t) TOTP_ROLL_VALUE_FN(uint8_t, int8_t) -TOTP_ROLL_VALUE_FN(size_t, int16_t) \ No newline at end of file +TOTP_ROLL_VALUE_FN(size_t, int16_t); \ No newline at end of file diff --git a/applications/external/totp/lib/wolfssl b/applications/external/totp/lib/wolfssl new file mode 160000 index 000000000..d3d131d08 --- /dev/null +++ b/applications/external/totp/lib/wolfssl @@ -0,0 +1 @@ +Subproject commit d3d131d08d72b4e01cfff2fbe57d39cd46d6b0dc diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index 7e594a144..bd2d877b0 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -8,8 +8,9 @@ #include #include "../../types/common.h" #include "../../types/token_info.h" -#include "../../features_config.h" -#include "../crypto/crypto.h" +#include "../../config/app/config.h" +#include "../crypto/crypto_facade.h" +#include "../crypto/constants.h" #include "migrations/common_migration.h" #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf" @@ -110,7 +111,17 @@ static char* totp_config_file_backup_i(Storage* storage) { static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK) { + bool conf_file_exists = storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK; + if(!conf_file_exists && + storage_common_stat(storage, EXT_PATH("authenticator"), NULL) == FSE_OK) { + FURI_LOG_I(LOGGING_TAG, "Application catalog needs to be migrated"); + FS_Error migration_result = + storage_common_migrate(storage, EXT_PATH("authenticator"), CONFIG_FILE_DIRECTORY_PATH); + FURI_LOG_I(LOGGING_TAG, "Migrated catalog. Result code: %d", (int)migration_result); + conf_file_exists = storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK; + } + + if(conf_file_exists) { FURI_LOG_D(LOGGING_TAG, "Config file %s found", CONFIG_FILE_PATH); if(!flipper_format_file_open_existing(fff_data_file, CONFIG_FILE_PATH)) { FURI_LOG_E(LOGGING_TAG, "Error opening existing file %s", CONFIG_FILE_PATH); @@ -118,18 +129,19 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { return false; } } else { - FURI_LOG_D(LOGGING_TAG, "Config file %s is not found. Will create new.", CONFIG_FILE_PATH); if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { - FURI_LOG_D( - LOGGING_TAG, - "Directory %s doesn't exist. Will create new.", - CONFIG_FILE_DIRECTORY_PATH); + FURI_LOG_D(LOGGING_TAG, "Config file directory doesn't exist. Will create new"); if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { - FURI_LOG_E(LOGGING_TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); + FURI_LOG_E( + LOGGING_TAG, + "Error creating config file directory %s", + CONFIG_FILE_DIRECTORY_PATH); return false; } } + FURI_LOG_D(LOGGING_TAG, "Config file %s is not found. Will create new.", CONFIG_FILE_PATH); + if(!flipper_format_file_open_new(fff_data_file, CONFIG_FILE_PATH)) { totp_close_config_file(fff_data_file); FURI_LOG_E(LOGGING_TAG, "Error creating new file %s", CONFIG_FILE_PATH); @@ -139,14 +151,21 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { flipper_format_write_header_cstr( fff_data_file, CONFIG_FILE_HEADER, CONFIG_FILE_ACTUAL_VERSION); + uint32_t tmp_uint32 = CRYPTO_LATEST_VERSION; + flipper_format_write_uint32(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &tmp_uint32, 1); + + tmp_uint32 = DEFAULT_CRYPTO_KEY_SLOT; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &tmp_uint32, 1); + flipper_format_write_comment_cstr( fff_data_file, - "Config file format specification can be found here: https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md"); + "Config file format specification can be found here: https://t.ly/zwQjE"); float tmp_tz = 0; flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1); - uint32_t tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; + tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); @@ -154,7 +173,11 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1); - tmp_uint32 = 0; + tmp_uint32 = AutomationKeyboardLayoutQWERTY; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1); + + tmp_uint32 = 0; //-V1048 flipper_format_write_uint32(fff_data_file, TOTP_CONFIG_KEY_FONT, &tmp_uint32, 1); if(!flipper_format_rewind(fff_data_file)) { @@ -233,6 +256,12 @@ bool totp_config_file_update_automation_method(const PluginState* plugin_state) break; } + tmp_uint32 = plugin_state->automation_kb_layout; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + break; + } + update_result = true; } while(false); @@ -265,6 +294,12 @@ bool totp_config_file_update_user_settings(const PluginState* plugin_state) { break; } + tmp_uint32 = plugin_state->automation_kb_layout; + if(!flipper_format_insert_or_update_uint32( + file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + break; + } + update_result = true; } while(false); @@ -344,9 +379,39 @@ bool totp_config_file_load(PluginState* const plugin_state) { } } + uint32_t tmp_uint32; + + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &tmp_uint32, 1)) { + FURI_LOG_E(LOGGING_TAG, "Missing required " TOTP_CONFIG_KEY_CRYPTO_VERSION "property"); + break; + } + + plugin_state->crypto_settings.crypto_version = tmp_uint32; + + if(!flipper_format_rewind(fff_data_file)) { + break; + } + + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &tmp_uint32, 1)) { + FURI_LOG_E( + LOGGING_TAG, "Missing required " TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT "property"); + break; + } + + plugin_state->crypto_settings.crypto_key_slot = tmp_uint32; + + if(!flipper_format_rewind(fff_data_file)) { + break; + } + if(!flipper_format_read_hex( - fff_data_file, TOTP_CONFIG_KEY_BASE_IV, &plugin_state->base_iv[0], TOTP_IV_SIZE)) { - FURI_LOG_D(LOGGING_TAG, "Missing base IV"); + fff_data_file, + TOTP_CONFIG_KEY_SALT, + &plugin_state->crypto_settings.salt[0], + CRYPTO_SALT_LENGTH)) { + FURI_LOG_D(LOGGING_TAG, "Missing salt"); } if(!flipper_format_rewind(fff_data_file)) { @@ -357,22 +422,23 @@ bool totp_config_file_load(PluginState* const plugin_state) { if(flipper_format_get_value_count( fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, &crypto_size) && crypto_size > 0) { - plugin_state->crypto_verify_data = malloc(sizeof(uint8_t) * crypto_size); - furi_check(plugin_state->crypto_verify_data != NULL); - plugin_state->crypto_verify_data_length = crypto_size; + plugin_state->crypto_settings.crypto_verify_data = + malloc(sizeof(uint8_t) * crypto_size); + furi_check(plugin_state->crypto_settings.crypto_verify_data != NULL); + plugin_state->crypto_settings.crypto_verify_data_length = crypto_size; if(!flipper_format_read_hex( fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, + plugin_state->crypto_settings.crypto_verify_data, crypto_size)) { FURI_LOG_D(LOGGING_TAG, "Missing crypto verify token"); - free(plugin_state->crypto_verify_data); - plugin_state->crypto_verify_data = NULL; - plugin_state->crypto_verify_data_length = 0; + free(plugin_state->crypto_settings.crypto_verify_data); + plugin_state->crypto_settings.crypto_verify_data = NULL; + plugin_state->crypto_settings.crypto_verify_data_length = 0; } } else { - plugin_state->crypto_verify_data = NULL; - plugin_state->crypto_verify_data_length = 0; + plugin_state->crypto_settings.crypto_verify_data = NULL; + plugin_state->crypto_settings.crypto_verify_data_length = 0; } if(!flipper_format_rewind(fff_data_file)) { @@ -390,13 +456,17 @@ bool totp_config_file_load(PluginState* const plugin_state) { } if(!flipper_format_read_bool( - fff_data_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { - plugin_state->pin_set = true; + fff_data_file, + TOTP_CONFIG_KEY_PINSET, + &plugin_state->crypto_settings.pin_required, + 1)) { + plugin_state->crypto_settings.pin_required = true; } - flipper_format_rewind(fff_data_file); + if(!flipper_format_rewind(fff_data_file)) { + break; + } - uint32_t tmp_uint32; if(!flipper_format_read_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1)) { tmp_uint32 = NotificationMethodSound | NotificationMethodVibro; @@ -404,7 +474,9 @@ bool totp_config_file_load(PluginState* const plugin_state) { plugin_state->notification_method = tmp_uint32; - flipper_format_rewind(fff_data_file); + if(!flipper_format_rewind(fff_data_file)) { + break; + } if(!flipper_format_read_uint32( fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_METHOD, &tmp_uint32, 1)) { @@ -413,6 +485,21 @@ bool totp_config_file_load(PluginState* const plugin_state) { plugin_state->automation_method = tmp_uint32; + if(!flipper_format_rewind(fff_data_file)) { + break; + } + + if(!flipper_format_read_uint32( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, &tmp_uint32, 1)) { + tmp_uint32 = AutomationKeyboardLayoutQWERTY; + } + + plugin_state->automation_kb_layout = tmp_uint32; + + if(!flipper_format_rewind(fff_data_file)) { + break; + } + if(!flipper_format_read_uint32(fff_data_file, TOTP_CONFIG_KEY_FONT, &tmp_uint32, 1)) { tmp_uint32 = 0; } @@ -425,7 +512,9 @@ bool totp_config_file_load(PluginState* const plugin_state) { plugin_state->config_file_context->config_file = fff_data_file; plugin_state->config_file_context->token_info_iterator_context = totp_token_info_iterator_alloc( - storage, plugin_state->config_file_context->config_file, plugin_state->iv); + storage, + plugin_state->config_file_context->config_file, + &plugin_state->crypto_settings); result = true; } while(false); @@ -438,21 +527,39 @@ bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state) flipper_format_rewind(config_file); bool update_result = false; do { + uint32_t tmp_uint32 = plugin_state->crypto_settings.crypto_version; + if(!flipper_format_insert_or_update_uint32( + config_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &tmp_uint32, 1)) { + break; + } + + tmp_uint32 = plugin_state->crypto_settings.crypto_key_slot; + if(!flipper_format_insert_or_update_uint32( + config_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &tmp_uint32, 1)) { + break; + } + if(!flipper_format_insert_or_update_hex( - config_file, TOTP_CONFIG_KEY_BASE_IV, plugin_state->base_iv, TOTP_IV_SIZE)) { + config_file, + TOTP_CONFIG_KEY_SALT, + &plugin_state->crypto_settings.salt[0], + CRYPTO_SALT_LENGTH)) { break; } if(!flipper_format_insert_or_update_hex( config_file, TOTP_CONFIG_KEY_CRYPTO_VERIFY, - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length)) { + plugin_state->crypto_settings.crypto_verify_data, + plugin_state->crypto_settings.crypto_verify_data_length)) { break; } if(!flipper_format_insert_or_update_bool( - config_file, TOTP_CONFIG_KEY_PINSET, &plugin_state->pin_set, 1)) { + config_file, + TOTP_CONFIG_KEY_PINSET, + &plugin_state->crypto_settings.pin_required, + 1)) { break; } @@ -480,6 +587,7 @@ void totp_config_file_reset(PluginState* const plugin_state) { bool totp_config_file_update_encryption( PluginState* plugin_state, + uint8_t new_crypto_key_slot, const uint8_t* new_pin, uint8_t new_pin_length) { FlipperFormat* config_file = plugin_state->config_file_context->config_file; @@ -489,23 +597,28 @@ bool totp_config_file_update_encryption( return false; } - uint8_t old_iv[TOTP_IV_SIZE]; - memcpy(&old_iv[0], &plugin_state->iv[0], TOTP_IV_SIZE); - - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); - memset(&plugin_state->base_iv[0], 0, TOTP_IV_SIZE); - if(plugin_state->crypto_verify_data != NULL) { - free(plugin_state->crypto_verify_data); - plugin_state->crypto_verify_data = NULL; + if(!totp_crypto_check_key_slot(new_crypto_key_slot)) { + return false; } - CryptoSeedIVResult seed_result = - totp_crypto_seed_iv(plugin_state, new_pin_length > 0 ? new_pin : NULL, new_pin_length); + CryptoSettings old_crypto_settings = plugin_state->crypto_settings; + + memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); + memset(&plugin_state->crypto_settings.salt[0], 0, CRYPTO_SALT_LENGTH); + if(plugin_state->crypto_settings.crypto_verify_data != NULL) { + free(plugin_state->crypto_settings.crypto_verify_data); + plugin_state->crypto_settings.crypto_verify_data = NULL; + } + + plugin_state->crypto_settings.crypto_key_slot = new_crypto_key_slot; + plugin_state->crypto_settings.crypto_version = CRYPTO_LATEST_VERSION; + + CryptoSeedIVResult seed_result = totp_crypto_seed_iv( + &plugin_state->crypto_settings, new_pin_length > 0 ? new_pin : NULL, new_pin_length); if(seed_result & CryptoSeedIVResultFlagSuccess && - seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { - if(!totp_config_file_update_crypto_signatures(plugin_state)) { - return false; - } + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData && + !totp_config_file_update_crypto_signatures(plugin_state)) { + return false; } else if(seed_result == CryptoSeedIVResultFailed) { return false; } @@ -552,12 +665,15 @@ bool totp_config_file_update_encryption( size_t plain_token_length; uint8_t* plain_token = totp_crypto_decrypt( - encrypted_token, secret_bytes_count, &old_iv[0], &plain_token_length); + encrypted_token, secret_bytes_count, &old_crypto_settings, &plain_token_length); free(encrypted_token); size_t encrypted_token_length; encrypted_token = totp_crypto_encrypt( - plain_token, plain_token_length, &plugin_state->iv[0], &encrypted_token_length); + plain_token, + plain_token_length, + &plugin_state->crypto_settings, + &encrypted_token_length); memset_s(plain_token, plain_token_length, 0, plain_token_length); free(plain_token); @@ -588,6 +704,36 @@ bool totp_config_file_update_encryption( return result; } +bool totp_config_file_ensure_latest_encryption( + PluginState* plugin_state, + const uint8_t* pin, + uint8_t pin_length) { + bool result = true; + if(plugin_state->crypto_settings.crypto_version < CRYPTO_LATEST_VERSION) { + FURI_LOG_I(LOGGING_TAG, "Migration to crypto v%d is needed", CRYPTO_LATEST_VERSION); + char* backup_path = totp_config_file_backup(plugin_state); + if(backup_path != NULL) { + free(backup_path); + uint8_t crypto_key_slot = plugin_state->crypto_settings.crypto_key_slot; + if(!totp_crypto_check_key_slot(crypto_key_slot)) { + crypto_key_slot = DEFAULT_CRYPTO_KEY_SLOT; + } + + result = + totp_config_file_update_encryption(plugin_state, crypto_key_slot, pin, pin_length); + FURI_LOG_I( + LOGGING_TAG, + "Migration to crypto v%d is done. Result: %d", + CRYPTO_LATEST_VERSION, + result); + } else { + result = false; + } + } + + return result; +} + TokenInfoIteratorContext* totp_config_get_token_iterator_context(const PluginState* plugin_state) { return plugin_state->config_file_context->token_info_iterator_context; -} +} \ No newline at end of file diff --git a/applications/external/totp/services/config/config.h b/applications/external/totp/services/config/config.h index d2fe957c6..38bc06ba2 100644 --- a/applications/external/totp/services/config/config.h +++ b/applications/external/totp/services/config/config.h @@ -73,15 +73,29 @@ void totp_config_file_close(PluginState* const plugin_state); /** * @brief Updates config file encryption by re-encrypting it using new user's PIN and new randomly generated IV * @param plugin_state application state + * @param new_crypto_key_slot new crypto key slot to be used * @param new_pin new user's PIN * @param new_pin_length new user's PIN length * @return \c true if config file encryption successfully updated; \c false otherwise */ bool totp_config_file_update_encryption( PluginState* plugin_state, + uint8_t new_crypto_key_slot, const uint8_t* new_pin, uint8_t new_pin_length); +/** + * @brief Ensures application config file uses latest encryption and upgrades encryption if needed + * @param plugin_state application state + * @param pin user's PIN + * @param pin_length user's PIN length + * @return \c true if operation succeeded; \c false otherwise + */ +bool totp_config_file_ensure_latest_encryption( + PluginState* plugin_state, + const uint8_t* pin, + uint8_t pin_length); + /** * @brief Gets token info iterator context * @param plugin_state application state diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index ba6658fb5..97c8f0d0a 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -2,9 +2,9 @@ #include -#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("authenticator") +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/totp") #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION (6) +#define CONFIG_FILE_ACTUAL_VERSION (9) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" @@ -14,8 +14,11 @@ #define TOTP_CONFIG_KEY_TOKEN_DURATION "TokenDuration" #define TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES "TokenAutomationFeatures" #define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto" -#define TOTP_CONFIG_KEY_BASE_IV "BaseIV" +#define TOTP_CONFIG_KEY_SALT "Salt" #define TOTP_CONFIG_KEY_PINSET "PinIsSet" #define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod" #define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod" +#define TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT "AutomationKbLayout" #define TOTP_CONFIG_KEY_FONT "Font" +#define TOTP_CONFIG_KEY_CRYPTO_VERSION "CryptoVersion" +#define TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT "CryptoKeySlot" diff --git a/applications/external/totp/services/config/migrations/common_migration.c b/applications/external/totp/services/config/migrations/common_migration.c index e8537da0b..33a441e72 100644 --- a/applications/external/totp/services/config/migrations/common_migration.c +++ b/applications/external/totp/services/config/migrations/common_migration.c @@ -1,8 +1,11 @@ #include "common_migration.h" #include "../constants.h" #include "../../../types/token_info.h" +#include "../../../types/automation_kb_layout.h" #include +#define TOTP_OLD_CONFIG_KEY_BASE_IV "BaseIV" + bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, FlipperFormat* fff_backup_data_file) { @@ -17,8 +20,35 @@ bool totp_config_migrate_to_latest( break; } - if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str)) { - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str); + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, temp_str); + } else { + uint32_t old_crypto_version = 1; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_VERSION, &old_crypto_version, 1); + } + + flipper_format_rewind(fff_backup_data_file); + + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, temp_str); + } else { + uint32_t default_old_key_slot = 2; + flipper_format_write_uint32( + fff_data_file, TOTP_CONFIG_KEY_CRYPTO_KEY_SLOT, &default_old_key_slot, 1); + } + + flipper_format_rewind(fff_backup_data_file); + + if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_SALT, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_SALT, temp_str); + } else if( + flipper_format_rewind(fff_backup_data_file) && + flipper_format_read_string( + fff_backup_data_file, TOTP_OLD_CONFIG_KEY_BASE_IV, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_SALT, temp_str); } flipper_format_rewind(fff_backup_data_file); @@ -68,6 +98,21 @@ bool totp_config_migrate_to_latest( flipper_format_rewind(fff_backup_data_file); + if(flipper_format_read_string( + fff_backup_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, temp_str)) { + flipper_format_write_string( + fff_data_file, TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, temp_str); + } else { + uint32_t default_automation_kb_layout = AutomationKeyboardLayoutQWERTY; + flipper_format_write_uint32( + fff_data_file, + TOTP_CONFIG_KEY_AUTOMATION_KB_LAYOUT, + &default_automation_kb_layout, + 1); + } + + flipper_format_rewind(fff_backup_data_file); + while(true) { if(!flipper_format_read_string( fff_backup_data_file, TOTP_CONFIG_KEY_TOKEN_NAME, temp_str)) { diff --git a/applications/external/totp/services/config/token_info_iterator.c b/applications/external/totp/services/config/token_info_iterator.c index 1f4a75776..3d47b9616 100644 --- a/applications/external/totp/services/config/token_info_iterator.c +++ b/applications/external/totp/services/config/token_info_iterator.c @@ -4,9 +4,10 @@ #include #include #include "../../types/common.h" +#include "../../types/crypto_settings.h" #define CONFIG_FILE_PART_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf.part" -#define STREAM_COPY_BUFFER_SIZE 128 +#define STREAM_COPY_BUFFER_SIZE (128) struct TokenInfoIteratorContext { size_t total_count; @@ -15,7 +16,7 @@ struct TokenInfoIteratorContext { size_t last_seek_index; TokenInfo* current_token; FlipperFormat* config_file; - uint8_t* iv; + CryptoSettings* crypto_settings; Storage* storage; }; @@ -237,8 +238,10 @@ static bool return result; } -TokenInfoIteratorContext* - totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv) { +TokenInfoIteratorContext* totp_token_info_iterator_alloc( + Storage* storage, + FlipperFormat* config_file, + CryptoSettings* crypto_settings) { Stream* stream = flipper_format_get_raw_stream(config_file); stream_rewind(stream); size_t tokens_count = 0; @@ -256,7 +259,7 @@ TokenInfoIteratorContext* context->total_count = tokens_count; context->current_token = token_info_alloc(); context->config_file = config_file; - context->iv = iv; + context->crypto_settings = crypto_settings; context->storage = storage; return context; } @@ -453,7 +456,7 @@ bool totp_token_info_iterator_go_to(TokenInfoIteratorContext* context, size_t to furi_string_get_cstr(temp_str), furi_string_size(temp_str), PlainTokenSecretEncodingBase32, - context->iv)) { + context->crypto_settings)) { FURI_LOG_W( LOGGING_TAG, "Token \"%s\" has plain secret", @@ -544,4 +547,6 @@ void totp_token_info_iterator_attach_to_config_file( TokenInfoIteratorContext* context, FlipperFormat* config_file) { context->config_file = config_file; + Stream* stream = flipper_format_get_raw_stream(context->config_file); + stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart); } \ No newline at end of file diff --git a/applications/external/totp/services/config/token_info_iterator.h b/applications/external/totp/services/config/token_info_iterator.h index 7e9a65853..ce4d8c72b 100644 --- a/applications/external/totp/services/config/token_info_iterator.h +++ b/applications/external/totp/services/config/token_info_iterator.h @@ -28,11 +28,13 @@ enum TotpIteratorUpdateTokenResults { * @brief Initializes a new token info iterator * @param storage storage reference * @param config_file config file to use - * @param iv initialization vector (IV) to be used for encryption\decryption + * @param crypto_settings crypto settings * @return Token info iterator context */ -TokenInfoIteratorContext* - totp_token_info_iterator_alloc(Storage* storage, FlipperFormat* config_file, uint8_t* iv); +TokenInfoIteratorContext* totp_token_info_iterator_alloc( + Storage* storage, + FlipperFormat* config_file, + CryptoSettings* crypto_settings); /** * @brief Navigates iterator to the token with given index diff --git a/applications/external/totp/services/crypto/common_types.h b/applications/external/totp/services/crypto/common_types.h new file mode 100644 index 000000000..fb8e4c83c --- /dev/null +++ b/applications/external/totp/services/crypto/common_types.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +typedef uint8_t CryptoSeedIVResult; + +enum CryptoSeedIVResults { + + /** + * @brief IV seeding operation failed + */ + CryptoSeedIVResultFailed = 0b00, + + /** + * @brief IV seeding operation succeeded + */ + CryptoSeedIVResultFlagSuccess = 0b01, + + /** + * @brief As a part of IV seeding operation new crypto verify data has been generated + */ + CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10 +}; \ No newline at end of file diff --git a/applications/external/totp/services/crypto/constants.h b/applications/external/totp/services/crypto/constants.h new file mode 100644 index 000000000..bb3affbd0 --- /dev/null +++ b/applications/external/totp/services/crypto/constants.h @@ -0,0 +1,14 @@ +#pragma once + +#include "polyfills.h" + +#define CRYPTO_IV_LENGTH (16) +#define CRYPTO_SALT_LENGTH (16) + +// According to this explanation: https://github.com/flipperdevices/flipperzero-firmware/issues/2885#issuecomment-1646664666 +// disabling usage of any key which is "the same across all devices" +#define ACCEPTABLE_CRYPTO_KEY_SLOT_START FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START +#define ACCEPTABLE_CRYPTO_KEY_SLOT_END FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END + +#define DEFAULT_CRYPTO_KEY_SLOT ACCEPTABLE_CRYPTO_KEY_SLOT_START +#define CRYPTO_LATEST_VERSION (3) \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_facade.c b/applications/external/totp/services/crypto/crypto_facade.c new file mode 100644 index 000000000..29e588073 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_facade.c @@ -0,0 +1,118 @@ +#include "crypto_facade.h" +#include "../../config/app/config.h" +#include +#include +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED +#include "crypto_v1.h" +#endif +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED +#include "crypto_v2.h" +#endif +#include "crypto_v3.h" +#include "constants.h" + +bool totp_crypto_check_key_slot(uint8_t key_slot) { + uint8_t empty_iv[CRYPTO_IV_LENGTH] = {0}; + if(key_slot < ACCEPTABLE_CRYPTO_KEY_SLOT_START || key_slot > ACCEPTABLE_CRYPTO_KEY_SLOT_END) { + return false; + } + + return furi_hal_crypto_enclave_ensure_key(key_slot) && + furi_hal_crypto_enclave_load_key(key_slot, empty_iv) && + furi_hal_crypto_enclave_unload_key(key_slot); +} + +uint8_t* totp_crypto_encrypt( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length) { +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED + if(crypto_settings->crypto_version == 1) { + return totp_crypto_encrypt_v1( + plain_data, plain_data_length, crypto_settings, encrypted_data_length); + } +#endif + +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED + if(crypto_settings->crypto_version == 2) { + return totp_crypto_encrypt_v2( + plain_data, plain_data_length, crypto_settings, encrypted_data_length); + } +#endif + + if(crypto_settings->crypto_version == 3) { + return totp_crypto_encrypt_v3( + plain_data, plain_data_length, crypto_settings, encrypted_data_length); + } + + furi_crash("Unsupported crypto version"); +} + +uint8_t* totp_crypto_decrypt( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length) { +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED + if(crypto_settings->crypto_version == 1) { + return totp_crypto_decrypt_v1( + encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length); + } +#endif + +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED + if(crypto_settings->crypto_version == 2) { + return totp_crypto_decrypt_v2( + encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length); + } +#endif + + if(crypto_settings->crypto_version == 3) { + return totp_crypto_decrypt_v3( + encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length); + } + + furi_crash("Unsupported crypto version"); +} + +CryptoSeedIVResult + totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length) { +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED + if(crypto_settings->crypto_version == 1) { + return totp_crypto_seed_iv_v1(crypto_settings, pin, pin_length); + } +#endif + +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED + if(crypto_settings->crypto_version == 2) { + return totp_crypto_seed_iv_v2(crypto_settings, pin, pin_length); + } +#endif + + if(crypto_settings->crypto_version == 3) { + return totp_crypto_seed_iv_v3(crypto_settings, pin, pin_length); + } + + furi_crash("Unsupported crypto version"); +} + +bool totp_crypto_verify_key(const CryptoSettings* crypto_settings) { +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED + if(crypto_settings->crypto_version == 1) { + return totp_crypto_verify_key_v1(crypto_settings); + } +#endif + +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED + if(crypto_settings->crypto_version == 2) { + return totp_crypto_verify_key_v2(crypto_settings); + } +#endif + + if(crypto_settings->crypto_version == 3) { + return totp_crypto_verify_key_v3(crypto_settings); + } + + furi_crash("Unsupported crypto version"); +} \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto.h b/applications/external/totp/services/crypto/crypto_facade.h similarity index 58% rename from applications/external/totp/services/crypto/crypto.h rename to applications/external/totp/services/crypto/crypto_facade.h index ab27191a8..bbcbf7c00 100644 --- a/applications/external/totp/services/crypto/crypto.h +++ b/applications/external/totp/services/crypto/crypto_facade.h @@ -1,68 +1,59 @@ #pragma once -#include "../../types/plugin_state.h" +#include +#include +#include +#include "../../types/crypto_settings.h" +#include "common_types.h" -typedef uint8_t CryptoSeedIVResult; - -enum CryptoSeedIVResults { - - /** - * @brief IV seeding operation failed - */ - CryptoSeedIVResultFailed = 0b00, - - /** - * @brief IV seeding operation succeeded - */ - CryptoSeedIVResultFlagSuccess = 0b01, - - /** - * @brief As a part of IV seeding operation new crypto verify data has been generated - */ - CryptoSeedIVResultFlagNewCryptoVerifyData = 0b10 -}; +/** + * @brief Checks whether key slot can be used for encryption purposes + * @param key_slot key slot index + * @return \c true if key slot can be used for encryption; \c false otherwise + */ +bool totp_crypto_check_key_slot(uint8_t key_slot); /** * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) * @param plain_data plain data to be encrypted * @param plain_data_length plain data length - * @param iv initialization vector (IV) to be used to encrypt plain data + * @param crypto_settings crypto settings * @param[out] encrypted_data_length encrypted data length * @return Encrypted data */ uint8_t* totp_crypto_encrypt( const uint8_t* plain_data, const size_t plain_data_length, - const uint8_t* iv, + const CryptoSettings* crypto_settings, size_t* encrypted_data_length); /** * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) * @param encrypted_data encrypted data to be decrypted * @param encrypted_data_length encrypted data length - * @param iv initialization vector (IV) to be used to encrypt plain data + * @param crypto_settings crypto settings * @param[out] decrypted_data_length decrypted data length * @return Decrypted data */ uint8_t* totp_crypto_decrypt( const uint8_t* encrypted_data, const size_t encrypted_data_length, - const uint8_t* iv, + const CryptoSettings* crypto_settings, size_t* decrypted_data_length); /** * @brief Seed initialization vector (IV) using user's PIN - * @param plugin_state application state + * @param crypto_settings crypto settings * @param pin user's PIN * @param pin_length user's PIN length * @return Results of seeding IV */ CryptoSeedIVResult - totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length); + totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length); /** * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption - * @param plugin_state application state + * @param crypto_settings crypto settings * @return \c true if cryptographic information is valid; \c false otherwise */ -bool totp_crypto_verify_key(const PluginState* plugin_state); \ No newline at end of file +bool totp_crypto_verify_key(const CryptoSettings* crypto_settings); diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto_v1.c similarity index 61% rename from applications/external/totp/services/crypto/crypto.c rename to applications/external/totp/services/crypto/crypto_v1.c index 95ddc3210..b6ead80b1 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto_v1.c @@ -1,20 +1,25 @@ -#include "crypto.h" +#include "crypto_v1.h" +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED +#include +#include #include #include #include #include "../../types/common.h" #include "memset_s.h" +#include "polyfills.h" #define CRYPTO_KEY_SLOT (2) #define CRYPTO_VERIFY_KEY_LENGTH (16) #define CRYPTO_ALIGNMENT_FACTOR (16) +#define TOTP_IV_SIZE (16) static const char* CRYPTO_VERIFY_KEY = "FFF_Crypto_pass"; -uint8_t* totp_crypto_encrypt( +uint8_t* totp_crypto_encrypt_v1( const uint8_t* plain_data, const size_t plain_data_length, - const uint8_t* iv, + const CryptoSettings* crypto_settings, size_t* encrypted_data_length) { uint8_t* encrypted_data; size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR; @@ -29,9 +34,9 @@ uint8_t* totp_crypto_encrypt( furi_check(encrypted_data != NULL); *encrypted_data_length = plain_data_aligned_length; - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_enclave_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(CRYPTO_KEY_SLOT); memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length); free(plain_data_aligned); @@ -40,37 +45,39 @@ uint8_t* totp_crypto_encrypt( furi_check(encrypted_data != NULL); *encrypted_data_length = plain_data_length; - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_enclave_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(CRYPTO_KEY_SLOT); } return encrypted_data; } -uint8_t* totp_crypto_decrypt( +uint8_t* totp_crypto_decrypt_v1( const uint8_t* encrypted_data, const size_t encrypted_data_length, - const uint8_t* iv, + const CryptoSettings* crypto_settings, size_t* decrypted_data_length) { *decrypted_data_length = encrypted_data_length; uint8_t* decrypted_data = malloc(*decrypted_data_length); furi_check(decrypted_data != NULL); - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, iv); + furi_hal_crypto_enclave_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(CRYPTO_KEY_SLOT); return decrypted_data; } -CryptoSeedIVResult - totp_crypto_seed_iv(PluginState* plugin_state, const uint8_t* pin, uint8_t pin_length) { +CryptoSeedIVResult totp_crypto_seed_iv_v1( + CryptoSettings* crypto_settings, + const uint8_t* pin, + uint8_t pin_length) { CryptoSeedIVResult result; - if(plugin_state->crypto_verify_data == NULL) { + if(crypto_settings->crypto_verify_data == NULL) { FURI_LOG_I(LOGGING_TAG, "Generating new IV"); - furi_hal_random_fill_buf(&plugin_state->base_iv[0], TOTP_IV_SIZE); + furi_hal_random_fill_buf(&crypto_settings->salt[0], CRYPTO_SALT_LENGTH); } - memcpy(&plugin_state->iv[0], &plugin_state->base_iv[0], TOTP_IV_SIZE); + memcpy(&crypto_settings->iv[0], &crypto_settings->salt[0], TOTP_IV_SIZE); if(pin != NULL && pin_length > 0) { uint8_t max_i; if(pin_length > TOTP_IV_SIZE) { @@ -80,7 +87,7 @@ CryptoSeedIVResult } for(uint8_t i = 0; i < max_i; i++) { - plugin_state->iv[i] = plugin_state->iv[i] ^ (uint8_t)(pin[i] * (i + 1)); + crypto_settings->iv[i] = crypto_settings->iv[i] ^ (uint8_t)(pin[i] * (i + 1)); } } else { uint8_t max_i; @@ -93,24 +100,24 @@ CryptoSeedIVResult const uint8_t* uid = (const uint8_t*)UID64_BASE; //-V566 for(uint8_t i = 0; i < max_i; i++) { - plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; + crypto_settings->iv[i] = crypto_settings->iv[i] ^ uid[i]; } } result = CryptoSeedIVResultFlagSuccess; - if(plugin_state->crypto_verify_data == NULL) { + if(crypto_settings->crypto_verify_data == NULL) { FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); - plugin_state->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); - furi_check(plugin_state->crypto_verify_data != NULL); - plugin_state->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; + crypto_settings->crypto_verify_data = malloc(CRYPTO_VERIFY_KEY_LENGTH); + furi_check(crypto_settings->crypto_verify_data != NULL); + crypto_settings->crypto_verify_data_length = CRYPTO_VERIFY_KEY_LENGTH; - plugin_state->crypto_verify_data = totp_crypto_encrypt( + crypto_settings->crypto_verify_data = totp_crypto_encrypt_v1( (const uint8_t*)CRYPTO_VERIFY_KEY, CRYPTO_VERIFY_KEY_LENGTH, - &plugin_state->iv[0], - &plugin_state->crypto_verify_data_length); + crypto_settings, + &crypto_settings->crypto_verify_data_length); - plugin_state->pin_set = pin != NULL && pin_length > 0; + crypto_settings->pin_required = pin != NULL && pin_length > 0; result |= CryptoSeedIVResultFlagNewCryptoVerifyData; } @@ -118,12 +125,12 @@ CryptoSeedIVResult return result; } -bool totp_crypto_verify_key(const PluginState* plugin_state) { +bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings) { size_t decrypted_key_length; - uint8_t* decrypted_key = totp_crypto_decrypt( - plugin_state->crypto_verify_data, - plugin_state->crypto_verify_data_length, - &plugin_state->iv[0], + uint8_t* decrypted_key = totp_crypto_decrypt_v1( + crypto_settings->crypto_verify_data, + crypto_settings->crypto_verify_data_length, + crypto_settings, &decrypted_key_length); bool key_valid = true; @@ -134,4 +141,5 @@ bool totp_crypto_verify_key(const PluginState* plugin_state) { free(decrypted_key); return key_valid; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_v1.h b/applications/external/totp/services/crypto/crypto_v1.h new file mode 100644 index 000000000..859615ae3 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v1.h @@ -0,0 +1,55 @@ +#pragma once + +#include "../../config/app/config.h" +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED +#include +#include +#include +#include "../../types/crypto_settings.h" +#include "common_types.h" + +/** + * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) + * @param plain_data plain data to be encrypted + * @param plain_data_length plain data length + * @param crypto_settings crypto settings + * @param[out] encrypted_data_length encrypted data length + * @return Encrypted data + */ +uint8_t* totp_crypto_encrypt_v1( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length); + +/** + * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) + * @param encrypted_data encrypted data to be decrypted + * @param encrypted_data_length encrypted data length + * @param crypto_settings crypto settings + * @param[out] decrypted_data_length decrypted data length + * @return Decrypted data + */ +uint8_t* totp_crypto_decrypt_v1( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length); + +/** + * @brief Seed initialization vector (IV) using user's PIN + * @param crypto_settings crypto settings + * @param pin user's PIN + * @param pin_length user's PIN length + * @return Results of seeding IV + */ +CryptoSeedIVResult + totp_crypto_seed_iv_v1(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length); + +/** + * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption + * @param crypto_settings crypto settings + * @return \c true if cryptographic information is valid; \c false otherwise + */ +bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings); +#endif diff --git a/applications/external/totp/services/crypto/crypto_v2.c b/applications/external/totp/services/crypto/crypto_v2.c new file mode 100644 index 000000000..498995619 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v2.c @@ -0,0 +1,190 @@ +#include "crypto_v2.h" +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED +#include +#include +#include +#include +#include +#include "../../types/common.h" +#include "../../config/wolfssl/config.h" +#include +#include "memset_s.h" +#include "constants.h" +#include "polyfills.h" + +#define CRYPTO_ALIGNMENT_FACTOR (16) + +static const uint8_t* get_device_uid() { + return (const uint8_t*)UID64_BASE; //-V566 +} + +static uint8_t get_device_uid_length() { + return furi_hal_version_uid_size(); +} + +static const uint8_t* get_crypto_verify_key() { + return get_device_uid(); +} + +static uint8_t get_crypto_verify_key_length() { + return get_device_uid_length(); +} + +uint8_t* totp_crypto_encrypt_v2( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length) { + uint8_t* encrypted_data; + size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR; + if(remain) { + size_t plain_data_aligned_length = plain_data_length - remain + CRYPTO_ALIGNMENT_FACTOR; + uint8_t* plain_data_aligned = malloc(plain_data_aligned_length); + furi_check(plain_data_aligned != NULL); + memset(plain_data_aligned, 0, plain_data_aligned_length); + memcpy(plain_data_aligned, plain_data, plain_data_length); + + encrypted_data = malloc(plain_data_aligned_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_aligned_length; + + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: enclave_load_key"); + furi_check( + furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length), + "Encryption failed: encrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: enclave_unload_key"); + + memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length); + free(plain_data_aligned); + } else { + encrypted_data = malloc(plain_data_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_length; + + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: store_load_key"); + furi_check( + furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length), + "Encryption failed: encrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: store_unload_key"); + } + + return encrypted_data; +} + +uint8_t* totp_crypto_decrypt_v2( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length) { + *decrypted_data_length = encrypted_data_length; + uint8_t* decrypted_data = malloc(*decrypted_data_length); + furi_check(decrypted_data != NULL); + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Decryption failed: enclave_load_key"); + furi_check( + furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length), + "Decryption failed: decrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Decryption failed: enclave_unload_key"); + return decrypted_data; +} + +CryptoSeedIVResult totp_crypto_seed_iv_v2( + CryptoSettings* crypto_settings, + const uint8_t* pin, + uint8_t pin_length) { + CryptoSeedIVResult result; + if(crypto_settings->crypto_verify_data == NULL) { + FURI_LOG_I(LOGGING_TAG, "Generating new salt"); + furi_hal_random_fill_buf(&crypto_settings->salt[0], CRYPTO_SALT_LENGTH); + } + + const uint8_t* device_uid = get_device_uid(); + uint8_t device_uid_length = get_device_uid_length(); + + uint8_t hmac_key_length = device_uid_length; + if(pin != NULL && pin_length > 0) { + hmac_key_length += pin_length; + } + + uint8_t* hmac_key = malloc(hmac_key_length); + furi_check(hmac_key != NULL); + + memcpy(hmac_key, device_uid, device_uid_length); + + if(pin != NULL && pin_length > 0) { + memcpy(hmac_key + device_uid_length, pin, pin_length); + } + + uint8_t hmac[WC_SHA512_DIGEST_SIZE] = {0}; + + Hmac hmac_context; + wc_HmacSetKey(&hmac_context, WC_SHA512, hmac_key, hmac_key_length); + wc_HmacUpdate(&hmac_context, &crypto_settings->salt[0], CRYPTO_SALT_LENGTH); + int hmac_result_code = wc_HmacFinal(&hmac_context, &hmac[0]); + wc_HmacFree(&hmac_context); + + memset_s(hmac_key, hmac_key_length, 0, hmac_key_length); + free(hmac_key); + + if(hmac_result_code == 0) { + uint8_t offset = + hmac[WC_SHA512_DIGEST_SIZE - 1] % (WC_SHA512_DIGEST_SIZE - CRYPTO_IV_LENGTH - 1); + memcpy(&crypto_settings->iv[0], &hmac[offset], CRYPTO_IV_LENGTH); + + result = CryptoSeedIVResultFlagSuccess; + if(crypto_settings->crypto_verify_data == NULL) { + const uint8_t* crypto_vkey = get_crypto_verify_key(); + uint8_t crypto_vkey_length = get_crypto_verify_key_length(); + FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); + crypto_settings->crypto_verify_data = malloc(crypto_vkey_length); + furi_check(crypto_settings->crypto_verify_data != NULL); + crypto_settings->crypto_verify_data_length = crypto_vkey_length; + + crypto_settings->crypto_verify_data = totp_crypto_encrypt_v2( + crypto_vkey, + crypto_vkey_length, + crypto_settings, + &crypto_settings->crypto_verify_data_length); + + crypto_settings->pin_required = pin != NULL && pin_length > 0; + + result |= CryptoSeedIVResultFlagNewCryptoVerifyData; + } + } else { + result = CryptoSeedIVResultFailed; + } + + return result; +} + +bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings) { + size_t decrypted_key_length; + uint8_t* decrypted_key = totp_crypto_decrypt_v2( + crypto_settings->crypto_verify_data, + crypto_settings->crypto_verify_data_length, + crypto_settings, + &decrypted_key_length); + + const uint8_t* crypto_vkey = get_crypto_verify_key(); + uint8_t crypto_vkey_length = get_crypto_verify_key_length(); + bool key_valid = true; + for(uint8_t i = 0; i < crypto_vkey_length && key_valid; i++) { + if(decrypted_key[i] != crypto_vkey[i]) key_valid = false; + } + + free(decrypted_key); + + return key_valid; +} +#endif diff --git a/applications/external/totp/services/crypto/crypto_v2.h b/applications/external/totp/services/crypto/crypto_v2.h new file mode 100644 index 000000000..c21edae20 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v2.h @@ -0,0 +1,55 @@ +#pragma once + +#include "../../config/app/config.h" +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED +#include +#include +#include +#include "../../types/crypto_settings.h" +#include "common_types.h" + +/** + * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) + * @param plain_data plain data to be encrypted + * @param plain_data_length plain data length + * @param crypto_settings crypto settings + * @param[out] encrypted_data_length encrypted data length + * @return Encrypted data + */ +uint8_t* totp_crypto_encrypt_v2( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length); + +/** + * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) + * @param encrypted_data encrypted data to be decrypted + * @param encrypted_data_length encrypted data length + * @param crypto_settings crypto settings + * @param[out] decrypted_data_length decrypted data length + * @return Decrypted data + */ +uint8_t* totp_crypto_decrypt_v2( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length); + +/** + * @brief Seed initialization vector (IV) using user's PIN + * @param crypto_settings crypto settings + * @param pin user's PIN + * @param pin_length user's PIN length + * @return Results of seeding IV + */ +CryptoSeedIVResult + totp_crypto_seed_iv_v2(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length); + +/** + * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption + * @param crypto_settings crypto settings + * @return \c true if cryptographic information is valid; \c false otherwise + */ +bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings); +#endif diff --git a/applications/external/totp/services/crypto/crypto_v3.c b/applications/external/totp/services/crypto/crypto_v3.c new file mode 100644 index 000000000..79ecb3d72 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v3.c @@ -0,0 +1,195 @@ +#include "crypto_v3.h" +#include +#include +#include +#include +#include +#include "../../types/common.h" +#include "../../config/wolfssl/config.h" +#include +#include +#include "memset_s.h" +#include "constants.h" +#include "polyfills.h" + +#define CRYPTO_ALIGNMENT_FACTOR (16) +#define PBKDF2_ITERATIONS_COUNT (200) + +static const uint8_t* get_device_uid() { + return (const uint8_t*)UID64_BASE; //-V566 +} + +static uint8_t get_device_uid_length() { + return furi_hal_version_uid_size(); +} + +static const uint8_t* get_crypto_verify_key() { + return get_device_uid(); +} + +static uint8_t get_crypto_verify_key_length() { + return get_device_uid_length(); +} + +uint8_t* totp_crypto_encrypt_v3( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length) { + uint8_t* encrypted_data; + size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR; + if(remain) { + size_t plain_data_aligned_length = plain_data_length - remain + CRYPTO_ALIGNMENT_FACTOR; + uint8_t* plain_data_aligned = malloc(plain_data_aligned_length); + furi_check(plain_data_aligned != NULL); + memset(plain_data_aligned, 0, plain_data_aligned_length); + memcpy(plain_data_aligned, plain_data, plain_data_length); + + encrypted_data = malloc(plain_data_aligned_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_aligned_length; + + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: enclave_load_key"); + furi_check( + furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length), + "Encryption failed: encrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: enclave_unload_key"); + + memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length); + free(plain_data_aligned); + } else { + encrypted_data = malloc(plain_data_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_length; + + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: enclave_load_key"); + furi_check( + furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length), + "Encryption failed: encrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: enclave_unload_key"); + } + + return encrypted_data; +} + +uint8_t* totp_crypto_decrypt_v3( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length) { + *decrypted_data_length = encrypted_data_length; + uint8_t* decrypted_data = malloc(*decrypted_data_length); + furi_check(decrypted_data != NULL); + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Decryption failed: enclave_load_key"); + furi_check( + furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length), + "Decryption failed: decrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Decryption failed: enclave_unload_key"); + return decrypted_data; +} + +CryptoSeedIVResult totp_crypto_seed_iv_v3( + CryptoSettings* crypto_settings, + const uint8_t* pin, + uint8_t pin_length) { + CryptoSeedIVResult result; + if(crypto_settings->crypto_verify_data == NULL) { + FURI_LOG_I(LOGGING_TAG, "Generating new salt"); + furi_hal_random_fill_buf(&crypto_settings->salt[0], CRYPTO_SALT_LENGTH); + } + + const uint8_t* device_uid = get_device_uid(); + uint8_t device_uid_length = get_device_uid_length(); + + uint8_t pbkdf_key_length = device_uid_length; + if(pin != NULL && pin_length > 0) { + pbkdf_key_length += pin_length; + } + + uint8_t* pbkdf_key = malloc(pbkdf_key_length); + furi_check(pbkdf_key != NULL); + + memcpy(pbkdf_key, device_uid, device_uid_length); + + if(pin != NULL && pin_length > 0) { + memcpy(pbkdf_key + device_uid_length, pin, pin_length); + } + + uint8_t pbkdf_output[WC_SHA512_DIGEST_SIZE] = {0}; + + int pbkdf_result_code = wc_PBKDF2( + &pbkdf_output[0], + pbkdf_key, + pbkdf_key_length, + &crypto_settings->salt[0], + CRYPTO_SALT_LENGTH, + PBKDF2_ITERATIONS_COUNT, + WC_SHA512_DIGEST_SIZE, + WC_SHA512); + + memset_s(pbkdf_key, pbkdf_key_length, 0, pbkdf_key_length); + free(pbkdf_key); + + if(pbkdf_result_code == 0) { + uint8_t offset = pbkdf_output[WC_SHA512_DIGEST_SIZE - 1] % + (WC_SHA512_DIGEST_SIZE - CRYPTO_IV_LENGTH - 1); + memcpy(&crypto_settings->iv[0], &pbkdf_output[offset], CRYPTO_IV_LENGTH); + result = CryptoSeedIVResultFlagSuccess; + if(crypto_settings->crypto_verify_data == NULL) { + const uint8_t* crypto_vkey = get_crypto_verify_key(); + uint8_t crypto_vkey_length = get_crypto_verify_key_length(); + FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); + crypto_settings->crypto_verify_data = malloc(crypto_vkey_length); + furi_check(crypto_settings->crypto_verify_data != NULL); + crypto_settings->crypto_verify_data_length = crypto_vkey_length; + + crypto_settings->crypto_verify_data = totp_crypto_encrypt_v3( + crypto_vkey, + crypto_vkey_length, + crypto_settings, + &crypto_settings->crypto_verify_data_length); + + crypto_settings->pin_required = pin != NULL && pin_length > 0; + + result |= CryptoSeedIVResultFlagNewCryptoVerifyData; + } + } else { + result = CryptoSeedIVResultFailed; + } + + memset_s(&pbkdf_output[0], WC_SHA512_DIGEST_SIZE, 0, WC_SHA512_DIGEST_SIZE); + + return result; +} + +bool totp_crypto_verify_key_v3(const CryptoSettings* crypto_settings) { + size_t decrypted_key_length; + uint8_t* decrypted_key = totp_crypto_decrypt_v3( + crypto_settings->crypto_verify_data, + crypto_settings->crypto_verify_data_length, + crypto_settings, + &decrypted_key_length); + + const uint8_t* crypto_vkey = get_crypto_verify_key(); + uint8_t crypto_vkey_length = get_crypto_verify_key_length(); + bool key_valid = true; + for(uint8_t i = 0; i < crypto_vkey_length && key_valid; i++) { + if(decrypted_key[i] != crypto_vkey[i]) key_valid = false; + } + + free(decrypted_key); + + return key_valid; +} \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_v3.h b/applications/external/totp/services/crypto/crypto_v3.h new file mode 100644 index 000000000..39c718aab --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v3.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include "../../types/crypto_settings.h" +#include "common_types.h" + +/** + * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) + * @param plain_data plain data to be encrypted + * @param plain_data_length plain data length + * @param crypto_settings crypto settings + * @param[out] encrypted_data_length encrypted data length + * @return Encrypted data + */ +uint8_t* totp_crypto_encrypt_v3( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length); + +/** + * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) + * @param encrypted_data encrypted data to be decrypted + * @param encrypted_data_length encrypted data length + * @param crypto_settings crypto settings + * @param[out] decrypted_data_length decrypted data length + * @return Decrypted data + */ +uint8_t* totp_crypto_decrypt_v3( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length); + +/** + * @brief Seed initialization vector (IV) using user's PIN + * @param crypto_settings crypto settings + * @param pin user's PIN + * @param pin_length user's PIN length + * @return Results of seeding IV + */ +CryptoSeedIVResult + totp_crypto_seed_iv_v3(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length); + +/** + * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption + * @param crypto_settings crypto settings + * @return \c true if cryptographic information is valid; \c false otherwise + */ +bool totp_crypto_verify_key_v3(const CryptoSettings* crypto_settings); \ No newline at end of file diff --git a/applications/external/totp/services/crypto/polyfills.h b/applications/external/totp/services/crypto/polyfills.h new file mode 100644 index 000000000..6e66d03d2 --- /dev/null +++ b/applications/external/totp/services/crypto/polyfills.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#ifndef FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START + +// FW Crypto API is outdated, let's polyfill it +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START (12u) +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END (100u) +#define furi_hal_crypto_enclave_ensure_key furi_hal_crypto_verify_key +#define furi_hal_crypto_enclave_load_key furi_hal_crypto_store_load_key +#define furi_hal_crypto_enclave_unload_key furi_hal_crypto_store_unload_key + +#endif \ No newline at end of file diff --git a/applications/external/totp/services/hmac/byteswap.c b/applications/external/totp/services/hmac/byteswap.c deleted file mode 100644 index e922deec0..000000000 --- a/applications/external/totp/services/hmac/byteswap.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "byteswap.h" - -uint32_t swap_uint32(uint32_t val) { - val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); - return (val << 16) | (val >> 16); -} - -uint64_t swap_uint64(uint64_t val) { - val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL); - val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL); - return (val << 32) | (val >> 32); -} diff --git a/applications/external/totp/services/hmac/byteswap.h b/applications/external/totp/services/hmac/byteswap.h deleted file mode 100644 index 2e3f1743f..000000000 --- a/applications/external/totp/services/hmac/byteswap.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -/** - * @brief Swap bytes in 32-bit value - * @param val value to swap bytes in - * @return Value with bytes swapped - */ -uint32_t swap_uint32(uint32_t val); - -/** - * @brief Swap bytes in 64-bit value - * @param val value to swap bytes in - * @return Value with bytes swapped - */ -uint64_t swap_uint64(uint64_t val); diff --git a/applications/external/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h deleted file mode 100644 index 3499cb800..000000000 --- a/applications/external/totp/services/hmac/hmac_common.h +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include "memxor.h" - -#define IPAD 0x36 -#define OPAD 0x5c - -/* Concatenate two preprocessor tokens. */ -#define _GLHMAC_CONCAT_(prefix, suffix) prefix##suffix -#define _GLHMAC_CONCAT(prefix, suffix) _GLHMAC_CONCAT_(prefix, suffix) - -#define HMAC_ALG _GLHMAC_CONCAT(sha, GL_HMAC_NAME) - -#define GL_HMAC_CTX _GLHMAC_CONCAT(HMAC_ALG, _ctx) -#define GL_HMAC_FN _GLHMAC_CONCAT(hmac_, HMAC_ALG) -#define GL_HMAC_FN_INIT _GLHMAC_CONCAT(HMAC_ALG, _init_ctx) -#define GL_HMAC_FN_BLOC _GLHMAC_CONCAT(HMAC_ALG, _process_block) -#define GL_HMAC_FN_PROC _GLHMAC_CONCAT(HMAC_ALG, _process_bytes) -#define GL_HMAC_FN_FINI _GLHMAC_CONCAT(HMAC_ALG, _finish_ctx) - -static void - hmac_hash(const void* key, size_t keylen, const void* in, size_t inlen, int pad, void* resbuf) { - struct GL_HMAC_CTX hmac_ctx; - char block[GL_HMAC_BLOCKSIZE]; - - memset(block, pad, sizeof block); - memxor(block, key, keylen); - - GL_HMAC_FN_INIT(&hmac_ctx); - GL_HMAC_FN_BLOC(block, sizeof block, &hmac_ctx); - GL_HMAC_FN_PROC(in, inlen, &hmac_ctx); - GL_HMAC_FN_FINI(&hmac_ctx, resbuf); -} - -int GL_HMAC_FN(const void* key, size_t keylen, const void* in, size_t inlen, void* resbuf) { - char optkeybuf[GL_HMAC_HASHSIZE]; - char innerhash[GL_HMAC_HASHSIZE]; - - /* Ensure key size is <= block size. */ - if(keylen > GL_HMAC_BLOCKSIZE) { - struct GL_HMAC_CTX keyhash; - - GL_HMAC_FN_INIT(&keyhash); - GL_HMAC_FN_PROC(key, keylen, &keyhash); - GL_HMAC_FN_FINI(&keyhash, optkeybuf); - - key = optkeybuf; - /* zero padding of the key to the block size - is implicit in the memxor. */ - keylen = sizeof optkeybuf; - } - - /* Compute INNERHASH from KEY and IN. */ - hmac_hash(key, keylen, in, inlen, IPAD, innerhash); - - /* Compute result from KEY and INNERHASH. */ - hmac_hash(key, keylen, innerhash, sizeof innerhash, OPAD, resbuf); - - return 0; -} diff --git a/applications/external/totp/services/hmac/hmac_sha1.c b/applications/external/totp/services/hmac/hmac_sha1.c deleted file mode 100644 index 0a78d569a..000000000 --- a/applications/external/totp/services/hmac/hmac_sha1.c +++ /dev/null @@ -1,24 +0,0 @@ -/* hmac-sha1.c -- hashed message authentication codes - Copyright (C) 2018-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include "hmac_sha1.h" - -#include "sha1.h" - -#define GL_HMAC_NAME 1 -#define GL_HMAC_BLOCKSIZE 64 -#define GL_HMAC_HASHSIZE 20 -#include "hmac_common.h" diff --git a/applications/external/totp/services/hmac/hmac_sha1.h b/applications/external/totp/services/hmac/hmac_sha1.h deleted file mode 100644 index 25ff2f648..000000000 --- a/applications/external/totp/services/hmac/hmac_sha1.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#define HMAC_SHA1_RESULT_SIZE 20 - -/* Compute Hashed Message Authentication Code with SHA-1, over BUFFER - data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the - output to pre-allocated 20 byte minimum RESBUF buffer. Return 0 on - success. */ -int hmac_sha1(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf); diff --git a/applications/external/totp/services/hmac/hmac_sha256.c b/applications/external/totp/services/hmac/hmac_sha256.c deleted file mode 100644 index 00ac2a177..000000000 --- a/applications/external/totp/services/hmac/hmac_sha256.c +++ /dev/null @@ -1,24 +0,0 @@ -/* hmac-sha256.c -- hashed message authentication codes - Copyright (C) 2018-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include "hmac_sha256.h" -#include "sha256.h" - -#define GL_HMAC_NAME 256 -#define GL_HMAC_BLOCKSIZE 64 -#define GL_HMAC_HASHSIZE 32 - -#include "hmac_common.h" diff --git a/applications/external/totp/services/hmac/hmac_sha256.h b/applications/external/totp/services/hmac/hmac_sha256.h deleted file mode 100644 index 9aeaf10d6..000000000 --- a/applications/external/totp/services/hmac/hmac_sha256.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#define HMAC_SHA256_RESULT_SIZE 32 - -/* Compute Hashed Message Authentication Code with SHA-256, over BUFFER - data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the - output to pre-allocated 32 byte minimum RESBUF buffer. Return 0 on - success. */ -int hmac_sha256(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf); diff --git a/applications/external/totp/services/hmac/hmac_sha512.c b/applications/external/totp/services/hmac/hmac_sha512.c deleted file mode 100644 index dc9342a91..000000000 --- a/applications/external/totp/services/hmac/hmac_sha512.c +++ /dev/null @@ -1,24 +0,0 @@ -/* hmac-sha512.c -- hashed message authentication codes - Copyright (C) 2018-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include "hmac_sha512.h" - -#include "sha512.h" - -#define GL_HMAC_NAME 512 -#define GL_HMAC_BLOCKSIZE 128 -#define GL_HMAC_HASHSIZE 64 -#include "hmac_common.h" diff --git a/applications/external/totp/services/hmac/hmac_sha512.h b/applications/external/totp/services/hmac/hmac_sha512.h deleted file mode 100644 index 712b7f4a0..000000000 --- a/applications/external/totp/services/hmac/hmac_sha512.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#define HMAC_SHA512_RESULT_SIZE 64 - -/* Compute Hashed Message Authentication Code with SHA-512, over BUFFER - data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the - output to pre-allocated 64 byte minimum RESBUF buffer. Return 0 on - success. */ -int hmac_sha512(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf); diff --git a/applications/external/totp/services/hmac/memxor.c b/applications/external/totp/services/hmac/memxor.c deleted file mode 100644 index ab6026aa3..000000000 --- a/applications/external/totp/services/hmac/memxor.c +++ /dev/null @@ -1,30 +0,0 @@ -/* memxor.c -- perform binary exclusive OR operation of two memory blocks. - Copyright (C) 2005, 2006 Free Software Foundation, Inc. - - 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 2, 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, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* Written by Simon Josefsson. The interface was inspired by memxor - in Niels Möller's Nettle. */ - -#include "memxor.h" - -void* memxor(void* /*restrict*/ dest, const void* /*restrict*/ src, size_t n) { - char const* s = (char const*)src; - char* d = (char*)dest; - - for(; n > 0; n--) *d++ ^= *s++; - - return dest; -} diff --git a/applications/external/totp/services/hmac/memxor.h b/applications/external/totp/services/hmac/memxor.h deleted file mode 100644 index 71aa604c8..000000000 --- a/applications/external/totp/services/hmac/memxor.h +++ /dev/null @@ -1,28 +0,0 @@ -/* memxor.h -- perform binary exclusive OR operation on memory blocks. - Copyright (C) 2005 Free Software Foundation, Inc. - - 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 2, 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, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* Written by Simon Josefsson. The interface was inspired by memxor - in Niels Möller's Nettle. */ - -#pragma once - -#include - -/* Compute binary exclusive OR of memory areas DEST and SRC, putting - the result in DEST, of length N bytes. Returns a pointer to - DEST. */ -void* memxor(void* /*restrict*/ dest, const void* /*restrict*/ src, size_t n); diff --git a/applications/external/totp/services/hmac/sha1.c b/applications/external/totp/services/hmac/sha1.c deleted file mode 100644 index 29f22e3c3..000000000 --- a/applications/external/totp/services/hmac/sha1.c +++ /dev/null @@ -1,251 +0,0 @@ -/* sha1.c - Functions to compute SHA1 message digest of files or - memory blocks according to the NIST specification FIPS-180-1. - - Copyright (C) 2000-2001, 2003-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Scott G. Miller - Credits: - Robert Klep -- Expansion function fix -*/ - -/* Specification. */ -#include "sha1.h" - -#include -#include - -#include "sha_pad_buffer.h" - -#ifdef WORDS_BIGENDIAN -#define SWAP(n) (n) -#else -#include "byteswap.h" -#define SWAP(n) swap_uint32(n) -#endif - -/* Take a pointer to a 160 bit block of data (five 32 bit ints) and - initialize it to the start constants of the SHA1 algorithm. This - must be called before using hash in the call to sha1_hash. */ -void sha1_init_ctx(struct sha1_ctx* ctx) { - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; - ctx->E = 0xc3d2e1f0; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/* Copy the 4 byte value from v into the memory location pointed to by *cp, - If your architecture allows unaligned access this is equivalent to - * (uint32_t *) cp = v */ -static void set_uint32(char* cp, uint32_t v) { - memcpy(cp, &v, sizeof v); -} - -/* Put result from CTX in first 20 bytes following RESBUF. The result - must be in little endian byte order. */ -void* sha1_read_ctx(const struct sha1_ctx* ctx, void* resbuf) { - char* r = resbuf; - set_uint32(r + 0 * sizeof ctx->A, SWAP(ctx->A)); - set_uint32(r + 1 * sizeof ctx->B, SWAP(ctx->B)); - set_uint32(r + 2 * sizeof ctx->C, SWAP(ctx->C)); - set_uint32(r + 3 * sizeof ctx->D, SWAP(ctx->D)); - set_uint32(r + 4 * sizeof ctx->E, SWAP(ctx->E)); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -void* sha1_finish_ctx(struct sha1_ctx* ctx, void* resbuf) { - /* Take yet unprocessed bytes into account. */ - uint32_t bytes = ctx->buflen; - size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; - - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if(ctx->total[0] < bytes) ++ctx->total[1]; - - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - ctx->buffer[size - 2] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); - ctx->buffer[size - 1] = SWAP(ctx->total[0] << 3); - - sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); - - /* Process last bytes. */ - sha1_process_block(ctx->buffer, size * 4, ctx); - - return sha1_read_ctx(ctx, resbuf); -} - -/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void* sha1_buffer(const char* buffer, size_t len, void* resblock) { - struct sha1_ctx ctx; - - /* Initialize the computation context. */ - sha1_init_ctx(&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - sha1_process_bytes(buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha1_finish_ctx(&ctx, resblock); -} - -void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx) { - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if(ctx->buflen != 0) { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if(ctx->buflen > 64) { - sha1_process_block(ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap, - because ctx->buflen < 64 ≤ (left_over + add) & ~63. */ - memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~63], ctx->buflen); - } - - buffer = (const char*)buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if(len >= 64) { -#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned) -#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(uint32_t) != 0) - if(UNALIGNED_P(buffer)) - while(len > 64) { - sha1_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); //-V1086 - buffer = (const char*)buffer + 64; - len -= 64; - } - else -#endif - { - sha1_process_block(buffer, len & ~63, ctx); - buffer = (const char*)buffer + (len & ~63); - len &= 63; - } - } - - /* Move remaining bytes in internal buffer. */ - if(len > 0) { - size_t left_over = ctx->buflen; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, len); - left_over += len; - if(left_over >= 64) { - sha1_process_block(ctx->buffer, 64, ctx); - left_over -= 64; - /* The regions in the following copy operation cannot overlap, - because left_over ≤ 64. */ - memcpy(ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between md5.c and sha1.c --- */ - -/* SHA1 round constants */ -static const int sha1_round_constants[4] = {0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6}; - -/* Round functions. Note that F2 is the same as F4. */ -#define F1(B, C, D) (D ^ (B & (C ^ D))) -#define F2_4(B, C, D) (B ^ C ^ D) -#define F3(B, C, D) ((B & C) | (D & (B | C))) -#define FN(I, B, C, D) (I == 0 ? F1(B, C, D) : (I == 2 ? F3(B, C, D) : F2_4(B, C, D))) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx) { - const uint32_t* words = buffer; - size_t nwords = len / sizeof(uint32_t); - const uint32_t* endp = words + nwords; - uint32_t x[16]; - uint32_t a = ctx->A; - uint32_t b = ctx->B; - uint32_t c = ctx->C; - uint32_t d = ctx->D; - uint32_t e = ctx->E; - uint32_t lolen = len; - - /* First increment the byte count. RFC 1321 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += lolen; - ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen); - -#define rol(x, n) (((x) << (n)) | ((uint32_t)(x) >> (32 - (n)))) - -#define M(I) \ - (tm = x[I & 0x0f] ^ x[(I - 14) & 0x0f] ^ x[(I - 8) & 0x0f] ^ x[(I - 3) & 0x0f], \ - (x[I & 0x0f] = rol(tm, 1))) - -#define R(A, B, C, D, E, F, K, M, KI) \ - do { \ - E += rol(A, 5) + F(KI, B, C, D) + K + M; \ - B = rol(B, 30); \ - } while(0) - - while(words < endp) { - uint32_t tm; - int t; - for(t = 0; t < 16; t++) { - x[t] = SWAP(*words); - words++; - } - - for(uint8_t i = 0; i < 80; i++) { - uint32_t m = i < 16 ? x[i] : M(i); - uint8_t ki = i / 20; - int k_const = sha1_round_constants[ki]; - R(a, b, c, d, e, FN, k_const, m, ki); - uint32_t tt = a; - a = e; - e = d; - d = c; - c = b; - b = tt; - } - - a = ctx->A += a; - b = ctx->B += b; - c = ctx->C += c; - d = ctx->D += d; - e = ctx->E += e; - } -} - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha1.h b/applications/external/totp/services/hmac/sha1.h deleted file mode 100644 index e9eb7712a..000000000 --- a/applications/external/totp/services/hmac/sha1.h +++ /dev/null @@ -1,84 +0,0 @@ -/* Declarations of functions and data types used for SHA1 sum - library functions. - Copyright (C) 2000-2001, 2003, 2005-2006, 2008-2022 Free Software - Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define SHA1_DIGEST_SIZE 20 - -/* Structure to save state of computation between the single steps. */ -struct sha1_ctx { - uint32_t A; - uint32_t B; - uint32_t C; - uint32_t D; - uint32_t E; - - uint32_t total[2]; - uint32_t buflen; /* ≥ 0, ≤ 128 */ - uint32_t buffer[32]; /* 128 bytes; the first buflen bytes are in use */ -}; - -/* Initialize structure containing state of computation. */ -extern void sha1_init_ctx(struct sha1_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ -extern void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ -extern void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 20 bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha1_finish_ctx(struct sha1_ctx* ctx, void* restrict resbuf); - -/* Put result from CTX in first 20 bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. */ -extern void* sha1_read_ctx(const struct sha1_ctx* ctx, void* restrict resbuf); - -/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha1_buffer(const char* buffer, size_t len, void* restrict resblock); - -#ifdef __cplusplus -} -#endif - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha256.c b/applications/external/totp/services/hmac/sha256.c deleted file mode 100644 index 09ba272e7..000000000 --- a/applications/external/totp/services/hmac/sha256.c +++ /dev/null @@ -1,283 +0,0 @@ -/* sha256.c - Functions to compute SHA256 message digest of files or - memory blocks according to the NIST specification FIPS-180-2. - - Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by David Madore, considerably copypasting from - Scott G. Miller's sha1.c -*/ - -/* Specification. */ -#include "sha256.h" - -#include -#include -#include "sha_pad_buffer.h" - -#ifdef WORDS_BIGENDIAN -#define SWAP(n) (n) -#else -#include "byteswap.h" -#define SWAP(n) swap_uint32(n) -#endif - -/* - Takes a pointer to a 256 bit block of data (eight 32 bit ints) and - initializes it to the start constants of the SHA256 algorithm. This - must be called before using hash in the call to sha256_hash -*/ -void sha256_init_ctx(struct sha256_ctx* ctx) { - ctx->state[0] = 0x6a09e667UL; - ctx->state[1] = 0xbb67ae85UL; - ctx->state[2] = 0x3c6ef372UL; - ctx->state[3] = 0xa54ff53aUL; - ctx->state[4] = 0x510e527fUL; - ctx->state[5] = 0x9b05688cUL; - ctx->state[6] = 0x1f83d9abUL; - ctx->state[7] = 0x5be0cd19UL; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/* Copy the value from v into the memory location pointed to by *CP, - If your architecture allows unaligned access, this is equivalent to - * (__typeof__ (v) *) cp = v */ -static void set_uint32(char* cp, uint32_t v) { - memcpy(cp, &v, sizeof v); -} - -/* Put result from CTX in first 32 bytes following RESBUF. - The result must be in little endian byte order. */ -void* sha256_read_ctx(const struct sha256_ctx* ctx, void* resbuf) { - int i; - char* r = resbuf; - - for(i = 0; i < 8; i++) set_uint32(r + i * sizeof ctx->state[0], SWAP(ctx->state[i])); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -static void sha256_conclude_ctx(struct sha256_ctx* ctx) { - /* Take yet unprocessed bytes into account. */ - size_t bytes = ctx->buflen; - size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; - - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if(ctx->total[0] < bytes) ++ctx->total[1]; - - /* Put the 64-bit file length in *bits* at the end of the buffer. - Use set_uint32 rather than a simple assignment, to avoid risk of - unaligned access. */ - set_uint32((char*)&ctx->buffer[size - 2], SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29))); - set_uint32((char*)&ctx->buffer[size - 1], SWAP(ctx->total[0] << 3)); - - sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); - - /* Process last bytes. */ - sha256_process_block(ctx->buffer, size * 4, ctx); -} - -void* sha256_finish_ctx(struct sha256_ctx* ctx, void* resbuf) { - sha256_conclude_ctx(ctx); - return sha256_read_ctx(ctx, resbuf); -} - -/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void* sha256_buffer(const char* buffer, size_t len, void* resblock) { - struct sha256_ctx ctx; - - /* Initialize the computation context. */ - sha256_init_ctx(&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - sha256_process_bytes(buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha256_finish_ctx(&ctx, resblock); -} - -void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx) { - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if(ctx->buflen != 0) { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if(ctx->buflen > 64) { - sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap, - because ctx->buflen < 64 ≤ (left_over + add) & ~63. */ - memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~63], ctx->buflen); - } - - buffer = (const char*)buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if(len >= 64) { -#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned) -#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(uint32_t) != 0) - if(UNALIGNED_P(buffer)) - while(len > 64) { - sha256_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); //-V1086 - buffer = (const char*)buffer + 64; - len -= 64; - } - else -#endif - { - sha256_process_block(buffer, len & ~63, ctx); - buffer = (const char*)buffer + (len & ~63); - len &= 63; - } - } - - /* Move remaining bytes in internal buffer. */ - if(len > 0) { - size_t left_over = ctx->buflen; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, len); - left_over += len; - if(left_over >= 64) { - sha256_process_block(ctx->buffer, 64, ctx); - left_over -= 64; - /* The regions in the following copy operation cannot overlap, - because left_over ≤ 64. */ - memcpy(ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between sha1.c and sha256.c --- */ - -/* SHA256 round constants */ -#define K(I) sha256_round_constants[I] -static const uint32_t sha256_round_constants[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, -}; - -/* Round functions. */ -#define F2(A, B, C) ((A & B) | (C & (A | B))) -#define F1(E, F, G) (G ^ (E & (F ^ G))) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx) { - const uint32_t* words = buffer; - size_t nwords = len / sizeof(uint32_t); - const uint32_t* endp = words + nwords; - uint32_t x[16]; - uint32_t a = ctx->state[0]; - uint32_t b = ctx->state[1]; - uint32_t c = ctx->state[2]; - uint32_t d = ctx->state[3]; - uint32_t e = ctx->state[4]; - uint32_t f = ctx->state[5]; - uint32_t g = ctx->state[6]; - uint32_t h = ctx->state[7]; - uint32_t lolen = len; - - /* First increment the byte count. FIPS PUB 180-2 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += lolen; - ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen); - -#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) -#define S0(x) (rol(x, 25) ^ rol(x, 14) ^ (x >> 3)) -#define S1(x) (rol(x, 15) ^ rol(x, 13) ^ (x >> 10)) -#define SS0(x) (rol(x, 30) ^ rol(x, 19) ^ rol(x, 10)) -#define SS1(x) (rol(x, 26) ^ rol(x, 21) ^ rol(x, 7)) - -#define M(I) \ - (tm = S1(x[(I - 2) & 0x0f]) + x[(I - 7) & 0x0f] + S0(x[(I - 15) & 0x0f]) + x[I & 0x0f], \ - x[I & 0x0f] = tm) - -#define R(A, B, C, D, E, F, G, H, K, M) \ - do { \ - t0 = SS0(A) + F2(A, B, C); \ - t1 = H + SS1(E) + F1(E, F, G) + K + M; \ - D += t1; \ - H = t0 + t1; \ - } while(0) - - while(words < endp) { - uint32_t tm; - uint32_t t0, t1; - int t; - /* FIXME: see sha1.c for a better implementation. */ - for(t = 0; t < 16; t++) { - x[t] = SWAP(*words); - words++; - } - - for(int i = 0; i < 64; i++) { - uint32_t xx = i < 16 ? x[i] : M(i); - R(a, b, c, d, e, f, g, h, K(i), xx); - uint32_t tt = a; - a = h; - h = g; - g = f; - f = e; - e = d; - d = c; - c = b; - b = tt; - } - - a = ctx->state[0] += a; - b = ctx->state[1] += b; - c = ctx->state[2] += c; - d = ctx->state[3] += d; - e = ctx->state[4] += e; - f = ctx->state[5] += f; - g = ctx->state[6] += g; - h = ctx->state[7] += h; - } -} - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha256.h b/applications/external/totp/services/hmac/sha256.h deleted file mode 100644 index 964f2eb97..000000000 --- a/applications/external/totp/services/hmac/sha256.h +++ /dev/null @@ -1,79 +0,0 @@ -/* Declarations of functions and data types used for SHA256 sum - library functions. - Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum { SHA256_DIGEST_SIZE = 256 / 8 }; - -/* Structure to save state of computation between the single steps. */ -struct sha256_ctx { - uint32_t state[8]; - - uint32_t total[2]; - size_t buflen; /* ≥ 0, ≤ 128 */ - uint32_t buffer[32]; /* 128 bytes; the first buflen bytes are in use */ -}; - -/* Initialize structure containing state of computation. */ -extern void sha256_init_ctx(struct sha256_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ -extern void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ -extern void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 32 (28) bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha256_finish_ctx(struct sha256_ctx* ctx, void* restrict resbuf); - -/* Put result from CTX in first 32 (28) bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. */ -extern void* sha256_read_ctx(const struct sha256_ctx* ctx, void* restrict resbuf); - -/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. - The result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha256_buffer(const char* buffer, size_t len, void* restrict resblock); - -#ifdef __cplusplus -} -#endif - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha512.c b/applications/external/totp/services/hmac/sha512.c deleted file mode 100644 index ffe2864fb..000000000 --- a/applications/external/totp/services/hmac/sha512.c +++ /dev/null @@ -1,309 +0,0 @@ -/* sha512.c - Functions to compute SHA512 message digest of files or - memory blocks according to the NIST specification FIPS-180-2. - - Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by David Madore, considerably copypasting from - Scott G. Miller's sha1.c -*/ - -/* Specification. */ -#include "sha512.h" - -#include -#include - -#include "byteswap.h" -#include "sha_pad_buffer.h" - -#define SWAP(n) swap_uint64(n) - -/* - Takes a pointer to a 512 bit block of data (eight 64 bit ints) and - initializes it to the start constants of the SHA512 algorithm. This - must be called before using hash in the call to sha512_hash -*/ -void sha512_init_ctx(struct sha512_ctx* ctx) { - ctx->state[0] = u64hilo(0x6a09e667, 0xf3bcc908); - ctx->state[1] = u64hilo(0xbb67ae85, 0x84caa73b); - ctx->state[2] = u64hilo(0x3c6ef372, 0xfe94f82b); - ctx->state[3] = u64hilo(0xa54ff53a, 0x5f1d36f1); - ctx->state[4] = u64hilo(0x510e527f, 0xade682d1); - ctx->state[5] = u64hilo(0x9b05688c, 0x2b3e6c1f); - ctx->state[6] = u64hilo(0x1f83d9ab, 0xfb41bd6b); - ctx->state[7] = u64hilo(0x5be0cd19, 0x137e2179); - - ctx->total[0] = ctx->total[1] = u64lo(0); - ctx->buflen = 0; -} - -/* Copy the value from V into the memory location pointed to by *CP, - If your architecture allows unaligned access, this is equivalent to - * (__typeof__ (v) *) cp = v */ -static void set_uint64(char* cp, u64 v) { - memcpy(cp, &v, sizeof v); -} - -/* Put result from CTX in first 64 bytes following RESBUF. - The result must be in little endian byte order. */ -void* sha512_read_ctx(const struct sha512_ctx* ctx, void* resbuf) { - int i; - char* r = resbuf; - - for(i = 0; i < 8; i++) set_uint64(r + i * sizeof ctx->state[0], SWAP(ctx->state[i])); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -static void sha512_conclude_ctx(struct sha512_ctx* ctx) { - /* Take yet unprocessed bytes into account. */ - size_t bytes = ctx->buflen; - size_t size = (bytes < 112) ? 128 / 8 : 128 * 2 / 8; - - /* Now count remaining bytes. */ - ctx->total[0] = u64plus(ctx->total[0], u64lo(bytes)); - if(u64lt(ctx->total[0], u64lo(bytes))) ctx->total[1] = u64plus(ctx->total[1], u64lo(1)); - - /* Put the 128-bit file length in *bits* at the end of the buffer. - Use set_uint64 rather than a simple assignment, to avoid risk of - unaligned access. */ - set_uint64( - (char*)&ctx->buffer[size - 2], - SWAP(u64or(u64shl(ctx->total[1], 3), u64shr(ctx->total[0], 61)))); - set_uint64((char*)&ctx->buffer[size - 1], SWAP(u64shl(ctx->total[0], 3))); - - sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 8 - bytes); - - /* Process last bytes. */ - sha512_process_block(ctx->buffer, size * 8, ctx); -} - -void* sha512_finish_ctx(struct sha512_ctx* ctx, void* resbuf) { - sha512_conclude_ctx(ctx); - return sha512_read_ctx(ctx, resbuf); -} - -/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void* sha512_buffer(const char* buffer, size_t len, void* resblock) { - struct sha512_ctx ctx; - - /* Initialize the computation context. */ - sha512_init_ctx(&ctx); - - /* Process whole buffer but last len % 128 bytes. */ - sha512_process_bytes(buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha512_finish_ctx(&ctx, resblock); -} - -void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx) { - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if(ctx->buflen != 0) { - size_t left_over = ctx->buflen; - size_t add = 256 - left_over > len ? len : 256 - left_over; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if(ctx->buflen > 128) { - sha512_process_block(ctx->buffer, ctx->buflen & ~127, ctx); - - ctx->buflen &= 127; - /* The regions in the following copy operation cannot overlap, - because ctx->buflen < 128 ≤ (left_over + add) & ~127. */ - memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~127], ctx->buflen); - } - - buffer = (const char*)buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if(len >= 128) { -#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned) -#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(u64) != 0) - if(UNALIGNED_P(buffer)) - while(len > 128) { - sha512_process_block(memcpy(ctx->buffer, buffer, 128), 128, ctx); //-V1086 - buffer = (const char*)buffer + 128; - len -= 128; - } - else -#endif - { - sha512_process_block(buffer, len & ~127, ctx); - buffer = (const char*)buffer + (len & ~127); - len &= 127; - } - } - - /* Move remaining bytes in internal buffer. */ - if(len > 0) { - size_t left_over = ctx->buflen; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, len); - left_over += len; - if(left_over >= 128) { - sha512_process_block(ctx->buffer, 128, ctx); - left_over -= 128; - /* The regions in the following copy operation cannot overlap, - because left_over ≤ 128. */ - memcpy(ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between sha1.c and sha512.c --- */ - -/* SHA512 round constants */ -#define K(I) sha512_round_constants[I] -static u64 const sha512_round_constants[80] = { - u64init(0x428a2f98, 0xd728ae22), u64init(0x71374491, 0x23ef65cd), - u64init(0xb5c0fbcf, 0xec4d3b2f), u64init(0xe9b5dba5, 0x8189dbbc), - u64init(0x3956c25b, 0xf348b538), u64init(0x59f111f1, 0xb605d019), - u64init(0x923f82a4, 0xaf194f9b), u64init(0xab1c5ed5, 0xda6d8118), - u64init(0xd807aa98, 0xa3030242), u64init(0x12835b01, 0x45706fbe), - u64init(0x243185be, 0x4ee4b28c), u64init(0x550c7dc3, 0xd5ffb4e2), - u64init(0x72be5d74, 0xf27b896f), u64init(0x80deb1fe, 0x3b1696b1), - u64init(0x9bdc06a7, 0x25c71235), u64init(0xc19bf174, 0xcf692694), - u64init(0xe49b69c1, 0x9ef14ad2), u64init(0xefbe4786, 0x384f25e3), - u64init(0x0fc19dc6, 0x8b8cd5b5), u64init(0x240ca1cc, 0x77ac9c65), - u64init(0x2de92c6f, 0x592b0275), u64init(0x4a7484aa, 0x6ea6e483), - u64init(0x5cb0a9dc, 0xbd41fbd4), u64init(0x76f988da, 0x831153b5), - u64init(0x983e5152, 0xee66dfab), u64init(0xa831c66d, 0x2db43210), - u64init(0xb00327c8, 0x98fb213f), u64init(0xbf597fc7, 0xbeef0ee4), - u64init(0xc6e00bf3, 0x3da88fc2), u64init(0xd5a79147, 0x930aa725), - u64init(0x06ca6351, 0xe003826f), u64init(0x14292967, 0x0a0e6e70), - u64init(0x27b70a85, 0x46d22ffc), u64init(0x2e1b2138, 0x5c26c926), - u64init(0x4d2c6dfc, 0x5ac42aed), u64init(0x53380d13, 0x9d95b3df), - u64init(0x650a7354, 0x8baf63de), u64init(0x766a0abb, 0x3c77b2a8), - u64init(0x81c2c92e, 0x47edaee6), u64init(0x92722c85, 0x1482353b), - u64init(0xa2bfe8a1, 0x4cf10364), u64init(0xa81a664b, 0xbc423001), - u64init(0xc24b8b70, 0xd0f89791), u64init(0xc76c51a3, 0x0654be30), - u64init(0xd192e819, 0xd6ef5218), u64init(0xd6990624, 0x5565a910), - u64init(0xf40e3585, 0x5771202a), u64init(0x106aa070, 0x32bbd1b8), - u64init(0x19a4c116, 0xb8d2d0c8), u64init(0x1e376c08, 0x5141ab53), - u64init(0x2748774c, 0xdf8eeb99), u64init(0x34b0bcb5, 0xe19b48a8), - u64init(0x391c0cb3, 0xc5c95a63), u64init(0x4ed8aa4a, 0xe3418acb), - u64init(0x5b9cca4f, 0x7763e373), u64init(0x682e6ff3, 0xd6b2b8a3), - u64init(0x748f82ee, 0x5defb2fc), u64init(0x78a5636f, 0x43172f60), - u64init(0x84c87814, 0xa1f0ab72), u64init(0x8cc70208, 0x1a6439ec), - u64init(0x90befffa, 0x23631e28), u64init(0xa4506ceb, 0xde82bde9), - u64init(0xbef9a3f7, 0xb2c67915), u64init(0xc67178f2, 0xe372532b), - u64init(0xca273ece, 0xea26619c), u64init(0xd186b8c7, 0x21c0c207), - u64init(0xeada7dd6, 0xcde0eb1e), u64init(0xf57d4f7f, 0xee6ed178), - u64init(0x06f067aa, 0x72176fba), u64init(0x0a637dc5, 0xa2c898a6), - u64init(0x113f9804, 0xbef90dae), u64init(0x1b710b35, 0x131c471b), - u64init(0x28db77f5, 0x23047d84), u64init(0x32caab7b, 0x40c72493), - u64init(0x3c9ebe0a, 0x15c9bebc), u64init(0x431d67c4, 0x9c100d4c), - u64init(0x4cc5d4be, 0xcb3e42b6), u64init(0x597f299c, 0xfc657e2a), - u64init(0x5fcb6fab, 0x3ad6faec), u64init(0x6c44198c, 0x4a475817), -}; - -/* Round functions. */ -#define F2(A, B, C) u64or(u64and(A, B), u64and(C, u64or(A, B))) -#define F1(E, F, G) u64xor(G, u64and(E, u64xor(F, G))) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 128 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx) { - u64 const* words = buffer; - u64 const* endp = words + len / sizeof(u64); - u64 x[16]; - u64 a = ctx->state[0]; - u64 b = ctx->state[1]; - u64 c = ctx->state[2]; - u64 d = ctx->state[3]; - u64 e = ctx->state[4]; - u64 f = ctx->state[5]; - u64 g = ctx->state[6]; - u64 h = ctx->state[7]; - u64 lolen = u64size(len); - - /* First increment the byte count. FIPS PUB 180-2 specifies the possible - length of the file up to 2^128 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] = u64plus(ctx->total[0], lolen); - ctx->total[1] = u64plus( - ctx->total[1], u64plus(u64size(len >> 31 >> 31 >> 2), u64lo(u64lt(ctx->total[0], lolen)))); - -#define S0(x) u64xor(u64rol(x, 63), u64xor(u64rol(x, 56), u64shr(x, 7))) -#define S1(x) u64xor(u64rol(x, 45), u64xor(u64rol(x, 3), u64shr(x, 6))) -#define SS0(x) u64xor(u64rol(x, 36), u64xor(u64rol(x, 30), u64rol(x, 25))) -#define SS1(x) u64xor(u64rol(x, 50), u64xor(u64rol(x, 46), u64rol(x, 23))) - -#define M(I) \ - (x[(I)&15] = u64plus( \ - x[(I)&15], \ - u64plus(S1(x[((I)-2) & 15]), u64plus(x[((I)-7) & 15], S0(x[((I)-15) & 15]))))) - -#define R(A, B, C, D, E, F, G, H, K, M) \ - do { \ - u64 t0 = u64plus(SS0(A), F2(A, B, C)); \ - u64 t1 = u64plus(H, u64plus(SS1(E), u64plus(F1(E, F, G), u64plus(K, M)))); \ - D = u64plus(D, t1); \ - H = u64plus(t0, t1); \ - } while(0) - - while(words < endp) { - int t; - /* FIXME: see sha1.c for a better implementation. */ - for(t = 0; t < 16; t++) { - x[t] = SWAP(*words); - words++; - } - - for(int i = 0; i < 80; i++) { - u64 xx = i < 16 ? x[i] : M(i); - R(a, b, c, d, e, f, g, h, K(i), xx); - u64 tt = a; - a = h; - h = g; - g = f; - f = e; - e = d; - d = c; - c = b; - b = tt; - } - - a = ctx->state[0] = u64plus(ctx->state[0], a); - b = ctx->state[1] = u64plus(ctx->state[1], b); - c = ctx->state[2] = u64plus(ctx->state[2], c); - d = ctx->state[3] = u64plus(ctx->state[3], d); - e = ctx->state[4] = u64plus(ctx->state[4], e); - f = ctx->state[5] = u64plus(ctx->state[5], f); - g = ctx->state[6] = u64plus(ctx->state[6], g); - h = ctx->state[7] = u64plus(ctx->state[7], h); - } -} - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha512.h b/applications/external/totp/services/hmac/sha512.h deleted file mode 100644 index 38590829e..000000000 --- a/applications/external/totp/services/hmac/sha512.h +++ /dev/null @@ -1,82 +0,0 @@ -/* Declarations of functions and data types used for SHA512 and SHA384 sum - library functions. - Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#pragma once - -#include -#include "u64.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum { SHA512_DIGEST_SIZE = 512 / 8 }; - -/* Structure to save state of computation between the single steps. */ -struct sha512_ctx { - u64 state[8]; - - u64 total[2]; - size_t buflen; /* ≥ 0, ≤ 256 */ - u64 buffer[32]; /* 256 bytes; the first buflen bytes are in use */ -}; - -/* Initialize structure containing state of computation. */ -extern void sha512_init_ctx(struct sha512_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 128!!! */ -extern void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 128. */ -extern void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 64 (48) bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha512_finish_ctx(struct sha512_ctx* ctx, void* restrict resbuf); - -/* Put result from CTX in first 64 (48) bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -extern void* sha512_read_ctx(const struct sha512_ctx* ctx, void* restrict resbuf); - -/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. - The result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha512_buffer(const char* buffer, size_t len, void* restrict resblock); - -#ifdef __cplusplus -} -#endif - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.c b/applications/external/totp/services/hmac/sha_pad_buffer.c deleted file mode 100644 index badedbcc7..000000000 --- a/applications/external/totp/services/hmac/sha_pad_buffer.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "sha_pad_buffer.h" -#include - -void sha_pad_buffer(uint8_t* buffer, size_t size) { - if(size > 0) { - buffer[0] = 0x80; - if(size > 1) { - memset(&buffer[1], 0, size - 1); - } - } -} \ No newline at end of file diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.h b/applications/external/totp/services/hmac/sha_pad_buffer.h deleted file mode 100644 index 7dba40fa9..000000000 --- a/applications/external/totp/services/hmac/sha_pad_buffer.h +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include - -void sha_pad_buffer(uint8_t* buffer, size_t size); \ No newline at end of file diff --git a/applications/external/totp/services/hmac/u64.h b/applications/external/totp/services/hmac/u64.h deleted file mode 100644 index 91e42e6c9..000000000 --- a/applications/external/totp/services/hmac/u64.h +++ /dev/null @@ -1,44 +0,0 @@ -/* uint64_t-like operations that work even on hosts lacking uint64_t - - Copyright (C) 2006, 2009-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Paul Eggert. */ - -#pragma once - -#include - -#ifndef _GL_U64_INLINE -#define _GL_U64_INLINE _GL_INLINE -#endif - -/* Return X rotated left by N bits, where 0 < N < 64. */ -#define u64rol(x, n) u64or(u64shl(x, n), u64shr(x, 64 - (n))) - -/* Native implementations are trivial. See below for comments on what - these operations do. */ -typedef uint64_t u64; -#define u64hilo(hi, lo) ((u64)(((u64)(hi) << 32) + (lo))) -#define u64init(hi, lo) u64hilo(hi, lo) -#define u64lo(x) ((u64)(x)) -#define u64size(x) u64lo(x) -#define u64lt(x, y) ((x) < (y)) -#define u64and(x, y) ((x) & (y)) -#define u64or(x, y) ((x) | (y)) -#define u64xor(x, y) ((x) ^ (y)) -#define u64plus(x, y) ((x) + (y)) -#define u64shl(x, n) ((x) << (n)) -#define u64shr(x, n) ((x) >> (n)) diff --git a/applications/external/totp/services/totp/totp.c b/applications/external/totp/services/totp/totp.c index 45a283b06..32232f21a 100644 --- a/applications/external/totp/services/totp/totp.c +++ b/applications/external/totp/services/totp/totp.c @@ -4,12 +4,16 @@ #include #include #include -#include "../hmac/hmac_sha1.h" -#include "../hmac/hmac_sha256.h" -#include "../hmac/hmac_sha512.h" -#include "../hmac/byteswap.h" +#include "../../config/wolfssl/config.h" +#include -#define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE +#define HMAC_MAX_RESULT_SIZE WC_SHA512_DIGEST_SIZE + +static uint64_t swap_uint64(uint64_t val) { + val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL); + return (val << 32) | (val >> 32); +} /** * @brief Generates the timeblock for a time in seconds. @@ -68,14 +72,34 @@ uint64_t totp_at( algo, plain_secret, plain_secret_length, totp_timecode(interval, for_time_adjusted)); } +static int totp_algo_common( + int type, + const uint8_t* key, + size_t key_length, + const uint8_t* input, + size_t input_length, + uint8_t* output) { + Hmac hmac; + int ret = wc_HmacSetKey(&hmac, type, key, key_length); + if(ret == 0) { + ret = wc_HmacUpdate(&hmac, input, input_length); + } + + if(ret == 0) { + ret = wc_HmacFinal(&hmac, output); + } + + wc_HmacFree(&hmac); + return ret == 0 ? wc_HmacSizeByType(type) : 0; +} + static int totp_algo_sha1( const uint8_t* key, size_t key_length, const uint8_t* input, size_t input_length, uint8_t* output) { - hmac_sha1(key, key_length, input, input_length, output); - return HMAC_SHA1_RESULT_SIZE; + return totp_algo_common(WC_SHA, key, key_length, input, input_length, output); } static int totp_algo_sha256( @@ -84,8 +108,7 @@ static int totp_algo_sha256( const uint8_t* input, size_t input_length, uint8_t* output) { - hmac_sha256(key, key_length, input, input_length, output); - return HMAC_SHA256_RESULT_SIZE; + return totp_algo_common(WC_SHA256, key, key_length, input, input_length, output); } static int totp_algo_sha512( @@ -94,8 +117,7 @@ static int totp_algo_sha512( const uint8_t* input, size_t input_length, uint8_t* output) { - hmac_sha512(key, key_length, input, input_length, output); - return HMAC_SHA512_RESULT_SIZE; + return totp_algo_common(WC_SHA512, key, key_length, input, input_length, output); } const TOTP_ALGO TOTP_ALGO_SHA1 = (TOTP_ALGO)(&totp_algo_sha1); diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 0849aad13..adfd4a9a5 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -4,7 +4,8 @@ #include #include #include -#include "features_config.h" +#include +#include "config/app/config.h" #include "services/config/config.h" #include "types/plugin_state.h" #include "types/token_info.h" @@ -14,60 +15,54 @@ #include "ui/scene_director.h" #include "ui/constants.h" #include "ui/common_dialogs.h" -#include "services/crypto/crypto.h" +#include "services/crypto/crypto_facade.h" #include "cli/cli.h" +#include "version.h" +#include -static void render_callback(Canvas* const canvas, void* ctx) { +struct TotpRenderCallbackContext { + FuriMutex* mutex; + PluginState* plugin_state; +}; + +static void render_callback(Canvas* const canvas, void* const ctx) { furi_assert(ctx); - PluginState* plugin_state = ctx; - if(furi_mutex_acquire(plugin_state->mutex, 25) == FuriStatusOk) { - totp_scene_director_render(canvas, plugin_state); - furi_mutex_release(plugin_state->mutex); + const struct TotpRenderCallbackContext* context = ctx; + if(furi_mutex_acquire(context->mutex, 25) == FuriStatusOk) { + totp_scene_director_render(canvas, context->plugin_state); + furi_mutex_release(context->mutex); } } -static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - +static void input_callback(InputEvent* const input_event, void* const ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; PluginEvent event = {.type = EventTypeKey, .input = *input_event}; furi_message_queue_put(event_queue, &event, FuriWaitForever); } -static bool totp_activate_initial_scene(PluginState* const plugin_state) { - if(plugin_state->crypto_verify_data == NULL) { - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "No", NULL, "Yes"); - dialog_message_set_text( - message, - "Would you like to setup PIN?", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - DialogMessageButton dialog_result = - dialog_message_show(plugin_state->dialogs_app, message); - dialog_message_free(message); - if(dialog_result == DialogMessageButtonRight) { - totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); - } else { - CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); - if(seed_result & CryptoSeedIVResultFlagSuccess && - seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { - if(!totp_config_file_update_crypto_signatures(plugin_state)) { - totp_dialogs_config_loading_error(plugin_state); - return false; - } - } else if(seed_result == CryptoSeedIVResultFailed) { - totp_dialogs_config_loading_error(plugin_state); - return false; - } +static bool first_run_init(PluginState* const plugin_state) { + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "No", NULL, "Yes"); + dialog_message_set_text( + message, + "Would you like to setup PIN?", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + DialogMessageButton dialog_result = dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + if(!totp_crypto_check_key_slot(plugin_state->crypto_settings.crypto_key_slot)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); - } - } else if(plugin_state->pin_set) { + if(dialog_result == DialogMessageButtonRight) { totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); } else { - CryptoSeedIVResult seed_result = totp_crypto_seed_iv(plugin_state, NULL, 0); + CryptoSeedIVResult seed_result = + totp_crypto_seed_iv(&plugin_state->crypto_settings, NULL, 0); if(seed_result & CryptoSeedIVResultFlagSuccess && seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { if(!totp_config_file_update_crypto_signatures(plugin_state)) { @@ -79,23 +74,65 @@ static bool totp_activate_initial_scene(PluginState* const plugin_state) { return false; } - if(totp_crypto_verify_key(plugin_state)) { - totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); - } else { - FURI_LOG_E( - LOGGING_TAG, - "Digital signature verification failed. Looks like conf file was created on another flipper and can't be used on any other"); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_buttons(message, "Exit", NULL, NULL); - dialog_message_set_text( - message, - "Digital signature verification failed", - SCREEN_WIDTH_CENTER, - SCREEN_HEIGHT_CENTER, - AlignCenter, - AlignCenter); - dialog_message_show(plugin_state->dialogs_app, message); - dialog_message_free(message); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); + } + + return true; +} + +static bool pinless_activation(PluginState* const plugin_state) { + CryptoSeedIVResult seed_result = totp_crypto_seed_iv(&plugin_state->crypto_settings, NULL, 0); + if(seed_result & CryptoSeedIVResultFlagSuccess && + seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { + if(!totp_config_file_update_crypto_signatures(plugin_state)) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + } else if(seed_result == CryptoSeedIVResultFailed) { + totp_dialogs_config_loading_error(plugin_state); + return false; + } + + if(totp_crypto_verify_key(&plugin_state->crypto_settings)) { + totp_config_file_ensure_latest_encryption(plugin_state, NULL, 0); + totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); + } else { + FURI_LOG_E( + LOGGING_TAG, + "Digital signature verification failed. Looks like conf file was created on another device and can't be used on any other"); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Exit", NULL, NULL); + dialog_message_set_text( + message, + "Digital signature verification failed", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); + return false; + } + + return true; +} + +static bool pin_activation(PluginState* const plugin_state) { + totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); + return true; +} + +static bool totp_activate_initial_scene(PluginState* const plugin_state) { + if(plugin_state->crypto_settings.crypto_verify_data == NULL) { + if(!first_run_init(plugin_state)) { + return false; + } + } else if(plugin_state->crypto_settings.pin_required) { + if(!pin_activation(plugin_state)) { + return false; + } + } else { + if(!pinless_activation(plugin_state)) { return false; } } @@ -108,6 +145,7 @@ static bool on_user_idle(void* context) { if(plugin_state->current_scene != TotpSceneAuthentication && plugin_state->current_scene != TotpSceneStandby) { totp_scene_director_activate_scene(plugin_state, TotpSceneAuthentication); + totp_scene_director_force_redraw(plugin_state); return true; } @@ -116,18 +154,17 @@ static bool on_user_idle(void* context) { static bool totp_plugin_state_init(PluginState* const plugin_state) { plugin_state->gui = furi_record_open(RECORD_GUI); - plugin_state->notification_app = furi_record_open(RECORD_NOTIFICATION); plugin_state->dialogs_app = furi_record_open(RECORD_DIALOGS); - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); if(!totp_config_file_load(plugin_state)) { totp_dialogs_config_loading_error(plugin_state); return false; } - plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt) { plugin_state->bt_type_code_worker_context = totp_bt_type_code_worker_init(); } else { @@ -135,7 +172,7 @@ static bool totp_plugin_state_init(PluginState* const plugin_state) { } #endif - if(plugin_state->pin_set) { + if(plugin_state->crypto_settings.pin_required) { 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); @@ -153,28 +190,37 @@ static void totp_plugin_state_free(PluginState* plugin_state) { } furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); totp_config_file_close(plugin_state); - if(plugin_state->crypto_verify_data != NULL) { - free(plugin_state->crypto_verify_data); + if(plugin_state->crypto_settings.crypto_verify_data != NULL) { + free(plugin_state->crypto_settings.crypto_verify_data); } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->bt_type_code_worker_context != NULL) { totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); plugin_state->bt_type_code_worker_context = NULL; } #endif - furi_mutex_free(plugin_state->mutex); + if(plugin_state->event_queue != NULL) { + furi_message_queue_free(plugin_state->event_queue); + } + free(plugin_state); } int32_t totp_app() { - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + FURI_LOG_I( + LOGGING_TAG, + "App version: %" PRIu8 ".%" PRIu8 ".%" PRIu8, + TOTP_APP_VERSION_MAJOR, + TOTP_APP_VERSION_MINOR, + TOTP_APP_VERSION_PATCH); + FURI_LOG_I(LOGGING_TAG, "WolfSSL version: " LIBWOLFSSL_VERSION_STRING); + PluginState* plugin_state = malloc(sizeof(PluginState)); furi_check(plugin_state != NULL); @@ -184,7 +230,7 @@ int32_t totp_app() { return 254; } - TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state, event_queue); + TotpCliContext* cli_context = totp_cli_register_command_handler(plugin_state); if(!totp_activate_initial_scene(plugin_state)) { FURI_LOG_E(LOGGING_TAG, "An error ocurred during activating initial scene\r\n"); @@ -193,12 +239,16 @@ int32_t totp_app() { } // Affecting dolphin level - // dolphin_deed(DolphinDeedPluginStart); + dolphin_deed(DolphinDeedPluginStart); + + FuriMutex* main_loop_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + struct TotpRenderCallbackContext render_context = { + .plugin_state = plugin_state, .mutex = main_loop_mutex}; // Set system callbacks ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, plugin_state); - view_port_input_callback_set(view_port, input_callback, event_queue); + view_port_draw_callback_set(view_port, render_callback, &render_context); + view_port_input_callback_set(view_port, input_callback, plugin_state->event_queue); // Open GUI and register view_port gui_add_view_port(plugin_state->gui, view_port, GuiLayerFullscreen); @@ -206,24 +256,24 @@ int32_t totp_app() { PluginEvent event; bool processing = true; while(processing) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - - if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) == FuriStatusOk) { - if(event_status == FuriStatusOk) { + if(furi_message_queue_get(plugin_state->event_queue, &event, FuriWaitForever) == + FuriStatusOk) { + if(event.type == EventForceCloseApp) { + processing = false; + } else if(event.type == EventForceRedraw) { + processing = true; //-V1048 + } else if(furi_mutex_acquire(main_loop_mutex, FuriWaitForever) == FuriStatusOk) { if(event.type == EventTypeKey && plugin_state->idle_timeout_context != NULL) { idle_timeout_report_activity(plugin_state->idle_timeout_context); } - if(event.type == EventForceCloseApp) { - processing = false; - } else { - processing = totp_scene_director_handle_event(&event, plugin_state); - } - } + processing = totp_scene_director_handle_event(&event, plugin_state); - view_port_update(view_port); - furi_mutex_release(plugin_state->mutex); + furi_mutex_release(main_loop_mutex); + } } + + view_port_update(view_port); } totp_cli_unregister_command_handler(cli_context); @@ -232,7 +282,7 @@ int32_t totp_app() { view_port_enabled_set(view_port, false); gui_remove_view_port(plugin_state->gui, view_port); view_port_free(view_port); - furi_message_queue_free(event_queue); + furi_mutex_free(main_loop_mutex); totp_plugin_state_free(plugin_state); return 0; } diff --git a/applications/external/totp/types/automation_kb_layout.h b/applications/external/totp/types/automation_kb_layout.h new file mode 100644 index 000000000..9c23e91ab --- /dev/null +++ b/applications/external/totp/types/automation_kb_layout.h @@ -0,0 +1,8 @@ +#pragma once + +typedef uint8_t AutomationKeyboardLayout; + +enum AutomationKeyboardLayouts { + AutomationKeyboardLayoutQWERTY = 0, + AutomationKeyboardLayoutAZERTY = 1 +}; \ No newline at end of file diff --git a/applications/external/totp/types/automation_method.h b/applications/external/totp/types/automation_method.h index b51e59e03..24c85ee76 100644 --- a/applications/external/totp/types/automation_method.h +++ b/applications/external/totp/types/automation_method.h @@ -1,13 +1,13 @@ #pragma once -#include "../features_config.h" +#include "../config/app/config.h" typedef uint8_t AutomationMethod; enum AutomationMethods { AutomationMethodNone = 0b00, AutomationMethodBadUsb = 0b01, -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED AutomationMethodBadBt = 0b10, #endif }; diff --git a/applications/external/totp/types/crypto_settings.h b/applications/external/totp/types/crypto_settings.h new file mode 100644 index 000000000..22daf147e --- /dev/null +++ b/applications/external/totp/types/crypto_settings.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include "../services/crypto/constants.h" + +typedef struct { + /** + * @brief Crypto key slot to be used + */ + uint8_t crypto_key_slot; + + /** + * @brief Crypto algorithms version to be used + */ + uint8_t crypto_version; + + /** + * @brief Initialization vector (IV) to be used for encryption\decryption + */ + uint8_t iv[CRYPTO_IV_LENGTH]; + + /** + * @brief Randomly-generated salt + */ + uint8_t salt[CRYPTO_SALT_LENGTH]; + + /** + * @brief Encrypted well-known data + */ + uint8_t* crypto_verify_data; + + /** + * @brief Encrypted well-known data length + */ + size_t crypto_verify_data_length; + + /** + * @brief Whether user's PIN is required or not + */ + bool pin_required; +} CryptoSettings; \ No newline at end of file diff --git a/applications/external/totp/types/event_type.h b/applications/external/totp/types/event_type.h index 7bdf6981f..138f528d8 100644 --- a/applications/external/totp/types/event_type.h +++ b/applications/external/totp/types/event_type.h @@ -3,4 +3,4 @@ typedef uint8_t EventType; -enum EventTypes { EventTypeTick, EventTypeKey, EventForceCloseApp }; +enum EventTypes { EventTypeTick, EventTypeKey, EventForceCloseApp, EventForceRedraw }; diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index 87ed51abd..388da5edf 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -3,17 +3,17 @@ #include #include #include -#include "../features_config.h" +#include "../config/app/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 +#include "automation_kb_layout.h" +#ifdef TOTP_BADBT_AUTOMATION_ENABLED #include "../workers/bt_type_code/bt_type_code.h" #endif - -#define TOTP_IV_SIZE (16) +#include "crypto_settings.h" /** * @brief Application state structure @@ -29,11 +29,6 @@ typedef struct { */ void* current_scene_state; - /** - * @brief Reference to the firmware notification subsystem - */ - NotificationApp* notification_app; - /** * @brief Reference to the firmware dialogs subsystem */ @@ -54,47 +49,22 @@ typedef struct { */ ConfigFileContext* config_file_context; - /** - * @brief Encrypted well-known string data - */ - uint8_t* crypto_verify_data; - - /** - * @brief Encrypted well-known string data length - */ - size_t crypto_verify_data_length; - - /** - * @brief Whether PIN is set by user or not - */ - bool pin_set; - - /** - * @brief Initialization vector (IV) to be used for encryption\decryption - */ - uint8_t iv[TOTP_IV_SIZE]; - - /** - * @brief Basic randomly-generated initialization vector (IV) - */ - uint8_t base_iv[TOTP_IV_SIZE]; - /** * @brief Notification method */ NotificationMethod notification_method; - /** - * @brief Main rendering loop mutex - */ - FuriMutex* mutex; - /** * @brief Automation method */ AutomationMethod automation_method; -#ifdef TOTP_BADBT_TYPE_ENABLED + /** + * @brief Automation keyboard layout to be used + */ + AutomationKeyboardLayout automation_kb_layout; + +#ifdef TOTP_BADBT_AUTOMATION_ENABLED /** * @brief Bad-Bluetooth worker context */ @@ -110,4 +80,14 @@ typedef struct { * @brief Font index to be used to draw TOTP token */ uint8_t active_font_index; + + /** + * @brief Application even queue + */ + FuriMessageQueue* event_queue; + + /** + * @brief Crypto settings + */ + CryptoSettings crypto_settings; } PluginState; diff --git a/applications/external/totp/types/token_info.c b/applications/external/totp/types/token_info.c index ab47f4e3e..1d1e73160 100644 --- a/applications/external/totp/types/token_info.c +++ b/applications/external/totp/types/token_info.c @@ -1,9 +1,10 @@ #include "token_info.h" +#include #include #include #include #include "common.h" -#include "../services/crypto/crypto.h" +#include "../services/crypto/crypto_facade.h" TokenInfo* token_info_alloc() { TokenInfo* tokenInfo = malloc(sizeof(TokenInfo)); @@ -25,7 +26,7 @@ bool token_info_set_secret( const char* plain_token_secret, size_t token_secret_length, PlainTokenSecretEncoding plain_token_secret_encoding, - const uint8_t* iv) { + const CryptoSettings* crypto_settings) { if(token_secret_length == 0) return false; uint8_t* plain_secret; size_t plain_secret_length; @@ -54,8 +55,8 @@ bool token_info_set_secret( free(token_info->token); } - token_info->token = - totp_crypto_encrypt(plain_secret, plain_secret_length, iv, &token_info->token_length); + token_info->token = totp_crypto_encrypt( + plain_secret, plain_secret_length, crypto_settings, &token_info->token_length); result = true; } else { result = false; diff --git a/applications/external/totp/types/token_info.h b/applications/external/totp/types/token_info.h index 163a0492e..969445dff 100644 --- a/applications/external/totp/types/token_info.h +++ b/applications/external/totp/types/token_info.h @@ -1,8 +1,9 @@ #pragma once -#include +#include #include #include +#include "crypto_settings.h" #define TOKEN_HASH_ALGO_SHA1_NAME "sha1" #define TOKEN_HASH_ALGO_STEAM_NAME "steam" @@ -200,7 +201,7 @@ void token_info_free(TokenInfo* token_info); * @param plain_token_secret plain token secret * @param token_secret_length plain token secret length * @param plain_token_secret_encoding plain token secret encoding - * @param iv initialization vecor (IV) to be used for encryption + * @param crypto_settings crypto settings * @return \c true if token successfully set; \c false otherwise */ bool token_info_set_secret( @@ -208,7 +209,7 @@ bool token_info_set_secret( const char* plain_token_secret, size_t token_secret_length, PlainTokenSecretEncoding plain_token_secret_encoding, - const uint8_t* iv); + const CryptoSettings* crypto_settings); /** * @brief Sets token digits count from \c uint8_t value diff --git a/applications/external/totp/ui/canvas_extensions.h b/applications/external/totp/ui/canvas_extensions.h index ab5519140..2e053b488 100644 --- a/applications/external/totp/ui/canvas_extensions.h +++ b/applications/external/totp/ui/canvas_extensions.h @@ -4,6 +4,15 @@ #include #include +/** + * @brief Draw string using given font + * @param canvas canvas to draw string at + * @param x horizontal position + * @param y vertical position + * @param text string to draw + * @param text_length string length + * @param font font to be used to draw string + */ void canvas_draw_str_ex( Canvas* canvas, uint8_t x, diff --git a/applications/external/totp/ui/common_dialogs.h b/applications/external/totp/ui/common_dialogs.h index 187d0e95c..1ddd80a75 100644 --- a/applications/external/totp/ui/common_dialogs.h +++ b/applications/external/totp/ui/common_dialogs.h @@ -3,5 +3,16 @@ #include #include "../types/plugin_state.h" +/** + * @brief Shows standard dialog about the fact that error occurred when loading config file + * @param plugin_state application state + * @return dialog button which user pressed to close the dialog + */ DialogMessageButton totp_dialogs_config_loading_error(PluginState* plugin_state); + +/** + * @brief Shows standard dialog about the fact that error occurred when updating config file + * @param plugin_state application state + * @return dialog button which user pressed to close the dialog + */ DialogMessageButton totp_dialogs_config_updating_error(PluginState* plugin_state); \ No newline at end of file diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index 657762a94..cddbd7136 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -1,8 +1,11 @@ #include "../types/common.h" +#include "../config/app/config.h" #include "scene_director.h" #include "scenes/authenticate/totp_scene_authenticate.h" #include "scenes/generate_token/totp_scene_generate_token.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include "scenes/add_new_token/totp_scene_add_new_token.h" +#endif #include "scenes/token_menu/totp_scene_token_menu.h" #include "scenes/app_settings/totp_app_settings.h" #include "scenes/standby/standby.h" @@ -16,9 +19,11 @@ void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene s case TotpSceneAuthentication: totp_scene_authenticate_activate(plugin_state); break; +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED case TotpSceneAddNewToken: totp_scene_add_new_token_activate(plugin_state); break; +#endif case TotpSceneTokenMenu: totp_scene_token_menu_activate(plugin_state); break; @@ -45,9 +50,11 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state case TotpSceneAuthentication: totp_scene_authenticate_deactivate(plugin_state); break; +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED case TotpSceneAddNewToken: totp_scene_add_new_token_deactivate(plugin_state); break; +#endif case TotpSceneTokenMenu: totp_scene_token_menu_deactivate(plugin_state); break; @@ -70,9 +77,11 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ case TotpSceneAuthentication: totp_scene_authenticate_render(canvas, plugin_state); break; +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED case TotpSceneAddNewToken: totp_scene_add_new_token_render(canvas, plugin_state); break; +#endif case TotpSceneTokenMenu: totp_scene_token_menu_render(canvas, plugin_state); break; @@ -98,9 +107,11 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con case TotpSceneAuthentication: processing = totp_scene_authenticate_handle_event(event, plugin_state); break; +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED case TotpSceneAddNewToken: processing = totp_scene_add_new_token_handle_event(event, plugin_state); break; +#endif case TotpSceneTokenMenu: processing = totp_scene_token_menu_handle_event(event, plugin_state); break; @@ -116,3 +127,8 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con return processing; } + +void totp_scene_director_force_redraw(PluginState* const plugin_state) { + PluginEvent event = {.type = EventForceRedraw}; + furi_message_queue_put(plugin_state->event_queue, &event, FuriWaitForever); +} \ No newline at end of file diff --git a/applications/external/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h index e45223997..1f09f9ea9 100644 --- a/applications/external/totp/ui/scene_director.h +++ b/applications/external/totp/ui/scene_director.h @@ -33,3 +33,9 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ * @return \c true if event handled and applilcation should continue; \c false if application should be closed */ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* const plugin_state); + +/** + * @brief Forces screen to be redraw\updated + * @param plugin_state application state + */ +void totp_scene_director_force_redraw(PluginState* const plugin_state); \ No newline at end of file diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c index 95aa4fbaa..389d651e7 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c @@ -1,5 +1,6 @@ #include "totp_input_text.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include #include @@ -50,3 +51,4 @@ void totp_input_text(Gui* gui, const char* header_text, InputTextResult* result) view_dispatcher_free(view_dispatcher); text_input_free(text_input); } +#endif diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h index 7ec116a9b..89980ad35 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h @@ -1,5 +1,7 @@ #pragma once +#include "../../../config/app/config.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include #define INPUT_BUFFER_SIZE (255) @@ -11,3 +13,4 @@ typedef struct { } InputTextResult; void totp_input_text(Gui* gui, const char* header_text, InputTextResult* result); +#endif diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index 6856d1e30..ae0a7bd48 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -1,4 +1,5 @@ #include "totp_scene_add_new_token.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include "../../../types/common.h" #include "../../constants.h" #include "../../scene_director.h" @@ -8,7 +9,6 @@ #include "../../ui_controls.h" #include "../../common_dialogs.h" #include -#include "../generate_token/totp_scene_generate_token.h" char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"}; char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"}; @@ -42,7 +42,7 @@ typedef struct { struct TotpAddContext { SceneState* scene_state; - uint8_t* iv; + const CryptoSettings* crypto_settings; }; enum TotpIteratorUpdateTokenResultsEx { TotpIteratorUpdateTokenResultInvalidSecret = 1 }; @@ -58,7 +58,7 @@ static TotpIteratorUpdateTokenResult add_token_handler(TokenInfo* tokenInfo, con context_t->scene_state->token_secret, context_t->scene_state->token_secret_length, PlainTokenSecretEncodingBase32, - context_t->iv)) { + context_t->crypto_settings)) { return TotpIteratorUpdateTokenResultInvalidSecret; } @@ -271,7 +271,7 @@ bool totp_scene_add_new_token_handle_event( break; case ConfirmButton: { struct TotpAddContext add_context = { - .iv = plugin_state->iv, .scene_state = scene_state}; + .scene_state = scene_state, .crypto_settings = &plugin_state->crypto_settings}; TokenInfoIteratorContext* iterator_context = totp_config_get_token_iterator_context(plugin_state); TotpIteratorUpdateTokenResult add_result = totp_token_info_iterator_add_new_token( @@ -318,3 +318,4 @@ void totp_scene_add_new_token_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } +#endif diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h index 7297869d0..686cbe37c 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h @@ -1,5 +1,7 @@ #pragma once +#include "../../../config/app/config.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" @@ -10,3 +12,4 @@ bool totp_scene_add_new_token_handle_event( const PluginEvent* const event, PluginState* plugin_state); void totp_scene_add_new_token_deactivate(PluginState* plugin_state); +#endif 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 c65557cfc..9369ad44d 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,26 +1,41 @@ #include "totp_app_settings.h" #include -#include +#include "totp_icons.h" #include #include #include "../../canvas_extensions.h" #include "../../ui_controls.h" #include "../../common_dialogs.h" #include "../../scene_director.h" -#include "../token_menu/totp_scene_token_menu.h" #include "../../constants.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" #include -#include "../../../features_config.h" -#ifdef TOTP_BADBT_TYPE_ENABLED +#include "../../../config/app/config.h" +#ifdef TOTP_BADBT_AUTOMATION_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif +#ifdef TOTP_BADBT_AUTOMATION_ENABLED +#define AUTOMATION_LIST_MAX_INDEX (3) +#else +#define AUTOMATION_LIST_MAX_INDEX (1) +#endif +#define BAD_KB_LAYOUT_LIST_MAX_INDEX (1) +#define FONT_TEST_STR_LENGTH (7) + static const char* YES_NO_LIST[] = {"NO", "YES"}; -static const char* ON_OFF_LIST[] = {"OFF", "ON"}; +static const char* AUTOMATION_LIST[] = { + "None", + "USB" +#ifdef TOTP_BADBT_AUTOMATION_ENABLED + , + "Bluetooth", + "BT and USB" +#endif +}; +static const char* BAD_KB_LAYOUT_LIST[] = {"QWERTY", "AZERTY"}; static const char* FONT_TEST_STR = "0123BCD"; -static const uint8_t FONT_TEST_STR_LENGTH = 7; typedef enum { HoursInput, @@ -28,10 +43,8 @@ typedef enum { FontSelect, SoundSwitch, VibroSwitch, - BadUsbSwitch, -#ifdef TOTP_BADBT_TYPE_ENABLED - BadBtSwitch, -#endif + AutomationSwitch, + BadKeyboardLayoutSelect, ConfirmButton } Control; @@ -40,11 +53,9 @@ typedef struct { uint8_t tz_offset_minutes; bool notification_sound; bool notification_vibro; - bool badusb_enabled; -#ifdef TOTP_BADBT_TYPE_ENABLED - bool badbt_enabled; -#endif - uint8_t y_offset; + AutomationMethod automation_method; + uint16_t y_offset; + AutomationKeyboardLayout automation_kb_layout; Control selected_control; uint8_t active_font; } SceneState; @@ -60,10 +71,11 @@ void totp_scene_app_settings_activate(PluginState* plugin_state) { scene_state->tz_offset_minutes = 60.0f * off_dec; scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound; scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro; - scene_state->badusb_enabled = plugin_state->automation_method & AutomationMethodBadUsb; -#ifdef TOTP_BADBT_TYPE_ENABLED - scene_state->badbt_enabled = plugin_state->automation_method & AutomationMethodBadBt; -#endif + scene_state->automation_method = + MIN(plugin_state->automation_method, AUTOMATION_LIST_MAX_INDEX); + scene_state->automation_kb_layout = + MIN(plugin_state->automation_kb_layout, BAD_KB_LAYOUT_LIST_MAX_INDEX); + scene_state->active_font = plugin_state->active_font_index; } @@ -83,127 +95,121 @@ static void two_digit_to_str(int8_t num, char* str) { void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state) { const SceneState* scene_state = plugin_state->current_scene_state; + if(scene_state->selected_control < FontSelect) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset"); + canvas_set_font(canvas, FontSecondary); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset"); - canvas_set_font(canvas, FontSecondary); + char tmp_str[4]; + two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); + canvas_draw_str_aligned( + canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); + ui_control_select_render( + canvas, + 36, + 10 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + &tmp_str[0], + scene_state->selected_control == HoursInput); - char tmp_str[4]; - two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]); - canvas_draw_str_aligned(canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:"); - ui_control_select_render( - canvas, - 36, - 10 - scene_state->y_offset, - SCREEN_WIDTH - 36, - &tmp_str[0], - scene_state->selected_control == HoursInput); + two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); + canvas_draw_str_aligned( + canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); + ui_control_select_render( + canvas, + 36, + 28 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + &tmp_str[0], + scene_state->selected_control == MinutesInput); - two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]); - canvas_draw_str_aligned( - canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:"); - ui_control_select_render( - canvas, - 36, - 28 - scene_state->y_offset, - SCREEN_WIDTH - 36, - &tmp_str[0], - scene_state->selected_control == MinutesInput); + } else if(scene_state->selected_control < SoundSwitch) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Font"); + canvas_set_font(canvas, FontSecondary); - canvas_draw_icon( - canvas, - SCREEN_WIDTH_CENTER - 5, - SCREEN_HEIGHT - 5 - scene_state->y_offset, - &I_totp_arrow_bottom_10x5); + const FONT_INFO* const font = available_fonts[scene_state->active_font]; + ui_control_select_render( + canvas, + 0, + 74 - scene_state->y_offset, + SCREEN_WIDTH - UI_CONTROL_VSCROLL_WIDTH, + font->name, + scene_state->selected_control == FontSelect); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Font"); - canvas_set_font(canvas, FontSecondary); + uint8_t font_x_offset = + SCREEN_WIDTH_CENTER - + (((font->charInfo[0].width + font->spacePixels) * FONT_TEST_STR_LENGTH) >> 1); + uint8_t font_y_offset = 108 - scene_state->y_offset - (font->height >> 1); + canvas_draw_str_ex( + canvas, font_x_offset, font_y_offset, FONT_TEST_STR, FONT_TEST_STR_LENGTH, font); - const FONT_INFO* const font = available_fonts[scene_state->active_font]; - ui_control_select_render( - canvas, - 0, - 74 - scene_state->y_offset, - SCREEN_WIDTH, - font->name, - scene_state->selected_control == FontSelect); + } else if(scene_state->selected_control < AutomationSwitch) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications"); + canvas_set_font(canvas, FontSecondary); - uint8_t font_x_offset = - SCREEN_WIDTH_CENTER - - (((font->charInfo[0].width + font->spacePixels) * FONT_TEST_STR_LENGTH) >> 1); - uint8_t font_y_offset = 108 - scene_state->y_offset - (font->height >> 1); - canvas_draw_str_ex( - canvas, font_x_offset, font_y_offset, FONT_TEST_STR, FONT_TEST_STR_LENGTH, font); + canvas_draw_str_aligned( + canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); + ui_control_select_render( + canvas, + 36, + 138 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + YES_NO_LIST[scene_state->notification_sound], + scene_state->selected_control == SoundSwitch); - canvas_draw_icon( - canvas, SCREEN_WIDTH_CENTER - 5, 123 - scene_state->y_offset, &I_totp_arrow_bottom_10x5); + canvas_draw_str_aligned( + canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); + ui_control_select_render( + canvas, + 36, + 156 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + YES_NO_LIST[scene_state->notification_vibro], + scene_state->selected_control == VibroSwitch); + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 0, 192 - scene_state->y_offset, AlignLeft, AlignTop, "Automation"); + canvas_set_font(canvas, FontSecondary); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications"); - canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 0, 209 - scene_state->y_offset, AlignLeft, AlignTop, "Method:"); + ui_control_select_render( + canvas, + 36, + 202 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + AUTOMATION_LIST[scene_state->automation_method], + scene_state->selected_control == AutomationSwitch); - canvas_draw_str_aligned(canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:"); - ui_control_select_render( - canvas, - 36, - 138 - scene_state->y_offset, - SCREEN_WIDTH - 36, - YES_NO_LIST[scene_state->notification_sound], - scene_state->selected_control == SoundSwitch); + canvas_draw_str_aligned( + canvas, 0, 227 - scene_state->y_offset, AlignLeft, AlignTop, "Layout:"); - canvas_draw_str_aligned(canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:"); - ui_control_select_render( - canvas, - 36, - 156 - scene_state->y_offset, - SCREEN_WIDTH - 36, - YES_NO_LIST[scene_state->notification_vibro], - scene_state->selected_control == VibroSwitch); + ui_control_select_render( + canvas, + 36, + 220 - scene_state->y_offset, + SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH, + BAD_KB_LAYOUT_LIST[scene_state->automation_kb_layout], + scene_state->selected_control == BadKeyboardLayoutSelect); - canvas_draw_icon( - canvas, SCREEN_WIDTH_CENTER - 5, 187 - scene_state->y_offset, &I_totp_arrow_bottom_10x5); + ui_control_button_render( + canvas, + SCREEN_WIDTH_CENTER - 24, + 242 - scene_state->y_offset, + 48, + 13, + "Confirm", + scene_state->selected_control == ConfirmButton); + } - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 0, 192 - scene_state->y_offset, AlignLeft, AlignTop, "Automation"); - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 0, 209 - scene_state->y_offset, AlignLeft, AlignTop, "BadUSB:"); - ui_control_select_render( - canvas, - 36, - 202 - scene_state->y_offset, - SCREEN_WIDTH - 36, - ON_OFF_LIST[scene_state->badusb_enabled], - scene_state->selected_control == BadUsbSwitch); - -#ifdef TOTP_BADBT_TYPE_ENABLED - canvas_draw_str_aligned(canvas, 0, 227 - scene_state->y_offset, AlignLeft, AlignTop, "BadBT:"); - ui_control_select_render( - canvas, - 36, - 220 - scene_state->y_offset, - SCREEN_WIDTH - 36, - ON_OFF_LIST[scene_state->badbt_enabled], - scene_state->selected_control == BadBtSwitch); -#endif - - ui_control_button_render( - canvas, - SCREEN_WIDTH_CENTER - 24, -#ifdef TOTP_BADBT_TYPE_ENABLED - 242 - scene_state->y_offset, -#else - 229 - scene_state->y_offset, -#endif - 48, - 13, - "Confirm", - scene_state->selected_control == ConfirmButton); + ui_control_vscroll_render( + canvas, SCREEN_WIDTH - 3, 0, SCREEN_HEIGHT, scene_state->selected_control, ConfirmButton); } bool totp_scene_app_settings_handle_event( @@ -268,14 +274,21 @@ bool totp_scene_app_settings_handle_event( scene_state->notification_sound = !scene_state->notification_sound; } else if(scene_state->selected_control == VibroSwitch) { scene_state->notification_vibro = !scene_state->notification_vibro; - } else if(scene_state->selected_control == BadUsbSwitch) { - scene_state->badusb_enabled = !scene_state->badusb_enabled; + } else if(scene_state->selected_control == AutomationSwitch) { + totp_roll_value_uint8_t( + &scene_state->automation_method, + 1, + 0, + AUTOMATION_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == BadKeyboardLayoutSelect) { + totp_roll_value_uint8_t( + &scene_state->automation_kb_layout, + 1, + 0, + BAD_KB_LAYOUT_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); } -#ifdef TOTP_BADBT_TYPE_ENABLED - else if(scene_state->selected_control == BadBtSwitch) { - scene_state->badbt_enabled = !scene_state->badbt_enabled; - } -#endif break; case InputKeyLeft: if(scene_state->selected_control == HoursInput) { @@ -295,14 +308,21 @@ bool totp_scene_app_settings_handle_event( scene_state->notification_sound = !scene_state->notification_sound; } else if(scene_state->selected_control == VibroSwitch) { scene_state->notification_vibro = !scene_state->notification_vibro; - } else if(scene_state->selected_control == BadUsbSwitch) { - scene_state->badusb_enabled = !scene_state->badusb_enabled; + } else if(scene_state->selected_control == AutomationSwitch) { + totp_roll_value_uint8_t( + &scene_state->automation_method, + -1, + 0, + AUTOMATION_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); + } else if(scene_state->selected_control == BadKeyboardLayoutSelect) { + totp_roll_value_uint8_t( + &scene_state->automation_kb_layout, + -1, + 0, + BAD_KB_LAYOUT_LIST_MAX_INDEX, + RollOverflowBehaviorRoll); } -#ifdef TOTP_BADBT_TYPE_ENABLED - else if(scene_state->selected_control == BadBtSwitch) { - scene_state->badbt_enabled = !scene_state->badbt_enabled; - } -#endif break; case InputKeyOk: break; @@ -323,22 +343,18 @@ bool totp_scene_app_settings_handle_event( (scene_state->notification_sound ? NotificationMethodSound : NotificationMethodNone) | (scene_state->notification_vibro ? NotificationMethodVibro : NotificationMethodNone); - plugin_state->automation_method = scene_state->badusb_enabled ? AutomationMethodBadUsb : - AutomationMethodNone; -#ifdef TOTP_BADBT_TYPE_ENABLED - plugin_state->automation_method |= scene_state->badbt_enabled ? AutomationMethodBadBt : - AutomationMethodNone; -#endif - + plugin_state->automation_method = scene_state->automation_method; plugin_state->active_font_index = scene_state->active_font; + plugin_state->automation_kb_layout = scene_state->automation_kb_layout; if(!totp_config_file_update_user_settings(plugin_state)) { totp_dialogs_config_updating_error(plugin_state); return false; } -#ifdef TOTP_BADBT_TYPE_ENABLED - if(!scene_state->badbt_enabled && plugin_state->bt_type_code_worker_context != NULL) { +#ifdef TOTP_BADBT_AUTOMATION_ENABLED + if((scene_state->automation_method & AutomationMethodBadBt) == 0 && + plugin_state->bt_type_code_worker_context != NULL) { totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context); plugin_state->bt_type_code_worker_context = NULL; } 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 23a919ed7..6913b99e8 100644 --- a/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c +++ b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c @@ -1,16 +1,16 @@ #include "totp_scene_authenticate.h" #include -#include +#include "totp_icons.h" #include #include "../../../types/common.h" #include "../../constants.h" #include "../../../services/config/config.h" #include "../../scene_director.h" #include "../../totp_scenes_enum.h" -#include "../../../services/crypto/crypto.h" +#include "../../../services/crypto/crypto_facade.h" #include "../../../types/user_pin_codes.h" -#define MAX_CODE_LENGTH TOTP_IV_SIZE +#define MAX_CODE_LENGTH CRYPTO_IV_LENGTH static const uint8_t PIN_ASTERISK_RADIUS = 3; static const uint8_t PIN_ASTERISK_STEP = (PIN_ASTERISK_RADIUS << 1) + 2; @@ -25,7 +25,7 @@ void totp_scene_authenticate_activate(PluginState* plugin_state) { scene_state->code_length = 0; memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); plugin_state->current_scene_state = scene_state; - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); } void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_state) { @@ -36,7 +36,7 @@ void totp_scene_authenticate_render(Canvas* const canvas, PluginState* plugin_st v_shift = -10; } - if(plugin_state->crypto_verify_data == NULL) { + if(plugin_state->crypto_settings.crypto_verify_data == NULL) { canvas_draw_str_aligned( canvas, SCREEN_WIDTH_CENTER, @@ -124,20 +124,22 @@ bool totp_scene_authenticate_handle_event( } } else if(event->input.type == InputTypeRelease && event->input.key == InputKeyOk) { CryptoSeedIVResult seed_result = totp_crypto_seed_iv( - plugin_state, &scene_state->code_input[0], scene_state->code_length); + &plugin_state->crypto_settings, &scene_state->code_input[0], scene_state->code_length); if(seed_result & CryptoSeedIVResultFlagSuccess && seed_result & CryptoSeedIVResultFlagNewCryptoVerifyData) { totp_config_file_update_crypto_signatures(plugin_state); } - if(totp_crypto_verify_key(plugin_state)) { + if(totp_crypto_verify_key(&plugin_state->crypto_settings)) { FURI_LOG_D(LOGGING_TAG, "PIN is valid"); + totp_config_file_ensure_latest_encryption( + plugin_state, &scene_state->code_input[0], scene_state->code_length); totp_scene_director_activate_scene(plugin_state, TotpSceneGenerateToken); } else { FURI_LOG_D(LOGGING_TAG, "PIN is NOT valid"); memset(&scene_state->code_input[0], 0, MAX_CODE_LENGTH); - memset(&plugin_state->iv[0], 0, TOTP_IV_SIZE); + memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); scene_state->code_length = 0; DialogMessage* message = dialog_message_alloc(); 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 e4a60b1e0..230438e63 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 @@ -1,22 +1,21 @@ +#include "totp_scene_generate_token.h" #include #include #include -#include +#include "totp_icons.h" #include #include #include -#include "totp_scene_generate_token.h" #include "../../canvas_extensions.h" #include "../../../types/token_info.h" #include "../../../types/common.h" #include "../../constants.h" #include "../../../services/config/config.h" #include "../../scene_director.h" -#include "../token_menu/totp_scene_token_menu.h" -#include "../../../features_config.h" +#include "../../../config/app/config.h" #include "../../../workers/generate_totp_code/generate_totp_code.h" #include "../../../workers/usb_type_code/usb_type_code.h" -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif @@ -34,50 +33,40 @@ typedef struct { typedef struct { char last_code[TokenDigitsCountMax + 1]; TotpUsbTypeCodeWorkerContext* usb_type_code_worker_context; - NotificationMessage const** notification_sequence_new_token; - NotificationMessage const** notification_sequence_automation; + NotificationMessage const* notification_sequence_new_token[8]; + NotificationMessage const* notification_sequence_automation[11]; FuriMutex* last_code_update_sync; TotpGenerateCodeWorkerContext* generate_code_worker_context; UiPrecalculatedDimensions ui_precalculated_dimensions; const FONT_INFO* active_font; + NotificationApp* notification_app; } SceneState; static const NotificationSequence* get_notification_sequence_new_token(const PluginState* plugin_state, SceneState* scene_state) { - if(scene_state->notification_sequence_new_token == NULL) { - uint8_t i = 0; - uint8_t length = 4; + if(scene_state->notification_sequence_new_token[0] == NULL) { + NotificationMessage const** sequence = &scene_state->notification_sequence_new_token[0]; + *(sequence++) = &message_display_backlight_on; + *(sequence++) = &message_green_255; if(plugin_state->notification_method & NotificationMethodVibro) { - length += 2; + *(sequence++) = &message_vibro_on; } if(plugin_state->notification_method & NotificationMethodSound) { - length += 2; + *(sequence++) = &message_note_c5; } - scene_state->notification_sequence_new_token = malloc(sizeof(void*) * length); - furi_check(scene_state->notification_sequence_new_token != NULL); - scene_state->notification_sequence_new_token[i++] = &message_display_backlight_on; - scene_state->notification_sequence_new_token[i++] = &message_green_255; + *(sequence++) = &message_delay_50; + if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_new_token[i++] = &message_vibro_on; + *(sequence++) = &message_vibro_off; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_new_token[i++] = &message_note_c5; + *(sequence++) = &message_sound_off; } - scene_state->notification_sequence_new_token[i++] = &message_delay_50; - - if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_new_token[i++] = &message_vibro_off; - } - - if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_new_token[i++] = &message_sound_off; - } - - scene_state->notification_sequence_new_token[i++] = NULL; + *(sequence++) = NULL; } return (NotificationSequence*)scene_state->notification_sequence_new_token; @@ -85,44 +74,33 @@ static const NotificationSequence* static const NotificationSequence* get_notification_sequence_automation(const PluginState* plugin_state, SceneState* scene_state) { - if(scene_state->notification_sequence_automation == NULL) { - uint8_t i = 0; - uint8_t length = 3; + if(scene_state->notification_sequence_automation[0] == NULL) { + NotificationMessage const** sequence = &scene_state->notification_sequence_automation[0]; + + *(sequence++) = &message_blue_255; if(plugin_state->notification_method & NotificationMethodVibro) { - length += 2; + *(sequence++) = &message_vibro_on; } if(plugin_state->notification_method & NotificationMethodSound) { - length += 6; + *(sequence++) = &message_note_d5; //-V525 + *(sequence++) = &message_delay_50; + *(sequence++) = &message_note_e4; + *(sequence++) = &message_delay_50; + *(sequence++) = &message_note_f3; } - scene_state->notification_sequence_automation = malloc(sizeof(void*) * length); - furi_check(scene_state->notification_sequence_automation != NULL); + *(sequence++) = &message_delay_50; - scene_state->notification_sequence_automation[i++] = &message_blue_255; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_automation[i++] = &message_vibro_on; + *(sequence++) = &message_vibro_off; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_automation[i++] = &message_note_d5; //-V525 - scene_state->notification_sequence_automation[i++] = &message_delay_50; - scene_state->notification_sequence_automation[i++] = &message_note_e4; - scene_state->notification_sequence_automation[i++] = &message_delay_50; - scene_state->notification_sequence_automation[i++] = &message_note_f3; + *(sequence++) = &message_sound_off; } - scene_state->notification_sequence_automation[i++] = &message_delay_50; - - if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_automation[i++] = &message_vibro_off; - } - - if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_automation[i++] = &message_sound_off; - } - - scene_state->notification_sequence_automation[i++] = NULL; + *(sequence++) = NULL; } return (NotificationSequence*)scene_state->notification_sequence_automation; @@ -154,7 +132,7 @@ static void draw_totp_code(Canvas* const canvas, const PluginState* const plugin } static void on_new_token_code_generated(bool time_left, void* context) { - const PluginState* plugin_state = context; + PluginState* const plugin_state = context; const TokenInfoIteratorContext* iterator_context = totp_config_get_token_iterator_context(plugin_state); if(totp_token_info_iterator_get_total_count(iterator_context) == 0) { @@ -175,13 +153,16 @@ static void on_new_token_code_generated(bool time_left, void* context) { if(time_left) { notification_message( - plugin_state->notification_app, - get_notification_sequence_new_token(plugin_state, plugin_state->current_scene_state)); + scene_state->notification_app, + get_notification_sequence_new_token(plugin_state, scene_state)); } + + totp_scene_director_force_redraw(plugin_state); } static void on_code_lifetime_updated_generated(float code_lifetime_percent, void* context) { - SceneState* scene_state = context; + PluginState* const plugin_state = context; + SceneState* scene_state = plugin_state->current_scene_state; scene_state->ui_precalculated_dimensions.progress_bar_width = (uint8_t)((float)(SCREEN_WIDTH - (PROGRESS_BAR_MARGIN << 1)) * code_lifetime_percent); scene_state->ui_precalculated_dimensions.progress_bar_x = @@ -189,6 +170,7 @@ static void on_code_lifetime_updated_generated(float code_lifetime_percent, void scene_state->ui_precalculated_dimensions.progress_bar_width) >> 1) + PROGRESS_BAR_MARGIN; + totp_scene_director_force_redraw(plugin_state); } void totp_scene_generate_token_activate(PluginState* plugin_state) { @@ -201,12 +183,18 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { scene_state->last_code_update_sync = furi_mutex_alloc(FuriMutexTypeNormal); if(plugin_state->automation_method & AutomationMethodBadUsb) { scene_state->usb_type_code_worker_context = totp_usb_type_code_worker_start( - scene_state->last_code, TokenDigitsCountMax + 1, scene_state->last_code_update_sync); + scene_state->last_code, + TokenDigitsCountMax + 1, + scene_state->last_code_update_sync, + plugin_state->automation_kb_layout); } scene_state->active_font = available_fonts[plugin_state->active_font_index]; + scene_state->notification_app = furi_record_open(RECORD_NOTIFICATION); + scene_state->notification_sequence_automation[0] = NULL; + scene_state->notification_sequence_new_token[0] = NULL; -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt) { if(plugin_state->bt_type_code_worker_context == NULL) { @@ -216,7 +204,8 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { plugin_state->bt_type_code_worker_context, scene_state->last_code, TokenDigitsCountMax + 1, - scene_state->last_code_update_sync); + scene_state->last_code_update_sync, + plugin_state->automation_kb_layout); } #endif const TokenInfoIteratorContext* iterator_context = @@ -226,7 +215,7 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { totp_token_info_iterator_get_current_token(iterator_context), scene_state->last_code_update_sync, plugin_state->timezone_offset, - plugin_state->iv); + &plugin_state->crypto_settings); totp_generate_code_worker_set_code_generated_handler( scene_state->generate_code_worker_context, &on_new_token_code_generated, plugin_state); @@ -234,7 +223,7 @@ void totp_scene_generate_token_activate(PluginState* plugin_state) { totp_generate_code_worker_set_lifetime_changed_handler( scene_state->generate_code_worker_context, &on_code_lifetime_updated_generated, - scene_state); + plugin_state); update_totp_params( plugin_state, totp_token_info_iterator_get_current_token_index(iterator_context)); @@ -257,7 +246,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ SCREEN_HEIGHT_CENTER + 10, AlignCenter, AlignCenter, - "Press OK button to add"); + "Press OK button to open menu"); return; } @@ -298,11 +287,10 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ canvas, SCREEN_WIDTH - 8, SCREEN_HEIGHT_CENTER - 24, &I_totp_arrow_right_8x9); } -#ifdef TOTP_AUTOMATION_ICONS_ENABLED if(plugin_state->automation_method & AutomationMethodBadUsb) { canvas_draw_icon( canvas, -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED SCREEN_WIDTH_CENTER - (plugin_state->automation_method & AutomationMethodBadBt ? 33 : 15), #else @@ -313,7 +301,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ &I_hid_usb_31x9); } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt && plugin_state->bt_type_code_worker_context != NULL && totp_bt_type_code_worker_is_advertising(plugin_state->bt_type_code_worker_context)) { @@ -325,7 +313,6 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ &I_hid_ble_31x9); } #endif -#endif } bool totp_scene_generate_token_handle_event( @@ -351,11 +338,11 @@ bool totp_scene_generate_token_handle_event( TotpUsbTypeCodeWorkerEventType, totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( - plugin_state->notification_app, + scene_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); return true; } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED else if( event->input.key == InputKeyUp && plugin_state->automation_method & AutomationMethodBadBt) { @@ -367,7 +354,7 @@ bool totp_scene_generate_token_handle_event( TotpBtTypeCodeWorkerEventType, totp_token_info_iterator_get_current_token(iterator_context)->automation_features); notification_message( - plugin_state->notification_app, + scene_state->notification_app, get_notification_sequence_automation(plugin_state, scene_state)); return true; } @@ -428,23 +415,17 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { totp_generate_code_worker_stop(scene_state->generate_code_worker_context); + furi_record_close(RECORD_NOTIFICATION); + if(plugin_state->automation_method & AutomationMethodBadUsb) { totp_usb_type_code_worker_stop(scene_state->usb_type_code_worker_context); } -#ifdef TOTP_BADBT_TYPE_ENABLED +#ifdef TOTP_BADBT_AUTOMATION_ENABLED if(plugin_state->automation_method & AutomationMethodBadBt) { totp_bt_type_code_worker_stop(plugin_state->bt_type_code_worker_context); } #endif - if(scene_state->notification_sequence_new_token != NULL) { - free(scene_state->notification_sequence_new_token); - } - - if(scene_state->notification_sequence_automation != NULL) { - free(scene_state->notification_sequence_automation); - } - furi_mutex_free(scene_state->last_code_update_sync); free(scene_state); diff --git a/applications/external/totp/ui/scenes/standby/standby.c b/applications/external/totp/ui/scenes/standby/standby.c index 8524cf2a8..14408ba55 100644 --- a/applications/external/totp/ui/scenes/standby/standby.c +++ b/applications/external/totp/ui/scenes/standby/standby.c @@ -1,5 +1,5 @@ #include "standby.h" -#include +#include "totp_icons.h" #include #include "../../constants.h" @@ -10,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/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c index 1340b5a8e..7eb4ea87c 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -7,9 +7,7 @@ #include "../../scene_director.h" #include "../../../services/config/config.h" #include "../../../types/token_info.h" -#include "../generate_token/totp_scene_generate_token.h" -#include "../add_new_token/totp_scene_add_new_token.h" -#include "../app_settings/totp_app_settings.h" +#include "../../../config/app/config.h" #include #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) @@ -130,7 +128,22 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt } else if(event->input.type == InputTypeRelease && event->input.key == InputKeyOk) { switch(scene_state->selected_control) { case AddNewToken: { +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken); +#else + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_set_header(message, "Information", 0, 0, AlignLeft, AlignTop); + dialog_message_set_text( + message, + "Read here\nhttps://t.ly/8ZOtj\n how to add new token", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); +#endif break; } case DeleteToken: { diff --git a/applications/external/totp/ui/totp_scenes_enum.h b/applications/external/totp/ui/totp_scenes_enum.h index 4624eddd9..fa7c427dc 100644 --- a/applications/external/totp/ui/totp_scenes_enum.h +++ b/applications/external/totp/ui/totp_scenes_enum.h @@ -1,5 +1,7 @@ #pragma once +#include "../config/app/config.h" + typedef uint8_t Scene; /** @@ -21,10 +23,12 @@ enum Scenes { */ TotpSceneGenerateToken, +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED /** * @brief Scene where user can add new token */ TotpSceneAddNewToken, +#endif /** * @brief Scene with a menu for given token, allowing user to do multiple actions diff --git a/applications/external/totp/ui/ui_controls.c b/applications/external/totp/ui/ui_controls.c index c6ad3e1fe..2bd31f931 100644 --- a/applications/external/totp/ui/ui_controls.c +++ b/applications/external/totp/ui/ui_controls.c @@ -1,5 +1,5 @@ #include "ui_controls.h" -#include +#include "totp_icons.h" #include #include "constants.h" @@ -113,3 +113,27 @@ void ui_control_button_render( canvas_set_color(canvas, ColorBlack); } } + +void ui_control_vscroll_render( + Canvas* const canvas, + uint8_t x, + uint8_t y, + uint8_t height, + uint8_t position, + uint8_t max_position) { + canvas_draw_line(canvas, x, y, x, y + height); + uint8_t block_height = height / MIN(10, max_position); + uint8_t block_position_y = + height * ((float)position / (float)max_position) - (block_height >> 1); + uint8_t block_position_y_abs = y + block_position_y; + if(block_position_y_abs + block_height > height) { + block_position_y_abs = height - block_height; + } + + canvas_draw_box( + canvas, + x - (UI_CONTROL_VSCROLL_WIDTH >> 1), + block_position_y_abs, + UI_CONTROL_VSCROLL_WIDTH, + block_height); +} diff --git a/applications/external/totp/ui/ui_controls.h b/applications/external/totp/ui/ui_controls.h index b97006a03..ccee4edfc 100644 --- a/applications/external/totp/ui/ui_controls.h +++ b/applications/external/totp/ui/ui_controls.h @@ -3,6 +3,8 @@ #include #include +#define UI_CONTROL_VSCROLL_WIDTH (3) + /** * @brief Renders TextBox control * @param canvas canvas to render control at @@ -51,3 +53,20 @@ void ui_control_select_render( uint8_t width, const char* text, bool is_selected); + +/** + * @brief Renders vertical scroll bar + * @param canvas canvas to render control at + * @param x horizontal position of a control to be rendered at + * @param y vertical position of a control to be rendered at + * @param height control height + * @param position current position + * @param max_position maximal position + */ +void ui_control_vscroll_render( + Canvas* const canvas, + uint8_t x, + uint8_t y, + uint8_t height, + uint8_t position, + uint8_t max_position); diff --git a/applications/external/totp/version.h b/applications/external/totp/version.h new file mode 100644 index 000000000..0b8597b0c --- /dev/null +++ b/applications/external/totp/version.h @@ -0,0 +1,5 @@ +#pragma once + +#define TOTP_APP_VERSION_MAJOR (4) +#define TOTP_APP_VERSION_MINOR (0) +#define TOTP_APP_VERSION_PATCH (3) \ No newline at end of file diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c index 0f8ad22ff..eff707099 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.c +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -11,7 +11,8 @@ #include "../../types/common.h" #include "../../types/token_info.h" #include "../type_code_common.h" -#include "../../features_config.h" +#include "../../config/app/config.h" +#include "../../services/config/constants.h" #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME_UL #define TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH @@ -31,6 +32,7 @@ struct TotpBtTypeCodeWorkerContext { char previous_bt_name[TOTP_BT_WORKER_BT_ADV_NAME_MAX_LEN]; uint8_t previous_bt_mac[TOTP_BT_WORKER_BT_MAC_ADDRESS_LEN]; #endif + AutomationKeyboardLayout keyboard_layout; }; static inline bool totp_type_code_worker_stop_requested() { @@ -71,7 +73,8 @@ static void totp_type_code_worker_type_code(TotpBtTypeCodeWorkerContext* context &furi_hal_bt_hid_kb_release, context->code_buffer, context->code_buffer_size, - context->flags); + context->flags, + context->keyboard_layout); furi_mutex_release(context->code_buffer_sync); } } @@ -117,11 +120,13 @@ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync) { + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout) { furi_check(context != NULL); context->code_buffer = code_buffer; context->code_buffer_size = code_buffer_size; context->code_buffer_sync = code_buffer_sync; + context->keyboard_layout = keyboard_layout; context->thread = furi_thread_alloc(); furi_thread_set_name(context->thread, "TOTPBtHidWorker"); furi_thread_set_stack_size(context->thread, 1024); @@ -225,4 +230,4 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context) { bool totp_bt_type_code_worker_is_advertising(const TotpBtTypeCodeWorkerContext* context) { return context->is_advertising; -} +} \ No newline at end of file diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.h b/applications/external/totp/workers/bt_type_code/bt_type_code.h index d0de55124..812acb62c 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.h +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.h @@ -3,8 +3,9 @@ #include #include #include +#include "../../types/automation_kb_layout.h" -#define TOTP_BT_KEYS_STORAGE_PATH EXT_PATH("authenticator/.bt_hid.keys") +#define TOTP_BT_KEYS_STORAGE_PATH EXT_PATH("apps_data/totp/.bt_hid.keys") typedef uint8_t TotpBtTypeCodeWorkerEvent; @@ -49,12 +50,14 @@ void totp_bt_type_code_worker_free(TotpBtTypeCodeWorkerContext* context); * @param code_buffer code buffer to be used to automate * @param code_buffer_size code buffer size * @param code_buffer_sync code buffer synchronization primitive + * @param keyboard_layout keyboard layout to be used */ void totp_bt_type_code_worker_start( TotpBtTypeCodeWorkerContext* context, char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync); + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout); /** * @brief Stops bluetooth token input automation worker diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c index 74482517f..20a7bb54c 100644 --- a/applications/external/totp/workers/generate_totp_code/generate_totp_code.c +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.c @@ -1,6 +1,7 @@ #include "generate_totp_code.h" #include -#include "../../services/crypto/crypto.h" +#include +#include "../../services/crypto/crypto_facade.h" #include "../../services/totp/totp.h" #include "../../services/convert/convert.h" #include @@ -14,7 +15,7 @@ struct TotpGenerateCodeWorkerContext { FuriMutex* code_buffer_sync; const TokenInfo* token_info; float timezone_offset; - uint8_t* iv; + const CryptoSettings* crypto_settings; TOTP_NEW_CODE_GENERATED_HANDLER on_new_code_generated_handler; void* on_new_code_generated_handler_context; TOTP_CODE_LIFETIME_CHANGED_HANDLER on_code_lifetime_changed_handler; @@ -69,7 +70,7 @@ static void generate_totp_code( if(token_info->token != NULL && token_info->token_length > 0) { size_t key_length; uint8_t* key = totp_crypto_decrypt( - token_info->token, token_info->token_length, context->iv, &key_length); + token_info->token, token_info->token_length, context->crypto_settings, &key_length); int_token_to_str( totp_at( @@ -147,14 +148,14 @@ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( const TokenInfo* token_info, FuriMutex* code_buffer_sync, float timezone_offset, - uint8_t* iv) { + const CryptoSettings* crypto_settings) { TotpGenerateCodeWorkerContext* context = malloc(sizeof(TotpGenerateCodeWorkerContext)); furi_check(context != NULL); context->code_buffer = code_buffer; context->token_info = token_info; context->code_buffer_sync = code_buffer_sync; context->timezone_offset = timezone_offset; - context->iv = iv; + context->crypto_settings = crypto_settings; context->thread = furi_thread_alloc(); furi_thread_set_name(context->thread, "TOTPGenerateWorker"); furi_thread_set_stack_size(context->thread, 2048); diff --git a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h index f351ffa68..eb234313e 100644 --- a/applications/external/totp/workers/generate_totp_code/generate_totp_code.h +++ b/applications/external/totp/workers/generate_totp_code/generate_totp_code.h @@ -38,7 +38,7 @@ enum TotGenerateCodeWorkerEvents { * @param token_info token info to be used to generate code * @param code_buffer_sync code buffer synchronization primitive * @param timezone_offset timezone offset to be used to generate code - * @param iv initialization vector (IV) to be used to decrypt token secret + * @param crypto_settings crypto settings * @return worker context */ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( @@ -46,7 +46,7 @@ TotpGenerateCodeWorkerContext* totp_generate_code_worker_start( const TokenInfo* token_info, FuriMutex* code_buffer_sync, float timezone_offset, - uint8_t* iv); + const CryptoSettings* crypto_settings); /** * @brief Stops generate code worker diff --git a/applications/external/totp/workers/type_code_common.c b/applications/external/totp/workers/type_code_common.c index 82a5a028e..122c0b2a5 100644 --- a/applications/external/totp/workers/type_code_common.c +++ b/applications/external/totp/workers/type_code_common.c @@ -3,7 +3,9 @@ #include #include "../../services/convert/convert.h" -static const uint8_t hid_number_keys[] = { +#define HID_KEYS_MAP_LENGTH (36) + +static const uint8_t hid_qwerty_keys_map[HID_KEYS_MAP_LENGTH] = { HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4, HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9, HID_KEYBOARD_A, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E, @@ -13,6 +15,16 @@ static const uint8_t hid_number_keys[] = { HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_W, HID_KEYBOARD_X, HID_KEYBOARD_Y, HID_KEYBOARD_Z}; +static const uint8_t hid_azerty_keys_map[HID_KEYS_MAP_LENGTH] = { + HID_KEYBOARD_0, HID_KEYBOARD_1, HID_KEYBOARD_2, HID_KEYBOARD_3, HID_KEYBOARD_4, + HID_KEYBOARD_5, HID_KEYBOARD_6, HID_KEYBOARD_7, HID_KEYBOARD_8, HID_KEYBOARD_9, + HID_KEYBOARD_Q, HID_KEYBOARD_B, HID_KEYBOARD_C, HID_KEYBOARD_D, HID_KEYBOARD_E, + HID_KEYBOARD_F, HID_KEYBOARD_G, HID_KEYBOARD_H, HID_KEYBOARD_I, HID_KEYBOARD_J, + HID_KEYBOARD_K, HID_KEYBOARD_L, HID_KEYBOARD_SEMICOLON, HID_KEYBOARD_N, HID_KEYBOARD_O, + HID_KEYBOARD_P, HID_KEYBOARD_A, HID_KEYBOARD_R, HID_KEYBOARD_S, HID_KEYBOARD_T, + HID_KEYBOARD_U, HID_KEYBOARD_V, HID_KEYBOARD_Z, HID_KEYBOARD_X, HID_KEYBOARD_Y, + HID_KEYBOARD_W}; + static uint32_t get_keystroke_delay(TokenAutomationFeature features) { if(features & TokenAutomationFeatureTypeSlower) { return 100; @@ -44,21 +56,38 @@ void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_release_fn, const char* code_buffer, uint8_t code_buffer_size, - TokenAutomationFeature features) { + TokenAutomationFeature features, + AutomationKeyboardLayout keyboard_layout) { furi_delay_ms(500); uint8_t i = 0; char cb_char; + const uint8_t* keyboard_layout_dict; + switch(keyboard_layout) { + case AutomationKeyboardLayoutQWERTY: + keyboard_layout_dict = &hid_qwerty_keys_map[0]; + break; + case AutomationKeyboardLayoutAZERTY: + keyboard_layout_dict = &hid_azerty_keys_map[0]; + break; + + default: + return; + } + 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 - 'A' + 10; } - if(char_index >= sizeof(hid_number_keys)) break; + if(char_index >= HID_KEYS_MAP_LENGTH) break; - uint16_t hid_kb_key = hid_number_keys[char_index]; - if(char_index > 9) { + uint16_t hid_kb_key = keyboard_layout_dict[char_index]; + + // For non-AZERTY press shift for all non-digit chars + // For AZERTY press shift for all characters + if(char_index > 9 || keyboard_layout == AutomationKeyboardLayoutAZERTY) { hid_kb_key |= KEY_MOD_LEFT_SHIFT; } diff --git a/applications/external/totp/workers/type_code_common.h b/applications/external/totp/workers/type_code_common.h index db357329a..81b273c36 100644 --- a/applications/external/totp/workers/type_code_common.h +++ b/applications/external/totp/workers/type_code_common.h @@ -1,6 +1,7 @@ #pragma once #include #include "../types/token_info.h" +#include "../types/automation_kb_layout.h" typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); @@ -11,10 +12,12 @@ typedef bool (*TOTP_AUTOMATION_KEY_HANDLER)(uint16_t key); * @param code_buffer code buffer to be typed * @param code_buffer_size code buffer size * @param features automation features + * @param keyboard_layout keyboard layout to be used */ void totp_type_code_worker_execute_automation( TOTP_AUTOMATION_KEY_HANDLER key_press_fn, TOTP_AUTOMATION_KEY_HANDLER key_release_fn, const char* code_buffer, uint8_t code_buffer_size, - TokenAutomationFeature features); \ No newline at end of file + TokenAutomationFeature features, + AutomationKeyboardLayout keyboard_layout); \ No newline at end of file diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.c b/applications/external/totp/workers/usb_type_code/usb_type_code.c index a391bdf82..4e3259424 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.c +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.c @@ -15,6 +15,7 @@ struct TotpUsbTypeCodeWorkerContext { FuriThread* thread; FuriMutex* code_buffer_sync; FuriHalUsbInterface* usb_mode_prev; + AutomationKeyboardLayout keyboard_layout; }; static void totp_type_code_worker_restore_usb_mode(TotpUsbTypeCodeWorkerContext* context) { @@ -45,7 +46,8 @@ static void totp_type_code_worker_type_code(TotpUsbTypeCodeWorkerContext* contex &furi_hal_hid_kb_release, context->code_buffer, context->code_buffer_size, - context->flags); + context->flags, + context->keyboard_layout); furi_mutex_release(context->code_buffer_sync); furi_delay_ms(100); @@ -83,7 +85,8 @@ static int32_t totp_type_code_worker_callback(void* context) { TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync) { + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout) { TotpUsbTypeCodeWorkerContext* context = malloc(sizeof(TotpUsbTypeCodeWorkerContext)); furi_check(context != NULL); context->code_buffer = code_buffer; @@ -91,6 +94,7 @@ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( context->code_buffer_sync = code_buffer_sync; context->thread = furi_thread_alloc(); context->usb_mode_prev = NULL; + context->keyboard_layout = keyboard_layout; furi_thread_set_name(context->thread, "TOTPUsbHidWorker"); furi_thread_set_stack_size(context->thread, 1024); furi_thread_set_context(context->thread, context); diff --git a/applications/external/totp/workers/usb_type_code/usb_type_code.h b/applications/external/totp/workers/usb_type_code/usb_type_code.h index 0a700e7fe..d2d1bdf82 100644 --- a/applications/external/totp/workers/usb_type_code/usb_type_code.h +++ b/applications/external/totp/workers/usb_type_code/usb_type_code.h @@ -3,6 +3,7 @@ #include #include #include +#include "../../types/automation_kb_layout.h" typedef uint8_t TotpUsbTypeCodeWorkerEvent; @@ -34,12 +35,14 @@ enum TotpUsbTypeCodeWorkerEvents { * @param code_buffer code buffer to be used to automate * @param code_buffer_size code buffer size * @param code_buffer_sync code buffer synchronization primitive + * @param keyboard_layout keyboard layout to be used * @return worker context */ TotpUsbTypeCodeWorkerContext* totp_usb_type_code_worker_start( char* code_buffer, uint8_t code_buffer_size, - FuriMutex* code_buffer_sync); + FuriMutex* code_buffer_sync, + AutomationKeyboardLayout keyboard_layout); /** * @brief Stops USB token input automation worker diff --git a/applications/external/tuning_fork/application.fam b/applications/external/tuning_fork/application.fam index 350eeb573..5e86a6ad1 100644 --- a/applications/external/tuning_fork/application.fam +++ b/applications/external/tuning_fork/application.fam @@ -10,7 +10,6 @@ App( fap_icon="tuning_fork_icon.png", 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", diff --git a/applications/external/uart_terminal/application.fam b/applications/external/uart_terminal/application.fam index 6d7e63d6d..1416c6d57 100644 --- a/applications/external/uart_terminal/application.fam +++ b/applications/external/uart_terminal/application.fam @@ -1,14 +1,14 @@ App( appid="uart_terminal", - name="[UART] Terminal", + name="[UART] UART Terminal", apptype=FlipperAppType.EXTERNAL, entry_point="uart_terminal_app", requires=["gui"], stack_size=1 * 1024, - order=90, 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.", + fap_version="1.1", + fap_description="Control various devices via the Flipper Zero UART interface.", ) diff --git a/applications/external/uart_terminal/uart_terminal_app_i.h b/applications/external/uart_terminal/uart_terminal_app_i.h index d90e38998..5d2e3078c 100644 --- a/applications/external/uart_terminal/uart_terminal_app_i.h +++ b/applications/external/uart_terminal/uart_terminal_app_i.h @@ -12,11 +12,15 @@ #include #include +#include + #define NUM_MENU_ITEMS (5) #define UART_TERMINAL_TEXT_BOX_STORE_SIZE (4096) #define UART_TERMINAL_TEXT_INPUT_STORE_SIZE (512) -#define UART_CH (FuriHalUartIdUSART1) +#define UART_CH \ + (XTREME_SETTINGS()->uart_general_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) struct UART_TerminalApp { Gui* gui; diff --git a/applications/external/uart_terminal/uart_terminal_uart.c b/applications/external/uart_terminal/uart_terminal_uart.c index e906c9e8e..b289987f5 100644 --- a/applications/external/uart_terminal/uart_terminal_uart.c +++ b/applications/external/uart_terminal/uart_terminal_uart.c @@ -51,6 +51,7 @@ static int32_t uart_worker(void* context) { } } + furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); furi_stream_buffer_free(uart->rx_stream); return 0; @@ -73,7 +74,12 @@ UART_TerminalUart* uart_terminal_uart_init(UART_TerminalApp* app) { furi_thread_start(uart->rx_thread); - furi_hal_console_disable(); + if(UART_CH == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH, app->BAUDRATE); + } + if(app->BAUDRATE == 0) { app->BAUDRATE = 115200; } @@ -90,8 +96,11 @@ void uart_terminal_uart_free(UART_TerminalUart* uart) { furi_thread_join(uart->rx_thread); furi_thread_free(uart->rx_thread); - furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); - furi_hal_console_enable(); + if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH); + } else { + furi_hal_console_enable(); + } free(uart); } \ No newline at end of file diff --git a/applications/external/unitemp/application.fam b/applications/external/unitemp/application.fam index 260069f3e..b3f9dec35 100644 --- a/applications/external/unitemp/application.fam +++ b/applications/external/unitemp/application.fam @@ -7,7 +7,6 @@ App( "gui", ], stack_size=2 * 1024, - order=100, fap_description="Universal temperature sensors reader", fap_version="1.4", fap_author="@quen0n & (fixes by @xMasterX)", diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c index 0a8e8ad17..d0d269126 100644 --- a/applications/external/unitemp/views/General_view.c +++ b/applications/external/unitemp/views/General_view.c @@ -17,10 +17,11 @@ */ #include "UnitempViews.h" #include "unitemp_icons.h" +#include -extern const Icon I_ButtonRight_4x7; -extern const Icon I_ButtonLeft_4x7; -extern const Icon I_Ok_btn_9x9; +// extern const Icon I_ButtonRight_4x7; +// extern const Icon I_ButtonLeft_4x7; +// extern const Icon I_Ok_btn_9x9; static View* view; diff --git a/applications/external/unitemp/views/Widgets_view.c b/applications/external/unitemp/views/Widgets_view.c index 97505d925..3055930cf 100644 --- a/applications/external/unitemp/views/Widgets_view.c +++ b/applications/external/unitemp/views/Widgets_view.c @@ -17,8 +17,9 @@ */ #include "UnitempViews.h" #include "unitemp_icons.h" +#include -extern const Icon I_DolphinCommon_56x48; +// extern const Icon I_DolphinCommon_56x48; void unitemp_widgets_alloc(void) { app->widget = widget_alloc(); @@ -202,4 +203,4 @@ void unitemp_widget_about_switch(void) { view_set_previous_callback(widget_get_view(app->widget), _help_exit_callback); view_dispatcher_switch_to_view(app->view_dispatcher, UnitempViewWidget); -} \ No newline at end of file +} diff --git a/applications/external/videopoker/application.fam b/applications/external/videopoker/application.fam index 129881648..4997132f2 100644 --- a/applications/external/videopoker/application.fam +++ b/applications/external/videopoker/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_VIDEOPOKER_GAME"], requires=["gui"], stack_size=2 * 1024, - order=270, fap_icon="pokerIcon.png", fap_category="Games", fap_author="@PixlEmly", diff --git a/applications/external/wav_player/application.fam b/applications/external/wav_player/application.fam index cc462d0ef..112add8ee 100644 --- a/applications/external/wav_player/application.fam +++ b/applications/external/wav_player/application.fam @@ -4,9 +4,9 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="wav_player_app", stack_size=4 * 1024, - order=46, 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/wav_player/wav_parser.c b/applications/external/wav_player/wav_parser.c index 8c1f22b19..1f534bacb 100644 --- a/applications/external/wav_player/wav_parser.c +++ b/applications/external/wav_player/wav_parser.c @@ -11,7 +11,7 @@ const char* format_text(FormatTag tag) { default: return "Unknown"; } -} +}; struct WavParser { WavHeaderChunk header; diff --git a/applications/external/wav_player/wav_parser.h b/applications/external/wav_player/wav_parser.h index 8d4139865..b442406f9 100644 --- a/applications/external/wav_player/wav_parser.h +++ b/applications/external/wav_player/wav_parser.h @@ -86,4 +86,4 @@ size_t wav_parser_get_data_len(WavParser* parser); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/applications/external/wav_player/wav_player.c b/applications/external/wav_player/wav_player.c index 14fd8a1e1..79462caf9 100644 --- a/applications/external/wav_player/wav_player.c +++ b/applications/external/wav_player/wav_player.c @@ -12,6 +12,7 @@ #include "wav_player_view.h" #include +#include "wav_player_icons.h" #include #define TAG "WavPlayer" diff --git a/applications/external/wav_player/wav_player_hal.c b/applications/external/wav_player/wav_player_hal.c index c7ea039e8..cf2688523 100644 --- a/applications/external/wav_player/wav_player_hal.c +++ b/applications/external/wav_player/wav_player_hal.c @@ -108,4 +108,4 @@ void wav_player_dma_start() { void wav_player_dma_stop() { LL_DMA_DisableChannel(DMA_INSTANCE); -} +} \ No newline at end of file diff --git a/applications/external/wav_player/wav_player_view.c b/applications/external/wav_player/wav_player_view.c index 0cb04d23f..285b3dd33 100644 --- a/applications/external/wav_player/wav_player_view.c +++ b/applications/external/wav_player/wav_player_view.c @@ -199,4 +199,4 @@ void wav_player_view_set_ctrl_callback(WavPlayerView* wav_view, WavPlayerCtrlCal void wav_player_view_set_context(WavPlayerView* wav_view, void* context) { furi_assert(wav_view); wav_view->context = context; -} +} \ No newline at end of file diff --git a/applications/external/wav_player/wav_player_view.h b/applications/external/wav_player/wav_player_view.h index 02ed4479c..b7b0ef908 100644 --- a/applications/external/wav_player/wav_player_view.h +++ b/applications/external/wav_player/wav_player_view.h @@ -75,4 +75,4 @@ void wav_player_view_set_context(WavPlayerView* wav_view, void* context); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/applications/external/weather_station/application.fam b/applications/external/weather_station/application.fam index 8dcaa1259..0221130a3 100644 --- a/applications/external/weather_station/application.fam +++ b/applications/external/weather_station/application.fam @@ -6,7 +6,8 @@ App( entry_point="weather_station_app", requires=["gui"], stack_size=4 * 1024, - order=50, + fap_description="Receive weather data from a wide range of supported Sub-1GHz remote sensor", + fap_version="1.1", fap_icon="weather_station_10px.png", fap_category="Sub-GHz", fap_icon_assets="images", diff --git a/applications/external/weather_station/helpers/weather_station_types.h b/applications/external/weather_station/helpers/weather_station_types.h index 111465978..e6c696bf8 100644 --- a/applications/external/weather_station/helpers/weather_station_types.h +++ b/applications/external/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.8" +#define WS_VERSION_APP "1.1" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/external/weather_station/protocols/auriol_ahfl.c b/applications/external/weather_station/protocols/auriol_ahfl.c new file mode 100644 index 000000000..80b89e380 --- /dev/null +++ b/applications/external/weather_station/protocols/auriol_ahfl.c @@ -0,0 +1,254 @@ +#include "auriol_ahfl.h" + +#define TAG "WSProtocolAuriol_AHFL" + +/* + * +Auriol AHFL 433B2 IPX4 sensor. + +Data layout: + IIIIIIII-B-X-CC-TTTTTTTTTTTT-HHHHHHH0-FFFF-SSSSSS +Exmpl.: 10111100-1-0-00-000011101000-01101100-0100-001011 + +- I: id, 8 bit +- B: where B is the battery status: 1=OK, 0=LOW, 1 bit +- X: tx-button, 1 bit (might not work) +- C: CC is the channel: 00=CH1, 01=CH2, 11=CH3, 2bit +- T: temperature, 12 bit: 2's complement, scaled by 10 +- H: humidity, 7 bits data, 1 bit 0 +- F: always 0x4 (0100) +- S: nibble sum, 6 bits + + * The sensor sends 42 bits 5 times, + * the packets are ppm modulated (distance coding) with a pulse of ~500 us + * followed by a short gap of ~1000 us for a 0 bit or a long ~2000 us gap for a + * 1 bit, the sync gap is ~4000 us. + * + */ + +#define AURIOL_AHFL_CONST_DATA 0b0100 + +static const SubGhzBlockConst ws_protocol_auriol_ahfl_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 42, +}; + +struct WSProtocolDecoderAuriol_AHFL { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; +}; + +struct WSProtocolEncoderAuriol_AHFL { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + auriol_AHFLDecoderStepReset = 0, + auriol_AHFLDecoderStepSaveDuration, + auriol_AHFLDecoderStepCheckDuration, +} auriol_AHFLDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_auriol_ahfl_decoder = { + .alloc = ws_protocol_decoder_auriol_ahfl_alloc, + .free = ws_protocol_decoder_auriol_ahfl_free, + + .feed = ws_protocol_decoder_auriol_ahfl_feed, + .reset = ws_protocol_decoder_auriol_ahfl_reset, + + .get_hash_data = ws_protocol_decoder_auriol_ahfl_get_hash_data, + .serialize = ws_protocol_decoder_auriol_ahfl_serialize, + .deserialize = ws_protocol_decoder_auriol_ahfl_deserialize, + .get_string = ws_protocol_decoder_auriol_ahfl_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_auriol_ahfl_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_auriol_ahfl = { + .name = WS_PROTOCOL_AURIOL_AHFL_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_auriol_ahfl_decoder, + .encoder = &ws_protocol_auriol_ahfl_encoder, +}; + +void* ws_protocol_decoder_auriol_ahfl_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderAuriol_AHFL* instance = malloc(sizeof(WSProtocolDecoderAuriol_AHFL)); + instance->base.protocol = &ws_protocol_auriol_ahfl; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_auriol_ahfl_free(void* context) { + furi_assert(context); + WSProtocolDecoderAuriol_AHFL* instance = context; + free(instance); +} + +void ws_protocol_decoder_auriol_ahfl_reset(void* context) { + furi_assert(context); + WSProtocolDecoderAuriol_AHFL* instance = context; + instance->decoder.parser_step = auriol_AHFLDecoderStepReset; +} + +static bool ws_protocol_auriol_ahfl_check(WSProtocolDecoderAuriol_AHFL* instance) { + uint8_t type = (instance->decoder.decode_data >> 6) & 0x0F; + + if(type != AURIOL_AHFL_CONST_DATA) { + // Fail const data check + return false; + } + + uint64_t payload = instance->decoder.decode_data >> 6; + // Checksum is the last 6 bits of data + uint8_t checksum_received = instance->decoder.decode_data & 0x3F; + uint8_t checksum_calculated = 0; + for(uint8_t i = 0; i < 9; i++) { + checksum_calculated += (payload >> (i * 4)) & 0xF; + } + return checksum_received == checksum_calculated; +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_auriol_ahfl_remote_controller(WSBlockGeneric* instance) { + instance->id = instance->data >> 34; + instance->battery_low = (instance->data >> 33) & 1; + instance->btn = (instance->data >> 32) & 1; + instance->channel = ((instance->data >> 30) & 0x3) + 1; + if(!((instance->data >> 29) & 1)) { + instance->temp = (float)((instance->data >> 18) & 0x07FF) / 10.0f; + } else { + instance->temp = (float)((~(instance->data >> 18) & 0x07FF) + 1) / -10.0f; + } + instance->humidity = (instance->data >> 11) & 0x7F; +} + +void ws_protocol_decoder_auriol_ahfl_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderAuriol_AHFL* instance = context; + + switch(instance->decoder.parser_step) { + case auriol_AHFLDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, ws_protocol_auriol_ahfl_const.te_short * 18) < + ws_protocol_auriol_ahfl_const.te_delta)) { + //Found syncPrefix + instance->decoder.parser_step = auriol_AHFLDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + case auriol_AHFLDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = auriol_AHFLDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = auriol_AHFLDecoderStepReset; + } + break; + case auriol_AHFLDecoderStepCheckDuration: + if(!level) { + if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_auriol_ahfl_const.te_short) < + ws_protocol_auriol_ahfl_const.te_delta) { + if(DURATION_DIFF(duration, ws_protocol_auriol_ahfl_const.te_short * 18) < + ws_protocol_auriol_ahfl_const.te_delta * 8) { + //Found syncPostfix + instance->decoder.parser_step = auriol_AHFLDecoderStepReset; + if((instance->decoder.decode_count_bit == + ws_protocol_auriol_ahfl_const.min_count_bit_for_found) && + ws_protocol_auriol_ahfl_check(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + ws_protocol_auriol_ahfl_remote_controller(&instance->generic); + if(instance->base.callback) { + instance->base.callback(&instance->base, instance->base.context); + } + } else if(instance->decoder.decode_count_bit == 1) { + instance->decoder.parser_step = auriol_AHFLDecoderStepSaveDuration; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else if( + DURATION_DIFF(duration, ws_protocol_auriol_ahfl_const.te_long) < + ws_protocol_auriol_ahfl_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = auriol_AHFLDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, ws_protocol_auriol_ahfl_const.te_long * 2) < + ws_protocol_auriol_ahfl_const.te_delta * 4) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = auriol_AHFLDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = auriol_AHFLDecoderStepReset; + } + } else { + instance->decoder.parser_step = auriol_AHFLDecoderStepReset; + } + } else { + instance->decoder.parser_step = auriol_AHFLDecoderStepReset; + } + break; + } +} + +uint8_t ws_protocol_decoder_auriol_ahfl_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderAuriol_AHFL* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus ws_protocol_decoder_auriol_ahfl_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderAuriol_AHFL* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + ws_protocol_decoder_auriol_ahfl_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderAuriol_AHFL* instance = context; + return ws_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, ws_protocol_auriol_ahfl_const.min_count_bit_for_found); +} + +void ws_protocol_decoder_auriol_ahfl_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderAuriol_AHFL* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} \ No newline at end of file diff --git a/applications/external/weather_station/protocols/auriol_ahfl.h b/applications/external/weather_station/protocols/auriol_ahfl.h new file mode 100644 index 000000000..1e29bc8cc --- /dev/null +++ b/applications/external/weather_station/protocols/auriol_ahfl.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_AURIOL_AHFL_NAME "Auriol AHFL" //Auriol AHFL 433B2 IPX4 + +typedef struct WSProtocolDecoderAuriol_AHFL WSProtocolDecoderAuriol_AHFL; +typedef struct WSProtocolEncoderAuriol_AHFL WSProtocolEncoderAuriol_AHFL; + +extern const SubGhzProtocolDecoder ws_protocol_auriol_ahfl_decoder; +extern const SubGhzProtocolEncoder ws_protocol_auriol_ahfl_encoder; +extern const SubGhzProtocol ws_protocol_auriol_ahfl; + +/** + * Allocate WSProtocolDecoderAuriol_AHFL. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderAuriol_AHFL* pointer to a WSProtocolDecoderAuriol_AHFL instance + */ +void* ws_protocol_decoder_auriol_ahfl_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderAuriol_AHFL. + * @param context Pointer to a WSProtocolDecoderAuriol_AHFL instance + */ +void ws_protocol_decoder_auriol_ahfl_free(void* context); + +/** + * Reset decoder WSProtocolDecoderAuriol_AHFL. + * @param context Pointer to a WSProtocolDecoderAuriol_AHFL instance + */ +void ws_protocol_decoder_auriol_ahfl_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderAuriol_AHFL instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_auriol_ahfl_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderAuriol_AHFL instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_auriol_ahfl_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderAuriol_AHFL. + * @param context Pointer to a WSProtocolDecoderAuriol_AHFL instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus ws_protocol_decoder_auriol_ahfl_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderAuriol_AHFL. + * @param context Pointer to a WSProtocolDecoderAuriol_AHFL instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + ws_protocol_decoder_auriol_ahfl_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderAuriol_AHFL instance + * @param output Resulting text + */ +void ws_protocol_decoder_auriol_ahfl_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c index 93dc25488..e0ec86068 100644 --- a/applications/external/weather_station/protocols/protocol_items.c +++ b/applications/external/weather_station/protocols/protocol_items.c @@ -18,6 +18,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_oregon_v1, &ws_protocol_tx_8300, &ws_protocol_wendox_w6726, + &ws_protocol_auriol_ahfl, }; const SubGhzProtocolRegistry weather_station_protocol_registry = { diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h index 712eb07f2..7d9bda243 100644 --- a/applications/external/weather_station/protocols/protocol_items.h +++ b/applications/external/weather_station/protocols/protocol_items.h @@ -18,5 +18,6 @@ #include "oregon_v1.h" #include "tx_8300.h" #include "wendox_w6726.h" +#include "auriol_ahfl.h" extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/external/weather_station/scenes/weather_station_receiver.c b/applications/external/weather_station/scenes/weather_station_receiver.c index 64df21fc5..76d808e7e 100644 --- a/applications/external/weather_station/scenes/weather_station_receiver.c +++ b/applications/external/weather_station/scenes/weather_station_receiver.c @@ -138,7 +138,7 @@ void weather_station_scene_receiver_on_enter(void* context) { if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); - } + }; if((app->txrx->txrx_state == WSTxRxStateIDLE) || (app->txrx->txrx_state == WSTxRxStateSleep)) { ws_begin( app, @@ -162,7 +162,7 @@ bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent ev if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); ws_sleep(app); - } + }; app->txrx->hopper_state = WSHopperStateOFF; app->txrx->idx_menu_chosen = 0; subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); diff --git a/applications/external/weather_station/views/weather_station_receiver.c b/applications/external/weather_station/views/weather_station_receiver.c index b3bcde591..488ef022f 100644 --- a/applications/external/weather_station/views/weather_station_receiver.c +++ b/applications/external/weather_station/views/weather_station_receiver.c @@ -1,6 +1,6 @@ #include "weather_station_receiver.h" #include "../weather_station_app_i.h" -#include +#include "weather_station_icons.h" #include #include diff --git a/applications/external/weather_station/weather_station_app_i.c b/applications/external/weather_station/weather_station_app_i.c index 9437f4cf4..e98c61ee5 100644 --- a/applications/external/weather_station/weather_station_app_i.c +++ b/applications/external/weather_station/weather_station_app_i.c @@ -145,7 +145,7 @@ void ws_hopper_update(WeatherStationApp* app) { if(app->txrx->txrx_state == WSTxRxStateRx) { ws_rx_end(app); - } + }; if(app->txrx->txrx_state == WSTxRxStateIDLE) { subghz_receiver_reset(app->txrx->receiver); app->txrx->preset->frequency = diff --git a/applications/external/wifi_deauther/application.fam b/applications/external/wifi_deauther/application.fam index 17c6978b9..770e99585 100644 --- a/applications/external/wifi_deauther/application.fam +++ b/applications/external/wifi_deauther/application.fam @@ -6,7 +6,6 @@ App( cdefines=["APP_WIFI_deauther"], requires=["gui"], stack_size=1 * 1024, - order=30, fap_icon="wifi_10px.png", fap_category="WiFi", fap_author="@Timmotools & @xMasterX", diff --git a/applications/external/wifi_deauther/wifi_deauther_app.c b/applications/external/wifi_deauther/wifi_deauther_app.c index 2e05a1ce0..28fb28d88 100644 --- a/applications/external/wifi_deauther/wifi_deauther_app.c +++ b/applications/external/wifi_deauther/wifi_deauther_app.c @@ -3,7 +3,6 @@ #include #include #include -#include static bool wifi_deauther_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -25,7 +24,6 @@ static void wifi_deauther_app_tick_event_callback(void* context) { WifideautherApp* wifi_deauther_app_alloc() { WifideautherApp* app = malloc(sizeof(WifideautherApp)); - dolphin_deed(DolphinDeedPluginStart); app->gui = furi_record_open(RECORD_GUI); diff --git a/applications/external/wifi_deauther/wifi_deauther_app_i.h b/applications/external/wifi_deauther/wifi_deauther_app_i.h index bab52f385..a26950d98 100644 --- a/applications/external/wifi_deauther/wifi_deauther_app_i.h +++ b/applications/external/wifi_deauther/wifi_deauther_app_i.h @@ -12,6 +12,12 @@ #include #include +#include + +#define UART_CH \ + (XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) + #define NUM_MENU_ITEMS (11) #define WIFI_deauther_TEXT_BOX_STORE_SIZE (4096) diff --git a/applications/external/wifi_deauther/wifi_deauther_uart.c b/applications/external/wifi_deauther/wifi_deauther_uart.c index 3c52bbce2..f7c3ffbf6 100644 --- a/applications/external/wifi_deauther/wifi_deauther_uart.c +++ b/applications/external/wifi_deauther/wifi_deauther_uart.c @@ -3,7 +3,6 @@ #include -#define UART_CH (FuriHalUartIdUSART1) #define BAUDRATE (115200) struct WifideautherUart { @@ -54,6 +53,7 @@ static int32_t uart_worker(void* context) { } } + furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); furi_stream_buffer_free(uart->rx_stream); return 0; @@ -76,7 +76,12 @@ WifideautherUart* wifi_deauther_uart_init(WifideautherApp* app) { furi_thread_start(uart->rx_thread); - furi_hal_console_disable(); + if(UART_CH == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH, BAUDRATE); + } + furi_hal_uart_set_br(UART_CH, BAUDRATE); furi_hal_uart_set_irq_cb(UART_CH, wifi_deauther_uart_on_irq_cb, uart); @@ -90,8 +95,11 @@ void wifi_deauther_uart_free(WifideautherUart* uart) { furi_thread_join(uart->rx_thread); furi_thread_free(uart->rx_thread); - furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); - furi_hal_console_enable(); + if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH); + } else { + furi_hal_console_enable(); + } free(uart); } \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/application.fam b/applications/external/wifi_marauder_companion/application.fam index bfd23232c..dab41464b 100644 --- a/applications/external/wifi_marauder_companion/application.fam +++ b/applications/external/wifi_marauder_companion/application.fam @@ -1,29 +1,12 @@ App( appid="esp32_wifi_marauder", name="[ESP32] WiFi Marauder", + fap_version=(6, 0), apptype=FlipperAppType.EXTERNAL, entry_point="wifi_marauder_app", requires=["gui"], stack_size=4 * 1024, - order=90, fap_icon="wifi_10px.png", fap_category="WiFi", - fap_private_libs=[ - Lib( - name="esp-serial-flasher", - fap_include_paths=["include"], - sources=[ - "src/esp_loader.c", - "src/esp_targets.c", - "src/md5_hash.c", - "src/protocol_common.c", - "src/protocol_uart.c", - "src/slip.c", - ], - cincludes=["lib/esp-serial-flasher/private_include"], - cdefines=["SERIAL_FLASHER_INTERFACE_UART=1", "MD5_ENABLED=1"], - ), - ], - cdefines=["SERIAL_FLASHER_INTERFACE_UART=1"], fap_icon_assets="assets", ) diff --git a/applications/external/wifi_marauder_companion/docs/README.md b/applications/external/wifi_marauder_companion/docs/README.md deleted file mode 100644 index 40b7a5ad0..000000000 --- a/applications/external/wifi_marauder_companion/docs/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## WiFi Marauder companion app for Flipper Zero - -Requires a connected dev board running Marauder FW. See install instructions from UberGuidoZ here: https://github.com/UberGuidoZ/Flipper/tree/main/Wifi_DevBoard#marauder-install-information - -## Support - -For app feedback, bugs, and feature requests, please create an issue here: https://github.com/0xchocolate/flipperzero-wifi-marauder/issues - -You can find me (0xchocolate) on discord as @cococode. diff --git a/applications/external/wifi_marauder_companion/docs/changelog.md b/applications/external/wifi_marauder_companion/docs/changelog.md deleted file mode 100644 index ae0f328e7..000000000 --- a/applications/external/wifi_marauder_companion/docs/changelog.md +++ /dev/null @@ -1,5 +0,0 @@ -## v0.4.0 - -Added Signal Monitor (thanks @justcallmekoko!) to support new sigmon command in Marauder v0.10.5: https://github.com/justcallmekoko/ESP32Marauder/releases - -Added keyboard and +5V support from unleashed (thanks @xMasterX!) diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/LICENSE b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/LICENSE deleted file mode 100644 index d64569567..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/README.md b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/README.md deleted file mode 100644 index b1ae448d7..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/README.md +++ /dev/null @@ -1,154 +0,0 @@ -# esp-serial-flasher - -`esp-serial-flasher` is a portable C library for flashing or loading apps to RAM of Espressif SoCs from other host microcontrollers. - -## Using the library -Espressif SoCs are normally programmed via serial interface (UART). The port layer for the given host microcontroller has to be implemented if not available. Details can be found in section below. - -Supported **host** microcontrollers: - -- STM32 -- Raspberry Pi SBC -- ESP32 -- Any MCU running Zephyr OS - -Supported **target** microcontrollers: - -- ESP32 -- ESP8266 -- ESP32-S2 -- ESP32-S3 -- ESP32-C3 -- ESP32-C2 -- ESP32-H2 - -Supported hardware interfaces: -- UART -- SPI (only for RAM download, experimental) - -For example usage check the `examples` directory. - -## Supporting a new host target - -In order to support a new target, following functions have to be implemented by user: - -- `loader_port_read()` -- `loader_port_write()` -- `loader_port_enter_bootloader()` -- `loader_port_delay_ms()` -- `loader_port_start_timer()` -- `loader_port_remaining_time()` - -For the SPI interface ports -- `loader_port_spi_set_cs()` -needs to be implemented as well. - -The following functions are part of the [io.h](include/io.h) header for convenience, however, the user does not have to strictly follow function signatures, as there are not called directly from library. - -- `loader_port_change_transmission_rate()` -- `loader_port_reset_target()` -- `loader_port_debug_print()` - -Prototypes of all functions mentioned above can be found in [io.h](include/io.h). - -## Configuration - -These are the configuration toggles available to the user: - -* `SERIAL_FLASHER_INTERFACE_UART/SERIAL_FLASHER_INTERFACE_SPI` - -This defines the hardware interface to use. SPI interface only supports RAM download mode and is in experimental stage and can undergo changes. - -Default: SERIAL_FLASHER_INTERFACE_UART - -* `MD5_ENABLED` - -If enabled, `esp-serial-flasher` is capable of verifying flash integrity after writing to flash. - -Default: Enabled -> Warning: As ROM bootloader of the ESP8266 does not support MD5_CHECK, this option has to be disabled! - -* `SERIAL_FLASHER_RESET_HOLD_TIME_MS` - -This is the time for which the reset pin is asserted when doing a hard reset in milliseconds. - -Default: 100 - -* `SERIAL_FLASHER_BOOT_HOLD_TIME_MS` - -This is the time for which the boot pin is asserted when doing a hard reset in milliseconds. - -Default: 50 - -Configuration can be passed to `cmake` via command line: - -``` -cmake -DMD5_ENABLED=1 .. && cmake --build . -``` - -### STM32 support - -The STM32 port makes use of STM32 HAL libraries, and these do not come with CMake support. In order to compile the project, `stm32-cmake` (a `CMake` support package) has to be pulled as submodule. - -``` -git clone --recursive https://github.com/espressif/esp-serial-flasher.git -``` - -If you have cloned this repository without the `--recursive` flag, you can initialize the submodule using the following command: - -``` -git submodule update --init -``` - -In addition to configuration parameters mentioned above, following definitions has to be set: - -- TOOLCHAIN_PREFIX: path to arm toolchain (i.e /home/user/gcc-arm-none-eabi-9-2019-q4-major) -- STM32Cube_DIR: path to STM32 Cube libraries (i.e /home/user/STM32Cube/Repository/STM32Cube_FW_F4_V1.25.0) -- STM32_CHIP: name of STM32 for which project should be compiled (i.e STM32F407VG) -- PORT: STM32 - -This can be achieved by passing definitions to the command line, such as: - -``` -cmake -DTOOLCHAIN_PREFIX="/path_to_toolchain" -DSTM32Cube_DIR="path_to_stm32Cube" -DSTM32_CHIP="STM32F407VG" -DPORT="STM32" .. && cmake --build . -``` - -Alternatively, those variables can be set in the top level `cmake` directory: - -``` -set(TOOLCHAIN_PREFIX path_to_toolchain) -set(STM32Cube_DIR path_to_stm32_HAL) -set(STM32_CHIP STM32F407VG) -set(PORT STM32) -``` - -### Zephyr support - -The Zephyr port is ready to be integrated into Zephyr apps as a Zephyr module. In the manifest file (west.yml), add: - -``` - - name: esp-flasher - url: https://github.com/espressif/esp-serial-flasher - revision: master - path: modules/lib/esp_flasher -``` - -And add - -``` -CONFIG_ESP_SERIAL_FLASHER=y -CONFIG_CONSOLE_GETCHAR=y -CONFIG_SERIAL_FLASHER_MD5_ENABLED=y -``` - -to the project configuration `prj.conf`. - -For the C/C++ source code, the example code provided in `examples/zephyr_example` can be used as a starting point. - -## Licence - -Code is distributed under Apache 2.0 license. - -## Known limitations - -Size of new binary image has to be known before flashing. diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/esp_loader.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/esp_loader.h deleted file mode 100644 index 43f4fb9d9..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/esp_loader.h +++ /dev/null @@ -1,313 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Used for backwards compatibility with the previous API */ -#define esp_loader_change_baudrate esp_loader_change_transmission_rate - -/** - * Macro which can be used to check the error code, - * and return in case the code is not ESP_LOADER_SUCCESS. - */ -#define RETURN_ON_ERROR(x) do { \ - esp_loader_error_t _err_ = (x); \ - if (_err_ != ESP_LOADER_SUCCESS) { \ - return _err_; \ - } \ -} while(0) - -/** - * @brief Error codes - */ -typedef enum { - ESP_LOADER_SUCCESS, /*!< Success */ - ESP_LOADER_ERROR_FAIL, /*!< Unspecified error */ - ESP_LOADER_ERROR_TIMEOUT, /*!< Timeout elapsed */ - ESP_LOADER_ERROR_IMAGE_SIZE, /*!< Image size to flash is larger than flash size */ - ESP_LOADER_ERROR_INVALID_MD5, /*!< Computed and received MD5 does not match */ - ESP_LOADER_ERROR_INVALID_PARAM, /*!< Invalid parameter passed to function */ - ESP_LOADER_ERROR_INVALID_TARGET, /*!< Connected target is invalid */ - ESP_LOADER_ERROR_UNSUPPORTED_CHIP, /*!< Attached chip is not supported */ - ESP_LOADER_ERROR_UNSUPPORTED_FUNC, /*!< Function is not supported on attached target */ - ESP_LOADER_ERROR_INVALID_RESPONSE /*!< Internal error */ -} esp_loader_error_t; - -/** - * @brief Supported targets - */ -typedef enum { - ESP8266_CHIP = 0, - ESP32_CHIP = 1, - ESP32S2_CHIP = 2, - ESP32C3_CHIP = 3, - ESP32S3_CHIP = 4, - ESP32C2_CHIP = 5, - ESP32H4_CHIP = 6, - ESP32H2_CHIP = 7, - ESP_MAX_CHIP = 8, - ESP_UNKNOWN_CHIP = 8 -} target_chip_t; - -/** - * @brief Application binary header - */ -typedef struct { - uint8_t magic; - uint8_t segments; - uint8_t flash_mode; - uint8_t flash_size_freq; - uint32_t entrypoint; -} esp_loader_bin_header_t; - -/** - * @brief Segment binary header - */ -typedef struct { - uint32_t addr; - uint32_t size; - uint8_t *data; -} esp_loader_bin_segment_t; - -/** - * @brief SPI pin configuration arguments - */ -typedef union { - struct { - uint32_t pin_clk: 6; - uint32_t pin_q: 6; - uint32_t pin_d: 6; - uint32_t pin_cs: 6; - uint32_t pin_hd: 6; - uint32_t zero: 2; - }; - uint32_t val; -} esp_loader_spi_config_t; - -/** - * @brief Connection arguments - */ -typedef struct { - uint32_t sync_timeout; /*!< Maximum time to wait for response from serial interface. */ - int32_t trials; /*!< Number of trials to connect to target. If greater than 1, - 100 millisecond delay is inserted after each try. */ -} esp_loader_connect_args_t; - -#define ESP_LOADER_CONNECT_DEFAULT() { \ - .sync_timeout = 100, \ - .trials = 10, \ -} - -/** - * @brief Connects to the target - * - * @param connect_args[in] Timing parameters to be used for connecting to target. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args); - -/** - * @brief Returns attached target chip. - * - * @warning This function can only be called after connection with target - * has been successfully established by calling esp_loader_connect(). - * - * @return One of target_chip_t - */ -target_chip_t esp_loader_get_target(void); - - -#ifdef SERIAL_FLASHER_INTERFACE_UART -/** - * @brief Initiates flash operation - * - * @param offset[in] Address from which flash operation will be performed. - * @param image_size[in] Size of the whole binary to be loaded into flash. - * @param block_size[in] Size of buffer used in subsequent calls to esp_loader_flash_write. - * - * @note image_size is size of the whole image, whereas, block_size is chunk of data sent - * to the target, each time esp_loader_flash_write function is called. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size, uint32_t block_size); - -/** - * @brief Writes supplied data to target's flash memory. - * - * @param payload[in] Data to be flashed into target's memory. - * @param size[in] Size of payload in bytes. - * - * @note size must not be greater that block_size supplied to previously called - * esp_loader_flash_start function. If size is less than block_size, - * remaining bytes of payload buffer will be padded with 0xff. - * Therefore, size of payload buffer has to be equal or greater than block_size. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_flash_write(void *payload, uint32_t size); - -/** - * @brief Ends flash operation. - * - * @param reboot[in] reboot the target if true. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_flash_finish(bool reboot); -#endif /* SERIAL_FLASHER_INTERFACE_UART */ - - -/** - * @brief Initiates mem operation, initiates loading for program into target RAM - * - * @param offset[in] Address from which mem operation will be performed. - * @param size[in] Size of the whole binary to be loaded into mem. - * @param block_size[in] Size of buffer used in subsequent calls to esp_loader_mem_write. - * - * @note image_size is size of the whole image, whereas, block_size is chunk of data sent - * to the target, each time esp_mem_flash_write function is called. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_mem_start(uint32_t offset, uint32_t size, uint32_t block_size); - - -/** - * @brief Writes supplied data to target's mem memory. - * - * @param payload[in] Data to be loaded into target's memory. - * @param size[in] Size of data in bytes. - * - * @note size must not be greater that block_size supplied to previously called - * esp_loader_mem_start function. - * Therefore, size of data buffer has to be equal or greater than block_size. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_mem_write(const void *payload, uint32_t size); - - -/** - * @brief Ends mem operation, finish loading for program into target RAM - * and send the entrypoint of ram_loadable app - * - * @param entrypoint[in] entrypoint of ram program. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_mem_finish(uint32_t entrypoint); - - -/** - * @brief Writes register. - * - * @param address[in] Address of register. - * @param reg_value[in] New register value. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_write_register(uint32_t address, uint32_t reg_value); - -/** - * @brief Reads register. - * - * @param address[in] Address of register. - * @param reg_value[out] Register value. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - */ -esp_loader_error_t esp_loader_read_register(uint32_t address, uint32_t *reg_value); - -/** - * @brief Change baud rate. - * - * @note Baud rate has to be also adjusted accordingly on host MCU, as - * target's baud rate is changed upon return from this function. - * - * @param transmission_rate[in] new baud rate to be set. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - * - ESP_LOADER_ERROR_UNSUPPORTED_FUNC Unsupported on the target - */ -esp_loader_error_t esp_loader_change_transmission_rate(uint32_t transmission_rate); - -/** - * @brief Verify target's flash integrity by checking MD5. - * MD5 checksum is computed from data pushed to target's memory by calling - * esp_loader_flash_write() function and compared against target's MD5. - * Target computes checksum based on offset and image_size passed to - * esp_loader_flash_start() function. - * - * @note This function is only available if MD5_ENABLED is set. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_INVALID_MD5 MD5 does not match - * - ESP_LOADER_ERROR_TIMEOUT Timeout - * - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error - * - ESP_LOADER_ERROR_UNSUPPORTED_FUNC Unsupported on the target - */ -#if MD5_ENABLED -esp_loader_error_t esp_loader_flash_verify(void); -#endif -/** - * @brief Toggles reset pin. - */ -void esp_loader_reset_target(void); - - - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/esp_loader_io.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/esp_loader_io.h deleted file mode 100644 index 2ff99b4f9..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/esp_loader_io.h +++ /dev/null @@ -1,112 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - - #pragma once - -#include -#include "esp_loader.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Changes the transmission rate of the used peripheral. - */ -esp_loader_error_t loader_port_change_transmission_rate(uint32_t transmission_rate); - -/** - * @brief Writes data over the io interface. - * - * @param data[in] Buffer with data to be written. - * @param size[in] Size of data in bytes. - * @param timeout[in] Timeout in milliseconds. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout elapsed - */ -esp_loader_error_t loader_port_write(const uint8_t *data, uint16_t size, uint32_t timeout); - -/** - * @brief Reads data from the io interface. - * - * @param data[out] Buffer into which received data will be written. - * @param size[in] Number of bytes to read. - * @param timeout[in] Timeout in milliseconds. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_TIMEOUT Timeout elapsed - */ -esp_loader_error_t loader_port_read(uint8_t *data, uint16_t size, uint32_t timeout); - -/** - * @brief Delay in milliseconds. - * - * @param ms[in] Number of milliseconds. - * - */ -void loader_port_delay_ms(uint32_t ms); - -/** - * @brief Starts timeout timer. - * - * @param ms[in] Number of milliseconds. - * - */ -void loader_port_start_timer(uint32_t ms); - -/** - * @brief Returns remaining time since timer was started by calling esp_loader_start_timer. - * 0 if timer has elapsed. - * - * @return Number of milliseconds. - * - */ -uint32_t loader_port_remaining_time(void); - -/** - * @brief Asserts bootstrap pins to enter boot mode and toggles reset pin. - * - * @note Reset pin should stay asserted for at least 20 milliseconds. - */ -void loader_port_enter_bootloader(void); - -/** - * @brief Toggles reset pin. - * - * @note Reset pin should stay asserted for at least 20 milliseconds. - */ -void loader_port_reset_target(void); - -/** - * @brief Function can be defined by user to print debug message. - * - * @note Empty weak function is used, otherwise. - * - */ -void loader_port_debug_print(const char *str); - -#ifdef SERIAL_FLASHER_INTERFACE_SPI -/** - * @brief Sets the chip select to a defined level - */ -void loader_port_spi_set_cs(uint32_t level); -#endif /* SERIAL_FLASHER_INTERFACE_SPI */ - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/serial_io.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/serial_io.h deleted file mode 100644 index e34939300..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/include/serial_io.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#warning Please replace serial_io.h with esp_loader_io.h and change the function names \ - to match the new API - -/* Defines used to avoid breaking existing ports */ -#define loader_port_change_baudrate loader_port_change_transmission_rate -#define loader_port_serial_write loader_port_write -#define loader_port_serial_read loader_port_read - -#include "esp_loader_io.h" diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_port.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_port.c deleted file mode 100644 index a6c8687c6..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_port.c +++ /dev/null @@ -1,181 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include "esp32_port.h" -#include "driver/uart.h" -#include "driver/gpio.h" -#include "esp_timer.h" -#include "esp_log.h" -#include "esp_idf_version.h" -#include - -#ifdef SERIAL_FLASHER_DEBUG_TRACE -static void transfer_debug_print(const uint8_t *data, uint16_t size, bool write) -{ - static bool write_prev = false; - - if (write_prev != write) { - write_prev = write; - printf("\n--- %s ---\n", write ? "WRITE" : "READ"); - } - - for (uint32_t i = 0; i < size; i++) { - printf("%02x ", data[i]); - } -} -#endif - -static int64_t s_time_end; -static int32_t s_uart_port; -static int32_t s_reset_trigger_pin; -static int32_t s_gpio0_trigger_pin; - -esp_loader_error_t loader_port_esp32_init(const loader_esp32_config_t *config) -{ - s_uart_port = config->uart_port; - s_reset_trigger_pin = config->reset_trigger_pin; - s_gpio0_trigger_pin = config->gpio0_trigger_pin; - - // Initialize UART - uart_config_t uart_config = { - .baud_rate = config->baud_rate, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) - .source_clk = UART_SCLK_DEFAULT, -#endif - }; - - int rx_buffer_size = config->rx_buffer_size ? config->rx_buffer_size : 400; - int tx_buffer_size = config->tx_buffer_size ? config->tx_buffer_size : 400; - QueueHandle_t *uart_queue = config->uart_queue ? config->uart_queue : NULL; - int queue_size = config->queue_size ? config->queue_size : 0; - - if ( uart_param_config(s_uart_port, &uart_config) != ESP_OK ) { - return ESP_LOADER_ERROR_FAIL; - } - if ( uart_set_pin(s_uart_port, config->uart_tx_pin, config->uart_rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) != ESP_OK ) { - return ESP_LOADER_ERROR_FAIL; - } - if ( uart_driver_install(s_uart_port, rx_buffer_size, tx_buffer_size, queue_size, uart_queue, 0) != ESP_OK ) { - return ESP_LOADER_ERROR_FAIL; - } - - // Initialize boot pin selection pins - gpio_reset_pin(s_reset_trigger_pin); - gpio_set_pull_mode(s_reset_trigger_pin, GPIO_PULLUP_ONLY); - gpio_set_direction(s_reset_trigger_pin, GPIO_MODE_OUTPUT); - - gpio_reset_pin(s_gpio0_trigger_pin); - gpio_set_pull_mode(s_gpio0_trigger_pin, GPIO_PULLUP_ONLY); - gpio_set_direction(s_gpio0_trigger_pin, GPIO_MODE_OUTPUT); - - return ESP_LOADER_SUCCESS; -} - -void loader_port_esp32_deinit(void) -{ - uart_driver_delete(s_uart_port); -} - - -esp_loader_error_t loader_port_write(const uint8_t *data, uint16_t size, uint32_t timeout) -{ - uart_write_bytes(s_uart_port, (const char *)data, size); - esp_err_t err = uart_wait_tx_done(s_uart_port, pdMS_TO_TICKS(timeout)); - - if (err == ESP_OK) { -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, size, true); -#endif - return ESP_LOADER_SUCCESS; - } else if (err == ESP_ERR_TIMEOUT) { - return ESP_LOADER_ERROR_TIMEOUT; - } else { - return ESP_LOADER_ERROR_FAIL; - } -} - - -esp_loader_error_t loader_port_read(uint8_t *data, uint16_t size, uint32_t timeout) -{ - int read = uart_read_bytes(s_uart_port, data, size, pdMS_TO_TICKS(timeout)); - - if (read < 0) { - return ESP_LOADER_ERROR_FAIL; - } else if (read < size) { -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, read, false); -#endif - return ESP_LOADER_ERROR_TIMEOUT; - } else { -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, read, false); -#endif - return ESP_LOADER_SUCCESS; - } -} - - -// Set GPIO0 LOW, then -// assert reset pin for 50 milliseconds. -void loader_port_enter_bootloader(void) -{ - gpio_set_level(s_gpio0_trigger_pin, 0); - loader_port_reset_target(); - loader_port_delay_ms(SERIAL_FLASHER_BOOT_HOLD_TIME_MS); - gpio_set_level(s_gpio0_trigger_pin, 1); -} - - -void loader_port_reset_target(void) -{ - gpio_set_level(s_reset_trigger_pin, 0); - loader_port_delay_ms(SERIAL_FLASHER_RESET_HOLD_TIME_MS); - gpio_set_level(s_reset_trigger_pin, 1); -} - - -void loader_port_delay_ms(uint32_t ms) -{ - usleep(ms * 1000); -} - - -void loader_port_start_timer(uint32_t ms) -{ - s_time_end = esp_timer_get_time() + ms * 1000; -} - - -uint32_t loader_port_remaining_time(void) -{ - int64_t remaining = (s_time_end - esp_timer_get_time()) / 1000; - return (remaining > 0) ? (uint32_t)remaining : 0; -} - - -void loader_port_debug_print(const char *str) -{ - printf("DEBUG: %s\n", str); -} - -esp_loader_error_t loader_port_change_transmission_rate(uint32_t baudrate) -{ - esp_err_t err = uart_set_baudrate(s_uart_port, baudrate); - return (err == ESP_OK) ? ESP_LOADER_SUCCESS : ESP_LOADER_ERROR_FAIL; -} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_port.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_port.h deleted file mode 100644 index c098762d2..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_port.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - - #pragma once - -#include "esp_loader_io.h" -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - uint32_t baud_rate; /*!< Initial baud rate, can be changed later */ - uint32_t uart_port; /*!< UART port */ - uint32_t uart_rx_pin; /*!< This pin will be configured as UART Rx pin */ - uint32_t uart_tx_pin; /*!< This pin will be configured as UART Tx pin */ - uint32_t reset_trigger_pin; /*!< This pin will be used to reset target chip */ - uint32_t gpio0_trigger_pin; /*!< This pin will be used to toggle set IO0 of target chip */ - uint32_t rx_buffer_size; /*!< Set to zero for default RX buffer size */ - uint32_t tx_buffer_size; /*!< Set to zero for default TX buffer size */ - uint32_t queue_size; /*!< Set to zero for default UART queue size */ - QueueHandle_t *uart_queue; /*!< Set to NULL, if UART queue handle is not - necessary. Otherwise, it will be assigned here */ -} loader_esp32_config_t; - -/** - * @brief Initializes serial interface. - * - * @param baud_rate[in] Communication speed. - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_FAIL Initialization failure - */ -esp_loader_error_t loader_port_esp32_init(const loader_esp32_config_t *config); - -/** - * @brief Deinitialize serial interface. - */ -void loader_port_esp32_deinit(void); - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_spi_port.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_spi_port.c deleted file mode 100644 index 55e8f3e57..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_spi_port.c +++ /dev/null @@ -1,298 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include "esp32_spi_port.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "esp_timer.h" -#include "esp_log.h" -#include "esp_idf_version.h" -#include - -// #define SERIAL_DEBUG_ENABLE - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0) -#define DMA_CHAN SPI_DMA_CH_AUTO -#else -#define DMA_CHAN 1 -#endif - -#define WORD_ALIGNED(ptr) ((size_t)ptr % sizeof(size_t) == 0) - -#ifdef SERIAL_DEBUG_ENABLE - -static void dec_to_hex_str(const uint8_t dec, uint8_t hex_str[3]) -{ - static const uint8_t dec_to_hex[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - - hex_str[0] = dec_to_hex[dec >> 4]; - hex_str[1] = dec_to_hex[dec & 0xF]; - hex_str[2] = '\0'; -} - -static void serial_debug_print(const uint8_t *data, uint16_t size, bool write) -{ - static bool write_prev = false; - uint8_t hex_str[3]; - - if(write_prev != write) { - write_prev = write; - printf("\n--- %s ---\n", write ? "WRITE" : "READ"); - } - - for(uint32_t i = 0; i < size; i++) { - dec_to_hex_str(data[i], hex_str); - printf("%s ", hex_str); - } -} - -#else -static void serial_debug_print(const uint8_t *data, uint16_t size, bool write) { } -#endif - -static spi_host_device_t s_spi_bus; -static spi_bus_config_t s_spi_config; -static spi_device_handle_t s_device_h; -static spi_device_interface_config_t s_device_config; -static int64_t s_time_end; -static uint32_t s_reset_trigger_pin; -static uint32_t s_strap_bit0_pin; -static uint32_t s_strap_bit1_pin; -static uint32_t s_strap_bit2_pin; -static uint32_t s_strap_bit3_pin; -static uint32_t s_spi_cs_pin; - -esp_loader_error_t loader_port_esp32_spi_init(const loader_esp32_spi_config_t *config) -{ - /* Initialize the global static variables*/ - s_spi_bus = config->spi_bus; - s_reset_trigger_pin = config->reset_trigger_pin; - s_strap_bit0_pin = config->strap_bit0_pin; - s_strap_bit1_pin = config->strap_bit1_pin; - s_strap_bit2_pin = config->strap_bit2_pin; - s_strap_bit3_pin = config->strap_bit3_pin; - s_spi_cs_pin = config->spi_cs_pin; - - /* Configure and initialize the SPI bus*/ - s_spi_config.mosi_io_num = config->spi_mosi_pin; - s_spi_config.miso_io_num = config->spi_miso_pin; - s_spi_config.sclk_io_num = config->spi_clk_pin; - s_spi_config.quadwp_io_num = config->spi_quadwp_pin; - s_spi_config.quadhd_io_num = config->spi_quadhd_pin; - s_spi_config.max_transfer_sz = 4096 * 4; - - if (spi_bus_initialize(s_spi_bus, &s_spi_config, DMA_CHAN) != ESP_OK) { - return ESP_LOADER_ERROR_FAIL; - } - - /* Configure and add the device */ - s_device_config.clock_speed_hz = config->frequency; - s_device_config.spics_io_num = -1; /* We're using the chip select pin as GPIO as we need to - chain multiple transactions with CS pulled down */ - s_device_config.flags = SPI_DEVICE_HALFDUPLEX; - s_device_config.queue_size = 16; - - if (spi_bus_add_device(s_spi_bus, &s_device_config, &s_device_h) != ESP_OK) { - return ESP_LOADER_ERROR_FAIL; - } - - /* Initialize the pins except for the strapping ones */ - gpio_reset_pin(s_reset_trigger_pin); - gpio_set_pull_mode(s_reset_trigger_pin, GPIO_PULLUP_ONLY); - gpio_set_direction(s_reset_trigger_pin, GPIO_MODE_OUTPUT); - gpio_set_level(s_reset_trigger_pin, 1); - - gpio_reset_pin(s_spi_cs_pin); - gpio_set_pull_mode(s_spi_cs_pin, GPIO_PULLUP_ONLY); - gpio_set_direction(s_spi_cs_pin, GPIO_MODE_OUTPUT); - gpio_set_level(s_spi_cs_pin, 1); - - return ESP_LOADER_SUCCESS; -} - - -void loader_port_esp32_spi_deinit(void) -{ - gpio_reset_pin(s_reset_trigger_pin); - gpio_reset_pin(s_spi_cs_pin); - spi_bus_remove_device(s_device_h); - spi_bus_free(s_spi_bus); -} - - -void loader_port_spi_set_cs(const uint32_t level) { - gpio_set_level(s_spi_cs_pin, level); -} - - -esp_loader_error_t loader_port_write(const uint8_t *data, const uint16_t size, const uint32_t timeout) -{ - /* Due to the fact that the SPI driver uses DMA for larger transfers, - and the DMA requirements, the buffer must be word aligned */ - if (data == NULL || !WORD_ALIGNED(data)) { - return ESP_LOADER_ERROR_INVALID_PARAM; - } - - serial_debug_print(data, size, true); - - spi_transaction_t transaction = { - .tx_buffer = data, - .rx_buffer = NULL, - .length = size * 8U, - .rxlength = 0, - }; - - esp_err_t err = spi_device_transmit(s_device_h, &transaction); - - if (err == ESP_OK) { - serial_debug_print(data, size, false); - return ESP_LOADER_SUCCESS; - } else if (err == ESP_ERR_TIMEOUT) { - return ESP_LOADER_ERROR_TIMEOUT; - } else { - return ESP_LOADER_ERROR_FAIL; - } -} - - -esp_loader_error_t loader_port_read(uint8_t *data, const uint16_t size, const uint32_t timeout) -{ - /* Due to the fact that the SPI driver uses DMA for larger transfers, - and the DMA requirements, the buffer must be word aligned */ - if (data == NULL || !WORD_ALIGNED(data)) { - return ESP_LOADER_ERROR_INVALID_PARAM; - } - - serial_debug_print(data, size, true); - - spi_transaction_t transaction = { - .tx_buffer = NULL, - .rx_buffer = data, - .rxlength = size * 8, - }; - - esp_err_t err = spi_device_transmit(s_device_h, &transaction); - - if (err == ESP_OK) { - serial_debug_print(data, size, false); - return ESP_LOADER_SUCCESS; - } else if (err == ESP_ERR_TIMEOUT) { - return ESP_LOADER_ERROR_TIMEOUT; - } else { - return ESP_LOADER_ERROR_FAIL; - } -} - - -void loader_port_enter_bootloader(void) -{ - /* - We have to initialize the GPIO pins for the target strapping pins here, - as they may overlap with target SPI pins. - For instance in the case of ESP32C3 MISO and strapping bit 0 pins overlap. - */ - spi_bus_remove_device(s_device_h); - spi_bus_free(s_spi_bus); - - gpio_reset_pin(s_strap_bit0_pin); - gpio_set_pull_mode(s_strap_bit0_pin, GPIO_PULLUP_ONLY); - gpio_set_direction(s_strap_bit0_pin, GPIO_MODE_OUTPUT); - - gpio_reset_pin(s_strap_bit1_pin); - gpio_set_pull_mode(s_strap_bit1_pin, GPIO_PULLUP_ONLY); - gpio_set_direction(s_strap_bit1_pin, GPIO_MODE_OUTPUT); - - gpio_reset_pin(s_strap_bit2_pin); - gpio_set_pull_mode(s_strap_bit2_pin, GPIO_PULLUP_ONLY); - gpio_set_direction(s_strap_bit2_pin, GPIO_MODE_OUTPUT); - - gpio_reset_pin(s_strap_bit3_pin); - gpio_set_pull_mode(s_strap_bit3_pin, GPIO_PULLUP_ONLY); - gpio_set_direction(s_strap_bit3_pin, GPIO_MODE_OUTPUT); - - /* Set the strapping pins and perform the reset sequence */ - gpio_set_level(s_strap_bit0_pin, 1); - gpio_set_level(s_strap_bit1_pin, 0); - gpio_set_level(s_strap_bit2_pin, 0); - gpio_set_level(s_strap_bit3_pin, 0); - loader_port_reset_target(); - loader_port_delay_ms(SERIAL_FLASHER_BOOT_HOLD_TIME_MS); - gpio_set_level(s_strap_bit3_pin, 1); - gpio_set_level(s_strap_bit0_pin, 0); - - /* Disable the strapping pins so they can be used by the slave later */ - gpio_reset_pin(s_strap_bit0_pin); - gpio_reset_pin(s_strap_bit1_pin); - gpio_reset_pin(s_strap_bit2_pin); - gpio_reset_pin(s_strap_bit3_pin); - - /* Restore the SPI bus pins */ - spi_bus_initialize(s_spi_bus, &s_spi_config, DMA_CHAN); - spi_bus_add_device(s_spi_bus, &s_device_config, &s_device_h); -} - - -void loader_port_reset_target(void) -{ - gpio_set_level(s_reset_trigger_pin, 0); - loader_port_delay_ms(SERIAL_FLASHER_RESET_HOLD_TIME_MS); - gpio_set_level(s_reset_trigger_pin, 1); -} - - -void loader_port_delay_ms(const uint32_t ms) -{ - usleep(ms * 1000); -} - - -void loader_port_start_timer(const uint32_t ms) -{ - s_time_end = esp_timer_get_time() + ms * 1000; -} - - -uint32_t loader_port_remaining_time(void) -{ - int64_t remaining = (s_time_end - esp_timer_get_time()) / 1000; - return (remaining > 0) ? (uint32_t)remaining : 0; -} - - -void loader_port_debug_print(const char *str) -{ - printf("DEBUG: %s\n", str); -} - - -esp_loader_error_t loader_port_change_transmission_rate(const uint32_t frequency) -{ - if (spi_bus_remove_device(s_device_h) != ESP_OK) { - return ESP_LOADER_ERROR_FAIL; - } - - uint32_t old_frequency = s_device_config.clock_speed_hz; - s_device_config.clock_speed_hz = frequency; - - if (spi_bus_add_device(s_spi_bus, &s_device_config, &s_device_h) != ESP_OK) { - s_device_config.clock_speed_hz = old_frequency; - return ESP_LOADER_ERROR_FAIL; - } - - return ESP_LOADER_SUCCESS; -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_spi_port.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_spi_port.h deleted file mode 100644 index 72304c5df..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/esp32_spi_port.h +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - - #pragma once - -#include "esp_loader_io.h" -#include "driver/spi_master.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - spi_host_device_t spi_bus; - uint32_t frequency; - uint32_t spi_clk_pin; - uint32_t spi_miso_pin; - uint32_t spi_mosi_pin; - uint32_t spi_cs_pin; - uint32_t spi_quadwp_pin; - uint32_t spi_quadhd_pin; - uint32_t reset_trigger_pin; - uint32_t strap_bit0_pin; - uint32_t strap_bit1_pin; - uint32_t strap_bit2_pin; - uint32_t strap_bit3_pin; -} loader_esp32_spi_config_t; - -/** - * @brief Initializes the SPI interface. - * - * @param config[in] Configuration structure - * - * @return - * - ESP_LOADER_SUCCESS Success - * - ESP_LOADER_ERROR_FAIL Initialization failure - */ -esp_loader_error_t loader_port_esp32_spi_init(const loader_esp32_spi_config_t *config); - -/** - * @brief Deinitializes the SPI interface. - */ -void loader_port_esp32_spi_deinit(void); - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/raspberry_port.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/raspberry_port.c deleted file mode 100644 index d733db702..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/raspberry_port.c +++ /dev/null @@ -1,302 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include "esp_loader_io.h" -#include "protocol.h" -#include -#include "raspberry_port.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef SERIAL_FLASHER_DEBUG_TRACE -static void transfer_debug_print(const uint8_t *data, uint16_t size, bool write) -{ - static bool write_prev = false; - - if (write_prev != write) { - write_prev = write; - printf("\n--- %s ---\n", write ? "WRITE" : "READ"); - } - - for (uint32_t i = 0; i < size; i++) { - printf("%02x ", data[i]); - } -} -#endif - -static int serial; -static int64_t s_time_end; -static int32_t s_reset_trigger_pin; -static int32_t s_gpio0_trigger_pin; - - -static speed_t convert_baudrate(int baud) -{ - switch (baud) { - case 50: return B50; - case 75: return B75; - case 110: return B110; - case 134: return B134; - case 150: return B150; - case 200: return B200; - case 300: return B300; - case 600: return B600; - case 1200: return B1200; - case 1800: return B1800; - case 2400: return B2400; - case 4800: return B4800; - case 9600: return B9600; - case 19200: return B19200; - case 38400: return B38400; - case 57600: return B57600; - case 115200: return B115200; - case 230400: return B230400; - case 460800: return B460800; - case 500000: return B500000; - case 576000: return B576000; - case 921600: return B921600; - case 1000000: return B1000000; - case 1152000: return B1152000; - case 1500000: return B1500000; - case 2000000: return B2000000; - case 2500000: return B2500000; - case 3000000: return B3000000; - case 3500000: return B3500000; - case 4000000: return B4000000; - default: return -1; - } -} - -static int serialOpen (const char *device, uint32_t baudrate) -{ - struct termios options; - int status, fd; - - if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1) { - printf("Error occured while opening serial port !\n"); - return -1 ; - } - - fcntl (fd, F_SETFL, O_RDWR) ; - - // Get and modify current options: - - tcgetattr (fd, &options); - speed_t baud = convert_baudrate(baudrate); - - if(baud < 0) { - printf("Invalid baudrate!\n"); - return -1; - } - - cfmakeraw (&options) ; - cfsetispeed (&options, baud) ; - cfsetospeed (&options, baud) ; - - options.c_cflag |= (CLOCAL | CREAD) ; - options.c_cflag &= ~(PARENB | CSTOPB | CSIZE) ; - options.c_cflag |= CS8 ; - options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ; - options.c_oflag &= ~OPOST ; - options.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl - options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // Disable any special handling of received bytes - - options.c_cc [VMIN] = 0 ; - options.c_cc [VTIME] = 10 ; // 1 Second - - tcsetattr (fd, TCSANOW, &options) ; - - ioctl (fd, TIOCMGET, &status); - - status |= TIOCM_DTR ; - status |= TIOCM_RTS ; - - ioctl (fd, TIOCMSET, &status); - - usleep (10000) ; // 10mS - - return fd ; -} - -static esp_loader_error_t change_baudrate(int file_desc, int baudrate) -{ - struct termios options; - speed_t baud = convert_baudrate(baudrate); - - if(baud < 0) { - return ESP_LOADER_ERROR_INVALID_PARAM; - } - - tcgetattr (file_desc, &options); - - cfmakeraw (&options) ; - cfsetispeed (&options, baud); - cfsetospeed (&options, baud); - - tcsetattr (file_desc, TCSANOW, &options); - - return ESP_LOADER_SUCCESS; -} - -static void set_timeout(uint32_t timeout) -{ - struct termios options; - - timeout /= 100; - timeout = MAX(timeout, 1); - - tcgetattr(serial, &options); - options.c_cc[VTIME] = timeout; - tcsetattr(serial, TCSANOW, &options); -} - -static esp_loader_error_t read_char(char *c, uint32_t timeout) -{ - set_timeout(timeout); - int read_bytes = read(serial, c, 1); - - if (read_bytes == 1) { - return ESP_LOADER_SUCCESS; - } else if (read_bytes == 0) { - return ESP_LOADER_ERROR_TIMEOUT; - } else { - return ESP_LOADER_ERROR_FAIL; - } -} - -static esp_loader_error_t read_data(char *buffer, uint32_t size) -{ - for (int i = 0; i < size; i++) { - uint32_t remaining_time = loader_port_remaining_time(); - RETURN_ON_ERROR( read_char(&buffer[i], remaining_time) ); - } - - return ESP_LOADER_SUCCESS; -} - -esp_loader_error_t loader_port_raspberry_init(const loader_raspberry_config_t *config) -{ - s_reset_trigger_pin = config->reset_trigger_pin; - s_gpio0_trigger_pin = config->gpio0_trigger_pin; - - serial = serialOpen(config->device, config->baudrate); - if (serial < 0) { - printf("Serial port could not be opened!\n"); - return ESP_LOADER_ERROR_FAIL; - } - - if (gpioInitialise() < 0) { - printf("pigpio initialisation failed\n"); - return ESP_LOADER_ERROR_FAIL; - } - - gpioSetMode(config->reset_trigger_pin, PI_OUTPUT); - gpioSetMode(config->gpio0_trigger_pin, PI_OUTPUT); - - return ESP_LOADER_SUCCESS; -} - - -esp_loader_error_t loader_port_write(const uint8_t *data, uint16_t size, uint32_t timeout) -{ - int written = write(serial, data, size); - - if (written < 0) { - return ESP_LOADER_ERROR_FAIL; - } else if (written < size) { -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, written, true); -#endif - return ESP_LOADER_ERROR_TIMEOUT; - } else { -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, written, true); -#endif - return ESP_LOADER_SUCCESS; - } -} - - -esp_loader_error_t loader_port_read(uint8_t *data, uint16_t size, uint32_t timeout) -{ - RETURN_ON_ERROR( read_data(data, size) ); - -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, size, false); -#endif - - return ESP_LOADER_SUCCESS; -} - - -// Set GPIO0 LOW, then assert reset pin for 50 milliseconds. -void loader_port_enter_bootloader(void) -{ - gpioWrite(s_gpio0_trigger_pin, 0); - loader_port_reset_target(); - loader_port_delay_ms(SERIAL_FLASHER_BOOT_HOLD_TIME_MS); - gpioWrite(s_gpio0_trigger_pin, 1); -} - - -void loader_port_reset_target(void) -{ - gpioWrite(s_reset_trigger_pin, 0); - loader_port_delay_ms(SERIAL_FLASHER_RESET_HOLD_TIME_MS); - gpioWrite(s_reset_trigger_pin, 1); -} - - -void loader_port_delay_ms(uint32_t ms) -{ - usleep(ms * 1000); -} - - -void loader_port_start_timer(uint32_t ms) -{ - s_time_end = clock() + (ms * (CLOCKS_PER_SEC / 1000)); -} - - -uint32_t loader_port_remaining_time(void) -{ - int64_t remaining = (s_time_end - clock()) / 1000; - return (remaining > 0) ? (uint32_t)remaining : 0; -} - - -void loader_port_debug_print(const char *str) -{ - printf("DEBUG: %s\n", str); -} - -esp_loader_error_t loader_port_change_transmission_rate(uint32_t baudrate) -{ - return change_baudrate(serial, baudrate); -} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/raspberry_port.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/raspberry_port.h deleted file mode 100644 index 803ab72dd..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/raspberry_port.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#pragma once - -#include -#include "esp_loader_io.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - const char *device; - uint32_t baudrate; - uint32_t reset_trigger_pin; - uint32_t gpio0_trigger_pin; -} loader_raspberry_config_t; - -esp_loader_error_t loader_port_raspberry_init(const loader_raspberry_config_t *config); - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/stm32_port.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/stm32_port.c deleted file mode 100644 index 716c9c91b..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/stm32_port.c +++ /dev/null @@ -1,140 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include -#include -#include -#include -#include -#include "stm32_port.h" - -static UART_HandleTypeDef *uart; -static GPIO_TypeDef* gpio_port_io0, *gpio_port_rst; -static uint16_t gpio_num_io0, gpio_num_rst; - -#ifdef SERIAL_FLASHER_DEBUG_TRACE -static void transfer_debug_print(const uint8_t *data, uint16_t size, bool write) -{ - static bool write_prev = false; - - if (write_prev != write) { - write_prev = write; - printf("\n--- %s ---\n", write ? "WRITE" : "READ"); - } - - for (uint32_t i = 0; i < size; i++) { - printf("%02x ", data[i]); - } -} -#endif - -static uint32_t s_time_end; - -esp_loader_error_t loader_port_write(const uint8_t *data, uint16_t size, uint32_t timeout) -{ - HAL_StatusTypeDef err = HAL_UART_Transmit(uart, (uint8_t *)data, size, timeout); - - if (err == HAL_OK) { -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, size, true); -#endif - return ESP_LOADER_SUCCESS; - } else if (err == HAL_TIMEOUT) { - return ESP_LOADER_ERROR_TIMEOUT; - } else { - return ESP_LOADER_ERROR_FAIL; - } -} - - -esp_loader_error_t loader_port_read(uint8_t *data, uint16_t size, uint32_t timeout) -{ - HAL_StatusTypeDef err = HAL_UART_Receive(uart, data, size, timeout); - - if (err == HAL_OK) { -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, size, false); -#endif - return ESP_LOADER_SUCCESS; - } else if (err == HAL_TIMEOUT) { - return ESP_LOADER_ERROR_TIMEOUT; - } else { - return ESP_LOADER_ERROR_FAIL; - } -} - -void loader_port_stm32_init(loader_stm32_config_t *config) - -{ - uart = config->huart; - gpio_port_io0 = config->port_io0; - gpio_port_rst = config->port_rst; - gpio_num_io0 = config->pin_num_io0; - gpio_num_rst = config->pin_num_rst; -} - -// Set GPIO0 LOW, then -// assert reset pin for 100 milliseconds. -void loader_port_enter_bootloader(void) -{ - HAL_GPIO_WritePin(gpio_port_io0, gpio_num_io0, GPIO_PIN_RESET); - loader_port_reset_target(); - HAL_Delay(SERIAL_FLASHER_BOOT_HOLD_TIME_MS); - HAL_GPIO_WritePin(gpio_port_io0, gpio_num_io0, GPIO_PIN_SET); -} - - -void loader_port_reset_target(void) -{ - HAL_GPIO_WritePin(gpio_port_rst, gpio_num_rst, GPIO_PIN_RESET); - HAL_Delay(SERIAL_FLASHER_RESET_HOLD_TIME_MS); - HAL_GPIO_WritePin(gpio_port_rst, gpio_num_rst, GPIO_PIN_SET); -} - - -void loader_port_delay_ms(uint32_t ms) -{ - HAL_Delay(ms); -} - - -void loader_port_start_timer(uint32_t ms) -{ - s_time_end = HAL_GetTick() + ms; -} - - -uint32_t loader_port_remaining_time(void) -{ - int32_t remaining = s_time_end - HAL_GetTick(); - return (remaining > 0) ? (uint32_t)remaining : 0; -} - - -void loader_port_debug_print(const char *str) -{ - printf("DEBUG: %s", str); -} - -esp_loader_error_t loader_port_change_transmission_rate(uint32_t baudrate) -{ - uart->Init.BaudRate = baudrate; - - if( HAL_UART_Init(uart) != HAL_OK ) { - return ESP_LOADER_ERROR_FAIL; - } - - return ESP_LOADER_SUCCESS; -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/stm32_port.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/stm32_port.h deleted file mode 100644 index a3a89a733..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/stm32_port.h +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#pragma once - -#include -#include "esp_loader_io.h" -#include "stm32f4xx_hal.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - UART_HandleTypeDef *huart; - GPIO_TypeDef *port_io0; - uint16_t pin_num_io0; - GPIO_TypeDef *port_rst; - uint16_t pin_num_rst; -} loader_stm32_config_t; - -void loader_port_stm32_init(loader_stm32_config_t *config); - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/zephyr_port.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/zephyr_port.c deleted file mode 100644 index 21270ba0a..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/zephyr_port.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2022 KT-Elektronik, Klaucke und Partner GmbH - * - * 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. - */ - -#include "zephyr_port.h" -#include -#include - -static const struct device *uart_dev; -static struct gpio_dt_spec enable_spec; -static struct gpio_dt_spec boot_spec; - -static struct tty_serial tty; -static char tty_rx_buf[CONFIG_ESP_SERIAL_FLASHER_UART_BUFSIZE]; -static char tty_tx_buf[CONFIG_ESP_SERIAL_FLASHER_UART_BUFSIZE]; - -#ifdef SERIAL_FLASHER_DEBUG_TRACE -static void transfer_debug_print(const uint8_t *data, uint16_t size, bool write) -{ - static bool write_prev = false; - - if (write_prev != write) { - write_prev = write; - printk("\n--- %s ---\n", write ? "WRITE" : "READ"); - } - - for (uint32_t i = 0; i < size; i++) { - printk("%02x ", data[i]); - } -} -#endif - -esp_loader_error_t configure_tty() -{ - if (tty_init(&tty, uart_dev) < 0 || - tty_set_rx_buf(&tty, tty_rx_buf, sizeof(tty_rx_buf)) < 0 || - tty_set_tx_buf(&tty, tty_tx_buf, sizeof(tty_tx_buf)) < 0) { - return ESP_LOADER_ERROR_FAIL; - } - - return ESP_LOADER_SUCCESS; -} - -esp_loader_error_t loader_port_read(uint8_t *data, const uint16_t size, const uint32_t timeout) -{ - if (!device_is_ready(uart_dev) || data == NULL || size == 0) { - return ESP_LOADER_ERROR_FAIL; - } - - ssize_t total_read = 0; - ssize_t remaining = size; - - tty_set_rx_timeout(&tty, timeout); - while (remaining > 0) { - const uint16_t chunk_size = remaining < CONFIG_ESP_SERIAL_FLASHER_UART_BUFSIZE ? - remaining : CONFIG_ESP_SERIAL_FLASHER_UART_BUFSIZE; - ssize_t read = tty_read(&tty, &data[total_read], chunk_size); - if (read < 0) { - return ESP_LOADER_ERROR_TIMEOUT; - } -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, read, false); -#endif - total_read += read; - remaining -= read; - } - - return ESP_LOADER_SUCCESS; -} - -esp_loader_error_t loader_port_write(const uint8_t *data, const uint16_t size, const uint32_t timeout) -{ - if (!device_is_ready(uart_dev) || data == NULL || size == 0) { - return ESP_LOADER_ERROR_FAIL; - } - - ssize_t total_written = 0; - ssize_t remaining = size; - - tty_set_tx_timeout(&tty, timeout); - while (remaining > 0) { - const uint16_t chunk_size = remaining < CONFIG_ESP_SERIAL_FLASHER_UART_BUFSIZE ? - remaining : CONFIG_ESP_SERIAL_FLASHER_UART_BUFSIZE; - ssize_t written = tty_write(&tty, &data[total_written], chunk_size); - if (written < 0) { - return ESP_LOADER_ERROR_TIMEOUT; - } -#ifdef SERIAL_FLASHER_DEBUG_TRACE - transfer_debug_print(data, written, true); -#endif - total_written += written; - remaining -= written; - } - - return ESP_LOADER_SUCCESS; -} - -esp_loader_error_t loader_port_zephyr_init(const loader_zephyr_config_t *config) -{ - uart_dev = config->uart_dev; - enable_spec = config->enable_spec; - boot_spec = config->boot_spec; - return configure_tty(); -} - -void loader_port_reset_target(void) -{ - gpio_pin_set_dt(&enable_spec, false); - loader_port_delay_ms(CONFIG_SERIAL_FLASHER_RESET_HOLD_TIME_MS); - gpio_pin_set_dt(&enable_spec, true); -} - -void loader_port_enter_bootloader(void) -{ - gpio_pin_set_dt(&boot_spec, false); - loader_port_reset_target(); - loader_port_delay_ms(CONFIG_SERIAL_FLASHER_BOOT_HOLD_TIME_MS); - gpio_pin_set_dt(&boot_spec, true); -} - -void loader_port_delay_ms(uint32_t ms) -{ - k_msleep(ms); -} - -static uint64_t s_time_end; - -void loader_port_start_timer(uint32_t ms) -{ - s_time_end = sys_clock_timeout_end_calc(Z_TIMEOUT_MS(ms)); -} - -uint32_t loader_port_remaining_time(void) -{ - int64_t remaining = k_ticks_to_ms_floor64(s_time_end - k_uptime_ticks()); - return (remaining > 0) ? (uint32_t)remaining : 0; -} - -esp_loader_error_t loader_port_change_transmission_rate(uint32_t baudrate) -{ - struct uart_config uart_config; - - if (!device_is_ready(uart_dev)) { - return ESP_LOADER_ERROR_FAIL; - } - - if (uart_config_get(uart_dev, &uart_config) != 0) { - return ESP_LOADER_ERROR_FAIL; - } - uart_config.baudrate = baudrate; - - if (uart_configure(uart_dev, &uart_config) != 0) { - return ESP_LOADER_ERROR_FAIL; - } - - /* bitrate-change can require tty re-configuration */ - return configure_tty(); -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/zephyr_port.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/zephyr_port.h deleted file mode 100644 index 0b104e375..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/port/zephyr_port.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022 KT-Elektronik, Klaucke und Partner GmbH - * - * 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. - */ - -#pragma once - -#include "esp_loader_io.h" -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - const struct device *uart_dev; - const struct gpio_dt_spec enable_spec; - const struct gpio_dt_spec boot_spec; -} loader_zephyr_config_t; - -esp_loader_error_t loader_port_zephyr_init(const loader_zephyr_config_t *config); - -#ifdef __cplusplus -} -#endif - diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/esp_targets.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/esp_targets.h deleted file mode 100644 index cf8af91fa..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/esp_targets.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright 2020 Espressif Systems (Shanghai) PTE LTD - * - * 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. - */ - -#pragma once - -#include -#include "esp_loader.h" - -typedef struct { - uint32_t cmd; - uint32_t usr; - uint32_t usr1; - uint32_t usr2; - uint32_t w0; - uint32_t mosi_dlen; - uint32_t miso_dlen; -} target_registers_t; - -esp_loader_error_t loader_detect_chip(target_chip_t *target, const target_registers_t **regs); -esp_loader_error_t loader_read_spi_config(target_chip_t target_chip, uint32_t *spi_config); -bool encryption_in_begin_flash_cmd(target_chip_t target); \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/md5_hash.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/md5_hash.h deleted file mode 100644 index eb5738c8b..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/md5_hash.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif -struct MD5Context { - uint32_t buf[4]; - uint32_t bits[2]; - uint8_t in[64]; -}; - -void MD5Init(struct MD5Context *context); -void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len); -void MD5Final(unsigned char digest[16], struct MD5Context *context); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/protocol.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/protocol.h deleted file mode 100644 index fb8b086fd..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/protocol.h +++ /dev/null @@ -1,230 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#pragma once - -#include -#include -#include "esp_loader.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define STATUS_FAILURE 1 -#define STATUS_SUCCESS 0 - -#define READ_DIRECTION 1 -#define WRITE_DIRECTION 0 - -#define MD5_SIZE 32 - -typedef enum __attribute__((packed)) -{ - FLASH_BEGIN = 0x02, - FLASH_DATA = 0x03, - FLASH_END = 0x04, - MEM_BEGIN = 0x05, - MEM_END = 0x06, - MEM_DATA = 0x07, - SYNC = 0x08, - WRITE_REG = 0x09, - READ_REG = 0x0a, - - SPI_SET_PARAMS = 0x0b, - SPI_ATTACH = 0x0d, - CHANGE_BAUDRATE = 0x0f, - FLASH_DEFL_BEGIN = 0x10, - FLASH_DEFL_DATA = 0x11, - FLASH_DEFL_END = 0x12, - SPI_FLASH_MD5 = 0x13, -} command_t; - -typedef enum __attribute__((packed)) -{ - RESPONSE_OK = 0x00, - INVALID_COMMAND = 0x05, // parameters or length field is invalid - COMMAND_FAILED = 0x06, // Failed to act on received message - INVALID_CRC = 0x07, // Invalid CRC in message - FLASH_WRITE_ERR = 0x08, // After writing a block of data to flash, the ROM loader reads the value back and the 8-bit CRC is compared to the data read from flash. If they don't match, this error is returned. - FLASH_READ_ERR = 0x09, // SPI read failed - READ_LENGTH_ERR = 0x0a, // SPI read request length is too long - DEFLATE_ERROR = 0x0b, // ESP32 compressed uploads only -} error_code_t; - -typedef struct __attribute__((packed)) -{ - uint8_t direction; - uint8_t command; // One of command_t - uint16_t size; - uint32_t checksum; -} command_common_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t erase_size; - uint32_t packet_count; - uint32_t packet_size; - uint32_t offset; - uint32_t encrypted; -} flash_begin_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t data_size; - uint32_t sequence_number; - uint32_t zero_0; - uint32_t zero_1; -} data_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t stay_in_loader; -} flash_end_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t total_size; - uint32_t blocks; - uint32_t block_size; - uint32_t offset; -} mem_begin_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t stay_in_loader; - uint32_t entry_point_address; -} mem_end_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint8_t sync_sequence[36]; -} sync_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t address; - uint32_t value; - uint32_t mask; - uint32_t delay_us; -} write_reg_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t address; -} read_reg_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t configuration; - uint32_t zero; // ESP32 ROM only -} spi_attach_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t new_baudrate; - uint32_t old_baudrate; -} change_baudrate_command_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t address; - uint32_t size; - uint32_t reserved_0; - uint32_t reserved_1; -} spi_flash_md5_command_t; - -typedef struct __attribute__((packed)) -{ - uint8_t direction; - uint8_t command; // One of command_t - uint16_t size; - uint32_t value; -} common_response_t; - -typedef struct __attribute__((packed)) -{ - uint8_t failed; - uint8_t error; -} response_status_t; - -typedef struct __attribute__((packed)) -{ - common_response_t common; - response_status_t status; -} response_t; - -typedef struct __attribute__((packed)) -{ - common_response_t common; - uint8_t md5[MD5_SIZE]; // ROM only - response_status_t status; -} rom_md5_response_t; - -typedef struct __attribute__((packed)) -{ - command_common_t common; - uint32_t id; - uint32_t total_size; - uint32_t block_size; - uint32_t sector_size; - uint32_t page_size; - uint32_t status_mask; -} write_spi_command_t; - -esp_loader_error_t loader_initialize_conn(esp_loader_connect_args_t *connect_args); - -#ifdef SERIAL_FLASHER_INTERFACE_UART -esp_loader_error_t loader_flash_begin_cmd(uint32_t offset, uint32_t erase_size, uint32_t block_size, uint32_t blocks_to_write, bool encryption); - -esp_loader_error_t loader_flash_data_cmd(const uint8_t *data, uint32_t size); - -esp_loader_error_t loader_flash_end_cmd(bool stay_in_loader); - -esp_loader_error_t loader_sync_cmd(void); - -esp_loader_error_t loader_spi_attach_cmd(uint32_t config); - -esp_loader_error_t loader_md5_cmd(uint32_t address, uint32_t size, uint8_t *md5_out); - -esp_loader_error_t loader_spi_parameters(uint32_t total_size); -#endif /* SERIAL_FLASHER_INTERFACE_UART */ - -esp_loader_error_t loader_mem_begin_cmd(uint32_t offset, uint32_t size, uint32_t blocks_to_write, uint32_t block_size); - -esp_loader_error_t loader_mem_data_cmd(const uint8_t *data, uint32_t size); - -esp_loader_error_t loader_mem_end_cmd(uint32_t entrypoint); - -esp_loader_error_t loader_write_reg_cmd(uint32_t address, uint32_t value, uint32_t mask, uint32_t delay_us); - -esp_loader_error_t loader_read_reg_cmd(uint32_t address, uint32_t *reg); - -esp_loader_error_t loader_change_baudrate_cmd(uint32_t baudrate); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/protocol_prv.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/protocol_prv.h deleted file mode 100644 index 3b9575606..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/protocol_prv.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#pragma once - -#include -#include -#include -#include "esp_loader.h" -#include "protocol.h" - -void log_loader_internal_error(error_code_t error); - -esp_loader_error_t send_cmd(const void *cmd_data, uint32_t size, uint32_t *reg_value); - -esp_loader_error_t send_cmd_with_data(const void *cmd_data, size_t cmd_size, - const void *data, size_t data_size); - -esp_loader_error_t send_cmd_md5(const void *cmd_data, size_t cmd_size, uint8_t md5_out[MD5_SIZE]); diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/slip.h b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/slip.h deleted file mode 100644 index 00f1f7d0d..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/private_include/slip.h +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#pragma once - -#include "esp_loader.h" -#include -#include - -esp_loader_error_t SLIP_receive_data(uint8_t *buff, size_t size); - -esp_loader_error_t SLIP_receive_packet(uint8_t *buff, size_t size); - -esp_loader_error_t SLIP_send(const uint8_t *data, size_t size); - -esp_loader_error_t SLIP_send_delimiter(void); diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/esp_loader.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/esp_loader.c deleted file mode 100644 index 6ef32673c..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/esp_loader.c +++ /dev/null @@ -1,415 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include "protocol.h" -#include "esp_loader_io.h" -#include "esp_loader.h" -#include "esp_targets.h" -#include "md5_hash.h" -#include -#include - -#ifndef MAX -#define MAX(a, b) ((a) > (b)) ? (a) : (b) -#endif - -#ifndef MIN -#define MIN(a, b) ((a) < (b)) ? (a) : (b) -#endif - -#ifndef ROUNDUP -#define ROUNDUP(a, b) (((int)a + (int)b - 1) / (int)b) -#endif - -static const uint32_t DEFAULT_TIMEOUT = 1000; -static const uint32_t DEFAULT_FLASH_TIMEOUT = 3000; // timeout for most flash operations -static const uint32_t LOAD_RAM_TIMEOUT_PER_MB = 2000000; // timeout (per megabyte) for erasing a region - -typedef enum { - SPI_FLASH_READ_ID = 0x9F -} spi_flash_cmd_t; - -static const target_registers_t *s_reg = NULL; -static target_chip_t s_target = ESP_UNKNOWN_CHIP; - -#if MD5_ENABLED - -static const uint32_t MD5_TIMEOUT_PER_MB = 800; -static struct MD5Context s_md5_context; -static uint32_t s_start_address; -static uint32_t s_image_size; - -static inline void init_md5(uint32_t address, uint32_t size) -{ - s_start_address = address; - s_image_size = size; - MD5Init(&s_md5_context); -} - -static inline void md5_update(const uint8_t *data, uint32_t size) -{ - MD5Update(&s_md5_context, data, size); -} - -static inline void md5_final(uint8_t digets[16]) -{ - MD5Final(digets, &s_md5_context); -} - -#else - -static inline void init_md5(uint32_t address, uint32_t size) { } -static inline void md5_update(const uint8_t *data, uint32_t size) { } -static inline void md5_final(uint8_t digets[16]) { } - -#endif - - -static uint32_t timeout_per_mb(uint32_t size_bytes, uint32_t time_per_mb) -{ - uint32_t timeout = time_per_mb * (size_bytes / 1e6); - return MAX(timeout, DEFAULT_FLASH_TIMEOUT); -} - -esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args) -{ - loader_port_enter_bootloader(); - - RETURN_ON_ERROR(loader_initialize_conn(connect_args)); - - RETURN_ON_ERROR(loader_detect_chip(&s_target, &s_reg)); - -#ifdef SERIAL_FLASHER_INTERFACE_UART - esp_loader_error_t err; - uint32_t spi_config; - if (s_target == ESP8266_CHIP) { - err = loader_flash_begin_cmd(0, 0, 0, 0, s_target); - } else { - RETURN_ON_ERROR( loader_read_spi_config(s_target, &spi_config) ); - loader_port_start_timer(DEFAULT_TIMEOUT); - err = loader_spi_attach_cmd(spi_config); - } - return err; -#endif /* SERIAL_FLASHER_INTERFACE_UART */ - return ESP_LOADER_SUCCESS; -} - -target_chip_t esp_loader_get_target(void) -{ - return s_target; -} - -#ifdef SERIAL_FLASHER_INTERFACE_UART -static uint32_t s_flash_write_size = 0; - -static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bits) -{ - if (mosi_bits > 0) { - RETURN_ON_ERROR( esp_loader_write_register(s_reg->mosi_dlen, mosi_bits - 1) ); - } - if (miso_bits > 0) { - RETURN_ON_ERROR( esp_loader_write_register(s_reg->miso_dlen, miso_bits - 1) ); - } - - return ESP_LOADER_SUCCESS; -} - -static esp_loader_error_t spi_set_data_lengths_8266(size_t mosi_bits, size_t miso_bits) -{ - uint32_t mosi_mask = (mosi_bits == 0) ? 0 : mosi_bits - 1; - uint32_t miso_mask = (miso_bits == 0) ? 0 : miso_bits - 1; - return esp_loader_write_register(s_reg->usr1, (miso_mask << 8) | (mosi_mask << 17)); -} - -static esp_loader_error_t spi_flash_command(spi_flash_cmd_t cmd, void *data_tx, size_t tx_size, void *data_rx, size_t rx_size) -{ - assert(rx_size <= 32); // Reading more than 32 bits back from a SPI flash operation is unsupported - assert(tx_size <= 64); // Writing more than 64 bytes of data with one SPI command is unsupported - - uint32_t SPI_USR_CMD = (1 << 31); - uint32_t SPI_USR_MISO = (1 << 28); - uint32_t SPI_USR_MOSI = (1 << 27); - uint32_t SPI_CMD_USR = (1 << 18); - uint32_t CMD_LEN_SHIFT = 28; - - // Save SPI configuration - uint32_t old_spi_usr; - uint32_t old_spi_usr2; - RETURN_ON_ERROR( esp_loader_read_register(s_reg->usr, &old_spi_usr) ); - RETURN_ON_ERROR( esp_loader_read_register(s_reg->usr2, &old_spi_usr2) ); - - if (s_target == ESP8266_CHIP) { - RETURN_ON_ERROR( spi_set_data_lengths_8266(tx_size, rx_size) ); - } else { - RETURN_ON_ERROR( spi_set_data_lengths(tx_size, rx_size) ); - } - - uint32_t usr_reg_2 = (7 << CMD_LEN_SHIFT) | cmd; - uint32_t usr_reg = SPI_USR_CMD; - if (rx_size > 0) { - usr_reg |= SPI_USR_MISO; - } - if (tx_size > 0) { - usr_reg |= SPI_USR_MOSI; - } - - RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr, usr_reg) ); - RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr2, usr_reg_2 ) ); - - if (tx_size == 0) { - // clear data register before we read it - RETURN_ON_ERROR( esp_loader_write_register(s_reg->w0, 0) ); - } else { - uint32_t *data = (uint32_t *)data_tx; - uint32_t words_to_write = (tx_size + 31) / (8 * 4); - uint32_t data_reg_addr = s_reg->w0; - - while (words_to_write--) { - uint32_t word = *data++; - RETURN_ON_ERROR( esp_loader_write_register(data_reg_addr, word) ); - data_reg_addr += 4; - } - } - - RETURN_ON_ERROR( esp_loader_write_register(s_reg->cmd, SPI_CMD_USR) ); - - uint32_t trials = 10; - while (trials--) { - uint32_t cmd_reg; - RETURN_ON_ERROR( esp_loader_read_register(s_reg->cmd, &cmd_reg) ); - if ((cmd_reg & SPI_CMD_USR) == 0) { - break; - } - } - - if (trials == 0) { - return ESP_LOADER_ERROR_TIMEOUT; - } - - RETURN_ON_ERROR( esp_loader_read_register(s_reg->w0, data_rx) ); - - // Restore SPI configuration - RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr, old_spi_usr) ); - RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr2, old_spi_usr2) ); - - return ESP_LOADER_SUCCESS; -} - -static esp_loader_error_t detect_flash_size(size_t *flash_size) -{ - uint32_t flash_id = 0; - - RETURN_ON_ERROR( spi_flash_command(SPI_FLASH_READ_ID, NULL, 0, &flash_id, 24) ); - uint32_t size_id = flash_id >> 16; - - if (size_id < 0x12 || size_id > 0x18) { - return ESP_LOADER_ERROR_UNSUPPORTED_CHIP; - } - - *flash_size = 1 << size_id; - - return ESP_LOADER_SUCCESS; -} - -static uint32_t calc_erase_size(const target_chip_t target, const uint32_t offset, - const uint32_t image_size) -{ - if (target != ESP8266_CHIP) { - return image_size; - } else { - /* Needed to fix a bug in the ESP8266 ROM */ - const uint32_t sectors_per_block = 16U; - const uint32_t sector_size = 4096U; - - const uint32_t num_sectors = (image_size + sector_size - 1) / sector_size; - const uint32_t start_sector = offset / sector_size; - - uint32_t head_sectors = sectors_per_block - (start_sector % sectors_per_block); - - /* The ROM bug deletes extra num_sectors if we don't cross the block boundary - and extra head_sectors if we do */ - if (num_sectors <= head_sectors) { - return ((num_sectors + 1) / 2) * sector_size; - } else { - return (num_sectors - head_sectors) * sector_size; - } - } -} - -esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size, uint32_t block_size) -{ - s_flash_write_size = block_size; - - size_t flash_size = 0; - if (detect_flash_size(&flash_size) == ESP_LOADER_SUCCESS) { - if (image_size > flash_size) { - return ESP_LOADER_ERROR_IMAGE_SIZE; - } - loader_port_start_timer(DEFAULT_TIMEOUT); - RETURN_ON_ERROR( loader_spi_parameters(flash_size) ); - } else { - loader_port_debug_print("Flash size detection failed, falling back to default"); - } - - init_md5(offset, image_size); - - bool encryption_in_cmd = encryption_in_begin_flash_cmd(s_target); - const uint32_t erase_size = calc_erase_size(esp_loader_get_target(), offset, image_size); - const uint32_t blocks_to_write = (image_size + block_size - 1) / block_size; - - const uint32_t erase_region_timeout_per_mb = 10000; - loader_port_start_timer(timeout_per_mb(erase_size, erase_region_timeout_per_mb)); - return loader_flash_begin_cmd(offset, erase_size, block_size, blocks_to_write, encryption_in_cmd); -} - - -esp_loader_error_t esp_loader_flash_write(void *payload, uint32_t size) -{ - uint32_t padding_bytes = s_flash_write_size - size; - uint8_t *data = (uint8_t *)payload; - uint32_t padding_index = size; - - if (size > s_flash_write_size) { - return ESP_LOADER_ERROR_INVALID_PARAM; - } - - const uint8_t padding_pattern = 0xFF; - while (padding_bytes--) { - data[padding_index++] = padding_pattern; - } - - md5_update(payload, (size + 3) & ~3); - - loader_port_start_timer(DEFAULT_TIMEOUT); - - return loader_flash_data_cmd(data, s_flash_write_size); -} - - -esp_loader_error_t esp_loader_flash_finish(bool reboot) -{ - loader_port_start_timer(DEFAULT_TIMEOUT); - - return loader_flash_end_cmd(!reboot); -} -#endif /* SERIAL_FLASHER_INTERFACE_UART */ - -esp_loader_error_t esp_loader_mem_start(uint32_t offset, uint32_t size, uint32_t block_size) -{ - uint32_t blocks_to_write = ROUNDUP(size, block_size); - loader_port_start_timer(timeout_per_mb(size, LOAD_RAM_TIMEOUT_PER_MB)); - return loader_mem_begin_cmd(offset, size, blocks_to_write, block_size); -} - - -esp_loader_error_t esp_loader_mem_write(const void *payload, uint32_t size) -{ - const uint8_t *data = (const uint8_t *)payload; - loader_port_start_timer(timeout_per_mb(size, LOAD_RAM_TIMEOUT_PER_MB)); - return loader_mem_data_cmd(data, size); -} - - -esp_loader_error_t esp_loader_mem_finish(uint32_t entrypoint) -{ - loader_port_start_timer(DEFAULT_TIMEOUT); - return loader_mem_end_cmd(entrypoint); -} - - -esp_loader_error_t esp_loader_read_register(uint32_t address, uint32_t *reg_value) -{ - loader_port_start_timer(DEFAULT_TIMEOUT); - - return loader_read_reg_cmd(address, reg_value); -} - - -esp_loader_error_t esp_loader_write_register(uint32_t address, uint32_t reg_value) -{ - loader_port_start_timer(DEFAULT_TIMEOUT); - - return loader_write_reg_cmd(address, reg_value, 0xFFFFFFFF, 0); -} - -esp_loader_error_t esp_loader_change_transmission_rate(uint32_t transmission_rate) -{ - if (s_target == ESP8266_CHIP) { - return ESP_LOADER_ERROR_UNSUPPORTED_FUNC; - } - - loader_port_start_timer(DEFAULT_TIMEOUT); - - return loader_change_baudrate_cmd(transmission_rate); -} - -#if MD5_ENABLED - -static void hexify(const uint8_t raw_md5[16], uint8_t hex_md5_out[32]) -{ - static const uint8_t dec_to_hex[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - for (int i = 0; i < 16; i++) { - *hex_md5_out++ = dec_to_hex[raw_md5[i] >> 4]; - *hex_md5_out++ = dec_to_hex[raw_md5[i] & 0xF]; - } -} - - -esp_loader_error_t esp_loader_flash_verify(void) -{ - if (s_target == ESP8266_CHIP) { - return ESP_LOADER_ERROR_UNSUPPORTED_FUNC; - } - - uint8_t raw_md5[16] = {0}; - - /* Zero termination and new line character require 2 bytes */ - uint8_t hex_md5[MD5_SIZE + 2] = {0}; - uint8_t received_md5[MD5_SIZE + 2] = {0}; - - md5_final(raw_md5); - hexify(raw_md5, hex_md5); - - loader_port_start_timer(timeout_per_mb(s_image_size, MD5_TIMEOUT_PER_MB)); - - RETURN_ON_ERROR( loader_md5_cmd(s_start_address, s_image_size, received_md5) ); - - bool md5_match = memcmp(hex_md5, received_md5, MD5_SIZE) == 0; - - if (!md5_match) { - hex_md5[MD5_SIZE] = '\n'; - received_md5[MD5_SIZE] = '\n'; - - loader_port_debug_print("Error: MD5 checksum does not match:\n"); - loader_port_debug_print("Expected:\n"); - loader_port_debug_print((char *)received_md5); - loader_port_debug_print("Actual:\n"); - loader_port_debug_print((char *)hex_md5); - - return ESP_LOADER_ERROR_INVALID_MD5; - } - - return ESP_LOADER_SUCCESS; -} - -#endif - -void esp_loader_reset_target(void) -{ - loader_port_reset_target(); -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/esp_targets.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/esp_targets.c deleted file mode 100644 index 8764d2369..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/esp_targets.c +++ /dev/null @@ -1,263 +0,0 @@ -/* Copyright 2020 Espressif Systems (Shanghai) PTE LTD - * - * 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. - */ - -#include "esp_targets.h" -#include - -#define MAX_MAGIC_VALUES 2 - -typedef esp_loader_error_t (*read_spi_config_t)(uint32_t efuse_base, uint32_t *spi_config); - -typedef struct { - target_registers_t regs; - uint32_t efuse_base; - uint32_t chip_magic_value[MAX_MAGIC_VALUES]; - read_spi_config_t read_spi_config; - bool encryption_in_begin_flash_cmd; -} esp_target_t; - -// This ROM address has a different value on each chip model -#define CHIP_DETECT_MAGIC_REG_ADDR 0x40001000 - -#define ESP8266_SPI_REG_BASE 0x60000200 -#define ESP32S2_SPI_REG_BASE 0x3f402000 -#define ESP32xx_SPI_REG_BASE 0x60002000 -#define ESP32_SPI_REG_BASE 0x3ff42000 - -static esp_loader_error_t spi_config_esp32(uint32_t efuse_base, uint32_t *spi_config); -static esp_loader_error_t spi_config_esp32xx(uint32_t efuse_base, uint32_t *spi_config); - -static const esp_target_t esp_target[ESP_MAX_CHIP] = { - - // ESP8266 - { - .regs = { - .cmd = ESP8266_SPI_REG_BASE + 0x00, - .usr = ESP8266_SPI_REG_BASE + 0x1c, - .usr1 = ESP8266_SPI_REG_BASE + 0x20, - .usr2 = ESP8266_SPI_REG_BASE + 0x24, - .w0 = ESP8266_SPI_REG_BASE + 0x40, - .mosi_dlen = 0, - .miso_dlen = 0, - }, - .efuse_base = 0, // Not used - .chip_magic_value = { 0xfff0c101, 0 }, - .read_spi_config = NULL, // Not used - }, - - // ESP32 - { - .regs = { - .cmd = ESP32_SPI_REG_BASE + 0x00, - .usr = ESP32_SPI_REG_BASE + 0x1c, - .usr1 = ESP32_SPI_REG_BASE + 0x20, - .usr2 = ESP32_SPI_REG_BASE + 0x24, - .w0 = ESP32_SPI_REG_BASE + 0x80, - .mosi_dlen = ESP32_SPI_REG_BASE + 0x28, - .miso_dlen = ESP32_SPI_REG_BASE + 0x2c, - }, - .efuse_base = 0x3ff5A000, - .chip_magic_value = { 0x00f01d83, 0 }, - .read_spi_config = spi_config_esp32, - }, - - // ESP32S2 - { - .regs = { - .cmd = ESP32S2_SPI_REG_BASE + 0x00, - .usr = ESP32S2_SPI_REG_BASE + 0x18, - .usr1 = ESP32S2_SPI_REG_BASE + 0x1c, - .usr2 = ESP32S2_SPI_REG_BASE + 0x20, - .w0 = ESP32S2_SPI_REG_BASE + 0x58, - .mosi_dlen = ESP32S2_SPI_REG_BASE + 0x24, - .miso_dlen = ESP32S2_SPI_REG_BASE + 0x28, - }, - .efuse_base = 0x3f41A000, - .chip_magic_value = { 0x000007c6, 0 }, - .read_spi_config = spi_config_esp32xx, - }, - - // ESP32C3 - { - .regs = { - .cmd = ESP32xx_SPI_REG_BASE + 0x00, - .usr = ESP32xx_SPI_REG_BASE + 0x18, - .usr1 = ESP32xx_SPI_REG_BASE + 0x1c, - .usr2 = ESP32xx_SPI_REG_BASE + 0x20, - .w0 = ESP32xx_SPI_REG_BASE + 0x58, - .mosi_dlen = ESP32xx_SPI_REG_BASE + 0x24, - .miso_dlen = ESP32xx_SPI_REG_BASE + 0x28, - }, - .efuse_base = 0x60008800, - .chip_magic_value = { 0x6921506f, 0x1b31506f }, - .read_spi_config = spi_config_esp32xx, - }, - - // ESP32S3 - { - .regs = { - .cmd = ESP32xx_SPI_REG_BASE + 0x00, - .usr = ESP32xx_SPI_REG_BASE + 0x18, - .usr1 = ESP32xx_SPI_REG_BASE + 0x1c, - .usr2 = ESP32xx_SPI_REG_BASE + 0x20, - .w0 = ESP32xx_SPI_REG_BASE + 0x58, - .mosi_dlen = ESP32xx_SPI_REG_BASE + 0x24, - .miso_dlen = ESP32xx_SPI_REG_BASE + 0x28, - }, - .efuse_base = 0x60007000, - .chip_magic_value = { 0x00000009, 0 }, - .read_spi_config = spi_config_esp32xx, - }, - - // ESP32C2 - { - .regs = { - .cmd = ESP32xx_SPI_REG_BASE + 0x00, - .usr = ESP32xx_SPI_REG_BASE + 0x18, - .usr1 = ESP32xx_SPI_REG_BASE + 0x1c, - .usr2 = ESP32xx_SPI_REG_BASE + 0x20, - .w0 = ESP32xx_SPI_REG_BASE + 0x58, - .mosi_dlen = ESP32xx_SPI_REG_BASE + 0x24, - .miso_dlen = ESP32xx_SPI_REG_BASE + 0x28, - }, - .efuse_base = 0x60008800, - .chip_magic_value = { 0x6f51306f, 0x7c41a06f }, - .read_spi_config = spi_config_esp32xx, - }, - // ESP32H4 - { - .regs = { - .cmd = ESP32xx_SPI_REG_BASE + 0x00, - .usr = ESP32xx_SPI_REG_BASE + 0x18, - .usr1 = ESP32xx_SPI_REG_BASE + 0x1c, - .usr2 = ESP32xx_SPI_REG_BASE + 0x20, - .w0 = ESP32xx_SPI_REG_BASE + 0x58, - .mosi_dlen = ESP32xx_SPI_REG_BASE + 0x24, - .miso_dlen = ESP32xx_SPI_REG_BASE + 0x28, - }, - .efuse_base = 0x6001A000, - .chip_magic_value = {0xca26cc22, 0x6881b06f}, // ESP32H4-BETA1, ESP32H4-BETA2 - .read_spi_config = spi_config_esp32xx, - }, - // ESP32H2 - { - .regs = { - .cmd = ESP32xx_SPI_REG_BASE + 0x00, - .usr = ESP32xx_SPI_REG_BASE + 0x18, - .usr1 = ESP32xx_SPI_REG_BASE + 0x1c, - .usr2 = ESP32xx_SPI_REG_BASE + 0x20, - .w0 = ESP32xx_SPI_REG_BASE + 0x58, - .mosi_dlen = ESP32xx_SPI_REG_BASE + 0x24, - .miso_dlen = ESP32xx_SPI_REG_BASE + 0x28, - }, - .efuse_base = 0x6001A000, - .chip_magic_value = {0xd7b73e80, 0}, - .read_spi_config = spi_config_esp32xx, - }, -}; - -const target_registers_t *get_esp_target_data(target_chip_t chip) -{ - return (const target_registers_t *)&esp_target[chip]; -} - -esp_loader_error_t loader_detect_chip(target_chip_t *target_chip, const target_registers_t **target_data) -{ - uint32_t magic_value; - RETURN_ON_ERROR( esp_loader_read_register(CHIP_DETECT_MAGIC_REG_ADDR, &magic_value) ); - - for (int chip = 0; chip < ESP_MAX_CHIP; chip++) { - for(int index = 0; index < MAX_MAGIC_VALUES; index++) { - if (magic_value == esp_target[chip].chip_magic_value[index]) { - *target_chip = (target_chip_t)chip; - *target_data = (target_registers_t *)&esp_target[chip]; - return ESP_LOADER_SUCCESS; - } - } - } - - return ESP_LOADER_ERROR_INVALID_TARGET; -} - -esp_loader_error_t loader_read_spi_config(target_chip_t target_chip, uint32_t *spi_config) -{ - const esp_target_t *target = &esp_target[target_chip]; - return target->read_spi_config(target->efuse_base, spi_config); -} - -static inline uint32_t efuse_word_addr(uint32_t efuse_base, uint32_t n) -{ - return efuse_base + (n * 4); -} - -// 30->GPIO32 | 31->GPIO33 -static inline uint8_t adjust_pin_number(uint8_t num) -{ - return (num >= 30) ? num + 2 : num; -} - - -static esp_loader_error_t spi_config_esp32(uint32_t efuse_base, uint32_t *spi_config) -{ - *spi_config = 0; - - uint32_t reg5, reg3; - RETURN_ON_ERROR( esp_loader_read_register(efuse_word_addr(efuse_base, 5), ®5) ); - RETURN_ON_ERROR( esp_loader_read_register(efuse_word_addr(efuse_base, 3), ®3) ); - - uint32_t pins = reg5 & 0xfffff; - - if (pins == 0 || pins == 0xfffff) { - return ESP_LOADER_SUCCESS; - } - - uint8_t clk = adjust_pin_number( (pins >> 0) & 0x1f ); - uint8_t q = adjust_pin_number( (pins >> 5) & 0x1f ); - uint8_t d = adjust_pin_number( (pins >> 10) & 0x1f ); - uint8_t cs = adjust_pin_number( (pins >> 15) & 0x1f ); - uint8_t hd = adjust_pin_number( (reg3 >> 4) & 0x1f ); - - if (clk == cs || clk == d || clk == q || q == cs || q == d || q == d) { - return ESP_LOADER_SUCCESS; - } - - *spi_config = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk; - - return ESP_LOADER_SUCCESS; -} - -// Applies for esp32s2, esp32c3 and esp32c3 -static esp_loader_error_t spi_config_esp32xx(uint32_t efuse_base, uint32_t *spi_config) -{ - *spi_config = 0; - - uint32_t reg1, reg2; - RETURN_ON_ERROR( esp_loader_read_register(efuse_word_addr(efuse_base, 18), ®1) ); - RETURN_ON_ERROR( esp_loader_read_register(efuse_word_addr(efuse_base, 19), ®2) ); - - uint32_t pins = ((reg1 >> 16) | ((reg2 & 0xfffff) << 16)) & 0x3fffffff; - - if (pins == 0 || pins == 0xffffffff) { - return ESP_LOADER_SUCCESS; - } - - *spi_config = pins; - return ESP_LOADER_SUCCESS; -} - -bool encryption_in_begin_flash_cmd(target_chip_t target) -{ - return target == ESP32_CHIP || target == ESP8266_CHIP; -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/md5_hash.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/md5_hash.c deleted file mode 100644 index 4da76bcb5..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/md5_hash.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - - -#include "md5_hash.h" -#include -#include - - -static void MD5Transform(uint32_t buf[4], uint32_t const in[16]); - - -/* ===== start - public domain MD5 implementation ===== */ -/* - * This code implements the MD5 message-digest algorithm. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -#ifndef WORDS_BIGENDIAN -#define byteReverse(buf, len) /* Nothing */ -#else -/* - * Note: this code is harmless on little-endian machines. - */ -static void byteReverse(unsigned char *buf, unsigned longs) -{ - uint32_t t; - do { - t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - *(uint32_t *) buf = t; - buf += 4; - } while (--longs); -} -#endif - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void MD5Init(struct MD5Context *ctx) -{ - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) -{ - uint32_t t; - - /* Update bitcount */ - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) { - ctx->bits[1]++; /* Carry from low to high */ - } - ctx->bits[1] += len >> 29; - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memcpy(p, buf, len); - return; - } - memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform((uint32_t *)ctx->buf, (uint32_t *) ctx->in); - buf += t; - len -= t; - } - /* Process data in 64-byte chunks */ - - while (len >= 64) { - memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform((uint32_t *)ctx->buf, (uint32_t *) ctx->in); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - - memcpy(ctx->in, buf, len); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -void MD5Final(unsigned char digest[16], struct MD5Context *ctx) -{ - unsigned count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform((uint32_t *)ctx->buf, (uint32_t *) ctx->in); - - /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - ((uint32_t *) ctx->in)[14] = ctx->bits[0]; - ((uint32_t *) ctx->in)[15] = ctx->bits[1]; - - MD5Transform((uint32_t *)ctx->buf, (uint32_t *) ctx->in); - byteReverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(struct MD5Context)); /* In case it's sensitive */ -} - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) -{ - register uint32_t a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} -/* ===== end - public domain MD5 implementation ===== */ diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_common.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_common.c deleted file mode 100644 index 8d1d9dd53..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_common.c +++ /dev/null @@ -1,301 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include "protocol.h" -#include "protocol_prv.h" -#include "esp_loader_io.h" -#include -#include - -#define CMD_SIZE(cmd) ( sizeof(cmd) - sizeof(command_common_t) ) - -static uint32_t s_sequence_number = 0; - -static uint8_t compute_checksum(const uint8_t *data, uint32_t size) -{ - uint8_t checksum = 0xEF; - - while (size--) { - checksum ^= *data++; - } - - return checksum; -} - -void log_loader_internal_error(error_code_t error) -{ - loader_port_debug_print("Error: "); - - switch (error) { - case INVALID_CRC: loader_port_debug_print("INVALID_CRC"); break; - case INVALID_COMMAND: loader_port_debug_print("INVALID_COMMAND"); break; - case COMMAND_FAILED: loader_port_debug_print("COMMAND_FAILED"); break; - case FLASH_WRITE_ERR: loader_port_debug_print("FLASH_WRITE_ERR"); break; - case FLASH_READ_ERR: loader_port_debug_print("FLASH_READ_ERR"); break; - case READ_LENGTH_ERR: loader_port_debug_print("READ_LENGTH_ERR"); break; - case DEFLATE_ERROR: loader_port_debug_print("DEFLATE_ERROR"); break; - default: loader_port_debug_print("UNKNOWN ERROR"); break; - } - - loader_port_debug_print("\n"); -} - - -esp_loader_error_t loader_flash_begin_cmd(uint32_t offset, - uint32_t erase_size, - uint32_t block_size, - uint32_t blocks_to_write, - bool encryption) -{ - uint32_t encryption_size = encryption ? sizeof(uint32_t) : 0; - - flash_begin_command_t flash_begin_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = FLASH_BEGIN, - .size = CMD_SIZE(flash_begin_cmd) - encryption_size, - .checksum = 0 - }, - .erase_size = erase_size, - .packet_count = blocks_to_write, - .packet_size = block_size, - .offset = offset, - .encrypted = 0 - }; - - s_sequence_number = 0; - - return send_cmd(&flash_begin_cmd, sizeof(flash_begin_cmd) - encryption_size, NULL); -} - - -esp_loader_error_t loader_flash_data_cmd(const uint8_t *data, uint32_t size) -{ - data_command_t data_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = FLASH_DATA, - .size = CMD_SIZE(data_cmd) + size, - .checksum = compute_checksum(data, size) - }, - .data_size = size, - .sequence_number = s_sequence_number++, - }; - - return send_cmd_with_data(&data_cmd, sizeof(data_cmd), data, size); -} - - -esp_loader_error_t loader_flash_end_cmd(bool stay_in_loader) -{ - flash_end_command_t end_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = FLASH_END, - .size = CMD_SIZE(end_cmd), - .checksum = 0 - }, - .stay_in_loader = stay_in_loader - }; - - return send_cmd(&end_cmd, sizeof(end_cmd), NULL); -} - - -esp_loader_error_t loader_mem_begin_cmd(uint32_t offset, uint32_t size, uint32_t blocks_to_write, uint32_t block_size) -{ - - mem_begin_command_t mem_begin_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = MEM_BEGIN, - .size = CMD_SIZE(mem_begin_cmd), - .checksum = 0 - }, - .total_size = size, - .blocks = blocks_to_write, - .block_size = block_size, - .offset = offset - }; - - s_sequence_number = 0; - - return send_cmd(&mem_begin_cmd, sizeof(mem_begin_cmd), NULL); -} - - -esp_loader_error_t loader_mem_data_cmd(const uint8_t *data, uint32_t size) -{ - data_command_t data_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = MEM_DATA, - .size = CMD_SIZE(data_cmd) + size, - .checksum = compute_checksum(data, size) - }, - .data_size = size, - .sequence_number = s_sequence_number++, - }; - return send_cmd_with_data(&data_cmd, sizeof(data_cmd), data, size); -} - -esp_loader_error_t loader_mem_end_cmd(uint32_t entrypoint) -{ - mem_end_command_t end_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = MEM_END, - .size = CMD_SIZE(end_cmd), - }, - .stay_in_loader = (entrypoint == 0), - .entry_point_address = entrypoint - }; - - return send_cmd(&end_cmd, sizeof(end_cmd), NULL); -} - - -esp_loader_error_t loader_sync_cmd(void) -{ - sync_command_t sync_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = SYNC, - .size = CMD_SIZE(sync_cmd), - .checksum = 0 - }, - .sync_sequence = { - 0x07, 0x07, 0x12, 0x20, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, - } - }; - - return send_cmd(&sync_cmd, sizeof(sync_cmd), NULL); -} - - -esp_loader_error_t loader_write_reg_cmd(uint32_t address, uint32_t value, - uint32_t mask, uint32_t delay_us) -{ - write_reg_command_t write_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = WRITE_REG, - .size = CMD_SIZE(write_cmd), - .checksum = 0 - }, - .address = address, - .value = value, - .mask = mask, - .delay_us = delay_us - }; - - return send_cmd(&write_cmd, sizeof(write_cmd), NULL); -} - - -esp_loader_error_t loader_read_reg_cmd(uint32_t address, uint32_t *reg) -{ - read_reg_command_t read_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = READ_REG, - .size = CMD_SIZE(read_cmd), - .checksum = 0 - }, - .address = address, - }; - - return send_cmd(&read_cmd, sizeof(read_cmd), reg); -} - - -esp_loader_error_t loader_spi_attach_cmd(uint32_t config) -{ - spi_attach_command_t attach_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = SPI_ATTACH, - .size = CMD_SIZE(attach_cmd), - .checksum = 0 - }, - .configuration = config, - .zero = 0 - }; - - return send_cmd(&attach_cmd, sizeof(attach_cmd), NULL); -} - -esp_loader_error_t loader_change_baudrate_cmd(uint32_t baudrate) -{ - change_baudrate_command_t baudrate_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = CHANGE_BAUDRATE, - .size = CMD_SIZE(baudrate_cmd), - .checksum = 0 - }, - .new_baudrate = baudrate, - .old_baudrate = 0 // ESP32 ROM only - }; - - return send_cmd(&baudrate_cmd, sizeof(baudrate_cmd), NULL); -} - -esp_loader_error_t loader_md5_cmd(uint32_t address, uint32_t size, uint8_t *md5_out) -{ - spi_flash_md5_command_t md5_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = SPI_FLASH_MD5, - .size = CMD_SIZE(md5_cmd), - .checksum = 0 - }, - .address = address, - .size = size, - .reserved_0 = 0, - .reserved_1 = 0 - }; - - return send_cmd_md5(&md5_cmd, sizeof(md5_cmd), md5_out); -} - -esp_loader_error_t loader_spi_parameters(uint32_t total_size) -{ - write_spi_command_t spi_cmd = { - .common = { - .direction = WRITE_DIRECTION, - .command = SPI_SET_PARAMS, - .size = 24, - .checksum = 0 - }, - .id = 0, - .total_size = total_size, - .block_size = 64 * 1024, - .sector_size = 4 * 1024, - .page_size = 0x100, - .status_mask = 0xFFFF, - }; - - return send_cmd(&spi_cmd, sizeof(spi_cmd), NULL); -} - -__attribute__ ((weak)) void loader_port_debug_print(const char *str) -{ - (void)(str); -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_spi.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_spi.c deleted file mode 100644 index bd04fe79f..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_spi.c +++ /dev/null @@ -1,312 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include "protocol.h" -#include "protocol_prv.h" -#include "esp_loader_io.h" -#include -#include - -typedef struct __attribute__((packed)) { - uint8_t cmd; - uint8_t addr; - uint8_t dummy; -} transaction_preamble_t; - -typedef enum { - TRANS_CMD_WRBUF = 0x01, - TRANS_CMD_RDBUF = 0x02, - TRANS_CMD_WRDMA = 0x03, - TRANS_CMD_RDDMA = 0x04, - TRANS_CMD_SEG_DONE = 0x05, - TRANS_CMD_ENQPI = 0x06, - TRANS_CMD_WR_DONE = 0x07, - TRANS_CMD_CMD8 = 0x08, - TRANS_CMD_CMD9 = 0x09, - TRANS_CMD_CMDA = 0x0A, - TRANS_CMD_EXQPI = 0xDD, -} transaction_cmd_t; - -/* Slave protocol registers */ -typedef enum { - SLAVE_REGISTER_VER = 0, - SLAVE_REGISTER_RXSTA = 4, - SLAVE_REGISTER_TXSTA = 8, - SLAVE_REGISTER_CMD = 12, -} slave_register_addr_t; - -#define SLAVE_STA_TOGGLE_BIT (0x01U << 0) -#define SLAVE_STA_INIT_BIT (0x01U << 1) -#define SLAVE_STA_BUF_LENGTH_POS 2U - -typedef enum { - SLAVE_STATE_INIT = SLAVE_STA_TOGGLE_BIT | SLAVE_STA_INIT_BIT, - SLAVE_STATE_FIRST_PACKET = SLAVE_STA_INIT_BIT, -} slave_state_t; - -typedef enum { - /* Target to host */ - SLAVE_CMD_IDLE = 0xAA, - SLAVE_CMD_READY = 0xA5, - /* Host to target */ - SLAVE_CMD_REBOOT = 0xFE, - SLAVE_CMD_COMM_REINIT = 0x5A, - SLAVE_CMD_DONE = 0x55, -} slave_cmd_t; - -static uint8_t s_slave_seq_tx; -static uint8_t s_slave_seq_rx; - -static esp_loader_error_t write_slave_reg(const uint8_t *data, const uint32_t addr, - const uint8_t size); -static esp_loader_error_t read_slave_reg(uint8_t *out_data, const uint32_t addr, - const uint8_t size); -static esp_loader_error_t handle_slave_state(const uint32_t status_reg_addr, uint8_t *seq_state, - bool *slave_ready, uint32_t *buf_size); -static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value); - -esp_loader_error_t loader_initialize_conn(esp_loader_connect_args_t *connect_args) -{ - for (uint8_t trial = 0; trial < connect_args->trials; trial++) { - uint8_t slave_ready_flag; - RETURN_ON_ERROR(read_slave_reg(&slave_ready_flag, SLAVE_REGISTER_CMD, - sizeof(slave_ready_flag))); - - if (slave_ready_flag != SLAVE_CMD_IDLE) { - loader_port_debug_print("Waiting for Slave to be idle...\n"); - loader_port_delay_ms(100); - } else { - break; - } - } - - const uint8_t reg_val = SLAVE_CMD_READY; - RETURN_ON_ERROR(write_slave_reg(®_val, SLAVE_REGISTER_CMD, sizeof(reg_val))); - - for (uint8_t trial = 0; trial < connect_args->trials; trial++) { - uint8_t slave_ready_flag; - RETURN_ON_ERROR(read_slave_reg(&slave_ready_flag, SLAVE_REGISTER_CMD, - sizeof(slave_ready_flag))); - - if (slave_ready_flag != SLAVE_CMD_READY) { - loader_port_debug_print("Waiting for Slave to be ready...\n"); - loader_port_delay_ms(100); - } else { - break; - } - } - - return ESP_LOADER_SUCCESS; -} - - -esp_loader_error_t send_cmd(const void *cmd_data, uint32_t size, uint32_t *reg_value) -{ - command_t command = ((const command_common_t *)cmd_data)->command; - - uint32_t buf_size; - bool slave_ready = false; - while (!slave_ready) { - RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_RXSTA, &s_slave_seq_rx, &slave_ready, - &buf_size)); - } - - if (size > buf_size) { - return ESP_LOADER_ERROR_INVALID_PARAM; - } - - /* Start and write the command */ - transaction_preamble_t preamble = {.cmd = TRANS_CMD_WRDMA}; - - loader_port_spi_set_cs(0); - RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble), - loader_port_remaining_time())); - RETURN_ON_ERROR(loader_port_write((const uint8_t *)cmd_data, size, - loader_port_remaining_time())); - loader_port_spi_set_cs(1); - - /* Terminate the write */ - loader_port_spi_set_cs(0); - preamble.cmd = TRANS_CMD_WR_DONE; - RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble), - loader_port_remaining_time())); - loader_port_spi_set_cs(1); - - return check_response(command, reg_value); -} - - -esp_loader_error_t send_cmd_with_data(const void *cmd_data, size_t cmd_size, - const void *data, size_t data_size) -{ - uint32_t buf_size; - bool slave_ready = false; - while (!slave_ready) { - RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_RXSTA, &s_slave_seq_rx, &slave_ready, - &buf_size)); - } - - if (cmd_size + data_size > buf_size) { - return ESP_LOADER_ERROR_INVALID_PARAM; - } - - /* Start and write the command and the data */ - transaction_preamble_t preamble = {.cmd = TRANS_CMD_WRDMA}; - - loader_port_spi_set_cs(0); - RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble), - loader_port_remaining_time())); - RETURN_ON_ERROR(loader_port_write((const uint8_t *)cmd_data, cmd_size, - loader_port_remaining_time())); - RETURN_ON_ERROR(loader_port_write((const uint8_t *)data, data_size, - loader_port_remaining_time())); - loader_port_spi_set_cs(1); - - /* Terminate the write */ - loader_port_spi_set_cs(0); - preamble.cmd = TRANS_CMD_WR_DONE; - RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble), - loader_port_remaining_time())); - loader_port_spi_set_cs(1); - - command_t command = ((const command_common_t *)cmd_data)->command; - return check_response(command, NULL); -} - - -static esp_loader_error_t read_slave_reg(uint8_t *out_data, const uint32_t addr, - const uint8_t size) -{ - transaction_preamble_t preamble = { - .cmd = TRANS_CMD_RDBUF, - .addr = addr, - }; - - loader_port_spi_set_cs(0); - RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble), - loader_port_remaining_time())); - RETURN_ON_ERROR(loader_port_read(out_data, size, loader_port_remaining_time())); - loader_port_spi_set_cs(1); - - return ESP_LOADER_SUCCESS; -} - - -static esp_loader_error_t write_slave_reg(const uint8_t *data, const uint32_t addr, - const uint8_t size) -{ - transaction_preamble_t preamble = { - .cmd = TRANS_CMD_WRBUF, - .addr = addr, - }; - - loader_port_spi_set_cs(0); - RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble), - loader_port_remaining_time())); - RETURN_ON_ERROR(loader_port_write(data, size, loader_port_remaining_time())); - loader_port_spi_set_cs(1); - - return ESP_LOADER_SUCCESS; -} - - -static esp_loader_error_t handle_slave_state(const uint32_t status_reg_addr, uint8_t *seq_state, - bool *slave_ready, uint32_t *buf_size) -{ - uint32_t status_reg; - RETURN_ON_ERROR(read_slave_reg((uint8_t *)&status_reg, status_reg_addr, - sizeof(status_reg))); - const slave_state_t state = status_reg & (SLAVE_STA_TOGGLE_BIT | SLAVE_STA_INIT_BIT); - - switch(state) { - case SLAVE_STATE_INIT: { - const uint32_t initial = 0U; - RETURN_ON_ERROR(write_slave_reg((uint8_t *)&initial, status_reg_addr, sizeof(initial))); - break; - } - - case SLAVE_STATE_FIRST_PACKET: { - *seq_state = state & SLAVE_STA_TOGGLE_BIT; - *buf_size = status_reg >> SLAVE_STA_BUF_LENGTH_POS; - *slave_ready = true; - break; - } - - default: { - const uint8_t new_seq = state & SLAVE_STA_TOGGLE_BIT; - if (new_seq != *seq_state) { - *seq_state = new_seq; - *buf_size = status_reg >> SLAVE_STA_BUF_LENGTH_POS; - *slave_ready = true; - } - break; - } - } - - return ESP_LOADER_SUCCESS; -} - - -static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value) -{ - response_t resp; - - uint32_t buf_size; - bool slave_ready = false; - while (!slave_ready) { - RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_TXSTA, &s_slave_seq_tx, &slave_ready, - &buf_size)); - } - - if (sizeof(resp) > buf_size) { - return ESP_LOADER_ERROR_INVALID_PARAM; - } - - transaction_preamble_t preamble = { - .cmd = TRANS_CMD_RDDMA, - }; - - loader_port_spi_set_cs(0); - RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble), - loader_port_remaining_time())); - RETURN_ON_ERROR(loader_port_read((uint8_t *)&resp, sizeof(resp), - loader_port_remaining_time())); - loader_port_spi_set_cs(1); - - /* Terminate the read */ - loader_port_spi_set_cs(0); - preamble.cmd = TRANS_CMD_CMD8; - RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble), - loader_port_remaining_time())); - loader_port_spi_set_cs(1); - - common_response_t *common = (common_response_t *)&resp; - if ((common->direction != READ_DIRECTION) || (common->command != cmd)) { - return ESP_LOADER_ERROR_INVALID_RESPONSE; - } - - response_status_t *status = - (response_status_t *)((uint8_t *)&resp + sizeof(resp) - sizeof(response_status_t)); - if (status->failed) { - log_loader_internal_error(status->error); - return ESP_LOADER_ERROR_INVALID_RESPONSE; - } - - if (reg_value != NULL) { - *reg_value = common->value; - } - - return ESP_LOADER_SUCCESS; -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_uart.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_uart.c deleted file mode 100644 index c5a51cec6..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/protocol_uart.c +++ /dev/null @@ -1,114 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include "protocol.h" -#include "protocol_prv.h" -#include "esp_loader_io.h" -#include "slip.h" -#include -#include - -static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value, void* resp, uint32_t resp_size); - -esp_loader_error_t loader_initialize_conn(esp_loader_connect_args_t *connect_args) { - esp_loader_error_t err; - int32_t trials = connect_args->trials; - - do { - loader_port_start_timer(connect_args->sync_timeout); - err = loader_sync_cmd(); - if (err == ESP_LOADER_ERROR_TIMEOUT) { - if (--trials == 0) { - return ESP_LOADER_ERROR_TIMEOUT; - } - loader_port_delay_ms(100); - } else if (err != ESP_LOADER_SUCCESS) { - return err; - } - } while (err != ESP_LOADER_SUCCESS); - - return err; -} - -esp_loader_error_t send_cmd(const void *cmd_data, uint32_t size, uint32_t *reg_value) -{ - response_t response; - command_t command = ((const command_common_t *)cmd_data)->command; - - RETURN_ON_ERROR( SLIP_send_delimiter() ); - RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, size) ); - RETURN_ON_ERROR( SLIP_send_delimiter() ); - - return check_response(command, reg_value, &response, sizeof(response)); -} - - -esp_loader_error_t send_cmd_with_data(const void *cmd_data, size_t cmd_size, - const void *data, size_t data_size) -{ - response_t response; - command_t command = ((const command_common_t *)cmd_data)->command; - - RETURN_ON_ERROR( SLIP_send_delimiter() ); - RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, cmd_size) ); - RETURN_ON_ERROR( SLIP_send(data, data_size) ); - RETURN_ON_ERROR( SLIP_send_delimiter() ); - - return check_response(command, NULL, &response, sizeof(response)); -} - - -esp_loader_error_t send_cmd_md5(const void *cmd_data, size_t cmd_size, uint8_t md5_out[MD5_SIZE]) -{ - rom_md5_response_t response; - command_t command = ((const command_common_t *)cmd_data)->command; - - RETURN_ON_ERROR( SLIP_send_delimiter() ); - RETURN_ON_ERROR( SLIP_send((const uint8_t *)cmd_data, cmd_size) ); - RETURN_ON_ERROR( SLIP_send_delimiter() ); - - RETURN_ON_ERROR( check_response(command, NULL, &response, sizeof(response)) ); - - memcpy(md5_out, response.md5, MD5_SIZE); - - return ESP_LOADER_SUCCESS; -} - - -static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value, void* resp, uint32_t resp_size) -{ - esp_loader_error_t err; - common_response_t *response = (common_response_t *)resp; - - do { - err = SLIP_receive_packet(resp, resp_size); - if (err != ESP_LOADER_SUCCESS) { - return err; - } - } while ((response->direction != READ_DIRECTION) || (response->command != cmd)); - - response_status_t *status = (response_status_t *)((uint8_t *)resp + resp_size - sizeof(response_status_t)); - - if (status->failed) { - log_loader_internal_error(status->error); - return ESP_LOADER_ERROR_INVALID_RESPONSE; - } - - if (reg_value != NULL) { - *reg_value = response->value; - } - - return ESP_LOADER_SUCCESS; -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/slip.c b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/slip.c deleted file mode 100644 index ba926169c..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/src/slip.c +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD - * - * 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. - */ - -#include "slip.h" -#include "esp_loader_io.h" - -static const uint8_t DELIMITER = 0xC0; -static const uint8_t C0_REPLACEMENT[2] = {0xDB, 0xDC}; -static const uint8_t DB_REPLACEMENT[2] = {0xDB, 0xDD}; - -static inline esp_loader_error_t peripheral_read(uint8_t *buff, const size_t size) -{ - return loader_port_read(buff, size, loader_port_remaining_time()); -} - -static inline esp_loader_error_t peripheral_write(const uint8_t *buff, const size_t size) -{ - return loader_port_write(buff, size, loader_port_remaining_time()); -} - -esp_loader_error_t SLIP_receive_data(uint8_t *buff, const size_t size) -{ - uint8_t ch; - - for (uint32_t i = 0; i < size; i++) { - RETURN_ON_ERROR( peripheral_read(&ch, 1) ); - - if (ch == 0xDB) { - RETURN_ON_ERROR( peripheral_read(&ch, 1) ); - if (ch == 0xDC) { - buff[i] = 0xC0; - } else if (ch == 0xDD) { - buff[i] = 0xDB; - } else { - return ESP_LOADER_ERROR_INVALID_RESPONSE; - } - } else { - buff[i] = ch; - } - } - - return ESP_LOADER_SUCCESS; -} - - -esp_loader_error_t SLIP_receive_packet(uint8_t *buff, const size_t size) -{ - uint8_t ch; - - // Wait for delimiter - do { - RETURN_ON_ERROR( peripheral_read(&ch, 1) ); - } while (ch != DELIMITER); - - // Workaround: bootloader sends two dummy(0xC0) bytes after response when baud rate is changed. - do { - RETURN_ON_ERROR( peripheral_read(&ch, 1) ); - } while (ch == DELIMITER); - - buff[0] = ch; - - RETURN_ON_ERROR( SLIP_receive_data(&buff[1], size - 1) ); - - // Wait for delimiter - do { - RETURN_ON_ERROR( peripheral_read(&ch, 1) ); - } while (ch != DELIMITER); - - return ESP_LOADER_SUCCESS; -} - - -esp_loader_error_t SLIP_send(const uint8_t *data, const size_t size) -{ - uint32_t to_write = 0; // Bytes ready to write as they are - uint32_t written = 0; // Bytes already written - - for (uint32_t i = 0; i < size; i++) { - if (data[i] != 0xC0 && data[i] != 0xDB) { - to_write++; // Queue this byte for writing - continue; - } - - // We have a byte that needs encoding, write the queue first - if (to_write > 0) { - RETURN_ON_ERROR( peripheral_write(&data[written], to_write) ); - } - - // Write the encoded byte - if (data[i] == 0xC0) { - RETURN_ON_ERROR( peripheral_write(C0_REPLACEMENT, 2) ); - } else { - RETURN_ON_ERROR( peripheral_write(DB_REPLACEMENT, 2) ); - } - - // Update to start again after the encoded byte - written = i + 1; - to_write = 0; - } - - // Write the rest of the bytes that didn't need encoding - if (to_write > 0) { - RETURN_ON_ERROR( peripheral_write(&data[written], to_write) ); - } - - return ESP_LOADER_SUCCESS; -} - - -esp_loader_error_t SLIP_send_delimiter(void) -{ - return peripheral_write(&DELIMITER, 1); -} diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/CMakeLists.txt b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/CMakeLists.txt deleted file mode 100644 index 97da4eaae..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -if (CONFIG_ESP_SERIAL_FLASHER) - zephyr_include_directories( - "${ZEPHYR_CURRENT_MODULE_DIR}/include" - "${ZEPHYR_CURRENT_MODULE_DIR}/port" - "${ZEPHYR_CURRENT_MODULE_DIR}/private_include" - ) - - zephyr_interface_library_named(esp_flasher) - - zephyr_library() - - zephyr_library_sources(${ZEPHYR_CURRENT_MODULE_DIR}/src/esp_loader.c - ${ZEPHYR_CURRENT_MODULE_DIR}/src/esp_targets.c - ${ZEPHYR_CURRENT_MODULE_DIR}/src/protocol_common.c - ${ZEPHYR_CURRENT_MODULE_DIR}/src/protocol_uart.c - ${ZEPHYR_CURRENT_MODULE_DIR}/src/slip.c - ${ZEPHYR_CURRENT_MODULE_DIR}/src/md5_hash.c - ${ZEPHYR_CURRENT_MODULE_DIR}/port/zephyr_port.c - ) - - target_compile_definitions(esp_flasher INTERFACE SERIAL_FLASHER_INTERFACE_UART) - - zephyr_library_link_libraries(esp_flasher) - - if(DEFINED MD5_ENABLED OR CONFIG_SERIAL_FLASHER_MD5_ENABLED) - target_compile_definitions(esp_flasher INTERFACE -DMD5_ENABLED=1) - endif() -endif() diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/Kconfig b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/Kconfig deleted file mode 100644 index ebb524c86..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -config ESP_SERIAL_FLASHER - bool "Enable ESP serial flasher library" - default y - select CONSOLE_SUBSYS - help - Select this option to enable the ESP serial flasher library. - -config ESP_SERIAL_FLASHER_UART_BUFSIZE - int "ESP Serial Flasher UART buffer size" - default 512 - help - Buffer size for UART TX and RX packets - -if ESP_SERIAL_FLASHER - rsource "../Kconfig" -endif diff --git a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/module.yml b/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/module.yml deleted file mode 100644 index 01d484fcb..000000000 --- a/applications/external/wifi_marauder_companion/lib/esp-serial-flasher/zephyr/module.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: esp-flasher - -build: - cmake: zephyr - kconfig: zephyr/Kconfig diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h index 402fca479..d223af79a 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h @@ -13,4 +13,3 @@ ADD_SCENE(wifi_marauder, script_stage_edit, ScriptStageEdit) ADD_SCENE(wifi_marauder, script_stage_add, ScriptStageAdd) ADD_SCENE(wifi_marauder, script_stage_edit_list, ScriptStageEditList) ADD_SCENE(wifi_marauder, sniffpmkid_options, SniffPmkidOptions) -ADD_SCENE(wifi_marauder, flasher, Flasher) diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c index 9e1719d08..59afded1e 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c @@ -1,7 +1,5 @@ #include "../wifi_marauder_app_i.h" -#include "../wifi_marauder_flasher.h" - char* _wifi_marauder_get_prefix_from_cmd(const char* command) { int end = strcspn(command, " "); char* prefix = (char*)malloc(sizeof(char) * (end + 1)); @@ -103,22 +101,14 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput); // Register callbacks to receive data - // setup callback for general log rx thread - if(app->flash_mode) { - wifi_marauder_uart_set_handle_rx_data_cb( - app->uart, - wifi_marauder_flash_handle_rx_data_cb); // setup callback for general log rx thread - } else { - wifi_marauder_uart_set_handle_rx_data_cb( - app->uart, - wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread - } wifi_marauder_uart_set_handle_rx_data_cb( - app->lp_uart, - wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread + app->uart, + wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread - if(app->flash_mode) { - wifi_marauder_flash_start_thread(app); + if(app->ok_to_save_pcaps) { + wifi_marauder_uart_set_handle_rx_data_cb( + app->pcap_uart, + wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread } // Get ready to send command @@ -159,14 +149,21 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { // Send command with newline '\n' if(app->selected_tx_string) { - wifi_marauder_uart_tx( - (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); - wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + if(app->ok_to_save_pcaps) { + wifi_marauder_usart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + wifi_marauder_usart_tx((uint8_t*)("\n"), 1); + } else { + wifi_marauder_xtreme_uart_tx( + (uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string)); + wifi_marauder_xtreme_uart_tx((uint8_t*)("\n"), 1); + } } // Run the script if the file with the script has been opened if(app->script != NULL) { app->script_worker = wifi_marauder_script_worker_alloc(); + app->script_worker->save_pcaps = app->ok_to_save_pcaps; wifi_marauder_script_worker_start(app->script_worker, app->script); } } @@ -182,11 +179,6 @@ bool wifi_marauder_scene_console_output_on_event(void* context, SceneManagerEven consumed = true; } else if(event.type == SceneManagerEventTypeTick) { consumed = true; - } else { - if(app->flash_worker_busy) { - // ignore button presses while flashing - consumed = true; - } } return consumed; @@ -197,17 +189,20 @@ void wifi_marauder_scene_console_output_on_exit(void* context) { // Automatically stop the scan when exiting view if(app->is_command) { - wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); - furi_delay_ms(50); - } + if(app->ok_to_save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); + } - if(app->flash_mode) { - wifi_marauder_flash_stop_thread(app); + furi_delay_ms(50); } // Unregister rx callback wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL); - wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); + if(app->ok_to_save_pcaps) { + wifi_marauder_uart_set_handle_rx_data_cb(app->pcap_uart, NULL); + } wifi_marauder_script_worker_free(app->script_worker); app->script_worker = NULL; diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_flasher.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_flasher.c deleted file mode 100644 index 2d6b8ea50..000000000 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_flasher.c +++ /dev/null @@ -1,266 +0,0 @@ -#include "../wifi_marauder_app_i.h" -#include "../wifi_marauder_flasher.h" - -enum SubmenuIndex { - SubmenuIndexS3Mode, - SubmenuIndexBoot, - SubmenuIndexPart, - SubmenuIndexNvs, - SubmenuIndexBootApp0, - SubmenuIndexApp, - SubmenuIndexCustom, - SubmenuIndexFlash, -}; - -static void wifi_marauder_scene_flasher_callback(void* context, uint32_t index) { - WifiMarauderApp* app = context; - - scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneFlasher, index); - - // browse for files - FuriString* predefined_filepath = furi_string_alloc_set_str(MARAUDER_APP_FOLDER); - FuriString* selected_filepath = furi_string_alloc(); - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, ".bin", &I_Text_10x10); - - // TODO refactor - switch(index) { - case SubmenuIndexS3Mode: - // toggle S3 mode - app->selected_flash_options[SelectedFlashS3Mode] = - !app->selected_flash_options[SelectedFlashS3Mode]; - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshSubmenu); - break; - case SubmenuIndexBoot: - app->selected_flash_options[SelectedFlashBoot] = - !app->selected_flash_options[SelectedFlashBoot]; - if(app->selected_flash_options[SelectedFlashBoot]) { - if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { - strncpy( - app->bin_file_path_boot, - furi_string_get_cstr(selected_filepath), - sizeof(app->bin_file_path_boot)); - } - } - if(app->bin_file_path_boot[0] == '\0') { - // if user didn't select a file, leave unselected - app->selected_flash_options[SelectedFlashBoot] = false; - } - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshSubmenu); - break; - case SubmenuIndexPart: - app->selected_flash_options[SelectedFlashPart] = - !app->selected_flash_options[SelectedFlashPart]; - if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { - strncpy( - app->bin_file_path_part, - furi_string_get_cstr(selected_filepath), - sizeof(app->bin_file_path_part)); - } - if(app->bin_file_path_part[0] == '\0') { - // if user didn't select a file, leave unselected - app->selected_flash_options[SelectedFlashPart] = false; - } - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshSubmenu); - break; - case SubmenuIndexNvs: - app->selected_flash_options[SelectedFlashNvs] = - !app->selected_flash_options[SelectedFlashNvs]; - if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { - strncpy( - app->bin_file_path_nvs, - furi_string_get_cstr(selected_filepath), - sizeof(app->bin_file_path_nvs)); - } - if(app->bin_file_path_nvs[0] == '\0') { - // if user didn't select a file, leave unselected - app->selected_flash_options[SelectedFlashNvs] = false; - } - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshSubmenu); - break; - case SubmenuIndexBootApp0: - app->selected_flash_options[SelectedFlashBootApp0] = - !app->selected_flash_options[SelectedFlashBootApp0]; - if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { - strncpy( - app->bin_file_path_boot_app0, - furi_string_get_cstr(selected_filepath), - sizeof(app->bin_file_path_boot_app0)); - } - if(app->bin_file_path_boot_app0[0] == '\0') { - // if user didn't select a file, leave unselected - app->selected_flash_options[SelectedFlashBootApp0] = false; - } - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshSubmenu); - break; - case SubmenuIndexApp: - app->selected_flash_options[SelectedFlashApp] = - !app->selected_flash_options[SelectedFlashApp]; - if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { - strncpy( - app->bin_file_path_app, - furi_string_get_cstr(selected_filepath), - sizeof(app->bin_file_path_app)); - } - if(app->bin_file_path_app[0] == '\0') { - // if user didn't select a file, leave unselected - app->selected_flash_options[SelectedFlashApp] = false; - } - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshSubmenu); - break; - case SubmenuIndexCustom: - app->selected_flash_options[SelectedFlashCustom] = - !app->selected_flash_options[SelectedFlashCustom]; - if(dialog_file_browser_show( - app->dialogs, selected_filepath, predefined_filepath, &browser_options)) { - strncpy( - app->bin_file_path_custom, - furi_string_get_cstr(selected_filepath), - sizeof(app->bin_file_path_custom)); - } - if(app->bin_file_path_custom[0] == '\0') { - // if user didn't select a file, leave unselected - app->selected_flash_options[SelectedFlashCustom] = false; - } - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshSubmenu); - break; - case SubmenuIndexFlash: - // count how many options are selected - app->num_selected_flash_options = 0; - for(bool* option = &app->selected_flash_options[SelectedFlashBoot]; - option < &app->selected_flash_options[NUM_FLASH_OPTIONS]; - ++option) { - if(*option) { - ++app->num_selected_flash_options; - } - } - if(app->num_selected_flash_options) { - // only start next scene if at least one option is selected - scene_manager_next_scene(app->scene_manager, WifiMarauderSceneConsoleOutput); - } - break; - } - - furi_string_free(selected_filepath); - furi_string_free(predefined_filepath); -} - -#define STR_SELECT "[x]" -#define STR_UNSELECT "[ ]" -#define STR_BOOT "Bootloader (" TOSTRING(ESP_ADDR_BOOT) ")" -#define STR_BOOT_S3 "Bootloader (" TOSTRING(ESP_ADDR_BOOT_S3) ")" -#define STR_PART "Part Table (" TOSTRING(ESP_ADDR_PART) ")" -#define STR_NVS "NVS (" TOSTRING(ESP_ADDR_NVS) ")" -#define STR_BOOT_APP0 "boot_app0 (" TOSTRING(ESP_ADDR_BOOT_APP0) ")" -#define STR_APP "Firmware (" TOSTRING(ESP_ADDR_APP) ")" -#define STR_CUSTOM "Custom" -#define STR_FLASH_S3 "[>] FLASH (ESP32-S3)" -#define STR_FLASH "[>] FLASH" -static void _refresh_submenu(WifiMarauderApp* app) { - Submenu* submenu = app->submenu; - - submenu_reset(app->submenu); - - submenu_set_header(submenu, "Browse for files to flash"); - submenu_add_item( - submenu, - app->selected_flash_options[SelectedFlashS3Mode] ? "[x] Using ESP32-S3" : - "[ ] Check if using S3", - SubmenuIndexS3Mode, - wifi_marauder_scene_flasher_callback, - app); - const char* strSelectBootloader = STR_UNSELECT " " STR_BOOT; - if(app->selected_flash_options[SelectedFlashS3Mode]) { - if(app->selected_flash_options[SelectedFlashBoot]) { - strSelectBootloader = STR_SELECT " " STR_BOOT_S3; - } else { - strSelectBootloader = STR_UNSELECT " " STR_BOOT_S3; - } - } else { - if(app->selected_flash_options[SelectedFlashBoot]) { - strSelectBootloader = STR_SELECT " " STR_BOOT; - } else { - strSelectBootloader = STR_UNSELECT " " STR_BOOT; - } - } - submenu_add_item( - submenu, strSelectBootloader, SubmenuIndexBoot, wifi_marauder_scene_flasher_callback, app); - submenu_add_item( - submenu, - app->selected_flash_options[SelectedFlashPart] ? STR_SELECT " " STR_PART : - STR_UNSELECT " " STR_PART, - SubmenuIndexPart, - wifi_marauder_scene_flasher_callback, - app); - submenu_add_item( - submenu, - app->selected_flash_options[SelectedFlashNvs] ? STR_SELECT " " STR_NVS : - STR_UNSELECT " " STR_NVS, - SubmenuIndexNvs, - wifi_marauder_scene_flasher_callback, - app); - submenu_add_item( - submenu, - app->selected_flash_options[SelectedFlashBootApp0] ? STR_SELECT " " STR_BOOT_APP0 : - STR_UNSELECT " " STR_BOOT_APP0, - SubmenuIndexBootApp0, - wifi_marauder_scene_flasher_callback, - app); - submenu_add_item( - submenu, - app->selected_flash_options[SelectedFlashApp] ? STR_SELECT " " STR_APP : - STR_UNSELECT " " STR_APP, - SubmenuIndexApp, - wifi_marauder_scene_flasher_callback, - app); - // TODO: custom addr - //submenu_add_item( - // submenu, app->selected_flash_options[SelectedFlashCustom] ? STR_SELECT " " STR_CUSTOM : STR_UNSELECT " " STR_CUSTOM, SubmenuIndexCustom, wifi_marauder_scene_flasher_callback, app); - submenu_add_item( - submenu, - app->selected_flash_options[SelectedFlashS3Mode] ? STR_FLASH_S3 : STR_FLASH, - SubmenuIndexFlash, - wifi_marauder_scene_flasher_callback, - app); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneFlasher)); - view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewSubmenu); -} - -void wifi_marauder_scene_flasher_on_enter(void* context) { - WifiMarauderApp* app = context; - - memset(app->selected_flash_options, 0, sizeof(app->selected_flash_options)); - app->bin_file_path_boot[0] = '\0'; - app->bin_file_path_part[0] = '\0'; - app->bin_file_path_nvs[0] = '\0'; - app->bin_file_path_boot_app0[0] = '\0'; - app->bin_file_path_app[0] = '\0'; - app->bin_file_path_custom[0] = '\0'; - - _refresh_submenu(app); -} - -bool wifi_marauder_scene_flasher_on_event(void* context, SceneManagerEvent event) { - WifiMarauderApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == WifiMarauderEventRefreshSubmenu) { - _refresh_submenu(app); - consumed = true; - } - } - - return consumed; -} - -void wifi_marauder_scene_flasher_on_exit(void* context) { - WifiMarauderApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c index 97b26fc7f..6e6be90df 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c @@ -68,6 +68,13 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { NO_ARGS, FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, + {"Evil Portal", + {"start"}, + 1, + {"evilportal -c start"}, + NO_ARGS, + FOCUS_CONSOLE_END, + SHOW_STOPSCAN_TIP}, {"Targeted Deauth", {"station", "manual"}, 2, @@ -83,11 +90,10 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { FOCUS_CONSOLE_END, SHOW_STOPSCAN_TIP}, {"Sniff", - {"beacon", "deauth", "esp", "pmkid", "probe", "pwn", "raw", "bt", "skim"}, - 9, + {"beacon", "deauth", "pmkid", "probe", "pwn", "raw", "bt", "skim"}, + 8, {"sniffbeacon", "sniffdeauth", - "sniffesp", "sniffpmkid", "sniffprobe", "sniffpwn", @@ -105,6 +111,7 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { TOGGLE_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"LED", {"hex", "pattern"}, 2, {"led -s", "led -p"}, INPUT_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Settings", {"display", "restore", "ForcePMKID", "ForceProbe", "SavePCAP", "EnableLED", "other"}, 7, @@ -118,10 +125,9 @@ const WifiMarauderItem items[NUM_MENU_ITEMS] = { TOGGLE_ARGS, FOCUS_CONSOLE_START, NO_TIP}, - {"Update", {"ota", "sd"}, 2, {"update -w", "update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, + {"Update", {"sd"}, 1, {"update -s"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Reboot", {""}, 1, {"reboot"}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Help", {""}, 1, {"help"}, NO_ARGS, FOCUS_CONSOLE_START, SHOW_STOPSCAN_TIP}, - {"Reflash ESP32 (WIP)", {""}, 1, {""}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Scripts", {""}, 1, {""}, NO_ARGS, FOCUS_CONSOLE_END, NO_TIP}, {"Save to flipper sdcard", // keep as last entry or change logic in callback below {""}, @@ -150,17 +156,6 @@ static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uin item->focus_console; app->show_stopscan_tip = item->show_stopscan_tip; - // TODO cleanup - if(index == NUM_MENU_ITEMS - 3) { - // flasher - app->is_command = false; - app->flash_mode = true; - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventStartFlasher); - return; - } - - app->flash_mode = false; - if(!app->is_command && selected_option_index == 0) { // View Log from start view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventStartLogViewer); @@ -243,6 +238,7 @@ void wifi_marauder_scene_start_on_enter(void* context) { } bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); WifiMarauderApp* app = context; bool consumed = false; @@ -271,10 +267,6 @@ bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state( app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); scene_manager_next_scene(app->scene_manager, WifiMarauderSceneSniffPmkidOptions); - } else if(event.event == WifiMarauderEventStartFlasher) { - scene_manager_set_scene_state( - app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index); - scene_manager_next_scene(app->scene_manager, WifiMarauderSceneFlasher); } consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c index ec4b97cc5..185af727e 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c @@ -46,9 +46,7 @@ void wifi_marauder_scene_text_input_on_enter(void* context) { // Setup view TextInput* text_input = app->text_input; // Add help message to header - if(app->flash_mode) { - text_input_set_header_text(text_input, "Enter destination address"); - } else if(app->special_case_input_step == 1) { + if(app->special_case_input_step == 1) { text_input_set_header_text(text_input, "Enter source MAC"); } else if(0 == strncmp("ssid -a -g", app->selected_tx_string, strlen("ssid -a -g"))) { text_input_set_header_text(text_input, "Enter # SSIDs to generate"); diff --git a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c index d4f1f8f36..6a591c4e0 100644 --- a/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c +++ b/applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c @@ -1,5 +1,25 @@ #include "../../wifi_marauder_app_i.h" +static void wifi_marauder_sniffpmkid_stage_hop_channels_setup_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + variable_item_set_current_value_index(item, stage->hop_channels); +} + +static void wifi_marauder_sniffpmkid_stage_hop_channels_change_callback(VariableItem* item) { + WifiMarauderApp* app = variable_item_get_context(item); + + uint8_t current_stage_index = variable_item_list_get_selected_item_index(app->var_item_list); + const WifiMarauderScriptMenuItem* menu_item = + &app->script_stage_menu->items[current_stage_index]; + + uint8_t option_index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, menu_item->options[option_index]); + + WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; + stage->hop_channels = option_index; +} + static void wifi_marauder_sniffpmkid_stage_force_deauth_setup_callback(VariableItem* item) { WifiMarauderApp* app = variable_item_get_context(item); WifiMarauderScriptStageSniffPmkid* stage = app->script_edit_selected_stage->stage; @@ -65,8 +85,8 @@ static void wifi_marauder_sniffpmkid_stage_timeout_select_callback(void* context } void wifi_marauder_script_stage_menu_sniffpmkid_load(WifiMarauderScriptStageMenu* stage_menu) { - stage_menu->num_items = 3; - stage_menu->items = malloc(3 * sizeof(WifiMarauderScriptMenuItem)); + stage_menu->num_items = 4; + stage_menu->items = malloc(4 * sizeof(WifiMarauderScriptMenuItem)); stage_menu->items[0] = (WifiMarauderScriptMenuItem){ .name = strdup("Force deauth"), @@ -88,4 +108,11 @@ void wifi_marauder_script_stage_menu_sniffpmkid_load(WifiMarauderScriptStageMenu .num_options = 1, .setup_callback = wifi_marauder_sniffpmkid_stage_timeout_setup_callback, .select_callback = wifi_marauder_sniffpmkid_stage_timeout_select_callback}; + stage_menu->items[3] = (WifiMarauderScriptMenuItem){ + .name = strdup("Hop Channels"), + .type = WifiMarauderScriptMenuItemTypeOptionsString, + .num_options = 2, + .options = {"no", "yes"}, + .setup_callback = wifi_marauder_sniffpmkid_stage_hop_channels_setup_callback, + .change_callback = wifi_marauder_sniffpmkid_stage_hop_channels_change_callback}; } \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c index 64dfacef5..a33e27cc5 100644 --- a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.c @@ -244,18 +244,30 @@ WifiMarauderScriptStageSniffPmkid* _wifi_marauder_script_get_stage_sniff_pmkid(c cJSON* channel_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "channel"); int channel = channel_json != NULL ? (int)cJSON_GetNumberValue(channel_json) : 0; + cJSON* timeout_json = cJSON_GetObjectItem(sniffpmkid_stage_json, "timeout"); int timeout = timeout_json != NULL ? (int)cJSON_GetNumberValue(timeout_json) : WIFI_MARAUDER_DEFAULT_TIMEOUT_SNIFF; + cJSON* force_deauth_json = cJSON_GetObjectItemCaseSensitive(sniffpmkid_stage_json, "forceDeauth"); bool force_deauth = cJSON_IsBool(force_deauth_json) ? force_deauth_json->valueint : true; + cJSON* hop_channels_json = + cJSON_GetObjectItemCaseSensitive(sniffpmkid_stage_json, "hopChannels"); + bool hop_channels = cJSON_IsBool(hop_channels_json) ? hop_channels_json->valueint : false; + WifiMarauderScriptStageSniffPmkid* sniff_pmkid_stage = (WifiMarauderScriptStageSniffPmkid*)malloc(sizeof(WifiMarauderScriptStageSniffPmkid)); + + if(sniff_pmkid_stage == NULL) { + // Handle memory allocation error + return NULL; + } sniff_pmkid_stage->channel = channel; sniff_pmkid_stage->timeout = timeout; sniff_pmkid_stage->force_deauth = force_deauth; + sniff_pmkid_stage->hop_channels = hop_channels; return sniff_pmkid_stage; } @@ -659,6 +671,9 @@ cJSON* _wifi_marauder_script_create_json_sniffpmkid( if(sniffpmkid_stage->timeout > 0) { cJSON_AddNumberToObject(sniffpmkid_json, "timeout", sniffpmkid_stage->timeout); } + // Hop channels + cJSON_AddBoolToObject(sniffpmkid_json, "hopChannels", sniffpmkid_stage->hop_channels); + return stage_json; } diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h index e11ee267f..2cf52196b 100644 --- a/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script.h @@ -196,6 +196,7 @@ typedef struct WifiMarauderScriptStageSniffEsp { typedef struct WifiMarauderScriptStageSniffPmkid { bool force_deauth; + bool hop_channels; int channel; int timeout; } WifiMarauderScriptStageSniffPmkid; diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c index d7799c300..3a0816e24 100644 --- a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c @@ -5,20 +5,32 @@ void _wifi_marauder_script_delay(WifiMarauderScriptWorker* worker, uint32_t dela for(uint32_t i = 0; i < delay_secs && worker->is_running; i++) furi_delay_ms(1000); } -void _send_stop() { +void _send_stop(bool save_pcaps) { const char stop_command[] = "stopscan\n"; - wifi_marauder_uart_tx((uint8_t*)(stop_command), strlen(stop_command)); + if(save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(stop_command), strlen(stop_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(stop_command), strlen(stop_command)); + } } -void _send_line_break() { - wifi_marauder_uart_tx((uint8_t*)("\n"), 1); +void _send_line_break(bool save_pcaps) { + if(save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)("\n"), 1); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)("\n"), 1); + } } -void _send_channel_select(int channel) { +void _send_channel_select(int channel, bool save_pcaps) { char command[30]; - wifi_marauder_uart_tx((uint8_t*)("\n"), 1); + _send_line_break(save_pcaps); snprintf(command, sizeof(command), "channel -s %d\n", channel); - wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + if(save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(command), strlen(command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(command), strlen(command)); + } } void _wifi_marauder_script_execute_scan( @@ -27,7 +39,7 @@ void _wifi_marauder_script_execute_scan( char command[15]; // Set channel if(stage->channel > 0) { - _send_channel_select(stage->channel); + _send_channel_select(stage->channel, worker->save_pcaps); } // Start scan if(stage->type == WifiMarauderScriptScanTypeAp) { @@ -35,12 +47,18 @@ void _wifi_marauder_script_execute_scan( } else { snprintf(command, sizeof(command), "scansta\n"); } - wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(command), strlen(command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(command), strlen(command)); + } + _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } -void _wifi_marauder_script_execute_select(WifiMarauderScriptStageSelect* stage) { +void _wifi_marauder_script_execute_select(WifiMarauderScriptStageSelect* stage, bool save_pcaps) { const char* select_type = NULL; switch(stage->type) { case WifiMarauderScriptSelectTypeAp: @@ -79,99 +97,177 @@ void _wifi_marauder_script_execute_select(WifiMarauderScriptStageSelect* stage) command, sizeof(command), "select %s -f \"%s\"\n", select_type, stage->filter); } - wifi_marauder_uart_tx((uint8_t*)command, command_length); + if(save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)command, command_length); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)command, command_length); + } } void _wifi_marauder_script_execute_deauth( WifiMarauderScriptStageDeauth* stage, WifiMarauderScriptWorker* worker) { const char attack_command[] = "attack -t deauth\n"; - wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(attack_command), strlen(attack_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + } + _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } void _wifi_marauder_script_execute_probe( WifiMarauderScriptStageProbe* stage, WifiMarauderScriptWorker* worker) { const char attack_command[] = "attack -t probe\n"; - wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(attack_command), strlen(attack_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + } + _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } void _wifi_marauder_script_execute_sniff_raw( WifiMarauderScriptStageSniffRaw* stage, WifiMarauderScriptWorker* worker) { const char sniff_command[] = "sniffraw\n"; - wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } + _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } void _wifi_marauder_script_execute_sniff_beacon( WifiMarauderScriptStageSniffBeacon* stage, WifiMarauderScriptWorker* worker) { const char sniff_command[] = "sniffbeacon\n"; - wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } + _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } void _wifi_marauder_script_execute_sniff_deauth( WifiMarauderScriptStageSniffDeauth* stage, WifiMarauderScriptWorker* worker) { const char sniff_command[] = "sniffdeauth\n"; - wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } + _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } void _wifi_marauder_script_execute_sniff_esp( WifiMarauderScriptStageSniffEsp* stage, WifiMarauderScriptWorker* worker) { const char sniff_command[] = "sniffesp\n"; - wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } + _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } void _wifi_marauder_script_execute_sniff_pmkid( WifiMarauderScriptStageSniffPmkid* stage, WifiMarauderScriptWorker* worker) { - char attack_command[50] = "sniffpmkid"; - int len = strlen(attack_command); + if(stage->hop_channels) { + for(int i = 1; i <= 11; i++) { + char attack_command[50] = "sniffpmkid"; + int len = strlen(attack_command); - if(stage->channel > 0) { - len += - snprintf(attack_command + len, sizeof(attack_command) - len, " -c %d", stage->channel); + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -c %d", i); + if(stage->force_deauth) { + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -d"); + } + + len += snprintf(attack_command + len, sizeof(attack_command) - len, "\n"); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)attack_command, len); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)attack_command, len); + } + + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(worker->save_pcaps); + } + } else { + char attack_command[50] = "sniffpmkid"; + int len = strlen(attack_command); + + if(stage->channel > 0) { + len += snprintf( + attack_command + len, sizeof(attack_command) - len, " -c %d", stage->channel); + } + + if(stage->force_deauth) { + len += snprintf(attack_command + len, sizeof(attack_command) - len, " -d"); + } + len += snprintf(attack_command + len, sizeof(attack_command) - len, "\n"); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)attack_command, len); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)attack_command, len); + } + + _wifi_marauder_script_delay(worker, stage->timeout); + _send_stop(worker->save_pcaps); } - - if(stage->force_deauth) { - len += snprintf(attack_command + len, sizeof(attack_command) - len, " -d"); - } - - len += snprintf(attack_command + len, sizeof(attack_command) - len, "\n"); - - wifi_marauder_uart_tx((uint8_t*)attack_command, len); - _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); } void _wifi_marauder_script_execute_sniff_pwn( WifiMarauderScriptStageSniffPwn* stage, WifiMarauderScriptWorker* worker) { const char sniff_command[] = "sniffpwn\n"; - wifi_marauder_uart_tx((uint8_t*)sniff_command, strlen(sniff_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(sniff_command), strlen(sniff_command)); + } + _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } void _wifi_marauder_script_execute_beacon_list( WifiMarauderScriptStageBeaconList* stage, WifiMarauderScriptWorker* worker) { const char clearlist_command[] = "clearlist -s\n"; - wifi_marauder_uart_tx((uint8_t*)(clearlist_command), strlen(clearlist_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(clearlist_command), strlen(clearlist_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(clearlist_command), strlen(clearlist_command)); + } char command[100]; char* ssid; @@ -179,8 +275,14 @@ void _wifi_marauder_script_execute_beacon_list( for(int i = 0; i < stage->ssid_count; i++) { ssid = stage->ssids[i]; snprintf(command, sizeof(command), "ssid -a -n \"%s\"", ssid); - wifi_marauder_uart_tx((uint8_t*)(command), strlen(command)); - _send_line_break(); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(command), strlen(command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(command), strlen(command)); + } + + _send_line_break(worker->save_pcaps); } if(stage->random_ssids > 0) { char add_random_command[50]; @@ -189,26 +291,46 @@ void _wifi_marauder_script_execute_beacon_list( sizeof(add_random_command), "ssid -a -r -g %d\n", stage->random_ssids); - wifi_marauder_uart_tx((uint8_t*)add_random_command, strlen(add_random_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)add_random_command, strlen(add_random_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)add_random_command, strlen(add_random_command)); + } } const char attack_command[] = "attack -t beacon -l\n"; - wifi_marauder_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(attack_command), strlen(attack_command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(attack_command), strlen(attack_command)); + } _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } void _wifi_marauder_script_execute_beacon_ap( WifiMarauderScriptStageBeaconAp* stage, WifiMarauderScriptWorker* worker) { const char command[] = "attack -t beacon -a\n"; - wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)(command), strlen(command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)(command), strlen(command)); + } _wifi_marauder_script_delay(worker, stage->timeout); - _send_stop(); + _send_stop(worker->save_pcaps); } -void _wifi_marauder_script_execute_exec(WifiMarauderScriptStageExec* stage) { +void _wifi_marauder_script_execute_exec(WifiMarauderScriptStageExec* stage, bool save_pcaps) { if(stage->command != NULL) { - wifi_marauder_uart_tx((uint8_t*)stage->command, strlen(stage->command)); + if(save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)stage->command, strlen(stage->command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)stage->command, strlen(stage->command)); + } + _send_line_break(save_pcaps); } } @@ -231,8 +353,14 @@ void wifi_marauder_script_execute_start(void* context) { sizeof(command), "settings -s EnableLED %s", script->enable_led ? "enable" : "disable"); - wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); - _send_line_break(); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)command, strlen(command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)command, strlen(command)); + } + + _send_line_break(worker->save_pcaps); } // Enables or disables PCAP saving according to script settings @@ -242,8 +370,14 @@ void wifi_marauder_script_execute_start(void* context) { sizeof(command), "settings -s SavePCAP %s", script->save_pcap ? "enable" : "disable"); - wifi_marauder_uart_tx((uint8_t*)command, strlen(command)); - _send_line_break(); + + if(worker->save_pcaps) { + wifi_marauder_usart_tx((uint8_t*)command, strlen(command)); + } else { + wifi_marauder_xtreme_uart_tx((uint8_t*)command, strlen(command)); + } + + _send_line_break(worker->save_pcaps); } } @@ -257,7 +391,8 @@ void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* co _wifi_marauder_script_execute_scan((WifiMarauderScriptStageScan*)stage_data, worker); break; case WifiMarauderScriptStageTypeSelect: - _wifi_marauder_script_execute_select((WifiMarauderScriptStageSelect*)stage_data); + _wifi_marauder_script_execute_select( + (WifiMarauderScriptStageSelect*)stage_data, worker->save_pcaps); break; case WifiMarauderScriptStageTypeDeauth: _wifi_marauder_script_execute_deauth((WifiMarauderScriptStageDeauth*)stage_data, worker); @@ -298,7 +433,8 @@ void wifi_marauder_script_execute_stage(WifiMarauderScriptStage* stage, void* co (WifiMarauderScriptStageBeaconAp*)stage_data, worker); break; case WifiMarauderScriptStageTypeExec: - _wifi_marauder_script_execute_exec((WifiMarauderScriptStageExec*)stage_data); + _wifi_marauder_script_execute_exec( + (WifiMarauderScriptStageExec*)stage_data, worker->save_pcaps); break; case WifiMarauderScriptStageTypeDelay: _wifi_marauder_script_execute_delay((WifiMarauderScriptStageDelay*)stage_data, worker); diff --git a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h index 76ff070d2..b000f9ed4 100644 --- a/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h +++ b/applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h @@ -15,6 +15,7 @@ typedef struct WifiMarauderScriptWorker { void (*callback_stage)(WifiMarauderScriptStage*, void*); void* context; bool is_running; + bool save_pcaps; } WifiMarauderScriptWorker; /** diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index 26092bdba..59fe4cb8d 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -2,7 +2,6 @@ #include #include -#include static bool wifi_marauder_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -24,7 +23,6 @@ static void wifi_marauder_app_tick_event_callback(void* context) { WifiMarauderApp* wifi_marauder_app_alloc() { WifiMarauderApp* app = malloc(sizeof(WifiMarauderApp)); - dolphin_deed(DolphinDeedPluginStart); app->gui = furi_record_open(RECORD_GUI); app->dialogs = furi_record_open(RECORD_DIALOGS); @@ -86,9 +84,6 @@ WifiMarauderApp* wifi_marauder_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, WifiMarauderAppViewSubmenu, submenu_get_view(app->submenu)); - app->flash_mode = false; - app->flash_worker_busy = false; - scene_manager_next_scene(app->scene_manager, WifiMarauderSceneStart); return app; @@ -164,7 +159,9 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { scene_manager_free(app->scene_manager); wifi_marauder_uart_free(app->uart); - wifi_marauder_uart_free(app->lp_uart); + if(app->ok_to_save_pcaps) { + wifi_marauder_uart_free(app->pcap_uart); + } // Close records furi_record_close(RECORD_GUI); @@ -189,8 +186,13 @@ int32_t wifi_marauder_app(void* p) { wifi_marauder_make_app_folder(wifi_marauder_app); wifi_marauder_load_settings(wifi_marauder_app); - wifi_marauder_app->uart = wifi_marauder_usart_init(wifi_marauder_app); - wifi_marauder_app->lp_uart = wifi_marauder_lp_uart_init(wifi_marauder_app); + if(wifi_marauder_app->ok_to_save_pcaps) { + wifi_marauder_app->uart = wifi_marauder_usart_init(wifi_marauder_app); + wifi_marauder_app->pcap_uart = wifi_marauder_lp_uart_init(wifi_marauder_app); + } else { + wifi_marauder_app->uart = + wifi_marauder_uart_init(wifi_marauder_app, XTREME_UART_CH, "WifiMarauderUartRxThread"); + } view_dispatcher_run(wifi_marauder_app->view_dispatcher); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.h b/applications/external/wifi_marauder_companion/wifi_marauder_app.h index 187a0aaaa..ceacbb489 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.h @@ -4,7 +4,7 @@ extern "C" { #endif -#define WIFI_MARAUDER_APP_VERSION "v0.5.1" +#define WIFI_MARAUDER_APP_VERSION "v0.6.0" typedef struct WifiMarauderApp WifiMarauderApp; 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 15d159450..13e76c0c2 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -26,8 +26,17 @@ #include #include #include +#include -#define NUM_MENU_ITEMS (19) +#define XTREME_UART_CH \ + (XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) + +#define US_ART_CH (FuriHalUartIdUSART1) +#define LP_UART_CH (FuriHalUartIdLPUART1) +#define BAUDRATE (115200) + +#define NUM_MENU_ITEMS (20) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) @@ -49,17 +58,6 @@ typedef enum WifiMarauderUserInputType { WifiMarauderUserInputTypeFileName } WifiMarauderUserInputType; -typedef enum SelectedFlashOptions { - SelectedFlashS3Mode, - SelectedFlashBoot, - SelectedFlashPart, - SelectedFlashNvs, - SelectedFlashBootApp0, - SelectedFlashApp, - SelectedFlashCustom, - NUM_FLASH_OPTIONS -} SelectedFlashOptions; - struct WifiMarauderApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -90,7 +88,7 @@ struct WifiMarauderApp { int open_log_file_num_pages; WifiMarauderUart* uart; - WifiMarauderUart* lp_uart; + WifiMarauderUart* pcap_uart; int selected_menu_index; int selected_option_index[NUM_MENU_ITEMS]; const char* selected_tx_string; @@ -125,19 +123,6 @@ struct WifiMarauderApp { int special_case_input_step; char special_case_input_src_addr[20]; char special_case_input_dst_addr[20]; - - // For flashing - TODO: put into its own struct? - bool selected_flash_options[NUM_FLASH_OPTIONS]; - int num_selected_flash_options; - char bin_file_path_boot[100]; - char bin_file_path_part[100]; - char bin_file_path_nvs[100]; - char bin_file_path_boot_app0[100]; - char bin_file_path_app[100]; - char bin_file_path_custom[100]; - FuriThread* flash_worker; - bool flash_worker_busy; - bool flash_mode; }; // Supported commands: diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h index ff03f31dd..b6d9f8274 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h @@ -9,7 +9,5 @@ typedef enum { WifiMarauderEventStartSettingsInit, WifiMarauderEventStartLogViewer, WifiMarauderEventStartScriptSelect, - WifiMarauderEventStartSniffPmkidOptions, - WifiMarauderEventStartFlasher, - WifiMarauderEventRefreshSubmenu + WifiMarauderEventStartSniffPmkidOptions } WifiMarauderCustomEvent; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_flasher.c b/applications/external/wifi_marauder_companion/wifi_marauder_flasher.c deleted file mode 100644 index ffc1acb78..000000000 --- a/applications/external/wifi_marauder_companion/wifi_marauder_flasher.c +++ /dev/null @@ -1,251 +0,0 @@ -#include "wifi_marauder_flasher.h" - -FuriStreamBuffer* flash_rx_stream; // TODO make safe -WifiMarauderApp* global_app; // TODO make safe -FuriTimer* timer; // TODO make - -static uint32_t _remaining_time = 0; -static void _timer_callback(void* context) { - UNUSED(context); - if(_remaining_time > 0) { - _remaining_time--; - } -} - -static esp_loader_error_t _flash_file(WifiMarauderApp* app, char* filepath, uint32_t addr) { - // TODO cleanup - esp_loader_error_t err; - static uint8_t payload[1024]; - File* bin_file = storage_file_alloc(app->storage); - - char user_msg[256]; - - // open file - if(!storage_file_open(bin_file, filepath, FSAM_READ, FSOM_OPEN_EXISTING)) { - storage_file_close(bin_file); - storage_file_free(bin_file); - dialog_message_show_storage_error(app->dialogs, "Cannot open file"); - return ESP_LOADER_ERROR_FAIL; - } - - uint64_t size = storage_file_size(bin_file); - - loader_port_debug_print("Erasing flash...this may take a while\n"); - err = esp_loader_flash_start(addr, size, sizeof(payload)); - if(err != ESP_LOADER_SUCCESS) { - storage_file_close(bin_file); - storage_file_free(bin_file); - snprintf(user_msg, sizeof(user_msg), "Erasing flash failed with error %d\n", err); - loader_port_debug_print(user_msg); - return err; - } - - loader_port_debug_print("Start programming\n"); - uint64_t last_updated = size; - while(size > 0) { - if((last_updated - size) > 50000) { - // inform user every 50k bytes - // TODO: draw a progress bar next update - snprintf(user_msg, sizeof(user_msg), "%llu bytes left.\n", size); - loader_port_debug_print(user_msg); - last_updated = size; - } - size_t to_read = MIN(size, sizeof(payload)); - uint16_t num_bytes = storage_file_read(bin_file, payload, to_read); - err = esp_loader_flash_write(payload, num_bytes); - if(err != ESP_LOADER_SUCCESS) { - snprintf(user_msg, sizeof(user_msg), "Packet could not be written! Error: %u\n", err); - storage_file_close(bin_file); - storage_file_free(bin_file); - loader_port_debug_print(user_msg); - return err; - } - - size -= num_bytes; - } - - loader_port_debug_print("Finished programming\n"); - - // TODO verify - - storage_file_close(bin_file); - storage_file_free(bin_file); - - return ESP_LOADER_SUCCESS; -} - -typedef struct { - SelectedFlashOptions selected; - const char* description; - char* path; - uint32_t addr; -} FlashItem; - -static void _flash_all_files(WifiMarauderApp* app) { - esp_loader_error_t err; - const int num_steps = app->num_selected_flash_options; - -#define NUM_FLASH_ITEMS 6 - FlashItem items[NUM_FLASH_ITEMS] = { - {SelectedFlashBoot, - "bootloader", - app->bin_file_path_boot, - app->selected_flash_options[SelectedFlashS3Mode] ? ESP_ADDR_BOOT_S3 : ESP_ADDR_BOOT}, - {SelectedFlashPart, "partition table", app->bin_file_path_part, ESP_ADDR_PART}, - {SelectedFlashNvs, "NVS", app->bin_file_path_nvs, ESP_ADDR_NVS}, - {SelectedFlashBootApp0, "boot_app0", app->bin_file_path_boot_app0, ESP_ADDR_BOOT_APP0}, - {SelectedFlashApp, "firmware", app->bin_file_path_app, ESP_ADDR_APP}, - {SelectedFlashCustom, "custom data", app->bin_file_path_custom, 0x0}, - /* if you add more entries, update NUM_FLASH_ITEMS above! */ - }; - - char user_msg[256]; - - int current_step = 1; - for(FlashItem* item = &items[0]; item < &items[NUM_FLASH_ITEMS]; ++item) { - if(app->selected_flash_options[item->selected]) { - snprintf( - user_msg, - sizeof(user_msg), - "Flashing %s (%d/%d) to address 0x%lx\n", - item->description, - current_step++, - num_steps, - item->addr); - loader_port_debug_print(user_msg); - err = _flash_file(app, item->path, item->addr); - if(err) { - break; - } - } - } -} - -static int32_t wifi_marauder_flash_bin(void* context) { - WifiMarauderApp* app = (void*)context; - esp_loader_error_t err; - - app->flash_worker_busy = true; - - // alloc global objects - flash_rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); - timer = furi_timer_alloc(_timer_callback, FuriTimerTypePeriodic, app); - - loader_port_debug_print("Connecting\n"); - esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT(); - err = esp_loader_connect(&connect_config); - if(err != ESP_LOADER_SUCCESS) { - char err_msg[256]; - snprintf(err_msg, sizeof(err_msg), "Cannot connect to target. Error: %u\n", err); - loader_port_debug_print(err_msg); - } - -#if 0 // still getting packet drops with this - // higher BR - if(!err) { - loader_port_debug_print("Increasing speed for faster flash\n"); - err = esp_loader_change_transmission_rate(230400); - if (err != ESP_LOADER_SUCCESS) { - char err_msg[256]; - snprintf( - err_msg, - sizeof(err_msg), - "Cannot change transmission rate. Error: %u\n", - err); - loader_port_debug_print(err_msg); - } - furi_hal_uart_set_br(FuriHalUartIdUSART1, 230400); - } -#endif - - if(!err) { - loader_port_debug_print("Connected\n"); - _flash_all_files(app); -#if 0 - loader_port_debug_print("Restoring transmission rate\n"); - furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); -#endif - loader_port_debug_print("Done flashing. Please reset the board manually.\n"); - } - - // done - app->flash_worker_busy = false; - - // cleanup - furi_stream_buffer_free(flash_rx_stream); - flash_rx_stream = NULL; - furi_timer_free(timer); - return 0; -} - -void wifi_marauder_flash_start_thread(WifiMarauderApp* app) { - global_app = app; - - app->flash_worker = furi_thread_alloc(); - furi_thread_set_name(app->flash_worker, "WifiMarauderFlashWorker"); - furi_thread_set_stack_size(app->flash_worker, 2048); - furi_thread_set_context(app->flash_worker, app); - furi_thread_set_callback(app->flash_worker, wifi_marauder_flash_bin); - furi_thread_start(app->flash_worker); -} - -void wifi_marauder_flash_stop_thread(WifiMarauderApp* app) { - furi_thread_join(app->flash_worker); - furi_thread_free(app->flash_worker); -} - -esp_loader_error_t loader_port_read(uint8_t* data, uint16_t size, uint32_t timeout) { - size_t read = furi_stream_buffer_receive(flash_rx_stream, data, size, pdMS_TO_TICKS(timeout)); - if(read < size) { - return ESP_LOADER_ERROR_TIMEOUT; - } else { - return ESP_LOADER_SUCCESS; - } -} - -esp_loader_error_t loader_port_write(const uint8_t* data, uint16_t size, uint32_t timeout) { - UNUSED(timeout); - wifi_marauder_uart_tx((uint8_t*)data, size); - return ESP_LOADER_SUCCESS; -} - -void loader_port_enter_bootloader(void) { - // unimplemented -} - -void loader_port_delay_ms(uint32_t ms) { - furi_delay_ms(ms); -} - -void loader_port_start_timer(uint32_t ms) { - _remaining_time = ms; - furi_timer_start(timer, pdMS_TO_TICKS(1)); -} - -uint32_t loader_port_remaining_time(void) { - return _remaining_time; -} - -extern void wifi_marauder_console_output_handle_rx_data_cb( - uint8_t* buf, - size_t len, - void* context); // TODO cleanup -void loader_port_debug_print(const char* str) { - if(global_app) - wifi_marauder_console_output_handle_rx_data_cb((uint8_t*)str, strlen(str), global_app); -} - -void loader_port_spi_set_cs(uint32_t level) { - UNUSED(level); - // unimplemented -} - -void wifi_marauder_flash_handle_rx_data_cb(uint8_t* buf, size_t len, void* context) { - UNUSED(context); - if(flash_rx_stream) { - furi_stream_buffer_send(flash_rx_stream, buf, len, 0); - } else { - // done flashing - if(global_app) wifi_marauder_console_output_handle_rx_data_cb(buf, len, global_app); - } -} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_flasher.h b/applications/external/wifi_marauder_companion/wifi_marauder_flasher.h deleted file mode 100644 index d875c2dbe..000000000 --- a/applications/external/wifi_marauder_companion/wifi_marauder_flasher.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "wifi_marauder_app_i.h" -#include "wifi_marauder_uart.h" -#define SERIAL_FLASHER_INTERFACE_UART /* TODO why is application.fam not passing this via cdefines */ -#include "esp_loader_io.h" - -#define ESP_ADDR_BOOT_S3 0x0 -#define ESP_ADDR_BOOT 0x1000 -#define ESP_ADDR_PART 0x8000 -#define ESP_ADDR_NVS 0x9000 -#define ESP_ADDR_BOOT_APP0 0xE000 -#define ESP_ADDR_APP 0x10000 - -void wifi_marauder_flash_start_thread(WifiMarauderApp* app); -void wifi_marauder_flash_stop_thread(WifiMarauderApp* app); -void wifi_marauder_flash_handle_rx_data_cb(uint8_t* buf, size_t len, void* context); \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c index 0c9147752..d4a1d3ce5 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c @@ -1,10 +1,6 @@ #include "wifi_marauder_app_i.h" #include "wifi_marauder_uart.h" -#define UART_CH (FuriHalUartIdUSART1) -#define LP_UART_CH (FuriHalUartIdLPUART1) -#define BAUDRATE (115200) - struct WifiMarauderUart { WifiMarauderApp* app; FuriHalUartId channel; @@ -58,8 +54,12 @@ static int32_t uart_worker(void* context) { return 0; } -void wifi_marauder_uart_tx(uint8_t* data, size_t len) { - furi_hal_uart_tx(UART_CH, data, len); +void wifi_marauder_xtreme_uart_tx(uint8_t* data, size_t len) { + furi_hal_uart_tx(XTREME_UART_CH, data, len); +} + +void wifi_marauder_usart_tx(uint8_t* data, size_t len) { + furi_hal_uart_tx(US_ART_CH, data, len); } void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len) { @@ -91,7 +91,7 @@ WifiMarauderUart* } WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app) { - return wifi_marauder_uart_init(app, UART_CH, "WifiMarauderUartRxThread"); + return wifi_marauder_uart_init(app, US_ART_CH, "WifiMarauderUartRxThread"); } WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app) { @@ -108,8 +108,9 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); if(uart->channel == FuriHalUartIdLPUART1) { furi_hal_uart_deinit(uart->channel); + } else { + furi_hal_console_enable(); } - furi_hal_console_enable(); free(uart); } diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.h b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h index e352cfec5..1a8d28c5d 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h @@ -9,8 +9,12 @@ typedef struct WifiMarauderUart WifiMarauderUart; void wifi_marauder_uart_set_handle_rx_data_cb( WifiMarauderUart* uart, void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)); -void wifi_marauder_uart_tx(uint8_t* data, size_t len); +void wifi_marauder_xtreme_uart_tx(uint8_t* data, size_t len); +void wifi_marauder_usart_tx(uint8_t* data, size_t len); void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len); WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app); WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app); void wifi_marauder_uart_free(WifiMarauderUart* uart); + +WifiMarauderUart* + wifi_marauder_uart_init(WifiMarauderApp* app, FuriHalUartId channel, const char* thread_name); \ No newline at end of file diff --git a/applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h b/applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h index 7b28ebd64..8ec46ce79 100644 --- a/applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h +++ b/applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h @@ -15,3 +15,9 @@ #define FLIPPERZERO_SERIAL_BAUD 115200 #define NA 0 + +#include + +#define UART_CH \ + (XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? FuriHalUartIdUSART1 : \ + FuriHalUartIdLPUART1) diff --git a/applications/external/wifi_scanner/application.fam b/applications/external/wifi_scanner/application.fam index 14f06ff7a..cd007eb48 100644 --- a/applications/external/wifi_scanner/application.fam +++ b/applications/external/wifi_scanner/application.fam @@ -5,7 +5,6 @@ App( entry_point="wifi_scanner_app", requires=["gui"], stack_size=2 * 1024, - order=110, fap_icon="wifi_10px.png", fap_category="WiFi", fap_author="@SequoiaSan & @xMasterX", diff --git a/applications/external/wifi_scanner/wifi_scanner.c b/applications/external/wifi_scanner/wifi_scanner.c index 1eb60fd00..07de32189 100644 --- a/applications/external/wifi_scanner/wifi_scanner.c +++ b/applications/external/wifi_scanner/wifi_scanner.c @@ -835,7 +835,7 @@ void send_serial_command(ESerialCommand command) { return; }; - furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + furi_hal_uart_tx(UART_CH, data, 1); } int32_t wifi_scanner_app(void* p) { @@ -905,11 +905,14 @@ int32_t wifi_scanner_app(void* p) { WIFI_APP_LOG_I("UART thread allocated"); // Enable uart listener -#if DISABLE_CONSOLE - furi_hal_console_disable(); -#endif - furi_hal_uart_set_br(FuriHalUartIdUSART1, FLIPPERZERO_SERIAL_BAUD); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_on_irq_cb, app); + if(UART_CH == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_init(UART_CH, FLIPPERZERO_SERIAL_BAUD); + } + + furi_hal_uart_set_br(UART_CH, FLIPPERZERO_SERIAL_BAUD); + furi_hal_uart_set_irq_cb(UART_CH, uart_on_irq_cb, app); WIFI_APP_LOG_I("UART Listener created"); // Because we assume that module was on before we launched the app. We need to ensure that module will be in initial state on app start @@ -1034,9 +1037,11 @@ int32_t wifi_scanner_app(void* p) { // Reset GPIO pins to default state furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -#if DISABLE_CONSOLE - furi_hal_console_enable(); -#endif + if(UART_CH == FuriHalUartIdLPUART1) { + furi_hal_uart_deinit(UART_CH); + } else { + furi_hal_console_enable(); + } view_port_enabled_set(view_port, false); diff --git a/applications/external/wiiec/application.fam b/applications/external/wiiec/application.fam index e84bba620..3576caf46 100644 --- a/applications/external/wiiec/application.fam +++ b/applications/external/wiiec/application.fam @@ -16,7 +16,6 @@ App( # sdk_headers="", # --- Run-time info stack_size=2 * 1024, - order=20, # --- FAP details sources=["wii_*.c", "gfx/*.c"], # fap_weburl="https://github.com/csBlueChip/FlipperZero_plugin_WiiChuck/", diff --git a/applications/external/wire_tester/LICENSE b/applications/external/wire_tester/LICENSE new file mode 100644 index 000000000..c34fc9f11 --- /dev/null +++ b/applications/external/wire_tester/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2023, Blaine Murphy +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. + +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. diff --git a/applications/external/wire_tester/application.fam b/applications/external/wire_tester/application.fam new file mode 100644 index 000000000..6a474dc9c --- /dev/null +++ b/applications/external/wire_tester/application.fam @@ -0,0 +1,15 @@ +App( + appid="wire_tester", + name="[GPIO] Wire Tester", + apptype=FlipperAppType.EXTERNAL, + entry_point="app_main", + requires=["gui"], + stack_size=1 * 1024, + fap_icon="icon.png", + fap_icon_assets="assets", + fap_category="GPIO", + fap_author="@unixispower", + fap_weburl="https://gitlab.com/unixispower/flipper-wire-tester", + fap_version="1.0", + fap_description="Beeps if a wire is continuous", +) diff --git a/applications/external/wire_tester/assets/background_128x64.png b/applications/external/wire_tester/assets/background_128x64.png new file mode 100644 index 000000000..224fcd03b Binary files /dev/null and b/applications/external/wire_tester/assets/background_128x64.png differ diff --git a/applications/external/wire_tester/icon.png b/applications/external/wire_tester/icon.png new file mode 100644 index 000000000..b2108e94d Binary files /dev/null and b/applications/external/wire_tester/icon.png differ diff --git a/applications/external/wire_tester/wire_tester.c b/applications/external/wire_tester/wire_tester.c new file mode 100644 index 000000000..3eef936a5 --- /dev/null +++ b/applications/external/wire_tester/wire_tester.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include "wire_tester_icons.h" +#include + +//#define TAG "wire_tester" + +static const uint32_t EVENT_PERIOD_MS = 10; // check for input changes often +static const float BEEP_FREQ = 2400.0f; // louder than other frequencies +static const float BEEP_VOL = 0.8f; +static const GpioPin* const INPUT_PIN = &gpio_ext_pb2; // pin 6 + +static void start_feedback(NotificationApp* notifications) { + // set LED to green + notification_message_block(notifications, &sequence_set_only_green_255); + + // start beep + if(furi_hal_speaker_acquire(1000)) { + furi_hal_speaker_start(BEEP_FREQ, BEEP_VOL); + } +} + +static void stop_feedback(NotificationApp* notifications) { + // clear LED + notification_message_block(notifications, &sequence_reset_rgb); + + // stop beep + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} + +static void draw_callback(Canvas* canvas, void* ctx) { + furi_assert(ctx); + canvas_clear(canvas); + canvas_draw_icon(canvas, 0, 0, &I_background_128x64); +} + +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 app_main(void* p) { + UNUSED(p); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + // force backlight on because our hands will be busy with wires + NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); + notification_message_block(notifications, &sequence_display_backlight_enforce_on); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, view_port); + 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); + view_port_update(view_port); + + stop_feedback(notifications); + + // set input to be normally high; it will be low when shorted to ground + furi_hal_gpio_init(INPUT_PIN, GpioModeInput, GpioPullUp, GpioSpeedLow); + + bool alarming = false; + bool running = true; + while(running) { + // start and stop feedback on the transition + bool continuous = !furi_hal_gpio_read(INPUT_PIN); + if(continuous && !alarming) { + start_feedback(notifications); + } else if(!continuous && alarming) { + stop_feedback(notifications); + } + alarming = continuous; + + // exit on back key + InputEvent event; + if(furi_message_queue_get(event_queue, &event, EVENT_PERIOD_MS) == FuriStatusOk) { + if((event.type == InputTypePress || event.type == InputTypeRepeat) && + event.key == InputKeyBack) { + running = false; + } + } + } + + // return control of the LED, beeper, and backlight + stop_feedback(notifications); + notification_message_block(notifications, &sequence_display_backlight_enforce_auto); + + view_port_enabled_set(view_port, false); + 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); + + return 0; +} diff --git a/applications/external/yatzee/application.fam b/applications/external/yatzee/application.fam index f633d8102..46480eb21 100644 --- a/applications/external/yatzee/application.fam +++ b/applications/external/yatzee/application.fam @@ -5,7 +5,6 @@ App( entry_point="yatzee_main", requires=["gui"], stack_size=4 * 1024, - order=99, fap_icon="images/yatzee_icon_10px.png", fap_category="Games", fap_icon_assets="images", diff --git a/applications/external/zombiez/application.fam b/applications/external/zombiez/application.fam index ca6120899..898488826 100644 --- a/applications/external/zombiez/application.fam +++ b/applications/external/zombiez/application.fam @@ -5,7 +5,6 @@ App( entry_point="zombiez_game_app", requires=["gui"], stack_size=2 * 1024, - order=280, fap_icon="zombie_10px.png", fap_category="Games", fap_author="@DevMilanIan & @xMasterX, (original By @Dooskington)", diff --git a/applications/external/zombiez/zombiez.c b/applications/external/zombiez/zombiez.c index 291e76966..a9bf74f7a 100644 --- a/applications/external/zombiez/zombiez.c +++ b/applications/external/zombiez/zombiez.c @@ -2,6 +2,7 @@ #include #include #include +#include //ORIGINAL REPO: https://github.com/Dooskington/flipperzero-zombiez //AUTHORS: https://github.com/Dooskington | https://github.com/DevMilanIan @@ -314,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; @@ -398,4 +399,4 @@ free_and_exit: furi_message_queue_free(event_queue); return return_code; -} +} \ No newline at end of file diff --git a/applications/main/archive/application.fam b/applications/main/archive/application.fam index 96ad36bea..3787b2bf7 100644 --- a/applications/main/archive/application.fam +++ b/applications/main/archive/application.fam @@ -8,6 +8,6 @@ App( stack_size=6 * 1024, order=0, sdk_headers=[ - "helpers/favorite_timeout.h", + "helpers/archive_helpers_ext.h", ], ) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index de1c30c21..d0111b363 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -676,6 +676,6 @@ void archive_refresh_dir(ArchiveBrowserView* browser) { 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)); + file_browser_worker_folder_refresh_sel(browser->worker, furi_string_get_cstr(str)); furi_string_free(str); } diff --git a/applications/main/archive/helpers/favorite_timeout.h b/applications/main/archive/helpers/archive_helpers_ext.h similarity index 90% rename from applications/main/archive/helpers/favorite_timeout.h rename to applications/main/archive/helpers/archive_helpers_ext.h index a46f7ec1e..cf5a9b64c 100644 --- a/applications/main/archive/helpers/favorite_timeout.h +++ b/applications/main/archive/helpers/archive_helpers_ext.h @@ -19,6 +19,8 @@ void favorite_timeout_callback(void* _ctx); void favorite_timeout_run(ViewDispatcher* view_dispatcher, SceneManager* scene_manager); +void run_with_default_app(const char* path); + #ifdef __cplusplus } #endif diff --git a/applications/main/archive/helpers/favorite_timeout.c b/applications/main/archive/helpers/favorite_timeout.c index 25385614e..f38fb53c3 100644 --- a/applications/main/archive/helpers/favorite_timeout.c +++ b/applications/main/archive/helpers/favorite_timeout.c @@ -1,4 +1,4 @@ -#include "favorite_timeout.h" +#include "archive_helpers_ext.h" #include bool process_favorite_launch(char** args) { diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index 096ed673d..c7b4d1292 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -3,6 +3,7 @@ #include "../helpers/archive_apps.h" #include "../helpers/archive_favorites.h" #include "../helpers/archive_browser.h" +#include "../helpers/archive_helpers_ext.h" #include "../views/archive_browser_view.h" #include "archive/scenes/archive_scene.h" @@ -52,9 +53,32 @@ static void archive_loader_callback(const void* message, void* context) { } } +static void archive_show_file(Loader* loader, const char* path) { + File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + bool text = true; + if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + uint8_t buf[1000]; + size_t read = storage_file_read(file, buf, sizeof(buf)); + for(size_t i = 0; i < read; i++) { + const char c = buf[i]; + if((c < ' ' || c > '~') && c != '\r' && c != '\n') { + text = false; + break; + } + } + } + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + + if(text) { + loader_start_detached_with_gui_error(loader, EXT_PATH("apps/Tools/text_viewer.fap"), path); + } else { + loader_start_detached_with_gui_error(loader, EXT_PATH("apps/Tools/hex_viewer.fap"), path); + } +} + static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selected, bool favorites) { - UNUSED(browser); Loader* loader = furi_record_open(RECORD_LOADER); const char* app_name = archive_get_flipper_app_name(selected->type); @@ -81,16 +105,38 @@ static void 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); + loader_start_detached_with_gui_error(loader, app_name, str); } } + } else if(selected->type == ArchiveFileTypeApplication) { + loader_start_detached_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); } else { - loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL); + archive_show_file(loader, furi_string_get_cstr(selected->path)); } furi_record_close(RECORD_LOADER); } +// Hijack existing archive code for default app choosing without needing archive running +void run_with_default_app(const char* path) { + // Kostily + FileInfo info; + Storage* storage = furi_record_open(RECORD_STORAGE); + bool is_dir = storage_common_stat(storage, path, &info) == FSE_OK && + info.flags & FSF_DIRECTORY; + furi_record_close(RECORD_STORAGE); + + // Velosipedy + ArchiveFile_t item; + ArchiveFile_t_init(&item); + furi_string_set(item.path, path); + archive_set_file_type(&item, path, is_dir, false); + + // Bydlo kod go brrr + archive_run_in_app(NULL, &item, false); + ArchiveFile_t_clear(&item); +} + void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) { ArchiveApp* archive = (ArchiveApp*)context; view_dispatcher_send_custom_event(archive->view_dispatcher, event); @@ -176,10 +222,10 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventFileMenuShow: + archive_show_file( + furi_record_open(RECORD_LOADER), furi_string_get_cstr(selected->path)); + furi_record_close(RECORD_LOADER); archive_show_file_menu(browser, false, false); - scene_manager_set_scene_state( - archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); - scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneShow); consumed = true; break; case ArchiveBrowserEventFileMenuPaste: diff --git a/applications/main/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h index 00ff4cf38..7f49cf3b5 100644 --- a/applications/main/archive/scenes/archive_scene_config.h +++ b/applications/main/archive/scenes/archive_scene_config.h @@ -4,4 +4,3 @@ 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/bad_kb/application.fam b/applications/main/bad_kb/application.fam index 0665c5661..4591625ec 100644 --- a/applications/main/bad_kb/application.fam +++ b/applications/main/bad_kb/application.fam @@ -7,4 +7,5 @@ App( icon="A_BadKb_14", order=70, fap_icon="icon.png", + fap_category="Tools", ) diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index c87fb5dbd..6eef678cf 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -7,4 +7,5 @@ App( icon="A_GPIO_14", order=50, fap_icon="icon.png", + fap_category="GPIO", ) diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart.c b/applications/main/gpio/scenes/gpio_scene_usb_uart.c index aa41aaf98..c5e085192 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart.c @@ -19,7 +19,7 @@ void gpio_scene_usb_uart_on_enter(void* context) { uint32_t prev_state = scene_manager_get_scene_state(app->scene_manager, GpioAppViewUsbUart); if(prev_state == 0) { scene_usb_uart = malloc(sizeof(SceneUsbUartBridge)); - scene_usb_uart->cfg.vcp_ch = 0; // TODO: settings load + scene_usb_uart->cfg.vcp_ch = 0; scene_usb_uart->cfg.uart_ch = 0; scene_usb_uart->cfg.flow_pins = 0; scene_usb_uart->cfg.baudrate_mode = 0; diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 9ac40b46a..5df50fcf2 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -8,6 +8,7 @@ App( stack_size=2 * 1024, order=60, fap_icon="icon.png", + fap_category="iButton", ) App( diff --git a/applications/main/ibutton/ibutton.c b/applications/main/ibutton/ibutton.c index 78c433f32..35cf7604c 100644 --- a/applications/main/ibutton/ibutton.c +++ b/applications/main/ibutton/ibutton.c @@ -2,7 +2,7 @@ #include #include -#include +#include #define TAG "iButtonApp" diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 6c770f9e9..d669e4298 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -8,6 +8,7 @@ App( stack_size=3 * 1024, order=40, fap_icon="icon.png", + fap_category="Infrared", ) App( diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index d4bfde99f..f7f4779ca 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -171,6 +171,10 @@ static Infrared* infrared_alloc() { view_dispatcher_add_view( view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack)); + infrared->move_view = infrared_move_view_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewMove, infrared_move_view_get_view(infrared->move_view)); + if(app_state->is_debug_enabled) { infrared->debug_view = infrared_debug_view_alloc(); view_dispatcher_add_view( @@ -218,6 +222,9 @@ static void infrared_free(Infrared* infrared) { view_dispatcher_remove_view(view_dispatcher, InfraredViewStack); view_stack_free(infrared->view_stack); + view_dispatcher_remove_view(view_dispatcher, InfraredViewMove); + infrared_move_view_free(infrared->move_view); + if(app_state->is_debug_enabled) { view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView); infrared_debug_view_free(infrared->debug_view); diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 3fa99cb02..54b5cabab 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -85,7 +85,7 @@ static void infrared_cli_print_usage(void) { printf("\tir decode []\r\n"); printf("\tir universal \r\n"); printf("\tir universal list \r\n"); - // TODO: Do not hardcode universal remote names + // TODO FL-3496: Do not hardcode universal remote names printf("\tAvailable universal remotes: tv audio ac projector\r\n"); } @@ -211,7 +211,7 @@ static bool infrared_cli_decode_raw_signal( size_t i; for(i = 0; i < raw_signal->timings_size; ++i) { - // TODO: Any infrared_check_decoder_ready() magic? + // TODO FL-3523: Any infrared_check_decoder_ready() magic? const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]); if(message) { diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h index 55b70fc5e..4e69b886e 100644 --- a/applications/main/infrared/infrared_i.h +++ b/applications/main/infrared/infrared_i.h @@ -31,6 +31,7 @@ #include "scenes/infrared_scene.h" #include "views/infrared_progress_view.h" #include "views/infrared_debug_view.h" +#include "views/infrared_move_view.h" #include "rpc/rpc_app.h" @@ -70,6 +71,7 @@ typedef struct { InfraredEditTarget edit_target : 8; InfraredEditMode edit_mode : 8; int32_t current_button_index; + int32_t current_button_index_move_orig; uint32_t last_transmit_time; } InfraredAppState; @@ -95,6 +97,7 @@ struct Infrared { ViewStack* view_stack; InfraredDebugView* debug_view; + InfraredMoveView* move_view; ButtonPanel* button_panel; Loading* loading; @@ -116,6 +119,7 @@ typedef enum { InfraredViewPopup, InfraredViewStack, InfraredViewDebugView, + InfraredViewMove, } InfraredView; typedef enum { diff --git a/applications/main/infrared/infrared_remote.c b/applications/main/infrared/infrared_remote.c index d3dfc2cce..70d1b59ef 100644 --- a/applications/main/infrared/infrared_remote.c +++ b/applications/main/infrared/infrared_remote.c @@ -108,6 +108,15 @@ bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) { return infrared_remote_store(remote); } +void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest) { + furi_assert(index_orig < InfraredButtonArray_size(remote->buttons)); + furi_assert(index_dest < InfraredButtonArray_size(remote->buttons)); + + InfraredRemoteButton* button; + InfraredButtonArray_pop_at(&button, remote->buttons, index_orig); + InfraredButtonArray_push_at(remote->buttons, index_dest, button); +} + bool infrared_remote_store(InfraredRemote* remote) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* ff = flipper_format_file_alloc(storage); diff --git a/applications/main/infrared/infrared_remote.h b/applications/main/infrared/infrared_remote.h index 6eac193d3..47aa77e2e 100644 --- a/applications/main/infrared/infrared_remote.h +++ b/applications/main/infrared/infrared_remote.h @@ -23,6 +23,7 @@ bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* nam bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); bool infrared_remote_delete_button(InfraredRemote* remote, size_t index); +void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest); bool infrared_remote_store(InfraredRemote* remote); bool infrared_remote_load(InfraredRemote* remote, FuriString* path); diff --git a/applications/main/infrared/infrared_signal.c b/applications/main/infrared/infrared_signal.c index 9154dfbf6..3f9a51908 100644 --- a/applications/main/infrared/infrared_signal.c +++ b/applications/main/infrared/infrared_signal.c @@ -208,6 +208,21 @@ void infrared_signal_set_raw_signal( float duty_cycle) { infrared_signal_clear_timings(signal); + // If the frequency is out of bounds, set it to the closest bound same for duty cycle + // TODO: Should we return error instead? Also infrared_signal_is_valid is used only in CLI for some reason?! + if(frequency > INFRARED_MAX_FREQUENCY) { + frequency = INFRARED_MAX_FREQUENCY; + } else if(frequency < INFRARED_MIN_FREQUENCY) { + frequency = INFRARED_MIN_FREQUENCY; + } + if((duty_cycle <= (float)0) || (duty_cycle > (float)1)) { + duty_cycle = (float)0.33; + } + // In case of timings out of bounds we just call return + if((timings_size <= 0) || (timings_size > MAX_TIMINGS_AMOUNT)) { + return; + } + signal->is_raw = true; signal->payload.raw.timings_size = timings_size; diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h index f00a1817c..64f8807f5 100644 --- a/applications/main/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -7,6 +7,7 @@ ADD_SCENE(infrared, edit_delete_done, EditDeleteDone) ADD_SCENE(infrared, edit_button_select, EditButtonSelect) ADD_SCENE(infrared, edit_rename, EditRename) ADD_SCENE(infrared, edit_rename_done, EditRenameDone) +ADD_SCENE(infrared, edit_move, EditMove) ADD_SCENE(infrared, learn, Learn) ADD_SCENE(infrared, learn_done, LearnDone) ADD_SCENE(infrared, learn_enter_name, LearnEnterName) diff --git a/applications/main/infrared/scenes/infrared_scene_edit.c b/applications/main/infrared/scenes/infrared_scene_edit.c index 360ed49b3..02bba7a3f 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit.c +++ b/applications/main/infrared/scenes/infrared_scene_edit.c @@ -3,6 +3,7 @@ typedef enum { SubmenuIndexAddButton, SubmenuIndexRenameButton, + SubmenuIndexMoveButton, SubmenuIndexDeleteButton, SubmenuIndexRenameRemote, SubmenuIndexDeleteRemote, @@ -30,6 +31,12 @@ void infrared_scene_edit_on_enter(void* context) { SubmenuIndexRenameButton, infrared_scene_edit_submenu_callback, context); + submenu_add_item( + submenu, + "Move Button", + SubmenuIndexMoveButton, + infrared_scene_edit_submenu_callback, + context); submenu_add_item( submenu, "Delete Button", @@ -74,6 +81,9 @@ bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) { infrared->app_state.edit_mode = InfraredEditModeRename; scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); consumed = true; + } else if(submenu_index == SubmenuIndexMoveButton) { + scene_manager_next_scene(scene_manager, InfraredSceneEditMove); + consumed = true; } else if(submenu_index == SubmenuIndexDeleteButton) { infrared->app_state.edit_target = InfraredEditTargetButton; infrared->app_state.edit_mode = InfraredEditModeDelete; diff --git a/applications/main/infrared/scenes/infrared_scene_edit_button_select.c b/applications/main/infrared/scenes/infrared_scene_edit_button_select.c index a7f8a2bf7..5f5a1d8fa 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_button_select.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_button_select.c @@ -26,7 +26,6 @@ void infrared_scene_edit_button_select_on_enter(void* context) { infrared_scene_edit_button_select_submenu_callback, context); } - if(button_count && app_state->current_button_index != InfraredButtonIndexNone) { submenu_set_selected_item(submenu, app_state->current_button_index); app_state->current_button_index = InfraredButtonIndexNone; diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c new file mode 100644 index 000000000..370c352db --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -0,0 +1,44 @@ +#include "../infrared_i.h" + +static void infrared_scene_move_button(uint32_t index_old, uint32_t index_new, void* context) { + InfraredRemote* remote = context; + furi_assert(remote); + infrared_remote_move_button(remote, index_old, index_new); +} + +static const char* infrared_scene_get_btn_name(uint32_t index, void* context) { + InfraredRemote* remote = context; + furi_assert(remote); + InfraredRemoteButton* button = infrared_remote_get_button(remote, index); + return (infrared_remote_button_get_name(button)); +} + +void infrared_scene_edit_move_on_enter(void* context) { + Infrared* infrared = context; + InfraredRemote* remote = infrared->remote; + + infrared_move_view_set_callback(infrared->move_view, infrared_scene_move_button); + + uint32_t btn_count = infrared_remote_get_button_count(remote); + infrared_move_view_list_init( + infrared->move_view, btn_count, infrared_scene_get_btn_name, remote); + infrared_move_view_list_update(infrared->move_view); + + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove); +} + +bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + bool consumed = false; + + UNUSED(event); + UNUSED(infrared); + + return consumed; +} + +void infrared_scene_edit_move_on_exit(void* context) { + Infrared* infrared = context; + InfraredRemote* remote = infrared->remote; + infrared_remote_store(remote); +} diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 6eed1ba93..60d9d058b 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -2,13 +2,13 @@ typedef enum { SubmenuIndexUniversalTV, - SubmenuIndexUniversalMonitor, - SubmenuIndexUniversalDigitalSign, SubmenuIndexUniversalProjector, SubmenuIndexUniversalAudio, - SubmenuIndexUniversalLED, SubmenuIndexUniversalAirConditioner, SubmenuIndexUniversalFan, + SubmenuIndexUniversalMonitor, + SubmenuIndexUniversalDigitalSign, + SubmenuIndexUniversalLED, } SubmenuIndex; static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) { @@ -27,20 +27,6 @@ void infrared_scene_universal_on_enter(void* context) { infrared_scene_universal_submenu_callback, context); - submenu_add_item( - submenu, - "Monitors", - SubmenuIndexUniversalMonitor, - infrared_scene_universal_submenu_callback, - context); - - submenu_add_item( - submenu, - "Digital Signs", - SubmenuIndexUniversalDigitalSign, - infrared_scene_universal_submenu_callback, - context); - submenu_add_item( submenu, "Projectors", @@ -55,13 +41,6 @@ void infrared_scene_universal_on_enter(void* context) { infrared_scene_universal_submenu_callback, context); - submenu_add_item( - submenu, - "LEDs", - SubmenuIndexUniversalLED, - infrared_scene_universal_submenu_callback, - context); - submenu_add_item( submenu, "ACs", @@ -76,6 +55,27 @@ void infrared_scene_universal_on_enter(void* context) { infrared_scene_universal_submenu_callback, context); + submenu_add_item( + submenu, + "Monitors", + SubmenuIndexUniversalMonitor, + infrared_scene_universal_submenu_callback, + context); + + submenu_add_item( + submenu, + "Digital Signs", + SubmenuIndexUniversalDigitalSign, + infrared_scene_universal_submenu_callback, + context); + + submenu_add_item( + submenu, + "LEDs", + SubmenuIndexUniversalLED, + infrared_scene_universal_submenu_callback, + context); + submenu_set_selected_item( submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal)); @@ -91,27 +91,27 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexUniversalTV) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalTV); consumed = true; - } else if(event.event == SubmenuIndexUniversalMonitor) { - scene_manager_next_scene(scene_manager, InfraredSceneUniversalMonitor); - consumed = true; - } else if(event.event == SubmenuIndexUniversalDigitalSign) { - scene_manager_next_scene(scene_manager, InfraredSceneUniversalDigitalSign); - consumed = true; } else if(event.event == SubmenuIndexUniversalProjector) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector); consumed = true; } else if(event.event == SubmenuIndexUniversalAudio) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); consumed = true; - } else if(event.event == SubmenuIndexUniversalLED) { - scene_manager_next_scene(scene_manager, InfraredSceneUniversalLED); - consumed = true; } else if(event.event == SubmenuIndexUniversalAirConditioner) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAC); consumed = true; } else if(event.event == SubmenuIndexUniversalFan) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalFan); consumed = true; + } else if(event.event == SubmenuIndexUniversalMonitor) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalMonitor); + consumed = true; + } else if(event.event == SubmenuIndexUniversalDigitalSign) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalDigitalSign); + consumed = true; + } else if(event.event == SubmenuIndexUniversalLED) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalLED); + consumed = true; } scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event); } diff --git a/applications/main/infrared/scenes/infrared_scene_universal_ac.c b/applications/main/infrared/scenes/infrared_scene_universal_ac.c index 58f067735..cde4bee13 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_ac.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_ac.c @@ -1,6 +1,7 @@ #include "../infrared_i.h" #include "common/infrared_scene_universal_common.h" +#include void infrared_scene_universal_ac_on_enter(void* context) { infrared_scene_universal_common_on_enter(context); @@ -18,24 +19,26 @@ void infrared_scene_universal_ac_on_enter(void* context) { i, 0, 0, - 3, - 22, - &I_Off_25x27, - &I_Off_hvr_25x27, + 6, + 15, + &I_off_19x20, + &I_off_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 10, 37, &I_off_text_12x5); infrared_brute_force_add_record(brute_force, i++, "Off"); button_panel_add_item( button_panel, i, 1, 0, - 36, - 22, - &I_Dehumidify_25x27, - &I_Dehumidify_hvr_25x27, + 39, + 15, + &I_dry_19x20, + &I_dry_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 41, 37, &I_dry_text_15x5); infrared_brute_force_add_record(brute_force, i++, "Dh"); button_panel_add_item( button_panel, @@ -43,9 +46,9 @@ void infrared_scene_universal_ac_on_enter(void* context) { 0, 1, 3, - 59, - &I_CoolHi_25x27, - &I_CoolHi_hvr_25x27, + 49, + &I_max_24x23, + &I_max_hover_24x23, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Cool_hi"); @@ -54,39 +57,71 @@ void infrared_scene_universal_ac_on_enter(void* context) { i, 1, 1, - 36, - 59, - &I_HeatHi_25x27, - &I_HeatHi_hvr_25x27, + 37, + 49, + &I_max_24x23, + &I_max_hover_24x23, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Heat_hi"); - button_panel_add_item( - button_panel, - i, - 0, - 2, - 3, - 91, - &I_CoolLo_25x27, - &I_CoolLo_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); + if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { + button_panel_add_item( + button_panel, + i, + 0, + 2, + 3, + 100, + &I_celsius_24x23, + &I_celsius_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + } else { + button_panel_add_item( + button_panel, + i, + 0, + 2, + 3, + 100, + &I_fahren_24x23, + &I_fahren_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + } infrared_brute_force_add_record(brute_force, i++, "Cool_lo"); - button_panel_add_item( - button_panel, - i, - 1, - 2, - 36, - 91, - &I_HeatLo_25x27, - &I_HeatLo_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); + + if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { + button_panel_add_item( + button_panel, + i, + 1, + 2, + 37, + 100, + &I_celsius_24x23, + &I_celsius_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + } else { + button_panel_add_item( + button_panel, + i, + 1, + 2, + 37, + 100, + &I_fahren_24x23, + &I_fahren_hover_24x23, + infrared_scene_universal_common_item_callback, + context); + } infrared_brute_force_add_record(brute_force, i++, "Heat_lo"); - button_panel_add_label(button_panel, 6, 10, FontPrimary, "AC remote"); + button_panel_add_icon(button_panel, 0, 60, &I_cool_30x51); + button_panel_add_icon(button_panel, 34, 60, &I_heat_30x51); + + button_panel_add_label(button_panel, 22, 10, FontPrimary, "ACs"); view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_universal_audio.c b/applications/main/infrared/scenes/infrared_scene_universal_audio.c index 00c86fff4..769aee73b 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_audio.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_audio.c @@ -18,82 +18,88 @@ void infrared_scene_universal_audio_on_enter(void* context) { i, 0, 0, - 3, - 11, - &I_Power_25x27, - &I_Power_hvr_25x27, + 6, + 13, + &I_power_19x20, + &I_power_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 4, 35, &I_power_text_24x5); infrared_brute_force_add_record(brute_force, i++, "Power"); button_panel_add_item( button_panel, i, 1, 0, - 36, - 11, - &I_Mute_25x27, - &I_Mute_hvr_25x27, + 39, + 13, + &I_mute_19x20, + &I_mute_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 39, 35, &I_mute_text_19x5); infrared_brute_force_add_record(brute_force, i++, "Mute"); button_panel_add_item( button_panel, i, 0, 1, - 3, - 41, - &I_Play_25x27, - &I_Play_hvr_25x27, + 6, + 42, + &I_play_19x20, + &I_play_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 6, 64, &I_play_text_19x5); infrared_brute_force_add_record(brute_force, i++, "Play"); button_panel_add_item( button_panel, i, - 1, - 1, - 36, - 41, - &I_Pause_25x27, - &I_Pause_hvr_25x27, + 0, + 2, + 6, + 71, + &I_pause_19x20, + &I_pause_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 4, 93, &I_pause_text_23x5); infrared_brute_force_add_record(brute_force, i++, "Pause"); button_panel_add_item( button_panel, i, 0, - 2, 3, - 71, - &I_TrackPrev_25x27, - &I_TrackPrev_hvr_25x27, + 6, + 101, + &I_prev_19x20, + &I_prev_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 6, 123, &I_prev_text_19x5); infrared_brute_force_add_record(brute_force, i++, "Prev"); button_panel_add_item( button_panel, i, 1, - 2, - 36, - 71, - &I_TrackNext_25x27, - &I_TrackNext_hvr_25x27, + 3, + 39, + 101, + &I_next_19x20, + &I_next_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 39, 123, &I_next_text_19x6); infrared_brute_force_add_record(brute_force, i++, "Next"); button_panel_add_item( button_panel, i, - 0, - 3, - 3, - 101, - &I_Vol_down_25x27, - &I_Vol_down_hvr_25x27, + 1, + 2, + 37, + 77, + &I_voldown_24x21, + &I_voldown_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); @@ -101,16 +107,17 @@ void infrared_scene_universal_audio_on_enter(void* context) { button_panel, i, 1, - 3, - 36, - 101, - &I_Vol_up_25x27, - &I_Vol_up_hvr_25x27, + 1, + 37, + 43, + &I_volup_24x21, + &I_volup_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Vol_up"); - button_panel_add_label(button_panel, 1, 8, FontPrimary, "Mus. remote"); + button_panel_add_label(button_panel, 18, 10, FontPrimary, "Audio"); + button_panel_add_icon(button_panel, 34, 56, &I_vol_ac_text_30x30); view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_universal_digital_sign.c b/applications/main/infrared/scenes/infrared_scene_universal_digital_sign.c index 4e84fc6af..d15aa5d62 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_digital_sign.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_digital_sign.c @@ -18,52 +18,58 @@ void infrared_scene_universal_digital_sign_on_enter(void* context) { i, 0, 0, - 3, - 19, - &I_Power_25x27, - &I_Power_hvr_25x27, + 6, + 24, + &I_power_19x20, + &I_power_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 4, 46, &I_power_text_24x5); infrared_brute_force_add_record(brute_force, i++, "POWER"); + button_panel_add_item( button_panel, i, 1, 0, - 36, - 19, - &I_Input_25x27, - &I_Input_hvr_25x27, + 38, + 24, + &I_input_19x20, + &I_input_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 36, 46, &I_input_text_24x5); infrared_brute_force_add_record(brute_force, i++, "SOURCE"); + button_panel_add_item( button_panel, i, 0, 1, - 3, - 64, - &I_Play_25x27, - &I_Play_hvr_25x27, + 6, + 58, + &I_play_19x20, + &I_play_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 6, 80, &I_play_text_19x5); infrared_brute_force_add_record(brute_force, i++, "PLAY"); + button_panel_add_item( button_panel, i, 1, 1, - 36, - 64, - &I_Stop_25x27, - &I_Stop_hvr_25x27, + 38, + 58, + &I_stop_19x20, + &I_stop_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 38, 80, &I_stop_text_19x5); infrared_brute_force_add_record(brute_force, i++, "STOP"); - button_panel_add_label(button_panel, 6, 11, FontPrimary, "Digital Sign"); - button_panel_add_label(button_panel, 17, 60, FontSecondary, "Control"); + button_panel_add_label(button_panel, 1, 11, FontPrimary, "Digital Signs"); view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_universal_fan.c b/applications/main/infrared/scenes/infrared_scene_universal_fan.c index 967e15c24..4e8834bb5 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_fan.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_fan.c @@ -19,34 +19,38 @@ void infrared_scene_universal_fan_on_enter(void* context) { i, 0, 0, - 3, + 6, 24, - &I_Power_25x27, - &I_Power_hvr_25x27, + &I_power_19x20, + &I_power_hover_19x20, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Power"); + button_panel_add_icon(button_panel, 4, 46, &I_power_text_24x5); + button_panel_add_item( button_panel, i, 1, 0, - 36, + 39, 24, - &I_Mode_25x27, - &I_Mode_hvr_25x27, + &I_mode_19x20, + &I_mode_hover_19x20, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Mode"); + button_panel_add_icon(button_panel, 39, 46, &I_mode_text_20x5); + button_panel_add_item( button_panel, i, - 0, 1, - 3, - 66, - &I_Vol_up_25x27, - &I_Vol_up_hvr_25x27, + 1, + 37, + 55, + &I_volup_24x21, + &I_volup_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Speed_up"); @@ -54,11 +58,11 @@ void infrared_scene_universal_fan_on_enter(void* context) { button_panel, i, 1, - 1, - 36, - 66, - &I_Vol_down_25x27, - &I_Vol_down_hvr_25x27, + 2, + 37, + 89, + &I_voldown_24x21, + &I_voldown_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Speed_dn"); @@ -66,31 +70,32 @@ void infrared_scene_universal_fan_on_enter(void* context) { button_panel, i, 0, - 2, - 3, - 98, - &I_Rotate_25x27, - &I_Rotate_hvr_25x27, + 1, + 6, + 58, + &I_rotate_19x20, + &I_rotate_hover_19x20, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Rotate"); + button_panel_add_icon(button_panel, 4, 80, &I_rotate_text_24x5); + button_panel_add_item( button_panel, i, - 1, + 0, 2, - 36, - 98, - &I_Timer_25x27, - &I_Timer_hvr_25x27, + 6, + 87, + &I_timer_19x20, + &I_timer_hover_19x20, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Timer"); + button_panel_add_icon(button_panel, 4, 109, &I_timer_text_23x5); - button_panel_add_label(button_panel, 5, 11, FontPrimary, "Fan remote"); - button_panel_add_label(button_panel, 20, 63, FontSecondary, "Speed"); - button_panel_add_label(button_panel, 8, 23, FontSecondary, "Pwr"); - button_panel_add_label(button_panel, 40, 23, FontSecondary, "Mod"); + button_panel_add_label(button_panel, 20, 11, FontPrimary, "Fans"); + button_panel_add_icon(button_panel, 34, 68, &I_speed_text_30x30); view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_universal_led.c b/applications/main/infrared/scenes/infrared_scene_universal_led.c index 7f3eac28d..bd5de3bad 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_led.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_led.c @@ -18,52 +18,58 @@ void infrared_scene_universal_led_on_enter(void* context) { i, 0, 0, - 3, - 19, - &I_Power_25x27, - &I_Power_hvr_25x27, + 6, + 24, + &I_power_19x20, + &I_power_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 4, 46, &I_power_text_24x5); infrared_brute_force_add_record(brute_force, i++, "POWER"); + button_panel_add_item( button_panel, i, 1, 0, - 36, - 19, - &I_Flash_25x27, - &I_Flash_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); - infrared_brute_force_add_record(brute_force, i++, "FLASH"); - button_panel_add_item( - button_panel, - i, - 0, - 1, - 3, - 64, - &I_Vol_up_25x27, - &I_Vol_up_hvr_25x27, + 37, + 21, + &I_volup_24x21, + &I_volup_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "BRIGHTNESS+"); + button_panel_add_item( button_panel, i, 1, 1, - 36, - 64, - &I_Vol_down_25x27, - &I_Vol_down_hvr_25x27, + 37, + 55, + &I_voldown_24x21, + &I_voldown_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "BRIGHTNESS-"); - button_panel_add_label(button_panel, 5, 11, FontPrimary, "LED Remote"); - button_panel_add_label(button_panel, 13, 60, FontSecondary, "Brightness"); + button_panel_add_icon(button_panel, 34, 34, &I_bright_text_30x30); + + button_panel_add_item( + button_panel, + i, + 0, + 1, + 6, + 53, + &I_flash_19x20, + &I_flash_hover_19x20, + infrared_scene_universal_common_item_callback, + context); + button_panel_add_icon(button_panel, 6, 75, &I_flash_text_21x5); + infrared_brute_force_add_record(brute_force, i++, "FLASH"); + + button_panel_add_label(button_panel, 21, 11, FontPrimary, "LEDs"); view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_universal_monitor.c b/applications/main/infrared/scenes/infrared_scene_universal_monitor.c index 2effefa00..ab643af2a 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_monitor.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_monitor.c @@ -19,52 +19,58 @@ void infrared_scene_universal_monitor_on_enter(void* context) { i, 0, 0, - 3, + 6, 24, - &I_Power_25x27, - &I_Power_hvr_25x27, + &I_power_19x20, + &I_power_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 4, 46, &I_power_text_24x5); infrared_brute_force_add_record(brute_force, i++, "POWER"); + button_panel_add_item( button_panel, i, 1, 0, - 36, + 38, 24, - &I_Input_25x27, - &I_Input_hvr_25x27, + &I_input_19x20, + &I_input_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 36, 46, &I_input_text_24x5); infrared_brute_force_add_record(brute_force, i++, "SOURCE"); + button_panel_add_item( button_panel, i, 0, 1, - 3, - 66, - &I_Mode_25x27, - &I_Mode_hvr_25x27, + 6, + 58, + &I_mode_19x20, + &I_mode_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 6, 80, &I_menu_text_20x5); infrared_brute_force_add_record(brute_force, i++, "MENU"); + button_panel_add_item( button_panel, i, 1, 1, - 36, - 66, - &I_Exit_25x27, - &I_Exit_hvr_25x27, + 38, + 58, + &I_exit_19x20, + &I_exit_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 39, 80, &I_exit_text_18x5); infrared_brute_force_add_record(brute_force, i++, "EXIT"); - button_panel_add_label(button_panel, 11, 11, FontPrimary, "Monitor"); - button_panel_add_label(button_panel, 19, 60, FontSecondary, "Menu"); + button_panel_add_label(button_panel, 10, 11, FontPrimary, "Monitors"); view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_universal_projector.c b/applications/main/infrared/scenes/infrared_scene_universal_projector.c index 3db3f929f..72b53413c 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_projector.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_projector.c @@ -18,46 +18,49 @@ void infrared_scene_universal_projector_on_enter(void* context) { i, 0, 0, - 3, - 19, - &I_Power_25x27, - &I_Power_hvr_25x27, + 6, + 24, + &I_power_19x20, + &I_power_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 4, 46, &I_power_text_24x5); infrared_brute_force_add_record(brute_force, i++, "Power"); button_panel_add_item( button_panel, i, 1, 0, - 36, - 19, - &I_Mute_25x27, - &I_Mute_hvr_25x27, + 39, + 24, + &I_mute_19x20, + &I_mute_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 39, 46, &I_mute_text_19x5); infrared_brute_force_add_record(brute_force, i++, "Mute"); button_panel_add_item( button_panel, i, - 0, 1, - 3, - 64, - &I_Vol_up_25x27, - &I_Vol_up_hvr_25x27, + 1, + 37, + 55, + &I_volup_24x21, + &I_volup_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Vol_up"); + button_panel_add_item( button_panel, i, 1, - 1, - 36, - 64, - &I_Vol_down_25x27, - &I_Vol_down_hvr_25x27, + 2, + 37, + 89, + &I_voldown_24x21, + &I_voldown_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); @@ -65,29 +68,31 @@ void infrared_scene_universal_projector_on_enter(void* context) { button_panel, i, 0, - 2, - 3, - 101, - &I_Play_25x27, - &I_Play_hvr_25x27, + 1, + 6, + 58, + &I_play_19x20, + &I_play_hover_19x20, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Play"); + button_panel_add_icon(button_panel, 6, 80, &I_play_text_19x5); button_panel_add_item( button_panel, i, - 1, + 0, 2, - 36, - 101, - &I_Pause_25x27, - &I_Pause_hvr_25x27, + 6, + 87, + &I_pause_19x20, + &I_pause_hover_19x20, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Pause"); + button_panel_add_icon(button_panel, 4, 109, &I_pause_text_23x5); - button_panel_add_label(button_panel, 10, 11, FontPrimary, "Projector"); - button_panel_add_label(button_panel, 17, 60, FontSecondary, "Volume"); + button_panel_add_label(button_panel, 7, 11, FontPrimary, "Projectors"); + button_panel_add_icon(button_panel, 34, 68, &I_vol_ac_text_30x30); view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/scenes/infrared_scene_universal_tv.c b/applications/main/infrared/scenes/infrared_scene_universal_tv.c index e21bf8f90..44025a564 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal_tv.c +++ b/applications/main/infrared/scenes/infrared_scene_universal_tv.c @@ -18,77 +18,82 @@ void infrared_scene_universal_tv_on_enter(void* context) { i, 0, 0, - 3, - 19, - &I_Power_25x27, - &I_Power_hvr_25x27, + 6, + 16, + &I_power_19x20, + &I_power_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 4, 38, &I_power_text_24x5); infrared_brute_force_add_record(brute_force, i++, "Power"); button_panel_add_item( button_panel, i, 1, 0, - 36, - 19, - &I_Mute_25x27, - &I_Mute_hvr_25x27, + 39, + 16, + &I_mute_19x20, + &I_mute_hover_19x20, infrared_scene_universal_common_item_callback, context); + button_panel_add_icon(button_panel, 39, 38, &I_mute_text_19x5); + + button_panel_add_icon(button_panel, 0, 66, &I_ch_text_31x34); + button_panel_add_icon(button_panel, 35, 66, &I_vol_tv_text_29x34); + infrared_brute_force_add_record(brute_force, i++, "Mute"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 38, + 53, + &I_volup_24x21, + &I_volup_hover_24x21, + infrared_scene_universal_common_item_callback, + context); + + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); button_panel_add_item( button_panel, i, 0, 1, 3, - 66, - &I_Vol_up_25x27, - &I_Vol_up_hvr_25x27, - infrared_scene_universal_common_item_callback, - context); - infrared_brute_force_add_record(brute_force, i++, "Vol_up"); - button_panel_add_item( - button_panel, - i, - 1, - 1, - 36, - 66, - &I_Up_25x27, - &I_Up_hvr_25x27, + 53, + &I_ch_up_24x21, + &I_ch_up_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Ch_next"); button_panel_add_item( button_panel, i, - 0, + 1, 2, - 3, - 98, - &I_Vol_down_25x27, - &I_Vol_down_hvr_25x27, + 38, + 91, + &I_voldown_24x21, + &I_voldown_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); button_panel_add_item( button_panel, i, - 1, + 0, 2, - 36, - 98, - &I_Down_25x27, - &I_Down_hvr_25x27, + 3, + 91, + &I_ch_down_24x21, + &I_ch_down_hover_24x21, infrared_scene_universal_common_item_callback, context); infrared_brute_force_add_record(brute_force, i++, "Ch_prev"); - button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote"); - button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); - button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch"); + button_panel_add_label(button_panel, 22, 10, FontPrimary, "TVs"); view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); diff --git a/applications/main/infrared/views/infrared_move_view.c b/applications/main/infrared/views/infrared_move_view.c new file mode 100644 index 000000000..d838a5f82 --- /dev/null +++ b/applications/main/infrared/views/infrared_move_view.c @@ -0,0 +1,215 @@ +#include "infrared_move_view.h" + +#include +#include + +#include +#include + +#define LIST_ITEMS 4U +#define LIST_LINE_H 13U +#define HEADER_H 12U +#define MOVE_X_OFFSET 5U + +struct InfraredMoveView { + View* view; + InfraredMoveCallback move_cb; + void* cb_context; +}; + +typedef struct { + const char** btn_names; + uint32_t btn_number; + int32_t list_offset; + int32_t item_idx; + bool is_moving; + + InfraredMoveGetItemCallback get_item_cb; +} InfraredMoveViewModel; + +static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) { + InfraredMoveViewModel* model = _model; + + UNUSED(model); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move"); + + bool show_scrollbar = model->btn_number > LIST_ITEMS; + + canvas_set_font(canvas, FontSecondary); + + for(uint32_t i = 0; i < MIN(model->btn_number, LIST_ITEMS); i++) { + int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->btn_number, 0u); + uint8_t x_offset = (model->is_moving && model->item_idx == idx) ? MOVE_X_OFFSET : 0; + uint8_t y_offset = HEADER_H + i * LIST_LINE_H; + uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1); + + canvas_set_color(canvas, ColorBlack); + if(model->item_idx == idx) { + canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, x_offset, y_offset); + canvas_draw_dot(canvas, x_offset + 1, y_offset); + canvas_draw_dot(canvas, x_offset, y_offset + 1); + canvas_draw_dot(canvas, x_offset, y_offset + LIST_LINE_H - 1); + canvas_draw_dot(canvas, box_end_x - 1, y_offset); + canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1); + } + canvas_draw_str_aligned( + canvas, x_offset + 3, y_offset + 3, AlignLeft, AlignTop, model->btn_names[idx]); + } + + if(show_scrollbar) { + elements_scrollbar_pos( + canvas, + canvas_width(canvas), + HEADER_H, + canvas_height(canvas) - HEADER_H, + model->item_idx, + model->btn_number); + } +} + +static void update_list_offset(InfraredMoveViewModel* model) { + int32_t bounds = model->btn_number > (LIST_ITEMS - 1) ? 2 : model->btn_number; + + if((model->btn_number > (LIST_ITEMS - 1)) && + (model->item_idx >= ((int32_t)model->btn_number - 1))) { + model->list_offset = model->item_idx - (LIST_ITEMS - 1); + } else if(model->list_offset < model->item_idx - bounds) { + model->list_offset = CLAMP( + model->item_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)model->btn_number - bounds, 0); + } else if(model->list_offset > model->item_idx - bounds) { + model->list_offset = CLAMP(model->item_idx - 1, (int32_t)model->btn_number - bounds, 0); + } +} + +static bool infrared_move_view_input_callback(InputEvent* event, void* context) { + InfraredMoveView* move_view = context; + + bool consumed = false; + + if(((event->type == InputTypeShort || event->type == InputTypeRepeat)) && + ((event->key == InputKeyUp) || (event->key == InputKeyDown))) { + bool is_moving = false; + uint32_t index_old = 0; + uint32_t index_new = 0; + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + is_moving = model->is_moving; + index_old = model->item_idx; + if(event->key == InputKeyUp) { + if(model->item_idx <= 0) { + model->item_idx = model->btn_number; + } + model->item_idx--; + } else if(event->key == InputKeyDown) { + model->item_idx++; + if(model->item_idx >= (int32_t)(model->btn_number)) { + model->item_idx = 0; + } + } + index_new = model->item_idx; + update_list_offset(model); + }, + !is_moving); + if((is_moving) && (move_view->move_cb)) { + move_view->move_cb(index_old, index_new, move_view->cb_context); + infrared_move_view_list_update(move_view); + } + consumed = true; + } + + if((event->key == InputKeyOk) && (event->type == InputTypeShort)) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { model->is_moving = !(model->is_moving); }, + true); + consumed = true; + } + return consumed; +} + +static void infrared_move_view_on_exit(void* context) { + furi_assert(context); + InfraredMoveView* move_view = context; + + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + if(model->btn_names) { + free(model->btn_names); + model->btn_names = NULL; + } + model->btn_number = 0; + model->get_item_cb = NULL; + }, + false); + move_view->cb_context = NULL; +} + +void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback) { + furi_assert(move_view); + move_view->move_cb = callback; +} + +void infrared_move_view_list_init( + InfraredMoveView* move_view, + uint32_t item_count, + InfraredMoveGetItemCallback load_cb, + void* context) { + furi_assert(move_view); + move_view->cb_context = context; + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + furi_assert(model->btn_names == NULL); + model->btn_names = malloc(sizeof(char*) * item_count); + model->btn_number = item_count; + model->get_item_cb = load_cb; + }, + false); +} + +void infrared_move_view_list_update(InfraredMoveView* move_view) { + furi_assert(move_view); + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + for(uint32_t i = 0; i < model->btn_number; i++) { + if(!model->get_item_cb) break; + model->btn_names[i] = model->get_item_cb(i, move_view->cb_context); + } + }, + true); +} + +InfraredMoveView* infrared_move_view_alloc(void) { + InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView)); + move_view->view = view_alloc(); + view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel)); + view_set_draw_callback(move_view->view, infrared_move_view_draw_callback); + view_set_input_callback(move_view->view, infrared_move_view_input_callback); + view_set_exit_callback(move_view->view, infrared_move_view_on_exit); + view_set_context(move_view->view, move_view); + return move_view; +} + +void infrared_move_view_free(InfraredMoveView* move_view) { + view_free(move_view->view); + free(move_view); +} + +View* infrared_move_view_get_view(InfraredMoveView* move_view) { + return move_view->view; +} diff --git a/applications/main/infrared/views/infrared_move_view.h b/applications/main/infrared/views/infrared_move_view.h new file mode 100644 index 000000000..b9b0cd864 --- /dev/null +++ b/applications/main/infrared/views/infrared_move_view.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +typedef struct InfraredMoveView InfraredMoveView; + +typedef void (*InfraredMoveCallback)(uint32_t index_old, uint32_t index_new, void* context); + +typedef const char* (*InfraredMoveGetItemCallback)(uint32_t index, void* context); + +InfraredMoveView* infrared_move_view_alloc(void); + +void infrared_move_view_free(InfraredMoveView* debug_view); + +View* infrared_move_view_get_view(InfraredMoveView* debug_view); + +void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback); + +void infrared_move_view_list_init( + InfraredMoveView* move_view, + uint32_t item_count, + InfraredMoveGetItemCallback load_cb, + void* context); + +void infrared_move_view_list_update(InfraredMoveView* move_view); \ No newline at end of file diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 9489b1b1f..76eeaf9d8 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -8,6 +8,7 @@ App( stack_size=2 * 1024, order=20, fap_icon="icon.png", + fap_category="RFID", ) App( diff --git a/applications/main/lfrfid/lfrfid.c b/applications/main/lfrfid/lfrfid.c index fe88cd91d..eccc72e4d 100644 --- a/applications/main/lfrfid/lfrfid.c +++ b/applications/main/lfrfid/lfrfid.c @@ -1,6 +1,6 @@ #include "lfrfid_i.h" #include -#include +#include static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -193,12 +193,9 @@ int32_t lfrfid_app(char* args) { scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); dolphin_deed(DolphinDeedRfidEmulate); } else { - // TODO: exit properly - lfrfid_free(app); - return 0; + view_dispatcher_stop(app->view_dispatcher); } } - } else { view_dispatcher_attach_to_gui( app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index a22d64af5..0e7f74379 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -8,6 +8,7 @@ App( stack_size=5 * 1024, order=30, fap_icon="icon.png", + fap_category="NFC", ) App( diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index aa932a3d8..00feb8484 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -14,4 +14,4 @@ enum NfcCustomEvent { NfcCustomEventRpcSessionClose, NfcCustomEventUpdateLog, NfcCustomEventSaveShadow, -}; +}; \ No newline at end of file diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 1b83ffad7..b8b2542c5 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -1,7 +1,7 @@ #include "nfc_i.h" #include #include -#include +#include bool nfc_custom_event_callback(void* context, uint32_t event) { furi_assert(context); diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 0b7e75475..e96174381 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -146,7 +146,7 @@ static void nfc_cli_apdu(Cli* cli, FuriString* args) { resp_size = (tx_rx.rx_bits / 8) * 2; if(!resp_size) { printf("No response\r\n"); - break; + continue; } resp_buffer = malloc(resp_size); uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size); diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index f11d14798..6232aaf30 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -3,6 +3,7 @@ ADD_SCENE(nfc, read, Read) ADD_SCENE(nfc, saved_menu, SavedMenu) ADD_SCENE(nfc, extra_actions, ExtraActions) ADD_SCENE(nfc, set_type, SetType) +ADD_SCENE(nfc, set_type_mf_uid, SetTypeMfUid) ADD_SCENE(nfc, set_sak, SetSak) ADD_SCENE(nfc, set_atqa, SetAtqa) ADD_SCENE(nfc, set_uid, SetUid) diff --git a/applications/main/nfc/scenes/nfc_scene_file_select.c b/applications/main/nfc/scenes/nfc_scene_file_select.c index 374a933d1..ce7ec92f4 100644 --- a/applications/main/nfc/scenes/nfc_scene_file_select.c +++ b/applications/main/nfc/scenes/nfc_scene_file_select.c @@ -3,6 +3,8 @@ void nfc_scene_file_select_on_enter(void* context) { Nfc* nfc = context; + nfc_device_data_clear(&nfc->dev->dev_data); + // Process file_select return nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc); if(!furi_string_size(nfc->dev->load_path)) { diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index a7b97aac0..a432e69f7 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -58,7 +58,8 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(strcmp(nfc->dev->dev_name, "") != 0) { nfc_device_delete(nfc->dev, true); } - if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid)) { + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetUid) && + (!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetTypeMfUid))) { nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; } strlcpy(nfc->dev->dev_name, nfc->text_store, strlen(nfc->text_store) + 1); diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index cadf2eb69..0da633c63 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -4,6 +4,7 @@ enum SubmenuIndex { SubmenuIndexNFCA4, SubmenuIndexNFCA7, + SubmenuIndexMFClassicCustomUID, SubmenuIndexGeneratorsStart, }; @@ -23,6 +24,12 @@ void nfc_scene_set_type_on_enter(void* context) { submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc); submenu_add_item( submenu, "NFC-A 4-bytes UID", SubmenuIndexNFCA4, nfc_scene_set_type_submenu_callback, nfc); + submenu_add_item( + submenu, + "Mifare Classic Custom UID", + SubmenuIndexMFClassicCustomUID, + nfc_scene_set_type_submenu_callback, + nfc); // Generators int i = SubmenuIndexGeneratorsStart; @@ -49,6 +56,10 @@ bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { nfc->dev->format = NfcDeviceSaveFormatUid; scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); consumed = true; + } else if(event.event == SubmenuIndexMFClassicCustomUID) { + nfc_device_clear(nfc->dev); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetTypeMfUid); + consumed = true; } else { nfc_device_clear(nfc->dev); nfc->generator = nfc_generators[event.event - SubmenuIndexGeneratorsStart]; diff --git a/applications/main/nfc/scenes/nfc_scene_set_type_mf_uid.c b/applications/main/nfc/scenes/nfc_scene_set_type_mf_uid.c new file mode 100644 index 000000000..55919500a --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_set_type_mf_uid.c @@ -0,0 +1,103 @@ +#include "../nfc_i.h" +#include "lib/nfc/helpers/nfc_generators.h" + +enum SubmenuIndex { + SubmenuIndexMFC1k4b, + SubmenuIndexMFC4k4b, + SubmenuIndexMFC1k7b, + SubmenuIndexMFC4k7b, + SubmenuIndexMFCMini, +}; + +static const NfcGenerator ganeator_gag = { + .name = "Mifare Classic Custom UID", + .generator_func = NULL, +}; + +void nfc_scene_set_type_mf_uid_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_set_type_mf_uid_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, + "Mifare Classic 1k 4byte UID", + SubmenuIndexMFC1k4b, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic 4k 4byte UID", + SubmenuIndexMFC4k4b, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic 1k 7byte UID", + SubmenuIndexMFC1k7b, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic 4k 7byte UID", + SubmenuIndexMFC4k7b, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic Mini", + SubmenuIndexMFCMini, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_set_type_mf_uid_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + bool correct_index = false; + MfClassicType mf_type = MfClassicType1k; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMFC1k4b) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + mf_type = MfClassicType1k; + correct_index = true; + } else if(event.event == SubmenuIndexMFC1k7b) { + nfc->dev->dev_data.nfc_data.uid_len = 7; + mf_type = MfClassicType1k; + correct_index = true; + } else if(event.event == SubmenuIndexMFC4k4b) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + mf_type = MfClassicType4k; + correct_index = true; + } else if(event.event == SubmenuIndexMFC4k7b) { + nfc->dev->dev_data.nfc_data.uid_len = 7; + mf_type = MfClassicType4k; + correct_index = true; + } else if(event.event == SubmenuIndexMFCMini) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + mf_type = MfClassicTypeMini; + correct_index = true; + } + if(correct_index) { + nfc->generator = &ganeator_gag; + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSetTypeMfUid, mf_type); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_set_type_mf_uid_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 54606b68e..80ea5f6d0 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -35,6 +35,21 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); consumed = true; } + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetTypeMfUid)) { + MfClassicType mf_type = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSetTypeMfUid); + if(mf_type > MfClassicTypeMini) { + furi_crash("Nfc unknown type"); + } + nfc_generate_mf_classic_ext( + &nfc->dev->dev_data, + nfc->dev_edit_data.uid_len, + mf_type, + false, + nfc->dev_edit_data.uid); + scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); + consumed = true; + } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); consumed = true; diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index edc86b38d..cc0da1533 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -19,6 +19,7 @@ App( order=10, fap_libs=["hwdrivers"], fap_icon="icon.png", + fap_category="Sub-GHz", ) App( diff --git a/applications/main/subghz/helpers/subghz_txrx.c b/applications/main/subghz/helpers/subghz_txrx.c index d878c0e04..5c36a781a 100644 --- a/applications/main/subghz/helpers/subghz_txrx.c +++ b/applications/main/subghz/helpers/subghz_txrx.c @@ -336,7 +336,6 @@ static void subghz_txrx_tx_stop(SubGhzTxRx* instance) { } subghz_txrx_idle(instance); subghz_txrx_speaker_off(instance); - //Todo: Show message } FlipperFormat* subghz_txrx_get_fff_data(SubGhzTxRx* instance) { @@ -629,13 +628,16 @@ const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance) { return subghz_devices_get_name(instance->radio_device); } -bool subghz_txrx_radio_device_is_frequecy_valid(SubGhzTxRx* instance, uint32_t frequency) { +bool subghz_txrx_radio_device_is_frequency_valid(SubGhzTxRx* instance, uint32_t frequency) { furi_assert(instance); return subghz_devices_is_frequency_valid(instance->radio_device, frequency); } -bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t frequency) { +bool subghz_txrx_radio_device_is_tx_allowed(SubGhzTxRx* instance, uint32_t frequency) { + // TODO: Remake this function to check if the frequency is allowed on specific module - for modules not based on CC1101 furi_assert(instance); + UNUSED(frequency); + /* furi_assert(instance->txrx_state != SubGhzTxRxStateSleep); subghz_devices_idle(instance->radio_device); @@ -645,6 +647,8 @@ bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t freque subghz_devices_idle(instance->radio_device); return ret; + */ + return true; } void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state) { diff --git a/applications/main/subghz/helpers/subghz_txrx.h b/applications/main/subghz/helpers/subghz_txrx.h index 76c7c8ead..12fed2a9b 100644 --- a/applications/main/subghz/helpers/subghz_txrx.h +++ b/applications/main/subghz/helpers/subghz_txrx.h @@ -334,9 +334,9 @@ const char* subghz_txrx_radio_device_get_name(SubGhzTxRx* instance); * @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); +bool subghz_txrx_radio_device_is_frequency_valid(SubGhzTxRx* instance, uint32_t frequency); -bool subghz_txrx_radio_device_is_tx_alowed(SubGhzTxRx* instance, uint32_t frequency); +bool subghz_txrx_radio_device_is_tx_allowed(SubGhzTxRx* instance, uint32_t frequency); void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state); bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance); diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c index 4854bc882..a83e17da6 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -30,7 +30,7 @@ bool subghz_txrx_gen_data_protocol( subghz_receiver_search_decoder_base_by_name(instance->receiver, protocol_name); if(instance->decoder_result == NULL) { - //TODO: Error + //TODO FL-3502: Error // furi_string_set(error_str, "Protocol not\nfound!"); // scene_manager_next_scene(scene_manager, SubGhzSceneShowErrorSub); FURI_LOG_E(TAG, "Protocol not found!"); @@ -199,7 +199,7 @@ bool subghz_txrx_gen_faac_slh_protocol( uint32_t frequency, uint32_t serial, uint8_t btn, - uint16_t cnt, + uint32_t cnt, uint32_t seed, const char* manufacture_name) { SubGhzTxRx* txrx = context; diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h index dc7dfbe7e..e0ebec685 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h @@ -88,7 +88,7 @@ bool subghz_txrx_gen_faac_slh_protocol( uint32_t frequency, uint32_t serial, uint8_t btn, - uint16_t cnt, + uint32_t cnt, uint32_t seed, const char* manufacture_name); diff --git a/applications/main/subghz/scenes/subghz_scene_radio_settings.c b/applications/main/subghz/scenes/subghz_scene_radio_settings.c index c0f4f22f3..6f2b99b26 100644 --- a/applications/main/subghz/scenes/subghz_scene_radio_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_radio_settings.c @@ -41,6 +41,14 @@ const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { "+5", "+10", }; +const uint32_t debug_counter_val[DEBUG_COUNTER_COUNT] = { + 1, + 2, + 3, + 4, + 5, + 10, +}; static void subghz_scene_radio_settings_set_device(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); @@ -69,62 +77,28 @@ static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, debug_counter_text[index]); - - switch(index) { - case 0: - furi_hal_subghz_set_rolling_counter_mult(1); - break; - case 1: - furi_hal_subghz_set_rolling_counter_mult(2); - break; - case 2: - furi_hal_subghz_set_rolling_counter_mult(3); - break; - case 3: - furi_hal_subghz_set_rolling_counter_mult(4); - break; - case 4: - furi_hal_subghz_set_rolling_counter_mult(5); - break; - case 5: - furi_hal_subghz_set_rolling_counter_mult(10); - break; - default: - break; - } + furi_hal_subghz_set_rolling_counter_mult(debug_counter_val[index]); } -// static void subghz_scene_receiver_config_set_ext_mod_power(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, ext_mod_power_text[index]); - -// furi_hal_subghz_set_external_power_disable(index == 1); -// if(index == 1) { -// furi_hal_subghz_disable_ext_power(); -// } else { -// furi_hal_subghz_enable_ext_power(); -// } - -// subghz->last_settings->external_module_power_5v_disable = index == 1; -// subghz_last_settings_save(subghz->last_settings); -// } - static void subghz_scene_reciever_config_set_ext_mod_power_amp_text(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, ext_mod_power_amp_text[index]); - if(index == 1) { - furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); - } else { - furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog); - } - subghz->last_settings->external_module_power_amp = index == 1; + + // Set globally in furi hal + furi_hal_subghz_set_ext_power_amp(subghz->last_settings->external_module_power_amp); + subghz_last_settings_save(subghz->last_settings); + + // reinit external device + const SubGhzRadioDeviceType current = subghz_txrx_radio_device_get(subghz->txrx); + if(current != SubGhzRadioDeviceTypeInternal) { + subghz_txrx_radio_device_set(subghz->txrx, SubGhzRadioDeviceTypeInternal); + subghz_txrx_radio_device_set(subghz->txrx, current); + } } static void subghz_scene_receiver_config_set_timestamp_file_names(VariableItem* item) { @@ -161,7 +135,7 @@ void subghz_scene_radio_settings_on_enter(void* context) { item = variable_item_list_add( variable_item_list, - "Ext high power", + "Ext Power Amp", EXT_MOD_POWER_AMP_COUNT, subghz_scene_reciever_config_set_ext_mod_power_amp_text, subghz); @@ -171,7 +145,7 @@ void subghz_scene_radio_settings_on_enter(void* context) { item = variable_item_list_add( variable_item_list, - "Time in names", + "Time In Names", TIMESTAMP_NAMES_COUNT, subghz_scene_receiver_config_set_timestamp_file_names, subghz); @@ -179,59 +153,18 @@ void subghz_scene_radio_settings_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, timestamp_names_text[value_index]); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - item = variable_item_list_add( - variable_item_list, - "Counter incr.", - DEBUG_COUNTER_COUNT, - subghz_scene_receiver_config_set_debug_counter, - subghz); - switch(furi_hal_subghz_get_rolling_counter_mult()) { - case 1: - value_index = 0; - break; - case 2: - value_index = 1; - break; - case 3: - value_index = 2; - break; - case 4: - value_index = 3; - break; - case 5: - value_index = 4; - break; - case 10: - value_index = 5; - break; - default: - break; - } - } else { - item = variable_item_list_add( - variable_item_list, - "Counter incr.", - 3, - subghz_scene_receiver_config_set_debug_counter, - subghz); - switch(furi_hal_subghz_get_rolling_counter_mult()) { - case 1: - value_index = 0; - break; - case 2: - value_index = 1; - break; - case 3: - value_index = 2; - break; - default: - // Reset to default value - value_index = 0; - furi_hal_subghz_set_rolling_counter_mult(1); - break; - } - } + item = variable_item_list_add( + variable_item_list, + "Counter Incr.", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? DEBUG_COUNTER_COUNT : 3, + subghz_scene_receiver_config_set_debug_counter, + subghz); + value_index = value_index_uint32( + furi_hal_subghz_get_rolling_counter_mult(), + debug_counter_val, + furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? DEBUG_COUNTER_COUNT : 3); + furi_hal_subghz_set_rolling_counter_mult(debug_counter_val[value_index]); + variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, debug_counter_text[value_index]); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index ac09ebcea..6b58efcfb 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -167,7 +167,7 @@ void subghz_scene_receiver_on_enter(void* context) { } furi_string_free(item_name); furi_string_free(item_time); - subghz_scene_receiver_update_statusbar(subghz); + subghz_view_receiver_set_callback( subghz->subghz_receiver, subghz_scene_receiver_callback, subghz); subghz_txrx_set_rx_calback(subghz->txrx, subghz_scene_add_to_history_callback, subghz); @@ -182,6 +182,8 @@ void subghz_scene_receiver_on_enter(void* context) { furi_check( subghz_txrx_load_decoder_by_name_protocol(subghz->txrx, SUBGHZ_PROTOCOL_BIN_RAW_NAME)); + subghz_scene_receiver_update_statusbar(subghz); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index fc1c1a8e9..d77d93b90 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -25,7 +25,7 @@ static bool subghz_scene_receiver_info_update_parser(void* context) { if(subghz_txrx_load_decoder_by_name_protocol( subghz->txrx, subghz_history_get_protocol_name(subghz->history, subghz->idx_menu_chosen))) { - //todo we are trying to deserialize without checking for errors, since it is assumed that we just received this signal + // we are trying to deserialize without checking for errors, since it is assumed that we just received this chignal subghz_protocol_decoder_base_deserialize( subghz_txrx_get_decoder(subghz->txrx), subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen)); diff --git a/applications/main/subghz/scenes/subghz_scene_set_seed.c b/applications/main/subghz/scenes/subghz_scene_set_seed.c index e6850d1e3..e5778b84f 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_seed.c +++ b/applications/main/subghz/scenes/subghz_scene_set_seed.c @@ -45,6 +45,13 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) { seed = subghz->secure_data->seed[0] << 24 | subghz->secure_data->seed[1] << 16 | subghz->secure_data->seed[2] << 8 | subghz->secure_data->seed[3]; + if(seed == 0) { + furi_string_set(subghz->error_str, "Seed value\ncan not be 0."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + consumed = true; + break; + } + generated_protocol = subghz_txrx_gen_keeloq_bft_protocol( subghz->txrx, "AM650", @@ -73,6 +80,12 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) { seed = subghz->secure_data->seed[0] << 24 | subghz->secure_data->seed[1] << 16 | subghz->secure_data->seed[2] << 8 | subghz->secure_data->seed[3]; + if(seed == 0) { + furi_string_set(subghz->error_str, "Seed value\ncan not be 0."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + consumed = true; + break; + } if(state == SubmenuIndexFaacSLH_433) { generated_protocol = subghz_txrx_gen_faac_slh_protocol( subghz->txrx, @@ -108,8 +121,14 @@ bool subghz_scene_set_seed_on_event(void* context, SceneManagerEvent event) { } } + // Reset Seed, Fix, Cnt in secure data after successful or unsuccessful generation + memset(subghz->secure_data->seed, 0, sizeof(subghz->secure_data->seed)); + memset(subghz->secure_data->cnt, 0, sizeof(subghz->secure_data->cnt)); + memset(subghz->secure_data->fix, 0, sizeof(subghz->secure_data->fix)); + if(generated_protocol) { subghz_file_name_clear(subghz); + scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneSetType, SubGhzCustomEventManagerSet); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName); diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 64e927250..e5a92608e 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -611,7 +611,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { break; case SubmenuIndexSomfyTelis: generated_protocol = subghz_txrx_gen_somfy_telis_protocol( - subghz->txrx, "AM650", 433920000, key & 0x00FFFFFF, 0x2, 0x0003); + subghz->txrx, "AM650", 433420000, key & 0x00FFFFFF, 0x2, 0x0003); break; case SubmenuIndexDoorHan_433_92: generated_protocol = subghz_txrx_gen_keeloq_protocol( diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index c8f513759..b1e0982eb 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -4,7 +4,7 @@ #include #include "subghz_i.h" #include -#include +#include #include #define TAG "SubGhzApp" diff --git a/applications/main/subghz/subghz_extended_freq.c b/applications/main/subghz/subghz_extended_freq.c index bbd44f639..488b3312d 100644 --- a/applications/main/subghz/subghz_extended_freq.c +++ b/applications/main/subghz/subghz_extended_freq.c @@ -1,6 +1,7 @@ #include #include #include +#include #include void subghz_extended_freq() { @@ -16,4 +17,14 @@ void subghz_extended_freq() { flipper_format_free(file); furi_record_close(RECORD_STORAGE); + + // Load external module power amp setting (TODO: move to other place) + // TODO: Disable this when external module is not CC1101 E07 + SubGhzLastSettings* last_settings = subghz_last_settings_alloc(); + subghz_last_settings_load(last_settings, 0); + + // Set globally in furi hal + furi_hal_subghz_set_ext_power_amp(last_settings->external_module_power_amp); + + subghz_last_settings_free(last_settings); } diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index f91f128cf..5cb33fd2b 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -115,14 +115,15 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { break; } - if(!subghz_txrx_radio_device_is_frequecy_valid(subghz->txrx, temp_data32)) { + if(!subghz_txrx_radio_device_is_frequency_valid(subghz->txrx, temp_data32)) { FURI_LOG_E(TAG, "Frequency not supported on chosen radio module"); load_key_state = SubGhzLoadKeyStateUnsuportedFreq; break; } - if(!subghz_txrx_radio_device_is_tx_alowed(subghz->txrx, temp_data32)) { - FURI_LOG_E(TAG, "This frequency can only be used for RX on chosen radio module"); + // TODO: use different frequency allowed lists for differnet modules (non cc1101) + if(!furi_hal_subghz_is_tx_allowed(temp_data32)) { + FURI_LOG_E(TAG, "This frequency can only be used for RX"); load_key_state = SubGhzLoadKeyStateOnlyRx; break; } @@ -141,7 +142,7 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); if(!strcmp(furi_string_get_cstr(temp_str), "CUSTOM")) { - //Todo add Custom_preset_module + //TODO FL-3551: add Custom_preset_module //delete preset if it already exists subghz_setting_delete_custom_preset(setting, furi_string_get_cstr(temp_str)); //load custom preset from file @@ -308,10 +309,14 @@ bool subghz_save_protocol_to_file( if(!storage_simply_remove(storage, dev_file_name)) { break; } - //ToDo check Write + stream_seek(flipper_format_stream, 0, StreamOffsetFromStart); stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); + if(storage_common_stat(storage, dev_file_name, NULL) != FSE_OK) { + break; + } + saved = true; } while(0); furi_string_free(file_dir); diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 7b9937ef8..465312b19 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -19,8 +19,8 @@ #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER "ExtPower" -#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP "ExtPowerAmp" #define SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES "TimestampNames" +#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP "ExtPowerAmp" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -82,13 +82,13 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count 1); flipper_format_read_bool( fff_data_file, - SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP, - (bool*)&temp_external_module_power_amp, + SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, + (bool*)&temp_timestamp_file_names, 1); flipper_format_read_bool( fff_data_file, - SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, - (bool*)&temp_timestamp_file_names, + SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP, + (bool*)&temp_external_module_power_amp, 1); } else { @@ -103,8 +103,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; instance->external_module_enabled = false; - instance->external_module_power_amp = false; instance->timestamp_file_names = false; + instance->external_module_power_amp = false; } else { instance->frequency = temp_frequency; @@ -127,8 +127,12 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->timestamp_file_names = temp_timestamp_file_names; + // External power amp CC1101 instance->external_module_power_amp = temp_external_module_power_amp; + // Set globally in furi hal + furi_hal_subghz_set_ext_power_amp(instance->external_module_power_amp); + /*/} else { instance->preset = temp_preset; }*/ @@ -201,15 +205,15 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { } if(!flipper_format_insert_or_update_bool( file, - SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP, - &instance->external_module_power_amp, + SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, + &instance->timestamp_file_names, 1)) { break; } if(!flipper_format_insert_or_update_bool( file, - SUBGHZ_LAST_SETTING_FIELD_TIMESTAMP_FILE_NAMES, - &instance->timestamp_file_names, + SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER_AMP, + &instance->external_module_power_amp, 1)) { break; } diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 7800c9081..9f39bb0d6 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -315,7 +315,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { uint32_t frequency_candidate = model->history_frequency[model->selected_index]; if(frequency_candidate == 0 || // !furi_hal_subghz_is_frequency_valid(frequency_candidate) || - !subghz_txrx_radio_device_is_frequecy_valid( + !subghz_txrx_radio_device_is_frequency_valid( instance->txrx, frequency_candidate) || prev_freq_to_save == frequency_candidate) { frequency_candidate = 0; @@ -339,7 +339,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { uint32_t frequency_candidate = subghz_frequency_find_correct(model->frequency); if(frequency_candidate == 0 || // !furi_hal_subghz_is_frequency_valid(frequency_candidate) || - !subghz_txrx_radio_device_is_frequecy_valid( + !subghz_txrx_radio_device_is_frequency_valid( instance->txrx, frequency_candidate) || prev_freq_to_save == frequency_candidate) { frequency_candidate = 0; @@ -356,7 +356,7 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { uint32_t frequency_candidate = subghz_frequency_find_correct(model->frequency); if(frequency_candidate == 0 || // !furi_hal_subghz_is_frequency_valid(frequency_candidate) || - !subghz_txrx_radio_device_is_frequecy_valid( + !subghz_txrx_radio_device_is_frequency_valid( instance->txrx, frequency_candidate) || prev_freq_to_save == frequency_candidate) { frequency_candidate = 0; diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam index 3bab607cd..a60f0721f 100644 --- a/applications/main/u2f/application.fam +++ b/applications/main/u2f/application.fam @@ -7,4 +7,5 @@ App( icon="A_U2F_14", order=80, fap_icon="icon.png", + fap_category="USB", ) diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c index 34f037aed..8ee0b4a2a 100644 --- a/applications/main/u2f/u2f_data.c +++ b/applications/main/u2f/u2f_data.c @@ -8,7 +8,7 @@ #define TAG "U2F" #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2 -#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE 11 +#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT #define U2F_CERT_STOCK 0 // Stock certificate, private key is encrypted with factory key #define U2F_CERT_USER 1 // User certificate, private key is encrypted with unique key @@ -130,7 +130,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { // Generate random IV furi_hal_random_fill_buf(iv, 16); - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -139,7 +139,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -172,8 +172,8 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { uint8_t key_slot = 0; uint32_t version = 0; - // Check if unique key exists in secure eclave(typo?) and generate it if missing - if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; + // Check if unique key exists in secure eclave and generate it if missing + if(!furi_hal_crypto_enclave_ensure_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; FuriString* filetype; filetype = furi_string_alloc(); @@ -220,7 +220,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -231,7 +231,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } else { if(!flipper_format_read_hex(flipper_format, "Data", cert_key, 32)) { FURI_LOG_E(TAG, "Missing data"); @@ -286,7 +286,7 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Missing data"); break; } - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -296,7 +296,7 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); state = true; } while(0); } @@ -318,7 +318,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { furi_hal_random_fill_buf(iv, 16); furi_hal_random_fill_buf(key, 32); - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -327,7 +327,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -392,7 +392,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Missing data"); break; } - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -402,7 +402,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); if(cnt.control == U2F_COUNTER_CONTROL_VAL) { *cnt_val = cnt.counter; state = true; @@ -434,7 +434,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { cnt.control = U2F_COUNTER_CONTROL_VAL; cnt.counter = cnt_val; - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -443,7 +443,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); diff --git a/applications/main/xtreme_app/application.fam b/applications/main/xtreme_app/application.fam index 0fd447773..df57ae40f 100644 --- a/applications/main/xtreme_app/application.fam +++ b/applications/main/xtreme_app/application.fam @@ -7,4 +7,5 @@ App( icon="A_Xtreme_14", order=90, fap_icon="icon.png", + fap_category="assets", ) 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 b40191cff..16a9436ad 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -13,7 +13,9 @@ 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, protocols_gpio, ProtocolsGpio) ADD_SCENE(xtreme_app, misc, Misc) ADD_SCENE(xtreme_app, misc_screen, MiscScreen) +ADD_SCENE(xtreme_app, misc_screen_color, MiscScreenColor) 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_interface.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c index e6b62d034..a98ca56c5 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c @@ -51,18 +51,25 @@ bool xtreme_app_scene_interface_on_event(void* context, SceneManagerEvent event) consumed = true; switch(event.event) { case VarItemListIndexGraphics: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneInterfaceGraphics, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceGraphics); break; case VarItemListIndexMainmenu: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneInterfaceMainmenu, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceMainmenu); break; case VarItemListIndexLockscreen: + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneInterfaceLockscreen, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceLockscreen); break; case VarItemListIndexStatusbar: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneInterfaceStatusbar, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceStatusbar); break; case VarItemListIndexFileBrowser: + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneInterfaceFilebrowser, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceFilebrowser); break; default: 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 ed1951626..93093a54f 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 @@ -54,7 +54,7 @@ void xtreme_app_scene_interface_filebrowser_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Sort Dirs First", + "Folders Above Files", 2, xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed, app); 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 fb70b5622..c03b2abb3 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 @@ -27,10 +27,34 @@ static void xtreme_app_scene_interface_graphics_asset_pack_changed(VariableItem* app->apply_pack = true; } -const char* const anim_speed_names[] = - {"25%", "50%", "75%", "100%", "125%", "150%", "175%", "200%", "225%", "250%", "275%", "300%"}; -const uint32_t anim_speed_values[COUNT_OF(anim_speed_names)] = - {25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300}; +const char* const anim_speed_names[] = { + "25%", + "50%", + "75%", + "100%", + "125%", + "150%", + "175%", + "200%", + "225%", + "250%", + "275%", + "300%", +}; +const uint32_t anim_speed_values[COUNT_OF(anim_speed_names)] = { + 25, + 50, + 75, + 100, + 125, + 150, + 175, + 200, + 225, + 250, + 275, + 300, +}; static void xtreme_app_scene_interface_graphics_anim_speed_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -52,9 +76,23 @@ const char* const cycle_anims_names[] = { "2 H", "6 H", "12 H", - "24 H"}; -const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] = - {-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400}; + "24 H", +}; +const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] = { + -1, + 0, + 30, + 60, + 300, + 600, + 900, + 1800, + 3600, + 7200, + 21600, + 43200, + 86400, +}; static void xtreme_app_scene_interface_graphics_cycle_anims_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -96,7 +134,7 @@ void xtreme_app_scene_interface_graphics_on_enter(void* context) { variable_item_set_current_value_text( item, app->asset_pack_index == 0 ? - "SFW" : + "Default" : *CharList_get(app->asset_pack_names, app->asset_pack_index - 1)); item = variable_item_list_add( @@ -132,7 +170,7 @@ void xtreme_app_scene_interface_graphics_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Fallback Anim", + "Credits Anim", 2, xtreme_app_scene_interface_graphics_fallback_anim_changed, app); 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 394dc0d0c..b949b0410 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 @@ -82,6 +82,15 @@ static void xtreme_app_scene_interface_lockscreen_lockscreen_prompt_changed(Vari app->save_settings = true; } +static void + xtreme_app_scene_interface_lockscreen_lockscreen_transparent_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()->lockscreen_transparent = value; + app->save_settings = true; +} + void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); @@ -108,7 +117,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Allow RPC while locked", + "Allow RPC While Locked", 2, xtreme_app_scene_interface_lockscreen_allow_locked_rpc_commands_changed, app); @@ -162,6 +171,16 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->lockscreen_prompt); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_prompt ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, + "Transparent (see animation)", + 2, + xtreme_app_scene_interface_lockscreen_lockscreen_transparent_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->lockscreen_transparent); + variable_item_set_current_value_text( + item, xtreme_settings->lockscreen_transparent ? "ON" : "OFF"); + variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_interface_lockscreen_var_item_list_callback, app); 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 fc2d81bfa..98d9f0f35 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 @@ -17,6 +17,11 @@ void xtreme_app_scene_interface_mainmenu_var_item_list_callback(void* context, u const char* const menu_style_names[MenuStyleCount] = { "List", "Wii", + "DSi", + "PS4", + "Vertical", + "C64", + "Eurocorp", }; static void xtreme_app_scene_interface_mainmenu_menu_style_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); 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 5a81d5d77..b45163ead 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 @@ -67,6 +67,8 @@ void xtreme_app_scene_interface_mainmenu_add_on_enter(void* context) { XtremeApp* app = context; Submenu* submenu = app->submenu; + submenu_set_header(submenu, "Add Menu App:"); + submenu_add_item( submenu, "Main App", @@ -81,8 +83,6 @@ void xtreme_app_scene_interface_mainmenu_add_on_enter(void* context) { xtreme_app_scene_interface_mainmenu_add_submenu_callback, app); - submenu_set_header(submenu, "Add Menu App:"); - view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewSubmenu); } 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 50b3c14ec..953c08c95 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 @@ -13,8 +13,15 @@ void xtreme_app_scene_interface_statusbar_var_item_list_callback(void* context, view_dispatcher_send_custom_event(app->view_dispatcher, index); } -const char* const battery_icon_names[BatteryIconCount] = - {"OFF", "Bar", "%", "Inv. %", "Retro 3", "Retro 5", "Bar %"}; +const char* const battery_icon_names[BatteryIconCount] = { + "OFF", + "Bar", + "%", + "Inv. %", + "Retro 3", + "Retro 5", + "Bar %", +}; static void xtreme_app_scene_interface_statusbar_battery_icon_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); 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 1e5e61fc0..677bebe8f 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c @@ -5,6 +5,7 @@ enum VarItemListIndex { VarItemListIndexDolphin, VarItemListIndexChangeDeviceName, VarItemListIndexChargeCap, + VarItemListIndexShowXtremeIntro, }; void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) { @@ -50,6 +51,8 @@ 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); + variable_item_list_add(var_item_list, "Show Xtreme Intro", 0, NULL, app); + variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_misc_var_item_list_callback, app); @@ -68,14 +71,30 @@ bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) { consumed = true; switch(event.event) { case VarItemListIndexScreen: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscScreen, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); break; case VarItemListIndexDolphin: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscDolphin, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscDolphin); break; case VarItemListIndexChangeDeviceName: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscRename, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename); break; + case VarItemListIndexShowXtremeIntro: { + 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; + } default: 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 index e6425d20b..e4d344e09 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c @@ -29,10 +29,30 @@ static void xtreme_app_scene_misc_dolphin_dolphin_angry_changed(VariableItem* it 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}; +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); 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 index b86326aae..972f5f0db 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c @@ -4,7 +4,13 @@ enum VarItemListIndex { VarItemListIndexDarkMode, VarItemListIndexLeftHanded, VarItemListIndexRgbBacklight, - VarItemListIndexLcdColor, + VarItemListIndexLcdColor0, + VarItemListIndexLcdColor1, + VarItemListIndexLcdColor2, + VarItemListIndexRainbowLcd, + VarItemListIndexRainbowSpeed, + VarItemListIndexRainbowInterval, + VarItemListIndexRainbowSaturation, }; void xtreme_app_scene_misc_screen_var_item_list_callback(void* context, uint32_t index) { @@ -30,13 +36,117 @@ static void xtreme_app_scene_misc_screen_hand_orient_changed(VariableItem* item) } } -static void xtreme_app_scene_misc_screen_lcd_color_changed(VariableItem* item) { +static const struct { + char* name; + RgbColor color; +} lcd_colors[] = { + {"Orange", {255, 69, 0}}, + {"Red", {255, 0, 0}}, + {"Maroon", {128, 0, 0}}, + {"Yellow", {255, 255, 0}}, + {"Olive", {128, 128, 0}}, + {"Lime", {0, 255, 0}}, + {"Green", {0, 128, 0}}, + {"Aqua", {0, 255, 127}}, + {"Cyan", {0, 210, 210}}, + {"Azure", {0, 127, 255}}, + {"Teal", {0, 128, 128}}, + {"Blue", {0, 0, 255}}, + {"Navy", {0, 0, 128}}, + {"Purple", {128, 0, 128}}, + {"Fuchsia", {255, 0, 255}}, + {"Pink", {173, 31, 173}}, + {"Brown", {165, 42, 42}}, + {"White", {255, 192, 203}}, +}; +static void xtreme_app_scene_misc_screen_lcd_color_changed(VariableItem* item, uint8_t led) { 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); + variable_item_set_current_value_text(item, lcd_colors[index].name); + rgb_backlight_set_color(led, lcd_colors[index].color); + app->save_backlight = true; +} +static void xtreme_app_scene_misc_screen_lcd_color_0_changed(VariableItem* item) { + xtreme_app_scene_misc_screen_lcd_color_changed(item, 0); +} +static void xtreme_app_scene_misc_screen_lcd_color_1_changed(VariableItem* item) { + xtreme_app_scene_misc_screen_lcd_color_changed(item, 1); +} +static void xtreme_app_scene_misc_screen_lcd_color_2_changed(VariableItem* item) { + xtreme_app_scene_misc_screen_lcd_color_changed(item, 2); +} + +const char* const rainbow_lcd_names[RGBBacklightRainbowModeCount] = { + "OFF", + "Wave", + "Static", +}; +static void xtreme_app_scene_misc_screen_rainbow_lcd_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, rainbow_lcd_names[index]); + rgb_backlight_set_rainbow_mode(index); + app->save_backlight = true; +} + +static void xtreme_app_scene_misc_screen_rainbow_speed_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item) + 1; + char str[4]; + snprintf(str, sizeof(str), "%d", index); + variable_item_set_current_value_text(item, str); + rgb_backlight_set_rainbow_speed(index); + app->save_backlight = true; +} + +const char* const rainbow_interval_names[] = { + "0.1 S", + "0.2 S", + "0.25 S", + "0.5 S", + "0.75 S", + "1 S", + "1.25 S", + "1.5 S", + "1.75 S", + "2 S", + "2.5 S", + "3 S", + "4 S", + "5 S", +}; +const uint32_t rainbow_interval_values[COUNT_OF(rainbow_interval_names)] = { + 100, + 200, + 250, + 500, + 750, + 1000, + 1250, + 1500, + 1750, + 2000, + 2500, + 3000, + 4000, + 5000, +}; +static void xtreme_app_scene_misc_screen_rainbow_interval_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, rainbow_interval_names[index]); + rgb_backlight_set_rainbow_interval(rainbow_interval_values[index]); + app->save_backlight = true; +} + +static void xtreme_app_scene_misc_screen_rainbow_saturation_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item) + 1; + char str[4]; + snprintf(str, sizeof(str), "%d", index); + variable_item_set_current_value_text(item, str); + rgb_backlight_set_rainbow_saturation(index); app->save_backlight = true; - notification_message(app->notification, &sequence_display_backlight_on); } void xtreme_app_scene_misc_screen_on_enter(void* context) { @@ -60,15 +170,87 @@ void xtreme_app_scene_misc_screen_on_enter(void* context) { 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"); + struct { + uint8_t led; + const char* str; + VariableItemChangeCallback cb; + } lcd_cols[] = { + {2, "LCD Left", xtreme_app_scene_misc_screen_lcd_color_2_changed}, + {1, "LCD Middle", xtreme_app_scene_misc_screen_lcd_color_1_changed}, + {0, "LCD Right", xtreme_app_scene_misc_screen_lcd_color_0_changed}, + }; + size_t lcd_sz = COUNT_OF(lcd_colors); + + for(size_t i = 0; i < COUNT_OF(lcd_cols); i++) { + item = variable_item_list_add(var_item_list, lcd_cols[i].str, lcd_sz, lcd_cols[i].cb, app); + RgbColor color = rgb_backlight_get_color(lcd_cols[i].led); + bool found = false; + for(size_t i = 0; i < lcd_sz; i++) { + if(rgbcmp(&color, &lcd_colors[i].color) != 0) continue; + value_index = i; + found = true; + break; + } + variable_item_set_current_value_index(item, found ? value_index : lcd_sz); + if(found) { + variable_item_set_current_value_text(item, lcd_colors[value_index].name); + } else { + char str[7]; + snprintf(str, sizeof(str), "%02X%02X%02X", color.r, color.g, color.b); + variable_item_set_current_value_text(item, str); + } + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + } + item = variable_item_list_add( var_item_list, - "LCD Color", - rgb_backlight_get_color_count(), - xtreme_app_scene_misc_screen_lcd_color_changed, + "Rainbow LCD", + RGBBacklightRainbowModeCount, + xtreme_app_scene_misc_screen_rainbow_lcd_changed, app); - value_index = rgb_backlight_get_settings()->display_color_index; + value_index = rgb_backlight_get_rainbow_mode(); 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_current_value_text(item, rainbow_lcd_names[value_index]); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + + item = variable_item_list_add( + var_item_list, + "Rainbow Speed", + 25, + xtreme_app_scene_misc_screen_rainbow_speed_changed, + app); + value_index = rgb_backlight_get_rainbow_speed(); + variable_item_set_current_value_index(item, value_index - 1); + char speed_str[4]; + snprintf(speed_str, sizeof(speed_str), "%d", value_index); + variable_item_set_current_value_text(item, speed_str); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + + item = variable_item_list_add( + var_item_list, + "Rainbow Interval", + COUNT_OF(rainbow_interval_values), + xtreme_app_scene_misc_screen_rainbow_interval_changed, + app); + value_index = value_index_uint32( + rgb_backlight_get_rainbow_interval(), + rainbow_interval_values, + COUNT_OF(rainbow_interval_values)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, rainbow_interval_names[value_index]); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + + item = variable_item_list_add( + var_item_list, + "Rainbow Saturation", + 255, + xtreme_app_scene_misc_screen_rainbow_saturation_changed, + app); + value_index = rgb_backlight_get_rainbow_saturation(); + variable_item_set_current_value_index(item, value_index - 1); + char saturation_str[4]; + snprintf(saturation_str, sizeof(saturation_str), "%d", value_index); + variable_item_set_current_value_text(item, saturation_str); variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); variable_item_list_set_enter_callback( @@ -110,12 +292,23 @@ bool xtreme_app_scene_misc_screen_on_event(void* context, SceneManagerEvent even if(change) { XTREME_SETTINGS()->rgb_backlight = !XTREME_SETTINGS()->rgb_backlight; app->save_settings = true; + app->save_backlight = true; notification_message(app->notification, &sequence_display_backlight_on); + rgb_backlight_reconfigure(XTREME_SETTINGS()->rgb_backlight); scene_manager_previous_scene(app->scene_manager); scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); } break; } + case VarItemListIndexLcdColor0: + case VarItemListIndexLcdColor1: + case VarItemListIndexLcdColor2: + scene_manager_set_scene_state( + app->scene_manager, + XtremeAppSceneMiscScreenColor, + event.event - VarItemListIndexLcdColor0); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreenColor); + break; default: break; } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen_color.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen_color.c new file mode 100644 index 000000000..a998a1cb3 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen_color.c @@ -0,0 +1,59 @@ +#include "../xtreme_app.h" + +enum ByteInputResult { + ByteInputResultOk, +}; + +void xtreme_app_scene_misc_screen_color_byte_input_callback(void* context) { + XtremeApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, ByteInputResultOk); +} + +void xtreme_app_scene_misc_screen_color_on_enter(void* context) { + XtremeApp* app = context; + ByteInput* byte_input = app->byte_input; + + byte_input_set_header_text(byte_input, "Set LCD Color (#RRGGBB)"); + + app->lcd_color = rgb_backlight_get_color( + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMiscScreenColor)); + + byte_input_set_result_callback( + byte_input, + xtreme_app_scene_misc_screen_color_byte_input_callback, + NULL, + app, + (void*)&app->lcd_color, + sizeof(app->lcd_color)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewByteInput); +} + +bool xtreme_app_scene_misc_screen_color_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case ByteInputResultOk: + rgb_backlight_set_color( + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMiscScreenColor), + app->lcd_color); + app->save_backlight = true; + scene_manager_previous_scene(app->scene_manager); + break; + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_screen_color_on_exit(void* context) { + XtremeApp* 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/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index 67c80b14e..5912d1e80 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -5,6 +5,7 @@ enum VarItemListIndex { VarItemListIndexBadbtRemember, VarItemListIndexSubghzFreqs, VarItemListIndexSubghzExtend, + VarItemListIndexGpioPins, }; void xtreme_app_scene_protocols_var_item_list_callback(void* context, uint32_t index) { @@ -63,6 +64,9 @@ void xtreme_app_scene_protocols_on_enter(void* context) { variable_item_set_current_value_index(item, app->subghz_extend); variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); + item = variable_item_list_add(var_item_list, "GPIO Pins", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_protocols_var_item_list_callback, app); @@ -81,8 +85,13 @@ bool xtreme_app_scene_protocols_on_event(void* context, SceneManagerEvent event) consumed = true; switch(event.event) { case VarItemListIndexSubghzFreqs: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqs, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqs); break; + case VarItemListIndexGpioPins: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocolsGpio, 0); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsGpio); + break; default: break; } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c index f8c779cc5..dfe77d26c 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c @@ -59,9 +59,13 @@ bool xtreme_app_scene_protocols_freqs_on_event(void* context, SceneManagerEvent consumed = true; switch(event.event) { case VarItemListIndexStaticFreqs: + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFreqsStatic, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic); break; case VarItemListIndexHopperFreqs: + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneProtocolsFreqsHopper, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper); break; default: diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_gpio.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_gpio.c new file mode 100644 index 000000000..76bb03f75 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_gpio.c @@ -0,0 +1,147 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexSpiCc1101Handle, + VarItemListIndexSpiNrf24Handle, + VarItemListIndexUartEspChannel, + VarItemListIndexUartNmeaChannel, + VarItemListIndexUartGeneralChannel, +}; + +void xtreme_app_scene_protocols_gpio_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_gpio_cc1101_handle_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + XTREME_SETTINGS()->spi_cc1101_handle = + variable_item_get_current_value_index(item) == 0 ? SpiDefault : SpiExtra; + variable_item_set_current_value_text( + item, XTREME_SETTINGS()->spi_cc1101_handle == SpiDefault ? "Default" : "Extra"); + app->save_settings = true; +} + +static void xtreme_app_scene_protocols_gpio_nrf24_handle_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + XTREME_SETTINGS()->spi_nrf24_handle = + variable_item_get_current_value_index(item) == 0 ? SpiDefault : SpiExtra; + variable_item_set_current_value_text( + item, XTREME_SETTINGS()->spi_nrf24_handle == SpiDefault ? "Default" : "Extra"); + app->save_settings = true; +} + +static void xtreme_app_scene_protocols_gpio_esp32_channel_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + XTREME_SETTINGS()->uart_esp_channel = + variable_item_get_current_value_index(item) == 0 ? UARTDefault : UARTExtra; + variable_item_set_current_value_text( + item, XTREME_SETTINGS()->uart_esp_channel == UARTDefault ? "13,14" : "15,16"); + app->save_settings = true; +} + +static void xtreme_app_scene_protocols_gpio_nmea_channel_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + XTREME_SETTINGS()->uart_nmea_channel = + variable_item_get_current_value_index(item) == 0 ? UARTDefault : UARTExtra; + variable_item_set_current_value_text( + item, XTREME_SETTINGS()->uart_nmea_channel == UARTDefault ? "13,14" : "15,16"); + app->save_settings = true; +} + +static void xtreme_app_scene_protocols_gpio_general_channel_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + XTREME_SETTINGS()->uart_general_channel = + variable_item_get_current_value_index(item) == 0 ? UARTDefault : UARTExtra; + variable_item_set_current_value_text( + item, XTREME_SETTINGS()->uart_general_channel == UARTDefault ? "13,14" : "15,16"); + app->save_settings = true; +} + +void xtreme_app_scene_protocols_gpio_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, + "SPI CC1101 Handle", + 2, + xtreme_app_scene_protocols_gpio_cc1101_handle_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->spi_cc1101_handle); + variable_item_set_current_value_text( + item, xtreme_settings->spi_cc1101_handle == SpiDefault ? "Default" : "Extra"); + + item = variable_item_list_add( + var_item_list, + "SPI NRF24 Handle", + 2, + xtreme_app_scene_protocols_gpio_nrf24_handle_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->spi_nrf24_handle); + variable_item_set_current_value_text( + item, xtreme_settings->spi_nrf24_handle == SpiDefault ? "Default" : "Extra"); + + item = variable_item_list_add( + var_item_list, + "UART ESP32/ESP8266 Channel", + 2, + xtreme_app_scene_protocols_gpio_esp32_channel_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->uart_esp_channel); + variable_item_set_current_value_text( + item, xtreme_settings->uart_esp_channel == UARTDefault ? "13,14" : "15,16"); + + item = variable_item_list_add( + var_item_list, + "UART NMEA Channel", + 2, + xtreme_app_scene_protocols_gpio_nmea_channel_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->uart_nmea_channel); + variable_item_set_current_value_text( + item, xtreme_settings->uart_nmea_channel == UARTDefault ? "13,14" : "15,16"); + + item = variable_item_list_add( + var_item_list, + "UART General Channel", + 2, + xtreme_app_scene_protocols_gpio_general_channel_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->uart_general_channel); + variable_item_set_current_value_text( + item, xtreme_settings->uart_general_channel == UARTDefault ? "13,14" : "15,16"); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_protocols_gpio_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsGpio)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_protocols_gpio_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state( + app->scene_manager, XtremeAppSceneProtocolsGpio, event.event); + consumed = true; + switch(event.event) { + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_protocols_gpio_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_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c index 1f1c1de70..373902ff8 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c @@ -4,7 +4,6 @@ enum VarItemListIndex { VarItemListIndexInterface, VarItemListIndexProtocols, VarItemListIndexMisc, - VarItemListIndexVersion, }; void xtreme_app_scene_start_var_item_list_callback(void* context, uint32_t index) { @@ -26,7 +25,7 @@ void xtreme_app_scene_start_on_enter(void* context) { 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, furi_string_get_cstr(app->version_tag), 0, NULL, app); + variable_item_list_set_header(var_item_list, furi_string_get_cstr(app->version_tag)); variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_start_var_item_list_callback, app); @@ -46,27 +45,17 @@ bool xtreme_app_scene_start_on_event(void* context, SceneManagerEvent event) { consumed = true; switch(event.event) { case VarItemListIndexInterface: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneInterface, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterface); break; case VarItemListIndexProtocols: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocols, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocols); break; case VarItemListIndexMisc: + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMisc, 0); scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc); break; - case VarItemListIndexVersion: { - 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; - } default: break; } diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index 1fde58a32..ce198a3e4 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -100,7 +100,7 @@ bool xtreme_app_apply(XtremeApp* app) { if(app->save_level || app->save_angry) { Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); if(app->save_level) { - int32_t xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0; + int32_t xp = app->dolphin_level > 1 ? DOLPHIN_LEVELS[app->dolphin_level - 2] : 0; dolphin->state->data.icounter = xp + 1; } if(app->save_angry) { @@ -193,6 +193,10 @@ XtremeApp* xtreme_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, XtremeAppViewTextInput, text_input_get_view(app->text_input)); + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, XtremeAppViewByteInput, byte_input_get_view(app->byte_input)); + app->popup = popup_alloc(); view_dispatcher_add_view(app->view_dispatcher, XtremeAppViewPopup, popup_get_view(app->popup)); @@ -315,6 +319,8 @@ void xtreme_app_free(XtremeApp* app) { submenu_free(app->submenu); view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewTextInput); text_input_free(app->text_input); + view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewByteInput); + byte_input_free(app->byte_input); view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewPopup); popup_free(app->popup); view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewDialogEx); diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index 995b00310..7fbee8bcb 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ typedef struct { VariableItemList* var_item_list; Submenu* submenu; TextInput* text_input; + ByteInput* byte_input; Popup* popup; DialogEx* dialog_ex; @@ -57,6 +59,7 @@ typedef struct { uint8_t subghz_hopper_index; char subghz_freq_buffer[XTREME_SUBGHZ_FREQ_BUFFER_SIZE]; bool subghz_extend; + RgbColor lcd_color; char device_name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; int32_t dolphin_level; int32_t dolphin_angry; @@ -79,6 +82,7 @@ typedef enum { XtremeAppViewVarItemList, XtremeAppViewSubmenu, XtremeAppViewTextInput, + XtremeAppViewByteInput, XtremeAppViewPopup, XtremeAppViewDialogEx, } XtremeAppView; diff --git a/applications/services/applications.h b/applications/services/applications.h index 426d6d2d2..7dbb7063f 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -51,6 +51,12 @@ extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; extern const FlipperInternalApplication FLIPPER_SYSTEM_APPS[]; extern const size_t FLIPPER_SYSTEM_APPS_COUNT; +/* Debug apps + * Can only be spawned by loader by name + */ +extern const FlipperInternalApplication FLIPPER_DEBUG_APPS[]; +extern const size_t FLIPPER_DEBUG_APPS_COUNT; + extern const FlipperInternalApplication FLIPPER_ARCHIVE; /* Settings list diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index ea790a576..a7e6f7317 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -90,7 +90,6 @@ static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { dialog_message_set_buttons(bt->dialog_message, "Cancel", "OK", NULL); DialogMessageButton button = dialog_message_show(bt->dialogs, bt->dialog_message); furi_string_free(pin_str); - return button == DialogMessageButtonCenter; } @@ -216,40 +215,6 @@ static void bt_rpc_send_bytes_callback(void* context, uint8_t* bytes, size_t byt } } -// Open BT Connection -void bt_open_rpc_connection(Bt* bt) { - if(!bt->rpc_session && bt->status == BtStatusConnected) { - // Clear BT_RPC_EVENT_DISCONNECTED because it might be set from previous session - furi_event_flag_clear(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); - if(bt->profile == BtProfileSerial) { - // Open RPC session - bt->rpc_session = rpc_session_open(bt->rpc, RpcOwnerBle); - if(bt->rpc_session) { - FURI_LOG_I(TAG, "Open RPC connection"); - rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); - rpc_session_set_buffer_is_empty_callback( - bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty); - rpc_session_set_context(bt->rpc_session, bt); - furi_hal_bt_serial_set_event_callback( - RPC_BUFFER_SIZE, bt_serial_event_callback, bt); - furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatusActive); - } else { - FURI_LOG_W(TAG, "RPC is busy, failed to open new session"); - } - } - } -} - -void bt_close_rpc_connection(Bt* bt) { - if(bt->profile == BtProfileSerial && bt->rpc_session) { - FURI_LOG_I(TAG, "Close RPC connection"); - furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); - rpc_session_close(bt->rpc_session); - furi_hal_bt_serial_set_event_callback(0, NULL, NULL); - bt->rpc_session = NULL; - } -} - // Called from GAP thread static bool bt_on_gap_event_callback(GapEvent event, void* context) { furi_assert(context); @@ -340,6 +305,39 @@ static void bt_show_warning(Bt* bt, const char* text) { dialog_message_show(bt->dialogs, bt->dialog_message); } +void bt_open_rpc_connection(Bt* bt) { + if(!bt->rpc_session && bt->status == BtStatusConnected) { + // Clear BT_RPC_EVENT_DISCONNECTED because it might be set from previous session + furi_event_flag_clear(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); + if(bt->profile == BtProfileSerial) { + // Open RPC session + bt->rpc_session = rpc_session_open(bt->rpc, RpcOwnerBle); + if(bt->rpc_session) { + FURI_LOG_I(TAG, "Open RPC connection"); + rpc_session_set_send_bytes_callback(bt->rpc_session, bt_rpc_send_bytes_callback); + rpc_session_set_buffer_is_empty_callback( + bt->rpc_session, furi_hal_bt_serial_notify_buffer_is_empty); + rpc_session_set_context(bt->rpc_session, bt); + furi_hal_bt_serial_set_event_callback( + RPC_BUFFER_SIZE, bt_serial_event_callback, bt); + furi_hal_bt_serial_set_rpc_status(FuriHalBtSerialRpcStatusActive); + } else { + FURI_LOG_W(TAG, "RPC is busy, failed to open new session"); + } + } + } +} + +void bt_close_rpc_connection(Bt* bt) { + if(bt->profile == BtProfileSerial && bt->rpc_session) { + FURI_LOG_I(TAG, "Close RPC connection"); + furi_event_flag_set(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); + rpc_session_close(bt->rpc_session); + furi_hal_bt_serial_set_event_callback(0, NULL, NULL); + bt->rpc_session = NULL; + } +} + static void bt_change_profile(Bt* bt, BtMessage* message) { if(furi_hal_bt_is_ble_gatt_gap_supported()) { bt_settings_load(&bt->bt_settings); diff --git a/applications/services/cli/application.fam b/applications/services/cli/application.fam index 463b12c46..7a57bb607 100644 --- a/applications/services/cli/application.fam +++ b/applications/services/cli/application.fam @@ -5,6 +5,6 @@ App( entry_point="cli_srv", cdefines=["SRV_CLI"], stack_size=4 * 1024, - order=20, + order=30, sdk_headers=["cli.h", "cli_vcp.h"], ) diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index a6aaa8429..e44c5d763 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -101,25 +101,18 @@ void cli_print_usage(const char* cmd, const char* usage, const char* arg) { } void cli_motd() { - printf("\r\n" - " _.-------.._ -,\r\n" - " .-\"```\"--..,,_/ /`-, -, \\ \r\n" - " .:\" /:/ /'\\ \\ ,_..., `. | |\r\n" - " / ,----/:/ /`\\ _\\~`_-\"` _;\r\n" - " ' / /`\"\"\"'\\ \\ \\.~`_-' ,-\"'/ \r\n" - " | | | 0 | | .-' ,/` /\r\n" - " | ,..\\ \\ ,.-\"` ,/` /\r\n" - " ; : `/`\"\"\\` ,/--==,/-----,\r\n" - " | `-...| -.___-Z:_______J...---;\r\n" - " : ` _-'\r\n" - " _L_ _ ___ ___ ___ ___ ____--\"`___ _ ___\r\n" - "| __|| | |_ _|| _ \\| _ \\| __|| _ \\ / __|| | |_ _|\r\n" - "| _| | |__ | | | _/| _/| _| | / | (__ | |__ | |\r\n" - "|_| |____||___||_| |_| |___||_|_\\ \\___||____||___|\r\n" - "\r\n" - "Welcome to Flipper Zero Command Line Interface!\r\n" - "Read Manual https://docs.flipperzero.one\r\n" - "\r\n"); + printf( + "\033[0m\r\n" + " ,-' \\_/ `\\\r\n" + "\033[1;31m__\033[0m \033[1;31m__\033[37m_\033[0m ___ ___ ___ ( , |\r\n" + "\033[1;31m\\\033[0m \033[1;31m\\/\033[0m \033[1;31m/\033[0m \033[1m|_ _ __ ___ _ __ ___ ___\033[0m / ___| | |_ _| `-.-'`-.-'/|_|\r\n" + " \033[1;31m\\\033[0m \033[1;31m/\033[37m| __| \'__/ _ \\ \'_ ` _ \\ / _ \\\033[0m | | | | | | \\ / | |\r\n" + " \033[1;31m/\033[0m \033[1;31m\\\033[37m| |_| | | __/ | | | | | __/\033[0m | |___| |___ | | |=[]=: / ,'\r\n" + "\033[1;31m/_/\\_\\\033[37m\\__|_| \\___|_| |_| |_|\\___|\033[0m \\____|_____|___| / `\\ '\r\n" + " : \\/ )\r\n" + "Welcome to the \033[1;31mX\033[37mtreme\033[0m Command Line Interface! | / ;\r\n" + "Visit \033[1;31mhttps://flipper-xtre.me/\033[0m for even more fun | / /" + "\r\n"); const Version* firmware_version = furi_hal_version_get_firmware_version(); if(firmware_version) { diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index 34aa34dd3..b465e01e8 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -33,7 +33,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } @@ -88,7 +88,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) { } while(0); if(key_loaded) { - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } } @@ -108,7 +108,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } @@ -160,7 +160,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { } while(0); if(key_loaded) { - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } } @@ -175,14 +175,14 @@ void crypto_cli_has_key(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } printf("Successfully loaded key from slot %d", key_slot); - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } while(0); } @@ -251,25 +251,25 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) { if(key_slot > 0) { uint8_t iv[16] = {0}; if(key_slot > 1) { - if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot - 1, iv)) { printf( "Slot %d before %d is empty, which is not allowed", key_slot - 1, key_slot); break; } - furi_hal_crypto_store_unload_key(key_slot - 1); + furi_hal_crypto_enclave_unload_key(key_slot - 1); } - if(furi_hal_crypto_store_load_key(key_slot, iv)) { - furi_hal_crypto_store_unload_key(key_slot); + if(furi_hal_crypto_enclave_load_key(key_slot, iv)) { + furi_hal_crypto_enclave_unload_key(key_slot); printf("Key slot %d is already used", key_slot); break; } } uint8_t slot; - if(furi_hal_crypto_store_add_key(&key, &slot)) { + if(furi_hal_crypto_enclave_store_key(&key, &slot)) { printf("Success. Stored to slot: %d", slot); } else { printf("Failure"); diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 0d079e213..88ec09fce 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -55,6 +55,7 @@ struct AnimationManager { FuriString* freezed_animation_name; int32_t freezed_animation_time_left; ViewStack* view_stack; + bool _dummy_mode; // Unused, kept for compatibility }; static StorageAnimation* diff --git a/applications/services/desktop/application.fam b/applications/services/desktop/application.fam index a6d82d368..da6e2b802 100644 --- a/applications/services/desktop/application.fam +++ b/applications/services/desktop/application.fam @@ -13,5 +13,5 @@ App( provides=["desktop_settings"], conflicts=["updater"], stack_size=2 * 1024, - order=50, + order=60, ) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index b7dbda34e..9e4f33c9a 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -9,6 +10,7 @@ #include #include #include +#include #include #include "animations/animation_manager.h" @@ -37,103 +39,69 @@ 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); canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8); } -static void desktop_clock_upd_time(Desktop* desktop, bool forced) { +static void desktop_clock_update(Desktop* desktop) { furi_assert(desktop); FuriHalRtcDateTime curr_dt; furi_hal_rtc_get_datetime(&curr_dt); + bool time_format_12 = locale_get_time_format() == LocaleTimeFormat12h; - if(forced) { - desktop->clock_type = (locale_get_time_format() == LocaleTimeFormat24h); - } - - if(forced || (desktop->minute != curr_dt.minute)) { - if(desktop->clock_type) { - desktop->hour = curr_dt.hour; - } else { - desktop->hour = (curr_dt.hour > 12) ? curr_dt.hour - 12 : - ((curr_dt.hour == 0) ? 12 : curr_dt.hour); - } - desktop->minute = curr_dt.minute; + if(desktop->time_hour != curr_dt.hour || desktop->time_minute != curr_dt.minute || + desktop->time_format_12 != time_format_12) { + desktop->time_format_12 = time_format_12; + desktop->time_hour = curr_dt.hour; + desktop->time_minute = curr_dt.minute; view_port_update(desktop->clock_viewport); } } -static void desktop_clock_toggle_view(Desktop* desktop, bool is_enabled) { +static void desktop_clock_reconfigure(Desktop* desktop) { furi_assert(desktop); - desktop_clock_upd_time(desktop, true); + desktop_clock_update(desktop); - if(is_enabled) { // && !furi_timer_is_running(desktop->update_clock_timer)) { + if(XTREME_SETTINGS()->statusbar_clock) { furi_timer_start(desktop->update_clock_timer, furi_ms_to_ticks(1000)); - } else if(!is_enabled) { //&& furi_timer_is_running(desktop->update_clock_timer)) { + } else { furi_timer_stop(desktop->update_clock_timer); } - view_port_enabled_set(desktop->clock_viewport, is_enabled); + view_port_enabled_set(desktop->clock_viewport, XTREME_SETTINGS()->statusbar_clock); } -static uint8_t desktop_clock_get_num_w(uint8_t num) { - if(num == 1) { - return 3; - } else if(num == 4) { - return 6; - } else { - return 5; - } -} - -static const char* digit[10] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; - static void desktop_clock_draw_callback(Canvas* canvas, void* context) { furi_assert(context); furi_assert(canvas); Desktop* desktop = context; - uint8_t d[4] = { - desktop->minute % 10, - desktop->minute / 10, - desktop->hour % 10, - desktop->hour / 10, - }; - canvas_set_font(canvas, FontPrimary); - uint8_t new_w = desktop_clock_get_num_w(d[0]) + //c1 - desktop_clock_get_num_w(d[1]) + //c2 - desktop_clock_get_num_w(d[2]) + //c3 - desktop_clock_get_num_w(d[3]) + //c4 - 2 + 4; // ":" + 4 separators + uint8_t hour = desktop->time_hour; + if(desktop->time_format_12) { + if(hour > 12) { + hour -= 12; + } + if(hour == 0) { + hour = 12; + } + } - // further away from the battery charge indicator, if the smallest minute is 1 - view_port_set_width(desktop->clock_viewport, new_w - !(d[0] == 1)); + char buffer[20]; + snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute); - uint8_t x = new_w; + // TODO FL-3515: never do that, may cause visual glitches + view_port_set_width( + desktop->clock_viewport, + canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1)); - uint8_t y = 8; - uint8_t offset_r; - - canvas_draw_str_aligned(canvas, x, y, AlignRight, AlignBottom, digit[d[0]]); - offset_r = desktop_clock_get_num_w(d[0]); - - canvas_draw_str_aligned(canvas, x -= (offset_r + 1), y, AlignRight, AlignBottom, digit[d[1]]); - offset_r = desktop_clock_get_num_w(d[1]); - - canvas_draw_str_aligned(canvas, x -= (offset_r + 1), y - 1, AlignRight, AlignBottom, ":"); - offset_r = 2; - - canvas_draw_str_aligned(canvas, x -= (offset_r + 1), y, AlignRight, AlignBottom, digit[d[2]]); - offset_r = desktop_clock_get_num_w(d[2]); - - canvas_draw_str_aligned(canvas, x -= (offset_r + 1), y, AlignRight, AlignBottom, digit[d[3]]); + canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignBottom, buffer); } static void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) { @@ -153,7 +121,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); - desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); + desktop_clock_reconfigure(desktop); desktop_auto_lock_arm(desktop); return true; case DesktopGlobalAutoLock: @@ -223,8 +191,8 @@ static void desktop_clock_timer_callback(void* context) { furi_assert(context); Desktop* desktop = context; - if(gui_get_count_of_enabled_view_port_in_layer(desktop->gui, GuiLayerStatusBarLeft) < 6) { - desktop_clock_upd_time(desktop, false); + if(gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6) { + desktop_clock_update(desktop); view_port_enabled_set(desktop->clock_viewport, true); } else { @@ -397,12 +365,7 @@ Desktop* desktop_alloc() { } gui_add_view_port(desktop->gui, desktop->stealth_mode_icon_viewport, GuiLayerStatusBarLeft); - // Special case: autostart application is already running desktop->loader = furi_record_open(RECORD_LOADER); - if(loader_is_locked(desktop->loader) && - animation_manager_is_animation_loaded(desktop->animation_manager)) { - animation_manager_unload_and_stall_animation(desktop->animation_manager); - } desktop->notification = furi_record_open(RECORD_NOTIFICATION); desktop->app_start_stop_subscription = furi_pubsub_subscribe( @@ -419,11 +382,6 @@ Desktop* desktop_alloc() { desktop->update_clock_timer = furi_timer_alloc(desktop_clock_timer_callback, FuriTimerTypePeriodic, desktop); - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - - desktop_clock_upd_time(desktop, true); - furi_record_create(RECORD_DESKTOP, desktop); return desktop; @@ -492,7 +450,12 @@ void desktop_run_keybind(Desktop* instance, InputType _type, InputKey _key) { } else if(!strncmp(keybind, "Wipe Device", MAX_KEYBIND_LENGTH)) { loader_start_detached_with_gui_error(instance->loader, "Storage", "wipe"); } else { - loader_start_detached_with_gui_error(instance->loader, keybind, NULL); + if(storage_common_exists(furi_record_open(RECORD_STORAGE), keybind)) { + run_with_default_app(keybind); + } else { + loader_start_detached_with_gui_error(instance->loader, keybind, NULL); + } + furi_record_close(RECORD_STORAGE); } } @@ -518,7 +481,7 @@ int32_t desktop_srv(void* p) { DESKTOP_KEYBINDS_LOAD(&desktop->keybinds, sizeof(desktop->keybinds)); - desktop_clock_toggle_view(desktop, XTREME_SETTINGS()->statusbar_clock); + desktop_clock_reconfigure(desktop); scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); @@ -542,6 +505,12 @@ int32_t desktop_srv(void* p) { scene_manager_next_scene(desktop->scene_manager, DesktopSceneFault); } + // Special case: autostart application is already running + if(loader_is_locked(desktop->loader) && + animation_manager_is_animation_loaded(desktop->animation_manager)) { + animation_manager_unload_and_stall_animation(desktop->animation_manager); + } + view_dispatcher_run(desktop->view_dispatcher); furi_crash("That was unexpected"); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index d42bf2616..b1cc2ef42 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -55,10 +55,10 @@ struct Desktop { ViewStack* locked_view_stack; DesktopSettings settings; - Keybind keybinds[KeybindTypeCount][KeybindKeyCount]; DesktopViewPinInput* pin_input_view; ViewPort* lock_icon_viewport; + ViewPort* _dummy_mode_icon_viewport; // Unused, kept for compatibility ViewPort* clock_viewport; ViewPort* stealth_mode_icon_viewport; @@ -74,11 +74,14 @@ struct Desktop { FuriTimer* update_clock_timer; FuriPubSub* status_pubsub; - uint8_t hour; - uint8_t minute; - bool clock_type : 1; // true - 24h false - 12h + + uint8_t time_hour; + uint8_t time_minute; + bool time_format_12 : 1; // 1 - 12 hour, 0 - 24H bool in_transition : 1; + + Keybind keybinds[KeybindTypeCount][KeybindKeyCount]; }; Desktop* desktop_alloc(); diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index bcc8eee20..b10cfa6c7 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -81,12 +81,12 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { furi_record_close(RECORD_LOADER); consumed = true; break; - case DesktopLockMenuEventLock: + case DesktopLockMenuEventLockKeypad: desktop_scene_lock_menu_save_settings(desktop); desktop_lock(desktop, false); consumed = true; break; - case DesktopLockMenuEventLockPin: + case DesktopLockMenuEventLockPinCode: desktop_scene_lock_menu_save_settings(desktop); if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock(desktop, true); diff --git a/applications/services/desktop/scenes/desktop_scene_locked.c b/applications/services/desktop/scenes/desktop_scene_locked.c index ce2a6b96e..a96041a8a 100644 --- a/applications/services/desktop/scenes/desktop_scene_locked.c +++ b/applications/services/desktop/scenes/desktop_scene_locked.c @@ -93,6 +93,10 @@ bool desktop_scene_locked_on_event(void* context, SceneManagerEvent event) { desktop_unlock(desktop); consumed = true; break; + case DesktopLockedEventCoversClosed: + notification_message(desktop->notification, &sequence_display_backlight_off); + consumed = true; + break; case DesktopLockedEventUpdate: if(desktop_view_locked_is_locked_hint_visible(desktop->locked_view)) { notification_message( diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index b4ab2e4e8..c74f75c1b 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -97,16 +97,6 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; } break; - case DesktopMainEventOpenLockMenu: - scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); - consumed = true; - break; - - case DesktopMainEventOpenDebug: - scene_manager_next_scene(desktop->scene_manager, DesktopSceneDebug); - consumed = true; - break; - case DesktopMainEventLockKeypad: desktop_lock(desktop, false); consumed = true; @@ -117,6 +107,16 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; break; + case DesktopMainEventOpenLockMenu: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneLockMenu); + consumed = true; + break; + + case DesktopMainEventOpenDebug: + scene_manager_next_scene(desktop->scene_manager, DesktopSceneDebug); + consumed = true; + break; + case DesktopMainEventOpenArchive: #ifdef APP_ARCHIVE desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE); diff --git a/applications/services/desktop/views/desktop_events.h b/applications/services/desktop/views/desktop_events.h index 5fada45c1..344fed529 100644 --- a/applications/services/desktop/views/desktop_events.h +++ b/applications/services/desktop/views/desktop_events.h @@ -1,18 +1,26 @@ #pragma once typedef enum { + // Events with _ are unused, kept for compatibility + DesktopMainEventLockWithPin, DesktopMainEventOpenLockMenu, DesktopMainEventOpenArchive, + _DesktopMainEventOpenFavoriteLeftShort, + _DesktopMainEventOpenFavoriteLeftLong, + _DesktopMainEventOpenFavoriteRightShort, + _DesktopMainEventOpenFavoriteRightLong, DesktopMainEventOpenMenu, DesktopMainEventOpenDebug, DesktopMainEventOpenPowerOff, - DesktopMainEventLockKeypad, - DesktopMainEventLockWithPin, - DesktopLockedEventOpenPowerOff, + _DesktopDummyEventOpenLeft, + _DesktopDummyEventOpenDown, + _DesktopDummyEventOpenOk, + DesktopLockedEventUnlocked, DesktopLockedEventUpdate, DesktopLockedEventShowPinInput, + DesktopLockedEventCoversClosed, DesktopPinInputEventResetWrongPinLabel, DesktopPinInputEventUnlocked, @@ -26,11 +34,9 @@ typedef enum { DesktopDebugEventSaveState, DesktopDebugEventExit, - DesktopLockMenuEventSettings, - DesktopLockMenuEventLock, - DesktopLockMenuEventLockPin, - DesktopLockMenuEventLockPinOff, - DesktopLockMenuEventXtreme, + DesktopLockMenuEventLockPinCode, + _DesktopLockMenuEventDummyModeOn, + _DesktopLockMenuEventDummyModeOff, DesktopLockMenuEventStealthModeOn, DesktopLockMenuEventStealthModeOff, @@ -45,4 +51,11 @@ typedef enum { DesktopGlobalBeforeAppStarted, DesktopGlobalAfterAppFinished, DesktopGlobalAutoLock, + + DesktopMainEventLockKeypad, + DesktopLockedEventOpenPowerOff, + DesktopLockMenuEventSettings, + DesktopLockMenuEventLockKeypad, + DesktopLockMenuEventLockPinOff, + DesktopLockMenuEventXtreme, } DesktopEvent; diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 204dd2cfd..10e5860dd 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -259,10 +259,10 @@ bool desktop_lock_menu_input_callback(InputEvent* event, void* context) { if(event->key == InputKeyOk && event->type == InputTypeShort) { switch(pin_lock) { case 0: - desktop_event = DesktopLockMenuEventLock; + desktop_event = DesktopLockMenuEventLockKeypad; break; case 1: - desktop_event = DesktopLockMenuEventLockPin; + desktop_event = DesktopLockMenuEventLockPinCode; break; case 2: desktop_event = DesktopLockMenuEventLockPinOff; diff --git a/applications/services/desktop/views/desktop_view_lock_menu.h b/applications/services/desktop/views/desktop_view_lock_menu.h index 67d0f6f67..78b981c50 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.h +++ b/applications/services/desktop/views/desktop_view_lock_menu.h @@ -14,21 +14,24 @@ typedef void (*DesktopLockMenuViewCallback)(DesktopEvent event, void* context); struct DesktopLockMenuView { View* view; DesktopLockMenuViewCallback callback; + void* context; + NotificationApp* notification; Bt* bt; bool save_notification; bool save_xtreme; bool save_bt; - void* context; }; typedef struct { uint8_t idx; + bool _dummy_mode; // Unused, kept for compatibility + bool stealth_mode; + bool pin_is_set; int pin_lock; bool show_lock_menu; DesktopLockMenuView* lock_menu; - bool stealth_mode; } DesktopLockMenuViewModel; void desktop_lock_menu_set_callback( diff --git a/applications/services/desktop/views/desktop_view_locked.c b/applications/services/desktop/views/desktop_view_locked.c index 69ace11ed..2720cdbfc 100644 --- a/applications/services/desktop/views/desktop_view_locked.c +++ b/applications/services/desktop/views/desktop_view_locked.c @@ -94,7 +94,9 @@ void desktop_view_locked_draw_lockscreen(Canvas* canvas, void* m) { snprintf(date_str, 14, "%.2d-%.2d-%.4d", datetime.day, datetime.month, datetime.year); } - canvas_draw_icon(canvas, 0, 0 + y, &I_Lockscreen); + if(!xtreme_settings->lockscreen_transparent) { + canvas_draw_icon(canvas, 0, 0 + y, &I_Lockscreen); + } if(xtreme_settings->lockscreen_time) { canvas_set_font(canvas, FontBigNumbers); canvas_draw_str(canvas, 0, 64 + y, time_str); @@ -163,6 +165,7 @@ void desktop_view_locked_update(DesktopViewLocked* locked_view) { if(view_state == DesktopViewLockedStateCoverClosing && !desktop_view_locked_cover_move(model, true)) { + locked_view->callback(DesktopLockedEventCoversClosed, locked_view->context); model->view_state = DesktopViewLockedStateLocked; } else if( view_state == DesktopViewLockedStateCoverOpening && diff --git a/applications/services/desktop/views/desktop_view_main.c b/applications/services/desktop/views/desktop_view_main.c index 2cf1f6881..1b8518aa1 100644 --- a/applications/services/desktop/views/desktop_view_main.c +++ b/applications/services/desktop/views/desktop_view_main.c @@ -13,6 +13,8 @@ struct DesktopMainView { View* view; DesktopMainViewCallback callback; void* context; + TimerHandle_t _poweroff_timer; // Unused, kept for compatibility + bool _dummy_mode; // Unused, kept for compatibility }; void desktop_main_set_callback( diff --git a/applications/services/dialogs/application.fam b/applications/services/dialogs/application.fam index a81bd24b9..eb2b40e28 100644 --- a/applications/services/dialogs/application.fam +++ b/applications/services/dialogs/application.fam @@ -6,6 +6,6 @@ App( cdefines=["SRV_DIALOGS"], requires=["gui"], stack_size=1 * 1024, - order=30, + order=40, sdk_headers=["dialogs.h"], ) diff --git a/applications/services/dolphin/application.fam b/applications/services/dolphin/application.fam index e179862cc..5d275a7b7 100644 --- a/applications/services/dolphin/application.fam +++ b/applications/services/dolphin/application.fam @@ -5,7 +5,7 @@ App( entry_point="dolphin_srv", cdefines=["SRV_DOLPHIN"], stack_size=1 * 1024, - order=40, + order=50, sdk_headers=[ "dolphin.h", "helpers/dolphin_state.h", diff --git a/applications/services/dolphin/helpers/dolphin_deed.c b/applications/services/dolphin/helpers/dolphin_deed.c index 676e61774..8d33d9527 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.c +++ b/applications/services/dolphin/helpers/dolphin_deed.c @@ -35,11 +35,17 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {2, DolphinAppIbutton}, // DolphinDeedIbuttonAdd {3, DolphinAppBadKb}, // DolphinDeedBadKbPlayScript + {3, DolphinAppPlugin}, // DolphinDeedU2fAuthorized {1, DolphinAppPlugin}, // DolphinDeedGpioUartBridge - {1, DolphinAppPlugin}, // DolphinDeedPluginStart + // Values set to 0 to handle deeds in loader not by individual apps + {0, DolphinAppPlugin}, // DolphinDeedPluginStart + {0, DolphinAppPlugin}, // DolphinDeedPluginGameStart + {0, DolphinAppPlugin}, // DolphinDeedPluginGameWin + // Only for pentesting apps, yielded by loader + {2, DolphinAppPlugin}, // DolphinDeedPluginInternalStart }; static uint8_t dolphin_deed_limits[] = { diff --git a/applications/services/dolphin/helpers/dolphin_deed.h b/applications/services/dolphin/helpers/dolphin_deed.h index d81166417..9cf30a3e6 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.h +++ b/applications/services/dolphin/helpers/dolphin_deed.h @@ -53,9 +53,15 @@ typedef enum { DolphinDeedBadKbPlayScript, DolphinDeedU2fAuthorized, + DolphinDeedGpioUartBridge, + // Values set to 0 to handle deeds in loader not by individual apps DolphinDeedPluginStart, + DolphinDeedPluginGameStart, + DolphinDeedPluginGameWin, + // Only for pentesting apps, yielded by loader + DolphinDeedPluginInternalStart, DolphinDeedMAX, diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c index c7770f084..277b2963c 100644 --- a/applications/services/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -13,10 +13,10 @@ #define DOLPHIN_STATE_HEADER_MAGIC 0xD0 #define DOLPHIN_STATE_HEADER_VERSION 0x01 -const int DOLPHIN_LEVELS[DOLPHIN_LEVEL_COUNT] = {100, 200, 300, 450, 600, 750, 950, 1150, - 1350, 1600, 1850, 2100, 2400, 2700, 3000, 3350, - 3700, 4050, 4450, 4850, 5250, 5700, 6150, 6600, - 7100, 7600, 8100, 8650, 9200}; +const uint32_t DOLPHIN_LEVELS[] = {100, 200, 300, 450, 600, 750, 950, 1150, 1350, 1600, + 1850, 2100, 2400, 2700, 3000, 3350, 3700, 4050, 4450, 4850, + 5250, 5700, 6150, 6600, 7100, 7600, 8100, 8650, 9200}; +const size_t DOLPHIN_LEVEL_COUNT = COUNT_OF(DOLPHIN_LEVELS); DolphinState* dolphin_state_alloc() { return malloc(sizeof(DolphinState)); @@ -78,8 +78,8 @@ uint64_t dolphin_state_timestamp() { return furi_hal_rtc_datetime_to_timestamp(&datetime); } -bool dolphin_state_is_levelup(int icounter) { - for(int i = 0; i < DOLPHIN_LEVEL_COUNT; ++i) { +bool dolphin_state_is_levelup(uint32_t icounter) { + for(size_t i = 0; i < DOLPHIN_LEVEL_COUNT; ++i) { if((icounter == DOLPHIN_LEVELS[i])) { return true; } @@ -87,12 +87,8 @@ bool dolphin_state_is_levelup(int icounter) { return false; } -const int* dolphin_get_levels() { - return DOLPHIN_LEVELS; -} - -uint8_t dolphin_get_level(int icounter) { - for(int i = 0; i < DOLPHIN_LEVEL_COUNT; ++i) { +uint8_t dolphin_get_level(uint32_t icounter) { + for(size_t i = 0; i < DOLPHIN_LEVEL_COUNT; ++i) { if(icounter <= DOLPHIN_LEVELS[i]) { return i + 1; } @@ -100,20 +96,18 @@ uint8_t dolphin_get_level(int icounter) { return DOLPHIN_LEVEL_COUNT + 1; } -uint32_t dolphin_state_xp_above_last_levelup(int icounter) { - for(int i = DOLPHIN_LEVEL_COUNT; i >= 0; --i) { - if(icounter >= DOLPHIN_LEVELS[i]) { - return icounter - DOLPHIN_LEVELS[i]; - } +uint32_t dolphin_state_xp_above_last_levelup(uint32_t icounter) { + uint8_t level_idx = dolphin_get_level(icounter) - 1; // Level = index + 1 + if(level_idx > 0) { + return icounter - DOLPHIN_LEVELS[level_idx - 1]; // Get prev level } return icounter; } -uint32_t dolphin_state_xp_to_levelup(int icounter) { - for(int i = 0; i < DOLPHIN_LEVEL_COUNT; ++i) { - if(icounter <= DOLPHIN_LEVELS[i]) { - return DOLPHIN_LEVELS[i] - icounter; - } +uint32_t dolphin_state_xp_to_levelup(uint32_t icounter) { + uint8_t level_idx = dolphin_get_level(icounter) - 1; // Level = index + 1 + if(level_idx < DOLPHIN_LEVEL_COUNT) { + return DOLPHIN_LEVELS[level_idx] - icounter; } return (uint32_t)-1; } @@ -172,7 +166,7 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { FURI_LOG_D( TAG, - "icounter %ld, butthurt %ld", + "icounter %lu, butthurt %ld", dolphin_state->data.icounter, dolphin_state->data.butthurt); } @@ -194,7 +188,7 @@ void dolphin_state_increase_level(DolphinState* dolphin_state) { void dolphin_state_clear_limits(DolphinState* dolphin_state) { furi_assert(dolphin_state); - for(int i = 0; i < DolphinAppMAX; ++i) { + for(size_t i = 0; i < DolphinAppMAX; ++i) { dolphin_state->data.icounter_daily_limit[i] = 0; } dolphin_state->data.butthurt_daily_limit = 0; diff --git a/applications/services/dolphin/helpers/dolphin_state.h b/applications/services/dolphin/helpers/dolphin_state.h index 1826ce9ec..21a23f2c1 100644 --- a/applications/services/dolphin/helpers/dolphin_state.h +++ b/applications/services/dolphin/helpers/dolphin_state.h @@ -12,7 +12,8 @@ extern "C" { #define DOLPHIN_STATE_OLD_PATH INT_PATH(".dolphin.state") #define DOLPHIN_STATE_PATH CFG_PATH("dolphin.state") -#define DOLPHIN_LEVEL_COUNT 29 +extern const uint32_t DOLPHIN_LEVELS[]; +extern const size_t DOLPHIN_LEVEL_COUNT; #define BUTTHURT_MAX 14 #define BUTTHURT_MIN 0 @@ -49,17 +50,15 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed); void dolphin_state_butthurted(DolphinState* dolphin_state); -uint32_t dolphin_state_xp_to_levelup(int icounter); +uint32_t dolphin_state_xp_to_levelup(uint32_t icounter); -uint32_t dolphin_state_xp_above_last_levelup(int icounter); +uint32_t dolphin_state_xp_above_last_levelup(uint32_t icounter); -const int* dolphin_get_levels(); - -bool dolphin_state_is_levelup(int icounter); +bool dolphin_state_is_levelup(uint32_t icounter); void dolphin_state_increase_level(DolphinState* dolphin_state); -uint8_t dolphin_get_level(int icounter); +uint8_t dolphin_get_level(uint32_t icounter); #ifdef __cplusplus } diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam index 864dc6cd6..869d964dd 100644 --- a/applications/services/gui/application.fam +++ b/applications/services/gui/application.fam @@ -9,7 +9,7 @@ App( "notification", ], stack_size=2 * 1024, - order=60, + order=70, sdk_headers=[ "gui.h", "icon_i.h", diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 4310c6c75..f043369b6 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -15,6 +15,10 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = { [FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, [FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0}, [FontBatteryPercent] = {.leading_default = 11, .leading_min = 9, .height = 6, .descender = 0}, + [FontScummRomanOutline] = + {.leading_default = 12, .leading_min = 11, .height = 12, .descender = 2}, + [FontScummRoman] = {.leading_default = 12, .leading_min = 11, .height = 10, .descender = 2}, + [FontEurocorp] = {.leading_default = 12, .leading_min = 11, .height = 16, .descender = 2}, }; Canvas* canvas_init() { @@ -161,6 +165,15 @@ void canvas_set_font(Canvas* canvas, Font font) { case FontBatteryPercent: u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr); break; + case FontScummRomanOutline: + u8g2_SetFont(&canvas->fb, u8g2_font_lucasarts_scumm_subtitle_o_tr); + break; + case FontScummRoman: + u8g2_SetFont(&canvas->fb, u8g2_font_lucasarts_scumm_subtitle_r_tr); + break; + case FontEurocorp: + u8g2_SetFont(&canvas->fb, u8g2_font_eurocorp_tr); + break; default: furi_crash(NULL); break; diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index 7a2ae1873..1ae291eaa 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -20,6 +20,11 @@ typedef enum { ColorXOR = 0x02, } Color; +/** Provide defines to permit checking if new are fonts available*/ +#define CANVAS_HAS_FONT_SCUMM_ROMAN_OUTLINE = 1 +#define CANVAS_HAS_FONT_SCUMM_ROMAN = 1 +#define CANVAS_HAS_FONT_EUROCORP = 1 + /** Fonts enumeration */ typedef enum { FontPrimary, @@ -27,6 +32,9 @@ typedef enum { FontKeyboard, FontBigNumbers, FontBatteryPercent, + FontScummRomanOutline, + FontScummRoman, + FontEurocorp, // Keep last for fonts number calculation FontTotalNumber, diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index f70a9f232..4944b3ac9 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -90,6 +90,29 @@ void elements_scrollbar_pos( } } +void elements_scrollbar_horizontal( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint16_t pos, + uint16_t total) { + furi_assert(canvas); + // prevent overflows + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x, y - 3, width, 3); + // dot line + canvas_set_color(canvas, ColorBlack); + for(uint8_t i = x; i < width + x; i += 2) { + canvas_draw_dot(canvas, i, y - 2); + } + // Position block + if(total) { + float block_w = ((float)width) / total; + canvas_draw_box(canvas, x + (block_w * pos), y - 3, MAX(block_w, 1), 3); + } +} + void elements_scrollbar(Canvas* canvas, uint16_t pos, uint16_t total) { furi_assert(canvas); @@ -236,11 +259,11 @@ static size_t } if(len_px > px_left) { - uint8_t excess_symbols_approximately = - roundf((float)(len_px - px_left) / ((float)len_px / (float)text_size)); + size_t excess_symbols_approximately = + ceilf((float)(len_px - px_left) / ((float)len_px / (float)text_size)); // reduce to 5 to be sure dash fit, and next line will be at least 5 symbols long if(excess_symbols_approximately > 0) { - excess_symbols_approximately = MAX(excess_symbols_approximately, 5); + excess_symbols_approximately = MAX(excess_symbols_approximately, 5u); result = text_size - excess_symbols_approximately - 1; } else { result = text_size; diff --git a/applications/services/gui/elements.h b/applications/services/gui/elements.h index e8029f75b..f565b0ad8 100644 --- a/applications/services/gui/elements.h +++ b/applications/services/gui/elements.h @@ -65,6 +65,23 @@ void elements_scrollbar_pos( uint16_t pos, uint16_t total); +/** Draw horizontal scrollbar on canvas at specific position. + * + * @param canvas Canvas instance + * @param x scrollbar position on X axis + * @param y scrollbar position on Y axis + * @param width scrollbar width + * @param pos current element + * @param total total elements + */ +void elements_scrollbar_horizontal( + Canvas* canvas, + uint8_t x, + uint8_t y, + uint8_t width, + uint16_t pos, + uint16_t total); + /** Draw scrollbar on canvas. * @note width 3px, height equal to canvas height * diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index d6445d38e..8f0b36877 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -20,11 +20,12 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { return NULL; } -uint8_t gui_get_count_of_enabled_view_port_in_layer(Gui* gui, GuiLayer layer) { +size_t gui_active_view_port_count(Gui* gui, GuiLayer layer) { furi_assert(gui); furi_check(layer < GuiLayerMAX); - uint8_t ret = 0; + size_t ret = 0; + gui_lock(gui); ViewPortArray_it_t it; ViewPortArray_it_last(it, gui->layers[layer]); while(!ViewPortArray_end_p(it)) { @@ -34,6 +35,8 @@ uint8_t gui_get_count_of_enabled_view_port_in_layer(Gui* gui, GuiLayer layer) { } ViewPortArray_previous(it); } + gui_unlock(gui); + return ret; } diff --git a/applications/services/gui/gui.h b/applications/services/gui/gui.h index d163b1e11..7e48b1602 100644 --- a/applications/services/gui/gui.h +++ b/applications/services/gui/gui.h @@ -141,8 +141,6 @@ Canvas* gui_direct_draw_acquire(Gui* gui); */ void gui_direct_draw_release(Gui* gui); -uint8_t gui_get_count_of_enabled_view_port_in_layer(Gui* gui, GuiLayer layer); - #ifdef __cplusplus } #endif diff --git a/applications/services/gui/gui_i.h b/applications/services/gui/gui_i.h index 4cd8df313..c7aa5ddda 100644 --- a/applications/services/gui/gui_i.h +++ b/applications/services/gui/gui_i.h @@ -62,7 +62,6 @@ struct Gui { FuriMutex* mutex; // Layers and Canvas - uint16_t hide_statusbar_count; bool lockdown; bool direct_draw; ViewPortArray_t layers[GuiLayerMAX]; @@ -74,8 +73,16 @@ struct Gui { FuriPubSub* input_events; uint8_t ongoing_input; ViewPort* ongoing_input_view_port; + + uint16_t hide_statusbar_count; }; +/** Find enabled ViewPort in ViewPortArray + * + * @param[in] array The ViewPortArray instance + * + * @return ViewPort instance or NULL + */ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); /** Update GUI, request redraw @@ -84,8 +91,30 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); */ void gui_update(Gui* gui); +/** Input event callback + * + * Used to receive input from input service or to inject new input events + * + * @param[in] value The value pointer (InputEvent*) + * @param ctx The context (Gui instance) + */ void gui_input_events_callback(const void* value, void* ctx); +/** Get count of view ports in layer + * + * @param gui The Gui instance + * @param[in] layer GuiLayer that we want to get count of view ports + */ +size_t gui_active_view_port_count(Gui* gui, GuiLayer layer); + +/** Lock GUI + * + * @param gui The Gui instance + */ void gui_lock(Gui* gui); +/** Unlock GUI + * + * @param gui The Gui instance + */ void gui_unlock(Gui* gui); diff --git a/applications/services/gui/icon_i.h b/applications/services/gui/icon_i.h index 1fa6d523f..f48d30696 100644 --- a/applications/services/gui/icon_i.h +++ b/applications/services/gui/icon_i.h @@ -12,5 +12,6 @@ struct Icon { const uint8_t frame_count; const uint8_t frame_rate; const uint8_t* const* frames; + Icon* original; }; diff --git a/applications/services/gui/modules/button_panel.c b/applications/services/gui/modules/button_panel.c index 6276c6a82..a6900dee3 100644 --- a/applications/services/gui/modules/button_panel.c +++ b/applications/services/gui/modules/button_panel.c @@ -29,6 +29,9 @@ typedef struct { const Icon* name_selected; } IconElement; +LIST_DEF(IconList, IconElement, M_POD_OPLIST) +#define M_OPL_IconList_t() LIST_OPLIST(IconList) + typedef struct ButtonItem { uint32_t index; ButtonItemCallback callback; @@ -47,6 +50,7 @@ struct ButtonPanel { typedef struct { ButtonMatrix_t button_matrix; + IconList_t icons; LabelList_t labels; uint16_t reserve_x; uint16_t reserve_y; @@ -114,7 +118,6 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re ButtonArray_t* array = ButtonMatrix_get(model->button_matrix, i); ButtonArray_init(*array); ButtonArray_reserve(*array, reserve_x); - // TODO: do we need to clear allocated memory of ptr-s to ButtonItem ?? } LabelList_init(model->labels); }, @@ -158,6 +161,7 @@ void button_panel_reset(ButtonPanel* button_panel) { model->selected_item_x = 0; model->selected_item_y = 0; LabelList_reset(model->labels); + IconList_reset(model->icons); ButtonMatrix_reset(model->button_matrix); }, true); @@ -220,9 +224,17 @@ static void button_panel_view_draw_callback(Canvas* canvas, void* _model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); + for + M_EACH(icon, model->icons, IconList_t) { + canvas_draw_icon(canvas, icon->x, icon->y, icon->name); + } + for(size_t x = 0; x < model->reserve_x; ++x) { for(size_t y = 0; y < model->reserve_y; ++y) { ButtonItem* button_item = *button_panel_get_item(model, x, y); + if(!button_item) { + continue; + } const Icon* icon_name = button_item->icon.name; if((model->selected_item_x == x) && (model->selected_item_y == y)) { icon_name = button_item->icon.name_selected; @@ -418,3 +430,24 @@ void button_panel_add_label( }, true); } + +// Draw an icon but don't make it a button. +void button_panel_add_icon( + ButtonPanel* button_panel, + uint16_t x, + uint16_t y, + const Icon* icon_name) { + furi_assert(button_panel); + + with_view_model( //-V773 + button_panel->view, + ButtonPanelModel * model, + { + IconElement* icon = IconList_push_raw(model->icons); + icon->x = x; + icon->y = y; + icon->name = icon_name; + icon->name_selected = icon_name; + }, + true); +} \ No newline at end of file diff --git a/applications/services/gui/modules/button_panel.h b/applications/services/gui/modules/button_panel.h index 4733b4695..daba51d3e 100644 --- a/applications/services/gui/modules/button_panel.h +++ b/applications/services/gui/modules/button_panel.h @@ -106,6 +106,19 @@ void button_panel_add_label( Font font, const char* label_str); +/** Add a non-button icon to button_panel module. + * + * @param button_panel ButtonPanel instance + * @param x x-coordinate to place icon + * @param y y-coordinate to place icon + * @param icon_name name of the icon to draw + */ +void button_panel_add_icon( + ButtonPanel* button_panel, + uint16_t x, + uint16_t y, + const Icon* icon_name); + #ifdef __cplusplus } #endif diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index 3b62daebe..8117329cc 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -4,6 +4,7 @@ #include #include +/** ByteInput type */ struct ByteInput { View* view; }; @@ -61,11 +62,11 @@ static const ByteInputKey keyboard_keys_row_2[] = { {enter_symbol, 95, 17}, }; -/** - * @brief Get row size - * - * @param row_index Index of row - * @return uint8_t Row size +/** Get row size + * + * @param row_index Index of row + * + * @return uint8_t Row size */ static uint8_t byte_input_get_row_size(uint8_t row_index) { uint8_t row_size = 0; @@ -84,11 +85,11 @@ static uint8_t byte_input_get_row_size(uint8_t row_index) { return row_size; } -/** - * @brief Get row pointer - * - * @param row_index Index of row - * @return const ByteInputKey* Row pointer +/** Get row pointer + * + * @param row_index Index of row + * + * @return const ByteInputKey* Row pointer */ static const ByteInputKey* byte_input_get_row(uint8_t row_index) { const ByteInputKey* row = NULL; @@ -107,12 +108,12 @@ static const ByteInputKey* byte_input_get_row(uint8_t row_index) { return row; } -/** - * @brief Get text from nibble - * - * @param byte byte value - * @param high_nibble Get from high nibble, otherwise low nibble - * @return char nibble text +/** Get text from nibble + * + * @param byte byte value + * @param high_nibble Get from high nibble, otherwise low nibble + * + * @return char nibble text */ static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { if(high_nibble) { @@ -151,11 +152,10 @@ static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { const char num_to_char[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; -/** - * @brief Draw input box (common view) - * - * @param canvas - * @param model +/** Draw input box (common view) + * + * @param canvas The canvas + * @param model The model */ static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { const uint8_t text_x = 8; @@ -263,11 +263,10 @@ static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { } } -/** - * @brief Draw input box (selected view) - * - * @param canvas - * @param model +/** Draw input box (selected view) + * + * @param canvas The canvas + * @param model The model */ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model) { const uint8_t text_x = 7; @@ -324,13 +323,12 @@ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model canvas_invert_color(canvas); } -/** - * @brief Set nibble at position - * - * @param data where to set nibble - * @param position byte position - * @param value char value - * @param high_nibble set high nibble +/** Set nibble at position + * + * @param data where to set nibble + * @param position byte position + * @param value char value + * @param high_nibble set high nibble */ static void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, bool high_nibble) { switch(value) { @@ -368,29 +366,28 @@ static void byte_input_set_nibble(uint8_t* data, uint8_t position, char value, b } } -/** - * @brief What currently selected - * - * @return true - keyboard selected, false - input selected +/** What currently selected + * + * @param model The model + * + * @return true - keyboard selected, false - input selected */ static bool byte_input_keyboard_selected(ByteInputModel* model) { return model->selected_row >= 0; } -/** - * @brief Do transition from keyboard - * - * @param model +/** Do transition from keyboard + * + * @param model The model */ static void byte_input_transition_from_keyboard(ByteInputModel* model) { model->selected_row += 1; model->selected_high_nibble = true; } -/** - * @brief Increase selected byte position - * - * @param model +/** Increase selected byte position + * + * @param model The model */ static void byte_input_inc_selected_byte(ByteInputModel* model) { if(model->selected_byte < model->bytes_count - 1) { @@ -409,18 +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; + model->selected_high_nibble = !model->selected_high_nibble; //-V547 byte_input_inc_selected_byte(model); } else { - model->selected_high_nibble = !model->selected_high_nibble; + model->selected_high_nibble = !model->selected_high_nibble; //-V547 } } } -/** - * @brief Decrease selected byte position - * - * @param model +/** Decrease selected byte position + * + * @param model The model */ static void byte_input_dec_selected_byte(ByteInputModel* model) { if(model->selected_byte > 0) { @@ -438,18 +434,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; + model->selected_high_nibble = !model->selected_high_nibble; //-V547 byte_input_dec_selected_byte(model); } else { - model->selected_high_nibble = !model->selected_high_nibble; + model->selected_high_nibble = !model->selected_high_nibble; //-V547 } } } -/** - * @brief Call input callback - * - * @param model +/** Call input callback + * + * @param model The model */ static void byte_input_call_input_callback(ByteInputModel* model) { if(model->input_callback != NULL) { @@ -457,10 +452,9 @@ static void byte_input_call_input_callback(ByteInputModel* model) { } } -/** - * @brief Call changed callback - * - * @param model +/** Call changed callback + * + * @param model The model */ static void byte_input_call_changed_callback(ByteInputModel* model) { if(model->changed_callback != NULL) { @@ -468,8 +462,9 @@ static void byte_input_call_changed_callback(ByteInputModel* model) { } } -/** - * @brief Clear selected byte +/** Clear selected byte + * + * @param model The model */ static void byte_input_clear_selected_byte(ByteInputModel* model) { @@ -479,10 +474,9 @@ static void byte_input_clear_selected_byte(ByteInputModel* model) { byte_input_call_changed_callback(model); } -/** - * @brief Handle up button - * - * @param model +/** Handle up button + * + * @param model The model */ static void byte_input_handle_up(ByteInputModel* model) { if(model->selected_row > -2) { @@ -500,10 +494,9 @@ static void byte_input_handle_up(ByteInputModel* model) { } } -/** - * @brief Handle down button - * - * @param model +/** Handle down button + * + * @param model The model */ static void byte_input_handle_down(ByteInputModel* model) { if(model->selected_row != -2) { @@ -527,12 +520,11 @@ static void byte_input_handle_down(ByteInputModel* model) { } } -/** - * @brief Handle left button - * - * @param model +/** Handle left button + * + * @param model The model */ -static void byte_input_handle_left(ByteInputModel* model) { // XXX +static void byte_input_handle_left(ByteInputModel* model) { if(byte_input_keyboard_selected(model)) { if(model->selected_column > 0) { model->selected_column -= 1; @@ -548,12 +540,11 @@ static void byte_input_handle_left(ByteInputModel* model) { // XXX } } -/** - * @brief Handle right button - * - * @param model +/** Handle right button + * + * @param model The model */ -static void byte_input_handle_right(ByteInputModel* model) { // XXX +static void byte_input_handle_right(ByteInputModel* model) { if(byte_input_keyboard_selected(model)) { if(model->selected_column < byte_input_get_row_size(model->selected_row) - 1) { model->selected_column += 1; @@ -569,10 +560,9 @@ static void byte_input_handle_right(ByteInputModel* model) { // XXX } } -/** - * @brief Handle OK button - * - * @param model +/** Handle OK button + * + * @param model The model */ static void byte_input_handle_ok(ByteInputModel* model) { if(byte_input_keyboard_selected(model)) { @@ -600,11 +590,10 @@ static void byte_input_handle_ok(ByteInputModel* model) { } } -/** - * @brief Draw callback - * - * @param canvas - * @param _model +/** Draw callback + * + * @param canvas The canvas + * @param _model The model */ static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { ByteInputModel* model = _model; @@ -698,13 +687,13 @@ static void byte_input_view_draw_callback(Canvas* canvas, void* _model) { } } -/** - * @brief Input callback - * - * @param event - * @param context - * @return true - * @return false +/** Input callback + * + * @param event The event + * @param context The context + * + * @return true + * @return false */ static bool byte_input_view_input_callback(InputEvent* event, void* context) { ByteInput* byte_input = context; @@ -773,10 +762,9 @@ static bool byte_input_view_input_callback(InputEvent* event, void* context) { return consumed; } -/** - * @brief Reset all input-related data in model - * - * @param model ByteInputModel +/** Reset all input-related data in model + * + * @param model The model */ static void byte_input_reset_model_input_data(ByteInputModel* model) { model->bytes = NULL; @@ -788,11 +776,6 @@ static void byte_input_reset_model_input_data(ByteInputModel* model) { model->first_visible_byte = 0; } -/** - * @brief Allocate and initialize byte input. This byte input is used to enter bytes. - * - * @return ByteInput instance pointer - */ ByteInput* byte_input_alloc() { ByteInput* byte_input = malloc(sizeof(ByteInput)); byte_input->view = view_alloc(); @@ -816,38 +799,17 @@ ByteInput* byte_input_alloc() { return byte_input; } -/** - * @brief Deinitialize and free byte input - * - * @param byte_input Byte input instance - */ void byte_input_free(ByteInput* byte_input) { furi_assert(byte_input); view_free(byte_input->view); free(byte_input); } -/** - * @brief Get byte input view - * - * @param byte_input byte input instance - * @return View instance that can be used for embedding - */ View* byte_input_get_view(ByteInput* byte_input) { furi_assert(byte_input); return byte_input->view; } -/** - * @brief Deinitialize and free byte input - * - * @param byte_input byte input instance - * @param input_callback input callback fn - * @param changed_callback changed callback fn - * @param callback_context callback context - * @param bytes buffer to use - * @param bytes_count buffer length - */ void byte_input_set_result_callback( ByteInput* byte_input, ByteInputCallback input_callback, @@ -869,12 +831,6 @@ void byte_input_set_result_callback( true); } -/** - * @brief Set byte input header text - * - * @param byte_input byte input instance - * @param text text to be shown - */ void byte_input_set_header_text(ByteInput* byte_input, const char* text) { with_view_model( byte_input->view, ByteInputModel * model, { model->header = text; }, true); diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 991e45f24..9f51bc4d6 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -32,6 +32,8 @@ typedef enum { (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit | \ WorkerEvtFolderRefresh | WorkerEvtConfigChange) +ARRAY_DEF(_idx_last_array, int32_t) // Unused, kept for compatibility + struct BrowserWorker { FuriThread* thread; @@ -39,17 +41,20 @@ struct BrowserWorker { FuriString* path_start; FuriString* path_current; FuriString* path_next; - bool keep_selection; + int32_t _item_sel_idx; // Unused, kept for compatibility uint32_t load_offset; uint32_t load_count; bool skip_assets; bool hide_dot_files; + _idx_last_array_t _idx_last; // Unused, kept for compatibility void* cb_ctx; BrowserWorkerFolderOpenCallback folder_cb; BrowserWorkerListLoadCallback list_load_cb; BrowserWorkerListItemCallback list_item_cb; BrowserWorkerLongLoadCallback long_load_cb; + + bool keep_selection; }; static bool browser_path_is_file(FuriString* path) { @@ -574,7 +579,7 @@ 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, const char* item_name) { +void file_browser_worker_folder_refresh_sel(BrowserWorker* browser, const char* item_name) { furi_assert(browser); if(item_name != NULL) { furi_string_set(browser->path_next, item_name); @@ -584,6 +589,11 @@ void file_browser_worker_folder_refresh(BrowserWorker* browser, const char* item furi_thread_flags_set(furi_thread_get_id(browser->thread), WorkerEvtFolderRefresh); } +void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) { + UNUSED(item_idx); + file_browser_worker_folder_refresh_sel(browser, NULL); +} + void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count) { furi_assert(browser); browser->load_offset = offset; diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index d73f153d2..430c5065b 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -70,7 +70,9 @@ 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, const char* item_name); +void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx); + +void file_browser_worker_folder_refresh_sel(BrowserWorker* browser, const char* item_name); void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count); diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index 43f818d7b..7fbc69d9b 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -11,6 +14,7 @@ struct Menu { View* view; + FuriTimer* scroll_timer; }; @@ -29,7 +33,9 @@ ARRAY_DEF(MenuItemArray, MenuItem, M_POD_OPLIST); typedef struct { MenuItemArray_t items; size_t position; + size_t scroll_counter; + size_t vertical_offset; } MenuModel; static void menu_process_up(Menu* menu); @@ -38,6 +44,50 @@ static void menu_process_left(Menu* menu); static void menu_process_right(Menu* menu); static void menu_process_ok(Menu* menu); +static void menu_short_name(MenuItem* item, FuriString* name) { + furi_string_set(name, item->label); + if(furi_string_start_with_str(name, "[")) { + size_t trim = furi_string_search_str(name, "] ", 1); + if(trim != STRING_FAILURE) { + furi_string_right(name, trim + 2); + } + } +} + +static void menu_string_to_upper_case(FuriString* str) { + for(size_t i = 0; i < furi_string_size(str); i++) { + char c = furi_string_get_char(str, i); + if(c >= 'a' && c <= 'z') { + furi_string_set_char(str, i, c - 'a' + 'A'); + } + } +} + +static void menu_centered_icon( + Canvas* canvas, + MenuItem* item, + size_t x, + size_t y, + size_t width, + size_t height) { + if(item->icon) { + canvas_draw_icon_animation( + canvas, + x + (width - item->icon->icon->width) / 2, + y + (height - item->icon->icon->height) / 2, + item->icon); + } +} + +static size_t menu_scroll_counter(MenuModel* model, bool selected) { + if(!selected) return 0; + size_t scroll_counter = model->scroll_counter; + if(scroll_counter > 0) { + scroll_counter--; + } + return scroll_counter; +} + static void menu_draw_callback(Canvas* canvas, void* _model) { MenuModel* model = _model; @@ -48,9 +98,25 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { if(items_count) { MenuItem* item; size_t shift_position; + FuriString* name = furi_string_alloc(); switch(XTREME_SETTINGS()->menu_style) { + case MenuStyleList: { + for(uint8_t i = 0; i < 3; i++) { + canvas_set_font(canvas, i == 1 ? FontPrimary : FontSecondary); + shift_position = (position + items_count + i - 1) % items_count; + item = MenuItemArray_get(model->items, shift_position); + menu_centered_icon(canvas, item, 4, 3 + 22 * i, 14, 14); + menu_short_name(item, name); + size_t scroll_counter = menu_scroll_counter(model, i == 1); + elements_scrollable_text_line( + canvas, 22, 14 + 22 * i, 98, name, scroll_counter, false); + } + // Frame and scrollbar + elements_frame(canvas, 0, 21, 128 - 5, 21); + elements_scrollbar(canvas, position, items_count); + break; + } case MenuStyleWii: { - FuriString* name = furi_string_alloc(); if(items_count > 6 && position >= 4) { if(position >= items_count - 2 + (items_count % 2)) { shift_position = position - (position % 2) - 4; @@ -63,100 +129,243 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontSecondary); size_t item_i; size_t x_off, y_off; - for(int i = 0; i < 6; i++) { + for(uint8_t i = 0; i < 6; i++) { item_i = shift_position + i; if(item_i >= items_count) continue; x_off = (i / 2) * 43 + 1; y_off = (i % 2) * 32; - size_t scroll_counter = 0; - if(item_i == position) { + bool selected = item_i == position; + if(selected) { elements_slightly_rounded_box(canvas, 0 + x_off, 0 + y_off, 40, 30); canvas_set_color(canvas, ColorWhite); - scroll_counter = model->scroll_counter; - if(scroll_counter < 1) { - scroll_counter = 0; - } else { - scroll_counter -= 1; - } } item = MenuItemArray_get(model->items, item_i); - if(item->icon) { - canvas_draw_icon_animation( - canvas, - (40 - item->icon->icon->width) / 2 + x_off, - (20 - item->icon->icon->height) / 2 + y_off, - item->icon); - } - furi_string_set(name, item->label); - if(furi_string_start_with_str(name, "[")) { - size_t trim = furi_string_search_str(name, "] ", 1); - if(trim != STRING_FAILURE) { - furi_string_right(name, trim + 2); - } - } + menu_centered_icon(canvas, item, x_off, y_off, 40, 20); + menu_short_name(item, name); + size_t scroll_counter = menu_scroll_counter(model, selected); elements_scrollable_text_line_centered( canvas, 20 + x_off, 26 + y_off, 36, name, scroll_counter, false, true); - if(item_i == position) { + if(selected) { canvas_set_color(canvas, ColorBlack); } else { elements_frame(canvas, 0 + x_off, 0 + y_off, 40, 30); } } - furi_string_free(name); break; } - case MenuStyleList: { - // First line - canvas_set_font(canvas, FontSecondary); - shift_position = (0 + position + items_count - 1) % items_count; - item = MenuItemArray_get(model->items, shift_position); - if(item->icon) { - canvas_draw_icon_animation( - canvas, - 4 + (14 - item->icon->icon->width) / 2, - 3 + (14 - item->icon->icon->height) / 2, - item->icon); + case MenuStyleDsi: { + for(int8_t i = -2; i <= 2; i++) { + shift_position = (position + items_count + i) % items_count; + item = MenuItemArray_get(model->items, shift_position); + size_t width = 24; + size_t height = 26; + size_t pos_x = 64; + size_t pos_y = 36; + if(i == 0) { + width += 6; + height += 4; + elements_bold_rounded_frame( + canvas, pos_x - width / 2, pos_y - height / 2, width, height + 5); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned( + canvas, pos_x - 9, pos_y + height / 2 + 1, AlignCenter, AlignBottom, "S"); + canvas_draw_str_aligned( + canvas, pos_x, pos_y + height / 2 + 1, AlignCenter, AlignBottom, "TAR"); + canvas_draw_str_aligned( + canvas, pos_x + 9, pos_y + height / 2 + 1, AlignCenter, AlignBottom, "T"); + + canvas_draw_rframe(canvas, 0, 0, 128, 18, 3); + canvas_draw_line(canvas, 60, 18, 64, 26); + canvas_draw_line(canvas, 64, 26, 68, 18); + canvas_set_color(canvas, ColorWhite); + canvas_draw_line(canvas, 60, 17, 68, 17); + canvas_draw_box(canvas, 62, 21, 5, 2); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontPrimary); + menu_short_name(item, name); + size_t scroll_counter = menu_scroll_counter(model, true); + elements_scrollable_text_line_centered( + canvas, + pos_x, + pos_y - height / 2 - 8, + 126, + name, + scroll_counter, + false, + true); + } else { + pos_x += (width + 6) * i; + pos_y += 2; + elements_slightly_rounded_frame( + canvas, pos_x - width / 2, pos_y - height / 2, width, height); + } + menu_centered_icon(canvas, item, pos_x - 7, pos_y - 7, 14, 14); } - canvas_draw_str(canvas, 22, 14, item->label); - // Second line main + elements_scrollbar_horizontal(canvas, 0, 64, 128, position, items_count); + break; + } + case MenuStylePs4: { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 1, 1, AlignLeft, AlignTop, furi_hal_version_get_name_ptr()); + char str[10]; + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + snprintf(str, 10, "Level %i", dolphin_get_level(dolphin->state->data.icounter)); + furi_record_close(RECORD_DOLPHIN); + canvas_draw_str_aligned(canvas, 127, 1, AlignRight, AlignTop, str); + for(int8_t i = -1; i <= 4; i++) { + shift_position = position + i; + if(shift_position >= items_count) continue; + item = MenuItemArray_get(model->items, shift_position); + size_t width = 20; + size_t height = 20; + size_t pos_x = 36; + size_t pos_y = 27; + if(i == 0) { + width += 10; + height += 10; + pos_y += 2; + canvas_draw_box(canvas, pos_x - width / 2, pos_y + height / 2, width, 9); + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned( + canvas, pos_x, pos_y + height / 2 + 1, AlignCenter, AlignTop, "Start"); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontSecondary); + menu_short_name(item, name); + size_t scroll_counter = menu_scroll_counter(model, true); + elements_scrollable_text_line( + canvas, + pos_x + width / 2 + 2, + pos_y + height / 2 + 7, + 74, + name, + scroll_counter, + false); + } else { + pos_x += (width + 1) * i + (i < 0 ? -6 : 6); + } + canvas_draw_frame(canvas, pos_x - width / 2, pos_y - height / 2, width, height); + menu_centered_icon(canvas, item, pos_x - 7, pos_y - 7, 14, 14); + } + elements_scrollbar_horizontal(canvas, 0, 64, 128, position, items_count); + break; + } + case MenuStyleVertical: { + canvas_set_orientation(canvas, CanvasOrientationVertical); + shift_position = model->vertical_offset; + canvas_set_font(canvas, FontSecondary); + size_t item_i; + size_t y_off; + for(size_t i = 0; i < 8; i++) { + item_i = shift_position + i; + if(item_i >= items_count) continue; + y_off = 16 * i; + bool selected = item_i == position; + if(selected) { + elements_slightly_rounded_box(canvas, 0, y_off, 64, 16); + canvas_set_color(canvas, ColorWhite); + } + item = MenuItemArray_get(model->items, item_i); + menu_centered_icon(canvas, item, 0, y_off, 16, 16); + menu_short_name(item, name); + size_t scroll_counter = menu_scroll_counter(model, selected); + elements_scrollable_text_line( + canvas, 17, y_off + 12, 46, name, scroll_counter, false); + if(selected) { + canvas_set_color(canvas, ColorBlack); + } + } + canvas_set_orientation(canvas, CanvasOrientationHorizontal); + break; + } + case MenuStyleC64: { + FuriString* memstr = furi_string_alloc(); + + size_t index; + size_t y_off, x_off; + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 0, AlignCenter, AlignTop, "* FLIPPADORE 64 BASIC *"); + + furi_string_printf(memstr, "%d BASIC BYTES FREE", memmgr_get_free_heap()); + + canvas_draw_str_aligned( + canvas, 64, 9, AlignCenter, AlignTop, furi_string_get_cstr(memstr)); + + canvas_set_font(canvas, FontKeyboard); + + for(size_t i = 0; i < 2; i++) { + for(size_t j = 0; j < 5; j++) { + index = i * 5 + j + (position - (position % 10)); + if(index >= items_count) continue; + y_off = (9 * j) + 13; + x_off = 64 * i; + bool selected = index == position; + size_t scroll_counter = menu_scroll_counter(model, selected); + if(selected) { + canvas_draw_box(canvas, x_off, y_off + 4, 64, 9); + canvas_set_color(canvas, ColorWhite); + } + item = MenuItemArray_get(model->items, index); + menu_short_name(item, name); + + FuriString* item_str = furi_string_alloc(); + + furi_string_printf(item_str, "%d.%s", index, furi_string_get_cstr(name)); + + elements_scrollable_text_line( + canvas, x_off + 2, y_off + 12, 64, item_str, scroll_counter, false); + + furi_string_free(item_str); + + if(selected) { + canvas_set_color(canvas, ColorBlack); + } + } + } + + furi_string_free(memstr); + + break; + } + case MenuStyleEurocorp: { +#ifdef CANVAS_HAS_FONT_EUROCORP + canvas_set_font(canvas, FontEurocorp); +#else canvas_set_font(canvas, FontPrimary); - shift_position = (1 + position + items_count - 1) % items_count; - item = MenuItemArray_get(model->items, shift_position); - if(item->icon) { - canvas_draw_icon_animation( - canvas, - 4 + (14 - item->icon->icon->width) / 2, - 25 + (14 - item->icon->icon->height) / 2, - item->icon); +#endif + for(uint8_t i = 0; i < 3; i++) { + canvas_set_color(canvas, ColorBlack); + shift_position = (position + items_count + i - 1) % items_count; + item = MenuItemArray_get(model->items, shift_position); + menu_short_name(item, name); + menu_string_to_upper_case(name); + size_t scroll_counter = menu_scroll_counter(model, i == 1); + if(i == 1) { + canvas_draw_box(canvas, 0, 22, 128, 22); + canvas_set_color(canvas, ColorWhite); + // Clip corner + for(uint8_t i = 0; i < 6; i++) { + for(uint8_t j = 0; j < 6; j++) { + if(j - i >= 0) { + canvas_draw_dot(canvas, 128 - i, 22 + j - i); + } + } + } + } + elements_scrollable_text_line( + canvas, 2, 19 + 22 * i, 128 - 3, name, scroll_counter, false); } - size_t scroll_counter = model->scroll_counter; - if(scroll_counter < 1) { - scroll_counter = 0; - } else { - scroll_counter -= 1; - } - elements_scrollable_text_line_str( - canvas, 22, 36, 98, item->label, scroll_counter, false, false); - // Third line - canvas_set_font(canvas, FontSecondary); - shift_position = (2 + position + items_count - 1) % items_count; - item = MenuItemArray_get(model->items, shift_position); - if(item->icon) { - canvas_draw_icon_animation( - canvas, - 4 + (14 - item->icon->icon->width) / 2, - 47 + (14 - item->icon->icon->height) / 2, - item->icon); - } - canvas_draw_str(canvas, 22, 58, item->label); - // Frame and scrollbar - elements_frame(canvas, 0, 21, 128 - 5, 21); - elements_scrollbar(canvas, position, items_count); break; } default: break; } + furi_string_free(name); } else { canvas_draw_str(canvas, 2, 32, "Empty"); elements_scrollbar(canvas, 0, 0); @@ -165,54 +374,57 @@ static void menu_draw_callback(Canvas* canvas, void* _model) { static bool menu_input_callback(InputEvent* event, void* context) { Menu* menu = context; - bool consumed = false; + bool consumed = true; + if(XTREME_SETTINGS()->menu_style == MenuStyleVertical && + furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)) { + if(event->key == InputKeyLeft) { + event->key = InputKeyRight; + } else if(event->key == InputKeyRight) { + event->key = InputKeyLeft; + } + } if(event->type == InputTypeShort) { switch(event->key) { case InputKeyUp: - consumed = true; menu_process_up(menu); break; case InputKeyDown: - consumed = true; menu_process_down(menu); break; case InputKeyLeft: - consumed = true; menu_process_left(menu); break; case InputKeyRight: - consumed = true; menu_process_right(menu); break; case InputKeyOk: - consumed = true; menu_process_ok(menu); break; default: + consumed = false; break; } } else if(event->type == InputTypeRepeat) { switch(event->key) { case InputKeyUp: - consumed = true; menu_process_up(menu); break; case InputKeyDown: - consumed = true; menu_process_down(menu); break; case InputKeyLeft: - consumed = true; menu_process_left(menu); break; case InputKeyRight: - consumed = true; menu_process_right(menu); break; default: + consumed = false; break; } + } else { + consumed = false; } return consumed; @@ -343,7 +555,19 @@ void menu_set_selected_item(Menu* menu, uint32_t index) { menu->view, MenuModel * model, { - if(index < MenuItemArray_size(model->items)) { + if(index < MenuItemArray_size(model->items) && index != model->position) { + model->scroll_counter = 0; + + MenuItem* item = MenuItemArray_get(model->items, model->position); + if(item && item->icon) { + icon_animation_stop(item->icon); + } + + item = MenuItemArray_get(model->items, index); + if(item && item->icon) { + icon_animation_start(item->icon); + } + model->position = index; } }, @@ -351,160 +575,208 @@ void menu_set_selected_item(Menu* menu, uint32_t index) { } static void menu_process_up(Menu* menu) { + size_t position; with_view_model( menu->view, MenuModel * model, { + position = model->position; size_t count = MenuItemArray_size(model->items); - MenuItem* item = MenuItemArray_get(model->items, model->position); - if(item && item->icon) { - icon_animation_stop(item->icon); - } + size_t vertical_offset = model->vertical_offset; switch(XTREME_SETTINGS()->menu_style) { - case MenuStyleWii: - if(model->position % 2 || (model->position == count - 1 && count % 2)) { - model->position--; - } else { - model->position++; - } - model->scroll_counter = 0; - break; case MenuStyleList: - if(model->position > 0) { - model->position--; + case MenuStyleEurocorp: + if(position > 0) { + position--; + if(vertical_offset && vertical_offset == position) { + vertical_offset--; + } } else { - model->position = count - 1; + position = count - 1; + vertical_offset = count - 8; + } + break; + case MenuStyleWii: + if(position % 2 || (position == count - 1 && count % 2)) { + position--; + } else { + position++; + } + vertical_offset = CLAMP(MAX((int)position - 4, 0), MAX((int)count - 8, 0), 0); + break; + case MenuStyleC64: + if(position > 0) { + position--; + } else { + position = count - 1; } break; default: break; } - item = MenuItemArray_get(model->items, model->position); - if(item && item->icon) { - icon_animation_start(item->icon); - } + model->vertical_offset = vertical_offset; }, - true); + false); + menu_set_selected_item(menu, position); } static void menu_process_down(Menu* menu) { + size_t position; with_view_model( menu->view, MenuModel * model, { + position = model->position; size_t count = MenuItemArray_size(model->items); - MenuItem* item = MenuItemArray_get(model->items, model->position); - if(item && item->icon) { - icon_animation_stop(item->icon); - } + size_t vertical_offset = model->vertical_offset; switch(XTREME_SETTINGS()->menu_style) { - case MenuStyleWii: - if(model->position % 2 || (model->position == count - 1 && count % 2)) { - model->position--; - } else { - model->position++; - } - model->scroll_counter = 0; - break; case MenuStyleList: - if(model->position < count - 1) { - model->position++; + case MenuStyleEurocorp: + if(position < count - 1) { + position++; + if(vertical_offset < count - 8 && vertical_offset == position - 7) { + vertical_offset++; + } } else { - model->position = 0; + position = 0; + vertical_offset = 0; + } + break; + case MenuStyleWii: + if(position % 2 || (position == count - 1 && count % 2)) { + position--; + } else { + position++; + } + vertical_offset = CLAMP(MAX((int)position - 4, 0), MAX((int)count - 8, 0), 0); + break; + case MenuStyleC64: + if(position < count - 1) { + position++; + } else { + position = 0; } break; default: break; } - item = MenuItemArray_get(model->items, model->position); - if(item && item->icon) { - icon_animation_start(item->icon); - } + model->vertical_offset = vertical_offset; }, - true); + false); + menu_set_selected_item(menu, position); } static void menu_process_left(Menu* menu) { - if(XTREME_SETTINGS()->menu_style == MenuStyleList) return; + size_t position; with_view_model( menu->view, MenuModel * model, { + position = model->position; size_t count = MenuItemArray_size(model->items); - MenuItem* item = MenuItemArray_get(model->items, model->position); - if(item && item->icon) { - icon_animation_stop(item->icon); - } + size_t vertical_offset = model->vertical_offset; switch(XTREME_SETTINGS()->menu_style) { case MenuStyleWii: - if(model->position < 2) { + if(position < 2) { if(count % 2) { - model->position = count - 1; + position = count - 1; } else { - model->position = count - 2 + model->position % 2; + position = count - 2 + position % 2; } } else { - model->position -= 2; + position -= 2; } - model->scroll_counter = 0; + vertical_offset = CLAMP(MAX((int)position - 4, 0), MAX((int)count - 8, 0), 0); break; + case MenuStyleDsi: + case MenuStylePs4: + case MenuStyleVertical: + if(position > 0) { + position--; + if(vertical_offset && vertical_offset == position) { + vertical_offset--; + } + } else { + position = count - 1; + vertical_offset = count - 8; + } + break; + case MenuStyleC64: + if((position % 10) < 5) { + position = position + 5; + } else if((position % 10) >= 5) { + position = position - 5; + } default: break; } - item = MenuItemArray_get(model->items, model->position); - if(item && item->icon) { - icon_animation_start(item->icon); - } + model->vertical_offset = vertical_offset; }, - true); + false); + menu_set_selected_item(menu, position); } static void menu_process_right(Menu* menu) { - if(XTREME_SETTINGS()->menu_style == MenuStyleList) return; + size_t position; with_view_model( menu->view, MenuModel * model, { + position = model->position; size_t count = MenuItemArray_size(model->items); - MenuItem* item = MenuItemArray_get(model->items, model->position); - if(item && item->icon) { - icon_animation_stop(item->icon); - } + size_t vertical_offset = model->vertical_offset; switch(XTREME_SETTINGS()->menu_style) { case MenuStyleWii: if(count % 2) { - if(model->position == count - 1) { - model->position = 0; - } else if(model->position == count - 2) { - model->position = count - 1; + if(position == count - 1) { + position = 0; + } else if(position == count - 2) { + position = count - 1; } else { - model->position += 2; + position += 2; } } else { - model->position += 2; - if(model->position >= count) { - model->position = model->position % 2; + position += 2; + if(position >= count) { + position = position % 2; } } - model->scroll_counter = 0; + vertical_offset = CLAMP(MAX((int)position - 4, 0), MAX((int)count - 8, 0), 0); break; + case MenuStyleDsi: + case MenuStylePs4: + case MenuStyleVertical: + if(position < count - 1) { + position++; + if(vertical_offset < count - 8 && vertical_offset == position - 7) { + vertical_offset++; + } + } else { + position = 0; + vertical_offset = 0; + } + break; + case MenuStyleC64: + if(position >= (count - count) && (position % 10) < 5) { + position = position + 5; + } else if((position % 10) >= 5 && position < count) { + position = position - 5; + } default: break; } - item = MenuItemArray_get(model->items, model->position); - if(item && item->icon) { - icon_animation_start(item->icon); - } + model->vertical_offset = vertical_offset; }, - true); + false); + menu_set_selected_item(menu, position); } static void menu_process_ok(Menu* menu) { diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 368f5be28..9915a1cc8 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -99,7 +99,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { const size_t item_position = position - model->window_position; const size_t items_on_screen = submenu_items_on_screen(!furi_string_empty(model->header), model->is_vertical); - uint8_t y_offset = furi_string_empty(model->header) ? 0 : 16; + uint8_t y_offset = furi_string_empty(model->header) ? 0 : item_height; if(item_position < items_on_screen) { if(position == model->position) { diff --git a/applications/services/gui/modules/submenu.h b/applications/services/gui/modules/submenu.h index 03337d777..f4fde65cf 100644 --- a/applications/services/gui/modules/submenu.h +++ b/applications/services/gui/modules/submenu.h @@ -87,6 +87,7 @@ void submenu_reset(Submenu* submenu); void submenu_set_selected_item(Submenu* submenu, uint32_t index); /** Set optional header for submenu + * Must be called before adding items OR after adding items but also call set_selected_item() after set_header() * * @param submenu Submenu instance * @param header header to set diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 26b88670e..2df6e5a05 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -24,24 +24,24 @@ typedef struct { const char* header; char* text_buffer; size_t text_buffer_size; - size_t minimum_length; - char extra_symbols[9]; bool clear_default_text; - bool cursor_select; - size_t cursor_pos; - TextInputCallback callback; void* callback_context; uint8_t selected_row; uint8_t selected_column; - uint8_t selected_keyboard; TextInputValidatorCallback validator_callback; void* validator_callback_context; FuriString* validator_text; bool validator_message_visible; + + size_t minimum_length; + char extra_symbols[9]; + bool cursor_select; + size_t cursor_pos; + uint8_t selected_keyboard; } TextInputModel; static const uint8_t keyboard_origin_x = 1; diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index 9b22ac2f0..d58441bd7 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -12,9 +12,10 @@ struct VariableItem { FuriString* current_value_text; uint8_t values_count; VariableItemChangeCallback change_callback; + void* context; + bool locked; FuriString* locked_message; - void* context; }; ARRAY_DEF(VariableItemArray, VariableItem, M_POD_OPLIST); @@ -23,6 +24,7 @@ struct VariableItemList { View* view; VariableItemListEnterCallback callback; void* context; + FuriTimer* scroll_timer; FuriTimer* locked_timer; }; @@ -31,6 +33,8 @@ typedef struct { VariableItemArray_t items; uint8_t position; uint8_t window_position; + + FuriString* header; size_t scroll_counter; bool locked_message_visible; } VariableItemListModel; @@ -41,23 +45,34 @@ static void variable_item_list_process_left(VariableItemList* variable_item_list static void variable_item_list_process_right(VariableItemList* variable_item_list); static void variable_item_list_process_ok(VariableItemList* variable_item_list); +static size_t variable_item_list_items_on_screen(bool header) { + size_t res = 4; + return (header) ? res - 1 : res; +} + static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { VariableItemListModel* 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); - uint8_t position = 0; - VariableItemArray_it_t it; + if(!furi_string_empty(model->header)) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 11, furi_string_get_cstr(model->header)); + } canvas_set_font(canvas, FontSecondary); + + uint8_t position = 0; + VariableItemArray_it_t it; for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); VariableItemArray_next(it)) { uint8_t item_position = position - model->window_position; - uint8_t items_on_screen = 4; - uint8_t y_offset = 0; + uint8_t items_on_screen = + variable_item_list_items_on_screen(!furi_string_empty(model->header)); + uint8_t y_offset = furi_string_empty(model->header) ? 0 : item_height; if(item_position < items_on_screen) { const VariableItem* item = VariableItemArray_cref(it); @@ -139,7 +154,9 @@ void variable_item_list_set_selected_item(VariableItemList* variable_item_list, VariableItemListModel * model, { uint8_t position = index; - if(position >= VariableItemArray_size(model->items)) { + const size_t items_size = VariableItemArray_size(model->items); + + if(position >= items_size) { position = 0; } @@ -150,11 +167,15 @@ void variable_item_list_set_selected_item(VariableItemList* variable_item_list, model->window_position -= 1; } - if(VariableItemArray_size(model->items) <= 4) { + uint8_t items_on_screen = + variable_item_list_items_on_screen(!furi_string_empty(model->header)); + + if(items_size <= items_on_screen) { model->window_position = 0; } else { - if(model->window_position >= (VariableItemArray_size(model->items) - 4)) { - model->window_position = (VariableItemArray_size(model->items) - 4); + const size_t pos = items_size - items_on_screen; + if(model->window_position > pos) { + model->window_position = pos; } } }, @@ -168,6 +189,22 @@ uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_it return idx; } +void variable_item_list_set_header(VariableItemList* variable_item_list, const char* header) { + furi_assert(variable_item_list); + + with_view_model( + variable_item_list->view, + VariableItemListModel * model, + { + if(header == NULL) { + furi_string_reset(model->header); + } else { + furi_string_set_str(model->header, header); + } + }, + true); +} + static bool variable_item_list_input_callback(InputEvent* event, void* context) { VariableItemList* variable_item_list = context; furi_assert(variable_item_list); @@ -243,7 +280,8 @@ void variable_item_list_process_up(VariableItemList* variable_item_list) { variable_item_list->view, VariableItemListModel * model, { - uint8_t items_on_screen = 4; + uint8_t items_on_screen = + variable_item_list_items_on_screen(!furi_string_empty(model->header)); if(model->position > 0) { model->position--; @@ -266,7 +304,8 @@ void variable_item_list_process_down(VariableItemList* variable_item_list) { variable_item_list->view, VariableItemListModel * model, { - uint8_t items_on_screen = 4; + uint8_t items_on_screen = + variable_item_list_items_on_screen(!furi_string_empty(model->header)); if(model->position < (VariableItemArray_size(model->items) - 1)) { model->position++; if((model->position - model->window_position) > (items_on_screen - 2) && @@ -400,6 +439,7 @@ VariableItemList* variable_item_list_alloc() { VariableItemArray_init(model->items); model->position = 0; model->window_position = 0; + model->header = furi_string_alloc(); model->scroll_counter = 0; }, true); @@ -417,6 +457,7 @@ void variable_item_list_free(VariableItemList* variable_item_list) { variable_item_list->view, VariableItemListModel * model, { + furi_string_free(model->header); VariableItemArray_it_t it; for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); VariableItemArray_next(it)) { @@ -450,6 +491,7 @@ void variable_item_list_reset(VariableItemList* variable_item_list) { furi_string_free(VariableItemArray_ref(it)->locked_message); } VariableItemArray_reset(model->items); + furi_string_reset(model->header); }, false); } diff --git a/applications/services/gui/modules/variable_item_list.h b/applications/services/gui/modules/variable_item_list.h index b12fb856f..4079e5a5f 100644 --- a/applications/services/gui/modules/variable_item_list.h +++ b/applications/services/gui/modules/variable_item_list.h @@ -74,6 +74,14 @@ void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list); +/** Set optional header for variable item list + * Must be called before adding items OR after adding items but also call set_selected_item() after set_header() + * + * @param variable_item_list VariableItemList instance + * @param header header to set + */ +void variable_item_list_set_header(VariableItemList* variable_item_list, const char* header); + /** Set item current selected index * * @param item VariableItem* instance diff --git a/applications/services/gui/view_dispatcher.c b/applications/services/gui/view_dispatcher.c index 920b3c139..83f0edbea 100644 --- a/applications/services/gui/view_dispatcher.c +++ b/applications/services/gui/view_dispatcher.c @@ -272,7 +272,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e } else if(view_dispatcher->navigation_event_callback) { // Dispatch navigation event if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) { - // TODO: should we allow view_dispatcher to stop without navigation_event_callback? + // TODO FL-3514: should we allow view_dispatcher to stop without navigation_event_callback? view_dispatcher_stop(view_dispatcher); return; } diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index 8c2ff6fe0..57c0fddb4 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -7,7 +7,7 @@ #include "gui.h" #include "gui_i.h" -// TODO add mutex to view_port ops +// TODO FL-3498: add mutex to view_port ops _Static_assert(ViewPortOrientationMAX == 4, "Incorrect ViewPortOrientation count"); _Static_assert( diff --git a/applications/services/input/application.fam b/applications/services/input/application.fam index 2569db5c9..d344fc350 100644 --- a/applications/services/input/application.fam +++ b/applications/services/input/application.fam @@ -5,6 +5,6 @@ App( entry_point="input_srv", cdefines=["SRV_INPUT"], stack_size=1 * 1024, - order=70, + order=80, sdk_headers=["input.h"], ) diff --git a/applications/services/input/input_cli.c b/applications/services/input/input_cli.c index d8ab003ea..d82e90e98 100644 --- a/applications/services/input/input_cli.c +++ b/applications/services/input/input_cli.c @@ -7,6 +7,7 @@ #include #include #include +#include static void input_cli_usage() { printf("Usage:\r\n"); @@ -48,14 +49,15 @@ static void input_cli_dump(Cli* cli, FuriString* args, Input* input) { static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { UNUSED(args); Gui* gui = furi_record_open(RECORD_GUI); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); printf("Using console keyboard feedback for flipper input\r\n"); printf("\r\nUsage:\r\n"); printf("\tMove = Arrows\r\n"); printf("\tOk = Enter\r\n"); - printf("\tHold Ok = Shift + Enter\r\n"); printf("\tBack = Backspace\r\n"); + printf("\tToggle hold for next key = Space\r\n"); printf("\r\nIn Keyboard:\r\n"); printf("\tType normally on PC Keyboard\r\n"); @@ -65,11 +67,11 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { printf("\tSave Text = Enter\r\n"); printf("\r\nPress CTRL+C to stop\r\n"); + bool hold = false; while(cli_is_connected(cli)) { char in_chr = cli_getc(cli); if(in_chr == CliSymbolAsciiETX) break; InputKey send_key = InputKeyMAX; - InputType send_type = InputTypeShort; ViewPort* view_port = gui->ongoing_input_view_port; if(view_port && view_port->input_callback == view_dispatcher_input_callback) { @@ -82,6 +84,7 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { if(in_chr == 0x11) { // Ctrl Q = Close text input send_key = InputKeyBack; } else if(text_input_insert_character(text_input, in_chr)) { + notification_message(notification, &sequence_display_backlight_on); continue; } } @@ -98,12 +101,13 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { send_key = in_chr - 'A'; // Arrows in same order as InputKey } break; - case CliSymbolAsciiBackspace: // Backspace = Back + case CliSymbolAsciiBackspace: // (minicom) Backspace = Back + case CliSymbolAsciiDel: // (putty/picocom) Backspace = Back send_key = InputKeyBack; break; - case 0x4d: // Shift Enter = Hold Ok - send_type = InputTypeLong; - /* fall through */ + case CliSymbolAsciiSpace: // Space = Toggle hold next key + hold = !hold; + break; case CliSymbolAsciiCR: // Enter = Ok send_key = InputKeyOk; break; @@ -114,11 +118,14 @@ static void input_cli_keyboard(Cli* cli, FuriString* args, Input* input) { } if(send_key != InputKeyMAX) { - input_fake_event(input, send_key, send_type); + notification_message(notification, &sequence_display_backlight_on); + input_fake_event(input, send_key, hold ? InputTypeLong : InputTypeShort); + hold = false; } } furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); } static void input_cli_send_print_usage() { diff --git a/applications/services/loader/application.fam b/applications/services/loader/application.fam index 397387a13..f4d006e07 100644 --- a/applications/services/loader/application.fam +++ b/applications/services/loader/application.fam @@ -7,7 +7,7 @@ App( requires=["gui"], provides=["loader_start"], stack_size=2 * 1024, - order=80, + order=90, sdk_headers=[ "loader.h", "firmware_api/firmware_api.h", diff --git a/applications/services/loader/firmware_api/firmware_api.cpp b/applications/services/loader/firmware_api/firmware_api.cpp index 6651bf112..47554f628 100644 --- a/applications/services/loader/firmware_api/firmware_api.cpp +++ b/applications/services/loader/firmware_api/firmware_api.cpp @@ -10,6 +10,19 @@ static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); +#ifdef APP_UNIT_TESTS +constexpr HashtableApiInterface mock_elf_api_interface{ + { + .api_version_major = 0, + .api_version_minor = 0, + .resolver_callback = &elf_resolve_from_hashtable, + }, + .table_cbegin = nullptr, + .table_cend = nullptr, +}; + +const ElfApiInterface* const firmware_api_interface = &mock_elf_api_interface; +#else constexpr HashtableApiInterface elf_api_interface{ { .api_version_major = (elf_api_version >> 16), @@ -19,10 +32,10 @@ constexpr HashtableApiInterface elf_api_interface{ .table_cbegin = elf_api_table.cbegin(), .table_cend = elf_api_table.cend(), }; - const ElfApiInterface* const firmware_api_interface = &elf_api_interface; +#endif extern "C" void furi_hal_info_get_api_version(uint16_t* major, uint16_t* minor) { - *major = elf_api_interface.api_version_major; - *minor = elf_api_interface.api_version_minor; + *major = firmware_api_interface->api_version_major; + *minor = firmware_api_interface->api_version_minor; } \ No newline at end of file diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 81e25e499..bc5e7c0a3 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -1,4 +1,5 @@ #include "loader.h" +#include "core/core_defines.h" #include "loader_i.h" #include #include @@ -47,7 +48,7 @@ LoaderStatus } 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 + // TODO FL-3522: 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); @@ -81,8 +82,8 @@ void loader_start_detached_with_gui_error(Loader* loader, const char* name, cons LoaderMessage message; message.type = LoaderMessageTypeStartByNameDetachedWithGuiError; - message.start.name = name; - message.start.args = args; + message.start.name = name ? strdup(name) : NULL; + message.start.args = args ? strdup(args) : NULL; furi_message_queue_put(loader->queue, &message, FuriWaitForever); } @@ -315,18 +316,25 @@ static FlipperInternalApplication const* loader_find_application_by_name_in_list } 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( - name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT); - } - if(!application) { - application = loader_find_application_by_name_in_list( - name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT); + const struct { + const FlipperInternalApplication* list; + const uint32_t count; + } lists[] = { + {FLIPPER_APPS, FLIPPER_APPS_COUNT}, + {FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT}, + {FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT}, + {FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT}, + }; + + for(size_t i = 0; i < COUNT_OF(lists); i++) { + const FlipperInternalApplication* application = + loader_find_application_by_name_in_list(name, lists[i].list, lists[i].count); + if(application) { + return application; + } } - return application; + return NULL; } static void loader_start_app_thread(Loader* loader, FlipperInternalApplicationFlag flags) { @@ -382,9 +390,7 @@ static void loader_log_status_error( 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); + FURI_LOG_E(TAG, "Status [%d]", status); } } @@ -665,7 +671,9 @@ int32_t loader_srv(void* p) { FLIPPER_ON_SYSTEM_START[i](); } - if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { + if((furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) && FLIPPER_AUTORUN_APP_NAME && + strlen(FLIPPER_AUTORUN_APP_NAME)) { + FURI_LOG_I(TAG, "Starting autorun app: %s", FLIPPER_AUTORUN_APP_NAME); loader_do_start_by_name(loader, FLIPPER_AUTORUN_APP_NAME, NULL, NULL); } @@ -683,6 +691,8 @@ int32_t loader_srv(void* p) { LoaderStatus status = loader_do_start_by_name( loader, message.start.name, message.start.args, error_message); loader_show_gui_error(status, error_message); + if(message.start.name) free((void*)message.start.name); + if(message.start.args) free((void*)message.start.args); furi_string_free(error_message); break; } diff --git a/applications/services/loader/loader_applications.c b/applications/services/loader/loader_applications.c index 8ae91d764..2391273f6 100644 --- a/applications/services/loader/loader_applications.c +++ b/applications/services/loader/loader_applications.c @@ -6,6 +6,7 @@ #include #include #include +#include #define TAG "LoaderApplications" @@ -119,6 +120,11 @@ static void loader_pubsub_callback(const void* message, void* context) { static void loader_applications_start_app(LoaderApplicationsApp* app) { const char* name = furi_string_get_cstr(app->fap_path); + if(!furi_string_start_with_str(app->fap_path, EXT_PATH("apps/Games/")) && + !furi_string_start_with_str(app->fap_path, EXT_PATH("apps/Media/"))) { + dolphin_deed(DolphinDeedPluginInternalStart); + } + // load app FuriThreadId thread_id = furi_thread_get_current_id(); FuriPubSubSubscription* subscription = @@ -154,4 +160,4 @@ static int32_t loader_applications_thread(void* p) { } return 0; -} \ No newline at end of file +} diff --git a/applications/services/loader/loader_cli.c b/applications/services/loader/loader_cli.c index f5178f01e..263b9601a 100644 --- a/applications/services/loader/loader_cli.c +++ b/applications/services/loader/loader_cli.c @@ -14,7 +14,7 @@ static void loader_cli_print_usage() { } static void loader_cli_list() { - printf("Applications:\r\n"); + printf("Apps:\r\n"); for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { printf("\t%s\r\n", FLIPPER_APPS[i].name); } @@ -31,7 +31,7 @@ static void loader_cli_info(Loader* loader) { if(!loader_is_locked(loader)) { printf("No application is running\r\n"); } else { - // TODO: print application name ??? + // TODO FL-3513: print application name ??? printf("Application is running\r\n"); } } diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index c33e6d101..d8b4d235c 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -20,20 +20,22 @@ struct Loader { LoaderMenu* loader_menu; LoaderApplications* loader_applications; LoaderAppData app; + MenuAppList_t menu_apps; }; typedef enum { LoaderMessageTypeStartByName, - LoaderMessageTypeStartByNameDetachedWithGuiError, LoaderMessageTypeAppClosed, LoaderMessageTypeShowMenu, - LoaderMessageTypeShowSettings, LoaderMessageTypeMenuClosed, LoaderMessageTypeApplicationsClosed, LoaderMessageTypeLock, LoaderMessageTypeUnlock, LoaderMessageTypeIsLocked, + + LoaderMessageTypeStartByNameDetachedWithGuiError, + LoaderMessageTypeShowSettings, } LoaderMessageType; typedef struct { diff --git a/applications/services/notification/application.fam b/applications/services/notification/application.fam index 985dc8c50..82f94085a 100644 --- a/applications/services/notification/application.fam +++ b/applications/services/notification/application.fam @@ -7,6 +7,6 @@ App( requires=["input"], provides=["notification_settings"], stack_size=int(1.5 * 1024), - order=90, + order=100, sdk_headers=["notification.h", "notification_messages.h"], ) diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index cce407cc4..038ebc42c 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -6,7 +6,7 @@ #include #include #include - +#include #include "notification.h" #include "notification_messages.h" #include "notification_app.h" @@ -105,7 +105,10 @@ static void notification_reset_notification_led_layer(NotificationLedLayer* laye furi_hal_light_set(layer->light, layer->value[LayerInternal]); } -static void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { +static void notification_reset_notification_layer( + NotificationApp* app, + uint8_t reset_mask, + float display_brightness_set) { if(reset_mask & reset_blink_mask) { furi_hal_light_blink_stop(); } @@ -125,6 +128,9 @@ static void notification_reset_notification_layer(NotificationApp* app, uint8_t notification_sound_off(); } if(reset_mask & reset_display_mask) { + if(!float_is_equal(display_brightness_set, app->settings.display_brightness)) { + furi_hal_light_set(LightBacklight, app->settings.display_brightness * 0xFF); + } furi_timer_start(app->display_timer, notification_settings_display_off_delay_ticks(app)); } } @@ -213,13 +219,14 @@ static void notification_process_notification_message( notification_apply_notification_led_layer( &app->display, notification_message->data.led.value * display_brightness_setting); + reset_mask |= reset_display_mask; } else { + reset_mask &= ~reset_display_mask; notification_reset_notification_led_layer(&app->display); if(furi_timer_is_running(app->display_timer)) { furi_timer_stop(app->display_timer); } } - reset_mask |= reset_display_mask; break; case NotificationMessageTypeLedDisplayBacklightEnforceOn: furi_assert(app->display_led_lock < UINT8_MAX); @@ -371,7 +378,7 @@ static void notification_process_notification_message( } if(reset_notifications) { - notification_reset_notification_layer(app, reset_mask); + notification_reset_notification_layer(app, reset_mask, display_brightness_setting); } } diff --git a/applications/services/power/application.fam b/applications/services/power/application.fam index 8c3ed6bd2..f14d88c54 100644 --- a/applications/services/power/application.fam +++ b/applications/services/power/application.fam @@ -13,7 +13,7 @@ App( "power_start", ], stack_size=1 * 1024, - order=100, + order=110, sdk_headers=["power_service/power.h"], ) diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 9cefd8d4c..ecb0566a4 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -38,7 +38,6 @@ typedef struct { bool gauge_is_ok; bool is_charging; bool is_shutdown_requested; - bool is_charge_capped; float current_charger; float current_gauge; @@ -56,6 +55,8 @@ typedef struct { uint8_t charge; uint8_t health; + + bool is_charge_capped; } PowerInfo; /** Power off device diff --git a/applications/services/power/power_service/power_i.h b/applications/services/power/power_service/power_i.h index 7cb756175..3492885a7 100644 --- a/applications/services/power/power_service/power_i.h +++ b/applications/services/power/power_service/power_i.h @@ -32,11 +32,6 @@ struct Power { Gui* gui; NotificationApp* notification; FuriPubSub* event_pubsub; - FuriPubSub* settings_events; - FuriPubSub* input_events_pubsub; - FuriPubSubSubscription* input_events_subscription; - FuriPubSubSubscription* app_start_stop_subscription; - FuriPubSubSubscription* settings_events_subscription; PowerEvent event; PowerState state; @@ -47,10 +42,16 @@ struct Power { uint8_t battery_level; uint8_t power_off_timeout; + FuriMutex* api_mtx; + + FuriPubSub* settings_events; + FuriPubSub* input_events_pubsub; + FuriPubSubSubscription* input_events_subscription; + FuriPubSubSubscription* app_start_stop_subscription; + FuriPubSubSubscription* settings_events_subscription; uint32_t shutdown_idle_delay_ms; FuriTimer* auto_shutdown_timer; Loader* loader; - FuriMutex* api_mtx; }; typedef enum { diff --git a/applications/services/rpc/rpc.c b/applications/services/rpc/rpc.c index b67e05125..f07ba3352 100644 --- a/applications/services/rpc/rpc.c +++ b/applications/services/rpc/rpc.c @@ -365,47 +365,45 @@ static void rpc_session_thread_state_callback(FuriThreadState thread_state, void } RpcSession* rpc_session_open(Rpc* rpc, RpcOwner owner) { - if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) || - XTREME_SETTINGS()->allow_locked_rpc_commands) { - furi_assert(rpc); - - RpcSession* session = malloc(sizeof(RpcSession)); - session->callbacks_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - session->stream = furi_stream_buffer_alloc(RPC_BUFFER_SIZE, 1); - session->rpc = rpc; - session->terminate = false; - session->decode_error = false; - session->owner = owner; - RpcHandlerDict_init(session->handlers); - - session->decoded_message = malloc(sizeof(PB_Main)); - session->decoded_message->cb_content.funcs.decode = rpc_pb_content_callback; - session->decoded_message->cb_content.arg = session; - - session->system_contexts = malloc(COUNT_OF(rpc_systems) * sizeof(void*)); - for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { - session->system_contexts[i] = rpc_systems[i].alloc(session); - } - - RpcHandler rpc_handler = { - .message_handler = rpc_close_session_process, - .decode_submessage = NULL, - .context = session, - }; - rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler); - - session->thread = - furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); - - furi_thread_set_state_context(session->thread, session); - furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback); - - furi_thread_start(session->thread); - - return session; - } else { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock) && + !XTREME_SETTINGS()->allow_locked_rpc_commands) return NULL; + + furi_assert(rpc); + + RpcSession* session = malloc(sizeof(RpcSession)); + session->callbacks_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + session->stream = furi_stream_buffer_alloc(RPC_BUFFER_SIZE, 1); + session->rpc = rpc; + session->terminate = false; + session->decode_error = false; + session->owner = owner; + RpcHandlerDict_init(session->handlers); + + session->decoded_message = malloc(sizeof(PB_Main)); + session->decoded_message->cb_content.funcs.decode = rpc_pb_content_callback; + session->decoded_message->cb_content.arg = session; + + session->system_contexts = malloc(COUNT_OF(rpc_systems) * sizeof(void*)); + for(size_t i = 0; i < COUNT_OF(rpc_systems); ++i) { + session->system_contexts[i] = rpc_systems[i].alloc(session); } + + RpcHandler rpc_handler = { + .message_handler = rpc_close_session_process, + .decode_submessage = NULL, + .context = session, + }; + rpc_add_handler(session, PB_Main_stop_session_tag, &rpc_handler); + + session->thread = furi_thread_alloc_ex("RpcSessionWorker", 3072, rpc_session_worker, session); + + furi_thread_set_state_context(session->thread, session); + furi_thread_set_state_callback(session->thread, rpc_session_thread_state_callback); + + furi_thread_start(session->thread); + + return session; } void rpc_session_close(RpcSession* session) { diff --git a/applications/services/rpc/rpc_gui.c b/applications/services/rpc/rpc_gui.c index 9ba20a832..9eff4bca6 100644 --- a/applications/services/rpc/rpc_gui.c +++ b/applications/services/rpc/rpc_gui.c @@ -279,7 +279,7 @@ static void rpc_system_gui_start_virtual_display_process(const PB_Main* request, return; } - // TODO: consider refactoring + // TODO FL-3511: consider refactoring // Using display framebuffer size as an XBM buffer size is like comparing apples and oranges // Glad they both are 1024 for now size_t buffer_size = canvas_get_buffer_size(rpc_gui->gui->canvas); diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index 96f43e1d7..eb8938974 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -9,7 +9,7 @@ #include "storage/filesystem_api_defines.h" #include "storage/storage.h" #include -#include +#include #include #include @@ -271,6 +271,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex }; PB_Storage_ListResponse* list = &response.content.storage_list_response; + bool include_md5 = request->content.storage_list_request.include_md5; + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + bool finish = false; int i = 0; @@ -296,6 +301,21 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex list->file[i].size = fileinfo.size; list->file[i].data = NULL; list->file[i].name = name; + + if(include_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf( //-V576 + md5_path, + "%s/%s", + request->content.storage_list_request.path, + name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } else { free(name); @@ -310,8 +330,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex response.has_next = false; rpc_send_and_release(session, &response); + furi_string_free(md5); + furi_string_free(md5_path); storage_dir_close(dir); storage_file_free(dir); + storage_file_free(file); furi_record_close(RECORD_STORAGE); } @@ -569,23 +592,10 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t size_to_read = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(size_to_read); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, size_to_read); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - + if(md5_string_calc_file(file, filename, md5, &file_error)) { PB_Main response = { .command_id = request->command_id, .command_status = PB_CommandStatus_OK, @@ -595,21 +605,15 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont char* md5sum = response.content.storage_md5sum_response.md5sum; size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); - (void)md5sum_size; - furi_assert(hash_size <= ((md5sum_size - 1) / 2)); //-V547 - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); - free(hash); - free(data); - storage_file_close(file); rpc_send_and_release(session, &response); } else { rpc_send_and_release_empty( - session, request->command_id, rpc_system_storage_get_file_error(file)); + session, request->command_id, rpc_system_storage_get_error(file_error)); } + furi_string_free(md5); storage_file_free(file); furi_record_close(RECORD_STORAGE); diff --git a/applications/services/storage/application.fam b/applications/services/storage/application.fam index 260609c5c..89fa48e63 100644 --- a/applications/services/storage/application.fam +++ b/applications/services/storage/application.fam @@ -7,7 +7,7 @@ App( requires=["storage_settings"], provides=["storage_start"], stack_size=3 * 1024, - order=10, + order=0, sdk_headers=["storage.h"], ) diff --git a/applications/services/storage/filesystem_api_internal.h b/applications/services/storage/filesystem_api_internal.h index 47214f21a..f86e77614 100644 --- a/applications/services/storage/filesystem_api_internal.h +++ b/applications/services/storage/filesystem_api_internal.h @@ -62,6 +62,12 @@ struct File { * @param file pointer to file object * @return current r/w pointer position * + * @var FS_File_Api::expand + * @brief Expand the file (allocate space for it) + * @param file pointer to file object + * @param size how many bytes to allocate + * @return success flag + * * @var FS_File_Api::truncate * @brief Truncate file size to current r/w pointer position * @param file pointer to file object @@ -98,6 +104,8 @@ typedef struct { uint64_t (*size)(void* context, File* file); bool (*const sync)(void* context, File* file); bool (*const eof)(void* context, File* file); + + bool (*const expand)(void* context, File* file, uint64_t size); } FS_File_Api; /** Dir api structure @@ -169,13 +177,14 @@ typedef struct { typedef struct { FS_Error (*const stat)(void* context, const char* path, FileInfo* fileinfo); FS_Error (*const remove)(void* context, const char* path); - FS_Error (*const rename)(void* context, const char* old, const char* new); FS_Error (*const mkdir)(void* context, const char* path); FS_Error (*const fs_info)( void* context, const char* fs_path, uint64_t* total_space, uint64_t* free_space); + + FS_Error (*const rename)(void* context, const char* old, const char* new); } FS_Common_Api; /** Full filesystem api structure */ diff --git a/applications/services/storage/storage.h b/applications/services/storage/storage.h index 1ee4ab820..2743255cd 100644 --- a/applications/services/storage/storage.h +++ b/applications/services/storage/storage.h @@ -117,6 +117,13 @@ bool storage_file_seek(File* file, uint32_t offset, bool from_start); */ uint64_t storage_file_tell(File* file); +/** Expand the file (allocate space for it) + * @param file pointer to file object. + * @param size how many bytes to allocate + * @return success flag + */ +bool storage_file_expand(File* file, uint64_t size); + /** Truncates the file size to the current position of the r/w pointer * @param file pointer to file object. * @return bool success flag diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index bf1478319..e69a189e0 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -505,34 +505,16 @@ static void storage_cli_md5(Cli* cli, FuriString* path) { UNUSED(cli); Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t buffer_size = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(buffer_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, buffer_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - printf("%02x", hash[i]); - } - printf("\r\n"); - - free(hash); - free(data); + if(md5_string_calc_file(file, furi_string_get_cstr(path), md5, &file_error)) { + printf("%s\r\n", furi_string_get_cstr(md5)); } else { - storage_cli_print_error(storage_file_get_error(file)); + storage_cli_print_error(file_error); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 1cfe06a52..a5fc8cd6e 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -203,6 +203,21 @@ uint64_t storage_file_tell(File* file) { return S_RETURN_UINT64; } +bool storage_file_expand(File* file, uint64_t size) { + S_FILE_API_PROLOGUE; + S_API_PROLOGUE; + + SAData data = { + .fexpand = { + .file = file, + .size = size, + }}; + + S_API_MESSAGE(StorageCommandFileExpand); + S_API_EPILOGUE; + return S_RETURN_BOOL; +} + bool storage_file_truncate(File* file) { S_FILE_API_PROLOGUE; S_API_PROLOGUE; diff --git a/applications/services/storage/storage_message.h b/applications/services/storage/storage_message.h index e77bad6ee..8be09f117 100644 --- a/applications/services/storage/storage_message.h +++ b/applications/services/storage/storage_message.h @@ -32,6 +32,11 @@ typedef struct { bool from_start; } SADataFSeek; +typedef struct { + File* file; + uint64_t size; +} SADataFExpand; + typedef struct { File* file; const char* path; @@ -97,6 +102,7 @@ typedef union { SADataFRead fread; SADataFWrite fwrite; SADataFSeek fseek; + SADataFExpand fexpand; SADataDOpen dopen; SADataDRead dread; @@ -141,7 +147,6 @@ typedef enum { StorageCommandCommonTimestamp, StorageCommandCommonStat, StorageCommandCommonRemove, - StorageCommandCommonRename, StorageCommandCommonMkDir, StorageCommandCommonFSInfo, StorageCommandSDFormat, @@ -149,6 +154,9 @@ typedef enum { StorageCommandSDInfo, StorageCommandSDStatus, StorageCommandCommonResolvePath, + + StorageCommandFileExpand, + StorageCommandCommonRename, } StorageCommand; typedef struct { diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 0eaf23b18..fef9a9891 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -206,6 +206,19 @@ static uint64_t storage_process_file_tell(Storage* app, File* file) { return ret; } +static bool storage_process_file_expand(Storage* app, File* file, const uint64_t size) { + bool ret = false; + StorageData* storage = get_storage_by_file(file, app->storage); + + if(storage == NULL) { + file->error_id = FSE_INVALID_PARAMETER; + } else { + FS_CALL(storage, file.expand(storage, file, size)); + } + + return ret; +} + static bool storage_process_file_truncate(Storage* app, File* file) { bool ret = false; StorageData* storage = get_storage_by_file(file, app->storage); @@ -417,7 +430,7 @@ static FS_Error storage_process_common_fs_info( } /****************** Raw SD API ******************/ -// TODO think about implementing a custom storage API to split that kind of api linkage +// TODO FL-3521: think about implementing a custom storage API to split that kind of api linkage #include "storages/storage_ext.h" static FS_Error storage_process_sd_format(Storage* app) { @@ -575,6 +588,10 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { message->return_data->uint64_value = storage_process_file_tell(app, message->data->file.file); break; + case StorageCommandFileExpand: + message->return_data->bool_value = storage_process_file_expand( + app, message->data->fexpand.file, message->data->fexpand.size); + break; case StorageCommandFileTruncate: message->return_data->bool_value = storage_process_file_truncate(app, message->data->file.file); diff --git a/applications/services/storage/storages/storage_ext.c b/applications/services/storage/storages/storage_ext.c index 4fd06df73..7592b82fe 100644 --- a/applications/services/storage/storages/storage_ext.c +++ b/applications/services/storage/storages/storage_ext.c @@ -100,7 +100,7 @@ FS_Error sd_unmount_card(StorageData* storage) { storage->status = StorageStatusNotReady; error = FR_DISK_ERR; - // TODO do i need to close the files? + // TODO FL-3522: do i need to close the files? f_mount(0, sd_data->path, 0); return storage_ext_parse_error(error); @@ -353,20 +353,20 @@ static uint16_t static uint16_t storage_ext_file_write(void* ctx, File* file, const void* buff, uint16_t const bytes_to_write) { + uint16_t bytes_written = 0; #ifdef FURI_RAM_EXEC UNUSED(ctx); UNUSED(file); UNUSED(buff); UNUSED(bytes_to_write); - return FSE_NOT_READY; + file->error_id = FSE_NOT_READY; #else StorageData* storage = ctx; SDFile* file_data = storage_get_storage_file_data(file, storage); - uint16_t bytes_written = 0; file->internal_error_id = f_write(file_data, buff, bytes_to_write, &bytes_written); file->error_id = storage_ext_parse_error(file->internal_error_id); - return bytes_written; #endif + return bytes_written; } static bool @@ -396,34 +396,50 @@ static uint64_t storage_ext_file_tell(void* ctx, File* file) { return position; } +static bool storage_ext_file_expand(void* ctx, File* file, const uint64_t size) { +#ifdef FURI_RAM_EXEC + UNUSED(ctx); + UNUSED(file); + UNUSED(size); + file->error_id = FSE_NOT_READY; +#else + StorageData* storage = ctx; + SDFile* file_data = storage_get_storage_file_data(file, storage); + + file->internal_error_id = f_expand(file_data, size, 1); + file->error_id = storage_ext_parse_error(file->internal_error_id); +#endif + return (file->error_id == FSE_OK); +} + static bool storage_ext_file_truncate(void* ctx, File* file) { #ifdef FURI_RAM_EXEC UNUSED(ctx); UNUSED(file); - return FSE_NOT_READY; + file->error_id = FSE_NOT_READY; #else StorageData* storage = ctx; SDFile* file_data = storage_get_storage_file_data(file, storage); file->internal_error_id = f_truncate(file_data); file->error_id = storage_ext_parse_error(file->internal_error_id); - return (file->error_id == FSE_OK); #endif + return (file->error_id == FSE_OK); } static bool storage_ext_file_sync(void* ctx, File* file) { #ifdef FURI_RAM_EXEC UNUSED(ctx); UNUSED(file); - return FSE_NOT_READY; + file->error_id = FSE_NOT_READY; #else StorageData* storage = ctx; SDFile* file_data = storage_get_storage_file_data(file, storage); file->internal_error_id = f_sync(file_data); file->error_id = storage_ext_parse_error(file->internal_error_id); - return (file->error_id == FSE_OK); #endif + return (file->error_id == FSE_OK); } static uint64_t storage_ext_file_size(void* ctx, File* file) { @@ -609,6 +625,7 @@ static const FS_Api fs_api = { .write = storage_ext_file_write, .seek = storage_ext_file_seek, .tell = storage_ext_file_tell, + .expand = storage_ext_file_expand, .truncate = storage_ext_file_truncate, .size = storage_ext_file_size, .sync = storage_ext_file_sync, diff --git a/applications/services/storage/storages/storage_int.c b/applications/services/storage/storages/storage_int.c index 50e3a7899..765bb8e93 100644 --- a/applications/services/storage/storages/storage_int.c +++ b/applications/services/storage/storages/storage_int.c @@ -455,6 +455,14 @@ static uint64_t storage_int_file_tell(void* ctx, File* file) { return position; } +static bool storage_int_file_expand(void* ctx, File* file, const uint64_t size) { + UNUSED(ctx); + UNUSED(file); + UNUSED(size); + file->error_id = FSE_NOT_IMPLEMENTED; + return (file->error_id == FSE_OK); +} + static bool storage_int_file_truncate(void* ctx, File* file) { StorageData* storage = ctx; lfs_t* lfs = lfs_get_from_storage(storage); @@ -703,6 +711,7 @@ static const FS_Api fs_api = { .write = storage_int_file_write, .seek = storage_int_file_seek, .tell = storage_int_file_tell, + .expand = storage_int_file_expand, .truncate = storage_int_file_truncate, .size = storage_int_file_size, .sync = storage_int_file_sync, diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 0785cd53b..f2a1add06 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -170,8 +170,6 @@ const AboutDialogScreen about_screens[] = { compliance_screen, address_screen}; -const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen); - int32_t about_settings_app(void* p) { UNUSED(p); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); @@ -193,7 +191,7 @@ int32_t about_settings_app(void* p) { int32_t ret = 0; while(1) { - if(screen_index >= about_screens_count - 1) { + if(screen_index >= COUNT_OF(about_screens) - 1) { dialog_message_set_buttons(message, "Back", NULL, NULL); } else { dialog_message_set_buttons(message, "Back", NULL, "Next"); @@ -209,7 +207,7 @@ int32_t about_settings_app(void* p) { screen_index--; } } else if(screen_result == DialogMessageButtonRight) { - if(screen_index < about_screens_count - 1) { + if(screen_index < COUNT_OF(about_screens) - 1) { screen_index++; } } else if(screen_result == DialogMessageButtonBack) { diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index 936592f4c..a18de1025 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -28,6 +28,7 @@ typedef enum { typedef enum { DesktopSettingsAppKeybindActionTypeMainApp, DesktopSettingsAppKeybindActionTypeExternalApp, + DesktopSettingsAppKeybindActionTypeOpenFile, DesktopSettingsAppKeybindActionTypeMoreActions, DesktopSettingsAppKeybindActionTypeRemoveKeybind, } DesktopSettingsAppKeybindActionType; 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 index 17791ecb3..dc0acb5b5 100644 --- 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 @@ -4,6 +4,7 @@ #include #include #include +#include static bool keybinds_fap_selector_item_callback( FuriString* file_path, @@ -29,17 +30,31 @@ static void case DesktopSettingsAppKeybindActionTypeMoreActions: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsAction); break; - case DesktopSettingsAppKeybindActionTypeExternalApp: { + case DesktopSettingsAppKeybindActionTypeExternalApp: + case DesktopSettingsAppKeybindActionTypeOpenFile: { + const char* base_path; + const char* extension; + bool hide_ext; + if(index == DesktopSettingsAppKeybindActionTypeExternalApp) { + base_path = EXT_PATH("apps"); + extension = ".fap"; + hide_ext = true; + } else { + base_path = STORAGE_EXT_PATH_PREFIX; + extension = "*"; + hide_ext = false; + } const DialogsFileBrowserOptions browser_options = { - .extension = ".fap", + .extension = extension, .icon = &I_unknown_10px, + .hide_dot_files = !XTREME_SETTINGS()->show_hidden_files, .skip_assets = true, - .hide_ext = true, + .hide_ext = hide_ext, .item_loader_callback = keybinds_fap_selector_item_callback, .item_loader_context = app, - .base_path = EXT_PATH("apps"), + .base_path = base_path, }; - FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); + FuriString* temp_path = furi_string_alloc_set_str(base_path); if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { furi_string_set_str(temp_path, keybind); } @@ -83,6 +98,13 @@ void desktop_settings_scene_keybinds_action_type_on_enter(void* context) { desktop_settings_scene_keybinds_action_type_submenu_callback, app); + submenu_add_item( + submenu, + "Open File", + DesktopSettingsAppKeybindActionTypeOpenFile, + desktop_settings_scene_keybinds_action_type_submenu_callback, + app); + submenu_add_item( submenu, "More Actions", @@ -112,7 +134,13 @@ void desktop_settings_scene_keybinds_action_type_on_enter(void* context) { } if(storage_file_exists(furi_record_open(RECORD_STORAGE), keybind)) { - selected = DesktopSettingsAppKeybindActionTypeExternalApp; + FuriString* tmp = furi_string_alloc_set(keybind); + if(furi_string_end_with_str(tmp, ".fap")) { + selected = DesktopSettingsAppKeybindActionTypeExternalApp; + } else { + selected = DesktopSettingsAppKeybindActionTypeOpenFile; + } + furi_string_free(tmp); } furi_record_close(RECORD_STORAGE); diff --git a/applications/system/updater/application.fam b/applications/system/updater/application.fam index cdf98530b..a693fa6f5 100644 --- a/applications/system/updater/application.fam +++ b/applications/system/updater/application.fam @@ -10,7 +10,7 @@ App( conflicts=["desktop"], entry_point="updater_srv", stack_size=2 * 1024, - order=110, + order=130, ) App( diff --git a/applications/system/updater/scenes/updater_scene_main.c b/applications/system/updater/scenes/updater_scene_main.c index 2ef0732ca..9fd68161f 100644 --- a/applications/system/updater/scenes/updater_scene_main.c +++ b/applications/system/updater/scenes/updater_scene_main.c @@ -80,7 +80,7 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) { break; case UpdaterCustomEventSdUnmounted: - // TODO: error out, stop worker (it's probably dead actually) + // TODO FL-3499: error out, stop worker (it's probably dead actually) break; default: break; diff --git a/applications/system/updater/updater.c b/applications/system/updater/updater.c index 248435b61..e749f3ce6 100644 --- a/applications/system/updater/updater.c +++ b/applications/system/updater/updater.c @@ -118,8 +118,10 @@ void updater_free(Updater* updater) { free(updater); } -int32_t updater_srv(const char* p) { - Updater* updater = updater_alloc(p); +int32_t updater_srv(void* p) { + const char* cfgpath = p; + + Updater* updater = updater_alloc(cfgpath); view_dispatcher_run(updater->view_dispatcher); updater_free(updater); diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index 5cfd072f4..50d54cf14 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -69,8 +69,6 @@ static bool update_task_resource_unpack_cb(const char* name, bool is_directory, static void update_task_cleanup_resources(UpdateTask* update_task, const uint32_t n_tar_entries) { ResourceManifestReader* manifest_reader = resource_manifest_reader_alloc(update_task->storage); do { - // storage_simply_remove_recursive(update_task->storage, EXT_PATH("apps")); - FURI_LOG_D(TAG, "Cleaning up old manifest"); if(!resource_manifest_reader_open(manifest_reader, EXT_PATH("Manifest"))) { FURI_LOG_W(TAG, "No existing manifest"); diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_0.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_0.png new file mode 100644 index 000000000..71e85fe8f Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_0.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_1.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_1.png new file mode 100644 index 000000000..31ab932b9 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_1.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_10.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_10.png new file mode 100644 index 000000000..da8f13680 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_10.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_11.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_11.png new file mode 100644 index 000000000..9c87945b5 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_11.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_12.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_12.png new file mode 100644 index 000000000..52ecb01c1 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_12.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_13.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_13.png new file mode 100644 index 000000000..165b0635a Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_13.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_14.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_14.png new file mode 100644 index 000000000..3ad1f1c2d Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_14.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_15.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_15.png new file mode 100644 index 000000000..dace07e83 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_15.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_16.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_16.png new file mode 100644 index 000000000..2f2321888 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_16.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_17.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_17.png new file mode 100644 index 000000000..ea67b3645 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_17.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_18.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_18.png new file mode 100644 index 000000000..e4526da94 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_18.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_19.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_19.png new file mode 100644 index 000000000..b6e3de1ac Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_19.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_2.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_2.png new file mode 100644 index 000000000..a76a00022 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_2.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_20.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_20.png new file mode 100644 index 000000000..b33656867 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_20.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_21.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_21.png new file mode 100644 index 000000000..6048810f0 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_21.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_22.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_22.png new file mode 100644 index 000000000..657788f2b Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_22.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_23.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_23.png new file mode 100644 index 000000000..852e778bf Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_23.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_24.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_24.png new file mode 100644 index 000000000..d8497ee6a Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_24.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_25.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_25.png new file mode 100644 index 000000000..d647aae1b Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_25.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_26.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_26.png new file mode 100644 index 000000000..83bc94316 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_26.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_27.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_27.png new file mode 100644 index 000000000..9f4e0ce7b Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_27.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_28.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_28.png new file mode 100644 index 000000000..894394ab3 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_28.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_29.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_29.png new file mode 100644 index 000000000..63babe793 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_29.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_3.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_3.png new file mode 100644 index 000000000..630f42f2e Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_3.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_30.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_30.png new file mode 100644 index 000000000..d062254d9 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_30.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_31.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_31.png new file mode 100644 index 000000000..01ebecda7 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_31.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_32.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_32.png new file mode 100644 index 000000000..a66d6a42d Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_32.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_33.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_33.png new file mode 100644 index 000000000..e80a66743 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_33.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_34.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_34.png new file mode 100644 index 000000000..cea25e209 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_34.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_35.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_35.png new file mode 100644 index 000000000..56e05f8f9 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_35.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_36.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_36.png new file mode 100644 index 000000000..8e72a7c39 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_36.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_37.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_37.png new file mode 100644 index 000000000..9f20081e5 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_37.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_38.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_38.png new file mode 100644 index 000000000..39e620954 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_38.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_39.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_39.png new file mode 100644 index 000000000..febe3bf0b Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_39.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_4.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_4.png new file mode 100644 index 000000000..7377c6080 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_4.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_40.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_40.png new file mode 100644 index 000000000..2540fd383 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_40.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_41.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_41.png new file mode 100644 index 000000000..63106c87a Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_41.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_42.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_42.png new file mode 100644 index 000000000..21038b47a Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_42.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_43.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_43.png new file mode 100644 index 000000000..09309607c Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_43.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_44.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_44.png new file mode 100644 index 000000000..2d4b51225 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_44.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_45.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_45.png new file mode 100644 index 000000000..0501b735a Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_45.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_46.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_46.png new file mode 100644 index 000000000..1edca6aed Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_46.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_47.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_47.png new file mode 100644 index 000000000..467fe93e0 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_47.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_48.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_48.png new file mode 100644 index 000000000..fb2c54aa9 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_48.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_49.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_49.png new file mode 100644 index 000000000..74cd345fa Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_49.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_5.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_5.png new file mode 100644 index 000000000..70ed4f559 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_5.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_50.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_50.png new file mode 100644 index 000000000..32221d347 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_50.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_51.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_51.png new file mode 100644 index 000000000..a81457ddc Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_51.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_52.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_52.png new file mode 100644 index 000000000..3fc75d870 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_52.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_53.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_53.png new file mode 100644 index 000000000..2e4528c73 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_53.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_54.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_54.png new file mode 100644 index 000000000..abab31964 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_54.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_55.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_55.png new file mode 100644 index 000000000..dfa5312f5 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_55.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_56.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_56.png new file mode 100644 index 000000000..59a194b14 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_56.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_57.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_57.png new file mode 100644 index 000000000..d142a38db Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_57.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_58.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_58.png new file mode 100644 index 000000000..d6a66da8b Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_58.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_59.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_59.png new file mode 100644 index 000000000..4ab56d31e Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_59.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_6.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_6.png new file mode 100644 index 000000000..7d4f0684e Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_6.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_60.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_60.png new file mode 100644 index 000000000..0cb8722e3 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_60.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_61.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_61.png new file mode 100644 index 000000000..7da7d1adf Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_61.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_62.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_62.png new file mode 100644 index 000000000..1bfd8d303 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_62.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_7.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_7.png new file mode 100644 index 000000000..0ff0f5a3f Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_7.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_8.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_8.png new file mode 100644 index 000000000..698776737 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_8.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_9.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_9.png new file mode 100644 index 000000000..606272119 Binary files /dev/null and b/assets/dolphin/external/L1_Sad_song_128x64/frame_9.png differ diff --git a/assets/dolphin/external/L1_Sad_song_128x64/meta.txt b/assets/dolphin/external/L1_Sad_song_128x64/meta.txt new file mode 100644 index 000000000..683e69004 --- /dev/null +++ b/assets/dolphin/external/L1_Sad_song_128x64/meta.txt @@ -0,0 +1,284 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 31 +Active frames: 55 +Frames order: 0 1 2 3 4 5 0 1 6 7 8 5 9 10 11 12 13 14 15 0 1 2 3 4 5 0 1 6 7 8 5 9 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 53 54 53 55 56 57 58 59 60 61 62 4 5 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 6 + +Bubble slots: 4 + +Slot: 0 +X: 65 +Y: 14 +Text: All by myself +AlignH: Left +AlignV: Bottom +StartFrame: 45 +EndFrame: 51 + +Slot: 0 +X: 5 +Y: 16 +Text: Don't want +AlignH: Right +AlignV: Bottom +StartFrame: 56 +EndFrame: 58 + +Slot: 0 +X: 15 +Y: 15 +Text: to be +AlignH: Right +AlignV: Bottom +StartFrame: 59 +EndFrame: 60 + +Slot: 0 +X: 14 +Y: 14 +Text: All by myself +AlignH: Right +AlignV: Bottom +StartFrame: 63 +EndFrame: 69 + +Slot: 0 +X: 81 +Y: 25 +Text: anymore +AlignH: Left +AlignV: Bottom +StartFrame: 72 +EndFrame: 74 + +Slot: 1 +X: 65 +Y: 14 +Text: Nevermind +AlignH: Left +AlignV: Bottom +StartFrame: 45 +EndFrame: 48 + +Slot: 1 +X: 65 +Y: 14 +Text: I'll find +AlignH: Left +AlignV: Bottom +StartFrame: 49 +EndFrame: 52 + +Slot: 1 +X: 2 +Y: 16 +Text: Someone like +AlignH: Right +AlignV: Bottom +StartFrame: 56 +EndFrame: 58 + +Slot: 1 +X: 11 +Y: 16 +Text: youuuuu +AlignH: Right +AlignV: Bottom +StartFrame: 59 +EndFrame: 60 + +Slot: 1 +X: 3 +Y: 14 +Text: I wish nothing +AlignH: Right +AlignV: Bottom +StartFrame: 64 +EndFrame: 66 + +Slot: 1 +X: 6 +Y: 14 +Text: but the best +AlignH: Right +AlignV: Bottom +StartFrame: 67 +EndFrame: 70 + +Slot: 1 +X: 81 +Y: 25 +Text: for you +AlignH: Left +AlignV: Bottom +StartFrame: 72 +EndFrame: 74 + +Slot: 2 +X: 65 +Y: 14 +Text: What have I +AlignH: Left +AlignV: Bottom +StartFrame: 45 +EndFrame: 48 + +Slot: 2 +X: 65 +Y: 14 +Text: become +AlignH: Left +AlignV: Bottom +StartFrame: 47 +EndFrame: 51 + +Slot: 2 +X: 6 +Y: 16 +Text: My dearest +AlignH: Right +AlignV: Bottom +StartFrame: 56 +EndFrame: 58 + +Slot: 2 +X: 14 +Y: 16 +Text: friend +AlignH: Right +AlignV: Bottom +StartFrame: 59 +EndFrame: 60 + +Slot: 2 +X: 17 +Y: 14 +Text: Everyone +AlignH: Right +AlignV: Bottom +StartFrame: 63 +EndFrame: 64 + +Slot: 2 +X: 17 +Y: 14 +Text: I know +AlignH: Right +AlignV: Bottom +StartFrame: 65 +EndFrame: 67 + +Slot: 2 +X: 17 +Y: 14 +Text: goes away +AlignH: Right +AlignV: Bottom +StartFrame: 68 +EndFrame: 70 + +Slot: 2 +X: 81 +Y: 25 +Text: in the\n end +AlignH: Left +AlignV: Bottom +StartFrame: 72 +EndFrame: 74 + +Slot: 3 +X: 73 +Y: 14 +Text: We could\n have been +AlignH: Left +AlignV: Bottom +StartFrame: 45 +EndFrame: 48 + +Slot: 3 +X: 73 +Y: 14 +Text: so good\n together +AlignH: Left +AlignV: Bottom +StartFrame: 49 +EndFrame: 51 + +Slot: 3 +X: 7 +Y: 17 +Text: We could\n have lived +AlignH: Right +AlignV: Bottom +StartFrame: 55 +EndFrame: 57 + +Slot: 3 +X: 7 +Y: 17 +Text: this dance\n forever +AlignH: Right +AlignV: Bottom +StartFrame: 58 +EndFrame: 60 + +Slot: 3 +X: 12 +Y: 14 +Text: But now +AlignH: Right +AlignV: Bottom +StartFrame: 64 +EndFrame: 65 + +Slot: 3 +X: 5 +Y: 14 +Text: who's gonna +AlignH: Right +AlignV: Bottom +StartFrame: 66 +EndFrame: 67 + +Slot: 3 +X: 7 +Y: 14 +Text: dance with +AlignH: Right +AlignV: Bottom +StartFrame: 68 +EndFrame: 69 + +Slot: 3 +X: 26 +Y: 14 +Text: me? +AlignH: Right +AlignV: Bottom +StartFrame: 70 +EndFrame: 70 + +Slot: 3 +X: 81 +Y: 25 +Text: Please +AlignH: Left +AlignV: Bottom +StartFrame: 72 +EndFrame: 74 + +Slot: 3 +X: 81 +Y: 25 +Text: stay +AlignH: Left +AlignV: Bottom +StartFrame: 74 +EndFrame: 75 diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index 297c04a20..ed1a827aa 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -161,3 +161,10 @@ Max butthurt: 14 Min level: 27 Max level: 30 Weight: 3 + +Name: L1_Sad_song_128x64 +Min butthurt: 11 +Max butthurt: 14 +Min level: 14 +Max level: 30 +Weight: 4 diff --git a/assets/icons/Infrared/CoolHi_25x27.png b/assets/icons/Infrared/CoolHi_25x27.png deleted file mode 100644 index cea29a5b9..000000000 Binary files a/assets/icons/Infrared/CoolHi_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/CoolHi_hvr_25x27.png b/assets/icons/Infrared/CoolHi_hvr_25x27.png deleted file mode 100644 index 692ac7b8b..000000000 Binary files a/assets/icons/Infrared/CoolHi_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/CoolLo_25x27.png b/assets/icons/Infrared/CoolLo_25x27.png deleted file mode 100644 index 23288e44f..000000000 Binary files a/assets/icons/Infrared/CoolLo_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/CoolLo_hvr_25x27.png b/assets/icons/Infrared/CoolLo_hvr_25x27.png deleted file mode 100644 index ae5316e4d..000000000 Binary files a/assets/icons/Infrared/CoolLo_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Dehumidify_25x27.png b/assets/icons/Infrared/Dehumidify_25x27.png deleted file mode 100644 index dca77ae41..000000000 Binary files a/assets/icons/Infrared/Dehumidify_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Dehumidify_hvr_25x27.png b/assets/icons/Infrared/Dehumidify_hvr_25x27.png deleted file mode 100644 index 2c593ca8d..000000000 Binary files a/assets/icons/Infrared/Dehumidify_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Down_hvr_25x27.png b/assets/icons/Infrared/Down_hvr_25x27.png deleted file mode 100644 index 76d181924..000000000 Binary files a/assets/icons/Infrared/Down_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Exit_25x27.png b/assets/icons/Infrared/Exit_25x27.png deleted file mode 100644 index a61d021a6..000000000 Binary files a/assets/icons/Infrared/Exit_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Exit_hvr_25x27.png b/assets/icons/Infrared/Exit_hvr_25x27.png deleted file mode 100644 index c8aafe842..000000000 Binary files a/assets/icons/Infrared/Exit_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Flash_25x27.png b/assets/icons/Infrared/Flash_25x27.png deleted file mode 100644 index ddf44945c..000000000 Binary files a/assets/icons/Infrared/Flash_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Flash_hvr_25x27.png b/assets/icons/Infrared/Flash_hvr_25x27.png deleted file mode 100644 index 09c358fcc..000000000 Binary files a/assets/icons/Infrared/Flash_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/HeatHi_25x27.png b/assets/icons/Infrared/HeatHi_25x27.png deleted file mode 100644 index a1724f995..000000000 Binary files a/assets/icons/Infrared/HeatHi_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/HeatHi_hvr_25x27.png b/assets/icons/Infrared/HeatHi_hvr_25x27.png deleted file mode 100644 index b92108d68..000000000 Binary files a/assets/icons/Infrared/HeatHi_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/HeatLo_25x27.png b/assets/icons/Infrared/HeatLo_25x27.png deleted file mode 100644 index af2e59d49..000000000 Binary files a/assets/icons/Infrared/HeatLo_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/HeatLo_hvr_25x27.png b/assets/icons/Infrared/HeatLo_hvr_25x27.png deleted file mode 100644 index 6708edb36..000000000 Binary files a/assets/icons/Infrared/HeatLo_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Input_25x27.png b/assets/icons/Infrared/Input_25x27.png deleted file mode 100644 index 3c120cc32..000000000 Binary files a/assets/icons/Infrared/Input_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Input_hvr_25x27.png b/assets/icons/Infrared/Input_hvr_25x27.png deleted file mode 100644 index 7a776005e..000000000 Binary files a/assets/icons/Infrared/Input_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Mode_25x27.png b/assets/icons/Infrared/Mode_25x27.png deleted file mode 100644 index 381ba8296..000000000 Binary files a/assets/icons/Infrared/Mode_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Mode_hvr_25x27.png b/assets/icons/Infrared/Mode_hvr_25x27.png deleted file mode 100644 index 64f459f55..000000000 Binary files a/assets/icons/Infrared/Mode_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Mute_25x27.png b/assets/icons/Infrared/Mute_25x27.png deleted file mode 100644 index d8812dd4f..000000000 Binary files a/assets/icons/Infrared/Mute_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Off_25x27.png b/assets/icons/Infrared/Off_25x27.png deleted file mode 100644 index c15100606..000000000 Binary files a/assets/icons/Infrared/Off_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Off_hvr_25x27.png b/assets/icons/Infrared/Off_hvr_25x27.png deleted file mode 100644 index d5e5e6f45..000000000 Binary files a/assets/icons/Infrared/Off_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Pause_25x27.png b/assets/icons/Infrared/Pause_25x27.png deleted file mode 100644 index a371ba817..000000000 Binary files a/assets/icons/Infrared/Pause_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Pause_hvr_25x27.png b/assets/icons/Infrared/Pause_hvr_25x27.png deleted file mode 100644 index 472d583db..000000000 Binary files a/assets/icons/Infrared/Pause_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Power_25x27.png b/assets/icons/Infrared/Power_25x27.png deleted file mode 100644 index 5ae493fbe..000000000 Binary files a/assets/icons/Infrared/Power_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Power_hvr_25x27.png b/assets/icons/Infrared/Power_hvr_25x27.png deleted file mode 100644 index 9425072c0..000000000 Binary files a/assets/icons/Infrared/Power_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Rotate_25x27.png b/assets/icons/Infrared/Rotate_25x27.png deleted file mode 100644 index 648634a09..000000000 Binary files a/assets/icons/Infrared/Rotate_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Rotate_hvr_25x27.png b/assets/icons/Infrared/Rotate_hvr_25x27.png deleted file mode 100644 index a2b5cf93d..000000000 Binary files a/assets/icons/Infrared/Rotate_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Stop_25x27.png b/assets/icons/Infrared/Stop_25x27.png deleted file mode 100644 index 203a9653b..000000000 Binary files a/assets/icons/Infrared/Stop_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Stop_hvr_25x27.png b/assets/icons/Infrared/Stop_hvr_25x27.png deleted file mode 100644 index 26c19c498..000000000 Binary files a/assets/icons/Infrared/Stop_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Timer_25x27.png b/assets/icons/Infrared/Timer_25x27.png deleted file mode 100644 index 2f1853a34..000000000 Binary files a/assets/icons/Infrared/Timer_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Timer_hvr_25x27.png b/assets/icons/Infrared/Timer_hvr_25x27.png deleted file mode 100644 index d4dffa544..000000000 Binary files a/assets/icons/Infrared/Timer_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/TrackNext_25x27.png b/assets/icons/Infrared/TrackNext_25x27.png deleted file mode 100644 index 7b8f28391..000000000 Binary files a/assets/icons/Infrared/TrackNext_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/TrackPrev_25x27.png b/assets/icons/Infrared/TrackPrev_25x27.png deleted file mode 100644 index 3cd2a8da6..000000000 Binary files a/assets/icons/Infrared/TrackPrev_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/TrackPrev_hvr_25x27.png b/assets/icons/Infrared/TrackPrev_hvr_25x27.png deleted file mode 100644 index 838055341..000000000 Binary files a/assets/icons/Infrared/TrackPrev_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Up_25x27.png b/assets/icons/Infrared/Up_25x27.png deleted file mode 100644 index b81a02e8a..000000000 Binary files a/assets/icons/Infrared/Up_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/Up_hvr_25x27.png b/assets/icons/Infrared/Up_hvr_25x27.png deleted file mode 100644 index cf71e5965..000000000 Binary files a/assets/icons/Infrared/Up_hvr_25x27.png and /dev/null differ diff --git a/assets/icons/Infrared/bright_text_30x30.png b/assets/icons/Infrared/bright_text_30x30.png new file mode 100644 index 000000000..90275124c Binary files /dev/null and b/assets/icons/Infrared/bright_text_30x30.png differ diff --git a/assets/icons/Infrared/celsius_24x23.png b/assets/icons/Infrared/celsius_24x23.png new file mode 100644 index 000000000..64d7a1db1 Binary files /dev/null and b/assets/icons/Infrared/celsius_24x23.png differ diff --git a/assets/icons/Infrared/celsius_hover_24x23.png b/assets/icons/Infrared/celsius_hover_24x23.png new file mode 100644 index 000000000..0488b40f5 Binary files /dev/null and b/assets/icons/Infrared/celsius_hover_24x23.png differ diff --git a/assets/icons/Infrared/Vol_up_hvr_25x27.png b/assets/icons/Infrared/ch_down_24x21.png similarity index 70% rename from assets/icons/Infrared/Vol_up_hvr_25x27.png rename to assets/icons/Infrared/ch_down_24x21.png index 90c2df47d..8c3f81c3d 100644 Binary files a/assets/icons/Infrared/Vol_up_hvr_25x27.png and b/assets/icons/Infrared/ch_down_24x21.png differ diff --git a/assets/icons/Infrared/Vol_down_25x27.png b/assets/icons/Infrared/ch_down_hover_24x21.png similarity index 70% rename from assets/icons/Infrared/Vol_down_25x27.png rename to assets/icons/Infrared/ch_down_hover_24x21.png index d7ae44558..9b840f9ce 100644 Binary files a/assets/icons/Infrared/Vol_down_25x27.png and b/assets/icons/Infrared/ch_down_hover_24x21.png differ diff --git a/assets/icons/Infrared/Vol_up_25x27.png b/assets/icons/Infrared/ch_text_31x34.png similarity index 70% rename from assets/icons/Infrared/Vol_up_25x27.png rename to assets/icons/Infrared/ch_text_31x34.png index c4d9e87a0..30e0f584c 100644 Binary files a/assets/icons/Infrared/Vol_up_25x27.png and b/assets/icons/Infrared/ch_text_31x34.png differ diff --git a/assets/icons/Infrared/ch_up_24x21.png b/assets/icons/Infrared/ch_up_24x21.png new file mode 100644 index 000000000..fa4074d12 Binary files /dev/null and b/assets/icons/Infrared/ch_up_24x21.png differ diff --git a/assets/icons/Infrared/ch_up_hover_24x21.png b/assets/icons/Infrared/ch_up_hover_24x21.png new file mode 100644 index 000000000..944a973f4 Binary files /dev/null and b/assets/icons/Infrared/ch_up_hover_24x21.png differ diff --git a/assets/icons/Infrared/Mute_hvr_25x27.png b/assets/icons/Infrared/cool_30x51.png similarity index 73% rename from assets/icons/Infrared/Mute_hvr_25x27.png rename to assets/icons/Infrared/cool_30x51.png index 155bd9004..38a8014bd 100644 Binary files a/assets/icons/Infrared/Mute_hvr_25x27.png and b/assets/icons/Infrared/cool_30x51.png differ diff --git a/assets/icons/Infrared/dry_19x20.png b/assets/icons/Infrared/dry_19x20.png new file mode 100644 index 000000000..c689c0675 Binary files /dev/null and b/assets/icons/Infrared/dry_19x20.png differ diff --git a/assets/icons/Infrared/dry_hover_19x20.png b/assets/icons/Infrared/dry_hover_19x20.png new file mode 100644 index 000000000..5b7196ae2 Binary files /dev/null and b/assets/icons/Infrared/dry_hover_19x20.png differ diff --git a/assets/icons/Infrared/dry_text_15x5.png b/assets/icons/Infrared/dry_text_15x5.png new file mode 100644 index 000000000..7696e1fc8 Binary files /dev/null and b/assets/icons/Infrared/dry_text_15x5.png differ diff --git a/assets/icons/Infrared/exit_19x20.png b/assets/icons/Infrared/exit_19x20.png new file mode 100644 index 000000000..b9356afa2 Binary files /dev/null and b/assets/icons/Infrared/exit_19x20.png differ diff --git a/assets/icons/Infrared/exit_hover_19x20.png b/assets/icons/Infrared/exit_hover_19x20.png new file mode 100644 index 000000000..91700ee61 Binary files /dev/null and b/assets/icons/Infrared/exit_hover_19x20.png differ diff --git a/assets/icons/Infrared/exit_text_18x5.png b/assets/icons/Infrared/exit_text_18x5.png new file mode 100644 index 000000000..f742de57d Binary files /dev/null and b/assets/icons/Infrared/exit_text_18x5.png differ diff --git a/assets/icons/Infrared/fahren_24x23.png b/assets/icons/Infrared/fahren_24x23.png new file mode 100644 index 000000000..d6f55e806 Binary files /dev/null and b/assets/icons/Infrared/fahren_24x23.png differ diff --git a/assets/icons/Infrared/fahren_hover_24x23.png b/assets/icons/Infrared/fahren_hover_24x23.png new file mode 100644 index 000000000..db922c557 Binary files /dev/null and b/assets/icons/Infrared/fahren_hover_24x23.png differ diff --git a/assets/icons/Infrared/flash_19x20.png b/assets/icons/Infrared/flash_19x20.png new file mode 100644 index 000000000..205363f17 Binary files /dev/null and b/assets/icons/Infrared/flash_19x20.png differ diff --git a/assets/icons/Infrared/flash_hover_19x20.png b/assets/icons/Infrared/flash_hover_19x20.png new file mode 100644 index 000000000..31cd75039 Binary files /dev/null and b/assets/icons/Infrared/flash_hover_19x20.png differ diff --git a/assets/icons/Infrared/flash_text_21x5.png b/assets/icons/Infrared/flash_text_21x5.png new file mode 100644 index 000000000..817a45808 Binary files /dev/null and b/assets/icons/Infrared/flash_text_21x5.png differ diff --git a/assets/icons/Infrared/Down_25x27.png b/assets/icons/Infrared/heat_30x51.png similarity index 73% rename from assets/icons/Infrared/Down_25x27.png rename to assets/icons/Infrared/heat_30x51.png index c13097778..aca27c7c8 100644 Binary files a/assets/icons/Infrared/Down_25x27.png and b/assets/icons/Infrared/heat_30x51.png differ diff --git a/assets/icons/Infrared/hourglass0_24x24.png b/assets/icons/Infrared/hourglass0_24x24.png new file mode 100644 index 000000000..a382d84e2 Binary files /dev/null and b/assets/icons/Infrared/hourglass0_24x24.png differ diff --git a/assets/icons/Infrared/hourglass1_24x24.png b/assets/icons/Infrared/hourglass1_24x24.png new file mode 100644 index 000000000..b4cc7b462 Binary files /dev/null and b/assets/icons/Infrared/hourglass1_24x24.png differ diff --git a/assets/icons/Infrared/hourglass2_24x24.png b/assets/icons/Infrared/hourglass2_24x24.png new file mode 100644 index 000000000..d2c3709f7 Binary files /dev/null and b/assets/icons/Infrared/hourglass2_24x24.png differ diff --git a/assets/icons/Infrared/hourglass3_24x24.png b/assets/icons/Infrared/hourglass3_24x24.png new file mode 100644 index 000000000..e7be1e995 Binary files /dev/null and b/assets/icons/Infrared/hourglass3_24x24.png differ diff --git a/assets/icons/Infrared/hourglass4_24x24.png b/assets/icons/Infrared/hourglass4_24x24.png new file mode 100644 index 000000000..49eee2f53 Binary files /dev/null and b/assets/icons/Infrared/hourglass4_24x24.png differ diff --git a/assets/icons/Infrared/hourglass5_24x24.png b/assets/icons/Infrared/hourglass5_24x24.png new file mode 100644 index 000000000..90e1d4b4e Binary files /dev/null and b/assets/icons/Infrared/hourglass5_24x24.png differ diff --git a/assets/icons/Infrared/hourglass6_24x24.png b/assets/icons/Infrared/hourglass6_24x24.png new file mode 100644 index 000000000..e68c744f0 Binary files /dev/null and b/assets/icons/Infrared/hourglass6_24x24.png differ diff --git a/assets/icons/Infrared/input_19x20.png b/assets/icons/Infrared/input_19x20.png new file mode 100644 index 000000000..37d0b2161 Binary files /dev/null and b/assets/icons/Infrared/input_19x20.png differ diff --git a/assets/icons/Infrared/input_hover_19x20.png b/assets/icons/Infrared/input_hover_19x20.png new file mode 100644 index 000000000..fd3a50312 Binary files /dev/null and b/assets/icons/Infrared/input_hover_19x20.png differ diff --git a/assets/icons/Infrared/input_text_24x5.png b/assets/icons/Infrared/input_text_24x5.png new file mode 100644 index 000000000..77e90bb95 Binary files /dev/null and b/assets/icons/Infrared/input_text_24x5.png differ diff --git a/assets/icons/Infrared/max_24x23.png b/assets/icons/Infrared/max_24x23.png new file mode 100644 index 000000000..d4163a65f Binary files /dev/null and b/assets/icons/Infrared/max_24x23.png differ diff --git a/assets/icons/Infrared/max_hover_24x23.png b/assets/icons/Infrared/max_hover_24x23.png new file mode 100644 index 000000000..65f97b0ce Binary files /dev/null and b/assets/icons/Infrared/max_hover_24x23.png differ diff --git a/assets/icons/Infrared/menu_text_20x5.png b/assets/icons/Infrared/menu_text_20x5.png new file mode 100644 index 000000000..765278517 Binary files /dev/null and b/assets/icons/Infrared/menu_text_20x5.png differ diff --git a/assets/icons/Infrared/mode_19x20.png b/assets/icons/Infrared/mode_19x20.png new file mode 100644 index 000000000..fa0a946b6 Binary files /dev/null and b/assets/icons/Infrared/mode_19x20.png differ diff --git a/assets/icons/Infrared/mode_hover_19x20.png b/assets/icons/Infrared/mode_hover_19x20.png new file mode 100644 index 000000000..92ada65be Binary files /dev/null and b/assets/icons/Infrared/mode_hover_19x20.png differ diff --git a/assets/icons/Infrared/mode_text_20x5.png b/assets/icons/Infrared/mode_text_20x5.png new file mode 100644 index 000000000..2c8457d6f Binary files /dev/null and b/assets/icons/Infrared/mode_text_20x5.png differ diff --git a/assets/icons/Infrared/mute_19x20.png b/assets/icons/Infrared/mute_19x20.png new file mode 100644 index 000000000..410e88ac2 Binary files /dev/null and b/assets/icons/Infrared/mute_19x20.png differ diff --git a/assets/icons/Infrared/mute_hover_19x20.png b/assets/icons/Infrared/mute_hover_19x20.png new file mode 100644 index 000000000..e9a5b3510 Binary files /dev/null and b/assets/icons/Infrared/mute_hover_19x20.png differ diff --git a/assets/icons/Infrared/mute_text_19x5.png b/assets/icons/Infrared/mute_text_19x5.png new file mode 100644 index 000000000..fa2d042a6 Binary files /dev/null and b/assets/icons/Infrared/mute_text_19x5.png differ diff --git a/assets/icons/Infrared/next_19x20.png b/assets/icons/Infrared/next_19x20.png new file mode 100644 index 000000000..512b68745 Binary files /dev/null and b/assets/icons/Infrared/next_19x20.png differ diff --git a/assets/icons/Infrared/next_hover_19x20.png b/assets/icons/Infrared/next_hover_19x20.png new file mode 100644 index 000000000..c84bfdb90 Binary files /dev/null and b/assets/icons/Infrared/next_hover_19x20.png differ diff --git a/assets/icons/Infrared/next_text_19x6.png b/assets/icons/Infrared/next_text_19x6.png new file mode 100644 index 000000000..74d53171f Binary files /dev/null and b/assets/icons/Infrared/next_text_19x6.png differ diff --git a/assets/icons/Infrared/off_19x20.png b/assets/icons/Infrared/off_19x20.png new file mode 100644 index 000000000..6d68d7e6e Binary files /dev/null and b/assets/icons/Infrared/off_19x20.png differ diff --git a/assets/icons/Infrared/off_hover_19x20.png b/assets/icons/Infrared/off_hover_19x20.png new file mode 100644 index 000000000..fddd3f917 Binary files /dev/null and b/assets/icons/Infrared/off_hover_19x20.png differ diff --git a/assets/icons/Infrared/off_text_12x5.png b/assets/icons/Infrared/off_text_12x5.png new file mode 100644 index 000000000..500adbf27 Binary files /dev/null and b/assets/icons/Infrared/off_text_12x5.png differ diff --git a/assets/icons/Infrared/pause_19x20.png b/assets/icons/Infrared/pause_19x20.png new file mode 100644 index 000000000..99196d23b Binary files /dev/null and b/assets/icons/Infrared/pause_19x20.png differ diff --git a/assets/icons/Infrared/pause_hover_19x20.png b/assets/icons/Infrared/pause_hover_19x20.png new file mode 100644 index 000000000..33e7d8eb2 Binary files /dev/null and b/assets/icons/Infrared/pause_hover_19x20.png differ diff --git a/assets/icons/Infrared/pause_text_23x5.png b/assets/icons/Infrared/pause_text_23x5.png new file mode 100644 index 000000000..72c7b0403 Binary files /dev/null and b/assets/icons/Infrared/pause_text_23x5.png differ diff --git a/assets/icons/Infrared/play_19x20.png b/assets/icons/Infrared/play_19x20.png new file mode 100644 index 000000000..880e977d2 Binary files /dev/null and b/assets/icons/Infrared/play_19x20.png differ diff --git a/assets/icons/Infrared/Vol_down_hvr_25x27.png b/assets/icons/Infrared/play_hover_19x20.png similarity index 70% rename from assets/icons/Infrared/Vol_down_hvr_25x27.png rename to assets/icons/Infrared/play_hover_19x20.png index c556a037a..4c837a144 100644 Binary files a/assets/icons/Infrared/Vol_down_hvr_25x27.png and b/assets/icons/Infrared/play_hover_19x20.png differ diff --git a/assets/icons/Infrared/play_text_19x5.png b/assets/icons/Infrared/play_text_19x5.png new file mode 100644 index 000000000..c5f067bcf Binary files /dev/null and b/assets/icons/Infrared/play_text_19x5.png differ diff --git a/assets/icons/Infrared/power_19x20.png b/assets/icons/Infrared/power_19x20.png new file mode 100644 index 000000000..12b927973 Binary files /dev/null and b/assets/icons/Infrared/power_19x20.png differ diff --git a/assets/icons/Infrared/power_hover_19x20.png b/assets/icons/Infrared/power_hover_19x20.png new file mode 100644 index 000000000..3a41249ff Binary files /dev/null and b/assets/icons/Infrared/power_hover_19x20.png differ diff --git a/assets/icons/Infrared/power_text_24x5.png b/assets/icons/Infrared/power_text_24x5.png new file mode 100644 index 000000000..88fff8e33 Binary files /dev/null and b/assets/icons/Infrared/power_text_24x5.png differ diff --git a/assets/icons/Infrared/prev_19x20.png b/assets/icons/Infrared/prev_19x20.png new file mode 100644 index 000000000..8d17cec57 Binary files /dev/null and b/assets/icons/Infrared/prev_19x20.png differ diff --git a/assets/icons/Infrared/prev_hover_19x20.png b/assets/icons/Infrared/prev_hover_19x20.png new file mode 100644 index 000000000..be9dce700 Binary files /dev/null and b/assets/icons/Infrared/prev_hover_19x20.png differ diff --git a/assets/icons/Infrared/prev_text_19x5.png b/assets/icons/Infrared/prev_text_19x5.png new file mode 100644 index 000000000..473b89745 Binary files /dev/null and b/assets/icons/Infrared/prev_text_19x5.png differ diff --git a/assets/icons/Infrared/rotate_19x20.png b/assets/icons/Infrared/rotate_19x20.png new file mode 100644 index 000000000..21f36da52 Binary files /dev/null and b/assets/icons/Infrared/rotate_19x20.png differ diff --git a/assets/icons/Infrared/rotate_hover_19x20.png b/assets/icons/Infrared/rotate_hover_19x20.png new file mode 100644 index 000000000..581e90987 Binary files /dev/null and b/assets/icons/Infrared/rotate_hover_19x20.png differ diff --git a/assets/icons/Infrared/rotate_text_24x5.png b/assets/icons/Infrared/rotate_text_24x5.png new file mode 100644 index 000000000..851eac339 Binary files /dev/null and b/assets/icons/Infrared/rotate_text_24x5.png differ diff --git a/assets/icons/Infrared/speed_text_30x30.png b/assets/icons/Infrared/speed_text_30x30.png new file mode 100644 index 000000000..c09eb1ab7 Binary files /dev/null and b/assets/icons/Infrared/speed_text_30x30.png differ diff --git a/assets/icons/Infrared/stop_19x20.png b/assets/icons/Infrared/stop_19x20.png new file mode 100644 index 000000000..28fc962a7 Binary files /dev/null and b/assets/icons/Infrared/stop_19x20.png differ diff --git a/assets/icons/Infrared/stop_hover_19x20.png b/assets/icons/Infrared/stop_hover_19x20.png new file mode 100644 index 000000000..60935caf7 Binary files /dev/null and b/assets/icons/Infrared/stop_hover_19x20.png differ diff --git a/assets/icons/Infrared/stop_text_19x5.png b/assets/icons/Infrared/stop_text_19x5.png new file mode 100644 index 000000000..2aea41ca1 Binary files /dev/null and b/assets/icons/Infrared/stop_text_19x5.png differ diff --git a/assets/icons/Infrared/timer_19x20.png b/assets/icons/Infrared/timer_19x20.png new file mode 100644 index 000000000..6c2bdf6bb Binary files /dev/null and b/assets/icons/Infrared/timer_19x20.png differ diff --git a/assets/icons/Infrared/timer_hover_19x20.png b/assets/icons/Infrared/timer_hover_19x20.png new file mode 100644 index 000000000..560ef895d Binary files /dev/null and b/assets/icons/Infrared/timer_hover_19x20.png differ diff --git a/assets/icons/Infrared/timer_text_23x5.png b/assets/icons/Infrared/timer_text_23x5.png new file mode 100644 index 000000000..ad2229f73 Binary files /dev/null and b/assets/icons/Infrared/timer_text_23x5.png differ diff --git a/assets/icons/Infrared/vol_ac_text_30x30.png b/assets/icons/Infrared/vol_ac_text_30x30.png new file mode 100644 index 000000000..068266d62 Binary files /dev/null and b/assets/icons/Infrared/vol_ac_text_30x30.png differ diff --git a/assets/icons/Infrared/vol_tv_text_29x34.png b/assets/icons/Infrared/vol_tv_text_29x34.png new file mode 100644 index 000000000..caef54c25 Binary files /dev/null and b/assets/icons/Infrared/vol_tv_text_29x34.png differ diff --git a/assets/icons/Infrared/voldown_24x21.png b/assets/icons/Infrared/voldown_24x21.png new file mode 100644 index 000000000..a80c59594 Binary files /dev/null and b/assets/icons/Infrared/voldown_24x21.png differ diff --git a/assets/icons/Infrared/voldown_hover_24x21.png b/assets/icons/Infrared/voldown_hover_24x21.png new file mode 100644 index 000000000..6bc57c70e Binary files /dev/null and b/assets/icons/Infrared/voldown_hover_24x21.png differ diff --git a/assets/icons/Infrared/volup_24x21.png b/assets/icons/Infrared/volup_24x21.png new file mode 100644 index 000000000..688552751 Binary files /dev/null and b/assets/icons/Infrared/volup_24x21.png differ diff --git a/assets/icons/Infrared/volup_hover_24x21.png b/assets/icons/Infrared/volup_hover_24x21.png new file mode 100644 index 000000000..5d790e796 Binary files /dev/null and b/assets/icons/Infrared/volup_hover_24x21.png differ diff --git a/assets/protobuf b/assets/protobuf index 08a907d95..7e011a958 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 08a907d95733600becc41c0602ef5ee4c4baf782 +Subproject commit 7e011a95863716e72e7c6b5d552bca241d688304 diff --git a/assets/resources/apps_data/esp_flasher/Black_Magic.bin b/assets/resources/apps_data/esp_flasher/Black_Magic.bin deleted file mode 100644 index 337fbe603..000000000 Binary files a/assets/resources/apps_data/esp_flasher/Black_Magic.bin and /dev/null differ diff --git a/assets/resources/apps_data/esp_flasher/Evil_Portal.bin b/assets/resources/apps_data/esp_flasher/Evil_Portal.bin deleted file mode 100644 index 993755ee5..000000000 Binary files a/assets/resources/apps_data/esp_flasher/Evil_Portal.bin and /dev/null differ diff --git a/assets/resources/apps_data/esp_flasher/Marauder.bin b/assets/resources/apps_data/esp_flasher/Marauder.bin deleted file mode 100644 index 379d6c313..000000000 Binary files a/assets/resources/apps_data/esp_flasher/Marauder.bin and /dev/null differ diff --git a/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/boot_app0.bin b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/boot_app0.bin new file mode 100644 index 000000000..13562cabb Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/boot_app0.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.bin b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.bin new file mode 100644 index 000000000..9d463929d Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.bootloader.bin b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.bootloader.bin new file mode 100644 index 000000000..25c62a975 Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.bootloader.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.partitions.bin b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.partitions.bin new file mode 100644 index 000000000..2108af950 Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/evilportal/EvilPortal.ino.partitions.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/bootloader.bin b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/bootloader.bin new file mode 100644 index 000000000..05f670d0c Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/bootloader.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/marauder_dev_board_pro.bin b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/marauder_dev_board_pro.bin new file mode 100644 index 000000000..6b9a53e88 Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/marauder_dev_board_pro.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/partitions.bin b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/partitions.bin new file mode 100644 index 000000000..fa042d671 Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/devpro-wroom/marauder/partitions.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/blackmagic.bin b/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/blackmagic.bin new file mode 100644 index 000000000..2bbc8fddb Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/blackmagic.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/bootloader.bin b/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/bootloader.bin new file mode 100644 index 000000000..ca1dfe208 Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/bootloader.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/partition-table.bin b/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/partition-table.bin new file mode 100644 index 000000000..a62b43b54 Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/blackmagic/partition-table.bin differ diff --git a/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/dualboot.bin b/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/dualboot.bin new file mode 100644 index 000000000..868e3fbfd Binary files /dev/null and b/assets/resources/apps_data/esp_flasher/assets/wifidev-s2/dualboot.bin differ diff --git a/assets/resources/apps_data/evil_portal/ap.config.txt b/assets/resources/apps_data/evil_portal/ap.config.txt deleted file mode 100644 index a69f2a885..000000000 --- a/assets/resources/apps_data/evil_portal/ap.config.txt +++ /dev/null @@ -1 +0,0 @@ -Xtreme Demo \ No newline at end of file diff --git a/assets/resources/apps_data/evil_portal/xtreme.html b/assets/resources/apps_data/evil_portal/html/xtreme.html similarity index 100% rename from assets/resources/apps_data/evil_portal/xtreme.html rename to assets/resources/apps_data/evil_portal/html/xtreme.html diff --git a/assets/resources/apps_data/magspoof/SamyExampleImage.mag b/assets/resources/apps_data/magspoof/SamyExampleImage.mag new file mode 100644 index 000000000..f452c0b69 --- /dev/null +++ b/assets/resources/apps_data/magspoof/SamyExampleImage.mag @@ -0,0 +1,7 @@ +Filetype: Flipper Mag device +Version: 1 +# Mag device track data +# Data matching image example in Samy's repo, for ease of comparison +Track 1: %B426684131234567^LASTNAME/FIRST^YYMMSSSDDDDDDDDDDDDDDDDDDDDDDDDD? +Track 2: ;426684131234567=230188855555555555555? +Track 3: diff --git a/assets/resources/apps_data/magspoof/SamyExpiredCard.mag b/assets/resources/apps_data/magspoof/SamyExpiredCard.mag new file mode 100644 index 000000000..012cc97a8 --- /dev/null +++ b/assets/resources/apps_data/magspoof/SamyExpiredCard.mag @@ -0,0 +1,7 @@ +Filetype: Flipper Mag device +Version: 1 +# Mag device track data +# Found in samyk's MagSpoof branch f150bb783237051fba7e4e6ed96a722e542a9663; using as test data, card is long expired +Track 1: %B493173000682759^URISTA HDZ-IVAN JAVIER ^150220100234000000? +Track 2: ;493173000682759=15022100000234? +Track 3: diff --git a/assets/resources/apps_data/magspoof/TestMagstripe.mag b/assets/resources/apps_data/magspoof/TestMagstripe.mag new file mode 100644 index 000000000..376869e6c --- /dev/null +++ b/assets/resources/apps_data/magspoof/TestMagstripe.mag @@ -0,0 +1,6 @@ +Filetype: Flipper Mag device +Version: 1 +# Mag device track data +Track 1: %B123456781234567^LASTNAME/FIRST^YYMMSSSDDDDDDDDDDDDDDDDDDDDDDDDD? +Track 2: ;123456781234567=YYMMSSSDDDDDDDDDDDDDD? +Track 3: diff --git a/assets/resources/badkb/Install_qFlipper_windows.txt b/assets/resources/badkb/Install_qFlipper_windows.txt deleted file mode 100644 index 942823215..000000000 --- a/assets/resources/badkb/Install_qFlipper_windows.txt +++ /dev/null @@ -1,42 +0,0 @@ -REM Written by @dexv -DELAY 2000 -GUI r -DELAY 500 -STRING powershell -ENTER -DELAY 1000 -STRING $url = "https://update.flipperzero.one/qFlipper/release/windows-amd64/portable" -ENTER -STRING $output = "$env:USERPROFILE\Documents\qFlipper.zip" -ENTER -STRING $destination = "$env:USERPROFILE\Documents\qFlipper" -ENTER -STRING $shortcutPath = "$env:USERPROFILE\Desktop\qFlipper.lnk" -ENTER -STRING $scriptPath = "$env:USERPROFILE\Documents\qFlipperInstall.ps1" -ENTER -STRING $driverPath = "$destination\STM32 Driver" -ENTER -STRING $installBat = "$driverPath\install.bat" -ENTER -STRING (New-Object System.Net.WebClient).DownloadFile($url, $output) -ENTER -STRING Expand-Archive -Path $output -DestinationPath $destination -Force -ENTER -STRING Set-Location -Path $destination -ENTER -STRING Start-Process -FilePath ".\qFlipper.exe" -ENTER -STRING Start-Process -Wait -FilePath "cmd.exe" -ArgumentList "/c $installBat" -ENTER -STRING $shell = New-Object -ComObject WScript.Shell -ENTER -STRING $shortcut = $shell.CreateShortcut($shortcutPath) -ENTER -STRING $shortcut.TargetPath = "$destination\qFlipper.exe" -ENTER -STRING $shortcut.Save() -ENTER -DELAY 500 -STRING "powershell -ExecutionPolicy Bypass -File $scriptPath" -ENTER diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 1fe72fde7..22bd473a7 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +# Last Updated 16th Aug, 2023 +# Last Checked 16th Aug, 2023 # # Model: Electrolux EACM-16 HP/N3 name: Off @@ -78,6 +78,7 @@ duty_cycle: 0.330000 data: 8972 4491 592 1651 592 1655 598 532 599 535 597 542 600 541 601 544 598 1656 597 526 595 1652 591 1658 595 539 593 545 597 545 597 546 596 537 594 529 592 535 596 1653 600 534 597 541 601 539 592 552 600 533 598 525 596 530 591 538 593 539 592 1665 598 1662 591 1673 601 533 598 526 595 533 598 532 600 534 597 540 591 548 594 550 592 542 600 523 598 528 593 536 595 537 594 543 599 542 600 543 599 517 594 7937 593 531 601 526 595 535 597 537 594 542 600 541 601 543 599 1654 599 523 598 528 593 536 596 538 594 542 600 541 590 552 600 532 599 524 597 528 593 536 595 537 595 541 601 539 593 551 591 542 600 522 599 527 594 536 595 537 594 543 599 540 591 552 600 532 600 523 598 527 594 535 596 537 595 542 600 540 591 552 600 532 600 523 598 528 593 536 595 538 593 543 599 541 601 543 599 535 596 527 594 532 600 531 601 534 597 540 592 549 593 552 600 534 597 525 596 529 592 1655 598 534 597 1656 597 1661 592 1671 592 1644 599 7934 596 529 592 535 597 535 597 538 593 544 598 543 599 545 597 538 593 1650 593 535 596 534 597 536 595 540 591 547 595 547 595 536 595 526 595 529 592 536 595 535 596 539 593 546 596 547 595 538 593 528 593 531 601 529 592 541 601 536 596 545 597 548 594 540 592 532 600 526 595 535 596 1656 597 541 601 540 592 553 599 534 597 526 595 532 599 531 600 533 598 539 593 548 594 552 600 535 596 1647 596 531 590 538 593 1656 597 538 594 545 597 545 597 518 593 # # Model: Daichi DA25AVQS1-W +# Compatible brands: Gree, Tosot name: Dh type: raw frequency: 38000 @@ -100,7 +101,7 @@ name: Heat_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 148 110377 9082 4422 707 499 706 500 704 1603 703 1606 701 505 700 507 699 506 700 507 699 506 700 1607 700 1608 700 1609 699 506 699 507 699 506 699 507 699 507 699 506 700 507 699 507 699 507 699 1608 699 1607 699 507 699 507 699 507 699 507 699 507 699 1609 699 507 699 1608 699 507 699 507 699 1609 699 507 699 19940 700 506 700 506 699 507 699 507 699 506 700 506 700 507 699 507 699 507 700 507 699 506 699 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 698 507 700 507 699 507 699 507 699 507 699 507 699 508 698 508 698 507 699 507 699 508 698 1610 699 508 698 +data: 9082 4422 707 499 706 500 704 1603 703 1606 701 505 700 507 699 506 700 507 699 506 700 1607 700 1608 700 1609 699 506 699 507 699 506 699 507 699 507 699 506 700 507 699 507 699 507 699 1608 699 1607 699 507 699 507 699 507 699 507 699 507 699 1609 699 507 699 1608 699 507 699 507 699 1609 699 507 699 19940 700 506 700 506 699 507 699 507 699 506 700 506 700 507 699 507 699 507 700 507 699 506 699 507 699 507 699 507 699 507 699 507 699 507 699 507 699 507 698 507 700 507 699 507 699 507 699 507 699 507 699 508 698 508 698 507 699 507 699 508 698 1610 699 508 698 # name: Heat_lo type: raw @@ -115,6 +116,7 @@ duty_cycle: 0.330000 data: 9106 4398 731 499 706 500 705 502 702 504 701 505 701 505 701 1606 701 505 701 1607 701 505 701 506 700 1607 700 506 700 506 700 505 700 505 701 506 700 506 700 506 699 506 700 506 700 1607 700 506 700 506 700 506 700 505 701 506 700 506 700 1608 699 506 700 1608 699 506 700 506 700 1608 700 506 700 19941 701 1606 700 505 701 505 701 506 700 505 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 701 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 700 506 699 506 700 506 700 1608 700 1607 700 506 700 506 700 # # Model: Saturn CS-TL09CHR +# Compatible brands: Tora name: Dh type: raw frequency: 38000 @@ -152,7 +154,7 @@ duty_cycle: 0.330000 data: 3013 1709 463 1085 462 1084 463 387 461 355 469 355 444 1084 463 387 461 378 436 1084 462 1084 487 355 469 1059 463 387 460 355 444 1083 463 1098 464 386 437 1083 463 1083 489 361 463 360 463 1082 464 361 462 376 462 1085 461 363 460 364 459 364 459 365 458 365 459 364 459 380 459 365 458 365 458 365 458 365 458 365 458 365 458 365 459 380 458 365 459 365 458 365 459 365 458 365 458 1089 458 365 458 380 459 1088 459 365 458 365 458 365 458 365 459 365 458 365 458 381 458 365 458 365 458 365 458 1089 458 365 458 365 458 365 458 381 458 365 458 365 458 365 458 365 458 365 458 365 458 365 458 381 457 366 457 366 457 366 457 366 457 366 458 365 458 366 457 381 458 366 458 366 457 366 457 366 457 366 457 366 457 366 457 381 458 366 457 366 457 366 457 366 457 366 457 366 457 366 457 382 457 366 457 366 457 366 457 366 457 366 457 367 457 366 457 382 457 367 456 1090 457 1090 456 1090 457 1090 457 1090 456 367 457 372 457 # # Model: Olimpia Splendid OS-SEAMH09EI -# Also compatible with Timberk, Royal Clima, Ballu +# Compatible brands: Timberk, Royal Clima, Ballu name: Dh type: raw frequency: 38000 @@ -225,13 +227,13 @@ name: Cool_hi type: raw frequency: 38000 duty_cycle: 0.330000 -data: 315 101050 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254 +data: 3094 3056 3093 4437 580 1648 572 534 576 1649 582 525 574 530 580 1646 574 1653 578 529 570 534 576 529 571 534 576 529 570 1655 576 1651 580 527 572 532 578 1647 573 1654 577 1651 580 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 526 573 531 579 525 574 531 579 525 574 531 579 1646 574 532 578 526 573 531 579 526 573 531 579 526 573 1652 579 527 572 1653 578 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 572 532 578 527 572 532 578 527 572 532 578 526 573 1652 579 527 572 532 578 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 526 573 531 579 525 574 530 580 525 574 530 580 525 574 530 580 524 575 529 581 524 575 529 571 534 576 528 571 533 577 528 571 533 577 528 571 533 577 527 572 532 578 527 572 532 578 526 573 531 579 526 573 531 579 525 574 531 579 525 574 530 580 525 574 1650 581 525 574 1651 580 1647 573 533 577 527 572 1653 578 528 572 1654 577 1650 581 1646 574 71637 254 # name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 284 19161 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547 +data: 3098 3053 3096 4435 572 1656 575 532 578 1648 572 534 576 530 570 1682 549 1652 579 527 572 534 576 1649 571 1656 575 1652 579 1649 571 1656 575 531 579 527 572 1653 578 1649 571 1656 575 531 579 527 572 532 578 527 572 533 577 527 572 533 577 527 573 532 578 527 572 532 578 527 573 532 578 527 572 1652 579 527 572 533 577 528 571 533 577 528 571 533 577 1648 572 533 577 1649 571 535 575 530 569 536 574 531 569 536 574 530 569 536 574 530 570 535 575 530 570 535 575 530 569 535 575 530 569 535 575 1649 571 535 575 531 568 536 574 531 568 536 574 531 568 536 574 531 569 536 574 530 569 536 574 530 569 535 575 530 569 535 575 530 569 535 575 530 570 535 575 529 570 534 576 529 570 534 576 529 570 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 534 576 528 571 533 577 528 571 533 577 528 572 533 577 528 571 533 577 528 572 1652 579 527 572 1653 578 529 570 534 576 529 570 535 575 529 570 1654 577 1677 554 1673 547 # name: Off type: raw @@ -256,7 +258,7 @@ name: Cool_lo type: raw frequency: 38000 duty_cycle: 0.330000 -data: 301 132136 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356 +data: 5036 2167 337 1766 361 689 358 692 366 684 363 1770 357 692 366 684 363 718 329 690 357 1776 361 687 360 1773 364 1767 360 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 718 329 1773 364 684 363 718 329 691 356 694 364 716 331 719 328 1775 362 1769 358 1774 363 1768 359 1772 365 714 333 1770 357 1774 363 716 331 719 328 722 336 715 332 718 329 721 326 724 334 716 331 719 328 722 336 715 332 718 329 1773 364 1767 360 1772 354 1777 360 719 328 721 326 725 333 717 330 29455 5036 2139 354 1777 360 688 359 691 367 714 333 1770 356 692 366 684 363 687 360 690 357 1776 361 688 359 1773 364 1768 359 689 358 1775 362 1769 357 1774 363 1768 359 1773 364 684 363 687 360 1773 364 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 1777 360 1771 355 693 365 685 362 1771 355 693 365 1768 359 1773 364 1767 360 689 358 692 366 685 362 1771 355 1775 362 687 360 690 357 1775 362 687 360 690 357 693 365 716 331 689 358 1774 363 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1771 355 693 365 1768 358 1773 364 684 363 687 360 690 357 693 365 1768 359 690 357 1776 361 688 359 691 356 694 364 686 361 689 358 692 366 685 362 1770 356 693 365 685 362 688 359 691 356 1777 360 1771 355 693 365 686 361 689 358 692 366 685 362 1770 356 # name: Off type: raw @@ -474,7 +476,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 CS-21H3A-B155 / KRF-51G/79F +# Model: Chigo KRF-51G/79F name: Off type: raw frequency: 38000 @@ -511,80 +513,42 @@ frequency: 38000 duty_cycle: 0.330000 data: 6059 7355 592 1633 592 1633 592 1633 592 1633 592 1633 592 1633 592 1633 618 1608 617 489 617 490 616 490 616 491 590 517 589 517 614 492 590 517 590 1636 589 1636 590 1636 590 1636 589 1636 590 1636 590 1636 590 1636 590 517 589 517 589 517 589 517 590 517 589 517 589 517 589 517 589 517 589 1636 614 492 590 1636 590 1636 590 1636 589 1636 590 1636 589 1637 589 517 589 1636 590 517 589 517 589 517 614 492 590 517 589 1636 590 517 589 1636 590 517 589 517 589 517 589 517 590 1636 590 517 589 1636 590 517 589 1636 589 1636 590 1636 590 1637 589 517 589 517 589 1637 588 1637 589 517 589 1637 589 1636 590 517 589 1636 590 1636 590 517 589 517 589 1637 589 517 589 517 589 1637 589 517 589 518 588 1637 589 518 588 1637 589 517 590 1637 588 518 588 518 588 1637 589 518 588 1637 589 518 588 1637 589 518 588 1637 589 1637 589 7357 589 # -# Model: Tosot T24H-ILF/I/T24H-ILU/O -# Compatible Brands: Gree -name: Off -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9072 4445 602 1586 603 477 601 478 600 479 599 480 598 481 598 482 597 482 572 1617 597 1593 572 1617 597 483 572 507 572 507 572 507 572 507 572 507 597 482 597 482 572 507 597 482 572 1618 571 507 572 507 572 507 572 507 572 507 572 507 572 1618 571 507 572 1618 572 507 572 507 572 1618 572 507 652 20153 572 507 597 482 572 507 572 507 597 482 597 482 572 507 596 483 572 507 572 507 572 507 572 507 572 507 572 1618 596 483 572 507 596 483 595 484 572 507 597 482 595 484 597 482 597 482 572 507 572 507 597 482 572 507 597 482 572 507 597 483 571 1618 597 482 675 40391 9179 4421 599 1590 599 481 597 482 597 482 597 481 598 482 597 482 597 482 597 1592 597 1592 598 1592 597 482 597 482 597 482 597 482 597 482 597 482 597 482 597 482 597 482 572 507 597 1593 597 482 572 507 572 507 572 507 572 507 572 508 571 1618 597 1593 596 1593 572 507 572 507 572 1618 597 482 678 20152 597 483 596 482 597 482 597 482 597 482 597 482 572 507 597 482 597 482 597 482 572 507 597 482 597 482 597 482 597 482 597 482 572 507 597 482 597 482 572 507 597 482 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 596 482 572 507 572 -# -name: Cool_hi -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9049 4446 575 1613 577 503 601 478 601 1590 599 480 598 482 572 507 572 507 572 508 571 507 572 507 572 507 572 508 571 508 571 507 573 507 572 508 571 508 572 507 572 508 571 507 572 1618 572 508 572 507 572 508 571 508 571 508 571 508 571 1618 572 508 571 1618 572 508 571 508 571 1619 571 508 651 20157 572 507 572 507 572 507 572 507 572 507 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 572 507 572 507 572 507 572 507 573 507 572 508 571 507 572 508 571 507 572 507 573 507 572 507 572 507 572 508 571 1619 571 507 572 1618 572 508 651 40424 9180 4422 599 1591 598 482 597 483 572 1618 572 507 598 482 597 482 572 507 572 507 598 482 572 507 598 482 572 507 598 482 597 482 572 507 572 507 572 507 573 507 572 507 573 507 572 1618 572 507 598 482 572 507 572 507 597 483 596 483 597 1593 572 1618 572 1618 572 508 572 507 572 1618 572 508 678 20157 572 507 597 482 597 482 597 481 598 482 572 507 572 507 572 507 572 507 597 482 572 507 597 482 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 508 572 507 572 507 572 507 572 507 572 507 572 508 571 508 571 1619 571 1618 572 508 571 507 572 -# -name: Cool_lo -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9075 4419 603 1586 603 476 602 478 600 1589 600 480 599 481 598 482 597 482 597 1593 597 1593 597 1593 597 482 597 482 572 507 572 507 596 483 573 507 572 507 573 507 598 482 572 507 573 1618 572 508 572 507 572 508 572 507 596 483 572 508 571 1618 596 484 572 1618 572 507 572 507 572 1618 572 508 651 20158 597 482 598 482 597 482 597 482 597 482 572 507 572 507 572 507 597 482 597 483 597 482 572 507 573 507 597 1594 572 507 597 482 572 507 597 482 598 482 572 508 571 508 571 508 572 507 572 507 597 482 597 483 572 508 571 508 571 508 596 483 597 1593 596 1593 652 40424 9181 4422 599 1591 598 481 598 482 572 1618 598 482 597 481 598 482 598 481 598 1593 597 1593 597 1592 598 482 598 482 597 482 597 482 598 482 597 482 597 482 598 482 597 482 597 482 598 1593 597 482 597 482 597 482 597 483 597 482 572 508 597 1593 596 1594 597 1593 597 483 571 508 571 1618 572 508 678 20157 572 507 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 573 507 572 507 573 507 572 507 573 507 572 507 572 507 572 507 572 508 571 507 572 508 571 508 571 508 571 508 572 1618 572 508 571 1618 572 -# -name: Heat_lo -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9048 4447 576 503 576 503 601 1589 600 1590 599 481 573 507 572 507 572 507 572 1618 572 1618 572 1618 572 507 572 507 573 507 572 508 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 572 507 573 507 572 508 571 507 572 507 572 508 571 1618 572 507 572 1618 572 508 571 507 572 1618 572 508 651 20157 572 507 572 507 572 507 572 507 572 508 572 507 572 507 572 507 572 508 571 508 572 508 571 508 571 508 571 1619 571 507 572 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 509 570 509 570 1620 570 1620 570 1620 570 1620 649 40424 9153 4450 573 506 573 507 572 1619 571 1618 572 508 571 508 571 508 572 508 571 1619 571 1619 571 1619 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 508 571 509 570 509 571 1619 571 509 571 509 570 509 571 509 570 509 570 509 570 1620 570 1621 569 1644 546 509 570 509 570 1620 570 510 676 20158 571 508 572 507 572 508 571 508 571 508 571 508 571 508 571 508 572 508 571 508 571 508 571 508 572 508 571 508 571 508 571 508 571 509 570 509 570 509 571 509 570 509 570 534 545 511 568 533 546 534 546 533 546 534 545 534 545 1645 545 534 545 1645 545 1644 546 -# -name: Heat_hi -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9072 4445 602 476 602 476 602 1588 600 1589 600 480 599 481 597 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 482 597 482 597 1592 598 481 598 481 598 481 598 481 598 481 598 481 598 1592 598 481 598 1592 598 481 598 481 598 1592 598 481 678 20126 598 481 598 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 597 481 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 482 597 481 598 481 598 1592 597 1592 597 481 677 40388 9180 4420 600 479 599 480 599 1591 599 1591 598 481 598 481 598 481 598 481 598 481 598 1591 598 1591 598 1591 599 480 599 481 598 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1591 598 481 598 481 598 481 598 481 598 481 598 481 598 1591 598 1592 598 1591 598 481 598 481 598 1592 598 481 704 20126 598 480 599 480 599 480 599 481 598 480 599 480 599 480 599 481 598 480 599 480 599 480 599 481 598 481 598 480 599 481 598 480 599 481 598 480 599 480 599 481 598 481 598 481 598 480 599 481 598 481 598 481 598 481 598 481 598 481 598 481 598 1591 599 481 598 -# -name: Dh -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 9076 4445 577 503 602 1587 603 477 601 1589 600 1590 599 481 573 507 572 507 572 1618 572 1618 572 1618 572 507 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 572 1618 572 507 573 507 572 507 572 507 573 507 572 507 572 1618 572 507 573 1618 572 507 572 507 572 1618 572 507 652 20157 597 482 573 507 597 483 572 507 572 507 573 507 572 507 572 507 596 483 597 482 572 507 572 507 572 507 597 1593 572 507 572 507 573 507 572 507 572 507 572 507 572 507 572 507 572 507 572 507 596 484 572 507 572 507 572 507 597 1593 596 483 572 1618 572 1618 652 40424 9181 4422 599 480 598 1591 599 481 598 1592 598 1592 598 481 598 481 598 481 598 1592 598 1592 598 1592 598 481 598 481 598 481 598 482 597 481 598 481 599 481 598 482 598 482 597 482 598 1592 598 482 597 482 597 482 597 482 597 482 598 482 597 1593 597 1593 597 1592 598 482 598 482 596 1593 597 482 703 20132 598 481 598 481 599 481 598 481 598 481 598 481 598 481 598 481 598 482 598 481 598 482 597 482 597 482 597 482 598 482 598 481 598 481 598 482 597 482 598 482 598 1592 598 482 597 482 597 482 598 482 597 482 597 482 572 507 572 507 598 482 597 1593 597 1593 572 -# # Model: LG Generic name: Off -type: parsed -protocol: NECext -address: 81 66 00 00 -command: 81 7E 00 00 -# +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8518 4239 547 1511 572 495 547 495 571 470 572 1512 571 470 570 471 569 473 569 1515 568 1515 568 474 568 473 568 475 567 473 568 473 568 474 567 475 567 473 567 474 568 473 568 474 567 1516 567 474 567 1516 567 475 567 474 567 474 567 1517 567 +# 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 -# +data: 8508 4216 570 1512 570 498 599 442 598 442 598 1485 597 445 595 446 594 448 566 476 566 475 566 476 566 475 566 475 566 476 565 474 566 475 566 476 566 474 566 475 566 1517 565 475 566 1518 566 475 567 1516 566 475 566 1516 567 1516 566 476 566 +# 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 -# +data: 8512 4239 546 1536 546 496 545 495 545 496 545 1539 569 471 568 473 542 499 542 500 542 499 542 501 541 499 542 499 542 500 542 499 542 500 541 1541 541 500 542 499 542 499 542 500 541 1542 542 500 541 1541 541 1541 542 1543 541 500 541 1542 540 +# 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 +data: 8518 4238 546 1535 547 494 546 495 546 495 546 1537 545 497 544 497 543 498 543 499 542 500 542 499 542 500 542 500 541 499 542 500 541 1542 541 501 541 499 541 500 541 1542 541 499 541 499 541 500 541 500 541 499 541 500 541 1542 541 499 541 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8512 4215 571 1512 570 497 599 442 598 443 597 1486 580 461 568 473 567 474 567 475 567 474 566 475 566 474 567 474 567 1516 566 475 567 474 566 1517 566 474 567 475 567 474 566 475 566 1516 566 474 567 1517 566 475 567 474 566 475 566 1518 566 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8514 4239 546 1536 546 495 546 494 546 495 546 1538 570 471 544 497 543 498 543 500 542 499 542 500 542 499 542 499 542 1541 541 501 541 499 541 1542 541 1543 540 1566 517 1567 517 524 517 1566 517 524 517 1567 516 1566 516 525 517 524 517 524 517 # # Model: Shivaki SSA18002 name: Off @@ -806,3 +770,40 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 535 314 532 314 533 313 533 312 534 313 533 312 508 24840 3569 1647 508 1213 536 342 505 342 504 341 505 1214 506 340 506 340 506 340 505 340 506 1214 506 341 504 1216 504 1217 503 343 503 1218 502 1219 501 1219 502 1219 502 1219 502 345 502 344 502 1219 502 345 501 345 502 345 502 345 501 345 501 345 501 345 501 345 501 345 502 345 501 1219 502 345 501 1219 501 345 502 345 501 345 501 1219 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1220 500 1219 501 1220 500 345 501 1220 500 345 501 1220 501 1220 500 34815 3564 1653 502 1218 502 344 502 345 501 344 502 1219 501 345 501 345 501 345 501 345 501 1219 502 345 501 1219 502 1220 501 345 502 1219 501 1219 502 1219 501 1219 502 1219 502 345 501 345 501 1220 501 345 502 345 501 345 501 345 501 345 501 345 501 345 502 345 501 345 502 345 501 345 501 1220 501 345 501 345 501 345 501 345 501 1220 500 345 501 346 501 1220 500 1220 501 345 501 345 501 346 500 1220 500 1220 500 1220 500 1220 500 346 500 346 500 346 500 345 501 346 500 345 501 1220 500 346 500 1220 500 1220 500 1220 500 346 500 346 500 346 500 34816 3565 1653 502 1219 501 344 502 345 501 345 501 1219 502 345 501 345 502 345 501 345 501 1219 501 345 501 1219 502 1219 501 345 501 1219 502 1219 501 1219 501 1219 501 1219 501 345 501 345 501 1219 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 345 501 1220 501 346 501 345 501 1220 501 346 500 346 500 1220 500 346 500 345 501 346 500 1220 500 1220 500 1220 501 1220 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 346 500 1220 500 346 500 1220 500 347 499 346 500 346 500 346 500 347 499 347 499 346 500 346 500 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 347 499 1222 498 1222 499 1222 498 347 499 348 498 348 498 347 499 371 475 348 498 348 498 348 498 371 475 1222 498 1246 474 1246 474 372 475 371 475 372 474 372 474 348 498 371 475 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1246 474 372 474 372 474 372 474 1246 474 1246 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 372 474 1246 474 372 474 1247 473 372 474 1246 474 1246 474 1246 474 +# +# Model: Legion LE-F30RH-IN +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6172 7369 602 1569 602 1569 602 1569 601 1570 573 1598 574 1598 573 1597 574 1598 574 526 573 526 573 527 572 528 571 529 570 529 570 530 569 530 568 1603 569 1603 569 1603 569 1603 569 1603 568 1603 568 1603 569 1604 568 531 568 531 568 531 568 532 567 531 568 555 544 532 567 555 543 1627 544 1628 543 1628 543 1628 543 1628 544 1628 543 1628 544 1629 543 556 543 556 543 556 543 556 543 556 543 556 543 556 543 555 543 1627 544 1628 543 1629 543 556 543 555 543 1628 543 1628 543 1629 543 556 543 556 544 555 544 1628 544 1629 543 556 543 556 543 556 543 556 543 556 543 555 543 1628 543 1629 543 555 543 1628 543 1628 543 1628 543 1628 543 1629 543 557 542 556 543 1629 543 556 543 556 543 555 543 1630 542 556 542 1629 543 557 543 1630 543 557 543 1630 543 1631 542 557 542 1631 542 557 543 1630 543 557 542 1630 543 558 542 7398 543 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6173 7369 602 1569 602 1569 602 1569 602 1570 573 1598 574 1597 574 1597 574 1598 573 525 574 526 573 526 573 527 572 528 571 529 570 530 569 530 568 1602 569 1603 568 1603 569 1604 569 1604 569 1604 569 1603 568 1604 568 531 568 531 568 555 544 555 544 555 544 555 544 555 544 554 544 1628 543 1605 567 1627 544 1627 544 1628 544 1628 544 1627 544 1628 544 555 544 555 544 555 544 555 544 556 543 555 544 556 543 555 544 1629 543 555 544 1628 544 555 544 554 544 1627 544 1628 543 1628 544 555 544 1629 543 555 543 1628 544 1629 543 555 544 556 543 555 543 1627 544 1628 543 1627 544 1628 543 1629 543 555 543 1628 543 1629 543 556 543 555 544 556 543 555 544 555 544 1628 544 557 543 557 543 556 544 1630 543 556 544 1630 543 556 544 1630 543 556 543 1629 544 1630 543 556 544 1630 543 556 543 1630 543 556 544 1630 543 557 543 7397 543 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6170 7369 573 1599 572 1598 573 1599 572 1598 573 1598 573 1598 572 1598 572 1600 571 528 570 529 570 530 569 531 568 531 568 531 568 531 568 531 568 1603 568 1604 568 1604 568 1604 567 1604 567 1604 568 1604 568 1605 567 532 567 533 566 532 567 532 567 556 543 557 542 556 543 556 542 1629 542 1629 543 1629 542 1629 543 1629 542 1629 542 1629 542 1630 542 557 542 557 542 556 543 557 542 556 543 556 543 556 543 556 543 1629 543 556 542 1629 543 557 542 556 543 1629 542 1629 543 1630 542 556 542 1629 542 556 542 1629 542 1630 542 557 542 557 542 557 542 557 542 557 542 556 542 1629 541 1630 541 557 542 1631 541 1631 541 1631 542 1631 542 1632 542 559 541 558 542 1631 542 559 541 559 541 558 542 1631 542 558 542 1632 541 558 542 1632 542 558 542 1631 542 1631 542 558 542 1632 541 558 542 1632 541 558 542 1632 542 559 542 7401 541 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6173 7370 600 1571 656 1515 603 1569 602 1570 600 1571 573 1598 574 1598 573 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 530 569 530 569 1603 569 1603 569 1603 569 1603 569 1603 568 1603 569 1603 569 1604 568 531 569 531 568 531 568 531 568 532 567 555 544 532 567 555 544 1603 568 1604 567 1604 567 1605 567 1628 544 1605 567 1628 543 1629 543 556 544 555 544 556 543 556 543 556 543 556 544 556 543 555 544 1629 543 555 544 1629 543 556 543 556 543 556 543 555 543 1629 543 556 544 1630 543 556 544 1629 544 1629 544 1629 544 1630 543 557 543 556 544 1629 543 1630 543 556 544 1629 543 1630 543 556 544 1629 543 1630 543 557 544 556 543 1630 543 557 543 557 543 1630 543 557 543 557 543 1630 543 557 543 1630 543 557 543 1630 543 557 543 1629 543 1630 543 557 543 1630 543 557 543 1630 543 557 543 1631 543 557 544 7399 543 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6201 7369 602 1568 603 1568 603 1569 601 1570 573 1598 573 1598 574 1597 574 1598 574 525 573 526 573 526 573 527 572 527 572 529 570 529 570 529 570 1602 569 1602 570 1602 570 1602 570 1603 569 530 569 1603 569 1603 569 530 569 530 569 530 569 530 569 529 569 1603 569 531 568 530 569 1603 568 1603 569 1603 569 1603 569 1603 569 1603 569 1603 569 1603 569 531 568 531 568 531 568 531 568 531 568 530 569 531 568 530 568 1604 568 530 569 1604 568 532 567 530 569 1603 568 1628 544 1628 544 556 544 1606 567 556 543 1629 543 1630 544 557 544 557 544 557 543 557 544 557 543 556 544 1629 543 1629 543 1629 543 1630 543 557 543 1629 543 1629 544 1630 543 557 544 557 543 557 543 557 543 1630 543 557 542 1630 543 557 543 1630 543 556 544 1630 543 556 544 1629 543 1630 543 557 543 1630 543 557 543 1630 543 557 543 1630 543 557 543 7398 543 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 6175 7369 602 1570 602 1570 601 1570 573 1598 574 1598 573 1597 574 1597 574 1598 574 525 574 526 573 526 573 527 572 528 571 529 570 529 570 530 568 1603 568 1627 544 1627 544 1627 544 1628 544 554 545 1627 544 1628 544 555 544 555 544 555 544 555 544 554 544 1627 545 555 544 554 545 1627 544 1627 544 1627 544 1627 544 1627 544 1603 568 1602 569 1603 569 530 569 529 570 529 570 529 570 529 570 529 570 529 570 528 570 1601 571 528 570 1602 570 529 570 528 570 1601 570 1600 571 1601 571 528 571 1601 570 528 570 1601 570 1602 570 529 570 529 570 528 570 1601 570 1601 570 1600 571 1601 571 528 570 1601 570 1601 571 528 571 528 571 529 570 528 571 528 570 1601 571 528 571 528 570 1603 570 529 571 1603 570 529 570 1603 570 529 570 1603 570 529 571 1602 571 1603 570 529 571 1603 570 529 571 1603 570 529 571 1603 570 530 570 7370 570 diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 11533746c..51ccb6569 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 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +# Last Checked 19th Aug, 2023 # name: Power type: parsed diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index bbae727a2..2c84eb829 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 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +#Last Updated 19th Aug, 2023 +#Last Checked 19th Aug, 2023 # name: Power type: raw @@ -26,7 +26,7 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 9086 4339 707 443 681 445 681 422 704 445 681 445 681 445 681 445 681 445 656 1563 683 1563 682 1563 707 1539 706 1562 682 1562 682 1564 680 1565 679 1566 679 448 679 1567 678 448 679 448 679 448 679 1566 679 448 679 448 678 1566 679 448 678 1566 678 1566 678 1566 678 448 678 1566 679 39846 9058 2132 679 -# +# name: Speed_up type: raw frequency: 38000 @@ -38,7 +38,7 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 9267 4339 708 443 682 445 682 422 734 416 711 416 711 416 711 415 712 415 658 1563 682 1564 682 1565 681 1587 684 1562 683 1563 683 1563 682 1564 681 1565 680 447 679 447 679 1566 679 1566 680 447 680 447 680 447 680 447 680 1566 679 1566 679 447 680 447 680 1566 679 1566 679 1566 679 -# +# name: Rotate type: raw frequency: 38000 @@ -68,7 +68,7 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1294 371 1351 322 443 1200 1295 346 1297 373 444 1224 445 1198 1300 345 498 1170 473 1171 496 1173 471 8057 1295 372 1297 347 444 1224 1296 347 1296 349 494 1172 470 1174 1293 376 469 1174 469 1200 468 1175 468 8082 1293 351 1318 349 468 1175 1292 377 1294 349 468 1177 491 1175 1293 350 442 1226 468 1175 468 1201 467 8083 1293 350 1268 401 467 1175 1293 351 1318 350 467 1175 442 1226 1293 350 467 1177 466 1200 468 1176 441 8133 1292 351 1267 400 468 1175 1292 351 1292 376 466 1177 467 1202 1291 352 442 1202 465 1203 441 1201 442 8132 1267 376 1267 402 440 1202 1266 377 1290 379 439 1226 416 1253 1241 402 415 1228 439 1229 414 1228 415 8161 1239 403 1240 404 439 1229 1240 403 1240 429 414 1229 414 1231 1264 404 413 1229 414 1255 413 1229 413 8161 1238 404 1239 405 437 1230 1239 404 1238 430 412 1230 412 1232 1262 405 412 1231 412 1256 412 1231 412 8162 1238 406 1237 406 411 1258 1237 406 1237 431 412 1256 387 1256 1213 456 386 1256 386 1257 412 1256 386 8164 1237 431 1212 431 386 1283 1212 431 1212 432 411 1257 386 1257 1211 457 386 1257 386 1258 411 1258 385 8164 1212 431 1212 432 411 1256 1213 431 1236 433 386 1256 387 1257 1238 430 386 1257 386 1282 386 1256 386 -# ON/Speed_up +#ON/Speed_up name: Power type: raw frequency: 38000 @@ -86,19 +86,19 @@ type: parsed protocol: NECext address: 4B 14 00 00 command: 80 7F 00 00 -# +# name: Mode type: parsed protocol: NECext address: 4B 14 00 00 command: 20 DF 00 00 -# +# name: Rotate type: parsed protocol: NECext address: 4B 14 00 00 command: C0 3F 00 00 -# +# name: Timer type: parsed protocol: NECext @@ -177,7 +177,7 @@ frequency: 38000 duty_cycle: 0.330000 data: 2323 611 796 669 800 664 802 692 773 1424 774 691 801 664 774 691 774 692 773 1423 800 668 822 667 798 1376 769 1427 771 696 769 696 770 1429 795 1404 794 672 794 1404 794 671 795 1404 794 51269 2214 691 769 1427 796 # -# ON/SPEED +#ON/SPEED name: Power type: parsed protocol: NECext @@ -225,7 +225,7 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3533 1663 476 420 450 1234 508 421 449 394 477 420 450 420 474 387 459 421 475 396 558 385 432 386 484 386 485 386 434 1263 479 419 447 395 475 394 476 393 478 393 478 392 478 1264 477 393 478 1264 478 1266 500 387 483 395 475 1268 473 397 474 397 474 397 474 397 474 1268 473 397 474 397 474 397 474 397 474 1268 472 1270 472 398 473 398 473 1269 473 398 473 398 473 397 474 1268 473 1269 473 398 473 398 473 1269 472 398 473 1269 472 398 473 1269 472 398 473 1269 472 398 473 74625 3552 1673 473 397 472 1270 472 399 471 399 471 400 470 400 470 401 470 401 470 401 469 401 470 402 469 402 469 402 469 1272 471 400 470 400 470 401 469 402 468 427 444 427 444 1273 471 400 470 1272 470 1271 471 400 470 401 469 1273 470 401 469 427 443 428 442 428 417 1300 469 426 443 428 417 453 442 428 417 1325 443 1274 468 427 443 428 417 1325 443 427 417 454 416 454 416 1326 417 1325 442 427 417 453 417 1325 417 453 417 1325 418 453 416 1326 416 453 417 1326 416 454 416 -# Timer UP +#Timer UP name: Timer type: parsed protocol: NEC @@ -333,7 +333,7 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1273 401 1272 426 416 1232 1273 427 1247 426 405 1242 442 1231 442 1231 1274 426 405 1242 431 1242 431 7968 1274 400 1273 426 416 1232 1273 426 1247 426 405 1241 432 1241 432 1241 1275 425 406 1267 406 1240 433 7967 1275 398 1275 424 407 1240 1276 424 1249 424 407 1239 434 1238 435 1238 1278 422 409 1237 436 1237 436 7963 1279 394 1279 420 411 1262 1243 430 1243 430 412 1234 439 1234 439 1233 1272 428 414 1232 441 1232 441 7958 1273 426 1248 426 405 1241 1275 426 1248 425 406 1266 407 1240 433 1240 1276 424 407 1265 408 1238 435 7965 1277 422 1251 423 408 1238 1278 423 1250 423 408 1264 409 1238 435 1264 1251 422 409 1264 409 1237 436 7964 1278 422 1251 422 409 1264 1251 422 1251 422 409 1263 410 1262 411 1262 1254 420 411 1262 411 1235 438 7961 1270 429 1244 429 413 1260 1245 428 1245 427 415 1258 415 1231 432 1241 1274 425 406 1267 406 1240 433 7965 1277 423 1250 423 408 1265 1250 423 1250 423 408 1264 409 1237 436 1237 1278 421 410 1262 411 1262 411 7961 1270 429 1244 429 413 1260 1245 429 1244 429 413 1260 413 1233 440 1259 1246 427 415 1259 414 1258 415 7959 1272 427 1246 427 415 1259 1246 427 1246 427 415 1257 406 1267 406 1241 1274 425 406 1266 407 1266 407 7965 1277 422 1251 422 409 1264 1251 421 1252 421 410 1262 411 1261 412 1235 1270 429 413 1260 413 1233 440 7958 1273 426 1247 425 406 1267 1249 425 1248 424 407 1266 407 1265 408 1265 1250 423 408 1264 409 1263 410 7962 1280 420 1253 420 411 1261 1244 430 1243 429 413 1259 414 1232 441 1231 1274 427 415 1257 406 1240 433 7965 1277 423 1250 423 408 1264 1251 422 1251 422 409 1262 411 1235 438 1235 1280 420 411 1260 413 1232 441 7957 1274 426 1247 425 406 1266 1250 425 1248 424 407 1265 408 1238 435 1264 1251 423 408 1263 410 1236 437 -# Osc +#Osc name: Rotate type: raw frequency: 38000 @@ -1161,13 +1161,13 @@ type: parsed protocol: NEC address: 80 00 00 00 command: 1B 00 00 00 -# Timer DOWN +#Timer DOWN name: Timer type: parsed protocol: NEC address: 80 00 00 00 command: 09 00 00 00 -# Rotate +#Rotate name: Rotate type: parsed protocol: NEC @@ -1179,7 +1179,7 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1397 357 1370 357 500 1188 1427 331 1343 383 473 1240 476 1240 476 1240 475 1240 475 1240 475 1240 1370 7358 1367 361 1366 361 496 1220 1366 361 1366 361 496 1220 495 1220 495 1220 495 1221 494 1220 495 1220 1366 7361 1365 361 1366 361 495 1221 1365 362 1365 362 494 1221 494 1221 494 1220 495 1220 495 1221 494 1220 1365 7361 1364 361 1366 362 494 1221 1365 362 1365 362 494 1221 494 1221 494 1221 494 1221 494 1221 494 1221 1365 7361 1364 362 1364 362 494 1221 1364 362 1365 362 495 1221 494 1221 494 1221 494 1221 494 1221 494 1221 1364 7361 1364 363 1364 363 493 1222 1364 363 1363 363 493 1223 492 1223 492 1223 492 1247 468 1223 492 1247 1339 7386 1338 388 1338 388 468 1247 1339 388 1338 388 468 1248 467 1247 468 1247 468 1247 468 1248 467 1247 1338 7387 1337 389 1338 389 467 1248 1338 389 1337 389 467 1248 467 1248 467 1248 467 1248 467 1248 467 1248 1337 7388 1336 389 1337 389 467 1249 1336 390 1337 390 466 1249 466 1249 466 1249 466 1249 466 1249 466 1248 1337 7388 1312 414 1312 414 465 1251 1335 391 1337 390 441 1274 441 1274 465 1249 464 1250 442 1274 441 1273 1337 7388 1311 414 1312 415 441 1274 1311 415 1311 415 441 1274 441 1274 441 1274 441 1274 441 1274 441 1274 1311 7413 1311 415 1312 415 441 1274 1311 415 1311 416 440 1275 440 1275 440 1275 440 1275 440 1275 439 1275 1310 7414 1309 416 1310 417 439 1276 1310 417 1309 417 438 1277 438 1277 438 1277 438 1301 413 1301 414 1301 1285 7439 1284 442 1284 442 414 1301 1284 443 1283 443 413 1302 413 1302 413 1302 413 1302 412 1302 413 1302 1283 7441 1283 443 1284 443 412 1303 1283 444 1282 444 412 1303 411 1303 412 1303 412 1303 412 1303 412 1303 1283 7441 1282 445 1281 445 411 1304 1282 470 1256 471 385 1330 385 1330 385 1330 385 1330 385 1330 385 1330 1256 7468 1255 471 1256 471 384 1331 1255 471 1255 472 383 1331 384 1331 383 1332 383 1332 383 1331 383 1332 1254 7470 1253 498 1228 499 356 1358 1228 499 1227 499 356 1359 356 1359 356 1359 356 1359 355 1360 355 1359 1227 7497 1226 526 1200 527 327 1387 1200 527 1199 554 300 1414 301 1415 299 1415 299 1415 300 1416 299 1415 1172 7553 1170 609 1117 583 270 1471 1117 637 1089 692 118 10334 871 -# Osc +#Osc name: Rotate type: raw frequency: 38000 @@ -1197,19 +1197,19 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1366 359 1366 360 496 1214 1367 360 1395 331 524 1185 474 1239 474 1238 475 1238 475 1238 1343 383 498 8205 1366 386 1338 386 470 1244 1338 386 1339 386 470 1243 470 1244 469 1244 469 1243 470 1244 1338 386 470 8234 1338 386 1339 386 469 1244 1338 386 1339 386 470 1243 470 1220 493 1219 494 1243 470 1244 1338 362 494 8233 1339 362 1363 386 469 1244 1338 386 1339 386 469 1244 469 1244 469 1244 469 1244 469 1244 1338 387 468 8234 1338 386 1338 387 468 1244 1338 387 1337 387 468 1244 469 1244 468 1245 469 1244 469 1244 1338 387 468 8234 1337 387 1338 387 465 1247 1338 387 1338 387 468 1245 468 1244 444 1269 468 1246 467 1245 1337 387 468 8235 1337 387 1337 388 468 1245 1336 388 1336 388 466 1246 467 1245 468 1245 467 1247 467 1245 1312 412 467 8235 1312 412 1312 412 443 1270 1335 390 1311 412 468 1246 466 1246 467 1246 467 1246 467 1246 1311 412 467 8236 1311 413 1311 413 442 1271 1311 413 1311 413 442 1271 466 1247 466 1246 442 1271 442 1271 1311 413 442 8260 1311 413 1311 413 442 1271 1311 413 1311 414 441 1271 442 1271 465 1248 464 1249 465 1247 1310 414 441 8261 1334 390 1310 414 466 1247 1335 390 1333 391 465 1248 465 1248 465 1248 465 1248 465 1248 1334 390 465 8237 1334 391 1333 390 465 1248 1309 415 1309 416 464 1249 464 1249 463 1250 462 1275 438 1274 1307 394 438 8287 1283 441 1283 441 438 1274 1283 441 1283 442 436 1276 414 1299 438 1275 413 1300 412 1300 1282 442 413 8289 1282 443 1281 443 412 1301 1281 443 1281 443 412 1301 411 1302 411 1302 410 1327 385 1327 1255 469 386 8316 1255 470 1254 470 385 1327 1255 470 1254 470 385 1327 385 1328 384 1328 385 1328 385 1328 1254 470 385 8317 1253 471 1253 470 385 1329 1253 471 1253 471 384 1329 383 1330 382 1330 382 1330 383 1330 1252 473 382 8344 1226 498 1226 498 357 1356 1226 498 1226 498 356 1356 356 1356 356 1356 356 1356 356 1356 1226 499 355 8346 1224 499 1225 525 329 1384 1198 526 1198 525 329 1384 328 1384 328 1384 328 1384 329 1384 1198 526 328 8373 1198 526 1198 527 327 1385 1197 553 1171 553 301 1412 300 1412 300 1412 300 1386 327 1412 1170 554 299 8401 1170 554 1170 555 298 1414 1169 581 1143 582 271 1440 272 1440 272 1440 272 1441 271 1441 1142 609 244 8458 1113 636 1088 663 178 1507 1088 691 1033 -# Timer OFF +#Timer OFF name: Timer type: raw frequency: 38000 duty_cycle: 0.330000 data: 3583 1639 534 386 484 1208 534 386 484 386 485 386 484 387 483 386 484 386 485 386 457 394 476 394 451 419 452 419 476 1237 506 393 477 394 475 395 474 396 473 397 473 398 472 1270 472 399 471 1271 471 1270 471 399 470 401 469 1273 469 402 468 427 443 427 443 427 443 1299 443 427 443 427 444 427 444 427 444 1298 443 1299 442 427 444 427 445 426 445 425 446 425 447 424 447 399 472 398 473 1294 445 426 446 400 471 399 472 1294 446 425 447 398 473 1295 446 400 471 398 472 74544 3552 1672 472 399 471 1271 471 399 471 400 470 400 470 400 470 400 471 400 470 400 470 401 470 401 469 401 469 401 470 1273 470 400 470 401 469 401 469 401 470 401 469 401 470 1273 470 401 469 1273 470 1272 469 401 469 401 469 1274 468 401 469 401 469 401 470 402 469 1273 469 401 469 401 469 402 469 401 469 1273 469 1273 469 401 469 402 468 402 469 426 444 427 444 427 444 426 445 426 444 1274 468 402 469 426 444 427 443 1275 467 403 467 427 443 1275 467 402 468 427 443 -# OFF +#OFF name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1366 388 1295 387 446 1164 1368 387 1296 387 448 1164 518 1164 493 1189 517 1165 492 1189 493 1189 493 7851 1365 387 1270 388 446 1190 1365 387 1295 387 447 1192 490 1193 489 1193 489 1194 488 1194 488 1193 489 7855 1337 386 1296 386 448 1193 1338 386 1296 386 448 1193 489 1193 489 1193 489 1193 489 1193 489 1193 489 7855 1337 386 1297 386 448 1194 1337 386 1296 386 448 1194 488 1194 488 1194 488 1194 488 1194 488 1194 488 8160 1336 387 1296 387 447 1194 1337 387 1296 386 448 1194 488 1194 488 1194 488 1194 488 1194 488 1194 488 7855 1336 386 1297 387 447 1195 1336 386 1297 386 448 1195 486 1195 487 1195 487 1195 487 1196 486 1196 486 7881 1310 387 1272 397 436 1245 1286 397 1285 397 436 1246 436 1246 436 1247 435 1247 435 1248 434 1248 434 7934 1259 424 1259 424 408 1274 1258 424 1259 424 408 1274 408 1274 408 1274 408 1274 408 1274 408 1274 408 8239 1258 425 1258 424 408 1274 1258 424 1258 425 407 1273 408 1273 409 1273 408 1273 408 1274 408 1273 408 7907 1283 424 1258 424 409 1248 1283 399 1283 400 433 1247 434 1247 434 1247 434 1247 434 1247 434 1247 434 7905 1282 424 1258 425 407 1273 1257 425 1257 425 407 1273 407 1274 407 1274 407 1274 407 1275 406 1275 406 7958 1230 478 1204 478 353 1328 1204 478 1204 453 378 1327 353 1328 353 1302 379 1301 380 1301 380 1300 381 -# +# name: Power type: raw frequency: 38000 @@ -1233,67 +1233,67 @@ type: parsed protocol: NECext address: 00 F3 00 00 command: 91 6E 00 00 -# +# name: Timer type: parsed protocol: NECext address: 00 F3 00 00 command: 96 69 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 9253 4427 684 486 656 486 656 486 682 461 681 1573 680 1575 678 464 677 491 651 1604 650 1604 650 1604 650 1604 650 491 651 491 651 1604 650 1604 651 491 651 491 651 1604 650 1604 650 491 651 491 651 491 652 1604 650 1604 651 1604 650 491 651 491 652 1604 650 1604 650 1604 651 491 651 39948 9250 2183 651 -# +# name: Speed_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 9226 4450 657 484 657 484 658 484 657 485 657 1596 658 1597 681 487 653 488 653 1602 652 1602 652 1602 652 1602 652 490 652 490 652 1602 652 1602 652 490 652 490 652 490 652 1603 652 490 652 490 652 490 652 1602 652 1602 652 1602 653 1602 652 490 652 1602 652 1602 652 1602 653 489 653 39949 9250 2179 653 -# OSC +#OSC name: Rotate type: raw frequency: 38000 duty_cycle: 0.330000 data: 9231 4449 657 484 658 483 659 483 659 483 659 1595 659 1595 659 485 681 461 680 1601 652 1602 653 1601 653 1601 653 488 654 488 654 1602 653 1601 653 488 654 488 654 1602 653 1602 652 1602 652 488 654 488 654 1602 653 1601 653 1602 652 488 654 488 654 488 654 1602 652 1602 653 488 654 39978 9229 2174 654 96468 9259 2146 679 -# +# name: Timer type: raw frequency: 38000 duty_cycle: 0.330000 data: 9384 4452 658 485 657 484 658 484 659 485 657 1597 658 1597 682 487 655 488 654 1603 652 1603 653 1603 653 1604 653 491 653 491 653 1603 653 1603 653 1603 653 491 653 1604 652 490 654 1603 652 490 653 490 653 1603 652 490 653 1603 652 490 653 1603 652 490 653 1603 652 1603 652 490 653 39953 9263 2181 652 -# +# name: Power type: parsed protocol: NEC address: 01 00 00 00 command: 83 00 00 00 -# +# name: Speed_up type: parsed protocol: NEC address: 01 00 00 00 command: 87 00 00 00 -# +# name: Timer type: parsed protocol: NEC address: 01 00 00 00 command: 8B 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 2256 695 788 1354 789 1349 789 1340 792 702 762 697 763 692 789 661 788 720 786 693 786 689 785 685 784 681 784 676 784 1334 784 1330 783 102265 2255 695 787 1356 786 1352 785 1348 785 681 783 676 783 671 784 666 784 724 784 696 783 691 784 686 784 681 783 676 784 1335 783 1330 783 -# OSC +#OSC name: Rotate type: raw frequency: 38000 duty_cycle: 0.330000 data: 2227 749 733 1382 761 1379 866 1292 841 566 898 591 868 588 866 582 760 1412 867 612 866 576 898 603 867 598 866 1256 759 695 867 1246 759 101611 2335 615 868 1245 899 1268 869 1266 867 566 898 591 760 694 761 689 760 1411 760 720 760 715 759 710 759 705 759 1363 760 696 758 1352 761 -# +# name: Rotate type: raw frequency: 38000 @@ -1305,13 +1305,13 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 2203 675 786 1388 867 1361 786 678 786 1444 758 708 759 707 760 707 786 735 757 760 786 735 757 736 781 711 757 734 759 733 785 734 758 101185 2198 708 757 1416 757 1442 756 685 755 1445 780 685 780 662 776 689 803 742 778 740 753 766 779 715 777 714 779 714 778 690 776 742 752 -# StrengthUp +#StrengthUp name: Speed_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 2224 685 782 1419 808 1418 779 687 757 1470 757 710 757 684 809 657 809 736 758 1469 758 736 756 1470 757 762 755 1446 782 1418 781 712 755 101352 2223 707 758 1417 834 1392 781 685 781 1446 780 687 754 687 754 713 804 741 779 1447 779 715 778 1447 779 740 752 1448 778 1422 779 690 775 -# StrengthDown +#StrengthDown name: Speed_dn type: raw frequency: 38000 @@ -1323,13 +1323,13 @@ type: parsed protocol: NECext address: 41 59 00 00 command: 05 FA 00 00 -# +# name: Speed_up type: parsed protocol: NECext address: 41 59 00 00 command: 44 BB 00 00 -# OFF +#OFF name: Power type: raw frequency: 38000 @@ -1347,37 +1347,37 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 2312 617 867 641 840 577 899 1269 866 1265 865 622 839 593 864 612 838 670 839 1306 839 611 865 1271 864 627 839 1259 865 590 866 611 840 102129 2316 670 813 667 813 664 811 1323 810 1319 759 702 759 697 759 692 759 750 759 1386 758 717 759 1376 758 706 759 1365 759 696 759 691 760 -# +# name: Speed_dn type: raw frequency: 38000 duty_cycle: 0.330000 data: 2232 725 759 722 759 717 759 1375 760 1370 760 702 759 697 759 692 788 1384 788 1356 788 1351 788 1347 787 1342 787 1338 786 670 785 1330 784 99591 2229 724 760 721 785 690 785 1349 784 1345 784 677 783 673 782 669 781 1391 781 1363 781 1359 780 1353 781 1349 780 1343 781 675 780 1334 780 -# +# name: Timer type: raw frequency: 38000 duty_cycle: 0.330000 data: 2230 724 760 720 761 715 761 1374 761 1370 760 701 760 696 760 691 786 1385 760 720 787 689 760 710 760 705 786 1339 785 670 785 665 784 98757 2224 729 754 726 755 721 754 1380 754 1375 754 706 754 701 754 696 754 1418 754 726 754 720 755 716 755 710 755 1369 755 700 755 696 755 -# +# name: Rotate type: raw frequency: 38000 duty_cycle: 0.330000 data: 2256 698 786 695 786 689 787 1348 787 1342 787 673 788 668 787 663 787 722 786 696 784 1353 786 1375 759 706 759 702 783 672 783 1332 782 102265 2310 645 838 668 812 664 811 1323 810 1319 810 651 809 647 808 642 808 701 807 673 807 1332 807 1327 807 658 808 653 807 648 807 1307 807 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1356 337 1423 337 516 1157 1298 409 1296 416 448 1274 451 1276 452 1274 450 1274 449 1277 449 1275 1301 7091 1301 407 1300 405 450 1267 1302 407 1301 414 450 1274 451 1275 451 1275 451 1276 450 1275 451 1274 1302 7074 1303 406 1302 406 450 1268 1301 409 1299 416 449 1277 472 1254 473 1253 473 1254 472 1254 473 1252 1323 7069 1325 384 1300 408 470 1247 1324 387 1299 417 471 1253 472 1254 472 1254 472 1254 472 1254 472 1252 1324 7052 1324 384 1300 408 472 1246 1324 387 1299 417 472 1253 472 1254 472 1254 472 1254 473 1254 472 1252 1324 7064 1325 385 1323 385 471 1246 1324 387 1323 394 471 1254 471 1254 472 1254 472 1254 472 1254 472 1252 1324 7053 1324 385 1323 385 471 1247 1323 387 1322 394 471 1254 472 1254 472 1255 471 1255 471 1255 472 1253 1323 7070 1323 385 1323 385 471 1247 1323 388 1322 395 470 1254 472 1255 471 1255 471 1255 471 1255 471 1253 1323 7054 1323 386 1322 386 470 1248 1322 413 1297 396 470 1255 470 1256 470 1256 470 1256 470 1256 471 1254 1322 7071 1322 411 1297 387 469 1249 1321 414 1296 396 470 1280 445 1281 446 1281 445 1282 445 1282 445 1280 1296 7076 1296 412 1296 412 444 1274 1297 413 1297 421 445 1281 445 1282 444 1282 444 1282 445 1282 444 1280 1296 7098 1295 412 1296 412 445 1275 1296 414 1296 421 445 1282 444 1282 445 1282 444 1283 444 1282 445 1280 1296 7082 1296 412 1296 412 445 1275 1295 414 1296 421 444 1282 444 1282 444 1283 444 1283 444 1282 445 1280 1296 7098 1296 413 1295 413 444 1275 1295 415 1295 422 444 1283 443 1282 444 1283 443 1282 445 1282 444 1281 1295 7082 1295 413 1295 413 444 1275 1295 415 1294 423 443 1283 443 1283 443 1283 443 1283 443 1283 443 1281 1294 7093 1295 413 1295 414 443 1276 1294 415 1295 423 443 1283 443 1283 443 1283 443 1283 444 1283 444 1280 1295 7082 1295 414 1294 414 443 1276 1294 416 1294 423 443 1283 443 1283 443 1283 443 1283 444 1283 444 1281 1294 7098 1295 414 1294 414 443 1276 1294 416 1294 424 442 1284 443 1283 443 1283 443 1284 443 1283 443 1281 1294 7083 1294 414 1294 415 442 1277 1293 417 1293 425 441 1284 442 1284 442 1284 442 1284 442 1284 443 1282 1293 7099 1294 415 1293 416 441 1278 1292 418 1292 425 441 1285 441 1285 441 1285 442 1285 441 1285 441 1283 1292 7083 1293 417 1291 441 415 1279 1292 443 1267 451 415 1286 440 1286 441 1286 440 1285 441 1286 440 1284 1291 7096 1292 441 1267 442 415 1304 1266 444 1266 451 415 1311 415 1287 439 1287 440 1311 415 1286 440 1285 1290 7085 1291 442 1241 467 414 1304 1266 445 1241 476 414 1313 414 1312 415 1311 415 1312 415 1312 414 1310 1241 7151 1242 467 1241 467 415 1305 1241 469 1241 477 412 1314 414 1312 414 1312 414 1312 414 1312 414 1310 1241 7135 1241 467 1241 468 389 1330 1241 470 1240 477 389 1337 389 1337 414 1312 414 1313 413 1313 389 1335 1241 7151 1241 469 1239 470 386 1331 1240 496 1214 503 362 1338 388 1338 388 1338 388 1338 388 1338 388 1336 1240 7130 1241 494 1214 495 361 1357 1214 497 1213 504 362 1364 362 1364 362 1364 363 1364 362 1364 362 1362 1214 7153 1239 495 1213 496 360 1358 1213 498 1212 531 334 1365 362 1365 361 1365 361 1365 362 1364 362 1363 1213 7162 1213 496 1212 522 334 1359 1212 524 1186 532 333 1367 359 1366 360 1366 361 1365 361 1365 361 1364 1211 7180 1212 523 1185 550 306 1387 1185 552 1157 586 278 1395 332 1394 332 1394 332 1393 333 1393 334 1391 1184 7190 1185 576 1131 604 250 1415 1158 660 1049 2341 251 1448 278 1422 305 1448 278 1448 278 1447 1130 7229 1157 -# +# name: Speed_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 1357 353 1327 382 448 1239 1333 409 1301 416 477 1246 452 1274 452 1274 453 1273 479 1246 1330 416 448 7967 1300 409 1299 410 446 1273 1299 412 1298 419 446 1280 446 1281 446 1281 446 1281 446 1279 1298 419 446 7958 1298 411 1297 410 446 1273 1298 412 1298 419 446 1280 446 1281 445 1281 446 1281 445 1279 1298 419 446 7967 1298 410 1298 411 445 1274 1297 413 1297 420 445 1281 445 1281 445 1281 445 1282 445 1279 1298 420 445 7957 1297 411 1297 411 445 1274 1297 413 1297 420 445 1281 445 1281 445 1282 444 1282 444 1280 1296 420 445 7962 1297 411 1297 411 445 1274 1297 413 1297 420 445 1282 444 1282 444 1282 444 1282 444 1280 1297 421 444 7957 1296 412 1297 411 444 1275 1296 414 1296 421 444 1282 444 1283 443 1283 443 1282 444 1281 1296 421 444 7968 1296 412 1296 412 444 1275 1296 414 1296 421 444 1283 443 1283 443 1283 443 1283 443 1281 1296 422 443 7958 1295 413 1295 413 443 1276 1295 415 1295 422 443 1283 443 1284 442 1284 442 1284 442 1282 1294 422 443 7970 1293 414 1294 414 442 1277 1294 416 1294 423 442 1284 442 1309 417 1309 417 1309 417 1307 1269 424 441 7978 1270 416 1292 414 442 1302 1269 440 1270 424 441 1309 417 1309 417 1310 417 1309 417 1307 1270 448 417 7994 1269 439 1269 439 416 1302 1269 441 1269 448 416 1310 416 1310 416 1310 416 1310 416 1308 1269 448 416 7984 1269 439 1268 440 416 1303 1268 442 1267 449 415 1310 416 1310 416 1310 416 1310 416 1309 1268 449 416 7994 1268 440 1268 440 415 1303 1268 442 1268 449 415 1311 415 1311 415 1311 415 1311 415 1309 1267 450 415 7985 1267 441 1267 441 414 1304 1266 444 1267 450 414 1311 415 1312 413 1312 414 1312 414 1310 1265 452 414 7991 1266 442 1242 466 413 1306 1241 468 1242 475 414 1337 388 1338 388 1338 388 1338 388 1336 1217 501 386 8013 1215 492 1216 492 387 1331 1216 494 1216 501 363 1363 362 1363 363 1363 363 1363 387 1338 1215 502 362 8047 1215 493 1215 492 363 1356 1215 495 1215 502 362 1364 361 1364 362 1364 362 1364 362 1363 1214 503 361 8063 1189 494 1214 494 361 1382 1189 496 1214 503 361 1390 335 1391 335 1391 335 1391 335 1389 1188 529 335 8074 1188 520 1188 520 334 1385 1187 522 1187 529 334 1392 333 1417 308 1418 308 1419 307 1417 1161 556 307 8092 1160 547 1161 547 307 1412 1160 550 1160 557 306 1419 306 1446 279 1421 305 1446 279 1445 1133 584 279 8125 1133 575 1133 576 277 1466 1106 603 1107 611 251 1474 251 1501 224 1502 224 1528 186 1511 1079 638 224 8228 1025 735 972 2640 786 -# +# name: Speed_up type: parsed protocol: NEC @@ -1389,13 +1389,13 @@ type: parsed protocol: NEC address: 01 00 00 00 command: 1D 00 00 00 -# +# name: Rotate type: parsed protocol: NEC address: 01 00 00 00 command: 18 00 00 00 -# +# name: Timer type: parsed protocol: NEC @@ -1407,13 +1407,13 @@ type: parsed protocol: NECext address: 80 DE 00 00 command: 00 FF 00 00 -# +# name: Speed_up type: parsed protocol: NECext address: 80 DE 00 00 command: 08 F7 00 00 -# +# name: Speed_dn type: parsed protocol: NECext @@ -1455,19 +1455,19 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1369 311 1327 312 498 1162 1286 355 1285 362 457 1221 458 1221 458 1221 458 1222 457 1221 458 1219 1312 6796 1310 330 1282 356 454 1219 1281 360 1280 367 452 1227 452 1227 452 1227 452 1227 452 1227 452 1225 1280 6815 1280 359 1279 359 451 1220 1280 361 1279 368 451 1228 451 1228 451 1228 451 1228 451 1228 451 1226 1279 6827 1279 382 1256 382 428 1245 1255 384 1256 391 427 1251 428 1252 426 1252 427 1252 427 1252 427 1250 1255 6838 1255 383 1255 383 426 1245 1255 385 1255 392 426 1252 427 1252 426 1252 426 1252 427 1252 426 1250 1255 6849 1255 383 1255 383 426 1245 1255 385 1254 392 426 1252 426 1252 426 1252 426 1252 426 1252 426 1250 1254 6835 1254 383 1254 383 426 1245 1254 385 1254 392 426 1252 426 1252 426 1252 426 1252 426 1252 426 1250 1254 6852 1254 383 1254 384 425 1245 1254 386 1253 392 426 1253 425 1252 426 1252 426 1253 425 1253 425 1251 1253 6835 1253 384 1253 384 425 1245 1253 386 1253 393 425 1252 425 1253 425 1253 425 1253 425 1253 425 1251 1253 6852 1252 384 1253 384 425 1246 1252 386 1253 393 425 1253 424 1253 425 1253 425 1254 424 1253 425 1252 1252 6835 1253 385 1252 385 424 1247 1252 387 1252 394 424 1254 424 1254 424 1254 424 1254 424 1254 424 1252 1251 6850 1251 386 1251 386 423 1248 1251 388 1250 395 423 1255 422 1256 422 1279 399 1280 398 1279 399 1277 1227 6862 1226 411 1226 411 398 1273 1226 413 1226 420 397 1280 398 1279 398 1280 397 1280 398 1280 398 1278 1226 6892 1226 411 1225 411 397 1273 1225 413 1225 420 397 1280 397 1280 398 1280 397 1280 397 1280 397 1279 1224 -# +# name: Speed_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 1308 332 1305 332 479 1190 1309 333 1307 364 455 1199 479 1198 481 1198 480 1198 480 1197 1331 339 480 7657 1306 332 1305 332 479 1194 1304 335 1305 342 478 1201 477 1201 478 1201 477 1201 478 1199 1305 342 478 7647 1305 333 1304 333 478 1194 1304 335 1304 342 477 1201 477 1201 478 1201 477 1201 477 1199 1305 342 478 7660 1303 334 1304 333 478 1194 1304 336 1303 343 477 1202 477 1202 476 1202 477 1202 476 1200 1304 343 477 7647 1303 334 1304 334 476 1195 1303 336 1303 343 476 1202 476 1202 476 1202 476 1202 476 1200 1303 343 476 7659 1302 335 1302 335 476 1196 1302 337 1302 344 475 1203 475 1203 475 1203 475 1203 475 1201 1302 344 476 7646 1302 335 1300 338 474 1197 1300 338 1301 345 474 1204 474 1204 474 1204 449 1229 449 1227 1275 370 449 7690 1275 362 1275 361 449 1222 1275 364 1275 371 448 1230 448 1230 448 1231 447 1232 446 1229 1274 372 447 -# +# name: Rotate type: raw frequency: 38000 duty_cycle: 0.330000 data: 1338 312 1380 310 446 1133 1313 356 1284 362 457 1221 457 1219 1285 363 456 1221 457 1222 482 1196 482 7655 1282 356 1281 357 453 1219 1279 360 1279 367 452 1226 452 1224 1280 367 452 1226 452 1226 452 1226 452 7671 1279 358 1279 358 452 1219 1279 360 1279 367 452 1227 451 1225 1279 367 451 1227 451 1227 451 1227 451 7682 1279 359 1278 359 451 1220 1278 361 1278 368 451 1227 450 1225 1278 368 451 1227 451 1228 450 1228 450 7691 1277 360 1277 360 450 1221 1277 362 1277 368 450 1228 449 1226 1277 369 449 1229 449 1252 426 1252 426 -# +# name: Timer type: raw frequency: 38000 @@ -1479,19 +1479,19 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3565 3379 984 2506 984 2505 985 797 900 2590 900 2590 926 2572 926 826 925 829 922 858 894 859 894 858 895 2604 894 859 894 859 894 2596 894 859 894 859 894 867 894 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 40343 3530 3468 895 2596 894 2596 894 859 894 2596 894 2596 894 2604 894 859 894 859 894 859 894 859 894 859 894 2604 894 859 894 859 894 2596 894 859 894 859 894 867 894 2596 894 2596 894 2596 894 2596 895 2596 894 866 895 -# +# name: Speed_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 3595 3377 930 2561 929 2561 929 853 900 2591 899 2590 900 2597 901 2590 925 2565 924 2567 922 2570 920 2596 894 866 895 858 895 858 895 2596 894 858 895 859 894 866 895 858 895 858 895 858 895 859 894 858 895 2604 894 40343 3533 3467 895 2595 895 2596 894 858 895 2596 894 2596 894 2604 895 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 859 894 859 894 859 894 859 894 2605 893 40319 3533 3442 920 2595 895 2596 894 858 895 2596 894 2596 894 2604 894 2596 894 2596 894 2596 894 2596 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 859 894 859 894 859 894 859 894 2604 894 -# +# name: Rotate type: raw frequency: 38000 duty_cycle: 0.330000 data: 3594 3380 981 2505 930 2560 930 853 900 2590 900 2590 900 2598 900 2589 900 853 924 2568 921 2571 919 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 2596 894 859 894 859 894 859 894 2604 894 40335 3532 3467 895 2596 894 2596 895 859 894 2596 894 2596 894 2605 894 2596 894 859 894 2596 894 2597 894 2596 894 867 894 859 894 859 894 2596 894 859 894 859 894 867 894 859 894 2597 894 859 894 859 894 859 894 2605 893 -# +# name: Timer type: raw frequency: 38000 @@ -1509,405 +1509,459 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1370 314 1375 320 519 1167 1370 322 1339 350 465 1221 467 1222 467 1222 466 1221 467 1221 467 1221 1317 7067 1317 372 1341 349 491 1198 1340 352 1337 353 486 1202 486 1226 462 1226 462 1227 461 1227 462 1227 1311 7073 1312 378 1311 378 462 1227 1311 378 1312 378 462 1227 462 1227 461 1227 462 1227 461 1227 461 1227 1311 7074 1311 378 1311 378 462 1227 1311 379 1310 379 461 1228 461 1228 460 1228 460 1228 460 1229 459 1228 1310 7076 1309 381 1309 380 460 1229 1310 381 1309 381 458 1230 458 1230 459 1230 459 1230 459 1230 458 1230 1309 7077 1309 380 1310 380 460 1229 1310 380 1310 380 459 1229 460 1229 459 1229 460 1229 459 1229 459 1229 1310 7075 1310 379 1310 380 459 1229 1310 380 1310 379 460 1229 460 1229 459 1229 460 1228 460 1229 459 1229 1310 7074 1310 380 1310 379 460 1229 1310 379 1310 379 460 1229 459 1229 459 1229 460 1229 460 1229 460 1228 1311 -# +# name: Speed_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 1394 321 1369 321 520 1141 1397 321 1368 321 519 1142 495 1194 495 1194 494 1195 494 1195 1366 351 491 7891 1341 350 1337 353 487 1201 1337 354 1336 354 487 1202 487 1202 487 1202 487 1202 487 1202 1336 354 487 7900 1336 354 1337 354 486 1202 1337 354 1336 354 487 1202 487 1202 487 1203 486 1202 487 1202 1336 354 487 7901 1335 354 1336 354 487 1203 1335 354 1336 355 486 1203 486 1203 486 1203 486 1203 486 1203 1335 355 486 7900 1335 355 1335 355 486 1203 1335 355 1335 355 486 1203 486 1204 485 1204 485 1204 485 1203 1335 356 485 7900 1334 356 1334 356 485 1204 1334 356 1334 356 485 1204 485 1204 484 1204 485 1204 485 1204 1333 357 484 7901 1333 357 1333 380 460 1228 1310 380 1310 380 460 1228 460 1228 460 1228 460 1228 460 1228 1310 380 460 7924 1309 380 1310 380 460 1229 1309 380 1310 380 460 1229 459 1229 460 1229 459 1230 459 1230 1309 381 458 -# +# name: Rotate type: raw frequency: 38000 duty_cycle: 0.330000 data: 1343 372 1318 372 467 1194 1346 372 1318 372 467 1193 525 1164 1400 319 495 1166 523 1166 523 1167 521 7890 1344 347 1340 350 489 1200 1339 351 1339 351 488 1201 488 1201 1338 351 488 1201 488 1201 488 1201 488 7899 1338 352 1338 352 487 1201 1339 352 1338 352 488 1201 488 1201 1339 352 487 1202 487 1201 488 1201 488 7899 1338 352 1338 352 487 1201 1338 352 1338 352 487 1202 487 1202 1337 353 486 1202 487 1202 487 1202 487 7900 1337 353 1337 353 486 1202 1338 353 1337 353 486 1202 487 1202 1338 353 486 1202 487 1202 487 1202 487 7900 1337 353 1337 353 486 1203 1336 354 1336 354 485 1203 486 1204 1335 354 485 1203 485 1203 486 1203 486 7901 1336 378 1312 355 484 1205 1335 378 1312 378 461 1227 462 1227 1313 378 461 1228 461 1228 461 1228 461 7925 1312 378 1312 378 461 1228 1311 378 1312 378 461 1228 461 1228 1312 378 461 1228 461 1228 461 1228 461 -# +# name: Timer type: raw frequency: 38000 duty_cycle: 0.330000 data: 1370 319 1371 321 519 1167 1371 322 1366 324 488 1198 466 1222 466 1222 1317 375 464 1221 467 1220 468 7915 1317 372 1341 350 490 1197 1340 376 1313 377 462 1226 462 1226 462 1226 1312 377 462 1226 462 1226 462 7921 1312 377 1313 377 462 1226 1312 377 1312 377 462 1226 462 1226 462 1226 1312 378 462 1226 462 1226 462 7922 1312 377 1313 377 462 1226 1313 377 1312 377 462 1226 462 1226 462 1226 1313 377 462 1226 462 1226 462 7921 1312 377 1312 377 462 1226 1312 377 1313 377 462 1226 462 1226 462 1226 1312 377 462 1226 462 1226 462 7921 1312 377 1313 377 462 1226 1313 377 1312 354 485 1202 486 1202 486 1202 1337 352 487 1202 487 1201 487 7897 1337 352 1337 351 488 1200 1339 351 1339 352 487 1201 487 1201 488 1201 1338 352 487 1201 487 1201 488 7896 1338 352 1337 352 487 1201 1337 352 1337 352 487 1201 487 1201 487 1201 1338 352 487 1201 487 1201 487 -# +# name: Mode 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 -# +# 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 -# +# name: Power type: parsed protocol: NECext address: 82 21 00 00 command: 1F E0 00 00 -# +# name: Power type: parsed protocol: NECext address: 82 21 00 00 command: 1B E4 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.33 data: 1278 421 1279 422 508 1210 1250 422 1278 421 509 1182 509 1183 508 1183 508 1183 508 1182 509 1184 1276 7327 1278 421 1279 421 509 1184 1276 422 1277 422 508 1183 508 1183 508 1182 509 1184 507 1184 507 1182 1278 7329 1351 345 1355 346 508 1183 1353 346 1353 346 509 1182 509 1182 509 1184 507 1183 508 1182 508 1183 1353 7251 1354 345 1354 346 508 1184 1352 346 1353 346 508 1184 507 1182 509 1185 506 1182 509 1183 508 1186 1350 7253 1352 345 1354 346 508 1183 1353 346 1353 345 509 1184 507 1183 508 1211 479 1184 507 1183 508 1185 1351 7254 1351 347 1352 348 506 1183 1353 347 1352 346 508 1183 508 1184 507 1187 504 1183 508 1185 506 1184 1352 -# +# name: Rotate type: raw frequency: 38000 duty_cycle: 0.33 data: 1278 421 1279 421 509 1183 1277 421 1278 422 508 1183 508 1182 509 1182 509 1182 509 1184 1276 421 509 8086 1279 421 1278 421 509 1183 1277 422 1277 422 508 1182 509 1183 508 1182 509 1184 507 1183 1352 347 508 8088 1353 346 1353 346 508 1184 1351 346 1353 347 507 1184 507 1184 507 1183 508 1187 504 1183 1353 346 508 8088 1352 345 1354 346 508 1183 1353 347 1352 346 508 1184 507 1185 506 1185 506 1184 507 1183 1349 350 508 8092 1345 352 1348 351 506 1185 1347 351 1348 352 505 1185 505 1185 506 1187 503 1185 505 1185 1347 354 504 8094 1250 446 1253 447 497 1193 1253 447 1252 446 497 1194 496 1195 495 1193 497 1195 495 1196 1252 446 495 -# +# name: Timer type: raw frequency: 38000 duty_cycle: 0.33 data: 1253 446 1253 447 495 1194 1254 446 1253 447 422 1268 422 1268 1253 448 421 1270 421 1269 422 1269 422 8176 1251 448 1251 447 421 1269 1253 445 1254 447 421 1267 423 1269 1253 446 422 1270 421 1269 422 1269 422 8174 1253 447 1252 446 423 1269 1252 447 1252 445 424 1268 423 1269 1252 447 422 1268 422 1269 422 1268 423 8174 1252 446 1253 448 493 1195 1254 446 1253 447 495 1196 495 1195 1252 447 496 1193 497 1194 498 1194 496 8098 1253 445 1254 446 499 1194 1252 446 1253 446 498 1192 498 1191 1254 447 503 1187 504 1187 504 1186 505 8091 1346 352 1347 353 505 1186 1346 352 1347 352 505 1185 506 1187 1345 351 507 1185 505 1185 505 1184 506 -# +# name: Power type: parsed protocol: NEC address: 30 00 00 00 command: 88 00 00 00 -# +# name: Speed_up type: parsed protocol: NEC address: 30 00 00 00 command: 8C 00 00 00 -# +# name: Speed_dn type: parsed protocol: NEC address: 30 00 00 00 command: 87 00 00 00 -# +# name: Power type: raw frequency: 38000 duty_cycle: 0.330000 data: 1255 435 1257 435 415 1278 1257 440 1256 435 415 1278 415 1278 415 1279 414 1279 414 1279 414 1278 1257 7176 1257 436 1256 435 415 1279 1256 438 1258 437 413 1279 414 1278 415 1278 415 1279 414 1279 414 1278 1257 7174 1256 436 1256 437 413 1278 1257 439 1257 435 415 1278 415 1278 415 1277 416 1279 414 1278 415 1278 1256 7175 1257 435 1257 435 415 1279 1256 440 1256 435 415 1278 415 1278 415 1278 415 1278 415 1280 413 1278 1257 -# +# name: Speed_up type: raw frequency: 38000 duty_cycle: 0.330000 data: 1284 409 1283 408 440 1253 1284 411 1285 407 441 1254 439 1253 440 1253 440 1251 442 1252 1285 409 439 7991 1285 409 1283 408 440 1253 1284 411 1285 407 441 1255 438 1253 440 1252 441 1251 442 1254 1283 407 441 7994 1283 410 1282 409 439 1254 1283 413 1283 409 439 1253 440 1253 440 1253 440 1253 440 1253 1284 407 441 7996 1284 409 1283 408 440 1253 1284 412 1284 408 440 1253 440 1253 440 1251 442 1253 440 1253 1284 409 439 7996 1284 407 1285 408 440 1254 1283 411 1285 409 439 1254 439 1253 440 1252 441 1253 440 1253 1284 408 440 7996 1284 408 1284 409 439 1255 1282 411 1285 408 440 1254 439 1252 441 1252 441 1254 439 1253 1284 407 441 -# +# name: Mode type: raw frequency: 38000 duty_cycle: 0.330000 data: 1251 439 1253 437 414 1282 1252 441 1255 436 415 1279 414 1280 413 1280 413 1279 1255 437 414 1279 414 8020 1253 438 1255 438 413 1281 1253 441 1255 438 413 1280 413 1279 414 1279 414 1280 1254 437 414 1280 413 8024 1253 438 1254 437 414 1281 1253 440 1256 437 414 1279 414 1279 414 1278 415 1280 1254 437 414 1280 413 8023 1254 440 1252 436 415 1279 1255 441 1255 436 415 1279 414 1279 414 1279 414 1279 1255 437 414 1279 414 8024 1253 437 1256 437 414 1280 1254 441 1255 437 414 1278 415 1280 413 1280 413 1280 1254 437 414 1279 414 8023 1254 438 1255 436 415 1280 1255 441 1255 436 415 1279 414 1279 414 1279 414 1280 1254 436 415 1280 413 -# +# name: Timer type: raw frequency: 38000 duty_cycle: 0.330000 data: 1252 437 1255 438 519 1174 1255 441 1255 437 518 1175 519 1173 520 1175 1253 437 518 1175 519 1175 518 7917 1253 438 1254 438 413 1281 1253 440 1256 437 414 1279 465 1228 465 1228 1254 436 415 1279 465 1227 466 7969 1253 438 1254 438 413 1280 1254 440 1256 436 415 1280 413 1278 466 1229 1254 437 414 1279 414 1278 415 8024 1252 437 1255 436 415 1279 1255 440 1256 437 414 1279 414 1279 414 1279 1255 437 414 1279 414 1278 415 -# +# name: Rotate type: raw frequency: 38000 duty_cycle: 0.330000 data: 1253 434 1258 435 492 1200 1258 438 1258 434 498 1194 494 1202 1257 433 494 1200 493 1200 493 1200 493 7940 1259 434 1258 433 493 1200 1259 438 1258 433 493 1199 494 1200 1259 434 492 1200 493 1200 493 1199 494 7939 1258 435 1257 434 497 1196 1258 437 1259 433 498 1196 492 1201 1258 434 497 1197 491 1200 493 1201 492 7941 1258 434 1258 435 495 1197 1258 437 1259 433 497 1197 497 1196 1259 434 496 1197 496 1197 496 1196 497 7939 1258 435 1257 434 495 1198 1258 438 1258 434 495 1198 495 1199 1257 435 494 1199 494 1197 496 1197 496 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 121 20917 311 387 1345 372 1316 372 450 1212 1319 400 1292 396 450 1210 479 1212 476 1238 450 1238 451 1238 451 1238 1345 7078 1316 372 1315 372 474 1216 1314 377 1314 374 472 1217 472 1217 472 1217 472 1217 472 1217 472 1217 1314 7106 1314 375 1313 374 472 1218 1313 378 1314 375 471 1218 471 1218 471 1218 471 1218 471 1218 471 1218 1313 7108 1312 376 1312 375 471 1219 1312 380 1312 376 470 1218 471 1218 471 1219 470 1218 471 1219 470 1219 1312 7110 1311 376 1312 376 470 1219 1312 381 1311 377 469 1219 470 1219 469 1220 469 1220 469 1220 469 1221 1310 7133 1286 402 1286 401 445 1245 1285 406 1286 401 445 1244 444 1244 445 1244 445 1244 445 1244 445 1245 1286 7134 1285 402 1286 402 444 1245 1286 406 1286 402 444 1245 444 1245 444 1245 444 1245 443 1245 444 1245 1285 7136 1284 403 1285 403 443 1245 1285 407 1285 403 443 1246 443 1246 443 1246 443 1246 443 1246 443 1246 1285 7135 1284 404 1284 404 442 1247 1284 408 1284 404 442 1247 442 1247 442 1247 442 1247 442 1248 441 1248 1283 7138 1282 405 1283 405 441 1249 1282 410 1281 407 440 1250 439 1249 440 1273 415 1274 390 1299 415 1274 1256 7166 1256 432 1256 431 415 1275 1255 436 1232 456 390 1299 415 1275 390 1299 390 1299 390 1299 390 1300 1231 7189 1231 457 1232 457 389 1301 1230 461 1231 458 388 1301 388 1301 388 1301 388 1326 363 1326 363 1327 1204 7217 1204 484 1205 484 362 1328 1203 488 1204 485 361 1328 361 1328 361 1328 361 1329 360 1353 335 1330 1202 7246 1177 511 1177 512 334 1356 1175 542 1150 539 307 1383 306 1383 306 1382 307 1409 280 1409 280 1410 1122 +# +name: Rotate +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1293 396 1293 395 450 1238 1294 399 1293 395 450 1238 451 1238 1294 395 450 1237 452 1237 452 1237 452 7964 1294 395 1318 370 475 1215 1316 377 1315 397 448 1241 448 1242 1290 398 447 1242 447 1242 447 1242 447 7970 1290 398 1292 372 473 1217 1316 377 1315 397 448 1217 473 1217 1316 372 473 1216 473 1217 473 1216 473 7944 1316 373 1316 372 473 1217 1316 376 1316 372 473 1216 473 1217 1316 372 473 1216 473 1216 473 1216 473 7942 1318 372 1317 371 474 1216 1317 375 1317 371 473 1216 473 1216 1318 371 474 1216 473 1216 473 1216 473 7943 1318 371 1318 371 474 1216 1318 375 1317 371 474 1216 474 1216 1318 371 474 1216 473 1216 473 1216 474 7943 1317 372 1317 372 473 1217 1317 376 1316 372 473 1217 472 1217 1316 372 473 1217 472 1217 472 1217 472 7944 1314 375 1314 398 447 1243 1290 403 1289 398 447 1243 446 1244 1289 399 446 1243 447 1243 446 1243 446 7971 1289 400 1289 399 446 1244 1289 403 1289 399 446 1243 446 1244 1289 399 446 1244 446 1244 445 1244 445 7972 1288 400 1289 399 446 1244 1290 403 1289 399 446 1244 445 1244 1289 399 446 1244 446 1244 445 1244 445 7946 1288 401 1288 400 469 1222 1311 380 1312 399 445 1244 445 1245 1289 399 445 1244 445 1245 445 1244 446 7971 1289 400 1289 400 444 1246 1289 403 1289 400 443 1246 444 1245 1272 416 445 1244 420 1269 444 1245 420 7996 1288 401 1288 400 420 1270 1264 429 1263 425 419 1270 419 1270 1264 425 419 1270 419 1270 419 1270 419 7995 1262 426 1263 425 419 1270 1263 430 1262 425 419 1270 419 1271 1262 426 418 1271 418 1271 418 1271 418 +# +name: Timer +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1321 393 1295 393 451 1211 1322 396 1295 393 451 1210 507 1182 507 1181 1351 364 479 1208 480 1208 480 7933 1322 366 1320 368 475 1213 1319 373 1318 370 474 1214 474 1215 474 1215 1317 371 473 1215 473 1215 473 7939 1315 372 1316 372 472 1216 1316 376 1315 373 471 1217 472 1218 470 1218 1313 398 446 1242 446 1242 446 7967 1262 425 1263 425 444 1245 1263 429 1287 400 445 1244 445 1243 446 1244 1288 400 445 1243 446 1243 446 7966 1288 400 1288 400 445 1244 1287 404 1288 400 445 1244 444 1244 444 1245 1286 401 444 1244 444 1245 444 7968 1261 426 1286 401 419 1270 1285 406 1285 402 444 1244 445 1244 445 1245 1287 400 444 1244 445 1244 444 7968 1288 400 1288 400 444 1244 1288 404 1287 399 445 1244 444 1244 445 1244 1263 425 444 1244 444 1244 444 7967 1261 427 1261 426 418 1272 1260 456 1235 452 392 1296 392 1296 393 1297 1235 453 392 1296 393 1296 393 7996 1261 427 1261 427 418 1270 1262 430 1285 402 444 1245 443 1245 444 1245 1286 402 444 1245 444 1245 444 7969 1286 402 1286 402 444 1245 1286 406 1285 402 444 1245 444 1245 444 1245 1286 402 444 1245 444 1245 444 7968 1284 403 1285 403 443 1246 1284 407 1284 403 443 1246 442 1246 443 1246 1284 403 443 1246 443 1246 442 7971 1284 405 1283 405 441 1247 1283 408 1283 405 441 1248 441 1248 440 1249 1281 406 440 1249 440 1249 440 7974 1281 408 1280 431 390 1299 1232 460 1256 432 389 1299 390 1299 389 1300 1231 456 390 1299 390 1299 390 8023 1231 457 1231 457 389 1300 1231 461 1230 458 388 1300 389 1301 388 1301 1230 458 388 1302 387 1301 388 8027 1228 484 1204 484 362 1327 1204 488 1203 484 362 1328 361 1328 361 1329 1202 486 360 1354 335 1329 360 8080 1176 512 1176 512 334 1356 1175 542 1149 539 306 1382 307 1384 305 1383 1149 566 279 1436 252 1410 279 +# +name: Mode +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 1373 341 1295 393 452 1211 1322 397 1295 393 451 1210 479 1210 479 1211 478 1237 1296 393 451 1236 453 7962 1321 367 1320 368 476 1214 1318 374 1318 370 474 1215 474 1215 474 1215 474 1216 1316 371 473 1215 474 7941 1316 372 1316 372 472 1217 1316 376 1316 372 473 1216 473 1216 473 1217 472 1217 1315 373 472 1217 472 7941 1315 374 1314 374 471 1218 1314 401 1291 398 447 1242 447 1242 447 1242 447 1243 1290 398 446 1242 447 7968 1290 399 1289 398 447 1243 1290 402 1290 398 447 1242 447 1242 447 1242 447 1243 1289 398 446 1243 446 7968 1289 399 1289 398 447 1243 1290 402 1290 398 446 1243 446 1243 446 1243 446 1243 1290 398 446 1243 446 7967 1289 399 1289 399 446 1244 1289 403 1289 398 446 1243 446 1243 446 1243 446 1244 1289 399 446 1243 446 7969 1288 400 1288 399 445 1244 1288 403 1288 400 445 1244 445 1244 445 1244 445 1244 1288 400 445 1244 420 7995 1261 427 1261 428 416 1296 1236 456 1236 452 392 1296 393 1296 393 1296 393 1296 1236 452 393 1296 393 7997 1260 428 1260 428 416 1273 1260 432 1259 428 417 1296 392 1272 417 1296 393 1296 1237 452 393 1296 393 8021 1236 452 1236 452 392 1297 1236 456 1236 452 393 1297 392 1297 392 1297 392 1298 1235 453 391 1298 391 +# +name: Power +type: parsed +protocol: NEC +address: 03 00 00 00 +command: D8 00 00 00 +# +name: Speed_up +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 4F 00 00 00 +# +name: Speed_dn +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 47 00 00 00 +# +name: Rotate +type: parsed +protocol: NEC +address: 03 00 00 00 +command: C3 00 00 00 +# +name: Timer +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 9B 00 00 00 diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index 628a3f530..c06a17cb3 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 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +# Last Updated 19th Aug, 2023 +# Last Checked 19th Aug, 2023 # # ON name: Power @@ -1078,3 +1078,9 @@ type: parsed protocol: NEC address: 01 00 00 00 command: 03 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 02 00 00 00 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 5c0a4c1a9..a6b5650ac 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 24th Jul, 2023 -# Last Checked 24th Jul, 2023 +# Last Checked 19th Aug, 2023 # name: Power type: parsed diff --git a/assets/unit_tests/storage/md5.txt b/assets/unit_tests/storage/md5.txt new file mode 100644 index 000000000..777e390be --- /dev/null +++ b/assets/unit_tests/storage/md5.txt @@ -0,0 +1 @@ +Yo dawg, I heard you like md5... \ No newline at end of file diff --git a/fbt b/fbt index ef41cc056..471285a76 100755 --- a/fbt +++ b/fbt @@ -29,7 +29,7 @@ if [ -z "$FBT_NO_SYNC" ]; then echo "\".git\" directory not found, please clone repo via \"git clone\""; exit 1; fi - git submodule update --init --depth 1 --jobs "$N_GIT_THREADS"; + git submodule update --init --jobs "$N_GIT_THREADS"; fi $SCONS_EP $SCONS_DEFAULT_FLAGS "$@" diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 8dd50b537..a61679bca 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,+,34.3,, +Version,+,35.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -164,6 +164,7 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/compress.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -284,6 +285,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*" @@ -555,6 +557,7 @@ Function,+,button_menu_get_view,View*,ButtonMenu* Function,+,button_menu_reset,void,ButtonMenu* Function,+,button_menu_set_header,void,"ButtonMenu*, const char*" Function,+,button_menu_set_selected_item,void,"ButtonMenu*, uint32_t" +Function,+,button_panel_add_icon,void,"ButtonPanel*, uint16_t, uint16_t, const Icon*" Function,+,button_panel_add_item,void,"ButtonPanel*, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t, const Icon*, const Icon*, ButtonItemCallback, void*" Function,+,button_panel_add_label,void,"ButtonPanel*, uint16_t, uint16_t, Font, const char*" Function,+,button_panel_alloc,ButtonPanel*, @@ -627,6 +630,13 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,+,compress_alloc,Compress*,uint16_t +Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_free,void,Compress* +Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,+,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" @@ -1027,14 +1037,20 @@ Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" +Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" +Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, -Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t -Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t +Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" +Function,+,furi_hal_crypto_unload_key,_Bool, Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index 32c9b619c..63da03e04 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -128,19 +128,22 @@ void furi_hal_resources_init_early() { furi_hal_resources_init_input_pins(GpioModeInput); + // Explicit, surviving reset, pulls + LL_PWR_EnablePUPDCfg(); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker + // SD Card stepdown control furi_hal_gpio_write(&gpio_periph_power, 1); furi_hal_gpio_init(&gpio_periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); // Display pins - furi_hal_gpio_write(&gpio_display_rst_n, 1); + furi_hal_gpio_write(&gpio_display_rst_n, 0); furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); - furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - - // Pullup display reset pin for shutdown - SET_BIT(PWR->PUCRB, gpio_display_rst_n.pin); - CLEAR_BIT(PWR->PDCRB, gpio_display_rst_n.pin); - SET_BIT(PWR->CR3, PWR_CR3_APC); + LL_PWR_EnableGPIOPullUp(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_0); // gpio_display_rst_n + furi_hal_gpio_write(&gpio_display_di, 0); + furi_hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_1); // gpio_display_di // Hard reset USB furi_hal_gpio_write(&gpio_usb_dm, 1); @@ -182,18 +185,6 @@ void furi_hal_resources_init() { // Button pins furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); - // Explicit pulls pins - LL_PWR_EnablePUPDCfg(); - LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker - LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro - - // Display pins - furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_display_rst_n, 0); - - furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_display_di, 0); - // SD pins furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_sdcard_cd, 0); @@ -225,7 +216,7 @@ void furi_hal_resources_init() { } int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { - // TODO: describe second ROW + // TODO FL-3500: describe second ROW if(gpio == &gpio_ext_pa7) return 2; else if(gpio == &gpio_ext_pa6) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ab4be83c0..4c5cf5da3 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,7 +1,7 @@ entry,status,name,type,params -Version,+,34.4,, +Version,+,35.1,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, -Header,+,applications/main/archive/helpers/favorite_timeout.h,, +Header,+,applications/main/archive/helpers/archive_helpers_ext.h,, Header,+,applications/services/applications.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -212,6 +212,7 @@ Header,+,lib/subghz/subghz_worker.h,, Header,+,lib/subghz/transmitter.h,, Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/compress.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -645,6 +646,7 @@ Function,+,button_menu_get_view,View*,ButtonMenu* Function,+,button_menu_reset,void,ButtonMenu* Function,+,button_menu_set_header,void,"ButtonMenu*, const char*" Function,+,button_menu_set_selected_item,void,"ButtonMenu*, uint32_t" +Function,+,button_panel_add_icon,void,"ButtonPanel*, uint16_t, uint16_t, const Icon*" Function,+,button_panel_add_item,void,"ButtonPanel*, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t, const Icon*, const Icon*, ButtonItemCallback, void*" Function,+,button_panel_add_label,void,"ButtonPanel*, uint16_t, uint16_t, Font, const char*" Function,+,button_panel_alloc,ButtonPanel*, @@ -720,6 +722,13 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,+,compress_alloc,Compress*,uint16_t +Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_free,void,Compress* +Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,+,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" @@ -802,21 +811,20 @@ Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed Function,+,dolphin_flush,void,Dolphin* -Function,+,dolphin_get_level,uint8_t,int -Function,+,dolphin_get_levels,const int*, +Function,+,dolphin_get_level,uint8_t,uint32_t Function,+,dolphin_get_pubsub,FuriPubSub*,Dolphin* Function,+,dolphin_state_alloc,DolphinState*, Function,+,dolphin_state_butthurted,void,DolphinState* Function,+,dolphin_state_clear_limits,void,DolphinState* Function,+,dolphin_state_free,void,DolphinState* Function,+,dolphin_state_increase_level,void,DolphinState* -Function,+,dolphin_state_is_levelup,_Bool,int +Function,+,dolphin_state_is_levelup,_Bool,uint32_t Function,+,dolphin_state_load,_Bool,DolphinState* Function,+,dolphin_state_on_deed,void,"DolphinState*, DolphinDeed" Function,+,dolphin_state_save,_Bool,DolphinState* Function,+,dolphin_state_timestamp,uint64_t, -Function,+,dolphin_state_xp_above_last_levelup,uint32_t,int -Function,+,dolphin_state_xp_to_levelup,uint32_t,int +Function,+,dolphin_state_xp_above_last_levelup,uint32_t,uint32_t +Function,+,dolphin_state_xp_to_levelup,uint32_t,uint32_t Function,+,dolphin_stats,DolphinStats,Dolphin* Function,+,dolphin_upgrade_level,void,Dolphin* Function,-,dprintf,int,"int, const char*, ..." @@ -841,6 +849,7 @@ Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_ Function,+,elements_scrollable_text_line_centered,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool, _Bool" Function,+,elements_scrollable_text_line_str,void,"Canvas*, uint8_t, uint8_t, uint8_t, const char*, size_t, _Bool, _Bool" Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" +Function,+,elements_scrollbar_horizontal,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" @@ -909,7 +918,8 @@ 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*, const char*" +Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" +Function,+,file_browser_worker_folder_refresh_sel,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* @@ -1161,14 +1171,20 @@ Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" +Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" +Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, -Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t -Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t +Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" +Function,+,furi_hal_crypto_unload_key,_Bool, Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, @@ -1440,6 +1456,7 @@ 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_ext_power_amp,_Bool, Function,+,furi_hal_subghz_get_lqi,uint8_t, Function,+,furi_hal_subghz_get_rolling_counter_mult,uint8_t, Function,+,furi_hal_subghz_get_rssi,float, @@ -1457,6 +1474,7 @@ Function,+,furi_hal_subghz_reset,void, Function,+,furi_hal_subghz_rx,void, 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_ext_power_amp,void,_Bool 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 @@ -1703,7 +1721,6 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,-,gui_get_count_of_enabled_view_port_in_layer,uint8_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" @@ -1718,6 +1735,8 @@ Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" +Function,+,hsv2rgb,RgbColor,HsvColor +Function,+,hsvcmp,int,"const HsvColor*, const HsvColor*" Function,-,hypot,double,"double, double" Function,-,hypotf,float,"float, float" Function,-,hypotl,long double,"long double, long double" @@ -2180,6 +2199,7 @@ Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* Function,-,nfc_generate_mf_classic,void,"NfcDeviceData*, uint8_t, MfClassicType" +Function,+,nfc_generate_mf_classic_ext,void,"NfcDeviceData*, uint8_t, MfClassicType, _Bool, uint8_t*" Function,+,nfc_get_dev_type,const char*,FuriHalNfcType Function,-,nfc_guess_protocol,const char*,NfcProtocol Function,+,nfc_mf_classic_type,const char*,MfClassicType @@ -2540,13 +2560,22 @@ Function,-,rfal_platform_spi_acquire,void, Function,-,rfal_platform_spi_release,void, Function,-,rfal_set_callback_context,void,void* Function,-,rfal_set_state_changed_callback,void,RfalStateChangedCallback -Function,+,rgb_backlight_get_color_count,uint8_t, -Function,+,rgb_backlight_get_color_text,const char*,uint8_t -Function,+,rgb_backlight_get_settings,RGBBacklightSettings*, +Function,+,rgb2hsv,HsvColor,RgbColor +Function,+,rgb_backlight_get_color,RgbColor,uint8_t +Function,+,rgb_backlight_get_rainbow_interval,uint32_t, +Function,+,rgb_backlight_get_rainbow_mode,RGBBacklightRainbowMode, +Function,+,rgb_backlight_get_rainbow_saturation,uint8_t, +Function,+,rgb_backlight_get_rainbow_speed,uint8_t, Function,-,rgb_backlight_load_settings,void, +Function,+,rgb_backlight_reconfigure,void,_Bool Function,+,rgb_backlight_save_settings,void, -Function,+,rgb_backlight_set_color,void,uint8_t -Function,-,rgb_backlight_update,void,uint8_t +Function,+,rgb_backlight_set_color,void,"uint8_t, RgbColor" +Function,+,rgb_backlight_set_rainbow_interval,void,uint32_t +Function,+,rgb_backlight_set_rainbow_mode,void,RGBBacklightRainbowMode +Function,+,rgb_backlight_set_rainbow_saturation,void,uint8_t +Function,+,rgb_backlight_set_rainbow_speed,void,uint8_t +Function,-,rgb_backlight_update,void,"uint8_t, _Bool" +Function,+,rgbcmp,int,"const RgbColor*, const RgbColor*" Function,-,rindex,char*,"const char*, int" Function,-,rint,double,double Function,-,rintf,float,float @@ -2575,6 +2604,7 @@ Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcApp Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" Function,-,rpmatch,int,const char* +Function,-,run_with_default_app,void,const char* Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" @@ -2666,6 +2696,7 @@ Function,+,storage_file_close,_Bool,File* Function,+,storage_file_copy_to_file,_Bool,"File*, File*, uint32_t" Function,+,storage_file_eof,_Bool,File* Function,+,storage_file_exists,_Bool,"Storage*, const char*" +Function,+,storage_file_expand,_Bool,"File*, uint64_t" Function,+,storage_file_free,void,File* Function,+,storage_file_get_error,FS_Error,File* Function,+,storage_file_get_error_desc,const char*,File* @@ -3137,6 +3168,7 @@ Function,+,variable_item_list_get_selected_item_index,uint8_t,VariableItemList* Function,+,variable_item_list_get_view,View*,VariableItemList* Function,+,variable_item_list_reset,void,VariableItemList* Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, VariableItemListEnterCallback, void*" +Function,+,variable_item_list_set_header,void,"VariableItemList*, const char*" Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t" Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" @@ -3297,10 +3329,14 @@ Variable,+,A_Sub1ghz_14,Icon, Variable,+,A_U2F_14,Icon, Variable,+,A_Xtreme_14,Icon, Variable,+,A_iButton_14,Icon, +Variable,+,DOLPHIN_LEVELS,const uint32_t[], +Variable,+,DOLPHIN_LEVEL_COUNT,const size_t, Variable,+,FLIPPER_APPS,const FlipperInternalApplication[], Variable,+,FLIPPER_APPS_COUNT,const size_t, Variable,-,FLIPPER_ARCHIVE,const FlipperInternalApplication, Variable,-,FLIPPER_AUTORUN_APP_NAME,const char*, +Variable,-,FLIPPER_DEBUG_APPS,const FlipperInternalApplication[], +Variable,-,FLIPPER_DEBUG_APPS_COUNT,const size_t, Variable,+,FLIPPER_EXTERNAL_APPS,const FlipperExternalApplication[], Variable,+,FLIPPER_EXTERNAL_APPS_COUNT,const size_t, Variable,-,FLIPPER_ON_SYSTEM_START,const FlipperInternalOnStartHook[], @@ -3352,21 +3388,13 @@ 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, @@ -3376,26 +3404,16 @@ 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_17x11,Icon, Variable,+,I_KeyBackspace_17x11,Icon, Variable,+,I_KeyKeyboardSelected_10x11,Icon, @@ -3407,22 +3425,14 @@ 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, @@ -3432,8 +3442,6 @@ 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, @@ -3443,8 +3451,6 @@ 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, @@ -3455,26 +3461,12 @@ 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, @@ -3482,26 +3474,100 @@ Variable,+,I_WarningDolphin_45x42,Icon, Variable,+,I_Warning_30x23,Icon, Variable,+,I_back_10px,Icon, Variable,+,I_badkb_10px,Icon, +Variable,+,I_bright_text_30x30,Icon, +Variable,+,I_celsius_24x23,Icon, +Variable,+,I_celsius_hover_24x23,Icon, +Variable,+,I_ch_down_24x21,Icon, +Variable,+,I_ch_down_hover_24x21,Icon, +Variable,+,I_ch_text_31x34,Icon, +Variable,+,I_ch_up_24x21,Icon, +Variable,+,I_ch_up_hover_24x21,Icon, +Variable,+,I_cool_30x51,Icon, Variable,+,I_dir_10px,Icon, +Variable,+,I_dry_19x20,Icon, +Variable,+,I_dry_hover_19x20,Icon, +Variable,+,I_dry_text_15x5,Icon, +Variable,+,I_exit_19x20,Icon, +Variable,+,I_exit_hover_19x20,Icon, +Variable,+,I_exit_text_18x5,Icon, +Variable,+,I_fahren_24x23,Icon, +Variable,+,I_fahren_hover_24x23,Icon, +Variable,+,I_flash_19x20,Icon, +Variable,+,I_flash_hover_19x20,Icon, +Variable,+,I_flash_text_21x5,Icon, +Variable,+,I_heat_30x51,Icon, +Variable,+,I_hourglass0_24x24,Icon, +Variable,+,I_hourglass1_24x24,Icon, +Variable,+,I_hourglass2_24x24,Icon, +Variable,+,I_hourglass3_24x24,Icon, +Variable,+,I_hourglass4_24x24,Icon, +Variable,+,I_hourglass5_24x24,Icon, +Variable,+,I_hourglass6_24x24,Icon, Variable,+,I_iButtonDolphinVerySuccess_108x52,Icon, Variable,+,I_iButtonKey_49x44,Icon, Variable,+,I_ibutt_10px,Icon, +Variable,+,I_input_19x20,Icon, +Variable,+,I_input_hover_19x20,Icon, +Variable,+,I_input_text_24x5,Icon, Variable,+,I_ir_10px,Icon, Variable,+,I_ir_scope_10px,Icon, Variable,+,I_keyboard_10px,Icon, Variable,+,I_loading_10px,Icon, +Variable,+,I_max_24x23,Icon, +Variable,+,I_max_hover_24x23,Icon, +Variable,+,I_menu_text_20x5,Icon, +Variable,+,I_mode_19x20,Icon, +Variable,+,I_mode_hover_19x20,Icon, +Variable,+,I_mode_text_20x5,Icon, Variable,+,I_music_10px,Icon, +Variable,+,I_mute_19x20,Icon, +Variable,+,I_mute_hover_19x20,Icon, +Variable,+,I_mute_text_19x5,Icon, +Variable,+,I_next_19x20,Icon, +Variable,+,I_next_hover_19x20,Icon, +Variable,+,I_next_text_19x6,Icon, +Variable,+,I_off_19x20,Icon, +Variable,+,I_off_hover_19x20,Icon, +Variable,+,I_off_text_12x5,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_pause_19x20,Icon, +Variable,+,I_pause_hover_19x20,Icon, +Variable,+,I_pause_text_23x5,Icon, +Variable,+,I_play_19x20,Icon, +Variable,+,I_play_hover_19x20,Icon, +Variable,+,I_play_text_19x5,Icon, +Variable,+,I_power_19x20,Icon, +Variable,+,I_power_hover_19x20,Icon, +Variable,+,I_power_text_24x5,Icon, +Variable,+,I_prev_19x20,Icon, +Variable,+,I_prev_hover_19x20,Icon, +Variable,+,I_prev_text_19x5,Icon, +Variable,+,I_rotate_19x20,Icon, +Variable,+,I_rotate_hover_19x20,Icon, +Variable,+,I_rotate_text_24x5,Icon, Variable,+,I_search_10px,Icon, +Variable,+,I_speed_text_30x30,Icon, +Variable,+,I_stop_19x20,Icon, +Variable,+,I_stop_hover_19x20,Icon, +Variable,+,I_stop_text_19x5,Icon, Variable,+,I_sub1_10px,Icon, Variable,+,I_subplaylist_10px,Icon, Variable,+,I_subrem_10px,Icon, +Variable,+,I_timer_19x20,Icon, +Variable,+,I_timer_hover_19x20,Icon, +Variable,+,I_timer_text_23x5,Icon, Variable,+,I_u2f_10px,Icon, Variable,+,I_unknown_10px,Icon, Variable,+,I_update_10px,Icon, +Variable,+,I_vol_ac_text_30x30,Icon, +Variable,+,I_vol_tv_text_29x34,Icon, +Variable,+,I_voldown_24x21,Icon, +Variable,+,I_voldown_hover_24x21,Icon, +Variable,+,I_volup_24x21,Icon, +Variable,+,I_volup_hover_24x21,Icon, Variable,-,MSIRangeTable,const uint32_t[16], Variable,-,SmpsPrescalerTable,const uint32_t[4][6], Variable,+,SystemCoreClock,uint32_t, @@ -3523,6 +3589,7 @@ Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*, Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus, Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_external_extra,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_nfc,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, diff --git a/firmware/targets/f7/ble_glue/ble_glue.c b/firmware/targets/f7/ble_glue/ble_glue.c index 1cb5501d9..746df71c7 100644 --- a/firmware/targets/f7/ble_glue/ble_glue.c +++ b/firmware/targets/f7/ble_glue/ble_glue.c @@ -222,7 +222,7 @@ bool ble_glue_wait_for_c2_start(int32_t timeout) { bool started = false; do { - // TODO: use mutex? + // TODO FL-3505: use mutex? started = ble_glue->status == BleGlueStatusC2Started; if(!started) { timeout--; diff --git a/firmware/targets/f7/fatfs/ffconf.h b/firmware/targets/f7/fatfs/ffconf.h index 8408a1ec1..3d9462fc5 100644 --- a/firmware/targets/f7/fatfs/ffconf.h +++ b/firmware/targets/f7/fatfs/ffconf.h @@ -68,7 +68,7 @@ #define _USE_FASTSEEK 1 /* This option switches fast seek feature. (0:Disable or 1:Enable) */ -#define _USE_EXPAND 0 +#define _USE_EXPAND 1 /* This option switches f_expand function. (0:Disable or 1:Enable) */ #define _USE_CHMOD 0 diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c index e8e542b32..d420524df 100644 --- a/firmware/targets/f7/fatfs/sd_spi_io.c +++ b/firmware/targets/f7/fatfs/sd_spi_io.c @@ -283,7 +283,7 @@ static SdSpiCmdAnswer cmd_answer.r1 = sd_spi_wait_for_data_and_read(); break; case SdSpiCmdAnswerTypeR1B: - // TODO: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1 + // TODO FL-3507: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1 cmd_answer.r1 = sd_spi_wait_for_data_and_read(); // In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B @@ -322,7 +322,7 @@ static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) { switch(responce & 0x1F) { case SdSpiDataResponceOK: - // TODO: check timings + // TODO FL-3508: check timings sd_spi_deselect_card(); sd_spi_select_card(); @@ -684,7 +684,7 @@ static SdSpiStatus sd_spi_cmd_write_blocks( } // Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN - // TODO: check bytes count + // TODO FL-3509: check bytes count sd_spi_write_byte(SD_DUMMY_BYTE); sd_spi_write_byte(SD_DUMMY_BYTE); diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index e463edeba..ad54ed718 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -13,7 +14,7 @@ #define ENCLAVE_SIGNATURE_SIZE 16 #define CRYPTO_BLK_LEN (4 * sizeof(uint32_t)) -#define CRYPTO_TIMEOUT (1000) +#define CRYPTO_TIMEOUT_US (1000000) #define CRYPTO_MODE_ENCRYPT 0U #define CRYPTO_MODE_INIT (AES_CR_MODE_0) @@ -24,6 +25,19 @@ #define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) #define CRYPTO_AES_CBC (AES_CR_CHMOD_0) +#define CRYPTO_AES_CTR (AES_CR_CHMOD_1) +#define CRYPTO_CTR_IV_LEN (12U) +#define CRYPTO_CTR_CTR_LEN (4U) + +#define CRYPTO_AES_GCM (AES_CR_CHMOD_1 | AES_CR_CHMOD_0) +#define CRYPTO_GCM_IV_LEN (12U) +#define CRYPTO_GCM_CTR_LEN (4U) +#define CRYPTO_GCM_TAG_LEN (16U) +#define CRYPTO_GCM_PH_INIT (0x0U << AES_CR_GCMPH_Pos) +#define CRYPTO_GCM_PH_HEADER (AES_CR_GCMPH_0) +#define CRYPTO_GCM_PH_PAYLOAD (AES_CR_GCMPH_1) +#define CRYPTO_GCM_PH_FINAL (AES_CR_GCMPH_1 | AES_CR_GCMPH_0) + static FuriMutex* furi_hal_crypto_mutex = NULL; static bool furi_hal_crypto_mode_init_done = false; @@ -80,7 +94,7 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end key.size = FuriHalCryptoKeySize256; key.data = key_data; furi_hal_random_fill_buf(key_data, 32); - if(!furi_hal_crypto_store_add_key(&key, &slot)) { + if(!furi_hal_crypto_enclave_store_key(&key, &slot)) { explicit_bzero(key_data, sizeof(key_data)); FURI_LOG_E(TAG, "Error writing key to slot %u", slot); return false; @@ -90,21 +104,21 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end return true; } -bool furi_hal_crypto_verify_key(uint8_t key_slot) { +bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot) { uint8_t keys_nb = 0; uint8_t valid_keys_nb = 0; uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; uint8_t empty_iv[16] = {0}; - furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb); + furi_hal_crypto_enclave_verify(&keys_nb, &valid_keys_nb); if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key if(key_slot > keys_nb) return false; } else { // Unique key if(keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing return false; for(uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) { - if(furi_hal_crypto_store_load_key(i, empty_iv)) { + if(furi_hal_crypto_enclave_load_key(i, empty_iv)) { last_valid_slot = i; - furi_hal_crypto_store_unload_key(i); + furi_hal_crypto_enclave_unload_key(i); break; } } @@ -116,14 +130,14 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) { return true; } -bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { +bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb) { furi_assert(keys_nb); furi_assert(valid_keys_nb); uint8_t keys = 0; uint8_t keys_valid = 0; uint8_t buffer[ENCLAVE_SIGNATURE_SIZE]; for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) { - if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { + if(furi_hal_crypto_enclave_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { keys++; if(furi_hal_crypto_encrypt( enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) { @@ -131,7 +145,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) == 0; } - furi_hal_crypto_store_unload_key(key_slot + 1); + furi_hal_crypto_enclave_unload_key(key_slot + 1); } } *keys_nb = keys; @@ -142,7 +156,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { return false; } -bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) { +bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot) { furi_assert(key); furi_assert(slot); @@ -208,6 +222,16 @@ static void crypto_key_init(uint32_t* key, uint32_t* iv) { AES1->IVR0 = iv[3]; } +static bool furi_hal_crypto_wait_flag(uint32_t flag) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CRYPTO_TIMEOUT_US); + while(!READ_BIT(AES1->SR, flag)) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } + return true; +} + static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { furi_check((blk_len <= 4) && (blk_len > 0)); @@ -219,14 +243,8 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { } } - uint32_t countdown = CRYPTO_TIMEOUT; - while(!READ_BIT(AES1->SR, AES_SR_CCF)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; } SET_BIT(AES1->CR, AES_CR_CCFC); @@ -240,7 +258,7 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { return true; } -bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { +bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv) { furi_assert(slot > 0 && slot <= 100); furi_assert(furi_hal_crypto_mutex); furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); @@ -263,7 +281,7 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { } } -bool furi_hal_crypto_store_unload_key(uint8_t slot) { +bool furi_hal_crypto_enclave_unload_key(uint8_t slot) { if(!furi_hal_bt_is_alive()) { return false; } @@ -279,6 +297,27 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) { return (shci_state == SHCI_Success); } +bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv) { + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + furi_hal_crypto_mode_init_done = false; + crypto_key_init((uint32_t*)key, (uint32_t*)iv); + + return true; +} + +bool furi_hal_crypto_unload_key(void) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + + furi_hal_bus_disable(FuriHalBusAES1); + + furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); + return true; +} + bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { bool state = false; @@ -310,14 +349,8 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) SET_BIT(AES1->CR, AES_CR_EN); - uint32_t countdown = CRYPTO_TIMEOUT; - while(!READ_BIT(AES1->SR, AES_SR_CCF)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; } SET_BIT(AES1->CR, AES_CR_CCFC); @@ -343,3 +376,360 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) return state; } + +static void crypto_key_init_bswap(uint32_t* key, uint32_t* iv, uint32_t chaining_mode) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG( + AES1->CR, + AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, + CRYPTO_DATATYPE_32B | CRYPTO_KEYSIZE_256B | chaining_mode); + + if(key != NULL) { + AES1->KEYR7 = __builtin_bswap32(key[0]); + AES1->KEYR6 = __builtin_bswap32(key[1]); + AES1->KEYR5 = __builtin_bswap32(key[2]); + AES1->KEYR4 = __builtin_bswap32(key[3]); + AES1->KEYR3 = __builtin_bswap32(key[4]); + AES1->KEYR2 = __builtin_bswap32(key[5]); + AES1->KEYR1 = __builtin_bswap32(key[6]); + AES1->KEYR0 = __builtin_bswap32(key[7]); + } + + AES1->IVR3 = __builtin_bswap32(iv[0]); + AES1->IVR2 = __builtin_bswap32(iv[1]); + AES1->IVR1 = __builtin_bswap32(iv[2]); + AES1->IVR0 = __builtin_bswap32(iv[3]); +} + +static bool + furi_hal_crypto_load_key_bswap(const uint8_t* key, const uint8_t* iv, uint32_t chaining_mode) { + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + crypto_key_init_bswap((uint32_t*)key, (uint32_t*)iv, chaining_mode); + + return true; +} + +static bool wait_for_crypto(void) { + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; + } + + SET_BIT(AES1->CR, AES_CR_CCFC); + + return true; +} + +static bool furi_hal_crypto_process_block_bswap(const uint8_t* in, uint8_t* out, size_t bytes) { + uint32_t block[CRYPTO_BLK_LEN / 4]; + memset(block, 0, sizeof(block)); + + memcpy(block, in, bytes); + + block[0] = __builtin_bswap32(block[0]); + block[1] = __builtin_bswap32(block[1]); + block[2] = __builtin_bswap32(block[2]); + block[3] = __builtin_bswap32(block[3]); + + if(!crypto_process_block(block, block, CRYPTO_BLK_LEN / 4)) { + return false; + } + + block[0] = __builtin_bswap32(block[0]); + block[1] = __builtin_bswap32(block[1]); + block[2] = __builtin_bswap32(block[2]); + block[3] = __builtin_bswap32(block[3]); + + memcpy(out, block, bytes); + + return true; +} + +static bool furi_hal_crypto_process_block_no_read_bswap(const uint8_t* in, size_t bytes) { + uint32_t block[CRYPTO_BLK_LEN / 4]; + memset(block, 0, sizeof(block)); + + memcpy(block, in, bytes); + + AES1->DINR = __builtin_bswap32(block[0]); + AES1->DINR = __builtin_bswap32(block[1]); + AES1->DINR = __builtin_bswap32(block[2]); + AES1->DINR = __builtin_bswap32(block[3]); + + return wait_for_crypto(); +} + +static void furi_hal_crypto_ctr_prep_iv(uint8_t* iv) { + /* append counter to IV */ + iv[CRYPTO_CTR_IV_LEN] = 0; + iv[CRYPTO_CTR_IV_LEN + 1] = 0; + iv[CRYPTO_CTR_IV_LEN + 2] = 0; + iv[CRYPTO_CTR_IV_LEN + 3] = 1; +} + +static bool furi_hal_crypto_ctr_payload(const uint8_t* input, uint8_t* output, size_t length) { + SET_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + + size_t last_block_bytes = length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + CLEAR_BIT(AES1->CR, AES_CR_EN); + return true; +} + +bool furi_hal_crypto_ctr( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* input, + uint8_t* output, + size_t length) { + /* prepare IV and counter */ + uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN]; + memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); //-V1086 + furi_hal_crypto_ctr_prep_iv(iv_and_counter); + + /* load key and IV and set the mode to CTR */ + if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_CTR)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* process the input and write to output */ + bool state = furi_hal_crypto_ctr_payload(input, output, length); + + furi_hal_crypto_unload_key(); + + return state; +} + +static void furi_hal_crypto_gcm_prep_iv(uint8_t* iv) { + /* append counter to IV */ + iv[CRYPTO_GCM_IV_LEN] = 0; + iv[CRYPTO_GCM_IV_LEN + 1] = 0; + iv[CRYPTO_GCM_IV_LEN + 2] = 0; + iv[CRYPTO_GCM_IV_LEN + 3] = 2; +} + +static bool furi_hal_crypto_gcm_init(bool decrypt) { + /* GCM init phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_INIT); + if(decrypt) { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT); + } else { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + } + + SET_BIT(AES1->CR, AES_CR_EN); + + if(!wait_for_crypto()) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + + return true; +} + +static bool furi_hal_crypto_gcm_header(const uint8_t* aad, size_t aad_length) { + /* GCM header phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_HEADER); + SET_BIT(AES1->CR, AES_CR_EN); + + size_t last_block_bytes = aad_length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < aad_length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + return true; +} + +static bool furi_hal_crypto_gcm_payload( + const uint8_t* input, + uint8_t* output, + size_t length, + bool decrypt) { + /* GCM payload phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_PAYLOAD); + SET_BIT(AES1->CR, AES_CR_EN); + + size_t last_block_bytes = length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!decrypt) { + MODIFY_REG( + AES1->CR, AES_CR_NPBLB, (CRYPTO_BLK_LEN - last_block_bytes) << AES_CR_NPBLB_Pos); + } + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + return true; +} + +static bool furi_hal_crypto_gcm_finish(size_t aad_length, size_t payload_length, uint8_t* tag) { + /* GCM final phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_FINAL); + + uint32_t last_block[CRYPTO_BLK_LEN / 4]; + memset(last_block, 0, sizeof(last_block)); + last_block[1] = __builtin_bswap32((uint32_t)(aad_length * 8)); + last_block[3] = __builtin_bswap32((uint32_t)(payload_length * 8)); + + if(!furi_hal_crypto_process_block_bswap((uint8_t*)&last_block[0], tag, CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + + return true; +} + +static bool furi_hal_crypto_gcm_compare_tag(const uint8_t* tag1, const uint8_t* tag2) { + uint8_t diff = 0; + + size_t i; + for(i = 0; i < CRYPTO_GCM_TAG_LEN; i++) { + diff |= tag1[i] ^ tag2[i]; + } + + return (diff == 0); +} + +bool furi_hal_crypto_gcm( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag, + bool decrypt) { + /* GCM init phase */ + + /* prepare IV and counter */ + uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN]; + memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); //-V1086 + furi_hal_crypto_gcm_prep_iv(iv_and_counter); + + /* load key and IV and set the mode to CTR */ + if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_GCM)) { + furi_hal_crypto_unload_key(); + return false; + } + + if(!furi_hal_crypto_gcm_init(decrypt)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* GCM header phase */ + + if(aad_length > 0) { + if(!furi_hal_crypto_gcm_header(aad, aad_length)) { + furi_hal_crypto_unload_key(); + return false; + } + } + + /* GCM payload phase */ + + if(!furi_hal_crypto_gcm_payload(input, output, length, decrypt)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* GCM final phase */ + + if(!furi_hal_crypto_gcm_finish(aad_length, length, tag)) { + furi_hal_crypto_unload_key(); + return false; + } + + furi_hal_crypto_unload_key(); + return true; +} + +FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag) { + if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, tag, false)) { + memset(output, 0, length); + memset(tag, 0, CRYPTO_GCM_TAG_LEN); + return FuriHalCryptoGCMStateError; + } + + return FuriHalCryptoGCMStateOk; +} + +FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + const uint8_t* tag) { + uint8_t dtag[CRYPTO_GCM_TAG_LEN]; + + if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, dtag, true)) { + memset(output, 0, length); + return FuriHalCryptoGCMStateError; + } + + if(!furi_hal_crypto_gcm_compare_tag(dtag, tag)) { + memset(output, 0, length); + return FuriHalCryptoGCMStateAuthFailure; + } + + return FuriHalCryptoGCMStateOk; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index a2c9232c0..4908cca69 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -24,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", "2"); + property_value_out(&property_context, NULL, 2, "format", "minor", "3"); } else { property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); - property_value_out(&property_context, NULL, 3, "device", "info", "minor", "3"); + property_value_out(&property_context, NULL, 3, "device", "info", "minor", "4"); } // Model name @@ -283,7 +283,7 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { // Signature verification uint8_t enclave_keys = 0; uint8_t enclave_valid_keys = 0; - bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys); + bool enclave_valid = furi_hal_crypto_enclave_verify(&enclave_keys, &enclave_valid_keys); if(sep == '.') { property_value_out( &property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys); @@ -298,6 +298,7 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { property_value_out(&property_context, NULL, 2, "radio", "alive", "false"); } + // RTC flags property_value_out( &property_context, "%u", @@ -305,8 +306,52 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { "system", "debug", furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)); + property_value_out( + &property_context, "%u", 2, "system", "lock", furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)); + property_value_out( + &property_context, + "%u", + 2, + "system", + "orient", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient)); + property_value_out( + &property_context, + "%u", + 3, + "system", + "sleep", + "legacy", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagLegacySleep)); + property_value_out( + &property_context, + "%u", + 2, + "system", + "stealth", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); + property_value_out( &property_context, "%u", 3, "system", "heap", "track", furi_hal_rtc_get_heap_track_mode()); + property_value_out(&property_context, "%u", 2, "system", "boot", furi_hal_rtc_get_boot_mode()); + property_value_out( + &property_context, + "%u", + 3, + "system", + "locale", + "time", + furi_hal_rtc_get_locale_timeformat()); + property_value_out( + &property_context, + "%u", + 3, + "system", + "locale", + "date", + furi_hal_rtc_get_locale_dateformat()); + property_value_out( + &property_context, "%u", 3, "system", "locale", "unit", furi_hal_rtc_get_locale_units()); property_value_out( &property_context, "%u", 3, "system", "log", "level", furi_hal_rtc_get_log_level()); diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index 2673b8a58..df7c23d56 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -45,7 +45,7 @@ void furi_hal_light_set(Light light, uint8_t value) { } if(light & LightBacklight) { if(XTREME_SETTINGS()->rgb_backlight) { - rgb_backlight_update(value); + rgb_backlight_update(value, false); } else { uint8_t prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index b249c8658..baffde1eb 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -701,7 +701,9 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) { rfalNfcWorker(); state = rfalNfcGetState(); ret = rfalNfcDataExchangeGetStatus(); - if(ret == ERR_BUSY) { + if(ret == ERR_WRONG_STATE) { + return false; + } else if(ret == ERR_BUSY) { if(DWT->CYCCNT - start > timeout_ms * clocks_in_ms) { FURI_LOG_D(TAG, "Timeout during data exchange"); return false; diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h index c87f04a9a..f4051926a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.h +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.h @@ -402,7 +402,6 @@ void furi_hal_nfc_ll_txrx_on(); void furi_hal_nfc_ll_txrx_off(); -// TODO rework all pollers with furi_hal_nfc_ll_txrx_bits FuriHalNfcReturn furi_hal_nfc_ll_txrx( uint8_t* txBuf, uint16_t txBufLen, diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 34b26b831..4d52960d8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -117,19 +117,23 @@ void furi_hal_resources_init_early() { furi_hal_resources_init_input_pins(GpioModeInput); + // Explicit, surviving reset, pulls + LL_PWR_EnablePUPDCfg(); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_9); // gpio_infrared_tx + // SD Card stepdown control furi_hal_gpio_write(&gpio_periph_power, 1); furi_hal_gpio_init(&gpio_periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); // Display pins - furi_hal_gpio_write(&gpio_display_rst_n, 1); + furi_hal_gpio_write(&gpio_display_rst_n, 0); furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); + LL_PWR_EnableGPIOPullUp(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_0); // gpio_display_rst_n + furi_hal_gpio_write(&gpio_display_di, 0); furi_hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); - - // Alternative pull configuration for shutdown - SET_BIT(PWR->PUCRB, DISPLAY_RST_Pin); - CLEAR_BIT(PWR->PDCRB, DISPLAY_RST_Pin); - SET_BIT(PWR->CR3, PWR_CR3_APC); + LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_1); // gpio_display_di // Hard reset USB furi_hal_gpio_write(&gpio_usb_dm, 1); @@ -171,19 +175,6 @@ void furi_hal_resources_init() { // Button pins furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); - // Explicit, surviving reset, pulls - LL_PWR_EnablePUPDCfg(); - LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_9); // gpio_infrared_tx - LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_B, LL_PWR_GPIO_BIT_8); // gpio_speaker - LL_PWR_EnableGPIOPullDown(LL_PWR_GPIO_A, LL_PWR_GPIO_BIT_8); // gpio_vibro - - // Display pins - furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_display_rst_n, 0); - - furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_display_di, 0); - // SD pins furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_gpio_write(&gpio_sdcard_cd, 0); diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c index 757ac2366..5c8d2f892 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c @@ -350,6 +350,15 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { .cs = &gpio_ext_pa4, }; +FuriHalSpiBusHandle furi_hal_spi_bus_handle_external_extra = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_external_event_callback, + .miso = &gpio_ext_pa6, + .mosi = &gpio_ext_pa7, + .sck = &gpio_ext_pb3, + .cs = &gpio_ext_pc3, +}; + inline static void furi_hal_spi_bus_d_handle_event_callback( FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event, diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.h b/firmware/targets/f7/furi_hal/furi_hal_spi_config.h index eab633a19..b2cf679cd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.h +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.h @@ -47,6 +47,20 @@ extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc; */ extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_external; +/** External on `furi_hal_spi_bus_r` + * Preset: `furi_hal_spi_preset_1edge_low_2m` + * + * miso: pa6 + * mosi: pa7 + * sck: pb3 + * cs: pc3 (software controlled) + * + * @warning not initialized by default, call `furi_hal_spi_bus_handle_init` to initialize + * Bus pins are floating on inactive state, CS high after initialization + * + */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_external_extra; + /** ST7567(Display) on `furi_hal_spi_bus_d` */ extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_display; diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index cef6bb2fb..007e29bd6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -53,7 +53,7 @@ typedef struct { const GpioPin* async_mirror_pin; uint8_t rolling_counter_mult; - bool timestamp_file_names : 1; + bool ext_power_amp : 1; bool extended_frequency_i : 1; } FuriHalSubGhz; @@ -62,6 +62,7 @@ volatile FuriHalSubGhz furi_hal_subghz = { .regulation = SubGhzRegulationTxRx, .async_mirror_pin = NULL, .rolling_counter_mult = 1, + .ext_power_amp = false, .extended_frequency_i = false, }; @@ -77,6 +78,14 @@ void furi_hal_subghz_set_extended_frequency(bool state_i) { furi_hal_subghz.extended_frequency_i = state_i; } +void furi_hal_subghz_set_ext_power_amp(bool enabled) { + furi_hal_subghz.ext_power_amp = enabled; +} + +bool furi_hal_subghz_get_ext_power_amp() { + return furi_hal_subghz.ext_power_amp; +} + void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } @@ -225,7 +234,7 @@ bool furi_hal_subghz_rx_pipe_not_empty() { cc1101_read_reg( &furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - // TODO: you can add a buffer overflow flag if needed + // TODO: Find reason why RXFIFO_OVERFLOW doesnt work correctly if(status->NUM_RXBYTES > 0) { return true; } else { diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index c7249e1a6..b390ac6cc 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -282,6 +282,11 @@ void furi_hal_subghz_stop_async_tx(); // */ // void furi_hal_subghz_select_radio_type(SubGhzRadioType state); +// External CC1101 Ebytes power amplifier control +void furi_hal_subghz_set_ext_power_amp(bool enabled); + +bool furi_hal_subghz_get_ext_power_amp(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/src/update.c b/firmware/targets/f7/src/update.c index c6235a150..520305410 100644 --- a/firmware/targets/f7/src/update.c +++ b/firmware/targets/f7/src/update.c @@ -38,7 +38,7 @@ static bool flipper_update_mount_sd() { } static bool flipper_update_init() { - // TODO: Configure missing peripherals properly + // TODO FL-3504: Configure missing peripherals properly furi_hal_bus_enable(FuriHalBusHSEM); furi_hal_bus_enable(FuriHalBusIPCC); furi_hal_bus_enable(FuriHalBusRNG); diff --git a/firmware/targets/furi_hal_include/furi_hal_crypto.h b/firmware/targets/furi_hal_include/furi_hal_crypto.h index 81788e96e..5e3b41040 100644 --- a/firmware/targets/furi_hal_include/furi_hal_crypto.h +++ b/firmware/targets/furi_hal_include/furi_hal_crypto.h @@ -1,6 +1,40 @@ /** * @file furi_hal_crypto.h + * * Cryptography HAL API + * + * !!! READ THIS FIRST !!! + * + * Flipper was never designed to be secure, nor it passed cryptography audit. + * Despite of the fact that keys are stored in secure enclave there are some + * types of attack that can be performed against AES engine to recover + * keys(theoretical). Also there is no way to securely deliver user keys to + * device and never will be. In addition device is fully open and there is no + * way to guarantee safety of your data, it can be easily dumped with debugger + * or modified code. + * + * Secure enclave on WB series is implemented on core2 FUS side and can be used + * only if core2 alive. Enclave is responsible for storing, loading and + * unloading keys to and from enclave/AES in secure manner(AES engine key + * registers will be locked when key from enclave loaded) + * + * There are 11 keys that we provision at factory: + * - 0 - Master key for secure key delivery. Impossible to use for anything but + * key provisioning. We don't plan to use it too. + * - 1 - 10 - Keys used by firmware. All devices got the same set of keys. You + * also can use them in your applications. + * + * Also there is a slot 11 that we use for device unique key. This slot is + * intentionally left blank till the moment of first use, so you can ensure that + * we don't know your unique key. Also you can provision this key by your self + * with crypto cli or API. + * + * Other slots can be used for your needs. But since enclave is sequential + * append only, we can not guarantee you that slots you want are free. NEVER USE + * THEM FOR PUBLIC APPLICATIONS. + * + * Also you can directly load raw keys into AES engine and use it for your + * needs. */ #pragma once @@ -12,10 +46,27 @@ extern "C" { #endif +/** Factory provisioned master key slot. Should never be used. */ +#define FURI_HAL_CRYPTO_ENCLAVE_MASTER_KEY_SLOT (0u) + +/** Factory provisioned keys slot range. All of them are exactly same on all flippers. */ +#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_START (1u) +#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END (10u) + +/** Device unique key slot. This key generated on first use or provisioned by user. Use furi_hal_crypto_enclave_ensure_key before using this slot. */ +#define FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT (11u) + +/** User key slot range. This slots can be used for your needs, but never use them in public apps. */ +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START (12u) +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END (100u) + +/** [Deprecated] Indicates availability of advanced crypto functions, will be dropped before v1.0 */ +#define FURI_HAL_CRYPTO_ADVANCED_AVAIL 1 + /** FuriHalCryptoKey Type */ typedef enum { FuriHalCryptoKeyTypeMaster, /**< Master key */ - FuriHalCryptoKeyTypeSimple, /**< Simple enencrypted key */ + FuriHalCryptoKeyTypeSimple, /**< Simple unencrypted key */ FuriHalCryptoKeyTypeEncrypted, /**< Encrypted with Master key */ } FuriHalCryptoKeyType; @@ -32,40 +83,89 @@ typedef struct { uint8_t* data; } FuriHalCryptoKey; -/** Initialize cryptography layer This includes AES engines, PKA and RNG - */ +/** FuriHalCryptoGCMState Result of a GCM operation */ +typedef enum { + FuriHalCryptoGCMStateOk, /**< operation successful */ + FuriHalCryptoGCMStateError, /**< error during encryption/decryption */ + FuriHalCryptoGCMStateAuthFailure, /**< tags do not match, auth failed */ +} FuriHalCryptoGCMState; + +/** Initialize cryptography layer(includes AES engines, PKA and RNG) */ void furi_hal_crypto_init(); -bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb); - -bool furi_hal_crypto_verify_key(uint8_t key_slot); - -/** Store key in crypto storage +/** Verify factory provisioned keys * - * @param key FuriHalCryptoKey to store. Only Master, Simple or - * Encrypted - * @param slot pinter to int where store slot number will be saved + * @param keys_nb The keys number of + * @param valid_keys_nb The valid keys number of + * + * @return true if all enclave keys are intact, false otherwise + */ +bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb); + +/** Ensure that requested slot and slots before this slot contains keys. + * + * This function is used to provision FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT. Also you + * may want to use it to generate some unique keys in user key slot range. + * + * @warning Because of the sequential nature of the secure enclave this + * method will generate key for all slots from + * FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END to the slot your requested. + * Keys are generated using on-chip RNG. + * + * @param[in] key_slot The key slot to enclave + * + * @return true if key exists or created, false if enclave corrupted + */ +bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot); + +/** Store key in crypto enclave + * + * @param key FuriHalCryptoKey to be stored + * @param slot pointer to int where enclave slot will be stored * * @return true on success */ -bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot); +bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot); -/** Init AES engine and load key from crypto store +/** Init AES engine and load key from crypto enclave * - * @param slot store slot number + * @warning Use only with furi_hal_crypto_enclave_unload_key() + * + * @param slot enclave slot * @param[in] iv pointer to 16 bytes Initialization Vector data * * @return true on success */ -bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv); +bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv); -/** Unload key engine and deinit AES engine +/** Unload key and deinit AES engine * - * @param slot store slot number + * @warning Use only with furi_hal_crypto_enclave_load_key() + * + * @param slot enclave slot * * @return true on success */ -bool furi_hal_crypto_store_unload_key(uint8_t slot); +bool furi_hal_crypto_enclave_unload_key(uint8_t slot); + +/** Init AES engine and load supplied key + * + * @warning Use only with furi_hal_crypto_unload_key() + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 16 bytes Initialization Vector data + * + * @return true on success + */ +bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv); + +/** Unload key and de-init AES engine + * + * @warning Use this function only with furi_hal_crypto_load_key() + * + * @return true on success + */ +bool furi_hal_crypto_unload_key(void); /** Encrypt data * @@ -87,6 +187,109 @@ bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) */ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size); +/** Encrypt the input using AES-CTR + * + * Decryption can be performed by supplying the ciphertext as input. Inits and + * deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * + * @return true on success + */ +bool furi_hal_crypto_ctr( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* input, + uint8_t* output, + size_t length); + +/** Encrypt/decrypt the input using AES-GCM + * + * When decrypting the tag generated needs to be compared to the tag attached to + * the ciphertext in a constant-time fashion. If the tags are not equal, the + * decryption failed and the plaintext returned needs to be discarded. Inits and + * deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes space for the tag + * @param decrypt true for decryption, false otherwise + * + * @return true on success + */ +bool furi_hal_crypto_gcm( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag, + bool decrypt); + +/** Encrypt the input using AES-GCM and generate a tag + * + * Inits and deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes space for the tag + * + * @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on + * failure + */ +FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag); + +/** Decrypt the input using AES-GCM and verify the provided tag + * + * Inits and deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes tag + * + * @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on + * failure, FuriHalCryptoGCMStateAuthFailure if the tag does not + * match + */ +FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + const uint8_t* tag); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index 95bb61db7..462f6cf7c 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -34,6 +34,7 @@ typedef enum { FuriHalVersionColorUnknown = 0x00, FuriHalVersionColorBlack = 0x01, FuriHalVersionColorWhite = 0x02, + FuriHalVersionColorTransparent = 0x03, } FuriHalVersionColor; /** Device Regions */ diff --git a/furi/core/thread.c b/furi/core/thread.c index 62ab6fec4..4139f2ebc 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -60,10 +60,10 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); - furi_log_print_format( //-V576 + furi_log_print_format( thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, TAG, - "%s allocation balance: %u", + "%s allocation balance: %zu", thread->name ? thread->name : "Thread", thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); diff --git a/furi/core/thread.h b/furi/core/thread.h index 022894ee8..692f2a100 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -28,7 +28,7 @@ typedef enum { FuriThreadPriorityNormal = 16, /**< Normal */ FuriThreadPriorityHigh = 17, /**< High */ FuriThreadPriorityHighest = 18, /**< Highest */ - FuriThreadPriorityIsr = 32, /**< Deffered Isr (highest possible) */ + FuriThreadPriorityIsr = (configMAX_PRIORITIES - 1), /**< Deferred ISR (highest possible) */ } FuriThreadPriority; /** FuriThread anonymous structure */ diff --git a/lib/SConscript b/lib/SConscript index f3d96a55c..ef1729891 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -14,6 +14,7 @@ env.Append( Dir("toolbox"), Dir("u8g2"), Dir("update_util"), + Dir("xtreme"), Dir("print"), Dir("music_worker"), ], @@ -23,7 +24,7 @@ env.Append( env.Append( CPPPATH=[ "#/", - "#/lib", # TODO: remove! + "#/lib", # TODO FL-3553: remove! "#/lib/mlib", # Ugly hack Dir("../assets/compiled"), diff --git a/lib/drivers/SK6805.c b/lib/drivers/SK6805.c index 572e1df97..e64ecba32 100644 --- a/lib/drivers/SK6805.c +++ b/lib/drivers/SK6805.c @@ -19,9 +19,10 @@ #include "SK6805.h" #include +#define TAG "SK6805" + /* ÐаÑтройки */ -#define SK6805_LED_COUNT 3 //КоличеÑтво Ñветодиодов на плате подÑветки -#define SK6805_LED_PIN &led_pin //Порт Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñветодиодов +#define SK6805_LED_PIN &led_pin // LED connection port #ifdef FURI_DEBUG #define DEBUG_PIN &gpio_ext_pa7 @@ -53,17 +54,18 @@ void SK6805_set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b) { led_buffer[led_index][0] = g; led_buffer[led_index][1] = r; led_buffer[led_index][2] = b; + FURI_LOG_T(TAG, "led: %d, r: %d, g: %d, b: %d", led_index, r, g, b); } void SK6805_update(void) { SK6805_init(); furi_kernel_lock(); uint32_t end; - /* ПоÑÐ»ÐµÐ´Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ° цветов Ñветодиодов */ + // Sequential sending LEDs for(uint8_t lednumber = 0; lednumber < SK6805_LED_COUNT; lednumber++) { - //ПоÑÐ»ÐµÐ´Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ° цветов Ñветодиода + // Sequential sending colors for(uint8_t color = 0; color < 3; color++) { - //ПоÑÐ»ÐµÐ´Ð¾Ð²Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ° битов цвета + // Sequentially sending color bits uint8_t i = 0b10000000; while(i != 0) { if(led_buffer[lednumber][color] & (i)) { diff --git a/lib/drivers/SK6805.h b/lib/drivers/SK6805.h index 7c58956fa..ed0dcdb36 100644 --- a/lib/drivers/SK6805.h +++ b/lib/drivers/SK6805.h @@ -21,6 +21,8 @@ #include +#define SK6805_LED_COUNT 3 //КоличеÑтво Ñветодиодов на плате подÑветки + /** * @brief Ð˜Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð»Ð¸Ð½Ð¸Ð¸ ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñветкой */ @@ -48,4 +50,4 @@ void SK6805_set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b); */ void SK6805_update(void); -#endif /* SK6805_H_ */ \ No newline at end of file +#endif /* SK6805_H_ */ diff --git a/lib/drivers/bq27220.c b/lib/drivers/bq27220.c index 92dbfcd6a..4a9feed9b 100644 --- a/lib/drivers/bq27220.c +++ b/lib/drivers/bq27220.c @@ -69,7 +69,7 @@ static bool bq27220_parameter_check( uint8_t checksum = bq27220_get_checksum(buffer, size + 2); buffer[0] = checksum; - buffer[1] = 4 + size; // TODO: why 4? + buffer[1] = 4 + size; // TODO FL-3519: why 4? if(!furi_hal_i2c_write_mem( handle, BQ27220_ADDRESS, CommandMACDataSum, buffer, 2, BQ27220_I2C_TIMEOUT)) { FURI_LOG_I(TAG, "CRC write failed"); diff --git a/lib/drivers/rgb_backlight.c b/lib/drivers/rgb_backlight.c index ec39efb0b..5a19ce0ca 100644 --- a/lib/drivers/rgb_backlight.c +++ b/lib/drivers/rgb_backlight.c @@ -1,6 +1,7 @@ /* RGB backlight FlipperZero driver Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Heavily modified by Willy-JL and Z3bro 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 @@ -19,156 +20,243 @@ #include "rgb_backlight.h" #include #include +#include -#define RGB_BACKLIGHT_SETTINGS_VERSION 5 +#define RGB_BACKLIGHT_SETTINGS_MAGIC 0x15 +#define RGB_BACKLIGHT_SETTINGS_VERSION 6 #define RGB_BACKLIGHT_SETTINGS_PATH CFG_PATH("rgb_backlight.settings") -#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, 69, 0}, - {"Red", 255, 0, 0}, - {"Maroon", 128, 0, 0}, - {"Yellow", 255, 255, 0}, - {"Olive", 128, 128, 0}, - {"Lime", 0, 255, 0}, - {"Green", 0, 128, 0}, - {"Aqua", 0, 255, 127}, - {"Cyan", 0, 210, 210}, - {"Azure", 0, 127, 255}, - {"Teal", 0, 128, 128}, - {"Blue", 0, 0, 255}, - {"Navy", 0, 0, 128}, - {"Purple", 128, 0, 128}, - {"Fuchsia", 255, 0, 255}, - {"Pink", 173, 31, 173}, - {"Brown", 165, 42, 42}, - {"White", 255, 192, 203}, +static struct { + RgbColor colors[SK6805_LED_COUNT]; + RGBBacklightRainbowMode rainbow_mode; + uint8_t rainbow_speed; + uint32_t rainbow_interval; + uint32_t rainbow_saturation; +} rgb_settings = { + .colors = + { + {255, 69, 0}, + {255, 69, 0}, + {255, 69, 0}, + }, + .rainbow_mode = RGBBacklightRainbowModeOff, + .rainbow_speed = 5, + .rainbow_interval = 250, + .rainbow_saturation = 255, }; -uint8_t rgb_backlight_get_color_count(void) { - return COLOR_COUNT; +static struct { + bool settings_loaded; + bool enabled; + bool last_rainbow; + uint8_t last_brightness; + RgbColor last_colors[SK6805_LED_COUNT]; + FuriTimer* rainbow_timer; + HsvColor rainbow_hsv; +} rgb_state = { + .settings_loaded = false, + .enabled = false, + .last_rainbow = true, + .last_brightness = 0, + .last_colors = + { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + }, + .rainbow_timer = NULL, + .rainbow_hsv = {0, 255, 255}, +}; + +static void rainbow_timer(void* ctx) { + UNUSED(ctx); + rgb_backlight_update(rgb_state.last_brightness, true); } -const char* rgb_backlight_get_color_text(uint8_t index) { - return colors[index].name; +void rgb_backlight_reconfigure(bool enabled) { + if(enabled && !rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + rgb_state.enabled = enabled; + + if(rgb_state.enabled && rgb_settings.rainbow_mode != RGBBacklightRainbowModeOff) { + if(rgb_state.rainbow_timer == NULL) { + rgb_state.rainbow_timer = furi_timer_alloc(rainbow_timer, FuriTimerTypePeriodic, NULL); + } else { + furi_timer_stop(rgb_state.rainbow_timer); + } + furi_timer_start(rgb_state.rainbow_timer, rgb_settings.rainbow_interval); + } else if(rgb_state.rainbow_timer != NULL) { + furi_timer_stop(rgb_state.rainbow_timer); + furi_timer_free(rgb_state.rainbow_timer); + rgb_state.rainbow_timer = NULL; + } + rgb_state.rainbow_hsv.s = rgb_settings.rainbow_saturation; + + rgb_backlight_update(rgb_state.last_brightness, false); } void rgb_backlight_load_settings(void) { - //Ðе загружать данные из внутренней памÑти при загрузке в режиме DFU - if(!furi_hal_is_normal_boot()) { - rgb_settings.settings_is_loaded = true; + // Do not load data from internal memory when booting in DFU mode + if(!furi_hal_is_normal_boot() || rgb_state.settings_loaded) { + rgb_state.settings_loaded = true; return; } - RGBBacklightSettings settings; - File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - const size_t settings_size = sizeof(RGBBacklightSettings); + saved_struct_load( + RGB_BACKLIGHT_SETTINGS_PATH, + &rgb_settings, + sizeof(rgb_settings), + RGB_BACKLIGHT_SETTINGS_MAGIC, + RGB_BACKLIGHT_SETTINGS_VERSION); - 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; + rgb_state.settings_loaded = true; + rgb_backlight_reconfigure(rgb_state.enabled); } 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); + saved_struct_save( + RGB_BACKLIGHT_SETTINGS_PATH, + &rgb_settings, + sizeof(rgb_settings), + RGB_BACKLIGHT_SETTINGS_MAGIC, + RGB_BACKLIGHT_SETTINGS_VERSION); +} - FURI_LOG_I(TAG, "saving settings to \"%s\"", RGB_BACKLIGHT_SETTINGS_PATH); +void rgb_backlight_set_color(uint8_t index, RgbColor color) { + if(index >= COUNT_OF(rgb_settings.colors)) return; + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + rgb_settings.colors[index] = color; + rgb_backlight_reconfigure(rgb_state.enabled); +} - memcpy(&settings, &rgb_settings, settings_size); +RgbColor rgb_backlight_get_color(uint8_t index) { + if(index >= COUNT_OF(rgb_settings.colors)) return (RgbColor){0, 0, 0}; + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.colors[index]; +} - bool fs_result = - storage_file_open(file, RGB_BACKLIGHT_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS); +void rgb_backlight_set_rainbow_mode(RGBBacklightRainbowMode rainbow_mode) { + if(rainbow_mode >= RGBBacklightRainbowModeCount) return; + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + rgb_settings.rainbow_mode = rainbow_mode; + rgb_backlight_reconfigure(rgb_state.enabled); +} - if(fs_result) { - uint16_t bytes_count = storage_file_write(file, &settings, settings_size); +RGBBacklightRainbowMode rgb_backlight_get_rainbow_mode() { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.rainbow_mode; +} - if(bytes_count != settings_size) { - fs_result = false; +void rgb_backlight_set_rainbow_speed(uint8_t rainbow_speed) { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + rgb_settings.rainbow_speed = rainbow_speed; +} + +uint8_t rgb_backlight_get_rainbow_speed() { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.rainbow_speed; +} + +void rgb_backlight_set_rainbow_interval(uint32_t rainbow_interval) { + if(rainbow_interval < 100) return; + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + rgb_settings.rainbow_interval = rainbow_interval; + rgb_backlight_reconfigure(rgb_state.enabled); +} + +uint32_t rgb_backlight_get_rainbow_interval() { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.rainbow_interval; +} + +void rgb_backlight_set_rainbow_saturation(uint8_t rainbow_saturation) { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + rgb_settings.rainbow_saturation = rainbow_saturation; + rgb_backlight_reconfigure(rgb_state.enabled); +} + +uint8_t rgb_backlight_get_rainbow_saturation() { + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + return rgb_settings.rainbow_saturation; +} + +void rgb_backlight_update(uint8_t brightness, bool tick) { + if(!rgb_state.enabled) return; + if(!rgb_state.settings_loaded) { + rgb_backlight_load_settings(); + } + + switch(rgb_settings.rainbow_mode) { + case RGBBacklightRainbowModeOff: { + if(!rgb_state.last_rainbow && rgb_state.last_brightness == brightness && + memcmp(rgb_state.last_colors, rgb_settings.colors, sizeof(rgb_settings.colors)) == 0) { + return; } + rgb_state.last_rainbow = false; + memcpy(rgb_state.last_colors, rgb_settings.colors, sizeof(rgb_settings.colors)); + + float bright = brightness / 255.0f; + for(uint8_t i = 0; i < SK6805_get_led_count(); i++) { + SK6805_set_led_color( + i, + rgb_settings.colors[i].r * bright, + rgb_settings.colors[i].g * bright, + rgb_settings.colors[i].b * bright); + } + break; } - if(fs_result) { - FURI_LOG_I(TAG, "save success"); - } else { - FURI_LOG_E(TAG, "save failed, %s", storage_file_get_error_desc(file)); + case RGBBacklightRainbowModeWave: + case RGBBacklightRainbowModeSolid: { + rgb_state.last_rainbow = true; + + if(tick && brightness) { + rgb_state.rainbow_hsv.h += rgb_settings.rainbow_speed; + } else { + if(rgb_state.last_brightness == brightness && rgb_state.last_rainbow) { + return; + } + rgb_state.rainbow_hsv.v = brightness; + } + + HsvColor hsv = rgb_state.rainbow_hsv; + RgbColor rgb = hsv2rgb(hsv); + + for(uint8_t i = 0; i < SK6805_get_led_count(); i++) { + if(i && rgb_settings.rainbow_mode == RGBBacklightRainbowModeWave) { + hsv.h += (50 * i); + rgb = hsv2rgb(hsv); + } + SK6805_set_led_color(i, rgb.r, rgb.g, rgb.b); + } + break; } - 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) + default: 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); } + rgb_state.last_brightness = brightness; SK6805_update(); } diff --git a/lib/drivers/rgb_backlight.h b/lib/drivers/rgb_backlight.h index c00eb0716..b289520fd 100644 --- a/lib/drivers/rgb_backlight.h +++ b/lib/drivers/rgb_backlight.h @@ -1,6 +1,7 @@ /* RGB backlight FlipperZero driver Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Heavily modified by Willy-JL and Z3bro 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 @@ -18,69 +19,89 @@ #include #include "SK6805.h" +#include #ifdef __cplusplus extern "C" { #endif -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; +typedef enum { + RGBBacklightRainbowModeOff, + RGBBacklightRainbowModeWave, + RGBBacklightRainbowModeSolid, + RGBBacklightRainbowModeCount, +} RGBBacklightRainbowMode; /** - * @brief Получить текущие наÑтройки RGB-подÑветки + * @brief Reconfigure rgb backlight with new settings * - * @return Указатель на Ñтруктуру наÑтроек + * @param enabled Whether the rgb backlight is enabled */ -RGBBacklightSettings* rgb_backlight_get_settings(void); +void rgb_backlight_reconfigure(bool enabled); /** - * @brief Загрузить наÑтройки подÑветки Ñ SD-карты + * @brief Load backlight settings from SD card */ -void rgb_backlight_load_settings(void); +void rgb_backlight_load_settings(); /** - * @brief Сохранить текущие наÑтройки RGB-подÑветки + * @brief Save Current RGB Lighting Settings */ -void rgb_backlight_save_settings(void); +void rgb_backlight_save_settings(); /** - * @brief Применить текущие наÑтройки RGB-подÑветки + * @brief Change the color of the backlight * - * @param brightness ЯркоÑть ÑÐ²ÐµÑ‡ÐµÐ½Ð¸Ñ (0-255) + * @param index What led to set the color to (0 - SK6805_LED_COUNT-1) + * @param color RGB color to use */ -void rgb_backlight_update(uint8_t brightness); +void rgb_backlight_set_color(uint8_t index, RgbColor color); + +RgbColor rgb_backlight_get_color(uint8_t index); /** - * @brief УÑтановить цвет RGB-подÑветки + * @brief Change rainbow mode * - * @param color_index Ð˜Ð½Ð´ÐµÐºÑ Ñ†Ð²ÐµÑ‚Ð° (0 - rgb_backlight_get_color_count()) + * @param rainbow_mode What mode to use (0 - RGBBacklightRainbowModeCount) */ -void rgb_backlight_set_color(uint8_t color_index); +void rgb_backlight_set_rainbow_mode(RGBBacklightRainbowMode rainbow_mode); + +RGBBacklightRainbowMode rgb_backlight_get_rainbow_mode(); /** - * @brief Получить количеÑтво доÑтупных цветов + * @brief Change rainbow speed * - * @return ЧиÑло доÑтупных вариантов цвета + * @param rainbow_speed What speed to use (0 - 255) */ -uint8_t rgb_backlight_get_color_count(void); +void rgb_backlight_set_rainbow_speed(uint8_t rainbow_speed); + +uint8_t rgb_backlight_get_rainbow_speed(); /** - * @brief Получить текÑтовое название цвета + * @brief Change rainbow interval * - * @param index Ð˜Ð½Ð´ÐµÐºÑ Ð¸Ð· доÑтупных вариантов цвета - * @return Указатель на Ñтроку Ñ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸ÐµÐ¼ цвета + * @param rainbow_interval What interval to use */ -const char* rgb_backlight_get_color_text(uint8_t index); +void rgb_backlight_set_rainbow_interval(uint32_t rainbow_interval); + +uint32_t rgb_backlight_get_rainbow_interval(); + +/** + * @brief Change rainbow saturation + * + * @param rainbow_saturation What saturation to use (0 - 255) + */ +void rgb_backlight_set_rainbow_saturation(uint8_t rainbow_saturation); + +uint8_t rgb_backlight_get_rainbow_saturation(); + +/** + * @brief Apply current RGB lighting settings + * + * @param brightness Backlight intensity (0-255) + * @param tick Whether this update was a tick (for rainbow) + */ +void rgb_backlight_update(uint8_t brightness, bool tick); #ifdef __cplusplus } diff --git a/lib/fatfs/ff.c b/lib/fatfs/ff.c index d39089578..2ce5f8b15 100644 --- a/lib/fatfs/ff.c +++ b/lib/fatfs/ff.c @@ -5371,7 +5371,7 @@ FRESULT f_mkfs ( /* Pre-determine the FAT type */ do { if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ - if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x2000000 || au > 128) { /* exFAT only, vol >= 32Ms or au > 128s ? */ fmt = FS_EXFAT; break; } } diff --git a/lib/flipper_application/application_manifest.h b/lib/flipper_application/application_manifest.h index 25e4f8d0a..d09ec9004 100644 --- a/lib/flipper_application/application_manifest.h +++ b/lib/flipper_application/application_manifest.h @@ -16,7 +16,7 @@ extern "C" { #define FAP_MANIFEST_SUPPORTED_VERSION 1 #define FAP_MANIFEST_MAX_APP_NAME_LENGTH 32 -#define FAP_MANIFEST_MAX_ICON_SIZE 32 // TODO: reduce size? +#define FAP_MANIFEST_MAX_ICON_SIZE 32 // TODO FL-3524: reduce size? #pragma pack(push, 1) diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index fc9dd06ba..bea7c1231 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -1,3 +1,4 @@ +#include "storage/storage.h" #include #include "elf_file.h" #include "elf_file_i.h" @@ -57,6 +58,13 @@ static void address_cache_put(AddressCache_t cache, int symEntry, Elf32_Addr sym /********************************************** ELF ***********************************************/ /**************************************************************************************************/ +static void elf_file_maybe_release_fd(ELFFile* elf) { + if(elf->fd) { + storage_file_free(elf->fd); + elf->fd = NULL; + } +} + static ELFSection* elf_file_get_section(ELFFile* elf, const char* name) { return ELFSectionDict_get(elf->sections, name); } @@ -507,7 +515,7 @@ static SectionType elf_preload_section( #endif // ignore .ARM and .rel.ARM sections - // TODO: how to do it not by name? + // TODO FL-3525: 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.") || @@ -764,7 +772,7 @@ void elf_file_free(ELFFile* elf) { free(elf->debug_link_info.debug_link); } - storage_file_free(elf->fd); + elf_file_maybe_release_fd(elf); free(elf); } @@ -792,7 +800,7 @@ bool elf_file_load_section_table(ELFFile* elf) { FuriString* name = furi_string_alloc(); FURI_LOG_D(TAG, "Scan ELF indexs..."); - // TODO: why we start from 1? + // TODO FL-3526: why we start from 1? for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) { Elf32_Shdr section_header; @@ -828,7 +836,7 @@ ElfProcessSectionResult elf_process_section( Elf32_Shdr section_header; // find section - // TODO: why we start from 1? + // TODO FL-3526: why we start from 1? for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) { furi_string_reset(section_name); if(!elf_read_section(elf, section_idx, §ion_header, section_name)) { @@ -855,6 +863,7 @@ ElfProcessSectionResult elf_process_section( } ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { + furi_check(elf->fd != NULL); ELFFileLoadStatus status = ELFFileLoadStatusSuccess; ELFSectionDict_it_t it; @@ -892,9 +901,10 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); total_size += itref->value.size; } - FURI_LOG_I(TAG, "Total size of loaded sections: %u", total_size); //-V576 + FURI_LOG_I(TAG, "Total size of loaded sections: %zu", total_size); } + elf_file_maybe_release_fd(elf); return status; } diff --git a/lib/ibutton/ibutton_worker_modes.c b/lib/ibutton/ibutton_worker_modes.c index 1b8e0a3b8..83e207de9 100644 --- a/lib/ibutton/ibutton_worker_modes.c +++ b/lib/ibutton/ibutton_worker_modes.c @@ -127,7 +127,7 @@ void ibutton_worker_mode_write_blank_tick(iButtonWorker* worker) { furi_assert(worker->key); const bool success = ibutton_protocols_write_blank(worker->protocols, worker->key); - // TODO: pass a proper result to the callback + // TODO FL-3527: pass a proper result to the callback const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK : iButtonWorkerWriteNoDetect; if(worker->write_cb != NULL) { @@ -139,7 +139,7 @@ void ibutton_worker_mode_write_copy_tick(iButtonWorker* worker) { furi_assert(worker->key); const bool success = ibutton_protocols_write_copy(worker->protocols, worker->key); - // TODO: pass a proper result to the callback + // TODO FL-3527: pass a proper result to the callback const iButtonWorkerWriteResult result = success ? iButtonWorkerWriteOK : iButtonWorkerWriteNoDetect; if(worker->write_cb != NULL) { diff --git a/lib/ibutton/protocols/blanks/rw1990.c b/lib/ibutton/protocols/blanks/rw1990.c index d3350fcf0..f86e43d99 100644 --- a/lib/ibutton/protocols/blanks/rw1990.c +++ b/lib/ibutton/protocols/blanks/rw1990.c @@ -62,7 +62,7 @@ bool rw1990_write_v1(OneWireHost* host, const uint8_t* data, size_t data_size) { onewire_host_write_bit(host, true); furi_delay_us(10000); - // TODO: Better error handling + // TODO FL-3528: Better error handling return rw1990_read_and_compare(host, data, data_size); } @@ -90,6 +90,6 @@ bool rw1990_write_v2(OneWireHost* host, const uint8_t* data, size_t data_size) { onewire_host_write_bit(host, false); furi_delay_us(10000); - // TODO: Better error handling + // TODO Fl-3528: Better error handling return rw1990_read_and_compare(host, data, data_size); } diff --git a/lib/ibutton/protocols/blanks/tm2004.c b/lib/ibutton/protocols/blanks/tm2004.c index ef6f0619e..b020a218d 100644 --- a/lib/ibutton/protocols/blanks/tm2004.c +++ b/lib/ibutton/protocols/blanks/tm2004.c @@ -21,7 +21,7 @@ bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size) { onewire_host_write(host, data[i]); answer = onewire_host_read(host); - // TODO: check answer CRC + // TODO FL-3529: check answer CRC // pulse indicating that data is correct furi_delay_us(600); @@ -37,6 +37,6 @@ bool tm2004_write(OneWireHost* host, const uint8_t* data, size_t data_size) { } } - // TODO: Better error handling + // TODO FL-3529: Better error handling return i == data_size; } diff --git a/lib/ibutton/protocols/dallas/dallas_common.c b/lib/ibutton/protocols/dallas/dallas_common.c index ebf57e555..6e99a3be2 100644 --- a/lib/ibutton/protocols/dallas/dallas_common.c +++ b/lib/ibutton/protocols/dallas/dallas_common.c @@ -149,7 +149,7 @@ bool dallas_common_emulate_search_rom(OneWireSlave* bus, const DallasCommonRomDa if(!onewire_slave_send_bit(bus, !bit)) return false; onewire_slave_receive_bit(bus); - // TODO: check for errors and return if any + // TODO FL-3530: check for errors and return if any } } diff --git a/lib/ibutton/protocols/dallas/protocol_ds1971.c b/lib/ibutton/protocols/dallas/protocol_ds1971.c index a806acb22..b65e64584 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds1971.c +++ b/lib/ibutton/protocols/dallas/protocol_ds1971.c @@ -53,7 +53,7 @@ const iButtonProtocolDallasBase ibutton_protocol_ds1971 = { .name = DS1971_FAMILY_NAME, .read = dallas_ds1971_read, - .write_blank = NULL, // TODO: Implement writing to blank + .write_blank = NULL, // TODO FL-3531: Implement writing to blank .write_copy = dallas_ds1971_write_copy, .emulate = dallas_ds1971_emulate, .save = dallas_ds1971_save, diff --git a/lib/ibutton/protocols/dallas/protocol_ds1992.c b/lib/ibutton/protocols/dallas/protocol_ds1992.c index 0b4d4b34f..7440882ea 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds1992.c +++ b/lib/ibutton/protocols/dallas/protocol_ds1992.c @@ -73,7 +73,7 @@ bool dallas_ds1992_read(OneWireHost* host, iButtonProtocolData* protocol_data) { bool dallas_ds1992_write_blank(OneWireHost* host, iButtonProtocolData* protocol_data) { DS1992ProtocolData* data = protocol_data; - // TODO: Make this work, currently broken + // TODO FL-3532: Make this work, currently broken return tm2004_write(host, (uint8_t*)data, sizeof(DallasCommonRomData) + DS1992_SRAM_DATA_SIZE); } diff --git a/lib/ibutton/protocols/dallas/protocol_ds1996.c b/lib/ibutton/protocols/dallas/protocol_ds1996.c index 5358b63e2..5970a67bb 100644 --- a/lib/ibutton/protocols/dallas/protocol_ds1996.c +++ b/lib/ibutton/protocols/dallas/protocol_ds1996.c @@ -159,7 +159,7 @@ static bool dallas_ds1996_command_callback(uint8_t command, void* context) { case DALLAS_COMMON_CMD_MATCH_ROM: case DALLAS_COMMON_CMD_OVERDRIVE_MATCH_ROM: - /* TODO: Match ROM command support */ + /* TODO FL-3533: Match ROM command support */ default: return false; } diff --git a/lib/lfrfid/lfrfid_dict_file.c b/lib/lfrfid/lfrfid_dict_file.c index 7ae84f8b6..18bf505f0 100644 --- a/lib/lfrfid/lfrfid_dict_file.c +++ b/lib/lfrfid/lfrfid_dict_file.c @@ -17,13 +17,13 @@ bool lfrfid_dict_file_save(ProtocolDict* dict, ProtocolId protocol, const char* if(!flipper_format_file_open_always(file, filename)) break; if(!flipper_format_write_header_cstr(file, LFRFID_DICT_FILETYPE, 1)) break; - // TODO: write comment about protocol types into file + // TODO FL-3517: write comment about protocol types into file if(!flipper_format_write_string_cstr( file, "Key type", protocol_dict_get_name(dict, protocol))) break; - // TODO: write comment about protocol sizes into file + // TODO FL-3517: write comment about protocol sizes into file protocol_dict_get_data(dict, protocol, data, data_size); diff --git a/lib/lfrfid/lfrfid_worker.h b/lib/lfrfid/lfrfid_worker.h index def9f89a4..22135097e 100644 --- a/lib/lfrfid/lfrfid_worker.h +++ b/lib/lfrfid/lfrfid_worker.h @@ -26,8 +26,8 @@ typedef enum { } LFRFIDWorkerReadType; typedef enum { - LFRFIDWorkerReadSenseStart, // TODO: not implemented - LFRFIDWorkerReadSenseEnd, // TODO: not implemented + LFRFIDWorkerReadSenseStart, // TODO FL-3516: not implemented + LFRFIDWorkerReadSenseEnd, // TODO FL-3516: not implemented LFRFIDWorkerReadSenseCardStart, LFRFIDWorkerReadSenseCardEnd, LFRFIDWorkerReadStartASK, diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c index dd54cffb0..04386a675 100644 --- a/lib/lfrfid/protocols/protocol_fdx_b.c +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -4,6 +4,7 @@ #include #include #include "lfrfid_protocols.h" +#include #define FDX_B_ENCODED_BIT_SIZE (128) #define FDX_B_ENCODED_BYTE_SIZE (((FDX_B_ENCODED_BIT_SIZE) / 8)) @@ -323,8 +324,12 @@ void protocol_fdx_b_render_brief_data(ProtocolFDXB* protocol, FuriString* result float temperature; if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { - float temperature_c = (temperature - 32) / 1.8; - furi_string_cat_printf(result, "T: %.2fC", (double)temperature_c); + if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { + float temperature_c = (temperature - 32.0f) / 1.8f; + furi_string_cat_printf(result, "T: %.2fC", (double)temperature_c); + } else { + furi_string_cat_printf(result, "T: %.2fF", (double)temperature); + } } else { furi_string_cat_printf(result, "T: ---"); } diff --git a/lib/lfrfid/protocols/protocol_hid_ex_generic.c b/lib/lfrfid/protocols/protocol_hid_ex_generic.c index 240128cbe..35500ab59 100644 --- a/lib/lfrfid/protocols/protocol_hid_ex_generic.c +++ b/lib/lfrfid/protocols/protocol_hid_ex_generic.c @@ -193,7 +193,7 @@ bool protocol_hid_ex_generic_write_data(ProtocolHIDEx* protocol, void* data) { }; void protocol_hid_ex_generic_render_data(ProtocolHIDEx* protocol, FuriString* result) { - // TODO: parser and render functions + // TODO FL-3518: parser and render functions UNUSED(protocol); furi_string_printf(result, "Generic HID Extended\r\nData: Unknown"); }; diff --git a/lib/lfrfid/tools/bit_lib.c b/lib/lfrfid/tools/bit_lib.c index 54decb3e8..e0d0ff402 100644 --- a/lib/lfrfid/tools/bit_lib.c +++ b/lib/lfrfid/tools/bit_lib.c @@ -38,7 +38,7 @@ uint8_t bit_lib_get_bits(const uint8_t* data, size_t position, uint8_t length) { if(shift == 0) { return data[position / 8] >> (8 - length); } else { - // TODO fix read out of bounds + // TODO FL-3534: fix read out of bounds uint8_t value = (data[position / 8] << (shift)); value |= data[position / 8 + 1] >> (8 - shift); value = value >> (8 - length); diff --git a/lib/littlefs b/lib/littlefs index 40dba4a55..611c9b20d 160000 --- a/lib/littlefs +++ b/lib/littlefs @@ -1 +1 @@ -Subproject commit 40dba4a556e0d81dfbe64301a6aa4e18ceca896c +Subproject commit 611c9b20db2b99faee261daa7cc9bbe175d3eaca diff --git a/lib/nfc/helpers/nfc_generators.c b/lib/nfc/helpers/nfc_generators.c index 50c89aba8..11148577c 100644 --- a/lib/nfc/helpers/nfc_generators.c +++ b/lib/nfc/helpers/nfc_generators.c @@ -333,15 +333,30 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { mful->version.storage_size = 0x15; } -void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { +void nfc_generate_mf_classic_ext( + NfcDeviceData* data, + uint8_t uid_len, + MfClassicType type, + bool random_uid, + uint8_t* uid) { nfc_generate_common_start(data); - nfc_generate_mf_classic_uid(data->mf_classic_data.block[0].value, uid_len); + if(random_uid) { + nfc_generate_mf_classic_uid(data->mf_classic_data.block[0].value, uid_len); + } else { + memcpy(data->mf_classic_data.block[0].value, uid, uid_len); + } nfc_generate_mf_classic_common(data, uid_len, type); // Set the UID - data->nfc_data.uid[0] = NXP_MANUFACTURER_ID; - for(int i = 1; i < uid_len; i++) { - data->nfc_data.uid[i] = data->mf_classic_data.block[0].value[i]; + if(random_uid) { + data->nfc_data.uid[0] = NXP_MANUFACTURER_ID; + for(int i = 1; i < uid_len; i++) { + data->nfc_data.uid[i] = data->mf_classic_data.block[0].value[i]; + } + } else { + for(int i = 0; i < uid_len; i++) { + data->nfc_data.uid[i] = data->mf_classic_data.block[0].value[i]; + } } MfClassicData* mfc = &data->mf_classic_data; @@ -395,6 +410,11 @@ void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType mfc->type = type; } +void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { + uint8_t uid = 0; + nfc_generate_mf_classic_ext(data, uid_len, type, true, &uid); +} + static void nfc_generate_mf_mini(NfcDeviceData* data) { nfc_generate_mf_classic(data, 4, MfClassicTypeMini); } diff --git a/lib/nfc/helpers/nfc_generators.h b/lib/nfc/helpers/nfc_generators.h index 8cee67067..5102d0bc3 100644 --- a/lib/nfc/helpers/nfc_generators.h +++ b/lib/nfc/helpers/nfc_generators.h @@ -2,6 +2,10 @@ #include "../nfc_device.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef void (*NfcGeneratorFunc)(NfcDeviceData* data); typedef struct { @@ -12,3 +16,14 @@ typedef struct { extern const NfcGenerator* const nfc_generators[]; void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type); + +void nfc_generate_mf_classic_ext( + NfcDeviceData* data, + uint8_t uid_len, + MfClassicType type, + bool random_uid, + uint8_t* uid); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index cd9eb336c..ec15879eb 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -1631,6 +1631,9 @@ void nfc_device_data_clear(NfcDeviceData* dev_data) { } else if(dev_data->protocol == NfcDeviceProtocolEMV) { memset(&dev_data->emv_data, 0, sizeof(EmvData)); } + + furi_string_reset(dev_data->parsed_data); + memset(&dev_data->nfc_data, 0, sizeof(FuriHalNfcDevData)); dev_data->protocol = NfcDeviceProtocolUnknown; furi_string_reset(dev_data->parsed_data); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index b916c65de..77c95de34 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -1025,30 +1025,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { deactivated = true; } if(!mf_classic_is_key_found(data, i, MfClassicKeyB)) { - if(mf_classic_is_key_found(data, i, MfClassicKeyA)) { - uint64_t found_key; - if(nfc_worker_mf_get_b_key_from_sector_trailer( - &tx_rx, i, key, &found_key)) { - FURI_LOG_D(TAG, "Found B key via reading sector %d", i); - mf_classic_set_key_found(data, i, MfClassicKeyB, found_key); - - if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { - nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); - } - - nfc_worker_mf_classic_key_attack(nfc_worker, found_key, &tx_rx, i + 1); - break; - } - } - if(mf_classic_authenticate_skip_activate( - &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { + &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { //-V547 FURI_LOG_D(TAG, "Key B found: %012llX", 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); } - deactivated = true; + deactivated = true; //-V1048 } else { // If the key B is marked as found and matches the searching key, invalidate it MfClassicSectorTrailer* sec_trailer = @@ -1060,12 +1044,12 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { if(mf_classic_is_key_found(data, i, MfClassicKeyB) && memcmp(sec_trailer->key_b, current_key, 6) == 0) { if(!mf_classic_authenticate_skip_activate( - &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { + &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { //-V547 mf_classic_set_key_not_found(data, i, MfClassicKeyB); FURI_LOG_D(TAG, "Key %dB not found in attack", i); } furi_hal_nfc_sleep(); - deactivated = true; + deactivated = true; //-V1048 } } if(mf_classic_is_key_found(data, i, MfClassicKeyA) && diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index 017b06cae..281463281 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -29,7 +29,7 @@ ReturnCode nfcv_inventory(uint8_t* uid) { ReturnCode ret = ERR_NONE; for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { - /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + /* TODO: needs proper abstraction via furi_hal(_ll)_* */ ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); if(ret == ERR_NONE) { @@ -89,7 +89,7 @@ ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { - /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + /* TODO: needs proper abstraction via furi_hal(_ll)_* */ ret = rfalNfcvPollerGetSystemInformation( RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received); diff --git a/lib/print/wrappers.c b/lib/print/wrappers.c index 5cfe10600..b248aeb3d 100644 --- a/lib/print/wrappers.c +++ b/lib/print/wrappers.c @@ -59,7 +59,6 @@ int __wrap_fflush(FILE* stream) { __attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) { UNUSED(file); UNUSED(line); - // TODO: message file and line number furi_crash(e); } @@ -68,6 +67,5 @@ __attribute__((__noreturn__)) void UNUSED(file); UNUSED(line); UNUSED(func); - // TODO: message file and line number furi_crash(e); } \ No newline at end of file diff --git a/lib/subghz/devices/devices.c b/lib/subghz/devices/devices.c index cc6a3e6e2..64ed545b5 100644 --- a/lib/subghz/devices/devices.c +++ b/lib/subghz/devices/devices.c @@ -7,29 +7,11 @@ void subghz_devices_init() { furi_check(!subghz_device_registry_is_valid()); subghz_device_registry_init(); - - SubGhzLastSettings* last_settings = subghz_last_settings_alloc(); - subghz_last_settings_load(last_settings, 0); - - if(last_settings->external_module_power_amp) { - furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull); - } - - subghz_last_settings_free(last_settings); } void subghz_devices_deinit(void) { furi_check(subghz_device_registry_is_valid()); subghz_device_registry_deinit(); - - SubGhzLastSettings* last_settings = subghz_last_settings_alloc(); - subghz_last_settings_load(last_settings, 0); - - if(last_settings->external_module_power_amp) { - furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeAnalog); - } - - subghz_last_settings_free(last_settings); } const SubGhzDevice* subghz_devices_get_by_name(const char* device_name) { @@ -50,7 +32,13 @@ bool subghz_devices_begin(const SubGhzDevice* device) { bool ret = false; furi_assert(device); if(device->interconnect->begin) { - ret = device->interconnect->begin(); + SubGhzDeviceConf conf = { + .ver = 1, + .extended_range = false, // TODO + .power_amp = furi_hal_subghz_get_ext_power_amp(), + }; + + ret = device->interconnect->begin(&conf); } return ret; } @@ -89,7 +77,6 @@ void subghz_devices_idle(const SubGhzDevice* device) { furi_assert(device); if(device->interconnect->idle) { device->interconnect->idle(); - furi_hal_gpio_write(&gpio_ext_pc3, 0); } } @@ -142,15 +129,6 @@ bool subghz_devices_set_tx(const SubGhzDevice* device) { furi_assert(device); if(device->interconnect->set_tx) { ret = device->interconnect->set_tx(); - - SubGhzLastSettings* last_settings = subghz_last_settings_alloc(); - subghz_last_settings_load(last_settings, 0); - - if(last_settings->external_module_power_amp) { - furi_hal_gpio_write(&gpio_ext_pc3, 1); - } - - subghz_last_settings_free(last_settings); } return ret; } @@ -191,14 +169,6 @@ void subghz_devices_set_rx(const SubGhzDevice* device) { furi_assert(device); if(device->interconnect->set_rx) { device->interconnect->set_rx(); - SubGhzLastSettings* last_settings = subghz_last_settings_alloc(); - subghz_last_settings_load(last_settings, 0); - - if(last_settings->external_module_power_amp) { - furi_hal_gpio_write(&gpio_ext_pc3, 0); - } - - subghz_last_settings_free(last_settings); } } diff --git a/lib/subghz/devices/registry.c b/lib/subghz/devices/registry.c index c0d5bb292..779ba81d7 100644 --- a/lib/subghz/devices/registry.c +++ b/lib/subghz/devices/registry.c @@ -22,7 +22,7 @@ void subghz_device_registry_init(void) { SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION, firmware_api_interface); - //ToDo: fix path to plugins + //TODO FL-3556: 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) { diff --git a/lib/subghz/devices/types.h b/lib/subghz/devices/types.h index 8a4198426..01df15940 100644 --- a/lib/subghz/devices/types.h +++ b/lib/subghz/devices/types.h @@ -12,12 +12,13 @@ #include #define SUBGHZ_RADIO_DEVICE_PLUGIN_APP_ID "subghz_radio_device" -#define SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION 1 +#define SUBGHZ_RADIO_DEVICE_PLUGIN_API_VERSION 2 typedef struct SubGhzDeviceRegistry SubGhzDeviceRegistry; typedef struct SubGhzDevice SubGhzDevice; +typedef struct SubGhzDeviceConf SubGhzDeviceConf; -typedef bool (*SubGhzBegin)(void); +typedef bool (*SubGhzBegin)(SubGhzDeviceConf* conf); typedef void (*SubGhzEnd)(void); typedef bool (*SubGhzIsConnect)(void); typedef void (*SubGhzReset)(void); @@ -89,3 +90,9 @@ struct SubGhzDevice { const char* name; const SubGhzDeviceInterconnect* interconnect; }; + +struct SubGhzDeviceConf { + uint8_t ver; + bool extended_range; + bool power_amp; +}; diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 1a35550c6..2a16cae85 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -274,7 +274,7 @@ static bool subghz_protocol_alutech_at_4n_gen_data( } if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { instance->generic.cnt = 0; } else { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 003cc5edd..21f5e6187 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -744,7 +744,6 @@ static bool bin_raw_debug("\r\n\r\n"); #endif - //todo can be optimized BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; memcpy( markup_temp, @@ -770,7 +769,6 @@ static bool } } } - //todo can be optimized if(bin_raw_type == BinRAWTypeGap) { if(data_temp != 0) { //there are sequences with the same number of bits diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index cea7ebf6f..0d9545020 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -186,7 +186,7 @@ static void subghz_protocol_encoder_came_atomo_get_upload( uint8_t pack[8] = {}; if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { instance->generic.cnt = 0; } else { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); diff --git a/lib/subghz/protocols/faac_slh.c b/lib/subghz/protocols/faac_slh.c index 62e8c37d2..3ae6c8e23 100644 --- a/lib/subghz/protocols/faac_slh.c +++ b/lib/subghz/protocols/faac_slh.c @@ -112,6 +112,9 @@ void subghz_protocol_encoder_faac_slh_free(void* context) { static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* instance) { if(instance->generic.seed != 0x0) { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } else { + // Do not generate new data, send data from buffer + return true; } uint32_t fix = instance->generic.serial << 4 | instance->generic.btn; uint32_t hop = 0; @@ -434,6 +437,9 @@ SubGhzProtocolStatus subghz_protocol_decoder_faac_slh_serialize( furi_assert(context); SubGhzProtocolDecoderFaacSLH* instance = context; + // Reset seed leftover from previous decoded signal + instance->generic.seed = 0x0; + SubGhzProtocolStatus res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index db46936c2..a274602f3 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -170,7 +170,7 @@ static bool subghz_protocol_keeloq_gen_data( } if(counter_up && prog_mode == PROG_MODE_OFF) { if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { instance->generic.cnt = 0; } else { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c index c5d21bcb0..998f17458 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.c +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -156,7 +156,7 @@ static bool subghz_protocol_kinggates_stylo_4k_gen_data( instance->generic.cnt = decrypt & 0xFFFF; if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { instance->generic.cnt = 0; } else { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index eca9c4e95..6448378f6 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -152,7 +152,7 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( } if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { instance->generic.cnt = 0; } else { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index d945d19a1..ae3fef9b5 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -265,7 +265,7 @@ SubGhzProtocolStatus furi_assert(context); UNUSED(context); UNUSED(flipper_format); - //ToDo stub, for backwards compatibility + // stub, for backwards compatibility return SubGhzProtocolStatusOk; } @@ -273,7 +273,6 @@ void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) { furi_assert(context); //SubGhzProtocolDecoderRAW* instance = context; UNUSED(context); - //ToDo no use furi_string_cat_printf(output, "RAW Data"); } diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index 4074d757b..be8ebacf5 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -131,7 +131,7 @@ static bool instance->generic.serial = data & 0xFFFFFF; if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { instance->generic.cnt = 0; } else { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index 5fbd90275..9a3882d87 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -125,7 +125,7 @@ static bool subghz_protocol_somfy_telis_gen_data( btn = subghz_protocol_somfy_telis_get_btn_code(); if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { instance->generic.cnt = 0; } else { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); diff --git a/lib/subghz/protocols/star_line.c b/lib/subghz/protocols/star_line.c index bf338b35d..75a7fd471 100644 --- a/lib/subghz/protocols/star_line.c +++ b/lib/subghz/protocols/star_line.c @@ -130,7 +130,7 @@ void subghz_protocol_encoder_star_line_free(void* context) { static bool subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) { if(instance->generic.cnt < 0xFFFF) { - if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) >= 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { instance->generic.cnt = 0; } else { instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 3534745b7..5d7118092 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -244,7 +244,9 @@ bool subghz_file_encoder_worker_start( furi_stream_buffer_reset(instance->stream); furi_string_set(instance->file_path, file_path); - instance->device = subghz_devices_get_by_name(radio_device_name); + if(radio_device_name) { + 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_keystore.c b/lib/subghz/subghz_keystore.c index 54ed15a99..636288aa6 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -122,7 +122,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, do { if(iv) { - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load decryption key"); break; } @@ -181,7 +181,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, } } while(ret > 0 && result); - if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + if(iv) furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); } while(false); free(encrypted_line); @@ -280,7 +280,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 subghz_keystore_mess_with_iv(iv); - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -326,7 +326,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 stream_write_char(stream, '\n'); encrypted_line_count++; } - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); size_t total_keys = SubGhzKeyArray_size(instance->data); result = encrypted_line_count == total_keys; if(result) { @@ -421,7 +421,7 @@ bool subghz_keystore_raw_encrypted_save( subghz_keystore_mess_with_iv(iv); - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -474,7 +474,7 @@ bool subghz_keystore_raw_encrypted_save( flipper_format_free(output_flipper_format); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(!result) break; @@ -576,7 +576,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* } } - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -604,7 +604,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len); } while(0); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(decrypted) result = true; } while(0); flipper_format_free(flipper_format); diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 8efe85f7b..6911793fc 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -247,7 +247,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 + //Todo FL-3535: 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 4eca17419..369f5206c 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -165,7 +165,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); subghz_tx_rx_worker_tx(instance, data, SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE); } else { - //todo checking that he managed to write all the data to the TX buffer + //TODO FL-3554: checking that it managed to write all the data to the TX buffer furi_stream_buffer_receive( instance->stream_tx, &data, size_tx, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); subghz_tx_rx_worker_tx(instance, data, size_tx); @@ -178,7 +178,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_stream_buffer_bytes_available(instance->stream_rx) == 0) { callback_rx = true; } - //todo checking that he managed to write all the data to the RX buffer + //TODO FL-3554: checking that it managed to write all the data to the RX buffer furi_stream_buffer_send( instance->stream_rx, &data, @@ -189,7 +189,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { callback_rx = false; } } else { - //todo RX buffer overflow + //TODO FL-3555: RX buffer overflow } } } diff --git a/lib/subghz/types.h b/lib/subghz/types.h index d0b500a85..954a5aff3 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -57,6 +57,7 @@ typedef enum { // Encoder issue SubGhzProtocolStatusErrorEncoderGetUpload = (-12), ///< Payload encoder failure // Special Values + SubGhzProtocolStatusErrorProtocolNotFound = (-13), ///< Protocol not found SubGhzProtocolStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization. } SubGhzProtocolStatus; diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index fafedfeb4..761755d2d 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -9,7 +9,7 @@ env.Append( ], SDK_HEADERS=[ File("api_lock.h"), - File("value_index.h"), + File("compress.h"), File("manchester_decoder.h"), File("manchester_encoder.h"), File("path.h"), @@ -22,6 +22,7 @@ env.Append( File("saved_struct.h"), File("version.h"), File("float_tools.h"), + File("value_index.h"), File("tar/tar_archive.h"), File("stream/stream.h"), File("stream/file_stream.h"), diff --git a/lib/toolbox/colors.c b/lib/toolbox/colors.c new file mode 100644 index 000000000..33ad8152c --- /dev/null +++ b/lib/toolbox/colors.c @@ -0,0 +1,95 @@ +// https://stackoverflow.com/a/14733008 + +#include "colors.h" + +inline int rgbcmp(const RgbColor* a, const RgbColor* b) { + return memcmp(a, b, sizeof(RgbColor)); +} + +inline int hsvcmp(const HsvColor* a, const HsvColor* b) { + return memcmp(a, b, sizeof(HsvColor)); +} + +RgbColor hsv2rgb(HsvColor hsv) { + RgbColor rgb; + uint8_t region, remainder, p, q, t; + + if(hsv.s == 0) { + rgb.r = hsv.v; + rgb.g = hsv.v; + rgb.b = hsv.v; + return rgb; + } + + region = hsv.h / 43; + remainder = (hsv.h - (region * 43)) * 6; + + p = (hsv.v * (255 - hsv.s)) >> 8; + q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8; + t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8; + + switch(region) { + case 0: + rgb.r = hsv.v; + rgb.g = t; + rgb.b = p; + break; + case 1: + rgb.r = q; + rgb.g = hsv.v; + rgb.b = p; + break; + case 2: + rgb.r = p; + rgb.g = hsv.v; + rgb.b = t; + break; + case 3: + rgb.r = p; + rgb.g = q; + rgb.b = hsv.v; + break; + case 4: + rgb.r = t; + rgb.g = p; + rgb.b = hsv.v; + break; + default: + rgb.r = hsv.v; + rgb.g = p; + rgb.b = q; + break; + } + + return rgb; +} + +HsvColor rgb2hsv(RgbColor rgb) { + HsvColor hsv; + uint8_t rgbMin, rgbMax; + + rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b); + rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b); + + hsv.v = rgbMax; + if(hsv.v == 0) { + hsv.h = 0; + hsv.s = 0; + return hsv; + } + + hsv.s = 255 * ((long)rgbMax - (long)rgbMin) / hsv.v; + if(hsv.s == 0) { + hsv.h = 0; + return hsv; + } + + if(rgbMax == rgb.r) + hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin); + else if(rgbMax == rgb.g) + hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin); + else + hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin); + + return hsv; +} diff --git a/lib/toolbox/colors.h b/lib/toolbox/colors.h new file mode 100644 index 000000000..902b1a5d0 --- /dev/null +++ b/lib/toolbox/colors.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct RgbColor { + uint8_t r; + uint8_t g; + uint8_t b; +} RgbColor; + +typedef struct HsvColor { + uint8_t h; + uint8_t s; + uint8_t v; +} HsvColor; + +int rgbcmp(const RgbColor* a, const RgbColor* b); + +int hsvcmp(const HsvColor* a, const HsvColor* b); + +RgbColor hsv2rgb(HsvColor hsv); + +HsvColor rgb2hsv(RgbColor rgb); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/crc32_calc.c b/lib/toolbox/crc32_calc.c index c8ae3524a..c0cd169b1 100644 --- a/lib/toolbox/crc32_calc.c +++ b/lib/toolbox/crc32_calc.c @@ -4,7 +4,7 @@ #define CRC_DATA_BUFFER_MAX_LEN 512 uint32_t crc32_calc_buffer(uint32_t crc, const void* buffer, size_t size) { - // TODO: consider removing dependency on LFS + // TODO FL-3547: consider removing dependency on LFS return ~lfs_crc(~crc, buffer, size); } diff --git a/lib/toolbox/md5_calc.c b/lib/toolbox/md5_calc.c new file mode 100644 index 000000000..b050295a1 --- /dev/null +++ b/lib/toolbox/md5_calc.c @@ -0,0 +1,44 @@ +#include "md5.h" +#include "md5_calc.h" + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error) { + bool result = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + const uint16_t size_to_read = 512; + uint8_t* data = malloc(size_to_read); + md5_context* md5_ctx = malloc(sizeof(md5_context)); + + md5_starts(md5_ctx); + while(true) { + uint16_t read_size = storage_file_read(file, data, size_to_read); + if(read_size == 0) break; + md5_update(md5_ctx, data, read_size); + } + md5_finish(md5_ctx, output); + free(md5_ctx); + free(data); + } + + if(file_error != NULL) { + *file_error = storage_file_get_error(file); + } + + storage_file_close(file); + return result; +} + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error) { + const size_t hash_size = 16; + unsigned char hash[hash_size]; + bool result = md5_calc_file(file, path, hash, file_error); + + if(result) { + furi_string_set(output, ""); + for(size_t i = 0; i < hash_size; i++) { + furi_string_cat_printf(output, "%02x", hash[i]); + } + } + + return result; +} \ No newline at end of file diff --git a/lib/toolbox/md5_calc.h b/lib/toolbox/md5_calc.h new file mode 100644 index 000000000..cf82e3718 --- /dev/null +++ b/lib/toolbox/md5_calc.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error); + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error); + +#ifdef __cplusplus +} +#endif diff --git a/lib/toolbox/stream/file_stream.c b/lib/toolbox/stream/file_stream.c index 064912168..095dce472 100644 --- a/lib/toolbox/stream/file_stream.c +++ b/lib/toolbox/stream/file_stream.c @@ -134,7 +134,7 @@ static size_t file_stream_size(FileStream* stream) { } static size_t file_stream_write(FileStream* stream, const uint8_t* data, size_t size) { - // TODO cache + // TODO FL-3545: cache size_t need_to_write = size; while(need_to_write > 0) { uint16_t was_written = @@ -148,7 +148,7 @@ static size_t file_stream_write(FileStream* stream, const uint8_t* data, size_t } static size_t file_stream_read(FileStream* stream, uint8_t* data, size_t size) { - // TODO cache + // TODO FL-3545: cache size_t need_to_read = size; while(need_to_read > 0) { uint16_t was_read = @@ -172,7 +172,7 @@ static bool file_stream_delete_and_insert( // open scratchpad Stream* scratch_stream = file_stream_alloc(_stream->storage); - // TODO: we need something like "storage_open_tmpfile and storage_close_tmpfile" + // TODO FL-3546: we need something like "storage_open_tmpfile and storage_close_tmpfile" FuriString* scratch_name; FuriString* tmp_name; tmp_name = furi_string_alloc(); diff --git a/lib/toolbox/stream/string_stream.c b/lib/toolbox/stream/string_stream.c index 075f0b26f..f8a360c03 100644 --- a/lib/toolbox/stream/string_stream.c +++ b/lib/toolbox/stream/string_stream.c @@ -106,7 +106,7 @@ static size_t string_stream_size(StringStream* stream) { } static size_t string_stream_write(StringStream* stream, const char* data, size_t size) { - // TODO: can be optimized for edge cases + // TODO FL-3544: can be optimized for edge cases size_t i; for(i = 0; i < size; i++) { string_stream_write_char(stream, data[i]); diff --git a/lib/u8g2/u8g2.h b/lib/u8g2/u8g2.h index 0068ea61d..0d830390a 100644 --- a/lib/u8g2/u8g2.h +++ b/lib/u8g2/u8g2.h @@ -5813,6 +5813,7 @@ extern const uint8_t u8g2_font_px437wyse700b_tn[] U8G2_FONT_SECTION("u8g2_font_p extern const uint8_t u8g2_font_px437wyse700b_mf[] U8G2_FONT_SECTION("u8g2_font_px437wyse700b_mf"); extern const uint8_t u8g2_font_px437wyse700b_mr[] U8G2_FONT_SECTION("u8g2_font_px437wyse700b_mr"); extern const uint8_t u8g2_font_px437wyse700b_mn[] U8G2_FONT_SECTION("u8g2_font_px437wyse700b_mn"); +extern const uint8_t u8g2_font_eurocorp_tr[] U8G2_FONT_SECTION("u8g2_font_eurocorp_tr"); /* end font list */ diff --git a/lib/u8g2/u8g2_fonts.c b/lib/u8g2/u8g2_fonts.c index 57ca70f80..a4e6ac85f 100644 --- a/lib/u8g2/u8g2_fonts.c +++ b/lib/u8g2/u8g2_fonts.c @@ -40249,7 +40249,7 @@ const uint8_t u8g2_font_open_iconic_www_8x_t[2724] U8G2_FONT_SECTION( "\377\377\0"; /* Fontname: ProFont10 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 0 */ @@ -40319,7 +40319,7 @@ const uint8_t u8g2_font_profont10_tf[2005] U8G2_FONT_SECTION("u8g2_font_profont1 "\310\0\377\13DZW\243h\246\15\222\2\0\0\0\4\377\377\0"; /* Fontname: ProFont10 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 0 */ @@ -40354,7 +40354,7 @@ const uint8_t u8g2_font_profont10_tr[886] U8G2_FONT_SECTION("u8g2_font_profont10 "U\4~\7\24j\227T\2\177\5\0b\3\0\0\0\4\377\377\0"; /* Fontname: ProFont10 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 0 */ @@ -40367,7 +40367,7 @@ const uint8_t u8g2_font_profont10_tn[184] U8G2_FONT_SECTION("u8g2_font_profont10 "\71\12\64\262SQ\246\15\222\2:\6!\263\211\1\0\0\0\4\377\377\0"; /* Fontname: ProFont10 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 2 */ @@ -40452,7 +40452,7 @@ const uint8_t u8g2_font_profont10_mf[2486] U8G2_FONT_SECTION("u8g2_font_profont1 "\17\325\364&b\221D$\21\211\214\42\22\0\0\0\0\4\377\377\0"; /* Fontname: ProFont10 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 2 */ @@ -40492,7 +40492,7 @@ const uint8_t u8g2_font_profont10_mr[1045] U8G2_FONT_SECTION("u8g2_font_profont1 "\315\364II;\13\0\177\6\315\364\371\6\0\0\0\4\377\377\0"; /* Fontname: ProFont10 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 2 */ @@ -40506,7 +40506,7 @@ const uint8_t u8g2_font_profont10_mn[207] U8G2_FONT_SECTION("u8g2_font_profont10 "\305\364\321\234\220\243\0\0\0\0\4\377\377\0"; /* Fontname: ProFont11 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 0 */ @@ -40584,7 +40584,7 @@ const uint8_t u8g2_font_profont11_tf[2267] U8G2_FONT_SECTION("u8g2_font_profont1 "\207$\263\15J\30\2\377\14M\232\247\234\271%C\230,\0\0\0\0\4\377\377\0"; /* Fontname: ProFont11 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 0 */ @@ -40622,7 +40622,7 @@ const uint8_t u8g2_font_profont11_tr[967] U8G2_FONT_SECTION("u8g2_font_profont11 "\0\0\4\377\377\0"; /* Fontname: ProFont11 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 0 */ @@ -40636,7 +40636,7 @@ const uint8_t u8g2_font_profont11_tn[202] U8G2_FONT_SECTION("u8g2_font_profont11 "C\0\0\0\0\4\377\377\0"; /* Fontname: ProFont11 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 2 */ @@ -40732,7 +40732,7 @@ const uint8_t u8g2_font_profont11_mf[2845] U8G2_FONT_SECTION("u8g2_font_profont1 "\20\245)\0\377\21\336\370\341$G\262$K\262$\213\206\64\232\0\0\0\0\4\377\377\0"; /* Fontname: ProFont11 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 2 */ @@ -40777,7 +40777,7 @@ const uint8_t u8g2_font_profont11_mr[1203] U8G2_FONT_SECTION("u8g2_font_profont1 "\225\212\316\21\177\7\336\370\371\67\0\0\0\0\4\377\377\0"; /* Fontname: ProFont11 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 2 */ @@ -40792,7 +40792,7 @@ const uint8_t u8g2_font_profont11_mn[236] U8G2_FONT_SECTION("u8g2_font_profont11 "\216\211:\1\0\0\0\4\377\377\0"; /* Fontname: ProFont12 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 0 */ @@ -40872,7 +40872,7 @@ const uint8_t u8g2_font_profont12_tf[2315] U8G2_FONT_SECTION("u8g2_font_profont1 "&\13\0\0\0\0\4\377\377\0"; /* Fontname: ProFont12 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 0 */ @@ -40910,7 +40910,7 @@ const uint8_t u8g2_font_profont12_tr[980] U8G2_FONT_SECTION("u8g2_font_profont12 "\2~\6\25\262\307\5\177\5\0\242\3\0\0\0\4\377\377\0"; /* Fontname: ProFont12 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 0 */ @@ -40924,7 +40924,7 @@ const uint8_t u8g2_font_profont12_tn[208] U8G2_FONT_SECTION("u8g2_font_profont12 ":\10\62\323\61\204C\0\0\0\0\4\377\377\0"; /* Fontname: ProFont12 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 2 */ @@ -41023,7 +41023,7 @@ const uint8_t u8g2_font_profont12_mf[2928] U8G2_FONT_SECTION("u8g2_font_profont1 "\226dI\26\15i\64\1\0\0\0\4\377\377\0"; /* Fontname: ProFont12 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 2 */ @@ -41068,7 +41068,7 @@ const uint8_t u8g2_font_profont12_mr[1212] U8G2_FONT_SECTION("u8g2_font_profont1 "L\33C\0~\11\336\370\251JE\347\15\177\7\336\370\371\67\0\0\0\0\4\377\377\0"; /* Fontname: ProFont12 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 2 */ @@ -41083,7 +41083,7 @@ const uint8_t u8g2_font_profont12_mn[235] U8G2_FONT_SECTION("u8g2_font_profont12 "\22u\2\0\0\0\4\377\377\0"; /* Fontname: ProFont15 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 0 */ @@ -41173,7 +41173,7 @@ const uint8_t u8g2_font_profont15_tf[2650] U8G2_FONT_SECTION("u8g2_font_profont1 "\260\244U\0\377\16fl\237(\207C\217\311\240V\206\4\0\0\0\4\377\377\0"; /* Fontname: ProFont15 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 0 */ @@ -41215,7 +41215,7 @@ const uint8_t u8g2_font_profont15_tr[1093] U8G2_FONT_SECTION("u8g2_font_profont1 "\4\377\377\0"; /* Fontname: ProFont15 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 0 */ @@ -41229,7 +41229,7 @@ const uint8_t u8g2_font_profont15_tn[219] U8G2_FONT_SECTION("u8g2_font_profont15 "\0\71\14N\362C\11j\214TS(\0:\6:\363A&\0\0\0\4\377\377\0"; /* Fontname: ProFont15 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 2 */ @@ -41345,7 +41345,7 @@ const uint8_t u8g2_font_profont15_mf[3472] U8G2_FONT_SECTION("u8g2_font_profont1 "\302$\214\6\271\64D\0\0\0\0\4\377\377\0"; /* Fontname: ProFont15 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 2 */ @@ -41398,7 +41398,7 @@ const uint8_t u8g2_font_profont15_mr[1460] U8G2_FONT_SECTION("u8g2_font_profont1 "J\42\235\237\0\177\7\377\371\363\377\3\0\0\0\4\377\377\0"; /* Fontname: ProFont15 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 2 */ @@ -41414,7 +41414,7 @@ const uint8_t u8g2_font_profont15_mn[278] U8G2_FONT_SECTION("u8g2_font_profont15 "\245!g\2:\12\357\371\63\251:\253\235\15\0\0\0\4\377\377\0"; /* Fontname: ProFont17 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 0 */ @@ -41522,7 +41522,7 @@ const uint8_t u8g2_font_profont17_tf[3206] U8G2_FONT_SECTION("u8g2_font_profont1 "\0\4\377\377\0"; /* Fontname: ProFont17 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 0 */ @@ -41570,7 +41570,7 @@ const uint8_t u8g2_font_profont17_tr[1308] U8G2_FONT_SECTION("u8g2_font_profont1 "\251\351\64\222\1~\12\70Xsf\23\311l\2\177\5\0\10\63\0\0\0\4\377\377\0"; /* Fontname: ProFont17 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 0 */ @@ -41586,7 +41586,7 @@ const uint8_t u8g2_font_profont17_tn[273] U8G2_FONT_SECTION("u8g2_font_profont17 ":\11\203\214\31\7\361A\0\0\0\0\4\377\377\0"; /* Fontname: ProFont17 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 2 */ @@ -41730,7 +41730,7 @@ const uint8_t u8g2_font_profont17_mf[4356] U8G2_FONT_SECTION("u8g2_font_profont1 "\377\377\0"; /* Fontname: ProFont17 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 2 */ @@ -41794,7 +41794,7 @@ const uint8_t u8g2_font_profont17_mr[1807] U8G2_FONT_SECTION("u8g2_font_profont1 "\10\31'\77\376\33\0\0\0\0\4\377\377\0"; /* Fontname: ProFont17 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 2 */ @@ -41812,7 +41812,7 @@ const uint8_t u8g2_font_profont17_mn[346] U8G2_FONT_SECTION("u8g2_font_profont17 "\235\210M=\2:\15\11'\77&c\353!\307\326\243\0\0\0\0\4\377\377\0"; /* Fontname: ProFont22 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 224/256 BBX Build Mode: 0 */ @@ -41950,7 +41950,7 @@ const uint8_t u8g2_font_profont22_tf[4184] U8G2_FONT_SECTION("u8g2_font_profont2 "\42\17'\306\317\252$\16D\16\2\345FnL\0\0\0\0\4\377\377\0"; /* Fontname: ProFont22 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 96/256 BBX Build Mode: 0 */ @@ -42011,7 +42011,7 @@ const uint8_t u8g2_font_profont22_tr[1704] U8G2_FONT_SECTION("u8g2_font_profont2 "\0\0\0\4\377\377\0"; /* Fontname: ProFont22 - Copyright: ProFont Distribution 2.2 Ñ Generated by Fontographer 4.1.5 + Copyright: ProFont Distribution 2.2 - Generated by Fontographer 4.1.5 Glyphs: 18/256 BBX Build Mode: 0 */ @@ -42029,7 +42029,7 @@ const uint8_t u8g2_font_profont22_tn[345] U8G2_FONT_SECTION("u8g2_font_profont22 "\310\31#\0:\15\244\236$\11\12\266\20\42X\10Q!\6" + "\205 \23\242H\10\23!pa\42D\221\20dB\14\12!*D\260\200\0\77\26\16\12\312\242\206\305" + "\203\241\304\310\203\302\331\271\361`\305\316\16\0@#\16\12\312\242\206\305\203\20\301L\204X\21bE\210" + "\25!V\204X\21bE\210*\202\14)\21\224I\7A\27\16\12\306&X,Q\222\6\315!C\245" + "H\15\23\26\17\232\33\61B+\16\12\306\260\206\11\223\20\242\206\204\20\65$\204\250!!D\240\10!" + "B\205\10\25\42T\210#!\216\204\210$\42T\210h\221\0C\30\16\12\306\242\206\305\203a\350\216\322" + "/Z\240 \21\242\10\211\20d\0D\25\16\12\306\20\350\210\30\24C\332\335\337=\60\367 \4\23\0" + "E\34\16\12\306\302\342\301\203\240\264 \225\25\11RD\251h\201\202D\210\42$B\220\1F+\16\12" + "\306\62\2\5\211\26)\204\206\20\32Bh\10\21\205B\210(\24BD\241\20B\10\205\20\32Bh\10" + "\301BC\10%\12\0G\42\16\12\306\261\244\305\203\20\301L\204+\21TDP\21AE\4B\21\352" + "d\211p\17D\64a\263\0H%\17\12\312@ND\71\21\345D\224\23QND\71\21\236\264pQ" + "ND\71\21\345D\224\23QND\71\21\1I\13\4\12\236`b\376_\304\0J\25\16\12\306\204," + "\225\332\271-i\360\334\25\315\36\210`\2\0K\42\16\12\302@:()\22\244H\220\42A\212D\13" + "&k\24\21!D\244\14\231\42\204J\220JF\0L%\16\12\306@TDP\21AE\4\25\21T" + "DP\21AE\4\25\21TDP\21AE\4}\360`\5\23\0M\65\22\12\326 \206\222!U\220" + "\60qB\14\231\22b\310\224\20C\246\204\30\62%\304\220)!\206L\11\61dJ\210!SB\14\231" + "\22b\310\224\20C\35\31\64\24N'\16\12\306 \346\310\20\24$\232\205\60\26\302X\30b!\214\205" + "\60\26\302X\10c!\214\205\60\26\302X\10s\7\5O#\16\12\306\242\206\305\203\20\301L\204+\21" + "\256D\270\22\341J\204+\21\256d\211p\17\36\204`\243\4\0P\26\16\12\306\20\350\210\230\20&\206" + "\204Xw\317\36|A\224\246\0Q\25\16\12\306r(\204\211\60)\202\234\273w\17\36\204h;\7R" + "\33\16\12\306\20\350\210\230\20&\206\204X\206\356\356\36\30\134\26\13\12\272" + "@\216 \71\202\344\10\222#H\216\134\71\12\311\21$]\15\206\352\245\360` \21\372\237=\30^\13" + "\256(\313E\322\34*%\16_\12\216\250\311\360\301\12&\0`\15\356(\313P\302H\13\267\23\213\16" + "a\24\253\11\272\25r\340\270b\245\16\235I\222\342\301\331\22\5b\30\253\11\272\220$\211\212A#\6" + "\215\30ab\204VV\234\20\241\342\0c\20\253\11\272\202\344A*\203\363\360\201\211$\0d\21\253\11" + "\272\20\306\204\20\25\252\354\253\7)\222\0e\25\253\11\272\222\342A\302)\310\240\31Af\340\300\7&" + "\222\0f\25\253\11\272\222\342A\302)\310\214 \63\202\314\300)\5\16\4g\21\253\11\272\221\302\225\15" + "\7\35\62f\312\205\222\4h\16\253\11\272\60\312\256\36vendor) || (dfu_suffix.idProduct != reference_params->product) || @@ -137,7 +137,7 @@ bool dfu_file_process_targets(const DfuUpdateTask* task, File* dfuf, const uint8 return UpdateBlockResult_Failed; } - /* TODO: look into TargetPrefix and validate/filter?.. */ + /* TODO FL-3562: look into TargetPrefix and validate/filter?.. */ for(uint32_t i_element = 0; i_element < target_prefix.dwNbElements; ++i_element) { bytes_read = storage_file_read(dfuf, &image_element, sizeof(ImageElementHeader)); if(bytes_read != sizeof(ImageElementHeader)) { diff --git a/lib/update_util/update_manifest.c b/lib/update_util/update_manifest.c index 795fdd5eb..47b2cc0b9 100644 --- a/lib/update_util/update_manifest.c +++ b/lib/update_util/update_manifest.c @@ -54,7 +54,7 @@ static bool FuriString* filetype; - // TODO: compare filetype? + // TODO FL-3543: compare filetype? filetype = furi_string_alloc(); update_manifest->valid = flipper_format_read_header(flipper_file, filetype, &update_manifest->manifest_version) && diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index 24aa07f4e..49bf67d0b 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -1,5 +1,6 @@ #include "xtreme.h" #include +#include #include #define TAG "XtremeSettings" @@ -19,6 +20,7 @@ XtremeSettings xtreme_settings = { .lockscreen_date = true, // ON .lockscreen_statusbar = true, // ON .lockscreen_prompt = true, // ON + .lockscreen_transparent = false, // OFF .battery_icon = BatteryIconBarPercent, // Bar % .statusbar_clock = false, // OFF .status_icons = true, // ON @@ -34,6 +36,11 @@ XtremeSettings xtreme_settings = { .rgb_backlight = false, // OFF .butthurt_timer = 21600, // 6 H .charge_cap = 100, // 100% + .spi_cc1101_handle = SpiDefault, // &furi_hal_spi_bus_handle_external + .spi_nrf24_handle = SpiDefault, // &furi_hal_spi_bus_handle_external + .uart_esp_channel = UARTDefault, // pin 13,14 + .uart_nmea_channel = UARTDefault, // pin 13,14 + .uart_general_channel = UARTDefault, // pin 13,14 }; void XTREME_SETTINGS_LOAD() { @@ -102,6 +109,10 @@ void XTREME_SETTINGS_LOAD() { x->lockscreen_prompt = b; } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "lockscreen_transparent", &b, 1)) { + x->lockscreen_transparent = b; + } + flipper_format_rewind(file); if(flipper_format_read_uint32(file, "battery_icon", &u, 1)) { x->battery_icon = CLAMP(u, BatteryIconCount - 1U, 0U); } @@ -161,9 +172,31 @@ 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_uint32(file, "spi_cc1101_handle", &u, 1)) { + x->spi_cc1101_handle = CLAMP(u, SpiCount - 1U, 0U); + } + flipper_format_rewind(file); + if(flipper_format_read_uint32(file, "spi_nrf24_handle", &u, 1)) { + x->spi_nrf24_handle = CLAMP(u, SpiCount - 1U, 0U); + } + flipper_format_rewind(file); + if(flipper_format_read_uint32(file, "uart_esp_channel", &u, 1)) { + x->uart_esp_channel = CLAMP(u, UARTCount - 1U, 0U); + } + flipper_format_rewind(file); + if(flipper_format_read_uint32(file, "uart_nmea_channel", &u, 1)) { + x->uart_nmea_channel = CLAMP(u, UARTCount - 1U, 0U); + } + flipper_format_rewind(file); + if(flipper_format_read_uint32(file, "uart_general_channel", &u, 1)) { + x->uart_general_channel = CLAMP(u, UARTCount - 1U, 0U); + } } flipper_format_free(file); furi_record_close(RECORD_STORAGE); + + rgb_backlight_reconfigure(x->rgb_backlight); } void XTREME_SETTINGS_SAVE() { @@ -180,13 +213,15 @@ void XTREME_SETTINGS_SAVE() { e = x->menu_style; flipper_format_write_uint32(file, "menu_style", &e, 1); flipper_format_write_bool(file, "bad_pins_format", &x->bad_pins_format, 1); - flipper_format_write_bool(file, "allow_locked_rpc_commands", &x->allow_locked_rpc_commands, 1); + flipper_format_write_bool( + file, "allow_locked_rpc_commands", &x->allow_locked_rpc_commands, 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); 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_bool(file, "lockscreen_transparent", &x->lockscreen_transparent, 1); e = x->battery_icon; flipper_format_write_uint32(file, "battery_icon", &e, 1); flipper_format_write_bool(file, "statusbar_clock", &x->statusbar_clock, 1); @@ -203,6 +238,16 @@ void XTREME_SETTINGS_SAVE() { 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); + e = x->spi_cc1101_handle; + flipper_format_write_uint32(file, "spi_cc1101_handle", &e, 1); + e = x->spi_nrf24_handle; + flipper_format_write_uint32(file, "spi_nrf24_handle", &e, 1); + e = x->uart_esp_channel; + flipper_format_write_uint32(file, "uart_esp_channel", &e, 1); + e = x->uart_nmea_channel; + flipper_format_write_uint32(file, "uart_nmea_channel", &e, 1); + e = x->uart_general_channel; + flipper_format_write_uint32(file, "uart_general_channel", &e, 1); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 103ec0443..16b4d57d2 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -28,9 +28,26 @@ typedef enum { typedef enum { MenuStyleList, MenuStyleWii, + MenuStyleDsi, + MenuStylePs4, + MenuStyleVertical, + MenuStyleC64, + MenuStyleEurocorp, MenuStyleCount, } MenuStyle; +typedef enum { + SpiDefault, // cs on pa4 + SpiExtra, // cs on pc3 + SpiCount, +} SpiHandle; + +typedef enum { + UARTDefault, // pin 13,14 + UARTExtra, // pin 15,16 + UARTCount, +} UARTChannel; + typedef struct { bool is_nsfw; // TODO: replace with packs text support @@ -48,6 +65,7 @@ typedef struct { bool lockscreen_date; bool lockscreen_statusbar; bool lockscreen_prompt; + bool lockscreen_transparent; BatteryIcon battery_icon; bool statusbar_clock; bool status_icons; @@ -63,6 +81,11 @@ typedef struct { bool rgb_backlight; uint32_t butthurt_timer; uint32_t charge_cap; + SpiHandle spi_cc1101_handle; + SpiHandle spi_nrf24_handle; + UARTChannel uart_esp_channel; + UARTChannel uart_nmea_channel; + UARTChannel uart_general_channel; } XtremeSettings; void XTREME_SETTINGS_LOAD(); diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 2d0d602c5..87fea3123 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -4,6 +4,13 @@ from dataclasses import dataclass, field from enum import Enum from typing import Callable, ClassVar, List, Optional, Tuple, Union +try: + from fbt.util import resolve_real_dir_node +except ImportError: + # When running outside of SCons, we don't have access to SCons.Node + def resolve_real_dir_node(node): + return node + class FlipperManifestException(Exception): pass @@ -147,7 +154,7 @@ class AppManager: FlipperApplication( *args, **kw, - _appdir=app_dir_node, + _appdir=resolve_real_dir_node(app_dir_node), _apppath=os.path.dirname(app_manifest_path), _appmanager=self, ), diff --git a/scripts/fbt/sdk/cache.py b/scripts/fbt/sdk/cache.py index b6f6edbe5..074cac6b9 100644 --- a/scripts/fbt/sdk/cache.py +++ b/scripts/fbt/sdk/cache.py @@ -237,6 +237,7 @@ class SdkCache: removed_entries = known_set - new_set if removed_entries: print(f"Removed: {removed_entries}") + self.loaded_dirty_version = True known_set -= removed_entries # If any of removed entries was a part of active API, that's a major bump if update_version and any( diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index ae850a8c3..fb36ef55a 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -45,7 +45,7 @@ def single_quote(arg_list): return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) -def extract_abs_dir(node): +def resolve_real_dir_node(node): if isinstance(node, SCons.Node.FS.EntryProxy): node = node.get() @@ -53,13 +53,7 @@ def extract_abs_dir(node): if os.path.exists(repo_dir.abspath): return repo_dir - -def extract_abs_dir_path(node): - abs_dir_node = extract_abs_dir(node) - if abs_dir_node is None: - raise StopError(f"Can't find absolute path for {node.name}") - - return abs_dir_node.abspath + raise StopError(f"Can't find absolute path for {node.name} ({node})") def path_as_posix(path): diff --git a/scripts/fbt_tools/compilation_db.py b/scripts/fbt_tools/compilation_db.py index 17ff6aaa3..1f829ddb4 100644 --- a/scripts/fbt_tools/compilation_db.py +++ b/scripts/fbt_tools/compilation_db.py @@ -38,7 +38,7 @@ from SCons.Tool.cxx import CXXSuffixes from SCons.Tool.cc import CSuffixes from SCons.Tool.asm import ASSuffixes, ASPPSuffixes -# TODO: Is there a better way to do this than this global? Right now this exists so that the +# TODO FL-3542: Is there a better way to do this than this global? Right now this exists so that the # emitter we add can record all of the things it emits, so that the scanner for the top level # compilation database can access the complete list, and also so that the writer has easy # access to write all of the files. But it seems clunky. How can the emitter and the scanner @@ -91,7 +91,7 @@ def make_emit_compilation_DB_entry(comstr): __COMPILATIONDB_ENV=env, ) - # TODO: Technically, these next two lines should not be required: it should be fine to + # TODO FL-3541: Technically, these next two lines should not be required: it should be fine to # cache the entries. However, they don't seem to update properly. Since they are quick # to re-generate disable caching and sidestep this problem. env.AlwaysBuild(entry) diff --git a/scripts/fbt_tools/crosscc.py b/scripts/fbt_tools/crosscc.py index d0631ca33..42fb4ce4b 100644 --- a/scripts/fbt_tools/crosscc.py +++ b/scripts/fbt_tools/crosscc.py @@ -2,6 +2,8 @@ import subprocess import gdb import objdump +import shutil + import strip from SCons.Action import _subproc from SCons.Errors import StopError @@ -11,7 +13,7 @@ from SCons.Tool import ar, asm, gcc, gnulink, gxx def prefix_commands(env, command_prefix, cmd_list): for command in cmd_list: if command in env: - env[command] = command_prefix + env[command] + env[command] = shutil.which(command_prefix + env[command]) def _get_tool_version(env, tool): diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 70b5762ca..7765c716a 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -8,11 +8,14 @@ from SCons.Errors import StopError def icons_emitter(target, source, env): + icons_src = env.GlobRecursive("*.png", env["ICON_SRC_DIR"]) + icons_src += env.GlobRecursive("frame_rate", env["ICON_SRC_DIR"]) + target = [ target[0].File(env.subst("${ICON_FILE_NAME}.c")), target[0].File(env.subst("${ICON_FILE_NAME}.h")), ] - return target, source + return target, icons_src def proto_emitter(target, source, env): @@ -104,21 +107,16 @@ def proto_ver_generator(target, source, env): def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"): - # Gathering icons sources try: os.mkdir(str(source_dir)) except FileExistsError: pass - icons_src = env.GlobRecursive("*.png", source_dir) - icons_src += env.GlobRecursive("frame_rate", source_dir) - - icons = env.IconBuilder( + return env.IconBuilder( target_dir, - source_dir, + None, + ICON_SRC_DIR=source_dir, ICON_FILE_NAME=icon_bundle_name, ) - env.Depends(icons, icons_src) - return icons def generate(env): @@ -141,14 +139,14 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" icons "${ABSPATHGETTERFUNC(SOURCE)}" "${TARGET.dir}" --filename ${ICON_FILE_NAME}', + '${PYTHON3} ${ASSETS_COMPILER} icons ${ICON_SRC_DIR} ${TARGET.dir} --filename "${ICON_FILE_NAME}"', "${ICONSCOMSTR}", ), emitter=icons_emitter, ), "ProtoBuilder": Builder( action=Action( - '${PYTHON3} "${NANOPB_COMPILER}" -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}', + "${PYTHON3} ${NANOPB_COMPILER} -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}", "${PROTOCOMSTR}", ), emitter=proto_emitter, @@ -157,14 +155,14 @@ def generate(env): ), "DolphinSymBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" dolphin -s dolphin_${DOLPHIN_RES_TYPE} "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', + "${PYTHON3} ${ASSETS_COMPILER} dolphin -s dolphin_${DOLPHIN_RES_TYPE} ${SOURCE} ${_DOLPHIN_OUT_DIR}", "${DOLPHINCOMSTR}", ), emitter=dolphin_emitter, ), "DolphinExtBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" dolphin "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', + "${PYTHON3} ${ASSETS_COMPILER} dolphin ${SOURCE} ${_DOLPHIN_OUT_DIR}", "${DOLPHINCOMSTR}", ), emitter=dolphin_emitter, diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index d46ecd8f3..392465a51 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -21,7 +21,7 @@ def generate(env, **kw): FBT_DEBUG_DIR="${FBT_SCRIPT_DIR}/debug", ) - if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto": + if (adapter_serial := env.subst("$SWD_TRANSPORT_SERIAL")) != "auto": env.Append( OPENOCD_OPTS=[ "-c", diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index 105f501aa..3645008c5 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -52,22 +52,16 @@ def AddFwProject(env, base_env, fw_type, fw_env_key): return project_env -def AddOpenOCDFlashTarget(env, targetenv, **kw): - openocd_target = env.OpenOCDFlash( - "#build/oocd-${BUILD_CFG}-flash.flag", - targetenv["FW_BIN"], - OPENOCD_COMMAND=[ - "-c", - "program ${SOURCE.posix} reset exit ${BASE_ADDRESS}", - ], - BUILD_CFG=targetenv.subst("$FIRMWARE_BUILD_CFG"), - BASE_ADDRESS=targetenv.subst("$IMAGE_BASE_ADDRESS"), +def AddFwFlashTarget(env, targetenv, **kw): + fwflash_target = env.FwFlash( + "#build/flash.flag", + targetenv["FW_ELF"], **kw, ) - env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target) + env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), fwflash_target) if env["FORCE"]: - env.AlwaysBuild(openocd_target) - return openocd_target + env.AlwaysBuild(fwflash_target) + return fwflash_target def AddJFlashTarget(env, targetenv, **kw): @@ -115,7 +109,7 @@ def generate(env): env.SetDefault(COPROCOMSTR="\tCOPRO\t${TARGET}") env.AddMethod(AddFwProject) env.AddMethod(DistCommand) - env.AddMethod(AddOpenOCDFlashTarget) + env.AddMethod(AddFwFlashTarget) env.AddMethod(GetProjectDirName) env.AddMethod(AddJFlashTarget) env.AddMethod(AddUsbFlashTarget) @@ -125,30 +119,53 @@ def generate(env): SELFUPDATE_SCRIPT="${FBT_SCRIPT_DIR}/selfupdate.py", DIST_SCRIPT="${FBT_SCRIPT_DIR}/sconsdist.py", COPRO_ASSETS_SCRIPT="${FBT_SCRIPT_DIR}/assets.py", + FW_FLASH_SCRIPT="${FBT_SCRIPT_DIR}/fwflash.py", ) env.Append( BUILDERS={ + "FwFlash": Builder( + action=[ + [ + "${PYTHON3}", + "${FW_FLASH_SCRIPT}", + "-d" if env["VERBOSE"] else "", + "--interface=${SWD_TRANSPORT}", + "--serial=${SWD_TRANSPORT_SERIAL}", + "${SOURCE}", + ], + Touch("${TARGET}"), + ] + ), "UsbInstall": Builder( action=[ - Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" -p ${FLIP_PORT} ${UPDATE_BUNDLE_DIR}/update.fuf' - ), + [ + "${PYTHON3}", + "${SELFUPDATE_SCRIPT}", + "-p", + "${FLIP_PORT}", + "${UPDATE_BUNDLE_DIR}/update.fuf", + ], Touch("${TARGET}"), ] ), "CoproBuilder": Builder( action=Action( [ - '${PYTHON3} "${COPRO_ASSETS_SCRIPT}" ' - "copro ${COPRO_CUBE_DIR} " - "${TARGET} ${COPRO_MCU_FAMILY} " - "--cube_ver=${COPRO_CUBE_VERSION} " - "--stack_type=${COPRO_STACK_TYPE} " - '--stack_file="${COPRO_STACK_BIN}" ' - "--stack_addr=${COPRO_STACK_ADDR} ", + [ + "${PYTHON3}", + "${COPRO_ASSETS_SCRIPT}", + "copro", + "${COPRO_CUBE_DIR}", + "${TARGET}", + "${COPRO_MCU_FAMILY}", + "--cube_ver=${COPRO_CUBE_VERSION}", + "--stack_type=${COPRO_STACK_TYPE}", + "--stack_file=${COPRO_STACK_BIN}", + "--stack_addr=${COPRO_STACK_ADDR}", + ] ], - "$COPROCOMSTR", + "${COPROCOMSTR}", ) ), } diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 51965681c..6059628f0 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -11,7 +11,7 @@ from fbt.appmanifest import FlipperApplication, FlipperAppType, FlipperManifestE from fbt.elfmanifest import assemble_manifest_data from fbt.fapassets import FileBundler from fbt.sdk.cache import SdkCache -from fbt.util import extract_abs_dir_path +from fbt.util import resolve_real_dir_node from SCons.Action import Action from SCons.Builder import Builder from SCons.Errors import UserError @@ -50,7 +50,13 @@ class AppBuilder: def _setup_app_env(self): self.app_env = self.fw_env.Clone( - FAP_SRC_DIR=self.app._appdir, FAP_WORK_DIR=self.app_work_dir + FAP_SRC_DIR=self.app._appdir, + FAP_WORK_DIR=self.app_work_dir, + ) + self.app_env.Append( + CPPDEFINES=[ + ("FAP_VERSION", f'"{".".join(map(str, self.app.fap_version))}"') + ], ) self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False) @@ -119,7 +125,7 @@ class AppBuilder: CPPDEFINES=lib_def.cdefines, CPPPATH=list( map( - lambda cpath: extract_abs_dir_path(self.app._appdir.Dir(cpath)), + lambda cpath: resolve_real_dir_node(self.app._appdir.Dir(cpath)), lib_def.cincludes, ) ), @@ -133,7 +139,7 @@ class AppBuilder: def _build_app(self): self.app_env.Append( LIBS=[*self.app.fap_libs, *self.private_libs], - CPPPATH=self.app_env.Dir(self.app_work_dir), + CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir], ) app_sources = list( @@ -176,13 +182,7 @@ class AppBuilder: deployable = False app_artifacts.dist_entries.append((deployable, fal_path)) else: - category = ( - "assets" - if self.app.apptype == FlipperAppType.MENUEXTERNAL - and not self.app.fap_category - else self.app.fap_category - ) - fap_path = f"apps/{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/scripts/fbt_tools/fbt_help.py b/scripts/fbt_tools/fbt_help.py index e9364a42b..701435639 100644 --- a/scripts/fbt_tools/fbt_help.py +++ b/scripts/fbt_tools/fbt_help.py @@ -16,8 +16,8 @@ Firmware & apps: Flashing & debugging: - flash, flash_blackmagic, jflash: - Flash firmware to target using debug probe + flash, jflash: + Flash firmware to target using SWD probe. See also SWD_TRANSPORT, SWD_TRANSPORT_SERIAL flash_usb, flash_usb_full: Install firmware using self-update package debug, debug_other, blackmagic: diff --git a/scripts/flipper/app.py b/scripts/flipper/app.py index 405c4c399..da43a1f11 100644 --- a/scripts/flipper/app.py +++ b/scripts/flipper/app.py @@ -15,10 +15,9 @@ class App: # Application specific initialization self.init() - def __call__(self, args=None, skip_logger_init=False): + def __call__(self, args=None): self.args, self.other_args = self.parser.parse_known_args(args=args) # configure log output - # if skip_logger_init: self.log_level = logging.DEBUG if self.args.debug else logging.INFO self.logger.setLevel(self.log_level) if not self.logger.hasHandlers(): diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 2c9c043d5..40af5cebc 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -150,7 +150,7 @@ class FlipperStorage: for line in lines: try: - # TODO: better decoding, considering non-ascii characters + # TODO FL-3539: better decoding, considering non-ascii characters line = line.decode("ascii") except Exception: continue @@ -193,7 +193,7 @@ class FlipperStorage: for line in lines: try: - # TODO: better decoding, considering non-ascii characters + # TODO FL-3539: better decoding, considering non-ascii characters line = line.decode("ascii") except Exception: continue diff --git a/scripts/flipper/utils/openocd.py b/scripts/flipper/utils/openocd.py index 1309055b8..a43568090 100644 --- a/scripts/flipper/utils/openocd.py +++ b/scripts/flipper/utils/openocd.py @@ -78,7 +78,7 @@ class OpenOCD: def _wait_for_openocd_tcl(self): """Wait for OpenOCD to start""" - # TODO: timeout + # TODO Fl-3538: timeout while True: stderr = self.process.stderr if not stderr: @@ -128,7 +128,7 @@ class OpenOCD: def _recv(self): """Read from the stream until the token (\x1a) was received.""" - # TODO: timeout + # TODO FL-3538: timeout data = bytes() while True: chunk = self.socket.recv(4096) diff --git a/scripts/flipper/utils/programmer.py b/scripts/flipper/utils/programmer.py index 84452d154..938065f7c 100644 --- a/scripts/flipper/utils/programmer.py +++ b/scripts/flipper/utils/programmer.py @@ -26,6 +26,10 @@ class Programmer(ABC): def option_bytes_set(self, file_path: str) -> bool: pass + @abstractmethod + def option_bytes_recover(self) -> bool: + pass + @abstractmethod def otp_write(self, address: int, file_path: str) -> bool: pass diff --git a/scripts/flipper/utils/programmer_openocd.py b/scripts/flipper/utils/programmer_openocd.py index 5a8029f37..77335ee5b 100644 --- a/scripts/flipper/utils/programmer_openocd.py +++ b/scripts/flipper/utils/programmer_openocd.py @@ -44,11 +44,11 @@ class OpenOCDProgrammer(Programmer): self.logger = logging.getLogger() def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool: - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) if mode == Programmer.RunMode.Run: - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) elif mode == Programmer.RunMode.Stop: - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) else: raise Exception("Unknown mode") @@ -96,11 +96,11 @@ class OpenOCDProgrammer(Programmer): def option_bytes_validate(self, file_path: str) -> bool: # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) # OpenOCD self.openocd.start() - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) # Generate Option Bytes data ob_data = OptionBytesData(file_path) @@ -133,7 +133,7 @@ class OpenOCDProgrammer(Programmer): self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error) # Stop OpenOCD - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) self.openocd.stop() return return_code @@ -143,11 +143,11 @@ class OpenOCDProgrammer(Programmer): def option_bytes_set(self, file_path: str) -> bool: # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) # OpenOCD self.openocd.start() - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) # Generate Option Bytes data ob_data = OptionBytesData(file_path) @@ -159,11 +159,11 @@ class OpenOCDProgrammer(Programmer): ob_dwords = int(ob_length / 8) # Clear flash errors - stm32.clear_flash_errors(self.openocd) + stm32.clear_flash_errors() # Unlock Flash and Option Bytes - stm32.flash_unlock(self.openocd) - stm32.option_bytes_unlock(self.openocd) + stm32.flash_unlock() + stm32.option_bytes_unlock() ob_need_to_apply = False @@ -194,16 +194,16 @@ class OpenOCDProgrammer(Programmer): self.openocd.write_32(device_reg_addr, ob_value) if ob_need_to_apply: - stm32.option_bytes_apply(self.openocd) + stm32.option_bytes_apply() else: self.logger.info("Option Bytes are already correct") # Load Option Bytes # That will reset and also lock the Option Bytes and the Flash - stm32.option_bytes_load(self.openocd) + stm32.option_bytes_load() # Stop OpenOCD - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) self.openocd.stop() return True @@ -233,11 +233,10 @@ class OpenOCDProgrammer(Programmer): self.logger.debug(f"Data: {data.hex().upper()}") # Start OpenOCD - oocd = self.openocd - oocd.start() + self.openocd.start() # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) try: # Check that OTP is empty for the given address @@ -245,7 +244,7 @@ class OpenOCDProgrammer(Programmer): already_written = True for i in range(0, data_size, 4): file_word = int.from_bytes(data[i : i + 4], "little") - device_word = oocd.read_32(address + i) + device_word = self.openocd.read_32(address + i) if device_word != 0xFFFFFFFF and device_word != file_word: self.logger.error( f"OTP memory at {address + i:08X} is not empty: {device_word:08X}" @@ -260,7 +259,7 @@ class OpenOCDProgrammer(Programmer): return OpenOCDProgrammerResult.Success self.reset(self.RunMode.Stop) - stm32.clear_flash_errors(oocd) + stm32.clear_flash_errors() # Write OTP memory by 8 bytes for i in range(0, data_size, 8): @@ -269,14 +268,14 @@ class OpenOCDProgrammer(Programmer): self.logger.debug( f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}" ) - stm32.write_flash_64(oocd, address + i, word_1, word_2) + stm32.write_flash_64(address + i, word_1, word_2) # Validate OTP memory validation_result = True for i in range(0, data_size, 4): file_word = int.from_bytes(data[i : i + 4], "little") - device_word = oocd.read_32(address + i) + device_word = self.openocd.read_32(address + i) if file_word != device_word: self.logger.error( f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}" @@ -284,11 +283,21 @@ class OpenOCDProgrammer(Programmer): validation_result = False finally: # Stop OpenOCD - stm32.reset(oocd, stm32.RunMode.Run) - oocd.stop() + stm32.reset(stm32.RunMode.Run) + self.openocd.stop() return ( OpenOCDProgrammerResult.Success if validation_result else OpenOCDProgrammerResult.ErrorValidation ) + + def option_bytes_recover(self) -> bool: + try: + self.openocd.start() + stm32 = STM32WB55(self.openocd) + stm32.reset(stm32.RunMode.Halt) + stm32.option_bytes_recover() + return True + finally: + self.openocd.stop() diff --git a/scripts/flipper/utils/register.py b/scripts/flipper/utils/register.py index 26d66730c..aad75eaca 100644 --- a/scripts/flipper/utils/register.py +++ b/scripts/flipper/utils/register.py @@ -16,6 +16,7 @@ class Register32: self.names = [definition.name for definition in definition_list] # typecheck self.address = address self.definition_list = definition_list + self.openocd = None # Validate that the definitions are not overlapping for i in range(len(definition_list)): @@ -76,6 +77,14 @@ class Register32: def __dir__(self): return self.names + def set_openocd(self, openocd: OpenOCD): + self.openocd = openocd + + def get_openocd(self) -> OpenOCD: + if self.openocd is None: + raise RuntimeError("OpenOCD is not installed") + return self.openocd + def set(self, value: int): for definition in self.definition_list: definition.value = (value >> definition.offset) & ( @@ -88,8 +97,8 @@ class Register32: value |= definition.value << definition.offset return value - def load(self, openocd: OpenOCD): - self.set(openocd.read_32(self.address)) + def load(self): + self.set(self.get_openocd().read_32(self.address)) - def store(self, openocd: OpenOCD): - openocd.write_32(self.address, self.get()) + def store(self): + self.get_openocd().write_32(self.address, self.get()) diff --git a/scripts/flipper/utils/stm32wb55.py b/scripts/flipper/utils/stm32wb55.py index 52a5ec4e3..9ea803220 100644 --- a/scripts/flipper/utils/stm32wb55.py +++ b/scripts/flipper/utils/stm32wb55.py @@ -108,23 +108,27 @@ class STM32WB55: 15: None, # Core 2 Options } - def __init__(self): + def __init__(self, openocd: OpenOCD): + self.openocd = openocd self.logger = logging.getLogger("STM32WB55") + self.FLASH_CR.set_openocd(self.openocd) + self.FLASH_SR.set_openocd(self.openocd) + class RunMode(Enum): Init = "init" Run = "run" Halt = "halt" - def reset(self, oocd: OpenOCD, mode: RunMode): + def reset(self, mode: RunMode): self.logger.debug("Resetting device") - oocd.send_tcl(f"reset {mode.value}") + self.openocd.send_tcl(f"reset {mode.value}") - def clear_flash_errors(self, oocd: OpenOCD): + def clear_flash_errors(self): # Errata 2.2.9: Flash OPTVERR flag is always set after system reset # And also clear all other flash error flags self.logger.debug("Resetting flash errors") - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() self.FLASH_SR.OP_ERR = 1 self.FLASH_SR.PROG_ERR = 1 self.FLASH_SR.WRP_ERR = 1 @@ -135,51 +139,51 @@ class STM32WB55: self.FLASH_SR.FAST_ERR = 1 self.FLASH_SR.RD_ERR = 1 self.FLASH_SR.OPTV_ERR = 1 - self.FLASH_SR.store(oocd) + self.FLASH_SR.store() - def flash_unlock(self, oocd: OpenOCD): + def flash_unlock(self): # Check if flash is already unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 0: self.logger.debug("Flash is already unlocked") return # Unlock flash self.logger.debug("Unlocking Flash") - oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) - oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) + self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) + self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) # Check if flash is unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 0: self.logger.debug("Flash unlocked") else: self.logger.error("Flash unlock failed") raise Exception("Flash unlock failed") - def option_bytes_unlock(self, oocd: OpenOCD): + def option_bytes_unlock(self): # Check if options is already unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 0: self.logger.debug("Options is already unlocked") return # Unlock options self.logger.debug("Unlocking Options") - oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) - oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) + self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) + self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) # Check if options is unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 0: self.logger.debug("Options unlocked") else: self.logger.error("Options unlock failed") raise Exception("Options unlock failed") - def option_bytes_lock(self, oocd: OpenOCD): + def option_bytes_lock(self): # Check if options is already locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 1: self.logger.debug("Options is already locked") return @@ -187,19 +191,19 @@ class STM32WB55: # Lock options self.logger.debug("Locking Options") self.FLASH_CR.OPT_LOCK = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check if options is locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 1: self.logger.debug("Options locked") else: self.logger.error("Options lock failed") raise Exception("Options lock failed") - def flash_lock(self, oocd: OpenOCD): + def flash_lock(self): # Check if flash is already locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 1: self.logger.debug("Flash is already locked") return @@ -207,31 +211,31 @@ class STM32WB55: # Lock flash self.logger.debug("Locking Flash") self.FLASH_CR.LOCK = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check if flash is locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 1: self.logger.debug("Flash locked") else: self.logger.error("Flash lock failed") raise Exception("Flash lock failed") - def option_bytes_apply(self, oocd: OpenOCD): + def option_bytes_apply(self): self.logger.debug("Applying Option Bytes") - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.OPT_STRT = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Wait for Option Bytes to be applied - self.flash_wait_for_operation(oocd) + self.flash_wait_for_operation() - def option_bytes_load(self, oocd: OpenOCD): + def option_bytes_load(self): self.logger.debug("Loading Option Bytes") - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.OBL_LAUNCH = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() def option_bytes_id_to_address(self, id: int) -> int: # Check if this option byte (dword) is mapped to a register @@ -241,16 +245,16 @@ class STM32WB55: return device_reg_addr - def flash_wait_for_operation(self, oocd: OpenOCD): + def flash_wait_for_operation(self): # Wait for flash operation to complete - # TODO: timeout + # TODO FL-3537: timeout while True: - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if self.FLASH_SR.BSY == 0: break - def flash_dump_status_register(self, oocd: OpenOCD): - self.FLASH_SR.load(oocd) + def flash_dump_status_register(self): + self.FLASH_SR.load() self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}") if self.FLASH_SR.EOP: self.logger.info(" End of operation") @@ -283,70 +287,87 @@ class STM32WB55: if self.FLASH_SR.PESD: self.logger.info(" Programming / erase operation suspended.") - def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int): + def write_flash_64(self, address: int, word_1: int, word_2: int): self.logger.debug(f"Writing flash at address {address:08x}") if address % 8 != 0: self.logger.error("Address must be aligned to 8 bytes") raise Exception("Address must be aligned to 8 bytes") - if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4): + if word_1 == self.openocd.read_32(address) and word_2 == self.openocd.read_32( + address + 4 + ): self.logger.debug("Data is already programmed") return - self.flash_unlock(oocd) + self.flash_unlock() # Check that no flash main memory operation is ongoing by checking the BSY bit - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if self.FLASH_SR.BSY: self.logger.error("Flash is busy") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash is busy") # Enable end of operation interrupts and error interrupts - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.EOPIE = 1 self.FLASH_CR.ERRIE = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check that flash memory program and erase operations are allowed if self.FLASH_SR.PESD: self.logger.error("Flash operations are not allowed") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash operations are not allowed") # Check and clear all error programming flags due to a previous programming. - self.clear_flash_errors(oocd) + self.clear_flash_errors() # Set the PG bit in the Flash memory control register (FLASH_CR) - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.PG = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed. # Write the first word - oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") + self.openocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") # Write the second word - oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") + self.openocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") # Wait for the BSY bit to be cleared - self.flash_wait_for_operation(oocd) + self.flash_wait_for_operation() # Check that EOP flag is set in the FLASH_SR register - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if not self.FLASH_SR.EOP: self.logger.error("Flash operation failed") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash operation failed") # Clear the EOP flag - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() self.FLASH_SR.EOP = 1 - self.FLASH_SR.store(oocd) + self.FLASH_SR.store() # Clear the PG bit in the FLASH_CR register - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.PG = 0 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() - self.flash_lock(oocd) + self.flash_lock() + + def option_bytes_recover(self): + self.openocd.send_tcl("mww 0x58004010 0x8000") # set OPTVERR to reset + # Replace flash_unlock and option_bytes_unlock with the following lines, if this does not work + # self.openocd.send_tcl("mww 0x58004008 0x45670123") # unlock FLASH + # self.openocd.send_tcl("mww 0x58004008 0xCDEF89AB") + # self.openocd.send_tcl("mww 0x5800400c 0x08192A3B") # unlock OB + # self.openocd.send_tcl("mww 0x5800400c 0x4C5D6E7F") + self.flash_unlock() + self.option_bytes_unlock() + self.openocd.send_tcl("mmw 0x58004020 0x3ffff1aa 0xffffffff") # Reset OB + self.openocd.send_tcl("mww 0x5800402c 0xff") # Reset WRP1AR + self.openocd.send_tcl("mww 0x58004030 0xff") # Reset WRP1BR + self.openocd.send_tcl("mmw 0x58004014 0x00020000 0") # OPTSTRT + self.openocd.send_tcl("mmw 0x58004014 0x08000000 0") # OBL_LAUNCH diff --git a/scripts/program.py b/scripts/fwflash.py similarity index 54% rename from scripts/program.py rename to scripts/fwflash.py index f3e7e3e2d..c119aaf80 100755 --- a/scripts/program.py +++ b/scripts/fwflash.py @@ -1,19 +1,24 @@ #!/usr/bin/env python3 import logging import os +import re import socket import subprocess import time import typing from abc import ABC, abstractmethod -from dataclasses import dataclass +from dataclasses import dataclass, field from flipper.app import App +# When adding an interface, also add it to SWD_TRANSPORT in fbt/ufbt options + class Programmer(ABC): + root_logger = logging.getLogger("Programmer") + @abstractmethod - def flash(self, bin: str) -> bool: + def flash(self, file_path: str, do_verify: bool) -> bool: pass @abstractmethod @@ -28,28 +33,46 @@ class Programmer(ABC): def set_serial(self, serial: str): pass + @classmethod + def _spawn_and_await(cls, process_params, show_progress: bool = False): + cls.root_logger.debug(f"Launching: {' '.join(process_params)}") + + process = subprocess.Popen( + process_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + + if show_progress: + while process.poll() is None: + time.sleep(0.25) + print(".", end="", flush=True) + print() + else: + process.wait() + + return process + @dataclass class OpenOCDInterface: name: str - file: str + config_file: str serial_cmd: str - additional_args: typing.Optional[list[str]] = None + additional_args: typing.Optional[list[str]] = field(default_factory=list) class OpenOCDProgrammer(Programmer): def __init__(self, interface: OpenOCDInterface): self.interface = interface - self.logger = logging.getLogger("OpenOCD") + self.logger = self.root_logger.getChild("OpenOCD") self.serial: typing.Optional[str] = None def _add_file(self, params: list[str], file: str): - params.append("-f") - params.append(file) + params += ["-f", file] def _add_command(self, params: list[str], command: str): - params.append("-c") - params.append(command) + params += ["-c", command] def _add_serial(self, params: list[str], serial: str): self._add_command(params, f"{self.interface.serial_cmd} {serial}") @@ -57,22 +80,27 @@ class OpenOCDProgrammer(Programmer): def set_serial(self, serial: str): self.serial = serial - def flash(self, bin: str) -> bool: - i = self.interface - + def flash(self, file_path: str, do_verify: bool) -> bool: if os.altsep: - bin = bin.replace(os.sep, os.altsep) + file_path = file_path.replace(os.sep, os.altsep) openocd_launch_params = ["openocd"] - self._add_file(openocd_launch_params, i.file) + self._add_file(openocd_launch_params, self.interface.config_file) if self.serial: self._add_serial(openocd_launch_params, self.serial) - if i.additional_args: - for a in i.additional_args: - self._add_command(openocd_launch_params, a) + for additional_arg in self.interface.additional_args: + self._add_command(openocd_launch_params, additional_arg) self._add_file(openocd_launch_params, "target/stm32wbx.cfg") self._add_command(openocd_launch_params, "init") - self._add_command(openocd_launch_params, f"program {bin} reset exit 0x8000000") + program_params = [ + "program", + f'"{file_path}"', + "verify" if do_verify else "", + "reset", + "exit", + "0x8000000" if file_path.endswith(".bin") else "", + ] + self._add_command(openocd_launch_params, " ".join(program_params)) # join the list of parameters into a string, but add quote if there are spaces openocd_launch_params_string = " ".join( @@ -81,17 +109,7 @@ class OpenOCDProgrammer(Programmer): self.logger.debug(f"Launching: {openocd_launch_params_string}") - process = subprocess.Popen( - openocd_launch_params, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - - while process.poll() is None: - time.sleep(0.25) - print(".", end="", flush=True) - print() - + process = self._spawn_and_await(openocd_launch_params, True) success = process.returncode == 0 if not success: @@ -102,35 +120,41 @@ class OpenOCDProgrammer(Programmer): return success def probe(self) -> bool: - i = self.interface - openocd_launch_params = ["openocd"] - self._add_file(openocd_launch_params, i.file) + self._add_file(openocd_launch_params, self.interface.config_file) if self.serial: self._add_serial(openocd_launch_params, self.serial) - if i.additional_args: - for a in i.additional_args: - self._add_command(openocd_launch_params, a) + for additional_arg in self.interface.additional_args: + self._add_command(openocd_launch_params, additional_arg) self._add_file(openocd_launch_params, "target/stm32wbx.cfg") self._add_command(openocd_launch_params, "init") self._add_command(openocd_launch_params, "exit") - self.logger.debug(f"Launching: {' '.join(openocd_launch_params)}") + process = self._spawn_and_await(openocd_launch_params) + success = process.returncode == 0 - process = subprocess.Popen( - openocd_launch_params, - stderr=subprocess.STDOUT, - stdout=subprocess.PIPE, - ) + output = process.stdout.read().decode("utf-8").strip() if process.stdout else "" + self.logger.debug(output) + # Find target voltage using regex + if match := re.search(r"Target voltage: (\d+\.\d+)", output): + voltage = float(match.group(1)) + if not success: + if voltage < 1: + self.logger.warning( + f"Found {self.get_name()}, but device is not connected" + ) + else: + self.logger.warning( + f"Device is connected, but {self.get_name()} failed to attach. Is System>Debug enabled?" + ) - # Wait for OpenOCD to end and get the return code - process.wait() - found = process.returncode == 0 + if "cannot read IDR" in output: + self.logger.warning( + f"Found {self.get_name()}, but failed to attach. Is device connected and is System>Debug enabled?" + ) + success = False - if process.stdout: - self.logger.debug(process.stdout.read().decode("utf-8").strip()) - - return found + return success def get_name(self) -> str: return self.interface.name @@ -187,7 +211,7 @@ def _resolve_hostname(hostname): def blackmagic_find_networked(serial: str): - if not serial: + if not serial or serial == "auto": serial = "blackmagic.local" # remove the tcp: prefix if it's there @@ -212,7 +236,7 @@ class BlackmagicProgrammer(Programmer): ): self.port_resolver = port_resolver self.name = name - self.logger = logging.getLogger("BlackmagicUSB") + self.logger = self.root_logger.getChild(f"Blackmagic{name}") self.port: typing.Optional[str] = None def _add_command(self, params: list[str], command: str): @@ -234,7 +258,15 @@ class BlackmagicProgrammer(Programmer): else: self.port = serial - def flash(self, bin: str) -> bool: + def _get_gdb_core_params(self) -> list[str]: + gdb_launch_params = ["arm-none-eabi-gdb"] + self._add_command(gdb_launch_params, f"target extended-remote {self.port}") + self._add_command(gdb_launch_params, "set pagination off") + self._add_command(gdb_launch_params, "set confirm off") + self._add_command(gdb_launch_params, "monitor swdp_scan") + return gdb_launch_params + + def flash(self, file_path: str, do_verify: bool) -> bool: if not self.port: if not self.probe(): return False @@ -242,12 +274,14 @@ class BlackmagicProgrammer(Programmer): # We can convert .bin to .elf with objcopy: # arm-none-eabi-objcopy -I binary -O elf32-littlearm --change-section-address=.data=0x8000000 -B arm -S app.bin app.elf # But I choose to use the .elf file directly because we are flashing our own firmware and it always has an elf predecessor. - elf = bin.replace(".bin", ".elf") - if not os.path.exists(elf): - self.logger.error( - f"Sorry, but Blackmagic can't flash .bin file, and {elf} doesn't exist" - ) - return False + + if file_path.endswith(".bin"): + file_path = file_path[:-4] + ".elf" + if not os.path.exists(file_path): + self.logger.error( + f"Sorry, but Blackmagic can't flash .bin file, and {file_path} doesn't exist" + ) + return False # arm-none-eabi-gdb build/f7-firmware-D/firmware.bin # -ex 'set pagination off' @@ -260,42 +294,25 @@ class BlackmagicProgrammer(Programmer): # -ex 'compare-sections' # -ex 'quit' - gdb_launch_params = ["arm-none-eabi-gdb", elf] - self._add_command(gdb_launch_params, f"target extended-remote {self.port}") - self._add_command(gdb_launch_params, "set pagination off") - self._add_command(gdb_launch_params, "set confirm off") - self._add_command(gdb_launch_params, "monitor swdp_scan") + gdb_launch_params = self._get_gdb_core_params() self._add_command(gdb_launch_params, "attach 1") self._add_command(gdb_launch_params, "set mem inaccessible-by-default off") self._add_command(gdb_launch_params, "load") - self._add_command(gdb_launch_params, "compare-sections") + if do_verify: + self._add_command(gdb_launch_params, "compare-sections") self._add_command(gdb_launch_params, "quit") + gdb_launch_params.append(file_path) - self.logger.debug(f"Launching: {' '.join(gdb_launch_params)}") - - process = subprocess.Popen( - gdb_launch_params, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - - while process.poll() is None: - time.sleep(0.5) - print(".", end="", flush=True) - print() - + process = self._spawn_and_await(gdb_launch_params, True) if not process.stdout: return False output = process.stdout.read().decode("utf-8").strip() - flashed = "Loading section .text," in output - - # Check flash verification - if "MIS-MATCHED!" in output: - flashed = False - - if "target image does not match the loaded file" in output: - flashed = False + flashed = ( + "Loading section .text," in output + and "MIS-MATCHED!" not in output + and "target image does not match the loaded file" not in output + ) if not flashed: self.logger.error("Blackmagic failed to flash") @@ -308,13 +325,29 @@ class BlackmagicProgrammer(Programmer): return False self.port = port + + gdb_launch_params = self._get_gdb_core_params() + self._add_command(gdb_launch_params, "quit") + + process = self._spawn_and_await(gdb_launch_params) + if not process.stdout or process.returncode != 0: + return False + + output = process.stdout.read().decode("utf-8").strip() + if "SW-DP scan failed!" in output: + self.logger.warning( + f"Found {self.get_name()} at {self.port}, but failed to attach. Is device connected and is System>Debug enabled?" + ) + return False return True def get_name(self) -> str: return self.name -programmers: list[Programmer] = [ +#################### + +local_flash_interfaces: list[Programmer] = [ OpenOCDProgrammer( OpenOCDInterface( "cmsis-dap", @@ -325,47 +358,66 @@ programmers: list[Programmer] = [ ), OpenOCDProgrammer( OpenOCDInterface( - "stlink", "interface/stlink.cfg", "hla_serial", ["transport select hla_swd"] + "stlink", + "interface/stlink.cfg", + "hla_serial", + ["transport select hla_swd"], ), ), BlackmagicProgrammer(blackmagic_find_serial, "blackmagic_usb"), ] -network_programmers = [ +network_flash_interfaces: list[Programmer] = [ BlackmagicProgrammer(blackmagic_find_networked, "blackmagic_wifi") ] +all_flash_interfaces = [*local_flash_interfaces, *network_flash_interfaces] + +#################### + class Main(App): + AUTO_INTERFACE = "auto" + def init(self): - self.subparsers = self.parser.add_subparsers(help="sub-command help") - self.parser_flash = self.subparsers.add_parser("flash", help="Flash a binary") - self.parser_flash.add_argument( - "bin", + Programmer.root_logger = self.logger + + self.parser.add_argument( + "filename", type=str, - help="Binary to flash", + help="File to flash", ) - interfaces = [i.get_name() for i in programmers] - interfaces.extend([i.get_name() for i in network_programmers]) - self.parser_flash.add_argument( + self.parser.add_argument( + "--verify", + "-v", + action="store_true", + help="Verify flash after programming", + default=False, + ) + self.parser.add_argument( "--interface", - choices=interfaces, + choices=( + self.AUTO_INTERFACE, + *[i.get_name() for i in all_flash_interfaces], + ), type=str, + default=self.AUTO_INTERFACE, help="Interface to use", ) - self.parser_flash.add_argument( + self.parser.add_argument( "--serial", type=str, + default=self.AUTO_INTERFACE, help="Serial number or port of the programmer", ) - self.parser_flash.set_defaults(func=self.flash) + self.parser.set_defaults(func=self.flash) - def _search_interface(self, serial: typing.Optional[str]) -> list[Programmer]: + def _search_interface(self, interface_list: list[Programmer]) -> list[Programmer]: found_programmers = [] - for p in programmers: + for p in interface_list: name = p.get_name() - if serial: + if (serial := self.args.serial) != self.AUTO_INTERFACE: p.set_serial(serial) self.logger.debug(f"Trying {name} with {serial}") else: @@ -373,29 +425,7 @@ class Main(App): if p.probe(): self.logger.debug(f"Found {name}") - found_programmers += [p] - else: - self.logger.debug(f"Failed to probe {name}") - - return found_programmers - - def _search_network_interface( - self, serial: typing.Optional[str] - ) -> list[Programmer]: - found_programmers = [] - - for p in network_programmers: - name = p.get_name() - - if serial: - p.set_serial(serial) - self.logger.debug(f"Trying {name} with {serial}") - else: - self.logger.debug(f"Trying {name}") - - if p.probe(): - self.logger.debug(f"Found {name}") - found_programmers += [p] + found_programmers.append(p) else: self.logger.debug(f"Failed to probe {name}") @@ -403,55 +433,59 @@ class Main(App): def flash(self): start_time = time.time() - bin_path = os.path.abspath(self.args.bin) + file_path = os.path.abspath(self.args.filename) - if not os.path.exists(bin_path): - self.logger.error(f"Binary file not found: {bin_path}") + if not os.path.exists(file_path): + self.logger.error(f"Binary file not found: {file_path}") return 1 - if self.args.interface: - i_name = self.args.interface - interfaces = [p for p in programmers if p.get_name() == i_name] - if len(interfaces) == 0: - interfaces = [p for p in network_programmers if p.get_name() == i_name] - else: - self.logger.info("Probing for interfaces...") - interfaces = self._search_interface(self.args.serial) + if self.args.interface != self.AUTO_INTERFACE: + available_interfaces = list( + filter( + lambda p: p.get_name() == self.args.interface, + all_flash_interfaces, + ) + ) - if len(interfaces) == 0: + else: + self.logger.info("Probing for local interfaces...") + available_interfaces = self._search_interface(local_flash_interfaces) + + if not available_interfaces: # Probe network blackmagic self.logger.info("Probing for network interfaces...") - interfaces = self._search_network_interface(self.args.serial) + available_interfaces = self._search_interface(network_flash_interfaces) - if len(interfaces) == 0: - self.logger.error("No interface found") + if not available_interfaces: + self.logger.error("No availiable interfaces") return 1 - - if len(interfaces) > 1: - self.logger.error("Multiple interfaces found: ") + elif len(available_interfaces) > 1: + self.logger.error("Multiple interfaces found:") self.logger.error( - f"Please specify '--interface={[i.get_name() for i in interfaces]}'" + f"Please specify '--interface={[i.get_name() for i in available_interfaces]}'" ) return 1 - interface = interfaces[0] + interface = available_interfaces.pop(0) - if self.args.serial: + if self.args.serial != self.AUTO_INTERFACE: interface.set_serial(self.args.serial) - self.logger.info( - f"Flashing {bin_path} via {interface.get_name()} with {self.args.serial}" - ) + self.logger.info(f"Using {interface.get_name()} with {self.args.serial}") else: - self.logger.info(f"Flashing {bin_path} via {interface.get_name()}") + self.logger.info(f"Using {interface.get_name()}") + self.logger.info(f"Flashing {file_path}") - if not interface.flash(bin_path): + if not interface.flash(file_path, self.args.verify): self.logger.error(f"Failed to flash via {interface.get_name()}") return 1 flash_time = time.time() - start_time - bin_size = os.path.getsize(bin_path) self.logger.info(f"Flashed successfully in {flash_time:.2f}s") - self.logger.info(f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s") + if file_path.endswith(".bin"): + bin_size = os.path.getsize(file_path) + self.logger.info( + f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s" + ) return 0 diff --git a/scripts/ob.py b/scripts/ob.py index 7010bdec5..b7a601612 100755 --- a/scripts/ob.py +++ b/scripts/ob.py @@ -22,6 +22,12 @@ class Main(App): self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes") self._add_args(self.parser_set) self.parser_set.set_defaults(func=self.set) + # Set command + self.parser_recover = self.subparsers.add_parser( + "recover", help="Recover Option Bytes" + ) + self._add_args(self.parser_recover) + self.parser_recover.set_defaults(func=self.recover) def _add_args(self, parser): parser.add_argument( @@ -75,6 +81,20 @@ class Main(App): return return_code + def recover(self): + self.logger.info("Setting Option Bytes") + + # OpenOCD + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + openocd.option_bytes_recover() + + return 0 + if __name__ == "__main__": Main()() diff --git a/scripts/otp.py b/scripts/otp.py index 19b8c4df4..e3f070999 100755 --- a/scripts/otp.py +++ b/scripts/otp.py @@ -17,6 +17,7 @@ OTP_COLORS = { "unknown": 0x00, "black": 0x01, "white": 0x02, + "transparent": 0x03, } OTP_REGIONS = { diff --git a/scripts/power.py b/scripts/power.py index 45a130c59..50bb2d4f7 100755 --- a/scripts/power.py +++ b/scripts/power.py @@ -1,5 +1,8 @@ #!/usr/bin/env python3 +import time +from typing import Optional + from flipper.app import App from flipper.storage import FlipperStorage from flipper.utils.cdc import resolve_port @@ -27,8 +30,20 @@ class Main(App): ) self.parser_reboot2dfu.set_defaults(func=self.reboot2dfu) - def _get_flipper(self): - if not (port := resolve_port(self.logger, self.args.port)): + def _get_flipper(self, retry_count: Optional[int] = 1): + port = None + self.logger.info(f"Attempting to find flipper with {retry_count} attempts.") + + for i in range(retry_count): + time.sleep(1) + self.logger.info(f"Attempting to find flipper #{i}.") + + if port := resolve_port(self.logger, self.args.port): + self.logger.info(f"Found flipper at {port}") + break + + if not port: + self.logger.info(f"Failed to find flipper") return None flipper = FlipperStorage(port) @@ -36,28 +51,28 @@ class Main(App): return flipper def power_off(self): - if not (flipper := self._get_flipper()): + if not (flipper := self._get_flipper(retry_count=10)): return 1 - self.logger.debug("Powering off") + self.logger.info("Powering off") flipper.send("power off" + "\r") flipper.stop() return 0 def reboot(self): - if not (flipper := self._get_flipper()): + if not (flipper := self._get_flipper(retry_count=10)): return 1 - self.logger.debug("Rebooting") + self.logger.info("Rebooting") flipper.send("power reboot" + "\r") flipper.stop() return 0 def reboot2dfu(self): - if not (flipper := self._get_flipper()): + if not (flipper := self._get_flipper(retry_count=10)): return 1 - self.logger.debug("Rebooting to DFU") + self.logger.info("Rebooting to DFU") flipper.send("power reboot2dfu" + "\r") flipper.stop() diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index b1830ab09..2cf43dce0 100755 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -4,7 +4,7 @@ import json import shutil import tarfile import zipfile -from os import makedirs, walk, environ +from os import makedirs, walk from os.path import basename, exists, join, relpath from ansi.color import fg diff --git a/scripts/testing/await_flipper.py b/scripts/testing/await_flipper.py index ea07d6be7..f8dffeb66 100755 --- a/scripts/testing/await_flipper.py +++ b/scripts/testing/await_flipper.py @@ -22,14 +22,14 @@ def flp_serial_by_name(flp_name): if os.path.exists(flp_serial): return flp_serial else: - logging.info(f"Couldn't find {logging.info} on this attempt.") + logging.info(f"Couldn't find {flp_name} on this attempt.") if os.path.exists(flp_name): return flp_name else: return "" -UPDATE_TIMEOUT = 60 * 4 # 4 minutes +UPDATE_TIMEOUT = 30 * 4 # 4 minutes def main(): @@ -50,7 +50,7 @@ def main(): if flipper == "": logging.error("Flipper not found!") - sys.exit(1) + exit(1) logging.info(f"Found Flipper at {flipper}") diff --git a/scripts/testing/units.py b/scripts/testing/units.py index fd8e29a73..db302e9da 100755 --- a/scripts/testing/units.py +++ b/scripts/testing/units.py @@ -20,14 +20,12 @@ def main(): logging.error("Flipper not found!") sys.exit(1) - with serial.Serial(flp_serial, timeout=10) as flipper: + with serial.Serial(flp_serial, timeout=150) as flipper: logging.info(f"Found Flipper at {flp_serial}") flipper.baudrate = 230400 flipper.flushOutput() flipper.flushInput() - flipper.timeout = 300 - flipper.read_until(b">: ").decode("utf-8") flipper.write(b"unit_tests\r") data = flipper.read_until(b">: ").decode("utf-8") diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index a3310a063..688858335 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -38,11 +38,11 @@ fbtenv_wget() fbtenv_restore_env() { TOOLCHAIN_ARCH_DIR_SED="$(echo "$TOOLCHAIN_ARCH_DIR" | sed 's/\//\\\//g')" - PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/python\/bin://g")"; - PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/bin://g")"; - PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/protobuf\/bin://g")"; - PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openocd\/bin://g")"; - PATH="$(echo "$PATH" | /usr/bin/sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openssl\/bin://g")"; + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/python\/bin://g")"; + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/bin://g")"; + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/protobuf\/bin://g")"; + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openocd\/bin://g")"; + PATH="$(echo "$PATH" | sed "s/$TOOLCHAIN_ARCH_DIR_SED\/openssl\/bin://g")"; if [ -n "${PS1:-""}" ]; then PS1="$(echo "$PS1" | sed 's/\[fbt\]//g')"; elif [ -n "${PROMPT:-""}" ]; then @@ -82,6 +82,9 @@ fbtenv_restore_env() fbtenv_check_sourced() { + if [ -n "${FBT_SKIP_CHECK_SOURCED:-""}" ]; then + return 0; + fi case "${ZSH_EVAL_CONTEXT:-""}" in *:file:*) setopt +o nomatch; # disabling 'no match found' warning in zsh return 0;; @@ -104,8 +107,6 @@ fbtenv_check_if_sourced_multiple_times() return 0; fi fi - echo "Warning! FBT environment script was sourced more than once!"; - echo "You might be doing things wrong, please open a new shell!"; return 1; } @@ -170,7 +171,7 @@ fbtenv_get_kernel_type() fbtenv_check_rosetta() { if [ "$ARCH_TYPE" = "arm64" ]; then - if ! /usr/bin/pgrep -q oahd; then + if ! pgrep -q oahd; then echo "Flipper Zero Toolchain needs Rosetta2 to run under Apple Silicon"; echo "Please instal it by typing 'softwareupdate --install-rosetta --agree-to-license'"; return 1; @@ -212,7 +213,7 @@ fbtenv_download_toolchain_tar() return 0; } -fbtenv_remove_old_tooclhain() +fbtenv_remove_old_toolchain() { printf "Removing old toolchain.."; rm -rf "${TOOLCHAIN_ARCH_DIR:?}"; @@ -243,12 +244,14 @@ fbtenv_unpack_toolchain() fbtenv_cleanup() { - printf "Cleaning up.."; if [ -n "${FBT_TOOLCHAIN_PATH:-""}" ]; then - rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.tar.gz; + printf "Cleaning up.."; rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.part; + if [ -z "${FBT_PRESERVE_TAR:-""}" ]; then + rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.tar.gz; + fi + echo "done"; fi - echo "done"; trap - 2; return 0; } @@ -301,16 +304,22 @@ fbtenv_download_toolchain() fbtenv_curl_wget_check || return 1; fbtenv_download_toolchain_tar || return 1; fi - fbtenv_remove_old_tooclhain; + fbtenv_remove_old_toolchain; fbtenv_unpack_toolchain || return 1; fbtenv_cleanup; return 0; } -fbtenv_print_version() +fbtenv_print_config() { - if [ -n "$FBT_VERBOSE" ]; then + if [ -n "${FBT_VERBOSE:-""}" ]; then echo "FBT: using toolchain version $(cat "$TOOLCHAIN_ARCH_DIR/VERSION")"; + if [ -n "${FBT_SKIP_CHECK_SOURCED:-""}" ]; then + echo "FBT: fbtenv will not check if it is sourced or not"; + fi + if [ -n "${FBT_PRESERVE_TAR:-""}" ]; then + echo "FBT: toolchain archives will be saved"; + fi fi } @@ -322,11 +331,13 @@ fbtenv_main() fbtenv_restore_env; return 0; fi - fbtenv_check_if_sourced_multiple_times; + if ! fbtenv_check_if_sourced_multiple_times; then + return 0; + fi; fbtenv_check_env_vars || return 1; fbtenv_check_download_toolchain || return 1; fbtenv_set_shell_prompt; - fbtenv_print_version; + fbtenv_print_config; PATH="$TOOLCHAIN_ARCH_DIR/python/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/bin:$PATH"; PATH="$TOOLCHAIN_ARCH_DIR/protobuf/bin:$PATH"; diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index 05ea4be2c..f30b157da 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -1,7 +1,7 @@ Set-StrictMode -Version 2.0 $ErrorActionPreference = "Stop" [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" -# TODO: fix +# TODO FL-3536: fix path to download_dir $download_dir = (Get-Item "$PSScriptRoot\..\..").FullName $toolchain_version = $args[0] $toolchain_target_path = $args[1] diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 9a9e0938c..1630135c2 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -1,12 +1,11 @@ -from SCons.Platform import TempFileMunge -from SCons.Node import FS -from SCons.Errors import UserError - - -import os import multiprocessing +import os import pathlib +from SCons.Errors import UserError +from SCons.Node import FS +from SCons.Platform import TempFileMunge + SetOption("num_jobs", multiprocessing.cpu_count()) SetOption("max_drift", 1) # SetOption("silent", False) @@ -67,16 +66,15 @@ core_env.Append(CPPDEFINES=GetOption("extra_defines")) # Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state -from fbt.util import ( - tempfile_arg_esc_func, - single_quote, - extract_abs_dir, - extract_abs_dir_path, - wrap_tempfile, - path_as_posix, -) -from fbt.appmanifest import FlipperAppType, FlipperApplication +from fbt.appmanifest import FlipperApplication, FlipperAppType from fbt.sdk.cache import SdkCache +from fbt.util import ( + path_as_posix, + resolve_real_dir_node, + single_quote, + tempfile_arg_esc_func, + wrap_tempfile, +) # Base environment with all tools loaded from SDK env = core_env.Clone( @@ -107,7 +105,7 @@ env = core_env.Clone( PROGSUFFIX=".elf", TEMPFILEARGESCFUNC=tempfile_arg_esc_func, SINGLEQUOTEFUNC=single_quote, - ABSPATHGETTERFUNC=extract_abs_dir_path, + ABSPATHGETTERFUNC=resolve_real_dir_node, APPS=[], UFBT_API_VERSION=SdkCache( core_env.subst("$SDK_DEFINITION"), load_version_only=True @@ -146,24 +144,20 @@ dist_env = env.Clone( ], ) -openocd_target = dist_env.OpenOCDFlash( +flash_target = dist_env.FwFlash( dist_env["UFBT_STATE_DIR"].File("flash"), - dist_env["FW_BIN"], - OPENOCD_COMMAND=[ - "-c", - "program ${SOURCE.posix} reset exit 0x08000000", - ], + dist_env["FW_ELF"], ) -dist_env.Alias("firmware_flash", openocd_target) -dist_env.Alias("flash", openocd_target) +dist_env.Alias("firmware_flash", flash_target) +dist_env.Alias("flash", flash_target) if env["FORCE"]: - env.AlwaysBuild(openocd_target) + env.AlwaysBuild(flash_target) firmware_jflash = dist_env.JFlash( dist_env["UFBT_STATE_DIR"].File("jflash"), dist_env["FW_BIN"], - JFLASHADDR="0x20000000", + JFLASHADDR="0x08000000", ) dist_env.Alias("firmware_jflash", firmware_jflash) dist_env.Alias("jflash", firmware_jflash) @@ -215,21 +209,6 @@ dist_env.PhonyTarget( GDBPYOPTS=debug_other_opts, ) - -dist_env.PhonyTarget( - "flash_blackmagic", - "$GDB $GDBOPTS $SOURCES $GDBFLASH", - source=dist_env["FW_ELF"], - GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", - GDBREMOTE="${BLACKMAGIC_ADDR}", - GDBFLASH=[ - "-ex", - "load", - "-ex", - "quit", - ], -) - flash_usb_full = dist_env.UsbInstall( dist_env["UFBT_STATE_DIR"].File("usbinstall"), [], @@ -277,7 +256,7 @@ for app in known_extapps: continue app_artifacts = appenv.BuildAppElf(app) - app_src_dir = extract_abs_dir(app_artifacts.app._appdir) + app_src_dir = resolve_real_dir_node(app_artifacts.app._appdir) app_artifacts.installer = [ appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact), appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug), @@ -348,6 +327,9 @@ appenv.PhonyTarget( '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', ) +# Update WiFi devboard firmware +dist_env.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") + # Linter dist_env.PhonyTarget( diff --git a/scripts/ufbt/commandline.scons b/scripts/ufbt/commandline.scons index 349b4ef25..99c34c35d 100644 --- a/scripts/ufbt/commandline.scons +++ b/scripts/ufbt/commandline.scons @@ -55,9 +55,21 @@ vars.AddVariables( "Blackmagic probe location", "auto", ), + EnumVariable( + "SWD_TRANSPORT", + help="SWD interface adapter type", + default="auto", + allowed_values=[ + "auto", + "cmsis-dap", + "stlink", + "blackmagic_usb", + "blackmagic_wifi", + ], + ), ( - "OPENOCD_ADAPTER_SERIAL", - "OpenOCD adapter serial number", + "SWD_TRANSPORT_SERIAL", + "SWD interface adapter serial number", "auto", ), ( diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py index 3f13edcdb..1df6a0591 100644 --- a/scripts/ufbt/site_tools/ufbt_help.py +++ b/scripts/ufbt/site_tools/ufbt_help.py @@ -20,12 +20,14 @@ Building: Build FAP app with appid={APPID}; upload & start it over USB Flashing & debugging: - flash, flash_blackmagic, *jflash: - Flash firmware to target using debug probe + flash, *jflash: + Flash firmware to target using SWD probe. See also SWD_TRANSPORT, SWD_TRANSPORT_SERIAL flash_usb, flash_usb_full: Install firmware using self-update package debug, debug_other, blackmagic: Start GDB + devboard_flash: + Update WiFi dev board with the latest firmware Other: cli: diff --git a/scripts/version.py b/scripts/version.py index f33c5e6e9..68670061b 100755 --- a/scripts/version.py +++ b/scripts/version.py @@ -41,10 +41,6 @@ class GitVersion: or "unknown" ) - force_no_dirty = os.environ.get("FORCE_NO_DIRTY", None) or "" - if force_no_dirty != "": - dirty = False - if "SOURCE_DATE_EPOCH" in os.environ: commit_date = datetime.utcfromtimestamp( int(os.environ["SOURCE_DATE_EPOCH"]) diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 0e75cd908..0b4f03da0 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -100,11 +100,6 @@ vars.AddVariables( "Version string for updater package", "${DIST_SUFFIX}", ), - ( - "FORCE_NO_DIRTY", - "Force disable dirty status of the build", - "", - ), ( "COPRO_CUBE_VERSION", "Cube version", @@ -180,9 +175,21 @@ vars.AddVariables( "Blackmagic probe location", "auto", ), + EnumVariable( + "SWD_TRANSPORT", + help="SWD interface adapter type", + default="auto", + allowed_values=[ + "auto", + "cmsis-dap", + "stlink", + "blackmagic_usb", + "blackmagic_wifi", + ], + ), ( - "OPENOCD_ADAPTER_SERIAL", - "OpenOCD adapter serial number", + "SWD_TRANSPORT_SERIAL", + "SWD interface adapter serial number", "auto", ), ( @@ -254,6 +261,15 @@ vars.AddVariables( "Full port name of Flipper to use, if multiple Flippers are connected", "auto", ), + EnumVariable( + "LANG_SERVER", + help="Language server type for vscode_dist.", + default="cpptools", + allowed_values=[ + "cpptools", + "clangd", + ], + ), ) Return("vars") diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 0cdc45557..b638b1018 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -3,7 +3,7 @@ from fbt.util import ( tempfile_arg_esc_func, single_quote, wrap_tempfile, - extract_abs_dir_path, + resolve_real_dir_node, ) import os @@ -20,7 +20,6 @@ variables_to_forward = [ # CI/CD variables "WORKFLOW_BRANCH_OR_TAG", "DIST_SUFFIX", - "FORCE_NO_DIRTY", # Python & other tools "HOME", "APPDATA", @@ -59,7 +58,7 @@ coreenv = VAR_ENV.Clone( PROGSUFFIX=".elf", ENV=forward_os_env, SINGLEQUOTEFUNC=single_quote, - ABSPATHGETTERFUNC=extract_abs_dir_path, + ABSPATHGETTERFUNC=resolve_real_dir_node, # Setting up temp file parameters - to overcome command line length limits TEMPFILEARGESCFUNC=tempfile_arg_esc_func, ROOT_DIR=Dir("#"), diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 5c6f18d68..97b7ac095 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -23,18 +23,14 @@ appenv.Replace( appenv.AppendUnique( CCFLAGS=[ - "-ggdb3", "-mword-relocations", "-mlong-calls", "-fno-common", "-nostdlib", - "-fvisibility=hidden", ], LINKFLAGS=[ "-Ur", "-Wl,-Ur", - # "-Wl,--orphan-handling=error", - "-Bsymbolic", "-nostartfiles", "-mlong-calls", "-fno-common",